rpcsx/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp

167 lines
3.5 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
2015-03-06 23:58:42 +01:00
#include "Emu/IdManager.h"
2014-08-23 16:51:51 +02:00
2016-04-14 00:23:53 +02:00
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/PPUOpcodes.h"
#include "sys_interrupt.h"
2016-08-19 23:14:10 +02:00
namespace vm { using namespace ps3; }
2016-05-13 15:55:34 +02:00
logs::channel sys_interrupt("sys_interrupt", logs::level::notice);
lv2_int_serv::lv2_int_serv(const std::shared_ptr<ppu_thread>& thread, u64 arg1, u64 arg2)
2017-01-25 18:50:30 +01:00
: thread(thread)
, arg1(arg1)
, arg2(arg2)
, id(idm::last_id())
{
}
void lv2_int_serv::exec()
{
thread->cmd_list
({
{ ppu_cmd::set_args, 2 }, arg1, arg2,
{ ppu_cmd::lle_call, 2 },
});
2015-07-12 23:02:02 +02:00
thread->notify();
}
void lv2_int_serv::join(ppu_thread& ppu, lv2_lock_t lv2_lock)
{
// Enqueue _sys_ppu_thread_exit call
thread->cmd_list
({
{ ppu_cmd::set_args, 1 }, u64{0},
{ ppu_cmd::set_gpr, 11 }, u64{41},
{ ppu_cmd::opcode, ppu_instructions::SC(0) },
});
thread->notify();
// Join thread (TODO)
while (!test(thread->state & cpu_flag::exit))
{
2015-07-12 23:02:02 +02:00
CHECK_EMU_STATUS;
LV2_UNLOCK, thread_ctrl::wait_for(1000);
}
2015-07-12 23:02:02 +02:00
// Cleanup
idm::remove<lv2_obj, lv2_int_serv>(id);
2015-07-12 23:02:02 +02:00
}
s32 sys_interrupt_tag_destroy(u32 intrtag)
{
sys_interrupt.warning("sys_interrupt_tag_destroy(intrtag=0x%x)", intrtag);
2015-07-12 23:02:02 +02:00
LV2_LOCK;
2015-03-02 22:09:20 +01:00
const auto tag = idm::get<lv2_obj, lv2_int_tag>(intrtag);
2015-07-12 23:02:02 +02:00
if (!tag)
{
return CELL_ESRCH;
}
2015-07-12 23:02:02 +02:00
if (tag->handler)
{
2015-07-12 23:02:02 +02:00
return CELL_EBUSY;
}
idm::remove<lv2_obj, lv2_int_tag>(intrtag);
2015-07-12 23:02:02 +02:00
return CELL_OK;
}
2015-07-12 23:02:02 +02:00
s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2)
{
sys_interrupt.warning("_sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=0x%x, arg1=0x%llx, arg2=0x%llx)", ih, intrtag, intrthread, arg1, arg2);
2015-07-12 23:02:02 +02:00
LV2_LOCK;
2015-07-12 23:02:02 +02:00
// Get interrupt tag
const auto tag = idm::get<lv2_obj, lv2_int_tag>(intrtag);
2015-07-12 23:02:02 +02:00
if (!tag)
{
return CELL_ESRCH;
}
2015-07-12 23:02:02 +02:00
// Get interrupt thread
const auto it = idm::get<ppu_thread>(intrthread);
2015-03-02 22:09:20 +01:00
2015-07-12 23:02:02 +02:00
if (!it)
{
return CELL_ESRCH;
}
2015-07-12 23:02:02 +02:00
// If interrupt thread is running, it's already established on another interrupt tag
if (!test(it->state & cpu_flag::stop))
2015-07-12 23:02:02 +02:00
{
return CELL_EAGAIN;
}
2015-07-12 23:02:02 +02:00
// It's unclear if multiple handlers can be established on single interrupt tag
if (tag->handler)
{
sys_interrupt.error("_sys_interrupt_thread_establish(): handler service already exists (intrtag=0x%x) -> CELL_ESTAT", intrtag);
2015-07-12 23:02:02 +02:00
return CELL_ESTAT;
}
tag->handler = idm::make_ptr<lv2_obj, lv2_int_serv>(it, arg1, arg2);
it->run();
2015-03-02 22:09:20 +01:00
*ih = tag->handler->id;
2015-07-12 23:02:02 +02:00
return CELL_OK;
}
s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13)
{
sys_interrupt.warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
2015-07-12 23:02:02 +02:00
LV2_LOCK;
const auto handler = idm::get<lv2_obj, lv2_int_serv>(ih);
2015-04-12 03:36:25 +02:00
if (!handler)
{
return CELL_ESRCH;
}
2015-07-12 23:02:02 +02:00
// Wait for sys_interrupt_thread_eoi() and destroy interrupt thread
handler->join(ppu, lv2_lock);
2015-07-12 23:02:02 +02:00
// Save TLS base
*r13 = handler->thread->gpr[13];
2015-03-02 22:09:20 +01:00
return CELL_OK;
}
void sys_interrupt_thread_eoi(ppu_thread& ppu) // Low-level PPU function example
{
// Low-level function body must guard all C++-ish calls and all objects with non-trivial destructors
thread_guard{ppu}, sys_interrupt.trace("sys_interrupt_thread_eoi()");
2015-04-20 01:49:13 +02:00
ppu.state += cpu_flag::ret;
// Throw if this syscall was not called directly by the SC instruction (hack)
if (ppu.lr == 0 || ppu.gpr[11] != 88)
{
// Low-level function must disable interrupts before throwing (not related to sys_interrupt_*, it's rather coincidence)
ppu.get()->interrupt_disable();
throw cpu_flag::ret;
}
2014-07-12 09:46:14 +02:00
}
2017-01-25 18:50:30 +01:00
lv2_int_tag::lv2_int_tag()
2017-01-25 18:50:30 +01:00
: id(idm::last_id())
{
}