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

sys_prx Improvements #7845

Merged
merged 3 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
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
200 changes: 192 additions & 8 deletions rpcs3/Emu/Cell/lv2/sys_prx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,55 @@ error_code _sys_prx_start_module(u32 id, u64 flags, vm::ptr<sys_prx_start_stop_m
return CELL_ESRCH;
}

if (prx->is_started.exchange(true))
return not_an_error(CELL_PRX_ERROR_ALREADY_STARTED);
switch (pOpt->cmd & 0xf)
{
case 1:
{
if (!prx->state.compare_and_swap_test(PRX_STATE_INITIALIZED, PRX_STATE_STARTING))
{
// The only error code here
return CELL_PRX_ERROR_ERROR;
}

pOpt->entry.set(prx->start ? prx->start.addr() : ~0ull);
pOpt->entry2.set(prx->prologue ? prx->prologue.addr() : ~0ull);
return CELL_OK;
}
case 2:
{
switch (const u64 res = pOpt->res)
{
case SYS_PRX_RESIDENT:
{
// No error code on invalid state, so throw on unexpected state
verify(HERE), prx->state.compare_and_swap_test(PRX_STATE_STARTING, PRX_STATE_STARTED);
return CELL_OK;
}
default:
{
if (res & 0xffff'ffffu)
{
// Unload the module (SYS_PRX_NO_RESIDENT expected)
sys_prx.warning("_sys_prx_start_module(): Start entry function returned SYS_PRX_NO_RESIDENT (res=0x%llx)", res);

// Thread-safe if called from liblv2.sprx, due to internal lwmutex lock before it
prx->state = PRX_STATE_STOPPED;
_sys_prx_unload_module(id, 0, vm::null);

// Return the exact value returned by the start function (as an error)
return static_cast<s32>(res);
}

// Return type of start entry function is s32
// And according to RE this path results in weird behavior
sys_prx.error("_sys_prx_start_module(): Start entry function returned weird value (res=0x%llx)", res);
return CELL_OK;
}
}
}
default:
return CELL_PRX_ERROR_ERROR;
}

pOpt->entry.set(prx->start ? prx->start.addr() : ~0ull);
pOpt->entry2.set(prx->prologue ? prx->prologue.addr() : ~0ull);
Expand All @@ -316,11 +363,80 @@ error_code _sys_prx_stop_module(u32 id, u64 flags, vm::ptr<sys_prx_start_stop_mo
return CELL_ESRCH;
}

if (!prx->is_started.exchange(false))
return not_an_error(CELL_PRX_ERROR_ALREADY_STOPPED);
if (!pOpt)
{
return CELL_EINVAL;
}

switch (pOpt->cmd & 0xf)
{
case 1:
{
switch (const auto old = prx->state.compare_and_swap(PRX_STATE_STARTED, PRX_STATE_STOPPING))
{
case PRX_STATE_INITIALIZED: return CELL_PRX_ERROR_NOT_STARTED;
case PRX_STATE_STOPPED: return CELL_PRX_ERROR_ALREADY_STOPPED;
case PRX_STATE_STOPPING: return CELL_PRX_ERROR_ALREADY_STOPPING; // Internal error
case PRX_STATE_STARTING: return CELL_PRX_ERROR_ERROR; // Internal error
case PRX_STATE_STARTED: break;
default:
fmt::throw_exception("Invalid prx state (%d)" HERE, old);
}

pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);
pOpt->entry2.set(prx->epilogue ? prx->epilogue.addr() : ~0ull);
return CELL_OK;
}
case 2:
{
switch (pOpt->res)
{
case 0:
{
// No error code on invalid state, so throw on unexpected state
verify(HERE), prx->state.compare_and_swap_test(PRX_STATE_STOPPING, PRX_STATE_STOPPED);
return CELL_OK;
}
case 1:
return CELL_PRX_ERROR_CAN_NOT_STOP; // Internal error
default:
// Nothing happens (probably unexpected value)
return CELL_OK;
}
}

// These commands are not used by liblv2.sprx
case 4: // Get start entry and stop functions
case 8: // Disable stop function execution
{
switch (const auto old = +prx->state)
{
case PRX_STATE_INITIALIZED: return CELL_PRX_ERROR_NOT_STARTED;
case PRX_STATE_STOPPED: return CELL_PRX_ERROR_ALREADY_STOPPED;
case PRX_STATE_STOPPING: return CELL_PRX_ERROR_ALREADY_STOPPING; // Internal error
case PRX_STATE_STARTING: return CELL_PRX_ERROR_ERROR; // Internal error
case PRX_STATE_STARTED: break;
default:
fmt::throw_exception("Invalid prx state (%d)" HERE, old);
}

pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);
pOpt->entry2.set(prx->epilogue ? prx->epilogue.addr() : ~0ull);
if (pOpt->cmd == 4u)
{
pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);
pOpt->entry2.set(prx->epilogue ? prx->epilogue.addr() : ~0ull);
}
else
{
// Disables stop function execution (but the real value can be read through _sys_prx_get_module_info)
sys_prx.todo("_sys_prx_stop_module(): cmd is 8 (stop function = *0x%x)", prx->stop);
//prx->stop = vm::null;
}

