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

Rémi Bernon rbernon at codeweavers.com
Tue Apr 28 10:11:41 CDT 2020


Pages are created on demand, depending on the initial values the process
requests. These values usually include NT version information, processor
features and number of physical pages.

The server maps the pages write-only and the clients map them read-only,
then the server updates the timestamps on every page using a 16 ms
timeout.

Note that it is hard to split the timestamp updates to a separate patch
as it would require to map the pages RW on the client side to keep the
ntoskrnl updates working, and potentially have some issues with RW pages
being shared across clients.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---

Note that we could also do one page per process if simpler is better,
but that would also increase the work to do on every update.

We could also count the number of users on each page and remove the page
once no process uses it anymore. But as the number of unique pages
should stay low, no cleanup is currently done and memory will be freed
on server exit.

 dlls/ntdll/server.c       | 29 ++++++++++++++-
 dlls/ntdll/tests/time.c   |  3 +-
 dlls/ntoskrnl.exe/instr.c | 12 ------
 server/file.h             |  2 +
 server/mapping.c          | 77 +++++++++++++++++++++++++++++++++++++++
 server/process.c          |  2 +
 server/protocol.def       | 12 ++++++
 server/trace.c            | 22 +++++++++++
 8 files changed, 144 insertions(+), 15 deletions(-)

diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c
index c0dd0f35fc3d..d0483c855e4a 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,18 @@ 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;
+    usd_init_t usd_init;
     NTSTATUS status;
-    int suspend;
+    int suspend, fd, i;
+    unsigned int usd_size;
+
+    usd_init.number_of_physical_pages = user_shared_data->NumberOfPhysicalPages;
+    usd_init.nt_major_version = user_shared_data->NtMajorVersion;
+    usd_init.nt_minor_version = user_shared_data->NtMinorVersion;
+    usd_init.nt_product_type = user_shared_data->NtProductType;
+    for (i = 0; i < ARRAY_SIZE(usd_init.processor_features); ++i)
+        usd_init.processor_features[i] = user_shared_data->ProcessorFeatures[i];
 
 #ifdef __APPLE__
     send_server_task_port();
@@ -1541,11 +1552,27 @@ void server_init_process_done(void)
 #endif
         req->entry    = wine_server_client_ptr( entry );
         req->gui      = (nt->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_CUI);
+        wine_server_add_data( req, &usd_init, sizeof(usd_init) );
         status = wine_server_call( req );
         suspend = reply->suspend;
+        usd_size = reply->usd_size;
     }
     SERVER_END_REQ;
 
+    if ((fd = receive_fd( &usd_handle )) != -1)
+    {
+        void *addr = user_shared_data;
+        SIZE_T size = usd_size;
+        ULONG old_prot;
+        NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READONLY, &old_prot );
+
+        munmap( user_shared_data, usd_size );
+        if (user_shared_data != mmap( user_shared_data, usd_size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0 ))
+            fatal_error( "failed to remap the process user shared data\n" );
+
+        close( fd );
+    }
+
     assert( !status );
     signal_start_process( entry, suspend );
 }
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c
index cc6e9f226e63..6e5d599cb62f 100644
--- a/dlls/ntdll/tests/time.c
+++ b/dlls/ntdll/tests/time.c
@@ -190,13 +190,12 @@ static void test_NtGetTickCount(void)
             diff = GetTickCount() - usd_time;
         else
             diff = pNtGetTickCount() - usd_time;
-        todo_wine
         ok(diff < 100, "NtGetTickCount - USD->TickCount too high, expected < 100 got %d\n", diff);
 
         usd_time = read_ksystem_time(&user_shared_data->SystemTime);
         NtQuerySystemTime(&system_time);
         diff = system_time.QuadPart - usd_time;
-        todo_wine
+        todo_wine_if(diff >= 100)
         ok(diff < 100, "NtQuerySystemTime - USD->SystemTime too high, expected < 100 got %d\n", diff);
 
         Sleep(50);
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..96c552bab197 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 usd_init_t *init, unsigned int *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..aae3d0cda972 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,82 @@ int get_page_size(void)
     return page_mask + 1;
 }
 
