mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-16 11:48:36 +12:00
sys_interrupt...
This commit is contained in:
parent
fe26db4d36
commit
68f0393cf3
6 changed files with 116 additions and 103 deletions
|
@ -12,14 +12,6 @@ namespace vm { using namespace ps3; }
|
|||
|
||||
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)
|
||||
: thread(thread)
|
||||
, arg1(arg1)
|
||||
, arg2(arg2)
|
||||
, id(idm::last_id())
|
||||
{
|
||||
}
|
||||
|
||||
void lv2_int_serv::exec()
|
||||
{
|
||||
thread->cmd_list
|
||||
|
@ -31,7 +23,7 @@ void lv2_int_serv::exec()
|
|||
thread->notify();
|
||||
}
|
||||
|
||||
void lv2_int_serv::join(ppu_thread& ppu, lv2_lock_t lv2_lock)
|
||||
void lv2_int_serv::join()
|
||||
{
|
||||
// Enqueue _sys_ppu_thread_exit call
|
||||
thread->cmd_list
|
||||
|
@ -42,93 +34,98 @@ void lv2_int_serv::join(ppu_thread& ppu, lv2_lock_t lv2_lock)
|
|||
});
|
||||
|
||||
thread->notify();
|
||||
|
||||
// Join thread (TODO)
|
||||
while (!test(thread->state & cpu_flag::exit))
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
LV2_UNLOCK, thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
idm::remove<lv2_obj, lv2_int_serv>(id);
|
||||
thread->join();
|
||||
}
|
||||
|
||||
s32 sys_interrupt_tag_destroy(u32 intrtag)
|
||||
error_code sys_interrupt_tag_destroy(u32 intrtag)
|
||||
{
|
||||
sys_interrupt.warning("sys_interrupt_tag_destroy(intrtag=0x%x)", intrtag);
|
||||
|
||||
LV2_LOCK;
|
||||
const auto tag = idm::withdraw<lv2_obj, lv2_int_tag>(intrtag, [](lv2_int_tag& tag) -> CellError
|
||||
{
|
||||
if (!tag.handler.expired())
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
const auto tag = idm::get<lv2_obj, lv2_int_tag>(intrtag);
|
||||
return {};
|
||||
});
|
||||
|
||||
if (!tag)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (tag->handler)
|
||||
if (tag.ret)
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
return tag.ret;
|
||||
}
|
||||
|
||||
idm::remove<lv2_obj, lv2_int_tag>(intrtag);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2)
|
||||
error_code _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);
|
||||
|
||||
LV2_LOCK;
|
||||
CellError error = CELL_EAGAIN;
|
||||
|
||||
// Get interrupt tag
|
||||
const auto tag = idm::get<lv2_obj, lv2_int_tag>(intrtag);
|
||||
|
||||
if (!tag)
|
||||
const u32 id = idm::import<lv2_obj, lv2_int_serv>([&]()
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
std::shared_ptr<lv2_int_serv> result;
|
||||
|
||||
// Get interrupt tag
|
||||
const auto tag = idm::check_unlocked<lv2_obj, lv2_int_tag>(intrtag);
|
||||
|
||||
if (!tag)
|
||||
{
|
||||
error = CELL_ESRCH;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get interrupt thread
|
||||
const auto it = idm::get_unlocked<ppu_thread>(intrthread);
|
||||
|
||||
if (!it)
|
||||
{
|
||||
error = CELL_ESRCH;
|
||||
return result;
|
||||
}
|
||||
|
||||
// If interrupt thread is running, it's already established on another interrupt tag
|
||||
if (!test(it->state & cpu_flag::stop))
|
||||
{
|
||||
error = CELL_EAGAIN;
|
||||
return result;
|
||||
}
|
||||
|
||||
// It's unclear if multiple handlers can be established on single interrupt tag
|
||||
if (!tag->handler.expired())
|
||||
{
|
||||
error = CELL_ESTAT;
|
||||
return result;
|
||||
}
|
||||
|
||||
result = std::make_shared<lv2_int_serv>(it, arg1, arg2);
|
||||
tag->handler = result;
|
||||
it->run();
|
||||
return result;
|
||||
});
|
||||
|
||||
if (id)
|
||||
{
|
||||
*ih = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// Get interrupt thread
|
||||
const auto it = idm::get<ppu_thread>(intrthread);
|
||||
|
||||
if (!it)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// If interrupt thread is running, it's already established on another interrupt tag
|
||||
if (!test(it->state & cpu_flag::stop))
|
||||
{
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
// 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);
|
||||
return CELL_ESTAT;
|
||||
}
|
||||
|
||||
tag->handler = idm::make_ptr<lv2_obj, lv2_int_serv>(it, arg1, arg2);
|
||||
|
||||
it->run();
|
||||
|
||||
*ih = tag->handler->id;
|
||||
|
||||
return CELL_OK;
|
||||
return error;
|
||||
}
|
||||
|
||||
s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13)
|
||||
error_code _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);
|
||||
|
||||
LV2_LOCK;
|
||||
|
||||
const auto handler = idm::get<lv2_obj, lv2_int_serv>(ih);
|
||||
const auto handler = idm::withdraw<lv2_obj, lv2_int_serv>(ih);
|
||||
|
||||
if (!handler)
|
||||
{
|
||||
|
@ -136,7 +133,7 @@ s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13
|
|||
}
|
||||
|
||||
// Wait for sys_interrupt_thread_eoi() and destroy interrupt thread
|
||||
handler->join(ppu, lv2_lock);
|
||||
handler->join();
|
||||
|
||||
// Save TLS base
|
||||
*r13 = handler->thread->gpr[13];
|
||||
|
@ -144,23 +141,15 @@ s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
void sys_interrupt_thread_eoi(ppu_thread& ppu) // Low-level PPU function example
|
||||
void sys_interrupt_thread_eoi(ppu_thread& ppu)
|
||||
{
|
||||
// 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()");
|
||||
sys_interrupt.trace("sys_interrupt_thread_eoi()");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
lv2_int_tag::lv2_int_tag()
|
||||
: id(idm::last_id())
|
||||
{
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue