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

417 lines
8.1 KiB
C++
Raw Normal View History

2019-03-27 18:15:21 +01:00
#include "stdafx.h"
2014-08-23 16:51:51 +02:00
#include "sys_vm.h"
#include "Emu/IdManager.h"
2019-07-14 16:55:26 +02:00
#include "Emu/Cell/PPUThread.h"
#include "Emu/Memory/vm_locking.h"
2016-05-25 12:31:29 +02:00
2020-01-09 20:23:18 +01:00
extern u64 get_timebased_time();
sys_vm_t::sys_vm_t(u32 _addr, u32 vsize, lv2_memory_container* ct, u32 psize)
2019-03-27 18:15:21 +01:00
: ct(ct)
, addr(_addr)
, size(vsize)
, psize(psize)
2019-03-27 18:15:21 +01:00
{
// Write ID
g_ids[addr >> 28].release(idm::last_id());
}
sys_vm_t::~sys_vm_t()
{
// Debug build : gcc and clang can not find the static var if retrieved directly in "release" function
constexpr auto invalid = id_manager::id_traits<sys_vm_t>::invalid;
2019-03-27 18:15:21 +01:00
// Free ID
g_ids[addr >> 28].release(invalid);
2019-03-27 18:15:21 +01:00
}
LOG_CHANNEL(sys_vm);
struct sys_vm_global_t
{
atomic_t<u32> total_vsize = 0;
};
2019-07-14 16:55:26 +02:00
error_code sys_vm_memory_map(ppu_thread& ppu, u32 vsize, u32 psize, u32 cid, u64 flag, u64 policy, vm::ptr<u32> addr)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
sys_vm.error("sys_vm_memory_map(vsize=0x%x, psize=0x%x, cid=0x%x, flags=0x%llx, policy=0x%llx, addr=*0x%x)", vsize, psize, cid, flag, policy, addr);
if (!vsize || !psize || vsize % 0x2000000 || vsize > 0x10000000 || psize > 0x10000000 || policy != SYS_VM_POLICY_AUTO_RECOMMENDED)
2016-05-25 12:31:29 +02:00
{
return CELL_EINVAL;
}
2015-07-11 22:44:53 +02:00
const auto idm_ct = idm::get<lv2_memory_container>(cid);
const auto ct = cid == SYS_MEMORY_CONTAINER_ID_INVALID ? g_fxo->get<lv2_memory_container>() : idm_ct.get();
2019-03-27 18:15:21 +01:00
if (!ct)
{
2016-05-25 12:31:29 +02:00
return CELL_ESRCH;
}
2014-10-01 15:18:38 +02:00
if (!g_fxo->get<sys_vm_global_t>()->total_vsize.fetch_op([vsize](u32& size)
{
// A single process can hold up to 256MB of virtual memory, even on DECR
if (0x10000000 - size < vsize)
{
return false;
}
size += vsize;
return true;
}).second)
{
return CELL_EBUSY;
}
2019-03-27 18:15:21 +01:00
if (!ct->take(psize))
{
g_fxo->get<sys_vm_global_t>()->total_vsize -= vsize;
2019-03-27 18:15:21 +01:00
return CELL_ENOMEM;
}
// Look for unmapped space
2018-11-21 13:03:10 +01:00
if (const auto area = vm::find_map(0x10000000, 0x10000000, 2 | (flag & SYS_MEMORY_PAGE_SIZE_MASK)))
2016-05-25 12:31:29 +02:00
{
// Alloc all memory (shall not fail)
verify(HERE), area->alloc(vsize);
idm::make<sys_vm_t>(area->addr, vsize, ct, psize);
2019-03-27 18:15:21 +01:00
// Write a pointer for the allocated memory
*addr = area->addr;
2018-08-14 15:08:45 +02:00
return CELL_OK;
2016-05-25 12:31:29 +02:00
}
2019-03-27 18:15:21 +01:00
ct->used -= psize;
g_fxo->get<sys_vm_global_t>()->total_vsize -= vsize;
2016-05-25 12:31:29 +02:00
return CELL_ENOMEM;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_memory_map_different(ppu_thread& ppu, u32 vsize, u32 psize, u32 cid, u64 flag, u64 policy, vm::ptr<u32> addr)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
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.
2019-07-14 16:55:26 +02:00
return sys_vm_memory_map(ppu, vsize, psize, cid, flag, policy, addr);
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_unmap(ppu_thread& ppu, u32 addr)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_unmap(addr=0x%x)", addr);
2019-03-27 18:15:21 +01:00
// Special case, check if its a start address by alignment
if (addr % 0x10000000)
{
2019-03-27 18:15:21 +01:00
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
verify(HERE), vm::unmap(addr);
// Return memory
vmo.ct->used -= vmo.psize;
g_fxo->get<sys_vm_global_t>()->total_vsize -= vmo.size;
});
if (!vmo)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_append_memory(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_append_memory(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
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;
}
2019-03-27 18:15:21 +01:00
if (!vmo.ct->take(size))
{
return CELL_ENOMEM;
}
vmo.psize += size;
return {};
});
if (!block)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
if (block.ret)
2019-03-27 18:15:21 +01:00
{
return block.ret;
2019-03-27 18:15:21 +01:00
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_return_memory(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_return_memory(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
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 < 0x100000ull + size)
{
return false;
}
value -= size;
return true;
});
if (!ok)
{
return CELL_EBUSY;
}
vmo.ct->used -= size;
return {};
});
if (!block)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
if (block.ret)
2019-03-27 18:15:21 +01:00
{
return block.ret;
2019-03-27 18:15:21 +01:00
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_lock(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_lock(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
if (!size)
{
return CELL_EINVAL;
}
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_unlock(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_unlock(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
if (!size)
{
return CELL_EINVAL;
}
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_touch(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_touch(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
if (!size)
{
return CELL_EINVAL;
}
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_flush(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_flush(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
if (!size)
{
return CELL_EINVAL;
}
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_invalidate(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2018-04-07 20:18:34 +02:00
sys_vm.warning("sys_vm_invalidate(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
if (!size)
{
return CELL_EINVAL;
}
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_store(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_store(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
if (!size)
{
return CELL_EINVAL;
}
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_sync(ppu_thread& ppu, u32 addr, u32 size)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_sync(addr=0x%x, size=0x%x)", addr, size);
2019-03-27 18:15:21 +01:00
if (!size)
{
return CELL_EINVAL;
}
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_test(ppu_thread& ppu, u32 addr, u32 size, vm::ptr<u64> result)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_test(addr=0x%x, size=0x%x, result=*0x%x)", addr, size, result);
2019-03-27 18:15:21 +01:00
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
2019-06-20 21:35:08 +02:00
if (!block || u64{addr} + size > u64{block->addr} + block->size)
2019-03-27 18:15:21 +01:00
{
return CELL_EINVAL;
}
*result = SYS_VM_STATE_ON_MEMORY;
return CELL_OK;
}
2019-07-14 16:55:26 +02:00
error_code sys_vm_get_statistics(ppu_thread& ppu, u32 addr, vm::ptr<sys_vm_statistics_t> stat)
{
ppu.state += cpu_flag::wait;
2019-07-14 16:55:26 +02:00
2016-05-25 12:31:29 +02:00
sys_vm.warning("sys_vm_get_statistics(addr=0x%x, stat=*0x%x)", addr, stat);
2019-03-27 18:15:21 +01:00
const auto block = idm::get<sys_vm_t>(sys_vm_t::find_id(addr));
if (!block || block->addr != addr)
{
return CELL_EINVAL;
}
2014-09-03 18:33:30 +02:00
stat->page_fault_ppu = 0;
stat->page_fault_spu = 0;
stat->page_in = 0;
stat->page_out = 0;
2019-03-27 18:15:21 +01:00
stat->pmem_total = block->psize;
2014-09-03 18:33:30 +02:00
stat->pmem_used = 0;
2020-01-09 20:23:18 +01:00
stat->timestamp = get_timebased_time();
return CELL_OK;
}
2019-03-27 18:15:21 +01:00
DECLARE(sys_vm_t::g_ids){};