[PATCH v6 3/4] server: Add USD support with timestamp updates.

Rémi Bernon rbernon at codeweavers.com
Mon May 11 06:12:48 CDT 2020


The USD page is created when the first process (wineboot.exe) completes
its creation, using its provided user_shared_data for initialization.

The server maps the page write-only and the clients map it read-only,
then the server updates the timestamps every 16 ms.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=29168
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/ntdll/ntdll_misc.h   |  1 +
 dlls/ntdll/server.c       |  4 +++
 dlls/ntdll/tests/info.c   |  1 -
 dlls/ntdll/tests/time.c   |  9 ++++--
 dlls/ntdll/thread.c       | 60 +++++++++++++++++++++++++++++++++++++
 dlls/ntoskrnl.exe/instr.c | 12 --------
 server/file.h             |  2 ++
 server/mapping.c          | 63 +++++++++++++++++++++++++++++++++++++++
 server/process.c          |  8 +++++
 server/protocol.def       |  1 +
 10 files changed, 145 insertions(+), 16 deletions(-)

diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 13771d230a88..4c8acab15dbf 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -221,6 +221,7 @@ extern void virtual_set_large_address_space(void) DECLSPEC_HIDDEN;
 extern void virtual_fill_image_information( const pe_image_info_t *pe_info,
                                             SECTION_IMAGE_INFORMATION *info ) DECLSPEC_HIDDEN;
 extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN;
+extern HANDLE user_shared_data_init_done(void) DECLSPEC_HIDDEN;
 
 /* completion */
 extern NTSTATUS NTDLL_AddCompletion( HANDLE hFile, ULONG_PTR CompletionValue,
diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c
index 41e6ea891c56..96f8e8f2a960 100644
--- a/dlls/ntdll/server.c
+++ b/dlls/ntdll/server.c
@@ -91,6 +91,7 @@
 #include "wine/server.h"
 #include "wine/debug.h"
 #include "ntdll_misc.h"
+#include "ddk/wdm.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(server);
 
@@ -1863,6 +1864,7 @@ void server_init_process_done(void)
     IMAGE_NT_HEADERS *nt = RtlImageNtHeader( peb->ImageBaseAddress );
     void *entry = (char *)peb->ImageBaseAddress + nt->OptionalHeader.AddressOfEntryPoint;
     NTSTATUS status;
+    HANDLE usd_handle = user_shared_data_init_done();
     int suspend;
 
 #ifdef __APPLE__
@@ -1886,10 +1888,12 @@ void server_init_process_done(void)
 #endif
         req->entry    = wine_server_client_ptr( entry );
         req->gui      = (nt->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_CUI);
+        req->usd_handle = wine_server_obj_handle( usd_handle );
         status = wine_server_call( req );
         suspend = reply->suspend;
     }
     SERVER_END_REQ;
+    NtClose( usd_handle );
 
     assert( !status );
     signal_start_process( entry, suspend );
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c
index fbc2e4e9d585..757ef2691004 100644
--- a/dlls/ntdll/tests/info.c
+++ b/dlls/ntdll/tests/info.c
@@ -2264,7 +2264,6 @@ static void test_queryvirtualmemory(void)
     todo_wine
     ok(mbi.AllocationProtect == PAGE_READONLY, "mbi.AllocationProtect is 0x%x, expected 0x%x\n", mbi.AllocationProtect, PAGE_READONLY);
     ok(mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%X\n", mbi.State, MEM_COMMIT);
-    todo_wine
     ok(mbi.Protect == PAGE_READONLY, "mbi.Protect is 0x%x\n", mbi.Protect);
     ok(mbi.Type == MEM_PRIVATE, "mbi.Type is 0x%x, expected 0x%x\n", mbi.Type, MEM_PRIVATE);
     todo_wine
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c
index d11093a243da..f50d4b4d7019 100644
--- a/dlls/ntdll/tests/time.c
+++ b/dlls/ntdll/tests/time.c
@@ -185,9 +185,10 @@ static void test_user_shared_data_time(void)
         t3 = GetTickCount();
     } while(t3 < t1 && i++ < 1); /* allow for wrap, but only once */
 
+    /* FIXME: not always in order, but should be close */
+    todo_wine_if(t1 > t2 && t1 - t2 < 50)
     ok(t1 <= t2, "USD TickCount / GetTickCount are out of order: %s %s\n",
        wine_dbgstr_longlong(t1), wine_dbgstr_longlong(t2));
-    todo_wine
     ok(t2 <= t3, "USD TickCount / GetTickCount are out of order: %s %s\n",
        wine_dbgstr_longlong(t2), wine_dbgstr_longlong(t3));
 
@@ -202,7 +203,8 @@ static void test_user_shared_data_time(void)
         t3 = system_time.QuadPart;
     } while(t3 < t1 && i++ < 1); /* allow for wrap, but only once */
 
-    todo_wine
+    /* FIXME: not always in order, but should be close */
+    todo_wine_if(t1 > t2 && t1 - t2 < 50 * TICKSPERMSEC)
     ok(t1 <= t2, "USD SystemTime / NtQuerySystemTime are out of order %s %s\n",
        wine_dbgstr_longlong(t1), wine_dbgstr_longlong(t2));
     ok(t2 <= t3, "USD SystemTime / NtQuerySystemTime are out of order %s %s\n",
@@ -220,7 +222,8 @@ static void test_user_shared_data_time(void)
             pRtlQueryUnbiasedInterruptTime(&t3);
         } while(t3 < t1 && i++ < 1); /* allow for wrap, but only once */
 
-        todo_wine
+        /* FIXME: not always in order, but should be close */
+        todo_wine_if(t1 > t2 && t1 - t2 < 50 * TICKSPERMSEC)
         ok(t1 <= t2, "USD InterruptTime / RtlQueryUnbiasedInterruptTime are out of order %s %s\n",
            wine_dbgstr_longlong(t1), wine_dbgstr_longlong(t2));
         ok(t2 <= t3 || broken(t2 == t3 + 82410089070) /* w864 has some weird offset on testbot */,
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index b25f87e43755..78fe4e4fdf86 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -56,6 +56,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(thread);
 #endif
 
 struct _KUSER_SHARED_DATA *user_shared_data = NULL;
+static size_t user_shared_data_size;
 static const WCHAR default_windirW[] = {'C',':','\\','w','i','n','d','o','w','s',0};
 
 void (WINAPI *kernel32_start_process)(LPTHREAD_START_ROUTINE,void*) = NULL;
@@ -212,6 +213,64 @@ static void set_process_name( int argc, char *argv[] )
 #endif  /* HAVE_PRCTL */
 }
 
+HANDLE user_shared_data_init_done(void)
+{
+    static const WCHAR wine_usdW[] = {'\\','K','e','r','n','e','l','O','b','j','e','c','t','s',
+                                      '\\','_','_','w','i','n','e','_','u','s','e','r','_','s','h','a','r','e','d','_','d','a','t','a',0};
+    OBJECT_ATTRIBUTES attr = {sizeof(attr)};
+    UNICODE_STRING wine_usd_str;
+    LARGE_INTEGER section_size;
+    NTSTATUS status;
+    HANDLE section;
+    SIZE_T size;
+    void *addr;
+    ULONG old_prot;
+    int res, fd, needs_close;
+
+    section_size.HighPart = 0;
+    section_size.LowPart = user_shared_data_size;
+
+    RtlInitUnicodeString( &wine_usd_str, wine_usdW );
+    InitializeObjectAttributes( &attr, &wine_usd_str, OBJ_OPENIF, NULL, NULL );
+    if ((status = NtCreateSection( &section, SECTION_ALL_ACCESS, &attr,
+                                   &section_size, PAGE_READWRITE, SEC_COMMIT, NULL )) &&
+        status != STATUS_OBJECT_NAME_EXISTS)
+    {
+        MESSAGE( "wine: failed to create or open the USD section: %08x\n", status );
+        exit(1);
+    }
+
+    if (status != STATUS_OBJECT_NAME_EXISTS)
+    {
+        addr = NULL;
+        size = user_shared_data_size;
+
+        if ((status = NtMapViewOfSection( section, NtCurrentProcess(), &addr, 0, 0, 0,
+                                          &size, 0, 0, PAGE_READWRITE )))
+        {
+            MESSAGE( "wine: failed to initialize the USD section: %08x\n", status );
+            exit(1);
+        }
+
+        memcpy( addr, user_shared_data, user_shared_data_size );
+        NtUnmapViewOfSection( NtCurrentProcess(), addr );
+    }
+
+    addr = user_shared_data;
+    size = user_shared_data_size;
+    NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READONLY, &old_prot );
+
+    if ((res = server_get_unix_fd( section, 0, &fd, &needs_close, NULL, NULL )) ||
+        (user_shared_data != mmap( user_shared_data, user_shared_data_size,
+                                   PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0 )))
+    {
+        MESSAGE( "wine: failed to remap the process USD: %d\n", res );
+        exit(1);
+    }
+    if (needs_close) close( fd );
+
+    return section;
+}
 
 /***********************************************************************
  *           thread_init
@@ -244,6 +303,7 @@ TEB *thread_init(void)
         exit(1);
     }
     user_shared_data = addr;
+    user_shared_data_size = size;
     memcpy( user_shared_data->NtSystemRoot, default_windirW, sizeof(default_windirW) );
 
     /* allocate and initialize the PEB and initial TEB */
diff --git a/dlls/ntoskrnl.exe/instr.c b/dlls/ntoskrnl.exe/instr.c
index 77803f07d725..0973b3a80a07 100644
--- a/dlls/ntoskrnl.exe/instr.c
+++ b/dlls/ntoskrnl.exe/instr.c
@@ -593,15 +593,6 @@ static void fake_syscall_function(void)
 }
 
 
-static void update_shared_data(void)
-{
-    struct _KUSER_SHARED_DATA *shared_data  = (struct _KUSER_SHARED_DATA *)wine_user_shared_data;
-
-    shared_data->u.TickCountQuad = GetTickCount64();
-    shared_data->u.TickCount.High2Time = shared_data->u.TickCount.High1Time;
-}
-
-
 /***********************************************************************
  *           emulate_instruction
  *
@@ -802,7 +793,6 @@ static DWORD emulate_instruction( EXCEPTION_RECORD *rec, CONTEXT *context )
             if (offset <= sizeof(KSHARED_USER_DATA) - data_size)
             {
                 ULONGLONG temp = 0;
-                update_shared_data();
                 memcpy( &temp, wine_user_shared_data + offset, data_size );
                 store_reg_word( context, instr[2], (BYTE *)&temp, long_op, rex );
                 context->Rip += prefixlen + len + 2;
@@ -823,7 +813,6 @@ static DWORD emulate_instruction( EXCEPTION_RECORD *rec, CONTEXT *context )
 
         if (offset <= sizeof(KSHARED_USER_DATA) - data_size)
         {
-            update_shared_data();
             switch (*instr)
             {
             case 0x8a: store_reg_byte( context, instr[1], wine_user_shared_data + offset, rex ); break;
@@ -845,7 +834,6 @@ static DWORD emulate_instruction( EXCEPTION_RECORD *rec, CONTEXT *context )
 
         if (offset <= sizeof(KSHARED_USER_DATA) - data_size)
         {
-            update_shared_data();
             memcpy( &context->Rax, wine_user_shared_data + offset, data_size );
             context->Rip += prefixlen + len + 1;
             return ExceptionContinueExecution;
diff --git a/server/file.h b/server/file.h
index 7395814dadd2..4c454de4d411 100644
--- a/server/file.h
+++ b/server/file.h
@@ -173,6 +173,8 @@ extern struct file *get_mapping_file( struct process *process, client_ptr_t base
 extern void free_mapped_views( struct process *process );
 extern int get_page_size(void);
 
+extern void init_kusd_mapping( struct mapping *mapping );
+
 /* device functions */
 
 extern struct object *create_named_pipe_device( struct object *root, const struct unicode_str *name );
diff --git a/server/mapping.c b/server/mapping.c
index 6990a1913d76..6970c86ffc1a 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -35,6 +35,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winternl.h"
+#include "ddk/wdm.h"
 
 #include "file.h"
 #include "handle.h"
@@ -943,6 +944,68 @@ int get_page_size(void)
     return page_mask + 1;
 }
 
