1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-26 04:32:35 +01:00

Merge pull request #996 from Nekotekina/master

Reservations improved
This commit is contained in:
B1ackDaemon 2015-02-08 22:08:09 +02:00
commit 4ba915bbdd
8 changed files with 97 additions and 87 deletions

View File

@ -213,7 +213,7 @@ static const reg_table_t reg_table[17] =
#endif
bool handle_access_violation(const u32 addr, x64_context* context)
bool handle_access_violation(const u32 addr, bool is_writing, x64_context* context)
{
// 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)
@ -279,7 +279,7 @@ bool handle_access_violation(const u32 addr, x64_context* context)
}
// check if fault is caused by reservation
if (vm::reservation_query(addr))
if (vm::reservation_query(addr, is_writing))
{
return true;
}
@ -292,56 +292,49 @@ bool handle_access_violation(const u32 addr, x64_context* context)
void _se_translator(unsigned int u, EXCEPTION_POINTERS* pExp)
{
const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)Memory.GetBaseAddr();
const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::g_base_addr;
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
if (u == EXCEPTION_ACCESS_VIOLATION && (u32)addr64 == addr64)
{
if (handle_access_violation((u32)addr64, pExp->ContextRecord))
{
// restore context (further code shouldn't be reached)
RtlRestoreContext(pExp->ContextRecord, nullptr);
// it's dangerous because destructors won't be executed
}
throw fmt::format("Access violation %s location 0x%llx", is_writing ? "writing" : "reading", addr64);
}
// else some fatal error (should crash)
}
extern LPTOP_LEVEL_EXCEPTION_FILTER filter_set;
LONG __stdcall exception_filter(_EXCEPTION_POINTERS* pExp)
const PVOID exception_handler = (atexit([]{ RemoveVectoredExceptionHandler(exception_handler); }), AddVectoredExceptionHandler(1, [](PEXCEPTION_POINTERS pExp) -> LONG
{
_se_translator(pExp->ExceptionRecord->ExceptionCode, pExp);
const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::g_base_addr;
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
if (filter_set)
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
(u32)addr64 == addr64 &&
GetCurrentNamedThread() &&
handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
{
return filter_set(pExp);
return EXCEPTION_CONTINUE_EXECUTION;
}
else
{
return EXCEPTION_CONTINUE_SEARCH;
}
}
LPTOP_LEVEL_EXCEPTION_FILTER filter_set = SetUnhandledExceptionFilter(exception_filter);
}));
#else
void signal_handler(int sig, siginfo_t* info, void* uct)
{
const u64 addr64 = (u64)info->si_addr - (u64)Memory.GetBaseAddr();
const u64 addr64 = (u64)info->si_addr - (u64)vm::g_base_addr;
const bool is_writing = ((ucontext_t*)uct)->uc_mcontext.gregs[REG_ERR] & 0x2;
if ((u32)addr64 == addr64 && GetCurrentNamedThread())
{
if (handle_access_violation((u32)addr64, (ucontext_t*)uct))
if (handle_access_violation((u32)addr64, is_writing, (ucontext_t*)uct))
{
return; // proceed execution
}
// TODO: this may be wrong
throw fmt::format("Access violation at location 0x%llx", addr64);
throw fmt::format("Access violation %s location 0x%llx", is_writing ? "writing" : "reading", addr64);
}
// else some fatal error
@ -451,8 +444,17 @@ void ThreadBase::Start()
#ifdef _WIN32
auto old_se_translator = _set_se_translator(_se_translator);
if (!exception_handler)
{
LOG_ERROR(GENERAL, "exception_handler not set");
return;
}
#else
if (sigaction_result == -1) assert(!"sigaction() failed");
if (sigaction_result == -1)
{
printf("sigaction() failed");
exit(EXIT_FAILURE);
}
#endif
SetCurrentNamedThread(this);
@ -590,8 +592,6 @@ void thread_t::start(std::function<void()> func)
#ifdef _WIN32
auto old_se_translator = _set_se_translator(_se_translator);
#else
if (sigaction_result == -1) assert(!"sigaction() failed");
#endif
NamedThreadBase info(name);

View File

@ -2911,9 +2911,7 @@ private:
}
void ECIWX(u32 rd, u32 ra, u32 rb)
{
//HACK!
const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb];
CPU.GPR[rd] = vm::read32(vm::cast(addr));
throw __FUNCTION__;
}
void LHZUX(u32 rd, u32 ra, u32 rb)
{
@ -2986,9 +2984,7 @@ private:
}
void ECOWX(u32 rs, u32 ra, u32 rb)
{
//HACK!
const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb];
vm::write32(vm::cast(addr), (u32)CPU.GPR[rs]);
throw __FUNCTION__;
}
void STHUX(u32 rs, u32 ra, u32 rb)
{

View File

@ -3271,15 +3271,16 @@ void Compiler::EQV(u32 ra, u32 rs, u32 rb, bool rc) {
}
void Compiler::ECIWX(u32 rd, u32 ra, u32 rb) {
auto addr_i64 = GetGpr(rb);
if (ra) {
auto ra_i64 = GetGpr(ra);
addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64);
}
throw __FUNCTION__;
//auto addr_i64 = GetGpr(rb);
//if (ra) {
// auto ra_i64 = GetGpr(ra);
// addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64);
//}
auto mem_i32 = ReadMemory(addr_i64, 32);
auto mem_i64 = m_ir_builder->CreateZExt(mem_i32, m_ir_builder->getInt64Ty());
SetGpr(rd, mem_i64);
//auto mem_i32 = ReadMemory(addr_i64, 32);
//auto mem_i64 = m_ir_builder->CreateZExt(mem_i32, m_ir_builder->getInt64Ty());
//SetGpr(rd, mem_i64);
}
void Compiler::LHZUX(u32 rd, u32 ra, u32 rb) {
@ -3430,13 +3431,14 @@ void Compiler::ORC(u32 ra, u32 rs, u32 rb, bool rc) {
}
void Compiler::ECOWX(u32 rs, u32 ra, u32 rb) {
auto addr_i64 = GetGpr(rb);
if (ra) {
auto ra_i64 = GetGpr(ra);
addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64);
}
throw __FUNCTION__;
//auto addr_i64 = GetGpr(rb);
//if (ra) {
// auto ra_i64 = GetGpr(ra);
// addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64);
//}
WriteMemory(addr_i64, GetGpr(rs, 32));
//WriteMemory(addr_i64, GetGpr(rs, 32));
}
void Compiler::STHUX(u32 rs, u32 ra, u32 rb) {

View File

@ -724,8 +724,8 @@ void Compiler::RunAllTests() {
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LHZU, 0, input, 5, 14, 0x10000);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LHZX, 0, input, 5, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LHZX, 1, input, 5, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECIWX, 0, input, 5, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECIWX, 1, input, 5, 14, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECIWX, 0, input, 5, 0, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECIWX, 1, input, 5, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LHZUX, 0, input, 5, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LHA, 0, input, 5, 0, 0x100F0);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LHA, 1, input, 5, 14, 0x100F0);
@ -765,10 +765,10 @@ void Compiler::RunAllTests() {
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LFDX, 0, input, 5, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LFDX, 1, input, 5, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LFDUX, 0, input, 5, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LWARX, 0, input, 5, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LWARX, 1, input, 5, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LDARX, 0, input, 5, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LDARX, 1, input, 5, 14, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LWARX, 0, input, 5, 0, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LWARX, 1, input, 5, 14, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LDARX, 0, input, 5, 0, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LDARX, 1, input, 5, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LSWI, 0, input, 5, 23, 0);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LSWI, 1, input, 5, 23, 2);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(LSWI, 2, input, 5, 23, 7);
@ -804,8 +804,8 @@ void Compiler::RunAllTests() {
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STB, 0, input, 3, 0, 0x10000);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STB, 1, input, 3, 14, 0x10000);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STBU, 0, input, 3, 14, 0x10000);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDCX_, 0, input, 3, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDCX_, 1, input, 3, 14, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDCX_, 0, input, 3, 0, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDCX_, 1, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STBX, 0, input, 3, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STBX, 1, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STBUX, 0, input, 3, 14, 23);
@ -814,8 +814,8 @@ void Compiler::RunAllTests() {
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STHU, 0, input, 3, 14, 0x10000);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STHX, 0, input, 3, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STHX, 1, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECOWX, 0, input, 3, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECOWX, 1, input, 3, 14, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECOWX, 0, input, 3, 0, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(ECOWX, 1, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STHUX, 0, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STHBRX, 0, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STW, 0, input, 3, 0, 0x10000);
@ -833,8 +833,8 @@ void Compiler::RunAllTests() {
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDU, 0, input, 3, 14, 0x10000);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDX, 0, input, 3, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDX, 1, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STWCX_, 0, input, 3, 0, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STWCX_, 1, input, 3, 14, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STWCX_, 0, input, 3, 0, 23);
//VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STWCX_, 1, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STDUX, 0, input, 3, 14, 23);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STFS, 0, input, 3, 0, 0x10000);
VERIFY_INSTRUCTION_AGAINST_INTERPRETER(STFS, 1, input, 3, 14, 0x10000);

