2012-11-14 23:39:56 +00:00
# include "stdafx.h"
2014-09-11 19:18:19 +00:00
# include "Log.h"
2015-01-16 14:36:53 +00:00
# include "rpcs3/Ini.h"
2015-01-04 21:46:31 +00:00
# include "Emu/System.h"
# include "Emu/CPU/CPUThread.h"
2015-01-04 21:59:17 +00:00
# include "Emu/SysCalls/SysCalls.h"
2012-11-14 23:39:56 +00:00
# include "Thread.h"
2014-12-05 16:12:15 +00:00
# ifdef _WIN32
# include <windows.h>
2015-01-04 21:46:31 +00:00
# else
2015-02-13 21:45:36 +00:00
# ifdef __APPLE__
2015-02-11 04:17:39 +00:00
# define _XOPEN_SOURCE
# define __USE_GNU
2015-02-13 21:45:36 +00:00
# endif
2015-01-04 21:46:31 +00:00
# include <signal.h>
# include <ucontext.h>
2014-12-05 16:12:15 +00:00
# endif
void SetCurrentThreadDebugName ( const char * threadName )
{
2015-01-12 18:12:06 +00:00
# if defined(_MSC_VER) // this is VS-specific way to set thread names for the debugger
2014-12-05 16:12:15 +00:00
# pragma pack(push,8)
2015-01-12 18:12:06 +00:00
2014-12-05 16:12:15 +00:00
struct THREADNAME_INFO
{
DWORD dwType ;
LPCSTR szName ;
DWORD dwThreadID ;
DWORD dwFlags ;
} info ;
2015-01-12 18:12:06 +00:00
2014-12-05 16:12:15 +00:00
# pragma pack(pop)
info . dwType = 0x1000 ;
info . szName = threadName ;
info . dwThreadID = - 1 ;
info . dwFlags = 0 ;
__try
{
RaiseException ( 0x406D1388 , 0 , sizeof ( info ) / sizeof ( ULONG_PTR ) , ( ULONG_PTR * ) & info ) ;
}
__except ( EXCEPTION_EXECUTE_HANDLER )
{
}
2015-01-12 18:12:06 +00:00
2014-12-05 16:12:15 +00:00
# endif
}
2015-01-04 21:46:31 +00:00
enum x64_reg_t : u32
{
2015-02-15 12:31:42 +00:00
X64R_RAX ,
X64R_RCX ,
X64R_RDX ,
X64R_RBX ,
X64R_RSP ,
X64R_RBP ,
X64R_RSI ,
X64R_RDI ,
X64R_R8 ,
X64R_R9 ,
X64R_R10 ,
X64R_R11 ,
X64R_R12 ,
X64R_R13 ,
X64R_R14 ,
X64R_R15 ,
X64R_XMM0 ,
X64R_XMM1 ,
X64R_XMM2 ,
X64R_XMM3 ,
X64R_XMM4 ,
X64R_XMM5 ,
X64R_XMM6 ,
X64R_XMM7 ,
X64R_XMM8 ,
X64R_XMM9 ,
X64R_XMM10 ,
X64R_XMM11 ,
X64R_XMM12 ,
X64R_XMM13 ,
X64R_XMM14 ,
X64R_XMM15 ,
X64R_AL ,
X64R_CL ,
X64R_DL ,
X64R_BL ,
X64R_AH ,
X64R_CH ,
X64R_DH ,
X64R_BH ,
X64_NOT_SET ,
X64_IMM8 ,
X64_IMM16 ,
2015-01-04 21:46:31 +00:00
X64_IMM32 ,
2015-02-15 12:31:42 +00:00
X64R_ECX = X64R_CL ,
2015-01-04 21:46:31 +00:00
} ;
enum x64_op_t : u32
{
2015-02-15 17:13:06 +00:00
X64OP_NONE ,
2015-01-04 21:46:31 +00:00
X64OP_LOAD , // obtain and put the value into x64 register (from Memory.ReadMMIO32, for example)
X64OP_STORE , // take the value from x64 register or an immediate and use it (pass in Memory.WriteMMIO32, for example)
// example: add eax,[rax] -> X64OP_LOAD_ADD (add the value to x64 register)
// example: add [rax],eax -> X64OP_LOAD_ADD_STORE (this will probably never happen for MMIO registers)
2015-02-15 12:31:42 +00:00
X64OP_MOVS ,
X64OP_XCHG ,
X64OP_CMPXCHG ,
2015-01-04 21:46:31 +00:00
} ;
2015-02-15 17:13:06 +00:00
void decode_x64_reg_op ( const u8 * code , x64_op_t & out_op , x64_reg_t & out_reg , size_t & out_size , size_t & out_length )
2015-01-04 21:46:31 +00:00
{
// simple analysis of x64 code allows to reinterpret MOV or other instructions in any desired way
2015-02-15 12:31:42 +00:00
out_length = 0 ;
u8 rex = 0 , pg2 = 0 ;
2015-01-04 21:46:31 +00:00
2015-02-15 12:31:42 +00:00
bool oso = false , lock = false , repne = false , repe = false ;
enum : u8
{
LOCK = 0xf0 ,
REPNE = 0xf2 ,
REPE = 0xf3 ,
} ;
2015-01-04 21:46:31 +00:00
// check prefixes:
2015-02-15 12:31:42 +00:00
for ( ; ; code + + , out_length + + )
2015-01-04 21:46:31 +00:00
{
switch ( const u8 prefix = * code )
{
2015-02-15 12:31:42 +00:00
case LOCK : // group 1
{
if ( lock )
{
LOG_ERROR ( GENERAL , " decode_x64_reg_op(%016llxh): LOCK prefix found twice " , ( size_t ) code - out_length ) ;
}
lock = true ;
continue ;
}
case REPNE : // group 1
{
if ( repne )
{
LOG_ERROR ( GENERAL , " decode_x64_reg_op(%016llxh): REPNE/REPNZ prefix found twice " , ( size_t ) code - out_length ) ;
}
repne = true ;
continue ;
}
case REPE : // group 1
{
if ( repe )
{
LOG_ERROR ( GENERAL , " decode_x64_reg_op(%016llxh): REP/REPE/REPZ prefix found twice " , ( size_t ) code - out_length ) ;
}
repe = true ;
continue ;
}
2015-01-04 21:46:31 +00:00
case 0x2e : // group 2
case 0x36 :
case 0x3e :
case 0x26 :
case 0x64 :
case 0x65 :
{
2015-02-15 12:31:42 +00:00
if ( pg2 )
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
LOG_ERROR ( GENERAL , " decode_x64_reg_op(%016llxh): 0x%02x (group 2 prefix) found after 0x%02x " , ( size_t ) code - out_length , prefix , pg2 ) ;
2015-01-04 21:46:31 +00:00
}
else
{
2015-02-15 12:31:42 +00:00
pg2 = prefix ; // probably, segment register
2015-01-04 21:46:31 +00:00
}
2015-02-15 12:31:42 +00:00
continue ;
2015-01-04 21:46:31 +00:00
}
2015-02-15 12:31:42 +00:00
case 0x66 : // group 3
{
if ( oso )
{
LOG_ERROR ( GENERAL , " decode_x64_reg_op(%016llxh): operand-size override prefix found twice " , ( size_t ) code - out_length ) ;
}
oso = true ;
continue ;
}
case 0x67 : // group 4
{
LOG_ERROR ( GENERAL , " decode_x64_reg_op(%016llxh): address-size override prefix found " , ( size_t ) code - out_length , prefix ) ;
2015-02-15 17:13:06 +00:00
out_op = X64OP_NONE ;
2015-02-15 12:31:42 +00:00
out_reg = X64_NOT_SET ;
out_size = 0 ;
out_length = 0 ;
return ;
}
2015-01-04 21:46:31 +00:00
default :
{
if ( ( prefix & 0xf0 ) = = 0x40 ) // check REX prefix
{
if ( rex )
{
2015-02-15 12:31:42 +00:00
LOG_ERROR ( GENERAL , " decode_x64_reg_op(%016llxh): 0x%02x (REX prefix) found after 0x%02x " , ( size_t ) code - out_length , prefix , rex ) ;
2015-01-04 21:46:31 +00:00
}
2015-02-15 12:31:42 +00:00
else
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
rex = prefix ;
2015-01-04 21:46:31 +00:00
}
continue ;
}
}
}
break ;
}
2015-02-15 12:31:42 +00:00
auto get_modRM_reg = [ ] ( const u8 * code , const u8 rex ) - > x64_reg_t
{
2015-02-15 17:13:06 +00:00
return ( x64_reg_t ) ( ( ( * code & 0x38 ) > > 3 | ( /* check REX.R bit */ rex & 4 ? 8 : 0 ) ) + X64R_RAX ) ;
2015-02-15 12:31:42 +00:00
} ;
auto get_modRM_reg_xmm = [ ] ( const u8 * code , const u8 rex ) - > x64_reg_t
{
2015-02-15 17:13:06 +00:00
return ( x64_reg_t ) ( ( ( * code & 0x38 ) > > 3 | ( /* check REX.R bit */ rex & 4 ? 8 : 0 ) ) + X64R_XMM0 ) ;
2015-02-15 12:31:42 +00:00
} ;
auto get_modRM_reg_lh = [ ] ( const u8 * code ) - > x64_reg_t
2015-01-04 21:46:31 +00:00
{
2015-02-15 17:13:06 +00:00
return ( x64_reg_t ) ( ( ( * code & 0x38 ) > > 3 ) + X64R_AL ) ;
2015-01-04 21:46:31 +00:00
} ;
2015-02-15 17:13:06 +00:00
auto get_op_size = [ ] ( const u8 rex , const bool oso ) - > size_t
2015-02-15 12:31:42 +00:00
{
return rex & 8 ? 8 : ( oso ? 2 : 4 ) ;
} ;
2015-02-15 17:13:06 +00:00
auto get_modRM_size = [ ] ( const u8 * code ) - > size_t
2015-01-04 21:46:31 +00:00
{
switch ( * code > > 6 ) // check Mod
{
case 0 : return ( * code & 0x07 ) = = 4 ? 2 : 1 ; // check SIB
case 1 : return ( * code & 0x07 ) = = 4 ? 3 : 2 ; // check SIB (disp8)
case 2 : return ( * code & 0x07 ) = = 4 ? 6 : 5 ; // check SIB (disp32)
default : return 1 ;
}
} ;
2015-02-15 12:31:42 +00:00
const u8 op1 = ( out_length + + , * code + + ) , op2 = code [ 0 ] , op3 = code [ 1 ] ;
switch ( op1 )
{
case 0x0f :
{
out_length + + , code + + ;
switch ( op2 )
{
case 0x7f :
{
if ( repe & & ! oso ) // MOVDQU xmm/m, xmm
{
out_op = X64OP_STORE ;
out_reg = get_modRM_reg_xmm ( code , rex ) ;
out_size = 16 ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
}
case 0xb0 :
{
if ( ! oso ) // CMPXCHG r8/m8, r8
{
out_op = X64OP_CMPXCHG ;
out_reg = rex & 8 ? get_modRM_reg ( code , rex ) : get_modRM_reg_lh ( code ) ;
out_size = 1 ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
}
case 0xb1 :
{
if ( true ) // CMPXCHG r/m, r (16, 32, 64)
{
out_op = X64OP_CMPXCHG ;
out_reg = get_modRM_reg ( code , rex ) ;
out_size = get_op_size ( rex , oso ) ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
}
}
break ;
}
case 0x86 :
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
if ( ! oso ) // XCHG r8/m8, r8
{
out_op = X64OP_XCHG ;
out_reg = rex & 8 ? get_modRM_reg ( code , rex ) : get_modRM_reg_lh ( code ) ;
out_size = 1 ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
}
case 0x87 :
{
if ( true ) // XCHG r/m, r (16, 32, 64)
{
out_op = X64OP_XCHG ;
out_reg = get_modRM_reg ( code , rex ) ;
out_size = get_op_size ( rex , oso ) ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
}
case 0x88 :
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
if ( ! lock & & ! oso ) // MOV r8/m8, r8
{
out_op = X64OP_STORE ;
out_reg = rex & 8 ? get_modRM_reg ( code , rex ) : get_modRM_reg_lh ( code ) ;
out_size = 1 ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
2015-01-04 21:46:31 +00:00
}
2015-02-15 12:31:42 +00:00
case 0x89 :
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
if ( ! lock ) // MOV r/m, r (16, 32, 64)
{
out_op = X64OP_STORE ;
out_reg = get_modRM_reg ( code , rex ) ;
out_size = get_op_size ( rex , oso ) ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
2015-01-04 21:46:31 +00:00
}
2015-02-15 12:31:42 +00:00
case 0x8a :
{
if ( ! lock & & ! oso ) // MOV r8, r8/m8
{
out_op = X64OP_LOAD ;
out_reg = rex & 8 ? get_modRM_reg ( code , rex ) : get_modRM_reg_lh ( code ) ;
out_size = 1 ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
}
case 0x8b :
{
if ( ! lock ) // MOV r, r/m (16, 32, 64)
{
out_op = X64OP_LOAD ;
out_reg = get_modRM_reg ( code , rex ) ;
out_size = get_op_size ( rex , oso ) ;
out_length + = get_modRM_size ( code ) ;
return ;
}
break ;
}
case 0xa4 :
{
if ( ! oso & & ! lock & & ! repe & & ! rex ) // MOVS
{
out_op = X64OP_MOVS ;
out_reg = X64_NOT_SET ;
out_size = 1 ;
return ;
}
if ( ! oso & & ! lock & & repe ) // REP MOVS
{
out_op = X64OP_MOVS ;
out_reg = rex & 8 ? X64R_RCX : X64R_ECX ;
out_size = 1 ;
return ;
}
break ;
}
case 0xc6 :
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
if ( ! lock & & ! oso & & get_modRM_reg ( code , 0 ) = = X64R_RAX ) // MOV r8/m8, imm8
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
out_op = X64OP_STORE ;
out_reg = X64_IMM8 ;
out_size = 1 ;
out_length + = get_modRM_size ( code ) + 1 ;
2015-01-04 21:46:31 +00:00
return ;
}
2015-02-15 12:31:42 +00:00
break ;
2015-01-04 21:46:31 +00:00
}
2015-02-15 12:31:42 +00:00
case 0xc7 :
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
if ( ! lock & & get_modRM_reg ( code , 0 ) = = X64R_RAX ) // MOV r/m, imm16/imm32 (16, 32, 64)
{
out_op = X64OP_STORE ;
out_reg = oso ? X64_IMM16 : X64_IMM32 ;
out_size = get_op_size ( rex , oso ) ;
out_length + = get_modRM_size ( code ) + ( oso ? 2 : 4 ) ;
return ;
}
break ;
2015-01-04 21:46:31 +00:00
}
}
2015-02-15 12:31:42 +00:00
2015-02-15 17:13:06 +00:00
LOG_WARNING ( GENERAL , " decode_x64_reg_op(%016llxh): unsupported opcode found (%016llX%016llX) " , ( size_t ) code - out_length , * ( be_t < u64 > * ) ( code - out_length ) , * ( be_t < u64 > * ) ( code - out_length + 8 ) ) ;
out_op = X64OP_NONE ;
2015-02-15 12:31:42 +00:00
out_reg = X64_NOT_SET ;
out_size = 0 ;
out_length = 0 ;
2015-01-04 21:46:31 +00:00
}
# ifdef _WIN32
2015-01-18 13:57:39 +00:00
typedef CONTEXT x64_context ;
2015-02-11 04:17:39 +00:00
# define X64REG(context, reg) (&(&context->Rax)[reg])
2015-01-18 13:57:39 +00:00
# else
typedef ucontext_t x64_context ;
2015-02-11 04:17:39 +00:00
# ifdef __APPLE__
# define X64REG(context, reg) (darwin_x64reg(context, reg))
uint64_t * darwin_x64reg ( x64_context * context , int reg )
{
2015-02-13 20:24:18 +00:00
auto * state = & context - > uc_mcontext - > __ss ;
switch ( reg )
{
case 0 : // RAX
return & state - > __rax ;
case 1 : // RCX
return & state - > __rcx ;
case 2 : // RDX
return & state - > __rdx ;
case 3 : // RBX
return & state - > __rbx ;
case 4 : // RSP
return & state - > __rsp ;
case 5 : // RBP
return & state - > __rbp ;
case 6 : // RSI
return & state - > __rsi ;
case 7 : // RDI
return & state - > __rdi ;
case 8 : // R8
return & state - > __r8 ;
case 9 : // R9
return & state - > __r9 ;
case 10 : // R10
return & state - > __r10 ;
case 11 : // R11
return & state - > __r11 ;
case 12 : // R12
return & state - > __r12 ;
case 13 : // R13
return & state - > __r13 ;
case 14 : // R14
return & state - > __r14 ;
case 15 : // R15
return & state - > __r15 ;
case 16 : // RIP
return & state - > __rip ;
default : // FAIL
assert ( 0 ) ;
}
2015-02-11 04:17:39 +00:00
}
# else
2015-02-13 20:24:18 +00:00
2015-02-11 04:17:39 +00:00
typedef decltype ( REG_RIP ) reg_table_t ;
2015-01-18 13:57:39 +00:00
static const reg_table_t reg_table [ 17 ] =
2015-01-04 21:46:31 +00:00
{
2015-01-18 13:57:39 +00:00
REG_RAX , REG_RCX , REG_RDX , REG_RBX , REG_RSP , REG_RBP , REG_RSI , REG_RDI ,
REG_R8 , REG_R9 , REG_R10 , REG_R11 , REG_R12 , REG_R13 , REG_R14 , REG_R15 , REG_RIP
} ;
2015-02-11 04:17:39 +00:00
# define X64REG(context, reg) (&context->uc_mcontext.gregs[reg_table[reg]])
2015-02-13 20:24:18 +00:00
2015-02-11 04:17:39 +00:00
# endif // __APPLE__
2015-01-18 13:57:39 +00:00
# endif
2015-02-15 17:13:06 +00:00
# define RAX(c) (*X64REG((c), 0))
# define RCX(c) (*X64REG((c), 1))
# define RDX(c) (*X64REG((c), 2))
# define RSI(c) (*X64REG((c), 6))
# define RDI(c) (*X64REG((c), 7))
# define RIP(c) (*X64REG((c), 16))
bool get_x64_reg_value ( x64_context * context , x64_reg_t reg , size_t d_size , size_t i_size , u64 & out_value )
{
// get x64 reg value (for store operations)
if ( reg - X64R_RAX < 16 )
{
// load the value from x64 register
const u64 reg_value = * X64REG ( context , reg - X64R_RAX ) ;
switch ( d_size )
{
case 1 : out_value = ( u8 ) reg_value ; return true ;
case 2 : out_value = ( u16 ) reg_value ; return true ;
case 4 : out_value = ( u32 ) reg_value ; return true ;
case 8 : out_value = reg_value ; return true ;
}
}
else if ( reg - X64R_AL < 4 & & d_size = = 1 )
{
out_value = ( u8 ) ( * X64REG ( context , reg - X64R_AL ) ) ;
2015-02-16 01:53:53 +00:00
return true ;
2015-02-15 17:13:06 +00:00
}
else if ( reg - X64R_AH < 4 & & d_size = = 1 )
{
out_value = ( u8 ) ( * X64REG ( context , reg - X64R_AH ) > > 8 ) ;
2015-02-16 01:53:53 +00:00
return true ;
2015-02-15 17:13:06 +00:00
}
else if ( reg = = X64_IMM32 )
{
// load the immediate value (assuming it's at the end of the instruction)
const s32 imm_value = * ( s32 * ) ( RIP ( context ) + i_size - 4 ) ;
switch ( d_size )
{
case 4 : out_value = ( u32 ) imm_value ; return true ;
case 8 : out_value = ( u64 ) imm_value ; return true ; // sign-extended
}
}
else if ( reg = = X64R_ECX )
{
out_value = ( u32 ) RCX ( context ) ;
2015-02-16 01:53:53 +00:00
return true ;
2015-02-15 17:13:06 +00:00
}
LOG_ERROR ( GENERAL , " get_x64_reg_value(): invalid arguments (reg=%d, d_size=%lld, i_size=%lld) " , reg , d_size , i_size ) ;
return false ;
}
bool put_x64_reg_value ( x64_context * context , x64_reg_t reg , size_t d_size , u64 value )
{
// save x64 reg value (for load operations)
if ( reg - X64R_RAX < 16 )
{
// store the value into x64 register
* X64REG ( context , reg - X64R_RAX ) = ( u32 ) value ;
return true ;
}
LOG_ERROR ( GENERAL , " put_x64_reg_value(): invalid destination (reg=%d, d_size=%lld, value=0x%llx) " , reg , d_size , value ) ;
return false ;
}
void fix_x64_reg_op ( x64_context * context , x64_op_t & op , x64_reg_t & reg , size_t & d_size , size_t & i_size )
{
2015-02-16 01:53:53 +00:00
if ( op = = X64OP_MOVS & & reg ! = X64_NOT_SET ) // get "full" access size from RCX register
2015-02-15 17:13:06 +00:00
{
u64 counter ;
if ( ! get_x64_reg_value ( context , reg , 8 , i_size , counter ) )
{
op = X64OP_NONE ;
reg = X64_NOT_SET ;
d_size = 0 ;
i_size = 0 ;
return ;
}
d_size * = counter ;
2015-02-16 01:53:53 +00:00
reg = X64_NOT_SET ;
return ;
2015-02-15 17:13:06 +00:00
}
}
2015-02-08 15:25:50 +00:00
bool handle_access_violation ( const u32 addr , bool is_writing , x64_context * context )
2015-01-18 13:57:39 +00:00
{
2015-02-15 17:13:06 +00:00
auto code = ( const u8 * ) RIP ( context ) ;
2015-02-15 12:31:42 +00:00
x64_op_t op ;
x64_reg_t reg ;
2015-02-15 17:13:06 +00:00
size_t d_size ;
size_t i_size ;
2015-02-15 12:31:42 +00:00
// decode single x64 instruction that causes memory access
2015-02-15 17:13:06 +00:00
decode_x64_reg_op ( code , op , reg , d_size , i_size ) ;
fix_x64_reg_op ( context , op , reg , d_size , i_size ) ;
2015-02-15 12:31:42 +00:00
2015-02-16 01:53:53 +00:00
if ( d_size + addr > = 0x100000000ull )
{
LOG_ERROR ( GENERAL , " Invalid d_size (0x%llx) " , d_size ) ;
return false ;
}
if ( op = = X64OP_CMPXCHG )
{
// detect whether this instruction can't actually modify memory to avoid breaking reservation;
// this may theoretically cause endless loop, but it shouldn't be a problem if only read_sync() generates such instruction
u64 cmp , exch ;
if ( ! get_x64_reg_value ( context , reg , d_size , i_size , cmp ) | | ! get_x64_reg_value ( context , X64R_RAX , d_size , i_size , exch ) )
{
return false ;
}
if ( cmp = = exch )
{
// could also be emulated without attempt to write memory
is_writing = false ;
}
}
2015-02-06 23:39:51 +00:00
// check if address is RawSPU MMIO register
if ( addr - RAW_SPU_BASE_ADDR < ( 6 * RAW_SPU_OFFSET ) & & ( addr % RAW_SPU_OFFSET ) > = RAW_SPU_PROB_OFFSET )
2015-01-04 21:46:31 +00:00
{
2015-02-15 12:31:42 +00:00
if ( d_size ! = 4 | | ! i_size )
{
2015-02-15 17:13:06 +00:00
LOG_ERROR ( GENERAL , " Invalid instruction (op=%d, reg=%d, d_size=%lld, i_size=%lld) " , op , reg , d_size , i_size ) ;
2015-02-15 12:31:42 +00:00
return false ;
2015-01-18 13:57:39 +00:00
}
switch ( op )
{
case X64OP_LOAD :
{
2015-02-15 17:13:06 +00:00
u32 value ;
if ( is_writing | | ! Memory . ReadMMIO32 ( addr , value ) | | ! put_x64_reg_value ( context , reg , d_size , re32 ( value ) ) )
{
return false ;
}
2015-01-18 13:57:39 +00:00
break ;
}
case X64OP_STORE :
{
2015-02-15 17:13:06 +00:00
u64 reg_value ;
if ( ! is_writing | | ! get_x64_reg_value ( context , reg , d_size , i_size , reg_value ) | | ! Memory . WriteMMIO32 ( addr , re32 ( ( u32 ) reg_value ) ) )
{
return false ;
}
2015-01-18 13:57:39 +00:00
break ;
}
2015-02-15 17:13:06 +00:00
case X64OP_MOVS : // TODO
2015-02-15 12:31:42 +00:00
default :
{
LOG_ERROR ( GENERAL , " Invalid operation (op=%d) " , op ) ;
return false ;
}
2015-01-18 13:57:39 +00:00
}
2015-02-15 17:13:06 +00:00
// skip processed instruction
RIP ( context ) + = i_size ;
2015-01-18 13:57:39 +00:00
return true ;
}
2015-01-04 21:46:31 +00:00
2015-02-06 23:39:51 +00:00
// check if fault is caused by reservation
2015-02-16 01:53:53 +00:00
if ( vm : : reservation_query ( addr , ( u32 ) d_size , is_writing ) )
2015-02-06 23:39:51 +00:00
{
return true ;
}
2015-01-18 13:57:39 +00:00
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
return false ;
}
2015-01-04 21:46:31 +00:00
2015-01-18 13:57:39 +00:00
# ifdef _WIN32
2015-01-04 21:46:31 +00:00
2015-01-18 13:57:39 +00:00
void _se_translator ( unsigned int u , EXCEPTION_POINTERS * pExp )
{
2015-02-08 13:38:08 +00:00
const u64 addr64 = ( u64 ) pExp - > ExceptionRecord - > ExceptionInformation [ 1 ] - ( u64 ) vm : : g_base_addr ;
2015-01-18 13:57:39 +00:00
const bool is_writing = pExp - > ExceptionRecord - > ExceptionInformation [ 0 ] ! = 0 ;
2015-02-08 13:38:08 +00:00
2015-01-18 13:57:39 +00:00
if ( u = = EXCEPTION_ACCESS_VIOLATION & & ( u32 ) addr64 = = addr64 )
{
2015-01-18 22:54:56 +00:00
throw fmt : : format ( " Access violation %s location 0x%llx " , is_writing ? " writing " : " reading " , addr64 ) ;
2015-01-04 21:46:31 +00:00
}
}
2015-02-08 13:38:08 +00:00
const PVOID exception_handler = ( atexit ( [ ] { RemoveVectoredExceptionHandler ( exception_handler ) ; } ) , AddVectoredExceptionHandler ( 1 , [ ] ( PEXCEPTION_POINTERS pExp ) - > LONG
2015-02-06 23:39:51 +00:00
{
2015-02-08 13:38:08 +00:00
const u64 addr64 = ( u64 ) pExp - > ExceptionRecord - > ExceptionInformation [ 1 ] - ( u64 ) vm : : g_base_addr ;
2015-02-08 15:25:50 +00:00
const bool is_writing = pExp - > ExceptionRecord - > ExceptionInformation [ 0 ] ! = 0 ;
2015-02-06 23:39:51 +00:00
2015-02-08 13:38:08 +00:00
if ( pExp - > ExceptionRecord - > ExceptionCode = = EXCEPTION_ACCESS_VIOLATION & &
( u32 ) addr64 = = addr64 & &
GetCurrentNamedThread ( ) & &
2015-02-08 15:25:50 +00:00
handle_access_violation ( ( u32 ) addr64 , is_writing , pExp - > ContextRecord ) )
2015-02-06 23:39:51 +00:00
{
2015-02-08 13:38:08 +00:00
return EXCEPTION_CONTINUE_EXECUTION ;
2015-02-06 23:39:51 +00:00
}
else
{
return EXCEPTION_CONTINUE_SEARCH ;
}
2015-02-08 13:38:08 +00:00
} ) ) ;
2015-02-06 23:39:51 +00:00
2015-01-04 21:46:31 +00:00
# else
2015-01-04 22:29:51 +00:00
void signal_handler ( int sig , siginfo_t * info , void * uct )
2015-01-04 21:46:31 +00:00
{
2015-02-08 13:38:08 +00:00
const u64 addr64 = ( u64 ) info - > si_addr - ( u64 ) vm : : g_base_addr ;
2015-02-11 04:17:39 +00:00
# ifdef __APPLE__
2015-02-13 20:24:18 +00:00
const bool is_writing = ( ( ucontext_t * ) uct ) - > uc_mcontext - > __es . __err & 0x2 ;
2015-02-11 04:17:39 +00:00
# else
2015-02-08 13:38:08 +00:00
const bool is_writing = ( ( ucontext_t * ) uct ) - > uc_mcontext . gregs [ REG_ERR ] & 0x2 ;
2015-02-11 04:17:39 +00:00
# endif
2015-02-08 13:38:08 +00:00
2015-01-18 13:57:39 +00:00
if ( ( u32 ) addr64 = = addr64 & & GetCurrentNamedThread ( ) )
2015-01-04 21:46:31 +00:00
{
2015-02-08 15:25:50 +00:00
if ( handle_access_violation ( ( u32 ) addr64 , is_writing , ( ucontext_t * ) uct ) )
2015-01-04 21:46:31 +00:00
{
2015-01-18 13:57:39 +00:00
return ; // proceed execution
2015-01-04 21:46:31 +00:00
}
2015-01-18 13:57:39 +00:00
// TODO: this may be wrong
2015-02-08 13:38:08 +00:00
throw fmt : : format ( " Access violation %s location 0x%llx " , is_writing ? " writing " : " reading " , addr64 ) ;
2015-01-04 21:46:31 +00:00
}
2015-01-08 22:17:26 +00:00
// else some fatal error
exit ( EXIT_FAILURE ) ;
2015-01-04 21:46:31 +00:00
}
const int sigaction_result = [ ] ( ) - > int
{
struct sigaction sa ;
sa . sa_flags = SA_SIGINFO ;
sigemptyset ( & sa . sa_mask ) ;
sa . sa_sigaction = signal_handler ;
return sigaction ( SIGSEGV , & sa , NULL ) ;
2015-01-04 22:29:51 +00:00
} ( ) ;
2015-01-04 21:46:31 +00:00
# endif
2014-06-19 13:50:18 +00:00
thread_local NamedThreadBase * g_tls_this_thread = nullptr ;
2014-06-20 11:00:36 +00:00
std : : atomic < u32 > g_thread_count ( 0 ) ;
2014-01-31 18:40:18 +00:00
NamedThreadBase * GetCurrentNamedThread ( )
{
2014-02-02 19:42:32 +00:00
return g_tls_this_thread ;
2013-06-30 08:46:29 +00:00
}
2014-08-20 14:23:48 +00:00
void SetCurrentNamedThread ( NamedThreadBase * value )
{
2015-01-16 14:36:53 +00:00
const auto old_value = g_tls_this_thread ;
2014-09-14 22:17:24 +00:00
if ( old_value = = value )
{
return ;
}
2015-02-06 23:39:51 +00:00
if ( old_value )
{
vm : : reservation_free ( ) ;
}
2014-09-14 22:17:24 +00:00
if ( value & & value - > m_tls_assigned . exchange ( true ) )
{
2015-01-18 22:54:56 +00:00
LOG_ERROR ( GENERAL , " Thread '%s' was already assigned to g_tls_this_thread of another thread " , value - > GetThreadName ( ) ) ;
2014-09-14 22:17:24 +00:00
g_tls_this_thread = nullptr ;
}
else
{
g_tls_this_thread = value ;
}
if ( old_value )
{
old_value - > m_tls_assigned = false ;
}
2014-08-20 14:23:48 +00:00
}
2014-01-31 18:40:18 +00:00
std : : string NamedThreadBase : : GetThreadName ( ) const
2013-06-30 08:46:29 +00:00
{
2014-01-31 18:40:18 +00:00
return m_name ;
2013-06-30 08:46:29 +00:00
}
2014-01-31 18:40:18 +00:00
void NamedThreadBase : : SetThreadName ( const std : : string & name )
2012-11-14 23:39:56 +00:00
{
2014-01-31 18:40:18 +00:00
m_name = name ;
}
2012-11-14 23:39:56 +00:00
2014-09-12 19:27:33 +00:00
void NamedThreadBase : : WaitForAnySignal ( u64 time ) // wait for Notify() signal or sleep
2014-08-25 18:09:48 +00:00
{
std : : unique_lock < std : : mutex > lock ( m_signal_mtx ) ;
2014-09-12 19:27:33 +00:00
m_signal_cv . wait_for ( lock , std : : chrono : : milliseconds ( time ) ) ;
2014-08-25 18:09:48 +00:00
}
void NamedThreadBase : : Notify ( ) // wake up waiting thread or nothing
{
m_signal_cv . notify_one ( ) ;
}
2014-01-31 18:40:18 +00:00
ThreadBase : : ThreadBase ( const std : : string & name )
: NamedThreadBase ( name )
, m_executor ( nullptr )
, m_destroy ( false )
, m_alive ( false )
{
2012-11-14 23:39:56 +00:00
}
2014-01-31 18:40:18 +00:00
ThreadBase : : ~ ThreadBase ( )
2013-06-30 08:46:29 +00:00
{
2014-01-31 18:40:18 +00:00
if ( IsAlive ( ) )
Stop ( false ) ;
2014-04-15 14:12:15 +00:00
2014-08-25 18:09:48 +00:00
delete m_executor ;
m_executor = nullptr ;
2013-06-30 08:46:29 +00:00
}
2014-01-31 18:40:18 +00:00
void ThreadBase : : Start ( )
2013-06-30 08:46:29 +00:00
{
2014-01-31 18:40:18 +00:00
if ( m_executor ) Stop ( ) ;
std : : lock_guard < std : : mutex > lock ( m_main_mutex ) ;
m_destroy = false ;
m_alive = true ;
2014-08-20 14:23:48 +00:00
m_executor = new std : : thread ( [ this ] ( )
{
2014-12-05 16:12:15 +00:00
SetCurrentThreadDebugName ( GetThreadName ( ) . c_str ( ) ) ;
2015-01-04 21:46:31 +00:00
# ifdef _WIN32
auto old_se_translator = _set_se_translator ( _se_translator ) ;
2015-02-08 13:38:08 +00:00
if ( ! exception_handler )
{
LOG_ERROR ( GENERAL , " exception_handler not set " ) ;
return ;
}
2015-01-04 21:46:31 +00:00
# else
2015-02-08 13:38:08 +00:00
if ( sigaction_result = = - 1 )
{
printf ( " sigaction() failed " ) ;
exit ( EXIT_FAILURE ) ;
}
2015-01-04 21:46:31 +00:00
# endif
2014-08-20 14:23:48 +00:00
SetCurrentNamedThread ( this ) ;
g_thread_count + + ;
2014-09-11 19:18:19 +00:00
try
{
Task ( ) ;
}
catch ( const char * e )
{
2014-09-14 22:17:24 +00:00
LOG_ERROR ( GENERAL , " %s: %s " , GetThreadName ( ) . c_str ( ) , e ) ;
2014-09-11 19:18:19 +00:00
}
catch ( const std : : string & e )
{
2014-09-14 22:17:24 +00:00
LOG_ERROR ( GENERAL , " %s: %s " , GetThreadName ( ) . c_str ( ) , e . c_str ( ) ) ;
2014-09-11 19:18:19 +00:00
}
2014-08-20 14:23:48 +00:00
m_alive = false ;
2014-09-14 22:17:24 +00:00
SetCurrentNamedThread ( nullptr ) ;
2014-08-20 14:23:48 +00:00
g_thread_count - - ;
2015-01-04 21:46:31 +00:00
# ifdef _WIN32
_set_se_translator ( old_se_translator ) ;
# endif
2014-08-20 14:23:48 +00:00
} ) ;
2013-06-30 08:46:29 +00:00
}
2014-02-14 19:50:02 +00:00
void ThreadBase : : Stop ( bool wait , bool send_destroy )
2012-11-14 23:39:56 +00:00
{
2014-01-31 18:40:18 +00:00
std : : lock_guard < std : : mutex > lock ( m_main_mutex ) ;
2013-06-30 08:46:29 +00:00
2014-02-14 19:50:02 +00:00
if ( send_destroy )
m_destroy = true ;
2013-06-30 08:46:29 +00:00
2014-01-31 18:40:18 +00:00
if ( ! m_executor )
return ;
if ( wait & & m_executor - > joinable ( ) & & m_alive )
{
m_executor - > join ( ) ;
2013-06-30 08:46:29 +00:00
}
else
{
2014-01-31 18:40:18 +00:00
m_executor - > detach ( ) ;
2013-06-30 08:46:29 +00:00
}
2014-01-31 18:40:18 +00:00
delete m_executor ;
m_executor = nullptr ;
2012-11-14 23:39:56 +00:00
}
2014-01-31 18:40:18 +00:00
bool ThreadBase : : Join ( ) const
2013-06-30 08:46:29 +00:00
{
2014-01-31 18:40:18 +00:00
std : : lock_guard < std : : mutex > lock ( m_main_mutex ) ;
if ( m_executor - > joinable ( ) & & m_alive & & m_executor ! = nullptr )
{
m_executor - > join ( ) ;
return true ;
}
return false ;
2013-06-30 08:46:29 +00:00
}
2014-01-31 18:40:18 +00:00
bool ThreadBase : : IsAlive ( ) const
2013-06-30 08:46:29 +00:00
{
2014-01-31 18:40:18 +00:00
std : : lock_guard < std : : mutex > lock ( m_main_mutex ) ;
return m_alive ;
2013-06-30 08:46:29 +00:00
}
2014-01-31 18:40:18 +00:00
bool ThreadBase : : TestDestroy ( ) const
2013-06-30 08:46:29 +00:00
{
2014-01-31 18:40:18 +00:00
return m_destroy ;
2013-06-30 08:46:29 +00:00
}
2015-01-17 16:14:58 +00:00
thread_t : : thread_t ( const std : : string & name , bool autojoin , std : : function < void ( ) > func )
: m_name ( name )
, m_state ( TS_NON_EXISTENT )
, m_autojoin ( autojoin )
2012-11-14 23:39:56 +00:00
{
2014-01-31 18:40:18 +00:00
start ( func ) ;
2012-11-14 23:39:56 +00:00
}
2015-01-17 16:14:58 +00:00
thread_t : : thread_t ( const std : : string & name , std : : function < void ( ) > func )
: m_name ( name )
, m_state ( TS_NON_EXISTENT )
, m_autojoin ( false )
2012-11-14 23:39:56 +00:00
{
2015-01-17 16:14:58 +00:00
start ( func ) ;
2014-01-31 18:40:18 +00:00
}
2012-11-14 23:39:56 +00:00
2015-01-17 16:14:58 +00:00
thread_t : : thread_t ( const std : : string & name )
: m_name ( name )
, m_state ( TS_NON_EXISTENT )
, m_autojoin ( false )
{
}
thread_t : : thread_t ( )
: m_state ( TS_NON_EXISTENT )
, m_autojoin ( false )
2014-01-31 18:40:18 +00:00
{
2013-06-30 08:46:29 +00:00
}
2015-01-16 14:36:53 +00:00
void thread_t : : set_name ( const std : : string & name )
2014-02-27 18:25:32 +00:00
{
2015-01-16 14:36:53 +00:00
m_name = name ;
}
2014-02-27 18:25:32 +00:00
2015-01-16 14:36:53 +00:00
thread_t : : ~ thread_t ( )
{
if ( m_state = = TS_JOINABLE )
{
2015-01-17 16:14:58 +00:00
if ( m_autojoin )
{
m_thr . join ( ) ;
}
else
{
m_thr . detach ( ) ;
}
2015-01-16 14:36:53 +00:00
}
}
void thread_t : : start ( std : : function < void ( ) > func )
{
if ( m_state . exchange ( TS_NON_EXISTENT ) = = TS_JOINABLE )
{
m_thr . join ( ) ; // forcefully join previously created thread
}
std : : string name = m_name ;
2014-02-27 18:25:32 +00:00
m_thr = std : : thread ( [ func , name ] ( )
2014-02-19 17:27:52 +00:00
{
2014-12-05 16:12:15 +00:00
SetCurrentThreadDebugName ( name . c_str ( ) ) ;
2015-01-04 21:46:31 +00:00
# ifdef _WIN32
auto old_se_translator = _set_se_translator ( _se_translator ) ;
# endif
2014-02-27 18:25:32 +00:00
NamedThreadBase info ( name ) ;
2014-08-20 14:23:48 +00:00
SetCurrentNamedThread ( & info ) ;
2014-06-19 13:50:18 +00:00
g_thread_count + + ;
2014-02-22 12:06:23 +00:00
2015-01-16 14:36:53 +00:00
if ( Ini . HLELogging . GetValue ( ) )
{
LOG_NOTICE ( HLE , name + " started " ) ;
}
2014-09-11 19:18:19 +00:00
try
{
func ( ) ;
}
catch ( const char * e )
{
2014-09-14 22:17:24 +00:00
LOG_ERROR ( GENERAL , " %s: %s " , name . c_str ( ) , e ) ;
2014-09-11 19:18:19 +00:00
}
catch ( const std : : string & e )
{
2014-09-14 22:17:24 +00:00
LOG_ERROR ( GENERAL , " %s: %s " , name . c_str ( ) , e . c_str ( ) ) ;
2014-09-11 19:18:19 +00:00
}
2014-06-19 13:50:18 +00:00
2015-01-16 14:36:53 +00:00
if ( Emu . IsStopped ( ) )
{
LOG_NOTICE ( HLE , name + " aborted " ) ;
}
else if ( Ini . HLELogging . GetValue ( ) )
{
LOG_NOTICE ( HLE , name + " ended " ) ;
}
2014-09-14 22:17:24 +00:00
SetCurrentNamedThread ( nullptr ) ;
2014-06-19 13:50:18 +00:00
g_thread_count - - ;
2015-01-04 21:46:31 +00:00
# ifdef _WIN32
_set_se_translator ( old_se_translator ) ;
# endif
2014-02-19 17:27:52 +00:00
} ) ;
2015-01-16 14:36:53 +00:00
if ( m_state . exchange ( TS_JOINABLE ) = = TS_JOINABLE )
{
assert ( ! " thread_t::start() failed " ) ; // probably started from another thread
}
2013-06-30 08:46:29 +00:00
}
2015-01-16 14:36:53 +00:00
void thread_t : : detach ( )
2013-06-30 08:46:29 +00:00
{
2015-01-16 14:36:53 +00:00
if ( m_state . exchange ( TS_NON_EXISTENT ) = = TS_JOINABLE )
{
m_thr . detach ( ) ;
}
else
{
assert ( ! " thread_t::detach() failed " ) ; // probably joined or detached
}
2014-01-31 18:40:18 +00:00
}
2015-01-16 14:36:53 +00:00
void thread_t : : join ( )
2014-01-31 18:40:18 +00:00
{
2015-01-16 14:36:53 +00:00
if ( m_state . exchange ( TS_NON_EXISTENT ) = = TS_JOINABLE )
{
m_thr . join ( ) ;
}
else
{
assert ( ! " thread_t::join() failed " ) ; // probably joined or detached
}
2014-01-31 18:40:18 +00:00
}
2015-01-16 14:36:53 +00:00
bool thread_t : : joinable ( ) const
2014-01-31 18:40:18 +00:00
{
2015-01-16 14:36:53 +00:00
//return m_thr.joinable();
return m_state = = TS_JOINABLE ;
2014-02-23 16:52:52 +00:00
}
2014-10-10 21:33:57 +00:00
2014-10-16 16:29:41 +00:00
bool waiter_map_t : : is_stopped ( u64 signal_id )
2014-10-10 21:33:57 +00:00
{
if ( Emu . IsStopped ( ) )
{
2014-12-28 13:15:22 +00:00
LOG_WARNING ( Log : : HLE , " %s: waiter_op() aborted (signal_id=0x%llx) " , m_name . c_str ( ) , signal_id ) ;
2014-10-10 21:33:57 +00:00
return true ;
}
return false ;
}
2014-10-17 20:13:25 +00:00
void waiter_map_t : : waiter_reg_t : : init ( )
2014-10-10 21:33:57 +00:00
{
2014-12-25 22:49:55 +00:00
if ( ! thread )
{
thread = GetCurrentNamedThread ( ) ;
2014-10-17 20:13:25 +00:00
2014-12-25 22:49:55 +00:00
std : : lock_guard < std : : mutex > lock ( map . m_mutex ) ;
2014-10-10 21:33:57 +00:00
2014-12-25 22:49:55 +00:00
// add waiter
map . m_waiters . push_back ( { signal_id , thread } ) ;
}
2014-10-10 21:33:57 +00:00
}
2014-10-16 16:29:41 +00:00
waiter_map_t : : waiter_reg_t : : ~ waiter_reg_t ( )
2014-10-10 21:33:57 +00:00
{
2014-12-25 22:49:55 +00:00
if ( thread )
2014-10-10 21:33:57 +00:00
{
2014-12-25 22:49:55 +00:00
std : : lock_guard < std : : mutex > lock ( map . m_mutex ) ;
// remove waiter
for ( s64 i = map . m_waiters . size ( ) - 1 ; i > = 0 ; i - - )
2014-10-10 21:33:57 +00:00
{
2014-12-25 22:49:55 +00:00
if ( map . m_waiters [ i ] . signal_id = = signal_id & & map . m_waiters [ i ] . thread = = thread )
{
map . m_waiters . erase ( map . m_waiters . begin ( ) + i ) ;
return ;
}
2014-10-10 21:33:57 +00:00
}
2014-10-16 16:29:41 +00:00
2014-12-25 22:49:55 +00:00
LOG_ERROR ( HLE , " %s(): waiter not found (signal_id=0x%llx, map='%s') " , __FUNCTION__ , signal_id , map . m_name . c_str ( ) ) ;
Emu . Pause ( ) ;
}
2014-10-10 21:33:57 +00:00
}
2014-10-16 16:29:41 +00:00
void waiter_map_t : : notify ( u64 signal_id )
2014-10-10 21:33:57 +00:00
{
2014-12-25 22:49:55 +00:00
if ( m_waiters . size ( ) )
2014-10-10 21:33:57 +00:00
{
2014-12-25 22:49:55 +00:00
std : : lock_guard < std : : mutex > lock ( m_mutex ) ;
// find waiter and signal
for ( auto & v : m_waiters )
2014-10-10 21:33:57 +00:00
{
2014-12-25 22:49:55 +00:00
if ( v . signal_id = = signal_id )
{
v . thread - > Notify ( ) ;
}
2014-10-10 21:33:57 +00:00
}
}
}
2014-12-25 20:30:34 +00:00
2015-01-17 16:36:23 +00:00
const std : : function < bool ( ) > SQUEUE_ALWAYS_EXIT = [ ] ( ) { return true ; } ;
const std : : function < bool ( ) > SQUEUE_NEVER_EXIT = [ ] ( ) { return false ; } ;
2015-01-17 16:14:58 +00:00
2015-01-16 17:09:53 +00:00
bool squeue_test_exit ( )
2014-12-25 20:30:34 +00:00
{
2015-01-16 17:09:53 +00:00
return Emu . IsStopped ( ) ;
2014-12-25 20:30:34 +00:00
}