diff --git a/README.md b/README.md index b1f14f25..9a2b5b32 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Wine. All those differences are also documented on the Included bug fixes and improvements =================================== -**Bugfixes and features included in the next upcoming release [18]:** +**Bugfixes and features included in the next upcoming release [19]:** * Add stub for KeWaitForMultipleObjects * Add stubs for D3DXCreateAnimationController interface @@ -50,6 +50,7 @@ Included bug fixes and improvements * Fix init of LONGLONG variable with a negative value in TGA decoder * Fix wrong colors in Wolfenstein (2009) ([Wine Bug #34692](https://bugs.winehq.org/show_bug.cgi?id=34692)) * Graphical issues in Inquisitor ([Wine Bug #32490](https://bugs.winehq.org/show_bug.cgi?id=32490)) +* Implement ntoskrnl driver testing framework. * Implement semi-stub for IDirectPlayVoiceClient::GetCompressionTypes ([Wine Bug #29238](https://bugs.winehq.org/show_bug.cgi?id=29238)) * Multiple applications start wrong executable if whitespace present in name ([Wine Bug #19666](https://bugs.winehq.org/show_bug.cgi?id=19666)) * Port Royale doesn't display ocean correctly ([Wine Bug #17913](https://bugs.winehq.org/show_bug.cgi?id=17913)) diff --git a/debian/changelog b/debian/changelog index 078ec78c..031fa411 100644 --- a/debian/changelog +++ b/debian/changelog @@ -14,6 +14,7 @@ wine-staging (1.7.35) UNRELEASED; urgency=low * Added patch to implement stubs for D3DXCreateAnimationController interface. * Added patch to implement semi-stub for IDirectPlayVoiceClient::GetCompressionTypes. * Added patch to fix cursor clip regression / broken raw input in multiple games. + * Added patches to implement ntoskrnl driver testing framework. * Removed patch to fix RandR on some broken nVidia systems (accepted upstream). * Removed patch to set last error on success in WSARecv (accepted upstream). * Removed patch to fix handling of subdirectory in FtpFindFirstFile (accepted upstream). diff --git a/patches/makedep-PARENTSPEC/0001-makedep-Add-support-for-PARENTSPEC-Makefile-variable.patch b/patches/makedep-PARENTSPEC/0001-makedep-Add-support-for-PARENTSPEC-Makefile-variable.patch index e257f2fe..7ec4a979 100644 --- a/patches/makedep-PARENTSPEC/0001-makedep-Add-support-for-PARENTSPEC-Makefile-variable.patch +++ b/patches/makedep-PARENTSPEC/0001-makedep-Add-support-for-PARENTSPEC-Makefile-variable.patch @@ -11,14 +11,12 @@ diff --git a/tools/makedep.c b/tools/makedep.c index 54aab45..d68bc03 100644 --- a/tools/makedep.c +++ b/tools/makedep.c -@@ -161,6 +161,7 @@ struct makefile - const char *top_src_dir; +@@ -162,4 +162,5 @@ struct makefile const char *top_obj_dir; const char *parent_dir; + const char *parent_spec; const char *module; const char *testdll; - const char *staticlib; @@ -2081,7 +2082,13 @@ static struct strarray output_sources( struct makefile *make, struct strarray *t char *spec_file = NULL; @@ -34,14 +32,12 @@ index 54aab45..d68bc03 100644 for (i = 0; i < make->delayimports.count; i++) strarray_add( &all_libs, strmake( "-l%s", make->delayimports.str[i] )); for (i = 0; i < make->imports.count; i++) -@@ -2597,6 +2604,7 @@ static void update_makefile( const char *path ) - strarray_set_value( &make->vars, "srcdir", src_dir_path( make, "" )); +@@ -2598,4 +2605,5 @@ static void update_makefile( const char *path ) make->parent_dir = get_expanded_make_variable( make, "PARENTSRC" ); + make->parent_spec = get_expanded_make_variable( make, "PARENTSPEC" ); make->module = get_expanded_make_variable( make, "MODULE" ); make->testdll = get_expanded_make_variable( make, "TESTDLL" ); - make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); -- 2.1.3 diff --git a/patches/ntoskrnl-DriverTest/0001-ntoskrnl.exe-tests-Add-initial-driver-testing-framew.patch b/patches/ntoskrnl-DriverTest/0001-ntoskrnl.exe-tests-Add-initial-driver-testing-framew.patch new file mode 100644 index 00000000..eb0fa2fd --- /dev/null +++ b/patches/ntoskrnl-DriverTest/0001-ntoskrnl.exe-tests-Add-initial-driver-testing-framew.patch @@ -0,0 +1,785 @@ +From daa8ccd8d94c1e8c53d26b2e985d75c7c7a47f1b Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +Date: Sun, 18 Jan 2015 05:42:10 +0100 +Subject: ntoskrnl.exe/tests: Add initial driver testing framework and + corrsponding changes to Makefile system. (rev 2) + +--- + aclocal.m4 | 31 +++ + configure.ac | 2 + + dlls/ntoskrnl.exe/tests/Makefile.in | 8 + + dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in | 11 + + dlls/ntoskrnl.exe/tests/driver.sys/driver.c | 130 ++++++++++++ + dlls/ntoskrnl.exe/tests/driver.sys/driver.h | 28 +++ + dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec | 1 + + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 226 +++++++++++++++++++++ + tools/make_makefiles | 9 +- + tools/makedep.c | 174 +++++++++++++++- + 10 files changed, 617 insertions(+), 3 deletions(-) + create mode 100644 dlls/ntoskrnl.exe/tests/Makefile.in + create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in + create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/driver.c + create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/driver.h + create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec + create mode 100644 dlls/ntoskrnl.exe/tests/ntoskrnl.c + +diff --git a/aclocal.m4 b/aclocal.m4 +index 3d43721..adc6b04 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -617,6 +617,28 @@ $ac_dir/crosstest: $ac_dir/Makefile __builddeps__ dummy + fi + } + ++wine_fn_config_resource () ++{ ++ ac_dir=$[1] ++ ac_name=$[2] ++ ac_flags=$[3] ++ ac_dll=$ac_name ++ ++ case $ac_name in ++ *.*) ;; ++ *) ac_dll=$ac_dll.dll ;; ++ esac ++ ++ ac_clean= ++ test -n "$CROSSTARGET" && ac_clean=`expr $ac_dir/$ac_dll : "\\(.*\\)\\."`_crossres.`expr $ac_dll : ".*\\.\\(.*\\)"` ++ test -n "$DLLEXT" || ac_clean="$ac_dir/$ac_dll" ++ ++ AS_VAR_IF([enable_tests],[no],[wine_fn_disabled_rules $ac_clean; return]) ++ ++ wine_fn_depend_rules ++ wine_fn_clean_rules $ac_clean ++} ++ + wine_fn_config_tool () + { + ac_dir=$[1] +@@ -722,6 +744,15 @@ wine_fn_config_test $1 ac_name[]ac_suffix [$2]dnl + m4_popdef([ac_suffix])dnl + m4_popdef([ac_name])]) + ++dnl **** Create a test resource makefile from config.status **** ++dnl ++dnl Usage: WINE_CONFIG_RESOURCE(dir,flags) ++dnl ++AC_DEFUN([WINE_CONFIG_RESOURCE],[AC_REQUIRE([WINE_CONFIG_HELPERS])dnl ++m4_pushdef([ac_name],[m4_bpatsubst([$1],[.*/\([^/\]*\)$],[\1])])dnl ++wine_fn_config_resource $1 ac_name [$2]dnl ++m4_popdef([ac_name])]) ++ + dnl **** Create a static lib makefile from config.status **** + dnl + dnl Usage: WINE_CONFIG_LIB(name,flags) +diff --git a/configure.ac b/configure.ac +index ab8945a..dfc3d07 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3126,4 +3126,6 @@ WINE_CONFIG_DLL(ntdsapi,,[implib]) + WINE_CONFIG_TEST(dlls/ntdsapi/tests) + WINE_CONFIG_DLL(ntoskrnl.exe,,[implib]) ++WINE_CONFIG_TEST(dlls/ntoskrnl.exe/tests) ++WINE_CONFIG_RESOURCE(dlls/ntoskrnl.exe/tests/driver.sys) + WINE_CONFIG_DLL(ntprint) + WINE_CONFIG_TEST(dlls/ntprint/tests) +diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in +new file mode 100644 +index 0000000..f0c2460 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/Makefile.in +@@ -0,0 +1,8 @@ ++TESTDLL = ntoskrnl.exe ++IMPORTS = advapi32 ++ ++C_SRCS = \ ++ ntoskrnl.c ++ ++RC_DLLS = \ ++ driver.sys +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in b/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in +new file mode 100644 +index 0000000..bc040e4 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in +@@ -0,0 +1,11 @@ ++RESOURCE = driver.sys ++IMPORTS = ntoskrnl.exe ++ ++WINEFLAGS = -Wb,--subsystem,native ++CROSSFLAGS = -nostartfiles -nostdlib -nodefaultlibs \ ++ -Wl,--subsystem,native \ ++ -Wl,--image-base,0x10000 \ ++ -Wl,-entry,_DriverEntry@8 ++ ++C_SRCS = \ ++ driver.c +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.c b/dlls/ntoskrnl.exe/tests/driver.sys/driver.c +new file mode 100644 +index 0000000..5756090 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.c +@@ -0,0 +1,130 @@ ++/* ++ * ntoskrnl.exe testing framework ++ * ++ * Copyright 2015 Sebastian Lackner ++ * Copyright 2015 Michael Müller ++ * Copyright 2015 Christian Costa ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winbase.h" ++#include "winternl.h" ++#include "winioctl.h" ++#include "ddk/wdm.h" ++ ++#include "driver.h" ++ ++const WCHAR driver_device[] = {'\\','D','e','v','i','c','e', ++ '\\','W','i','n','e','T','e','s','t','D','r','i','v','e','r',0}; ++const WCHAR driver_link[] = {'\\','D','o','s','D','e','v','i','c','e','s', ++ '\\','W','i','n','e','T','e','s','t','D','r','i','v','e','r',0}; ++ ++ ++static NTSTATUS test_basic_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) ++{ ++ const char str[] = "Wine is not an emulator"; ++ ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; ++ char *buffer = irp->AssociatedIrp.SystemBuffer; ++ int i; ++ ++ if (!buffer) ++ return STATUS_ACCESS_VIOLATION; ++ ++ if (length < sizeof(str)-1) ++ return STATUS_BUFFER_TOO_SMALL; ++ ++ for (i = 0; i < sizeof(str)-1; i++) ++ buffer[i] = str[i]; ++ ++ *info = sizeof(str)-1; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS WINAPI driver_Create(DEVICE_OBJECT *device, IRP *irp) ++{ ++ return STATUS_SUCCESS; ++} ++ ++static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) ++{ ++ IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); ++ NTSTATUS status = STATUS_NOT_SUPPORTED; ++ ULONG_PTR information = 0; ++ ++ switch (stack->Parameters.DeviceIoControl.IoControlCode) ++ { ++ case IOCTL_WINETEST_BASIC_IOCTL: ++ status = test_basic_ioctl(irp, stack, &information); ++ break; ++ ++ default: ++ break; ++ } ++ ++ irp->IoStatus.Status = status; ++ irp->IoStatus.Information = information; ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ return status; ++} ++ ++static NTSTATUS WINAPI driver_Close(DEVICE_OBJECT *device, IRP *irp) ++{ ++ return STATUS_SUCCESS; ++} ++ ++static VOID WINAPI driver_Unload(DRIVER_OBJECT *driver) ++{ ++ UNICODE_STRING linkW; ++ ++ DbgPrint("unloading driver\n"); ++ ++ RtlInitUnicodeString(&linkW, driver_link); ++ IoDeleteSymbolicLink(&linkW); ++ ++ IoDeleteDevice(driver->DeviceObject); ++} ++ ++NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, PUNICODE_STRING registry) ++{ ++ UNICODE_STRING nameW, linkW; ++ DEVICE_OBJECT *device; ++ NTSTATUS status; ++ ++ DbgPrint("loading driver\n"); ++ ++ /* Allow unloading of the driver */ ++ driver->DriverUnload = driver_Unload; ++ ++ /* Set driver functions */ ++ driver->MajorFunction[IRP_MJ_CREATE] = driver_Create; ++ driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_IoControl; ++ driver->MajorFunction[IRP_MJ_CLOSE] = driver_Close; ++ ++ RtlInitUnicodeString(&nameW, driver_device); ++ RtlInitUnicodeString(&linkW, driver_link); ++ ++ if (!(status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, ++ FILE_DEVICE_SECURE_OPEN, FALSE, &device))) ++ status = IoCreateSymbolicLink(&linkW, &nameW); ++ ++ return status; ++} +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.h b/dlls/ntoskrnl.exe/tests/driver.sys/driver.h +new file mode 100644 +index 0000000..372e908 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.h +@@ -0,0 +1,28 @@ ++/* ++ * ntoskrnl.exe testing framework ++ * ++ * Copyright 2015 Sebastian Lackner ++ * Copyright 2015 Michael Müller ++ * Copyright 2015 Christian Costa ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++ ++/* ++ * All custom IOCTLs need to have a function value >= 0x800. ++ */ ++ ++#define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec b/dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec +new file mode 100644 +index 0000000..76421d7 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec +@@ -0,0 +1 @@ ++# nothing to export +diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c +new file mode 100644 +index 0000000..9b8a6a7 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c +@@ -0,0 +1,226 @@ ++/* ++ * ntoskrnl.exe testing framework ++ * ++ * Copyright 2015 Sebastian Lackner ++ * Copyright 2015 Michael Müller ++ * Copyright 2015 Christian Costa ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++#include "windows.h" ++#include "winsvc.h" ++#include "winioctl.h" ++#include "wine/test.h" ++ ++#include "driver.sys/driver.h" ++ ++static const char driver_name[] = "WineTestDriver"; ++static const char device_path[] = "\\\\.\\WineTestDriver"; ++ ++/* extracts a driver from a resource to a filename */ ++static BOOL extract_resource(const char *name, const char *filename) ++{ ++ DWORD size, written = 0; ++ HGLOBAL reshandle; ++ HRSRC resinfo; ++ HANDLE file; ++ char *data; ++ ++ resinfo = FindResourceA(NULL, name, (LPCSTR)RT_RCDATA); ++ if (!resinfo) ++ return FALSE; ++ ++ reshandle = LoadResource(NULL, resinfo); ++ if (!reshandle) ++ return FALSE; ++ ++ data = LockResource(reshandle); ++ if (!data) ++ return FALSE; ++ ++ size = SizeofResource(NULL, resinfo); ++ if (!size) ++ return FALSE; ++ ++ file = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (file == INVALID_HANDLE_VALUE) ++ return FALSE; ++ ++ while (size) ++ { ++ if (!WriteFile(file, data, size, &written, NULL)) ++ { ++ CloseHandle(file); ++ return FALSE; ++ } ++ ++ data += written; ++ size -= written; ++ } ++ ++ CloseHandle(file); ++ return TRUE; ++} ++ ++static void wait_driver(SC_HANDLE service, SERVICE_STATUS *status) ++{ ++ for (;;) ++ { ++ Sleep(100); ++ ++ if (!QueryServiceStatus(service, status)) ++ { ++ ok(0, "QueryServiceStatus failed with %x\n", GetLastError()); ++ status->dwCurrentState = SERVICE_STOPPED; ++ return; ++ } ++ ++ if (status->dwCurrentState != SERVICE_STOP_PENDING && ++ status->dwCurrentState != SERVICE_START_PENDING) ++ return; ++ } ++} ++ ++/* unload a driver and delete the temporary file */ ++static void unload_driver(SC_HANDLE service, const char *filename) ++{ ++ SERVICE_STATUS status; ++ ++ if (service) ++ { ++ /* Wine specific hack - when the test is the first application ++ * in a wineprefix, then the driver will need some time to start up, ++ * before we can stop them again. */ ++ wait_driver(service, &status); ++ ++ ControlService(service, SERVICE_CONTROL_STOP, &status); ++ wait_driver(service, &status); ++ ok(status.dwCurrentState == SERVICE_STOPPED, ++ "expected SERVICE_STOPPED, got %d\n", status.dwCurrentState); ++ ++ DeleteService(service); ++ CloseServiceHandle(service); ++ } ++ if (filename) ++ { ++ ok(DeleteFileA(filename), ++ "DeleteFileA failed with %x\n", GetLastError()); ++ } ++} ++ ++/* load a driver and return the service handle */ ++static SC_HANDLE load_driver(char *filename) ++{ ++ SC_HANDLE manager, service; ++ SERVICE_STATUS status; ++ char temp[MAX_PATH]; ++ ++ manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS); ++ if (!manager) ++ return FALSE; ++ ++ /* before we start with the actual tests, make sure to terminate ++ * any old wine test drivers. */ ++ service = OpenServiceA(manager, driver_name, SERVICE_ALL_ACCESS); ++ if (service) unload_driver(service, NULL); ++ ++ /* extract the new driver which is embedded into a resource */ ++ GetTempPathA(sizeof(temp), temp); ++ GetTempFileNameA(temp, "drv", 0, filename); ++ ++ if (!extract_resource("driver.sys", filename)) ++ { ++ ok(0, "Failed to extract driver resource\n"); ++ goto err; ++ } ++ ++ trace("Trying to load driver %s\n", filename); ++ ++ /* load the new driver */ ++ service = CreateServiceA(manager, driver_name, driver_name, ++ SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, ++ SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, ++ filename, NULL, NULL, NULL, NULL, NULL); ++ if (!service) ++ { ++ ok(0, "CreateServiceA failed with %x\n", GetLastError()); ++ goto err; ++ } ++ ++ ok(StartServiceA(service, 0, NULL), ++ "StartServiceA failed with %x\n", GetLastError()); ++ ++ CloseServiceHandle(manager); ++ ++ /* wait for the service to start up properly */ ++ wait_driver(service, &status); ++ ok(status.dwCurrentState == SERVICE_RUNNING, ++ "expected SERVICE_RUNNING, got %d\n", status.dwCurrentState); ++ ++ if (status.dwCurrentState != SERVICE_RUNNING) ++ { ++ unload_driver(service, filename); ++ return NULL; ++ } ++ ++ return service; ++ ++err: ++ CloseServiceHandle(manager); ++ unload_driver(NULL, filename); ++ return NULL; ++} ++ ++static void test_basic_ioctl(void) ++{ ++ const char str[] = "Wine is not an emulator"; ++ DWORD bytes_returned; ++ char buf[32]; ++ HANDLE file; ++ BOOL res; ++ ++ file = CreateFileA(device_path, GENERIC_READ | GENERIC_WRITE, ++ 0, NULL, OPEN_EXISTING, 0, NULL); ++ if (file == INVALID_HANDLE_VALUE) ++ { ++ ok(0, "Connecting to driver failed with %x\n", GetLastError()); ++ return; ++ } ++ ++ res = DeviceIoControl(file, IOCTL_WINETEST_BASIC_IOCTL, NULL, 0, buf, ++ sizeof(buf), &bytes_returned, NULL); ++ ok(res, "DeviceIoControl failed with %x\n", GetLastError()); ++ ok(bytes_returned == sizeof(str)-1, "Unexpected number of bytes\n"); ++ ok(!memcmp(buf, str, sizeof(str)-1), "Unexpected response data\n"); ++ ++ CloseHandle(file); ++} ++ ++START_TEST(ntoskrnl) ++{ ++ char filename[MAX_PATH]; ++ SC_HANDLE service; ++ ++ if (!(service = load_driver(filename))) ++ { ++ win_skip("Failed to load driver, skipping tests.\n"); ++ return; ++ } ++ ++ test_basic_ioctl(); ++ ++ unload_driver(service, filename); ++} +diff --git a/tools/make_makefiles b/tools/make_makefiles +index f0b40c0..46b8389 100755 +--- a/tools/make_makefiles ++++ b/tools/make_makefiles +@@ -257,7 +257,7 @@ sub parse_makefile($) + while (/\\$/) { chop; $_ .= ; chomp; } # merge continued lines + next if (/^\s*$/); + +- if (/^\s*(MODULE|IMPORTLIB|TESTDLL|PARENTSRC|APPMODE)\s*=\s*(.*)/) ++ if (/^\s*(MODULE|IMPORTLIB|TESTDLL|RESOURCE|PARENTSRC|APPMODE)\s*=\s*(.*)/) + { + my $var = $1; + $make{$var} = $2; +@@ -476,6 +476,13 @@ sub update_makefiles(@) + (my $dir = $file) =~ s/^(.*)\/Makefile/$1/; + push @lines, "WINE_CONFIG_TEST($dir$flag_args)\n"; + } ++ elsif (defined($make{"RESOURCE"})) # test resource ++ { ++ die "MODULE should not be defined in $file" if defined $make{"MODULE"}; ++ die "STATICLIB should not be defined in $file" if defined $make{"STATICLIB"}; ++ (my $dir = $file) =~ s/^(.*)\/Makefile/$1/; ++ push @lines, "WINE_CONFIG_RESOURCE($dir$flag_args)\n"; ++ } + elsif (defined($make{"MODULE"}) && $make{"MODULE"} =~ /\.a$/) # import lib + { + die "MODULE should not be defined as static lib in $file" unless $file =~ /^dlls\//; +diff --git a/tools/makedep.c b/tools/makedep.c +index 54aab45..c9f48fe 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -164,4 +164,5 @@ struct makefile + const char *module; + const char *testdll; ++ const char *resource; + const char *staticlib; + const char *importlib; +@@ -466,6 +467,30 @@ static char *get_extension( char *filename ) + + + /******************************************************************* ++ * prepend_extension ++ */ ++static char *prepend_extension( const char *name, const char *prepend, const char *new_ext ) ++{ ++ int name_len, prepend_len = strlen(prepend); ++ const char *ext; ++ char *ret; ++ ++ ext = strrchr( name, '.' ); ++ if (ext && strchr(ext, '/')) ext = NULL; ++ name_len = ext ? (ext - name) : strlen( name ); ++ ++ if (!ext) ext = new_ext; ++ if (!ext) ext = ""; ++ ++ ret = xmalloc( name_len + prepend_len + strlen(ext) + 1 ); ++ memcpy( ret, name, name_len ); ++ memcpy( ret + name_len, prepend, prepend_len ); ++ strcpy( ret + name_len + prepend_len, ext ); ++ return ret; ++} ++ ++ ++/******************************************************************* + * replace_extension + */ + static char *replace_extension( const char *name, const char *old_ext, const char *new_ext ) +@@ -1747,6 +1772,7 @@ static struct strarray output_sources( struct makefile *make, struct strarray *t + struct strarray subdirs = empty_strarray; + struct strarray phony_targets = empty_strarray; + struct strarray all_targets = get_expanded_make_var_array( make, "PROGRAMS" ); ++ struct strarray resource_dlls = get_expanded_make_var_array( make, "RC_DLLS" ); + + for (i = 0; i < linguas.count; i++) + strarray_add( &mo_files, strmake( "%s/%s.mo", top_obj_dir_path( make, "po" ), linguas.str[i] )); +@@ -1980,7 +2006,7 @@ static struct strarray output_sources( struct makefile *make, struct strarray *t + } + else + { +- int need_cross = make->testdll || ++ int need_cross = make->testdll || make->resource || + (source->file->flags & FLAG_C_IMPLIB) || + (make->module && make->staticlib); + +@@ -1994,7 +2020,7 @@ static struct strarray output_sources( struct makefile *make, struct strarray *t + output_filenames( includes ); + output_filenames( make->define_args ); + output_filenames( extradefs ); +- if (make->module || make->staticlib || make->testdll) ++ if (make->module || make->staticlib || make->testdll || make->resource) + { + output_filenames( dll_flags ); + if (make->use_msvcrt) output_filenames( msvcrt_flags ); +@@ -2074,6 +2100,72 @@ static struct strarray output_sources( struct makefile *make, struct strarray *t + output( "\n" ); + } + ++ /* rules for resource dlls, call Makefile in subdirectory */ ++ ++ if (resource_dlls.count) ++ { ++ for (i = 0; i < resource_dlls.count; i++) ++ { ++ output( "%s/Makefile: dummy\n", resource_dlls.str[i] ); ++ output( "\t@cd %s && $(MAKE) %s/Makefile\n", make->top_obj_dir, ++ base_dir_path( make, resource_dlls.str[i] ) ); ++ } ++ ++ for (i = 0; i < resource_dlls.count; i++) ++ { ++ output( "%s/%s%s: %s/Makefile dummy\n", resource_dlls.str[i], ++ resource_dlls.str[i], dll_ext, resource_dlls.str[i] ); ++ output( "\t@cd %s && $(MAKE) %s%s\n", resource_dlls.str[i], ++ resource_dlls.str[i], dll_ext ); ++ } ++ ++ output( "%s:", obj_dir_path( make, "resource_dlls.o" )); ++ for (i = 0; i < resource_dlls.count; i++) ++ output( " %s/%s%s", resource_dlls.str[i], resource_dlls.str[i], dll_ext ); ++ output( "\n" ); ++ output( "\t(" ); ++ for (i = 0; i < resource_dlls.count; i++) ++ { ++ if (i != 0) output( "; \\\n " ); ++ output( "echo \"%s RCDATA %s/%s%s\"", resource_dlls.str[i], ++ resource_dlls.str[i], resource_dlls.str[i], dll_ext ); ++ } ++ output( ") | %s -o $@\n", tools_path( make, "wrc" ) ); ++ strarray_add( &clean_files, "resource_dlls.o" ); ++ strarray_add( &object_files, "resource_dlls.o" ); ++ ++ if (crosstarget && (make->testdll || (make->module && make->staticlib))) ++ { ++ for (i = 0; i < resource_dlls.count; i++) ++ { ++ char *name = prepend_extension( resource_dlls.str[i], "_crossres", ".dll" ); ++ output( "%s/%s: %s/Makefile dummy\n", resource_dlls.str[i], name, resource_dlls.str[i] ); ++ output( "\t@cd %s && $(MAKE) %s\n", resource_dlls.str[i], name ); ++ free( name ); ++ } ++ ++ output( "%s:", obj_dir_path( make, "resource_dlls.cross.o" )); ++ for (i = 0; i < resource_dlls.count; i++) ++ { ++ char *name = prepend_extension( resource_dlls.str[i], "_crossres", ".dll" ); ++ output( " %s/%s", resource_dlls.str[i], name ); ++ free( name ); ++ } ++ output( "\n" ); ++ output( "\t(" ); ++ for (i = 0; i < resource_dlls.count; i++) ++ { ++ char *name = prepend_extension( resource_dlls.str[i], "_crossres", ".dll" ); ++ if (i != 0) output( "; \\\n " ); ++ output( "echo \"%s RCDATA %s/%s\"", resource_dlls.str[i], resource_dlls.str[i], name ); ++ free( name ); ++ } ++ output( ") | %s -o $@\n", tools_path( make, "wrc" ) ); ++ strarray_add( &clean_files, "resource_dlls.cross.o" ); ++ strarray_add( &crossobj_files, "resource_dlls.cross.o" ); ++ } ++ } ++ + if (make->module && !make->staticlib) + { + struct strarray all_libs = empty_strarray; +@@ -2331,6 +2423,83 @@ static struct strarray output_sources( struct makefile *make, struct strarray *t + *testlist_files = strarray_replace_extension( &ok_files, ".ok", "" ); + } + ++ if (make->resource) ++ { ++ char *extra_wine_flags = get_expanded_make_variable( make, "WINEFLAGS" ); ++ char *extra_cross_flags = get_expanded_make_variable( make, "CROSSFLAGS" ); ++ struct strarray all_libs = empty_strarray; ++ char *module_path = obj_dir_path( make, make->resource ); ++ char *spec_file = NULL; ++ ++ if (!make->appmode.count) ++ spec_file = src_dir_path( make, replace_extension( make->resource, ".dll", ".spec" )); ++ for (i = 0; i < make->imports.count; i++) ++ strarray_add( &all_libs, strmake( "-l%s", make->imports.str[i] )); ++ strarray_addall( &all_libs, get_expanded_make_var_array( make, "LIBS" )); ++ ++ if (*dll_ext) ++ { ++ strarray_add( &all_targets, strmake( "%s%s", make->resource, dll_ext )); ++ strarray_add( &all_targets, strmake( "%s.fake", make->resource )); ++ output( "%s%s %s.fake:", module_path, dll_ext, module_path ); ++ } ++ else ++ { ++ strarray_add( &all_targets, make->resource ); ++ output( "%s:", module_path ); ++ } ++ if (spec_file) output_filename( spec_file ); ++ output_filenames_obj_dir( make, object_files ); ++ output_filenames_obj_dir( make, res_files ); ++ output( "\n" ); ++ output( "\t%s -o $@", tools_path( make, "winegcc" )); ++ output_filename( strmake( "-B%s", tools_dir_path( make, "winebuild" ))); ++ if (tools_dir) output_filename( strmake( "--sysroot=%s", top_obj_dir_path( make, "" ))); ++ output_filenames( target_flags ); ++ output_filenames( unwind_flags ); ++ if (spec_file) ++ { ++ output( " -shared %s", spec_file ); ++ output_filenames( make->extradllflags ); ++ } ++ else output_filenames( make->appmode ); ++ if (extra_wine_flags) output( " %s", extra_wine_flags ); ++ output_filenames_obj_dir( make, object_files ); ++ output_filenames_obj_dir( make, res_files ); ++ output_filenames( all_libs ); ++ output_filename( "$(LDFLAGS)" ); ++ output( "\n" ); ++ ++ if (crosstarget) ++ { ++ char *crossres = prepend_extension( make->resource, "_crossres", ".dll" ); ++ ++ strarray_add( &clean_files, crossres ); ++ output( "%s:", obj_dir_path( make, crossres )); ++ output_filenames_obj_dir( make, crossobj_files ); ++ output_filenames_obj_dir( make, res_files ); ++ output( "\n" ); ++ output( "\t%s -o $@ -b %s", tools_path( make, "winegcc" ), crosstarget ); ++ output_filename( strmake( "-B%s", tools_dir_path( make, "winebuild" ))); ++ if (tools_dir) output_filename( strmake( "--sysroot=%s", top_obj_dir_path( make, "" ))); ++ output_filename( "--lib-suffix=.cross.a" ); ++ if (spec_file) ++ { ++ output( " -shared %s", spec_file ); ++ output_filenames( make->extradllflags ); ++ } ++ else output_filenames( make->appmode ); ++ if (extra_cross_flags) output( " %s", extra_cross_flags ); ++ output_filenames_obj_dir( make, crossobj_files ); ++ output_filenames_obj_dir( make, res_files ); ++ output_filenames( all_libs ); ++ output_filename( "$(LDFLAGS)" ); ++ output( "\n" ); ++ strarray_add( &phony_targets, obj_dir_path( make, "crosstest" )); ++ if (make->obj_dir) output( "crosstest: %s\n", obj_dir_path( make, "crosstest" )); ++ } ++ } ++ + if (all_targets.count) + { + output( "all:" ); +@@ -2600,4 +2769,5 @@ static void update_makefile( const char *path ) + make->module = get_expanded_make_variable( make, "MODULE" ); + make->testdll = get_expanded_make_variable( make, "TESTDLL" ); ++ make->resource = get_expanded_make_variable( make, "RESOURCE" ); + make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); + make->importlib = get_expanded_make_variable( make, "IMPORTLIB" ); +-- +2.2.1 + diff --git a/patches/ntoskrnl-DriverTest/0002-ntoskrnl.exe-tests-Add-kernel-compliant-test-functio.patch b/patches/ntoskrnl-DriverTest/0002-ntoskrnl.exe-tests-Add-kernel-compliant-test-functio.patch new file mode 100644 index 00000000..ea40bbfb --- /dev/null +++ b/patches/ntoskrnl-DriverTest/0002-ntoskrnl.exe-tests-Add-kernel-compliant-test-functio.patch @@ -0,0 +1,752 @@ +From 2532d093c1ae0f4846879cca9c8e08f04a1e7bfc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Tue, 20 Jan 2015 18:39:36 +0100 +Subject: ntoskrnl.exe/tests: Add kernel compliant test functions. + +--- + dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +- + dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in | 3 +- + dlls/ntoskrnl.exe/tests/driver.sys/driver.c | 38 ++--- + dlls/ntoskrnl.exe/tests/driver.sys/driver.h | 18 ++- + dlls/ntoskrnl.exe/tests/driver.sys/test.c | 195 ++++++++++++++++++++++++ + dlls/ntoskrnl.exe/tests/driver.sys/test.h | 197 +++++++++++++++++++++++++ + dlls/ntoskrnl.exe/tests/driver.sys/util.h | 47 ++++++ + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 53 +++++-- + include/wine/test.h | 39 ++++- + 9 files changed, 551 insertions(+), 41 deletions(-) + create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/test.c + create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/test.h + create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/util.h + +diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +index b824250..c104002 100644 +--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec ++++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +@@ -1423,7 +1423,7 @@ + @ cdecl -private _strrev(str) msvcrt._strrev + @ cdecl -private _strset(str long) msvcrt._strset + @ cdecl -private _strupr(str) msvcrt._strupr +-@ cdecl -private _vsnprintf(ptr long str ptr) msvcrt._vsnprintf ++@ cdecl _vsnprintf(ptr long str ptr) msvcrt._vsnprintf + @ cdecl -private _vsnwprintf(ptr long wstr ptr) msvcrt._vsnwprintf + @ cdecl -private _wcsicmp(wstr wstr) msvcrt._wcsicmp + @ cdecl -private _wcslwr(wstr) msvcrt._wcslwr +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in b/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in +index bc040e4..b3a6839 100644 +--- a/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in +@@ -8,4 +8,5 @@ CROSSFLAGS = -nostartfiles -nostdlib -nodefaultlibs \ + -Wl,-entry,_DriverEntry@8 + + C_SRCS = \ +- driver.c ++ driver.c \ ++ test.c +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.c b/dlls/ntoskrnl.exe/tests/driver.sys/driver.c +index 5756090..81f938d 100644 +--- a/dlls/ntoskrnl.exe/tests/driver.sys/driver.c ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.c +@@ -30,6 +30,9 @@ + #include "winioctl.h" + #include "ddk/wdm.h" + ++#define WINE_KERNEL ++#include "util.h" ++#include "test.h" + #include "driver.h" + + const WCHAR driver_device[] = {'\\','D','e','v','i','c','e', +@@ -37,24 +40,17 @@ const WCHAR driver_device[] = {'\\','D','e','v','i','c','e', + const WCHAR driver_link[] = {'\\','D','o','s','D','e','v','i','c','e','s', + '\\','W','i','n','e','T','e','s','t','D','r','i','v','e','r',0}; + +- +-static NTSTATUS test_basic_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) ++KERNEL_TESTCASE(PsGetCurrentProcessId) + { +- const char str[] = "Wine is not an emulator"; +- ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; +- char *buffer = irp->AssociatedIrp.SystemBuffer; +- int i; +- +- if (!buffer) +- return STATUS_ACCESS_VIOLATION; +- +- if (length < sizeof(str)-1) +- return STATUS_BUFFER_TOO_SMALL; +- +- for (i = 0; i < sizeof(str)-1; i++) +- buffer[i] = str[i]; ++ test->processid = (DWORD)PsGetCurrentProcessId(); ++ ok(test->processid, "Expected processid to be non zero\n"); ++ return STATUS_SUCCESS; ++} + +- *info = sizeof(str)-1; ++KERNEL_TESTCASE(PsGetCurrentThread) ++{ ++ PETHREAD thread = PsGetCurrentThread(); ++ todo_wine ok(thread != NULL, "Expected thread to be non-NULL\n"); + return STATUS_SUCCESS; + } + +@@ -70,16 +66,20 @@ static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) + NTSTATUS status = STATUS_NOT_SUPPORTED; + ULONG_PTR information = 0; + ++#define DECLARE_TEST(name) \ ++ case WINE_IOCTL_##name: status = RUN_TESTCASE(name, irp, stack, &information); break; ++ + switch (stack->Parameters.DeviceIoControl.IoControlCode) + { +- case IOCTL_WINETEST_BASIC_IOCTL: +- status = test_basic_ioctl(irp, stack, &information); +- break; ++ DECLARE_TEST(PsGetCurrentProcessId); ++ DECLARE_TEST(PsGetCurrentThread); + + default: + break; + } + ++#undef DECLARE_TEST ++ + irp->IoStatus.Status = status; + irp->IoStatus.Information = information; + IoCompleteRequest(irp, IO_NO_INCREMENT); +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.h b/dlls/ntoskrnl.exe/tests/driver.sys/driver.h +index 372e908..e48bbdd 100644 +--- a/dlls/ntoskrnl.exe/tests/driver.sys/driver.h ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.h +@@ -20,9 +20,19 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + ++#include "test.h" + +-/* +- * All custom IOCTLs need to have a function value >= 0x800. +- */ ++#define WINE_IOCTL_PsGetCurrentProcessId WINE_TEST_IOCTL(0) ++#define WINE_IOCTL_PsGetCurrentThread WINE_TEST_IOCTL(1) ++ ++struct test_PsGetCurrentProcessId_state ++{ ++ struct kernel_test_state __state; ++ DWORD processid; /* output */ ++}; ++ ++struct test_PsGetCurrentThread_state ++{ ++ struct kernel_test_state __state; ++}; + +-#define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/test.c b/dlls/ntoskrnl.exe/tests/driver.sys/test.c +new file mode 100644 +index 0000000..5e17c90 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/test.c +@@ -0,0 +1,195 @@ ++/* ++ * ntoskrnl.exe testing framework ++ * ++ * Copyright 2015 Michael Müller ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winbase.h" ++#include "winternl.h" ++#include "winioctl.h" ++#include "ddk/wdm.h" ++ ++#define WINE_KERNEL ++#include "util.h" ++#include "test.h" ++ ++extern int _vsnprintf(char *buffer, size_t count, const char *format, __ms_va_list argptr); ++ ++static void safe_vsnprintf(struct kernel_test_state *state, const char *msg, __ms_va_list args) ++{ ++ int remaining; ++ int length; ++ ++ if (state->output.overflow) ++ return; ++ ++ if (state->output.offset >= sizeof(state->output.debug) - 1) ++ { ++ state->output.overflow = TRUE; ++ return; ++ } ++ ++ remaining = (sizeof(state->output.debug) - 1) - state->output.offset; ++ length = _vsnprintf(&state->output.debug[state->output.offset], remaining, msg, args); ++ if (length < 0) ++ { ++ state->output.debug[state->output.offset] = 0; ++ state->output.overflow = TRUE; ++ return; ++ } ++ ++ state->output.offset += length; ++} ++ ++static void __winetest_cdecl safe_snprintf(struct kernel_test_state *state, const char *msg, ...) ++{ ++ __ms_va_list valist; ++ ++ __ms_va_start(valist, msg); ++ safe_vsnprintf(state, msg, valist); ++ __ms_va_end(valist); ++} ++ ++void winetest_set_location(struct kernel_test_state *state, const char *file, int line) ++{ ++ state->temp.file = kernel_strrchr(file, '/'); ++ if (state->temp.file == NULL) ++ state->temp.file = kernel_strrchr(file, '\\'); ++ if (state->temp.file == NULL) ++ state->temp.file = file; ++ else ++ state->temp.file++; ++ state->temp.line = line; ++} ++ ++int winetest_vok(struct kernel_test_state *state, int condition, const char *msg, __ms_va_list args) ++{ ++ if (state->temp.todo_level) ++ { ++ if (condition) ++ { ++ safe_snprintf( state, "%s:%d: Test succeeded inside todo block: ", ++ state->temp.file, state->temp.line ); ++ safe_vsnprintf(state, msg, args); ++ state->output.todo_failures++; ++ return 0; ++ } ++ else ++ { ++ if (state->input.debug_level > 0) ++ { ++ safe_snprintf( state, "%s:%d: Test marked todo: ", ++ state->temp.file, state->temp.line ); ++ safe_vsnprintf(state, msg, args); ++ } ++ state->output.todo_successes++; ++ return 1; ++ } ++ } ++ else ++ { ++ if (!condition) ++ { ++ safe_snprintf( state, "%s:%d: Test failed: ", ++ state->temp.file, state->temp.line ); ++ safe_vsnprintf(state, msg, args); ++ state->output.failures++; ++ return 0; ++ } ++ else ++ { ++ if (state->input.report_success) ++ safe_snprintf( state, "%s:%d: Test succeeded\n", ++ state->temp.file, state->temp.line); ++ state->output.successes++; ++ return 1; ++ } ++ } ++} ++ ++void __winetest_cdecl winetest_ok(struct kernel_test_state *state, int condition, const char *msg, ...) ++{ ++ __ms_va_list valist; ++ ++ __ms_va_start(valist, msg); ++ winetest_vok(state, condition, msg, valist); ++ __ms_va_end(valist); ++} ++ ++void __winetest_cdecl winetest_trace(struct kernel_test_state *state, const char *msg, ...) ++{ ++ __ms_va_list valist; ++ ++ if (state->input.debug_level > 0) ++ { ++ safe_snprintf( state, "%s:%d: ", state->temp.file, state->temp.line ); ++ __ms_va_start(valist, msg); ++ safe_vsnprintf(state, msg, valist); ++ __ms_va_end(valist); ++ } ++} ++ ++void winetest_vskip(struct kernel_test_state *state, const char *msg, __ms_va_list args) ++{ ++ safe_snprintf( state, "%s:%d: Tests skipped: ", state->temp.file, state->temp.line ); ++ safe_vsnprintf(state, msg, args); ++ state->output.skipped++; ++} ++ ++void __winetest_cdecl winetest_skip(struct kernel_test_state *state, const char *msg, ...) ++{ ++ __ms_va_list valist; ++ __ms_va_start(valist, msg); ++ winetest_vskip(state, msg, valist); ++ __ms_va_end(valist); ++} ++ ++void __winetest_cdecl winetest_win_skip(struct kernel_test_state *state, const char *msg, ...) ++{ ++ __ms_va_list valist; ++ __ms_va_start(valist, msg); ++ if (!state->input.windows) ++ winetest_vskip(state, msg, valist); ++ else ++ winetest_vok(state, 0, msg, valist); ++ __ms_va_end(valist); ++} ++ ++void winetest_start_todo(struct kernel_test_state *state, int windows) ++{ ++ if (state->input.windows == windows) ++ state->temp.todo_level++; ++ state->temp.todo_do_loop=1; ++} ++ ++int winetest_loop_todo(struct kernel_test_state *state) ++{ ++ int do_loop=state->temp.todo_do_loop; ++ state->temp.todo_do_loop=0; ++ return do_loop; ++} ++ ++void winetest_end_todo(struct kernel_test_state *state, int windows) ++{ ++ if (state->input.windows == windows) ++ state->temp.todo_level--; ++} +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/test.h b/dlls/ntoskrnl.exe/tests/driver.sys/test.h +new file mode 100644 +index 0000000..eca3b46 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/test.h +@@ -0,0 +1,197 @@ ++/* ++ * ntoskrnl.exe testing framework ++ * ++ * Copyright 2015 Michael Müller ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++#ifndef _WINE_KERNEL_TEST_ ++#define _WINE_KERNEL_TEST_ ++ ++struct kernel_test_state ++{ ++ struct ++ { ++ int debug_level; ++ int report_success; ++ BOOL windows; ++ } input; ++ struct ++ { ++ const char *file; ++ int line; ++ int todo_level; ++ int todo_do_loop; ++ } temp; ++ struct ++ { ++ LONG failures; ++ LONG successes; ++ LONG todo_failures; ++ LONG todo_successes; ++ LONG skipped; ++ ++ BOOL overflow; ++ DWORD offset; ++ char debug[4096]; ++ } output; ++}; ++ ++#define WINE_TEST_IOCTL(index) CTL_CODE(FILE_DEVICE_UNKNOWN, (index) + 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) ++ ++#ifdef WINE_KERNEL ++ ++#if defined(__x86_64__) && defined(__GNUC__) && defined(__WINE_USE_MSVCRT) ++# define __ms_va_start(list,arg) __builtin_ms_va_start(list,arg) ++# define __ms_va_end(list) __builtin_ms_va_end(list) ++#else ++# define __ms_va_start(list,arg) va_start(list,arg) ++# define __ms_va_end(list) va_end(list) ++#endif ++ ++#if defined(__x86_64__) && defined(__GNUC__) && defined(__WINE_USE_MSVCRT) ++#define __winetest_cdecl __cdecl ++#define __ms_va_list __builtin_ms_va_list ++#else ++#define __winetest_cdecl ++#define __ms_va_list va_list ++#endif ++ ++#ifdef __GNUC__ ++void __winetest_cdecl winetest_ok(struct kernel_test_state *state, int condition, const char *msg, ...) __attribute__((format (printf,3,4) )); ++void __winetest_cdecl winetest_skip(struct kernel_test_state *state, const char *msg, ...) __attribute__((format (printf,2,3))); ++void __winetest_cdecl winetest_win_skip(struct kernel_test_state *state, const char *msg, ...) __attribute__((format (printf,2,3))); ++void __winetest_cdecl winetest_trace(struct kernel_test_state *state, const char *msg, ...) __attribute__((format (printf,2,3))); ++#else ++void __winetest_cdecl winetest_ok(struct kernel_test_state *state, int condition, const char *msg, ...); ++void __winetest_cdecl winetest_skip(struct kernel_test_state *state, const char *msg, ...); ++void __winetest_cdecl winetest_win_skip(struct kernel_test_state *state, const char *msg, ...); ++void __winetest_cdecl winetest_trace(struct kernel_test_state *state, const char *msg, ...); ++#endif /* __GNUC__ */ ++ ++void winetest_set_location(struct kernel_test_state *state, const char* file, int line); ++void winetest_start_todo(struct kernel_test_state *state, int windows); ++int winetest_loop_todo(struct kernel_test_state *state); ++void winetest_end_todo(struct kernel_test_state *state, int windows); ++ ++#define ok_(file, line) (winetest_set_location(__state, file, line), 0) ? (void)0 : winetest_ok ++#define skip_(file, line) (winetest_set_location(__state, file, line), 0) ? (void)0 : winetest_skip ++#define win_skip_(file, line) (winetest_set_location(__state, file, line), 0) ? (void)0 : winetest_win_skip ++#define trace_(file, line) (winetest_set_location(__state, file, line), 0) ? (void)0 : winetest_trace ++ ++#define _ok ok_(__FILE__, __LINE__) ++#define _skip skip_(__FILE__, __LINE__) ++#define _win_skip win_skip_(__FILE__, __LINE__) ++#define _trace trace_(__FILE__, __LINE__) ++ ++/* Variadic Macros */ ++#define ok(...) _ok(__state, __VA_ARGS__) ++#define skip(...) _skip(__state, __VA_ARGS__) ++#define win_skip(...) _win_skip(__state, __VA_ARGS__) ++#define trace(...) _trace(__state, __VA_ARGS__) ++ ++#define todo(windows) for (winetest_start_todo(__state, windows); \ ++ winetest_loop_todo(__state); \ ++ winetest_end_todo(__state, windows)) ++#define todo_wine todo(0) ++ ++#define KERNEL_TESTCASE(name) \ ++ static NTSTATUS test_##name(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info, \ ++ struct test_##name##_state *test, struct kernel_test_state *__state); \ ++ \ ++ static NTSTATUS __test_##name(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) \ ++ { \ ++ ULONG input_length = stack->Parameters.DeviceIoControl.InputBufferLength; \ ++ ULONG output_length = stack->Parameters.DeviceIoControl.OutputBufferLength; \ ++ struct kernel_test_state *state = irp->AssociatedIrp.SystemBuffer; \ ++ NTSTATUS status; \ ++ \ ++ if (!state) \ ++ return STATUS_ACCESS_VIOLATION; \ ++ \ ++ if (input_length < sizeof(struct test_##name##_state) || \ ++ output_length < sizeof(struct test_##name##_state)) \ ++ return STATUS_BUFFER_TOO_SMALL; \ ++ \ ++ kernel_memset(&state->temp, 0, sizeof(state->temp)); \ ++ kernel_memset(&state->output, 0, sizeof(state->output)); \ ++ status = test_##name(irp, stack, info, irp->AssociatedIrp.SystemBuffer, state); \ ++ \ ++ kernel_memset(&state->temp, 0, sizeof(state->temp)); \ ++ *info = sizeof(struct test_##name##_state); \ ++ return status; \ ++ } \ ++ \ ++ static NTSTATUS test_##name(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info, \ ++ struct test_##name##_state *test, struct kernel_test_state *__state) ++ ++#define RUN_TESTCASE(name, irp, stack, info) \ ++ __test_##name(irp, stack, info) ++ ++#else ++ ++#include ++ ++#define wine_run_kernel_test(device, ioctl, state, size, returned) \ ++ __wine_run_kernel_test(__FILE__, __LINE__, device, ioctl, state, size, returned) ++ ++static BOOL __wine_run_kernel_test(const char* file, int line, HANDLE device, DWORD ioctl, ++ void *data, DWORD size, DWORD *returned) ++{ ++ struct kernel_test_state *state = data; ++ DWORD bytes_returned; ++ BOOL res; ++ ++ if (!returned) ++ returned = &bytes_returned; ++ ++ memset(state, 0, sizeof(*state)); ++ state->input.debug_level = winetest_get_debug(); ++ state->input.report_success = winetest_get_report_success(); ++ state->input.windows = !strcmp(winetest_platform, "windows"); ++ ++ res = DeviceIoControl(device, ioctl, data, size, data, size, returned, NULL); ++ ++ if (returned == &bytes_returned) ++ ok_(file, line)(bytes_returned == size, ++ "DeviceIoControl returned %u bytes, expected %u bytes\n", bytes_returned, size); ++ ++ if (state->output.offset >= sizeof(state->output.debug)) ++ state->output.offset = sizeof(state->output.debug) - 1; ++ ++ if (state->output.offset) ++ { ++ state->output.debug[state->output.offset] = 0; ++ fprintf(stdout, "%s", state->output.debug); ++ if (state->output.debug[state->output.offset - 1] != '\n') ++ fprintf(stdout, "\n"); ++ } ++ ++ if (state->output.overflow) ++ skip_(file, line)("Buffer was too small, kernel debug output is truncated!\n"); ++ ++ winetest_add_failures(state->output.failures); ++ winetest_add_successes(state->output.successes); ++ winetest_add_todo_failures(state->output.todo_failures); ++ winetest_add_todo_successes(state->output.todo_successes); ++ winetest_add_skipped(state->output.skipped); ++ ++ return res; ++} ++ ++#endif ++ ++#endif /* _WINE_KERNEL_TEST_ */ +diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/util.h b/dlls/ntoskrnl.exe/tests/driver.sys/util.h +new file mode 100644 +index 0000000..881a4a2 +--- /dev/null ++++ b/dlls/ntoskrnl.exe/tests/driver.sys/util.h +@@ -0,0 +1,47 @@ ++/* ++ * ntoskrnl.exe testing framework ++ * ++ * Copyright 2015 Sebastian Lackner ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ */ ++ ++#include ++ ++static inline const char* kernel_strrchr(const char *str, int character) ++{ ++ const char *ret = NULL; ++ for (; *str; str++) ++ { ++ if (*str == character) ++ ret = str; ++ } ++ return ret; ++} ++ ++static inline void* kernel_memcpy(void *destination, const void *source, size_t num) ++{ ++ const char *src = source; ++ char *dst = destination; ++ while (num--) *dst++ = *src++; ++ return destination; ++} ++ ++static inline void* kernel_memset(void *ptr, int value, size_t num) ++{ ++ char *dst = ptr; ++ while (num--) *dst++ = value; ++ return ptr; ++} +diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c +index 9b8a6a7..64e9d97 100644 +--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c ++++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c +@@ -25,6 +25,7 @@ + #include "winioctl.h" + #include "wine/test.h" + ++#include "driver.sys/test.h" + #include "driver.sys/driver.h" + + static const char driver_name[] = "WineTestDriver"; +@@ -184,29 +185,52 @@ err: + return NULL; + } + +-static void test_basic_ioctl(void) ++static void test_PsGetCurrentProcessId(void) + { +- const char str[] = "Wine is not an emulator"; +- DWORD bytes_returned; +- char buf[32]; +- HANDLE file; ++ struct test_PsGetCurrentProcessId_state state; ++ DWORD processid; ++ HANDLE device; + BOOL res; + +- file = CreateFileA(device_path, GENERIC_READ | GENERIC_WRITE, +- 0, NULL, OPEN_EXISTING, 0, NULL); +- if (file == INVALID_HANDLE_VALUE) ++ trace("Running tests for PsGetCurrentProcessId\n"); ++ ++ device = CreateFileA(device_path, GENERIC_READ | GENERIC_WRITE, ++ 0, NULL, OPEN_EXISTING, 0, NULL); ++ if (device == INVALID_HANDLE_VALUE) + { + ok(0, "Connecting to driver failed with %x\n", GetLastError()); + return; + } + +- res = DeviceIoControl(file, IOCTL_WINETEST_BASIC_IOCTL, NULL, 0, buf, +- sizeof(buf), &bytes_returned, NULL); ++ res = wine_run_kernel_test(device, WINE_IOCTL_PsGetCurrentProcessId, &state, sizeof(state), NULL); + ok(res, "DeviceIoControl failed with %x\n", GetLastError()); +- ok(bytes_returned == sizeof(str)-1, "Unexpected number of bytes\n"); +- ok(!memcmp(buf, str, sizeof(str)-1), "Unexpected response data\n"); + +- CloseHandle(file); ++ processid = GetCurrentProcessId(); ++ ok(state.processid == processid, "Expected processid %u, got %u\n", processid, state.processid); ++ ++ CloseHandle(device); ++} ++ ++static void test_PsGetCurrentThread(void) ++{ ++ struct test_PsGetCurrentThread_state state; ++ HANDLE device; ++ BOOL res; ++ ++ trace("Running tests for PsGetCurrentThread\n"); ++ ++ device = CreateFileA(device_path, GENERIC_READ | GENERIC_WRITE, ++ 0, NULL, OPEN_EXISTING, 0, NULL); ++ if (device == INVALID_HANDLE_VALUE) ++ { ++ ok(0, "Connecting to driver failed with %x\n", GetLastError()); ++ return; ++ } ++ ++ res = wine_run_kernel_test(device, WINE_IOCTL_PsGetCurrentThread, &state, sizeof(state), NULL); ++ ok(res, "DeviceIoControl failed with %x\n", GetLastError()); ++ ++ CloseHandle(device); + } + + START_TEST(ntoskrnl) +@@ -220,7 +244,8 @@ START_TEST(ntoskrnl) + return; + } + +- test_basic_ioctl(); ++ test_PsGetCurrentProcessId(); ++ test_PsGetCurrentThread(); + + unload_driver(service, filename); + } +diff --git a/include/wine/test.h b/include/wine/test.h +index f8b608f..b7ef81a 100644 +--- a/include/wine/test.h ++++ b/include/wine/test.h +@@ -61,7 +61,13 @@ extern int winetest_loop_todo(void); + extern void winetest_end_todo( const char* platform ); + extern int winetest_get_mainargs( char*** pargv ); + extern LONG winetest_get_failures(void); ++extern int winetest_get_report_success(void); ++extern int winetest_get_debug(void); + extern void winetest_add_failures( LONG new_failures ); ++extern void winetest_add_successes( LONG new_successes ); ++extern void winetest_add_todo_failures( LONG new_todo_failures ); ++extern void winetest_add_todo_successes( LONG new_todo_successes ); ++extern void winetest_add_skipped( LONG new_skipped ); + extern void winetest_wait_child_process( HANDLE process ); + + extern const char *wine_dbgstr_wn( const WCHAR *str, int n ); +@@ -436,10 +442,39 @@ LONG winetest_get_failures(void) + return failures; + } + ++int winetest_get_report_success(void) ++{ ++ return report_success; ++} ++ ++int winetest_get_debug(void) ++{ ++ return winetest_debug; ++} ++ + void winetest_add_failures( LONG new_failures ) + { +- while (new_failures-- > 0) +- InterlockedIncrement( &failures ); ++ InterlockedExchangeAdd( &failures, new_failures ); ++} ++ ++void winetest_add_successes( LONG new_successes ) ++{ ++ InterlockedExchangeAdd( &successes, new_successes ); ++} ++ ++void winetest_add_todo_failures( LONG new_todo_failures ) ++{ ++ InterlockedExchangeAdd( &todo_failures, new_todo_failures ); ++} ++ ++void winetest_add_todo_successes( LONG new_todo_successes ) ++{ ++ InterlockedExchangeAdd( &todo_successes, new_todo_successes ); ++} ++ ++void winetest_add_skipped( LONG new_skipped ) ++{ ++ InterlockedExchangeAdd( &skipped, new_skipped ); + } + + void winetest_wait_child_process( HANDLE process ) +-- +2.2.1 + diff --git a/patches/ntoskrnl-DriverTest/definition b/patches/ntoskrnl-DriverTest/definition new file mode 100644 index 00000000..01e3ae96 --- /dev/null +++ b/patches/ntoskrnl-DriverTest/definition @@ -0,0 +1 @@ +Fixes: Implement ntoskrnl driver testing framework. diff --git a/patches/nvapi-Stub_DLL/0001-nvapi-First-implementation.patch b/patches/nvapi-Stub_DLL/0001-nvapi-First-implementation.patch index 25be2a94..4e20e09e 100644 --- a/patches/nvapi-Stub_DLL/0001-nvapi-First-implementation.patch +++ b/patches/nvapi-Stub_DLL/0001-nvapi-First-implementation.patch @@ -41,8 +41,7 @@ index 73e67bb..f76fa4d 100644 dnl Disable winetest too if tests are disabled enable_winetest=${enable_winetest:-$enable_tests} -@@ -3125,6 +3131,9 @@ WINE_CONFIG_TEST(dlls/ntdsapi/tests) - WINE_CONFIG_DLL(ntoskrnl.exe,,[implib]) +@@ -3128,4 +3134,7 @@ WINE_CONFIG_DLL(ntoskrnl.exe,,[implib]) WINE_CONFIG_DLL(ntprint) WINE_CONFIG_TEST(dlls/ntprint/tests) +WINE_CONFIG_DLL(nvapi,enable_win32) @@ -50,7 +49,6 @@ index 73e67bb..f76fa4d 100644 +WINE_CONFIG_DLL(nvapi64,enable_win64) WINE_CONFIG_DLL(nvcuda) WINE_CONFIG_TEST(dlls/nvcuda/tests) - WINE_CONFIG_DLL(objsel,,[clean]) diff --git a/dlls/nvapi/Makefile.in b/dlls/nvapi/Makefile.in new file mode 100644 index 0000000..606177f diff --git a/patches/nvcuda-CUDA_Support/0002-nvcuda-Add-stub-dll.patch b/patches/nvcuda-CUDA_Support/0002-nvcuda-Add-stub-dll.patch index 0bdec79c..4e98bc23 100644 --- a/patches/nvcuda-CUDA_Support/0002-nvcuda-Add-stub-dll.patch +++ b/patches/nvcuda-CUDA_Support/0002-nvcuda-Add-stub-dll.patch @@ -19,14 +19,12 @@ diff --git a/configure.ac b/configure.ac index b67a7e7..6e3108b 100644 --- a/configure.ac +++ b/configure.ac -@@ -3125,6 +3125,7 @@ WINE_CONFIG_TEST(dlls/ntdsapi/tests) - WINE_CONFIG_DLL(ntoskrnl.exe,,[implib]) +@@ -3128,4 +3128,5 @@ WINE_CONFIG_DLL(ntoskrnl.exe,,[implib]) WINE_CONFIG_DLL(ntprint) WINE_CONFIG_TEST(dlls/ntprint/tests) +WINE_CONFIG_DLL(nvcuda) WINE_CONFIG_DLL(objsel,,[clean]) WINE_CONFIG_DLL(odbc32,,[implib]) - WINE_CONFIG_DLL(odbccp32,,[implib]) diff --git a/dlls/nvcuda/Makefile.in b/dlls/nvcuda/Makefile.in new file mode 100644 index 0000000..4b33278 diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 104f0878..7548ab88 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -127,6 +127,7 @@ patch_enable_all () enable_ntdll_User_Shared_Data="$1" enable_ntdll_WRITECOPY="$1" enable_ntdll_WinSqm="$1" + enable_ntoskrnl_DriverTest="$1" enable_ntoskrnl_Emulator="$1" enable_ntoskrnl_KeWaitForMultipleObjects="$1" enable_nvapi_Stub_DLL="$1" @@ -408,6 +409,9 @@ patch_enable () ntdll-WinSqm) enable_ntdll_WinSqm="$2" ;; + ntoskrnl-DriverTest) + enable_ntoskrnl_DriverTest="$2" + ;; ntoskrnl-Emulator) enable_ntoskrnl_Emulator="$2" ;; @@ -2177,6 +2181,25 @@ if test "$enable_ntdll_WinSqm" -eq 1; then ) >> "$patchlist" fi +# Patchset ntoskrnl-DriverTest +# | +# | Modified files: +# | * aclocal.m4, configure.ac, dlls/ntoskrnl.exe/ntoskrnl.exe.spec, dlls/ntoskrnl.exe/tests/Makefile.in, +# | dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in, dlls/ntoskrnl.exe/tests/driver.sys/driver.c, +# | dlls/ntoskrnl.exe/tests/driver.sys/driver.h, dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec, +# | dlls/ntoskrnl.exe/tests/driver.sys/test.c, dlls/ntoskrnl.exe/tests/driver.sys/test.h, +# | dlls/ntoskrnl.exe/tests/driver.sys/util.h, dlls/ntoskrnl.exe/tests/ntoskrnl.c, include/wine/test.h, +# | tools/make_makefiles, tools/makedep.c +# | +if test "$enable_ntoskrnl_DriverTest" -eq 1; then + patch_apply ntoskrnl-DriverTest/0001-ntoskrnl.exe-tests-Add-initial-driver-testing-framew.patch + patch_apply ntoskrnl-DriverTest/0002-ntoskrnl.exe-tests-Add-kernel-compliant-test-functio.patch + ( + echo '+ { "Sebastian Lackner", "ntoskrnl.exe/tests: Add initial driver testing framework and corrsponding changes to Makefile system.", 2 },'; + echo '+ { "Michael Müller", "ntoskrnl.exe/tests: Add kernel compliant test functions.", 1 },'; + ) >> "$patchlist" +fi + # Patchset ntoskrnl-Emulator # | # | This patchset fixes the following Wine bugs: