sys_interrupt...

This commit is contained in:
Nekotekina 2017-02-04 19:30:21 +03:00
parent fe26db4d36
commit 68f0393cf3
6 changed files with 116 additions and 103 deletions

View file

@ -5,12 +5,7 @@
using ppu_function_t = void(*)(ppu_thread&); using ppu_function_t = void(*)(ppu_thread&);
// BIND_FUNC macro "converts" any appropriate HLE function to ppu_function_t, binding it to PPU thread context. // BIND_FUNC macro "converts" any appropriate HLE function to ppu_function_t, binding it to PPU thread context.
// If function already has type ppu_function_t, it's handled specially and classified as "low-level HLE function". #define BIND_FUNC(func) (static_cast<ppu_function_t>([](ppu_thread& ppu) {\
// 1) Low-level functions are bound directly so they don't save their name to ppu.last_function variable.
// 2) Low-level functions don't install thread_guar, so they are very limited, and may be dangerous.
// If you don't need "low-level function", be sure it's either `void()` or `void(ppu_thread& ppu, ppu_thread&)` for example.
#define BIND_FUNC(func) (std::is_same<decltype(func), ppu_function_t>::value ? reinterpret_cast<ppu_function_t>(func) : static_cast<ppu_function_t>([](ppu_thread& ppu){\
const thread_guard guard(ppu);\
const auto old_f = ppu.last_function;\ const auto old_f = ppu.last_function;\
ppu.last_function = #func;\ ppu.last_function = #func;\
ppu_func_detail::do_call(ppu, func);\ ppu_func_detail::do_call(ppu, func);\

View file

@ -54,9 +54,12 @@ void spu_int_ctrl_t::set(u64 ints)
{ {
LV2_LOCK; LV2_LOCK;
if (tag && tag->handler) if (tag)
{ {
tag->handler->exec(); if (auto handler = tag->handler.lock())
{
handler->exec();
}
} }
} }
} }

View file

@ -973,6 +973,9 @@ void fmt_class_string<CellError>::format(std::string& out, u64 arg)
STR_CASE(CELL_EOVERFLOW); STR_CASE(CELL_EOVERFLOW);
STR_CASE(CELL_ENOTMOUNTED); STR_CASE(CELL_ENOTMOUNTED);
STR_CASE(CELL_ENOTSDATA); STR_CASE(CELL_ENOTSDATA);
STR_CASE(CELL_ESDKVER);
STR_CASE(CELL_ENOLICDISC);
STR_CASE(CELL_ENOLICENT);
} }
return unknown; return unknown;

View file

@ -12,14 +12,6 @@ namespace vm { using namespace ps3; }
logs::channel sys_interrupt("sys_interrupt", logs::level::notice); 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() void lv2_int_serv::exec()
{ {
thread->cmd_list thread->cmd_list
@ -31,7 +23,7 @@ void lv2_int_serv::exec()
thread->notify(); 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 // Enqueue _sys_ppu_thread_exit call
thread->cmd_list thread->cmd_list
@ -42,93 +34,98 @@ void lv2_int_serv::join(ppu_thread& ppu, lv2_lock_t lv2_lock)
}); });
thread->notify(); thread->notify();
thread->join();
// Join thread (TODO)
while (!test(thread->state & cpu_flag::exit))
{
CHECK_EMU_STATUS;
LV2_UNLOCK, thread_ctrl::wait_for(1000);
} }
// Cleanup error_code sys_interrupt_tag_destroy(u32 intrtag)
idm::remove<lv2_obj, lv2_int_serv>(id);
}
s32 sys_interrupt_tag_destroy(u32 intrtag)
{ {
sys_interrupt.warning("sys_interrupt_tag_destroy(intrtag=0x%x)", 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
const auto tag = idm::get<lv2_obj, lv2_int_tag>(intrtag);
if (!tag)
{ {
return CELL_ESRCH; if (!tag.handler.expired())
}
if (tag->handler)
{ {
return CELL_EBUSY; return CELL_EBUSY;
} }
idm::remove<lv2_obj, lv2_int_tag>(intrtag); return {};
});
return CELL_OK;
}
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);
LV2_LOCK;
// Get interrupt tag
const auto tag = idm::get<lv2_obj, lv2_int_tag>(intrtag);
if (!tag) if (!tag)
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
if (tag.ret)
{
return tag.ret;
}
return CELL_OK;
}
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);
CellError error = CELL_EAGAIN;
const u32 id = idm::import<lv2_obj, lv2_int_serv>([&]()
{
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 // Get interrupt thread
const auto it = idm::get<ppu_thread>(intrthread); const auto it = idm::get_unlocked<ppu_thread>(intrthread);
if (!it) if (!it)
{ {
return CELL_ESRCH; error = CELL_ESRCH;
return result;
} }
// If interrupt thread is running, it's already established on another interrupt tag // If interrupt thread is running, it's already established on another interrupt tag
if (!test(it->state & cpu_flag::stop)) if (!test(it->state & cpu_flag::stop))
{ {
return CELL_EAGAIN; error = CELL_EAGAIN;
return result;
} }
// It's unclear if multiple handlers can be established on single interrupt tag // It's unclear if multiple handlers can be established on single interrupt tag
if (tag->handler) if (!tag->handler.expired())
{ {
sys_interrupt.error("_sys_interrupt_thread_establish(): handler service already exists (intrtag=0x%x) -> CELL_ESTAT", intrtag); error = CELL_ESTAT;
return CELL_ESTAT; return result;
} }
tag->handler = idm::make_ptr<lv2_obj, lv2_int_serv>(it, arg1, arg2); result = std::make_shared<lv2_int_serv>(it, arg1, arg2);
tag->handler = result;
it->run(); it->run();
return result;
});
*ih = tag->handler->id; if (id)
{
*ih = id;
return CELL_OK; return CELL_OK;
} }
s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13) return error;
}
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); sys_interrupt.warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
LV2_LOCK; const auto handler = idm::withdraw<lv2_obj, lv2_int_serv>(ih);
const auto handler = idm::get<lv2_obj, lv2_int_serv>(ih);
if (!handler) 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 // Wait for sys_interrupt_thread_eoi() and destroy interrupt thread
handler->join(ppu, lv2_lock); handler->join();
// Save TLS base // Save TLS base
*r13 = handler->thread->gpr[13]; *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; 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 sys_interrupt.trace("sys_interrupt_thread_eoi()");
thread_guard{ppu}, sys_interrupt.trace("sys_interrupt_thread_eoi()");
ppu.state += cpu_flag::ret; ppu.state += cpu_flag::ret;
// Throw if this syscall was not called directly by the SC instruction (hack) // Throw if this syscall was not called directly by the SC instruction (hack)
if (ppu.lr == 0 || ppu.gpr[11] != 88) 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; throw cpu_flag::ret;
} }
} }
lv2_int_tag::lv2_int_tag()
: id(idm::last_id())
{
}

View file

@ -8,11 +8,7 @@ struct lv2_int_tag final : lv2_obj
{ {
static const u32 id_base = 0x0a000000; static const u32 id_base = 0x0a000000;
const u32 id; std::weak_ptr<struct lv2_int_serv> handler;
std::shared_ptr<struct lv2_int_serv> handler;
lv2_int_tag();
}; };
struct lv2_int_serv final : lv2_obj struct lv2_int_serv final : lv2_obj
@ -20,20 +16,23 @@ struct lv2_int_serv final : lv2_obj
static const u32 id_base = 0x0b000000; static const u32 id_base = 0x0b000000;
const std::shared_ptr<ppu_thread> thread; const std::shared_ptr<ppu_thread> thread;
const u32 id;
const u64 arg1; const u64 arg1;
const u64 arg2; const u64 arg2;
lv2_int_serv(const std::shared_ptr<ppu_thread>& thread, u64 arg1, u64 arg2); lv2_int_serv(const std::shared_ptr<ppu_thread>& thread, u64 arg1, u64 arg2)
: thread(thread)
, arg1(arg1)
, arg2(arg2)
{
}
void exec(); void exec();
void join(ppu_thread& ppu, lv2_lock_t); void join();
}; };
// SysCalls // Syscalls
s32 sys_interrupt_tag_destroy(u32 intrtag);
s32 _sys_interrupt_thread_establish(vm::ps3::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2); error_code sys_interrupt_tag_destroy(u32 intrtag);
s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ps3::ptr<u64> r13); error_code _sys_interrupt_thread_establish(vm::ps3::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2);
error_code _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ps3::ptr<u64> r13);
void sys_interrupt_thread_eoi(ppu_thread& ppu); void sys_interrupt_thread_eoi(ppu_thread& ppu);

View file

@ -1173,20 +1173,44 @@ s32 sys_raw_spu_destroy(ppu_thread& ppu, u32 id)
// Stop thread // Stop thread
thread->state += cpu_flag::stop; thread->state += cpu_flag::stop;
// Kernel objects which must be removed
std::unordered_map<lv2_obj*, u32, pointer_hash<lv2_obj, alignof(void*)>> to_remove;
// Clear interrupt handlers // Clear interrupt handlers
for (auto& intr : thread->int_ctrl) for (auto& intr : thread->int_ctrl)
{ {
if (intr.tag) if (intr.tag)
{ {
if (intr.tag->handler) if (auto handler = intr.tag->handler.lock())
{ {
intr.tag->handler->join(ppu, lv2_lock); LV2_UNLOCK, handler->join();
to_remove.emplace(handler.get(), 0);
} }
idm::remove<lv2_obj, lv2_int_tag>(intr.tag->id); to_remove.emplace(intr.tag.get(), 0);
} }
} }
// Scan all kernel objects to determine IDs
idm::select<lv2_obj>([&](u32 id, lv2_obj& obj)
{
const auto found = to_remove.find(&obj);
if (found != to_remove.end())
{
found->second = id;
}
});
// Remove IDs
for (auto&& pair : to_remove)
{
if (pair.second >> 24 == 0xa)
idm::remove<lv2_obj, lv2_int_tag>(pair.second);
if (pair.second >> 24 == 0xb)
idm::remove<lv2_obj, lv2_int_serv>(pair.second);
}
idm::remove<RawSPUThread>(thread->id); idm::remove<RawSPUThread>(thread->id);
return CELL_OK; return CELL_OK;
@ -1219,7 +1243,7 @@ s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr
int_ctrl.tag = idm::make_ptr<lv2_obj, lv2_int_tag>(); int_ctrl.tag = idm::make_ptr<lv2_obj, lv2_int_tag>();
*intrtag = int_ctrl.tag->id; *intrtag = idm::last_id();
return CELL_OK; return CELL_OK;
} }