mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-01-02 06:40:05 +01:00
Replaces `std::shared_pointer` with `stx::atomic_ptr` and `stx::shared_ptr`. Notes to programmers: * This pr kills the use of `dynamic_cast`, `std::dynamic_pointer_cast` and `std::weak_ptr` on IDM objects, possible replacement is to save the object ID on the base object, then use idm::check/get_unlocked to the destination type via the saved ID which may be null. Null pointer check is how you can tell type mismatch (as dynamic cast) or object destruction (as weak_ptr locking). * Double-inheritance on IDM objects should be used with care, `stx::shared_ptr` does not support constant-evaluated pointer offsetting to parent/child type. * `idm::check/get_unlocked` can now be used anywhere. Misc fixes: * Fixes some segfaults with RPCN with interaction with IDM. * Fix deadlocks in access violation handler due locking recursion. * Fixes race condition in process exit-spawn on memory containers read. * Fix bug that theoretically can prevent RPCS3 from booting - fix `id_manager::typeinfo` comparison to compare members instead of `memcmp` which can fail spuriously on padding bytes. * Ensure all IDM inherited types of base, either has `id_base` or `id_type` defined locally, this allows to make getters such as `idm::get_unlocked<lv2_socket, lv2_socket_raw>()` which were broken before. (requires save-states invalidation) * Removes broken operator[] overload of `stx::shared_ptr` and `stx::single_ptr` for non-array types.
444 lines
9 KiB
C++
444 lines
9 KiB
C++
#include "stdafx.h"
|
|
#include "sys_vm.h"
|
|
#include "sys_process.h"
|
|
|
|
#include "Emu/IdManager.h"
|
|
#include "Emu/Cell/ErrorCodes.h"
|
|
#include "Emu/Cell/PPUThread.h"
|
|
#include "Emu/Cell/timers.hpp"
|
|
#include "Emu/Memory/vm_locking.h"
|
|
|
|
sys_vm_t::sys_vm_t(u32 _addr, u32 vsize, lv2_memory_container* ct, u32 psize)
|
|
: ct(ct)
|
|
, addr(_addr)
|
|
, size(vsize)
|
|
, psize(psize)
|
|
{
|
|
// Write ID
|
|
g_ids[addr >> 28].release(idm::last_id());
|
|
}
|
|
|
|
void sys_vm_t::save(utils::serial& ar)
|
|
{
|
|
USING_SERIALIZATION_VERSION(lv2_vm);
|
|
ar(ct->id, addr, size, psize);
|
|
}
|
|
|
|
sys_vm_t::~sys_vm_t()
|
|
{
|
|
// Free ID
|
|
g_ids[addr >> 28].release(id_manager::id_traits<sys_vm_t>::invalid);
|
|
}
|
|
|
|
LOG_CHANNEL(sys_vm);
|
|
|
|
struct sys_vm_global_t
|
|
{
|
|
atomic_t<u32> total_vsize = 0;
|
|
};
|
|
|
|
sys_vm_t::sys_vm_t(utils::serial& ar)
|
|
: ct(lv2_memory_container::search(ar))
|
|
, addr(ar)
|
|
, size(ar)
|
|
, psize(ar)
|
|
{
|
|
g_ids[addr >> 28].release(idm::last_id());
|
|
g_fxo->need<sys_vm_global_t>();
|
|
g_fxo->get<sys_vm_global_t>().total_vsize += size;
|
|
}
|
|
|
|
error_code sys_vm_memory_map(ppu_thread& ppu, u64 vsize, u64 psize, u32 cid, u64 flag, u64 policy, vm::ptr<u32> addr)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_memory_map(vsize=0x%x, psize=0x%x, cid=0x%x, flags=0x%x, policy=0x%x, addr=*0x%x)", vsize, psize, cid, flag, policy, addr);
|
|
|
|
if (!vsize || !psize || vsize % 0x200'0000 || vsize > 0x1000'0000 || psize % 0x1'0000 || policy != SYS_VM_POLICY_AUTO_RECOMMENDED)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
if (ppu.gpr[11] == 300 && psize < 0x10'0000)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto idm_ct = idm::get_unlocked<lv2_memory_container>(cid);
|
|
|
|
const auto ct = cid == SYS_MEMORY_CONTAINER_ID_INVALID ? &g_fxo->get<lv2_memory_container>() : idm_ct.get();
|
|
|
|
if (!ct)
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
if (!g_fxo->get<sys_vm_global_t>().total_vsize.fetch_op([vsize, has_root = g_ps3_process_info.has_root_perm()](u32& size)
|
|
{
|
|
// A single process can hold up to 256MB of virtual memory, even on DECR
|
|
// VSH can hold more
|
|
if ((has_root ? 0x1E000000 : 0x10000000) - size < vsize)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
size += static_cast<u32>(vsize);
|
|
return true;
|
|
}).second)
|
|
{
|
|
return CELL_EBUSY;
|
|
}
|
|
|
|
if (!ct->take(psize))
|
|
{
|
|
g_fxo->get<sys_vm_global_t>().total_vsize -= static_cast<u32>(vsize);
|
|
return CELL_ENOMEM;
|
|
}
|
|
|
|
// Look for unmapped space
|
|
if (const auto area = vm::find_map(0x10000000, 0x10000000, 2 | (flag & SYS_MEMORY_PAGE_SIZE_MASK)))
|
|
{
|
|
sys_vm.warning("sys_vm_memory_map(): Found VM 0x%x area (vsize=0x%x)", addr, vsize);
|
|
|
|
// Alloc all memory (shall not fail)
|
|
ensure(area->alloc(static_cast<u32>(vsize)));
|
|
vm::lock_sudo(area->addr, static_cast<u32>(vsize));
|
|
|
|
idm::make<sys_vm_t>(area->addr, static_cast<u32>(vsize), ct, static_cast<u32>(psize));
|
|
|
|
// Write a pointer for the allocated memory
|
|
ppu.check_state();
|
|
*addr = area->addr;
|
|
return CELL_OK;
|
|
}
|
|
|
|
ct->free(psize);
|
|
g_fxo->get<sys_vm_global_t>().total_vsize -= static_cast<u32>(vsize);
|
|
return CELL_ENOMEM;
|
|
}
|
|
|
|
error_code sys_vm_memory_map_different(ppu_thread& ppu, u64 vsize, u64 psize, u32 cid, u64 flag, u64 policy, vm::ptr<u32> addr)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_memory_map_different(vsize=0x%x, psize=0x%x, cid=0x%x, flags=0x%llx, policy=0x%llx, addr=*0x%x)", vsize, psize, cid, flag, policy, addr);
|
|
// TODO: if needed implement different way to map memory, unconfirmed.
|
|
|
|
return sys_vm_memory_map(ppu, vsize, psize, cid, flag, policy, addr);
|
|
}
|
|
|
|
error_code sys_vm_unmap(ppu_thread& ppu, u32 addr)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_unmap(addr=0x%x)", addr);
|
|
|
|
// Special case, check if its a start address by alignment
|
|
if (addr % 0x10000000)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
// Free block and info
|
|
const auto vmo = idm::withdraw<sys_vm_t>(sys_vm_t::find_id(addr), [&](sys_vm_t& vmo)
|
|
{
|
|
// Free block
|
|
ensure(vm::unmap(addr).second);
|
|
|
|
// Return memory
|
|
vmo.ct->free(vmo.psize);
|
|
g_fxo->get<sys_vm_global_t>().total_vsize -= vmo.size;
|
|
});
|
|
|
|
if (!vmo)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_append_memory(ppu_thread& ppu, u32 addr, u64 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_append_memory(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size || size % 0x100000)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::check<sys_vm_t>(sys_vm_t::find_id(addr), [&](sys_vm_t& vmo) -> CellError
|
|
{
|
|
if (vmo.addr != addr)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
if (!vmo.ct->take(size))
|
|
{
|
|
return CELL_ENOMEM;
|
|
}
|
|
|
|
vmo.psize += static_cast<u32>(size);
|
|
return {};
|
|
});
|
|
|
|
if (!block)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
if (block.ret)
|
|
{
|
|
return block.ret;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_return_memory(ppu_thread& ppu, u32 addr, u64 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_return_memory(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size || size % 0x100000)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::check<sys_vm_t>(sys_vm_t::find_id(addr), [&](sys_vm_t& vmo) -> CellError
|
|
{
|
|
if (vmo.addr != addr)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
auto [_, ok] = vmo.psize.fetch_op([&](u32& value)
|
|
{
|
|
if (value <= size || value - size < 0x100000ull)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
value -= static_cast<u32>(size);
|
|
return true;
|
|
});
|
|
|
|
if (!ok)
|
|
{
|
|
return CELL_EBUSY;
|
|
}
|
|
|
|
vmo.ct->free(size);
|
|
return {};
|
|
});
|
|
|
|
if (!block)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
if (block.ret)
|
|
{
|
|
return block.ret;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_lock(ppu_thread& ppu, u32 addr, u32 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_lock(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_unlock(ppu_thread& ppu, u32 addr, u32 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_unlock(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_touch(ppu_thread& ppu, u32 addr, u32 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_touch(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_flush(ppu_thread& ppu, u32 addr, u32 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_flush(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_invalidate(ppu_thread& ppu, u32 addr, u32 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_invalidate(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_store(ppu_thread& ppu, u32 addr, u32 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_store(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_sync(ppu_thread& ppu, u32 addr, u32 size)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_sync(addr=0x%x, size=0x%x)", addr, size);
|
|
|
|
if (!size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_test(ppu_thread& ppu, u32 addr, u32 size, vm::ptr<u64> result)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_test(addr=0x%x, size=0x%x, result=*0x%x)", addr, size, result);
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || u64{addr} + size > u64{block->addr} + block->size)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
ppu.check_state();
|
|
*result = SYS_VM_STATE_ON_MEMORY;
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code sys_vm_get_statistics(ppu_thread& ppu, u32 addr, vm::ptr<sys_vm_statistics_t> stat)
|
|
{
|
|
ppu.state += cpu_flag::wait;
|
|
|
|
sys_vm.warning("sys_vm_get_statistics(addr=0x%x, stat=*0x%x)", addr, stat);
|
|
|
|
const auto block = idm::get_unlocked<sys_vm_t>(sys_vm_t::find_id(addr));
|
|
|
|
if (!block || block->addr != addr)
|
|
{
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
ppu.check_state();
|
|
stat->page_fault_ppu = 0;
|
|
stat->page_fault_spu = 0;
|
|
stat->page_in = 0;
|
|
stat->page_out = 0;
|
|
stat->pmem_total = block->psize;
|
|
stat->pmem_used = 0;
|
|
stat->timestamp = get_timebased_time();
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
DECLARE(sys_vm_t::g_ids){};
|