Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
0936606c
Commit
0936606c
authored
May 21, 2020
by
Alexandre Julliard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ntdll: Centralize initialization of the user shared data.
Signed-off-by:
Alexandre Julliard
<
julliard@winehq.org
>
parent
ab350866
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
67 additions
and
88 deletions
+67
-88
nt.c
dlls/ntdll/nt.c
+4
-51
info.c
dlls/ntdll/tests/info.c
+0
-1
thread.c
dlls/ntdll/thread.c
+63
-30
version.c
dlls/ntdll/version.c
+0
-6
No files found.
dlls/ntdll/nt.c
View file @
0936606c
...
...
@@ -1105,22 +1105,8 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
if
(
regs2
[
2
]
&
(
1
<<
13
))
info
->
FeatureSet
|=
CPU_FEATURE_CX128
;
if
(
regs2
[
2
]
&
(
1
<<
27
))
info
->
FeatureSet
|=
CPU_FEATURE_XSAVE
;
user_shared_data
->
ProcessorFeatures
[
PF_FLOATING_POINT_EMULATED
]
=
!
(
regs2
[
3
]
&
1
);
user_shared_data
->
ProcessorFeatures
[
PF_RDTSC_INSTRUCTION_AVAILABLE
]
=
(
regs2
[
3
]
>>
4
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_PAE_ENABLED
]
=
(
regs2
[
3
]
>>
6
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_COMPARE_EXCHANGE_DOUBLE
]
=
(
regs2
[
3
]
>>
8
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_MMX_INSTRUCTIONS_AVAILABLE
]
=
(
regs2
[
3
]
>>
23
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_XMMI_INSTRUCTIONS_AVAILABLE
]
=
(
regs2
[
3
]
>>
25
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_XMMI64_INSTRUCTIONS_AVAILABLE
]
=
(
regs2
[
3
]
>>
26
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_SSE3_INSTRUCTIONS_AVAILABLE
]
=
regs2
[
2
]
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_XSAVE_ENABLED
]
=
(
regs2
[
2
]
>>
27
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_COMPARE_EXCHANGE128
]
=
(
regs2
[
2
]
>>
13
)
&
1
;
if
((
regs2
[
3
]
&
(
1
<<
26
))
&&
(
regs2
[
3
]
&
(
1
<<
24
))
&&
have_sse_daz_mode
())
/* has SSE2 and FXSAVE/FXRSTOR */
{
info
->
FeatureSet
|=
CPU_FEATURE_DAZ
;
user_shared_data
->
ProcessorFeatures
[
PF_SSE_DAZ_MODE_AVAILABLE
]
=
TRUE
;
}
if
(
regs
[
1
]
==
AUTH
&&
regs
[
3
]
==
ENTI
&&
regs
[
2
]
==
CAMD
)
{
...
...
@@ -1137,10 +1123,6 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
if
(
regs
[
0
]
>=
0x80000001
)
{
do_cpuid
(
0x80000001
,
regs2
);
/* get vendor features */
user_shared_data
->
ProcessorFeatures
[
PF_VIRT_FIRMWARE_ENABLED
]
=
(
regs2
[
2
]
>>
2
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_NX_ENABLED
]
=
(
regs2
[
3
]
>>
20
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_3DNOW_INSTRUCTIONS_AVAILABLE
]
=
(
regs2
[
3
]
>>
31
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_RDTSC_INSTRUCTION_AVAILABLE
]
=
(
regs2
[
3
]
>>
27
)
&
1
;
if
(
regs2
[
2
]
&
(
1
<<
2
))
info
->
FeatureSet
|=
CPU_FEATURE_VIRT
;
if
(
regs2
[
3
]
&
(
1
<<
20
))
info
->
FeatureSet
|=
CPU_FEATURE_NX
;
if
(
regs2
[
3
]
&
(
1
<<
27
))
info
->
FeatureSet
|=
CPU_FEATURE_TSC
;
...
...
@@ -1159,14 +1141,11 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
if
(
regs2
[
2
]
&
(
1
<<
5
))
info
->
FeatureSet
|=
CPU_FEATURE_VIRT
;
if
(
regs2
[
3
]
&
(
1
<<
21
))
info
->
FeatureSet
|=
CPU_FEATURE_DS
;
user_shared_data
->
ProcessorFeatures
[
PF_VIRT_FIRMWARE_ENABLED
]
=
(
regs2
[
2
]
>>
5
)
&
1
;
do_cpuid
(
0x80000000
,
regs
);
/* get vendor cpuid level */
if
(
regs
[
0
]
>=
0x80000001
)
{
do_cpuid
(
0x80000001
,
regs2
);
/* get vendor features */
user_shared_data
->
ProcessorFeatures
[
PF_NX_ENABLED
]
=
(
regs2
[
3
]
>>
20
)
&
1
;
user_shared_data
->
ProcessorFeatures
[
PF_RDTSC_INSTRUCTION_AVAILABLE
]
=
(
regs2
[
3
]
>>
27
)
&
1
;
if
(
regs2
[
3
]
&
(
1
<<
20
))
info
->
FeatureSet
|=
CPU_FEATURE_NX
;
if
(
regs2
[
3
]
&
(
1
<<
27
))
info
->
FeatureSet
|=
CPU_FEATURE_TSC
;
}
...
...
@@ -1191,10 +1170,6 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
int
value
;
valSize
=
sizeof
(
value
);
if
(
sysctlbyname
(
"hw.optional.floatingpoint"
,
&
value
,
&
valSize
,
NULL
,
0
)
==
0
)
user_shared_data
->
ProcessorFeatures
[
PF_FLOATING_POINT_EMULATED
]
=
!
value
;
valSize
=
sizeof
(
value
);
if
(
sysctlbyname
(
"hw.cpusubtype"
,
&
value
,
&
valSize
,
NULL
,
0
)
==
0
)
{
switch
(
value
)
...
...
@@ -1260,16 +1235,8 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
}
if
(
!
_stricmp
(
line
,
"features"
))
{
if
(
strstr
(
value
,
"vfpv3"
))
{
info
->
FeatureSet
|=
CPU_FEATURE_ARM_VFP_32
;
user_shared_data
->
ProcessorFeatures
[
PF_ARM_VFP_32_REGISTERS_AVAILABLE
]
=
TRUE
;
}
if
(
strstr
(
value
,
"neon"
))
{
info
->
FeatureSet
|=
CPU_FEATURE_ARM_NEON
;
user_shared_data
->
ProcessorFeatures
[
PF_ARM_NEON_INSTRUCTIONS_AVAILABLE
]
=
TRUE
;
}
if
(
strstr
(
value
,
"vfpv3"
))
info
->
FeatureSet
|=
CPU_FEATURE_ARM_VFP_32
;
if
(
strstr
(
value
,
"neon"
))
info
->
FeatureSet
|=
CPU_FEATURE_ARM_NEON
;
continue
;
}
}
...
...
@@ -1287,15 +1254,10 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
valsize
=
sizeof
(
value
);
if
(
!
sysctlbyname
(
"hw.floatingpoint"
,
&
value
,
&
valsize
,
NULL
,
0
))
{
info
->
FeatureSet
|=
CPU_FEATURE_ARM_VFP_32
;
user_shared_data
->
ProcessorFeatures
[
PF_ARM_VFP_32_REGISTERS_AVAILABLE
]
=
value
;
}
#else
FIXME
(
"CPU Feature detection not implemented.
\n
"
);
#endif
if
(
info
->
Level
>=
8
)
user_shared_data
->
ProcessorFeatures
[
PF_ARM_V8_INSTRUCTIONS_AVAILABLE
]
=
TRUE
;
info
->
Architecture
=
PROCESSOR_ARCHITECTURE_ARM
;
}
...
...
@@ -1337,16 +1299,8 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
}
if
(
!
_stricmp
(
line
,
"Features"
))
{
if
(
strstr
(
value
,
"crc32"
))
{
info
->
FeatureSet
|=
CPU_FEATURE_ARM_V8_CRC32
;
user_shared_data
->
ProcessorFeatures
[
PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE
]
=
TRUE
;
}
if
(
strstr
(
value
,
"aes"
))
{
info
->
FeatureSet
|=
CPU_FEATURE_ARM_V8_CRYPTO
;
user_shared_data
->
ProcessorFeatures
[
PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE
]
=
TRUE
;
}
if
(
strstr
(
value
,
"crc32"
))
info
->
FeatureSet
|=
CPU_FEATURE_ARM_V8_CRC32
;
if
(
strstr
(
value
,
"aes"
))
info
->
FeatureSet
|=
CPU_FEATURE_ARM_V8_CRYPTO
;
continue
;
}
}
...
...
@@ -1356,7 +1310,6 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
FIXME
(
"CPU Feature detection not implemented.
\n
"
);
#endif
info
->
Level
=
max
(
info
->
Level
,
8
);
user_shared_data
->
ProcessorFeatures
[
PF_ARM_V8_INSTRUCTIONS_AVAILABLE
]
=
TRUE
;
info
->
Architecture
=
PROCESSOR_ARCHITECTURE_ARM64
;
}
...
...
dlls/ntdll/tests/info.c
View file @
0936606c
...
...
@@ -2261,7 +2261,6 @@ static void test_queryvirtualmemory(void)
ok
(
status
==
STATUS_SUCCESS
,
"Expected STATUS_SUCCESS, got %08x
\n
"
,
status
);
ok
(
readcount
==
sizeof
(
MEMORY_BASIC_INFORMATION
),
"Expected to read %d bytes, got %ld
\n
"
,(
int
)
sizeof
(
MEMORY_BASIC_INFORMATION
),
readcount
);
ok
(
mbi
.
AllocationBase
==
user_shared_data
,
"mbi.AllocationBase is 0x%p, expected 0x%p
\n
"
,
mbi
.
AllocationBase
,
user_shared_data
);
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
);
ok
(
mbi
.
Protect
==
PAGE_READONLY
,
"mbi.Protect is 0x%x
\n
"
,
mbi
.
Protect
);
...
...
dlls/ntdll/thread.c
View file @
0936606c
...
...
@@ -53,7 +53,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(thread);
#endif
struct
_KUSER_SHARED_DATA
*
user_shared_data
=
NULL
;
static
size_t
user_shared_data_size
;
void
(
WINAPI
*
kernel32_start_process
)(
LPTHREAD_START_ROUTINE
,
void
*
)
=
NULL
;
...
...
@@ -183,6 +182,64 @@ int __cdecl __wine_dbg_output( const char *str )
return
unix_funcs
->
dbg_output
(
str
);
}
static
void
fill_user_shared_data
(
struct
_KUSER_SHARED_DATA
*
data
)
{
RTL_OSVERSIONINFOEXW
version
;
SYSTEM_CPU_INFORMATION
sci
;
SYSTEM_BASIC_INFORMATION
sbi
;
BOOLEAN
*
features
=
data
->
ProcessorFeatures
;
version
.
dwOSVersionInfoSize
=
sizeof
(
version
);
RtlGetVersion
(
&
version
);
virtual_get_system_info
(
&
sbi
);
NtQuerySystemInformation
(
SystemCpuInformation
,
&
sci
,
sizeof
(
sci
),
NULL
);
data
->
TickCountMultiplier
=
1
<<
24
;
data
->
LargePageMinimum
=
2
*
1024
*
1024
;
data
->
NtBuildNumber
=
version
.
dwBuildNumber
;
data
->
NtProductType
=
version
.
wProductType
;
data
->
ProductTypeIsValid
=
TRUE
;
data
->
NativeProcessorArchitecture
=
sci
.
Architecture
;
data
->
NtMajorVersion
=
version
.
dwMajorVersion
;
data
->
NtMinorVersion
=
version
.
dwMinorVersion
;
data
->
SuiteMask
=
version
.
wSuiteMask
;
data
->
NumberOfPhysicalPages
=
sbi
.
MmNumberOfPhysicalPages
;
wcscpy
(
data
->
NtSystemRoot
,
windows_dir
);
switch
(
sci
.
Architecture
)
{
case
PROCESSOR_ARCHITECTURE_INTEL
:
case
PROCESSOR_ARCHITECTURE_AMD64
:
features
[
PF_COMPARE_EXCHANGE_DOUBLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_CX8
);
features
[
PF_MMX_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_MMX
);
features
[
PF_XMMI_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_SSE
);
features
[
PF_3DNOW_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_3DNOW
);
features
[
PF_RDTSC_INSTRUCTION_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_TSC
);
features
[
PF_PAE_ENABLED
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_PAE
);
features
[
PF_XMMI64_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_SSE2
);
features
[
PF_SSE3_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_SSE3
);
features
[
PF_XSAVE_ENABLED
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_XSAVE
);
features
[
PF_COMPARE_EXCHANGE128
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_CX128
);
features
[
PF_SSE_DAZ_MODE_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_DAZ
);
features
[
PF_NX_ENABLED
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_NX
);
features
[
PF_SECOND_LEVEL_ADDRESS_TRANSLATION
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_2NDLEV
);
features
[
PF_VIRT_FIRMWARE_ENABLED
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_VIRT
);
features
[
PF_RDWRFSGSBASE_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_RDFS
);
features
[
PF_FASTFAIL_AVAILABLE
]
=
TRUE
;
break
;
case
PROCESSOR_ARCHITECTURE_ARM
:
features
[
PF_ARM_VFP_32_REGISTERS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_ARM_VFP_32
);
features
[
PF_ARM_NEON_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_ARM_NEON
);
features
[
PF_ARM_V8_INSTRUCTIONS_AVAILABLE
]
=
(
sci
.
Level
>=
8
);
break
;
case
PROCESSOR_ARCHITECTURE_ARM64
:
features
[
PF_ARM_V8_INSTRUCTIONS_AVAILABLE
]
=
TRUE
;
features
[
PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_ARM_V8_CRC32
);
features
[
PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE
]
=
!!
(
sci
.
FeatureSet
&
CPU_FEATURE_ARM_V8_CRYPTO
);
break
;
}
}
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'
,
...
...
@@ -194,11 +251,9 @@ HANDLE user_shared_data_init_done(void)
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
;
section_size
.
QuadPart
=
sizeof
(
*
user_shared_data
);
RtlInitUnicodeString
(
&
wine_usd_str
,
wine_usdW
);
InitializeObjectAttributes
(
&
attr
,
&
wine_usd_str
,
OBJ_OPENIF
,
NULL
,
NULL
);
...
...
@@ -213,8 +268,7 @@ HANDLE user_shared_data_init_done(void)
if
(
status
!=
STATUS_OBJECT_NAME_EXISTS
)
{
addr
=
NULL
;
size
=
user_shared_data_size
;
size
=
sizeof
(
*
user_shared_data
);
if
((
status
=
NtMapViewOfSection
(
section
,
NtCurrentProcess
(),
&
addr
,
0
,
0
,
0
,
&
size
,
0
,
0
,
PAGE_READWRITE
)))
{
...
...
@@ -222,16 +276,12 @@ HANDLE user_shared_data_init_done(void)
exit
(
1
);
}
memcpy
(
addr
,
user_shared_data
,
user_shared_data_size
);
fill_user_shared_data
(
addr
);
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
,
(
user_shared_data
!=
mmap
(
user_shared_data
,
sizeof
(
*
user_shared_data
)
,
PROT_READ
,
MAP_SHARED
|
MAP_FIXED
,
fd
,
0
)))
{
MESSAGE
(
"wine: failed to remap the process USD: %d
\n
"
,
res
);
...
...
@@ -251,11 +301,9 @@ HANDLE user_shared_data_init_done(void)
*/
TEB
*
thread_init
(
void
)
{
SYSTEM_BASIC_INFORMATION
sbi
;
TEB
*
teb
;
void
*
addr
;
SIZE_T
size
;
LARGE_INTEGER
now
;
NTSTATUS
status
;
struct
ntdll_thread_data
*
thread_data
;
...
...
@@ -266,15 +314,13 @@ TEB *thread_init(void)
addr
=
(
void
*
)
0x7ffe0000
;
size
=
0x1000
;
status
=
NtAllocateVirtualMemory
(
NtCurrentProcess
(),
&
addr
,
0
,
&
size
,
MEM_RESERVE
|
MEM_COMMIT
,
PAGE_READ
WRITE
);
MEM_RESERVE
|
MEM_COMMIT
,
PAGE_READ
ONLY
);
if
(
status
)
{
MESSAGE
(
"wine: failed to map the shared user data: %08x
\n
"
,
status
);
exit
(
1
);
}
user_shared_data
=
addr
;
user_shared_data_size
=
size
;
wcscpy
(
user_shared_data
->
NtSystemRoot
,
windows_dir
);
/* allocate and initialize the PEB and initial TEB */
...
...
@@ -317,20 +363,7 @@ TEB *thread_init(void)
unix_funcs
->
dbg_init
();
unix_funcs
->
get_paths
(
&
build_dir
,
&
data_dir
,
&
config_dir
);
/* initialize time values in user_shared_data */
NtQuerySystemTime
(
&
now
);
user_shared_data
->
SystemTime
.
LowPart
=
now
.
u
.
LowPart
;
user_shared_data
->
SystemTime
.
High1Time
=
user_shared_data
->
SystemTime
.
High2Time
=
now
.
u
.
HighPart
;
user_shared_data
->
u
.
TickCountQuad
=
(
now
.
QuadPart
-
server_start_time
)
/
10000
;
user_shared_data
->
u
.
TickCount
.
High2Time
=
user_shared_data
->
u
.
TickCount
.
High1Time
;
user_shared_data
->
TickCountLowDeprecated
=
user_shared_data
->
u
.
TickCount
.
LowPart
;
user_shared_data
->
TickCountMultiplier
=
1
<<
24
;
fill_cpu_info
();
virtual_get_system_info
(
&
sbi
);
user_shared_data
->
NumberOfPhysicalPages
=
sbi
.
MmNumberOfPhysicalPages
;
return
teb
;
}
...
...
dlls/ntdll/version.c
View file @
0936606c
...
...
@@ -540,12 +540,6 @@ done:
NtCurrentTeb
()
->
Peb
->
OSBuildNumber
=
current_version
->
dwBuildNumber
;
NtCurrentTeb
()
->
Peb
->
OSPlatformId
=
current_version
->
dwPlatformId
;
user_shared_data
->
NtProductType
=
current_version
->
wProductType
;
user_shared_data
->
ProductTypeIsValid
=
TRUE
;
user_shared_data
->
NtMajorVersion
=
current_version
->
dwMajorVersion
;
user_shared_data
->
NtMinorVersion
=
current_version
->
dwMinorVersion
;
user_shared_data
->
SuiteMask
=
current_version
->
wSuiteMask
;
TRACE
(
"got %d.%d platform %d build %x name %s service pack %d.%d product %d
\n
"
,
current_version
->
dwMajorVersion
,
current_version
->
dwMinorVersion
,
current_version
->
dwPlatformId
,
current_version
->
dwBuildNumber
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment