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@codeweavers.com ---
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.
v5: A standard mapping object is created and a handle is returned instead of directly using the unix fd.
The section is mapped with mmap instead of standard functions for multiple reasons:
* NtQueryVirtualMemory returns MEM_PRIVATE on Windows, using standard section mapping will return MEM_MAPPED instead.
* NtMapViewOfSection requires the view to be unmapped first with NtFreeVirtualMemory, this could create a race condition if a signal is received between the two calls. On the contrary, mmap with MAP_FIXED doesn't need the memory to be unmapped first.
dlls/ntdll/ntdll_misc.h | 1 + dlls/ntdll/server.c | 20 ++++++++++ dlls/ntdll/tests/time.c | 9 +++-- dlls/ntdll/thread.c | 2 + dlls/ntoskrnl.exe/instr.c | 12 ------ server/file.h | 2 + server/mapping.c | 84 +++++++++++++++++++++++++++++++++++++++ server/process.c | 1 + server/protocol.def | 2 + 9 files changed, 118 insertions(+), 15 deletions(-)
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 13771d230a88..ecce05df93c0 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 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 41e6ea891c56..d81d413c90a4 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,7 +1864,13 @@ 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; int suspend; + 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(); @@ -1886,11 +1893,24 @@ 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, user_shared_data, sizeof(*user_shared_data) ); status = wine_server_call( req ); suspend = reply->suspend; + usd_handle = wine_server_ptr_handle( reply->usd_handle ); } SERVER_END_REQ;
+ if (usd_handle) + { + int res, usd_fd, needs_close; + if ((res = server_get_unix_fd( usd_handle, 0, &usd_fd, &needs_close, NULL, NULL )) || + (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" ); + if (needs_close) close( usd_fd ); + NtClose( usd_handle ); + } + 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 b25f87e43755..6e5428bf0f06 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 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..269c8bed79f6 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 obj_handle_t get_usd_handle( 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..8cd2260feae1 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,89 @@ int get_page_size(void) return page_mask + 1; }
+struct object *kusd_mapping; +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 ); + +#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 +} + +obj_handle_t get_usd_handle( const void *usd_init, data_size_t usd_size ) +{ + /* keep it the same as user_shared_data_size in ntdll */ + size_t size = 0x10000; + struct fd *fd; + + if (sizeof(*kusd) != usd_size) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + + if (!kusd_mapping) + { + if (!(kusd_mapping = create_mapping( NULL, NULL, 0, size, SEC_COMMIT, 0, 0, NULL ))) + return 0; + make_object_static( kusd_mapping ); + + if (!(fd = mapping_get_fd( kusd_mapping ))) + return 0; + + if ((kusd = mmap( NULL, size, PROT_WRITE, MAP_SHARED, get_unix_fd( fd ), 0 )) != MAP_FAILED) + { + memcpy( kusd, usd_init, usd_size ); + kusd_set_current_time( NULL ); + } + release_object( fd ); + } + + if (kusd == MAP_FAILED) + { + set_error( STATUS_NO_MEMORY ); + return 0; + } + + return alloc_handle( current->process, kusd_mapping, SECTION_QUERY|SECTION_MAP_READ, 0 ); +} + /* create a file mapping */ DECL_HANDLER(create_mapping) { diff --git a/server/process.c b/server/process.c index 211207ed03b6..d5800b05a01c 100644 --- a/server/process.c +++ b/server/process.c @@ -1390,6 +1390,7 @@ 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); + reply->usd_handle = get_usd_handle( get_req_data(), get_req_data_size() ); }
/* open a handle to a process */ diff --git a/server/protocol.def b/server/protocol.def index 06a29b153ea0..7d1071cd86e3 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -854,8 +854,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,bytes); /* USD initialization data */ @REPLY int suspend; /* is process suspended? */ + obj_handle_t usd_handle; /* USD mapping handle */ @END