return CELL_OK;
}
default:
return CELL_PRX_ERROR_ERROR;
}

return CELL_OK;
}
Expand All @@ -330,13 +446,29 @@ error_code _sys_prx_unload_module(u32 id, u64 flags, vm::ptr<sys_prx_unload_modu
sys_prx.todo("_sys_prx_unload_module(id=0x%x, flags=0x%x, pOpt=*0x%x)", id, flags, pOpt);

// Get the PRX, free the used memory and delete the object and its ID
const auto prx = idm::withdraw<lv2_obj, lv2_prx>(id);
const auto prx = idm::withdraw<lv2_obj, lv2_prx>(id, [](lv2_prx& prx) -> CellPrxError
{
switch (prx.state)
{
case PRX_STATE_INITIALIZED:
case PRX_STATE_STOPPED:
return {};
default: break;
}

return CELL_PRX_ERROR_NOT_REMOVABLE;
});

if (!prx)
{
return CELL_PRX_ERROR_UNKNOWN_MODULE;
}

if (prx.ret)
{
return prx.ret;
}

ppu_unload_prx(*prx);

//s32 result = prx->exit ? prx->exit() : CELL_OK;
Expand Down Expand Up @@ -388,7 +520,59 @@ error_code _sys_prx_query_library()

error_code _sys_prx_get_module_list(u64 flags, vm::ptr<sys_prx_get_module_list_option_t> pInfo)
{
sys_prx.todo("_sys_prx_get_module_list(flags=%d, pInfo=*0x%x)", flags, pInfo);
if (flags & 0x1)
{
sys_prx.todo("_sys_prx_get_module_list(flags=%d, pInfo=*0x%x)", flags, pInfo);
}
else
{
sys_prx.warning("_sys_prx_get_module_list(flags=%d, pInfo=*0x%x)", flags, pInfo);
}

// TODO: Some action occurs if LSB of flags is set here

if (!(flags & 0x2))
{
// Do nothing
return CELL_OK;
}

if (pInfo->size == pInfo.size())
{
const u32 max_count = pInfo->max;
const auto idlist = +pInfo->idlist;
u32 count = 0;

if (max_count)
{
const std::string liblv2_path = vfs::get("/dev_flash/sys/external/liblv2.sprx");

idm::select<lv2_obj, lv2_prx>([&](u32 id, lv2_prx& prx)
{
if (count >= max_count)
{
return true;
}

if (prx.path == liblv2_path)
{
// Hide liblv2.sprx for now
return false;
}

idlist[count++] = id;
return false;
});
}

pInfo->count = count;
}
else
{
// TODO: A different structure should be served here with sizeof == 0x18
sys_prx.todo("_sys_prx_get_module_list(): Unknown structure specified (size=0x%llx)", pInfo->size);
}

return CELL_OK;
}

Expand Down
18 changes: 17 additions & 1 deletion rpcs3/Emu/Cell/lv2/sys_prx.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,27 @@ struct sys_prx_get_module_list_option_t
be_t<u32> unk; // 0
};

enum : u32
{
SYS_PRX_RESIDENT = 0,
SYS_PRX_NO_RESIDENT = 1,
};

// Unofficial names for PRX state
enum : u32
{
PRX_STATE_INITIALIZED,
PRX_STATE_STARTING, // In-between state between initialized and started (internal)
PRX_STATE_STARTED,
PRX_STATE_STOPPING, // In-between state between started and stopped (internal)
PRX_STATE_STOPPED, // Last state, the module cannot be restarted
};

struct lv2_prx final : lv2_obj, ppu_module
{
static const u32 id_base = 0x23000000;

atomic_t<bool> is_started = false;
atomic_t<u32> state = PRX_STATE_INITIALIZED;

std::unordered_map<u32, u32> specials;
std::unordered_map<u32, void*> imports;
Expand Down