+static KSHARED_USER_DATA *kusd = MAP_FAILED;
+static const timeout_t kusd_timeout = 16 * -TICKS_PER_SEC / 1000;
+
+static void kusd_set_current_time( void *private )
+{
+    ULONG system_time_high = current_time >> 32;
+    ULONG system_time_low = current_time & 0xffffffff;
+    ULONG interrupt_time_high = monotonic_time >> 32;
+    ULONG interrupt_time_low = monotonic_time & 0xffffffff;
+    ULONG tick_count_high = (monotonic_time * 1000 / TICKS_PER_SEC) >> 32;
+    ULONG tick_count_low = (monotonic_time * 1000 / TICKS_PER_SEC) & 0xffffffff;
+    KSHARED_USER_DATA *ptr = kusd;
+
+    add_timeout_user( kusd_timeout, kusd_set_current_time, NULL );
+
+    /* on X86 there should be total store order guarantees, so volatile is enough
+     * to ensure the stores aren't reordered by the compiler, and then they will
+     * always be seen in-order from other CPUs. On other archs, we need atomic
+     * intrinsics to guarantee that. */
+#if defined(__i386__) || defined(__x86_64__)
+    ptr->SystemTime.High2Time = system_time_high;
+    ptr->SystemTime.LowPart = system_time_low;
+    ptr->SystemTime.High1Time = system_time_high;
+
+    ptr->InterruptTime.High2Time = interrupt_time_high;
+    ptr->InterruptTime.LowPart = interrupt_time_low;
+    ptr->InterruptTime.High1Time = interrupt_time_high;
+
+    ptr->TickCount.High2Time = tick_count_high;
+    ptr->TickCount.LowPart = tick_count_low;
+    ptr->TickCount.High1Time = tick_count_high;
+    *(volatile ULONG *)&ptr->TickCountLowDeprecated = tick_count_low;
+#else
+    __atomic_store_n(&ptr->SystemTime.High2Time, system_time_high, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&ptr->SystemTime.LowPart, system_time_low, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&ptr->SystemTime.High1Time, system_time_high, __ATOMIC_SEQ_CST);
+
+    __atomic_store_n(&ptr->InterruptTime.High2Time, interrupt_time_high, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&ptr->InterruptTime.LowPart, interrupt_time_low, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&ptr->InterruptTime.High1Time, interrupt_time_high, __ATOMIC_SEQ_CST);
+
+    __atomic_store_n(&ptr->TickCount.High2Time, tick_count_high, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&ptr->TickCount.LowPart, tick_count_low, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&ptr->TickCount.High1Time, tick_count_high, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&ptr->TickCountLowDeprecated, tick_count_low, __ATOMIC_SEQ_CST);
+#endif
+}
+
+void init_kusd_mapping( struct mapping *mapping )
+{
+    if (kusd != MAP_FAILED) return;
+
+    grab_object( mapping );
+    make_object_static( &mapping->obj );
+
+    if ((kusd = mmap( NULL, mapping->size, PROT_WRITE, MAP_SHARED,
+                      get_unix_fd( mapping->fd ), 0 )) == MAP_FAILED)
+        set_error( STATUS_NO_MEMORY );
+    else
+        kusd_set_current_time( NULL );
+}
+
 /* create a file mapping */
 DECL_HANDLER(create_mapping)
 {
diff --git a/server/process.c b/server/process.c
index 211207ed03b6..6722addb0915 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1361,6 +1361,7 @@ DECL_HANDLER(init_process_done)
 {
     struct process_dll *dll;
     struct process *process = current->process;
+    struct mapping *mapping;
 
     if (is_process_init_done(process))
     {
@@ -1372,6 +1373,13 @@ DECL_HANDLER(init_process_done)
         set_error( STATUS_DLL_NOT_FOUND );
         return;
     }
+    if (!(mapping = get_mapping_obj( current->process, req->usd_handle, SECTION_MAP_WRITE )))
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+    init_kusd_mapping( mapping );
+    release_object( mapping );
 
     /* main exe is the first in the dll list */
     list_remove( &dll->entry );
diff --git a/server/protocol.def b/server/protocol.def
index 06a29b153ea0..2fb8e2ec80bd 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -854,6 +854,7 @@ struct rawinput_device
     mod_handle_t module;       /* main module base address */
     client_ptr_t ldt_copy;     /* address of LDT copy (in thread address space) */
     client_ptr_t entry;        /* process entry point */
+    obj_handle_t usd_handle;   /* USD mapping handle */
 @REPLY
     int          suspend;      /* is process suspended? */
 @END
-- 
2.26.1




More information about the wine-devel mailing list