server: Add USD support with timestamp updates.
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> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
dadf93f409
commit
a1c46c3806
|
@ -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,
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */,
|
||||
|
|
|
@ -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( §ion, SECTION_ALL_ACCESS, &attr,
|
||||
§ion_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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -872,6 +872,8 @@ struct init_process_done_request
|
|||
mod_handle_t module;
|
||||
client_ptr_t ldt_copy;
|
||||
client_ptr_t entry;
|
||||
obj_handle_t usd_handle;
|
||||
char __pad_44[4];
|
||||
};
|
||||
struct init_process_done_reply
|
||||
{
|
||||
|
@ -6682,7 +6684,7 @@ union generic_reply
|
|||
|
||||
/* ### protocol_version begin ### */
|
||||
|
||||
#define SERVER_PROTOCOL_VERSION 602
|
||||
#define SERVER_PROTOCOL_VERSION 603
|
||||
|
||||
/* ### protocol_version end ### */
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -784,7 +784,8 @@ C_ASSERT( FIELD_OFFSET(struct init_process_done_request, gui) == 12 );
|
|||
C_ASSERT( FIELD_OFFSET(struct init_process_done_request, module) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct init_process_done_request, ldt_copy) == 24 );
|
||||
C_ASSERT( FIELD_OFFSET(struct init_process_done_request, entry) == 32 );
|
||||
C_ASSERT( sizeof(struct init_process_done_request) == 40 );
|
||||
C_ASSERT( FIELD_OFFSET(struct init_process_done_request, usd_handle) == 40 );
|
||||
C_ASSERT( sizeof(struct init_process_done_request) == 48 );
|
||||
C_ASSERT( FIELD_OFFSET(struct init_process_done_reply, suspend) == 8 );
|
||||
C_ASSERT( sizeof(struct init_process_done_reply) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct init_thread_request, unix_pid) == 12 );
|
||||
|
|
|
@ -1317,6 +1317,7 @@ static void dump_init_process_done_request( const struct init_process_done_reque
|
|||
dump_uint64( ", module=", &req->module );
|
||||
dump_uint64( ", ldt_copy=", &req->ldt_copy );
|
||||
dump_uint64( ", entry=", &req->entry );
|
||||
fprintf( stderr, ", usd_handle=%04x", req->usd_handle );
|
||||
}
|
||||
|
||||
static void dump_init_process_done_reply( const struct init_process_done_reply *req )
|
||||
|
|
Loading…
Reference in New Issue