View File

@ -468,9 +468,10 @@ void SPUThread::EnqMfcCmd(MFCReg& MFCArgs)
}
else // store unconditional (may be wrong)
{
vm::reservation_break(ea);
ProcessCmd(MFC_PUT_CMD, tag, lsa, ea, 128);
vm::reservation_op(ea, 128, [this, tag, lsa, ea]()
{
ProcessCmd(MFC_PUT_CMD, tag, lsa, ea, 128);
});
if (op == MFC_PUTLLUC_CMD)
{

View File

@ -72,11 +72,6 @@ public:
Close();
}
static void* const GetBaseAddr()
{
return vm::g_base_addr;
}
void RegisterPages(u64 addr, u32 size);
void UnregisterPages(u64 addr, u32 size);

View File

@ -137,18 +137,19 @@ namespace vm
NamedThreadBase* g_reservation_owner = nullptr;
u32 g_reservation_addr = 0;
u32 g_reservation_size = 0;
reservation_mutex_t g_reservation_mutex;
void _reservation_set(u32 addr)
void _reservation_set(u32 addr, bool no_access = false)
{
//const auto stamp0 = get_time();
#ifdef _WIN32
DWORD old;
if (!VirtualProtect(vm::get_ptr(addr & ~0xfff), 4096, PAGE_READONLY, &old))
if (!VirtualProtect(vm::get_ptr(addr & ~0xfff), 4096, no_access ? PAGE_NOACCESS : PAGE_READONLY, &old))
#else
if (mprotect(vm::get_ptr(addr & ~0xfff), 4096, PROT_READ))
if (mprotect(vm::get_ptr(addr & ~0xfff), 4096, no_access ? PROT_NONE : PROT_READ))
#endif
{
throw fmt::format("vm::_reservation_set() failed (addr=0x%x)", addr);
@ -183,6 +184,7 @@ namespace vm
g_reservation_owner = nullptr;
g_reservation_addr = 0;
g_reservation_size = 0;
return true;
}
@ -204,7 +206,7 @@ namespace vm
bool broken = false;
assert(size == 1 || size == 2 || size == 4 || size == 8 || size == 128);
assert((addr + size & ~0xfff) == (addr & ~0xfff));
assert((addr + size - 1 & ~0xfff) == (addr & ~0xfff));
{
std::lock_guard<reservation_mutex_t> lock(g_reservation_mutex);
@ -213,7 +215,7 @@ namespace vm
//g_reservation_mutex.do_notify = false;
// break previous reservation
if (g_reservation_addr)
if (g_reservation_owner)
{
broken = _reservation_break(g_reservation_addr);
}
@ -221,11 +223,12 @@ namespace vm
// change memory protection to read-only
_reservation_set(addr);
// may not be necessary, just for sure:
// may not be necessary
_mm_mfence();
// set additional information
g_reservation_addr = addr;
g_reservation_size = size;
g_reservation_owner = GetCurrentNamedThread();
g_reservation_cb = callback;
@ -239,19 +242,25 @@ namespace vm
bool reservation_update(u32 addr, const void* data, u32 size)
{
assert(size == 1 || size == 2 || size == 4 || size == 8 || size == 128);
assert((addr + size & ~0xfff) == (addr & ~0xfff));
assert((addr + size - 1 & ~0xfff) == (addr & ~0xfff));
std::lock_guard<reservation_mutex_t> lock(g_reservation_mutex);
if (g_reservation_addr != addr || g_reservation_owner != GetCurrentNamedThread())
if (g_reservation_owner != GetCurrentNamedThread() || g_reservation_addr != addr || g_reservation_size != size)
{
// atomic update failed
return false;
}
// change memory protection to no access
_reservation_set(addr, true);
// update memory using privileged access
memcpy(vm::get_priv_ptr(addr), data, size);
// remove callback to not call it on successful update
g_reservation_cb = nullptr;
// free the reservation and restore memory protection
_reservation_break(addr);
@ -259,7 +268,7 @@ namespace vm
return true;
}
bool reservation_query(u32 addr)
bool reservation_query(u32 addr, bool is_writing)
{
std::lock_guard<reservation_mutex_t> lock(g_reservation_mutex);
@ -272,9 +281,12 @@ namespace vm
}
}
// break the reservation
_reservation_break(addr);
if (is_writing)
{
// break the reservation
_reservation_break(addr);
}
return true;
}
@ -291,25 +303,29 @@ namespace vm
void reservation_op(u32 addr, u32 size, std::function<void()> proc)
{
assert(size == 1 || size == 2 || size == 4 || size == 8 || size == 128);
assert((addr + size & ~0xfff) == (addr & ~0xfff));
assert((addr + size - 1 & ~0xfff) == (addr & ~0xfff));
std::lock_guard<reservation_mutex_t> lock(g_reservation_mutex);
// break previous reservation
if (g_reservation_addr)
if (g_reservation_owner != GetCurrentNamedThread() || g_reservation_addr != addr || g_reservation_size != size)
{
_reservation_break(g_reservation_addr);
if (g_reservation_owner)
{
_reservation_break(g_reservation_addr);
}
}
// change memory protection to read-only
_reservation_set(addr);
// change memory protection to no access
_reservation_set(addr, true);
// set additional information
g_reservation_addr = addr;
g_reservation_size = size;
g_reservation_owner = GetCurrentNamedThread();
g_reservation_cb = nullptr;
// may not be necessary, just for sure:
// may not be necessary
_mm_mfence();
// do the operation

View File

@ -34,7 +34,7 @@ namespace vm
bool reservation_acquire(void* data, u32 addr, u32 size, const std::function<void()>& callback = nullptr);
// attempt to atomically update reserved memory
bool reservation_update(u32 addr, const void* data, u32 size);
bool reservation_query(u32 addr);
bool reservation_query(u32 addr, bool is_writing);
void reservation_free();
// perform complete operation
void reservation_op(u32 addr, u32 size, std::function<void()> proc);