Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid closing the emulator after access violation #7428

Merged
merged 3 commits into from
Feb 13, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
91 changes: 55 additions & 36 deletions Utilities/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,32 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
return true;
}

thread_local bool access_violation_recovered = false;

// Hack: allocate memory in case the emulator is stopping
const auto hack_alloc = [&]()
{
// If failed the value remains true and std::terminate should be called
access_violation_recovered = true;

const auto area = vm::reserve_map(vm::any, addr & -0x10000, 0x10000);

if (!area)
{
return false;
}

if (area->flags & 0x100 || (is_writing && vm::check_addr(addr, std::max(1u, ::narrow<u32>(d_size)))))
{
// For 4kb pages or read only memory
utils::memory_protect(vm::base(addr & -0x1000), 0x1000, utils::protection::rw);
return true;
}

area->falloc(addr & -0x10000, 0x10000);
return vm::check_addr(addr, std::max(1u, ::narrow<u32>(d_size)), is_writing ? vm::page_writable : vm::page_readable);
};

if (cpu)
{
u32 pf_port_id = 0;
Expand Down Expand Up @@ -1377,22 +1403,23 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
sending_error = sys_event_port_send(pf_port_id, data1, data2, data3);
}

if (sending_error)
{
fmt::throw_exception("Unknown error %x while trying to pass page fault.", sending_error.value);
}

if (cpu->id_type() == 1)
{
// Deschedule
lv2_obj::sleep(*cpu);
}

if (sending_error)
{
vm_log.fatal("Unknown error %x while trying to pass page fault.", +sending_error);
cpu->state += cpu_flag::dbg_pause;
}

// Wait until the thread is recovered
for (std::shared_lock pf_lock(pf_events->pf_mutex);
pf_events->events.find(static_cast<u32>(data2)) != pf_events->events.end();)
pf_events->events.count(static_cast<u32>(data2)) && !sending_error;)
{
if (Emu.IsStopped())
if (cpu->is_stopped())
{
break;
}
Expand All @@ -1401,47 +1428,34 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
pf_events->cond.wait(pf_lock, 10000);
}

// Reschedule
if (cpu->test_stopped())
{
//
}

if (Emu.IsStopped())
// Reschedule, test cpu state and try recovery if stopped
if (cpu->test_stopped() && !hack_alloc())
{
// Hack: allocate memory in case the emulator is stopping
vm::falloc(addr & -0x10000, 0x10000);
std::terminate();
}

return true;
}

if (cpu->id_type() != 1)
{
vm_log.notice("\n%s", cpu->dump());
vm_log.fatal("Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr);
if (!access_violation_recovered)
{
vm_log.notice("\n%s", cpu->dump());
vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
}

// TODO:
// RawSPU: Send appropriate interrupt
// SPUThread: Send sys_spu exception event
cpu->state += cpu_flag::dbg_pause;
if (cpu->check_state())
{
// Hack: allocate memory in case the emulator is stopping
auto area = vm::reserve_map(vm::any, addr & -0x10000, 0x10000);

if (area->flags & 0x100)
{
// For 4kb pages
utils::memory_protect(vm::base(addr & -0x1000), 0x1000, utils::protection::rw);
}
else
{
area->falloc(addr & -0x10000, 0x10000);
}

return true;
if (cpu->check_state() && !hack_alloc())
{
std::terminate();
}

return true;
}
else
{
Expand All @@ -1456,19 +1470,24 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no

Emu.Pause();

if (cpu)
if (cpu && !access_violation_recovered)
{
vm_log.notice("\n%s", cpu->dump());
}

vm_log.fatal("Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr);
// Note: a thread may access violate more than once after hack_alloc recovery
// Do not log any further access violations in this case.
if (!access_violation_recovered)
{
vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
}

while (Emu.IsPaused())
{
thread_ctrl::wait();
}

if (Emu.IsStopped())
if (Emu.IsStopped() && !hack_alloc())
{
std::terminate();
}
Expand Down