|
| 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 | +} |
0 commit comments