[PATCH v4 2/2] server: Add USD support with timestamp updates.

Rémi Bernon rbernon at codeweavers.com
Thu Apr 30 11:58:59 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.

The tests todo_wine cannot be completely removed as the read may have
missed a server timestamp update whereas the time functions are still
directly calling native clocks to get their values.

It may then be possible to implement time using USD but that would
reduce the clocks accuracy down to the server update frequency.

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       | 25 ++++++++++++--
 dlls/ntdll/tests/time.c   |  9 +++--
 dlls/ntdll/thread.c       |  2 ++
 dlls/ntoskrnl.exe/instr.c | 12 -------
 server/file.h             |  2 ++
 server/mapping.c          | 69 +++++++++++++++++++++++++++++++++++++++
 server/process.c          |  8 +++++
 server/protocol.def       |  1 +
 9 files changed, 112 insertions(+), 17 deletions(-)

diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 7dca506aad42..107ebaab16dd 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -209,6 +209,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 size_t user_shared_data_size 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 c0dd0f35fc3d..4bf681af201a 100644
--- a/dlls/ntdll/server.c
+++ b/dlls/ntdll/server.c
@@ -81,6 +81,7 @@
 #include "wine/server.h"
 #include "wine/debug.h"
 #include "ntdll_misc.h"
+#include "ddk/wdm.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(server);
 
@@ -1517,8 +1518,15 @@ void server_init_process_done(void)
     PEB *peb = NtCurrentTeb()->Peb;
     IMAGE_NT_HEADERS *nt = RtlImageNtHeader( peb->ImageBaseAddress );
     void *entry = (char *)peb->ImageBaseAddress + nt->OptionalHeader.AddressOfEntryPoint;
+    obj_handle_t usd_handle;
     NTSTATUS status;
-    int suspend;
+    int suspend, usd_fd = -1;
+    sigset_t old_set;
+    SIZE_T size = user_shared_data_size;
+    void *addr = user_shared_data;
+    ULONG old_prot;
+
+    NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READONLY, &old_prot );
 
 #ifdef __APPLE__
     send_server_task_port();
@@ -1533,6 +1541,7 @@ void server_init_process_done(void)
     signal_init_process();
 
     /* Signal the parent process to continue */
+    pthread_sigmask( SIG_BLOCK, &server_block_set, &old_set );
     SERVER_START_REQ( init_process_done )
     {
         req->module   = wine_server_client_ptr( peb->ImageBaseAddress );
@@ -1541,10 +1550,22 @@ void server_init_process_done(void)
 #endif
         req->entry    = wine_server_client_ptr( entry );
         req->gui      = (nt->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_CUI);
-        status = wine_server_call( req );
+        wine_server_add_data( req, user_shared_data, sizeof(*user_shared_data) );
+        status = server_call_unlocked( req );
         suspend = reply->suspend;
     }
     SERVER_END_REQ;
+    if (!status) usd_fd = receive_fd( &usd_handle );
+    pthread_sigmask( SIG_SETMASK, &old_set, NULL );
+
+    if (usd_fd != -1)
+    {
+        munmap( user_shared_data, user_shared_data_size );
+        if (user_shared_data != mmap( user_shared_data, user_shared_data_size,
+                                      PROT_READ, MAP_SHARED | MAP_FIXED, usd_fd, 0 ))
+            fatal_error( "failed to remap the process user shared data\n" );
+        close( usd_fd );
+    }
 
     assert( !status );
     signal_start_process( entry, suspend );
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c
index fe71d4458c0f..7f81c20f21ec 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 e27b3a0c6dd3..ef3a94cf4828 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;
+size_t user_shared_data_size = 0;
 static const WCHAR default_windirW[] = {'C',':','\\','w','i','n','d','o','w','s',0};
 
 void (WINAPI *kernel32_start_process)(LPTHREAD_START_ROUTINE,void*) = NULL;
@@ -244,6 +245,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 */
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..d577aaed3cfa 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);
 
+int get_user_shared_data_fd( const void *usd_init, data_size_t usd_size );
+
 /* 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..d73d8de9c283 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,74 @@ int get_page_size(void)
     return page_mask + 1;
 }
 
+static int kusd_fd;
+static KSHARED_USER_DATA *kusd;
+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 );
+
+#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)))
+    __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);
+#else
+    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;
+    ptr->TickCountLowDeprecated = tick_count_low;
+#endif
+}
+
+int get_user_shared_data_fd( const void *usd_init, data_size_t usd_size )
+{
+    /* keep it the same as user_shared_data_size in ntdll */
+    size_t size = 0x10000;
+
+    if (sizeof(*kusd) != usd_size) return -1;
+    if (kusd) return kusd_fd;
+
+    if ((kusd_fd = create_temp_file( size )) == -1)
+        return -1;
+
+    if ((kusd = mmap( NULL, size, PROT_WRITE, MAP_SHARED, kusd_fd, 0 )) == MAP_FAILED)
+    {
+        close( kusd_fd );
+        return -1;
+    }
+
+    memcpy( kusd, usd_init, usd_size );
+
+    kusd_set_current_time( NULL );
+    return kusd_fd;
+}
+
 /* create a file mapping */
 DECL_HANDLER(create_mapping)
 {
diff --git a/server/process.c b/server/process.c
index 73984f363f59..80f9c5c6f97e 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1356,6 +1356,7 @@ DECL_HANDLER(init_process_done)
 {
     struct process_dll *dll;
     struct process *process = current->process;
+    int usd_fd;
 
     if (is_process_init_done(process))
     {
@@ -1367,6 +1368,13 @@ DECL_HANDLER(init_process_done)
         set_error( STATUS_DLL_NOT_FOUND );
         return;
     }
+    if ((usd_fd = get_user_shared_data_fd( get_req_data(), get_req_data_size() )) == -1)
+    {
+        set_error( STATUS_NO_MEMORY );
+        return;
+    }
+
+    send_client_fd( process, usd_fd, -1 );
 
     /* 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..b1fe198ad043 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 */
+    VARARG(usd,bytes);         /* USD initialization data */
 @REPLY
     int          suspend;      /* is process suspended? */
 @END
-- 
2.26.1




More information about the wine-devel mailing list