+struct kusd_mapping
+{
+    struct list entry;
+    int fd;
+    usd_init_t init;
+    KSHARED_USER_DATA *data;
+};
+
+static const timeout_t kusd_timeout = 16 * -TICKS_PER_SEC / 1000;
+static struct list kusd_mappings = LIST_INIT(kusd_mappings);
+
+static void kusd_mapping_set_current_time( struct kusd_mapping *kusd )
+{
+    kusd->data->SystemTime.High2Time = (current_time >> 32);
+    kusd->data->SystemTime.LowPart   = (current_time & 0xffffffff);
+    kusd->data->SystemTime.High1Time = (current_time >> 32);
+
+    kusd->data->InterruptTime.High2Time = (monotonic_time >> 32);
+    kusd->data->InterruptTime.LowPart   = (monotonic_time & 0xffffffff);
+    kusd->data->InterruptTime.High1Time = (monotonic_time >> 32);
+
+    kusd->data->TickCount.High2Time    = ((monotonic_time / 10000) >> 32);
+    kusd->data->TickCount.LowPart      = ((monotonic_time / 10000) & 0xffffffff);
+    kusd->data->TickCount.High1Time    = ((monotonic_time / 10000) >> 32);
+    kusd->data->TickCountLowDeprecated = ((monotonic_time / 10000) & 0xffffffff);
+}
+
+static void update_kusd_mappings( void *private )
+{
+    struct kusd_mapping *kusd;
+
+    add_timeout_user( kusd_timeout, update_kusd_mappings, NULL );
+
+    LIST_FOR_EACH_ENTRY( kusd, &kusd_mappings, struct kusd_mapping, entry )
+        kusd_mapping_set_current_time( kusd );
+}
+
+int get_user_shared_data_fd( const usd_init_t *init, unsigned int *size )
+{
+    static const WCHAR default_windirW[] = {'C',':','\\','w','i','n','d','o','w','s',0};
+    struct kusd_mapping *kusd;
+    int i;
+
+    LIST_FOR_EACH_ENTRY(kusd, &kusd_mappings, struct kusd_mapping, entry)
+    {
+        if (!memcmp(init, &kusd->init, sizeof(usd_init_t)))
+            goto done;
+    }
+
+    if (!(kusd = mem_alloc( sizeof(struct kusd_mapping) )))
+        return -1;
+
+    kusd->fd = create_temp_file( sizeof(*kusd->data) );
+    kusd->data = mmap( NULL, sizeof(*kusd->data), PROT_WRITE, MAP_SHARED, kusd->fd, 0 );
+    kusd->init = *init;
+
+    kusd->data->NumberOfPhysicalPages = init->number_of_physical_pages;
+    kusd->data->NtMajorVersion = init->nt_major_version;
+    kusd->data->NtMinorVersion = init->nt_minor_version;
+    kusd->data->NtProductType = init->nt_product_type;
+    for (i = 0; i < 64; ++i)
+        kusd->data->ProcessorFeatures[i] = init->processor_features[i];
+
+    memcpy( kusd->data->NtSystemRoot, default_windirW, sizeof(default_windirW) );
+    kusd->data->TickCountMultiplier = 1 << 24;
+
+    kusd_mapping_set_current_time( kusd );
+
+    if (list_empty( &kusd_mappings )) add_timeout_user( kusd_timeout, update_kusd_mappings, NULL );
+    list_add_tail( &kusd_mappings, &kusd->entry );
+
+done:
+    *size = sizeof(*kusd->data);
+    return kusd->fd;
+}
+
 /* create a file mapping */
 DECL_HANDLER(create_mapping)
 {
diff --git a/server/process.c b/server/process.c
index 73984f363f59..6bb18ec6617a 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1385,6 +1385,8 @@ DECL_HANDLER(init_process_done)
     if (req->gui) process->idle_event = create_event( NULL, NULL, 0, 1, 0, NULL );
     if (process->debugger) set_process_debug_flag( process, 1 );
     reply->suspend = (current->suspend || process->suspend);
+
+    send_client_fd( process, get_user_shared_data_fd( get_req_data(), &reply->usd_size ), -1 );
 }
 
 /* open a handle to a process */
diff --git a/server/protocol.def b/server/protocol.def
index 06a29b153ea0..4a935fd7efcf 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -787,6 +787,16 @@ struct rawinput_device
     user_handle_t  target;
 };
 
+/* Initial values for user shared data */
+typedef struct
+{
+    unsigned int   number_of_physical_pages;
+    unsigned int   nt_major_version;
+    unsigned int   nt_minor_version;
+    unsigned short nt_product_type;
+    unsigned char  processor_features[64];
+} usd_init_t;
+
 /****************************************************************/
 /* Request declarations */
 
@@ -854,8 +864,10 @@ 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,usd_init);      /* initial USD values */
 @REPLY
     int          suspend;      /* is process suspended? */
+    unsigned int usd_size;     /* size of USD mapping */
 @END
 
 
diff --git a/server/trace.c b/server/trace.c
index 2b58ed9fd2c0..31560eaad827 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -860,6 +860,28 @@ static void dump_varargs_startup_info( const char *prefix, data_size_t size )
     remove_data( size );
 }
 
+static void dump_varargs_usd_init( const char *prefix, data_size_t size )
+{
+    usd_init_t init;
+    int i;
+
+    memset( &init, 0, sizeof(init) );
+    memcpy( &init, cur_data, min( size, sizeof(init) ));
+
+    fprintf( stderr,
+             "%s{number_of_physical_pages=%u,nt_major_version=%04x,nt_minor_version=%04x,"
+             "nt_product_type=%02x,processor_features={",
+             prefix, init.number_of_physical_pages, init.nt_major_version, init.nt_minor_version,
+             init.nt_product_type );
+    for (i = 0; i < ARRAY_SIZE(init.processor_features); ++i)
+    {
+        if (i > 0) fputc( ',', stderr );
+        fprintf( stderr, "%02x", init.processor_features[i] );
+    }
+    fprintf( stderr, "}}" );
+    remove_data( size );
+}
+
 static void dump_varargs_input_records( const char *prefix, data_size_t size )
 {
     const INPUT_RECORD *rec = cur_data;
-- 
2.26.1




More information about the wine-devel mailing list