Skip to content

Commit 7c3a119

Browse files
diseanbinarymaster
authored andcommitted
[KMTESTS:HAL] Add a test for string I/O intrinsic functions
CORE-20078
1 parent 4a3a446 commit 7c3a119

File tree

3 files changed

+153
-1
lines changed

3 files changed

+153
-1
lines changed

modules/rostests/kmtests/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64")
124124
add_asm_files(KMTEST_DRV_ASM
125125
kmtest_drv/vm_detect.S)
126126
list(APPEND KMTEST_DRV_SOURCE
127-
${KMTEST_DRV_ASM})
127+
${KMTEST_DRV_ASM}
128+
hal/HalPortIo.c)
128129
endif()
129130

130131
add_library(kmtest_drv MODULE ${KMTEST_DRV_SOURCE})
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* PROJECT: ReactOS Kernel-Mode Tests
3+
* LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4+
* PURPOSE: Tests for string I/O intrinsic functions
5+
* COPYRIGHT: Copyright 2025 Dmitry Borisov <[email protected]>
6+
*/
7+
8+
/* INCLUDES *******************************************************************/
9+
10+
#include <kmt_test.h>
11+
12+
#define NDEBUG
13+
#include <debug.h>
14+
15+
/* GLOBALS ********************************************************************/
16+
17+
/* Compile with the highest optimization level to trigger a bug (See CORE-20078) */
18+
#ifdef _MSC_VER
19+
#pragma optimize("gst", on)
20+
#pragma auto_inline(on)
21+
#else
22+
#pragma GCC optimize("O3")
23+
#endif
24+
25+
/* Used to isolate the effects of intrinsics from each other */
26+
typedef struct _TEST_CONTEXT
27+
{
28+
PVOID OldBuffer;
29+
UCHAR Pad[78];
30+
USHORT Port;
31+
ULONG Size;
32+
PVOID Buffer;
33+
} TEST_CONTEXT, *PTEST_CONTEXT;
34+
35+
/* TEST FUNCTIONS *************************************************************/
36+
37+
static
38+
/*
39+
* Isolate the effects of intrinsics from each other
40+
* and hide the TEST_CONTEXT initialization from the optimizer.
41+
*/
42+
DECLSPEC_NOINLINE
43+
VOID
44+
TestWriteStringUshort(
45+
_In_ PTEST_CONTEXT Context)
46+
{
47+
__outwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(USHORT));
48+
49+
/*
50+
* The 'rep outsw' instruction increments or decrements the address in ESI by Size * 2.
51+
* Test for CORE-20078: the ESI value should be preserved across calls.
52+
*/
53+
ok_eq_pointer(Context->Buffer, Context->OldBuffer);
54+
}
55+
56+
static
57+
DECLSPEC_NOINLINE
58+
VOID
59+
TestWriteStringUlong(
60+
_In_ PTEST_CONTEXT Context)
61+
{
62+
__outdwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(ULONG));
63+
64+
ok_eq_pointer(Context->Buffer, Context->OldBuffer);
65+
}
66+
67+
static
68+
DECLSPEC_NOINLINE
69+
VOID
70+
TestWriteStringUchar(
71+
_In_ PTEST_CONTEXT Context)
72+
{
73+
__inbytestring(Context->Port, Context->Buffer, Context->Size);
74+
75+
ok_eq_pointer(Context->Buffer, Context->OldBuffer);
76+
}
77+
78+
static
79+
DECLSPEC_NOINLINE
80+
VOID
81+
TestReadStringUshort(
82+
_In_ PTEST_CONTEXT Context)
83+
{
84+
__inwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(USHORT));
85+
86+
ok_eq_pointer(Context->Buffer, Context->OldBuffer);
87+
}
88+
89+
static
90+
DECLSPEC_NOINLINE
91+
VOID
92+
TestReadStringUlong(
93+
_In_ PTEST_CONTEXT Context)
94+
{
95+
__outdwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(ULONG));
96+
97+
ok_eq_pointer(Context->Buffer, Context->OldBuffer);
98+
}
99+
100+
static
101+
DECLSPEC_NOINLINE
102+
VOID
103+
TestReadStringUchar(
104+
_In_ PTEST_CONTEXT Context)
105+
{
106+
__inbytestring(Context->Port, Context->Buffer, Context->Size);
107+
108+
ok_eq_pointer(Context->Buffer, Context->OldBuffer);
109+
}
110+
111+
static
112+
VOID
113+
TestStringIo(VOID)
114+
{
115+
TEST_CONTEXT Context;
116+
UCHAR Buffer[20];
117+
118+
/* End of the x86 I/O range */
119+
Context.Port = 0xFFFF - sizeof(ULONG);
120+
121+
Context.Buffer = Buffer;
122+
Context.OldBuffer = Buffer;
123+
Context.Size = sizeof(Buffer);
124+
125+
TestReadStringUchar(&Context);
126+
TestReadStringUshort(&Context);
127+
TestReadStringUlong(&Context);
128+
129+
/*
130+
* Check whether the driver is running inside a virtual machine
131+
* as it's not safe to write to I/O ports
132+
* without having the port resources assigned.
133+
*/
134+
if (!skip(KmtIsVirtualMachine, "Please run those tests in a supported virtual machine\n"))
135+
{
136+
TestWriteStringUchar(&Context);
137+
TestWriteStringUshort(&Context);
138+
TestWriteStringUlong(&Context);
139+
}
140+
}
141+
142+
START_TEST(HalPortIo)
143+
{
144+
TestStringIo();
145+
}

modules/rostests/kmtests/kmtest_drv/testlist.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ KMT_TESTFUNC Test_FsRtlLegal;
2626
KMT_TESTFUNC Test_FsRtlMcb;
2727
KMT_TESTFUNC Test_FsRtlRemoveDotsFromPath;
2828
KMT_TESTFUNC Test_FsRtlTunnel;
29+
#if defined(_M_IX86) || defined(_M_AMD64)
30+
KMT_TESTFUNC Test_HalPortIo;
31+
#endif
2932
KMT_TESTFUNC Test_HalSystemInfo;
3033
KMT_TESTFUNC Test_IoCreateFile;
3134
KMT_TESTFUNC Test_IoDeviceInterface;
@@ -110,6 +113,9 @@ const KMT_TEST TestList[] =
110113
{ "FsRtlMcb", Test_FsRtlMcb },
111114
{ "FsRtlRemoveDotsFromPath", Test_FsRtlRemoveDotsFromPath },
112115
{ "FsRtlTunnel", Test_FsRtlTunnel },
116+
#if defined(_M_IX86) || defined(_M_AMD64)
117+
{ "HalPortIo", Test_HalPortIo },
118+
#endif
113119
{ "HalSystemInfo", Test_HalSystemInfo },
114120
{ "IoCreateFile", Test_IoCreateFile },
115121
{ "IoDeviceInterface", Test_IoDeviceInterface },

0 commit comments

Comments
 (0)