2014-06-25 00:38:34 +02:00
|
|
|
#include "stdafx.h"
|
|
|
|
|
#include "Emu/Memory/Memory.h"
|
2015-03-06 23:58:42 +01:00
|
|
|
#include "Emu/System.h"
|
|
|
|
|
#include "Emu/IdManager.h"
|
2014-08-23 16:51:51 +02:00
|
|
|
|
2016-04-14 00:23:53 +02:00
|
|
|
#include "Emu/Cell/ErrorCodes.h"
|
2014-06-25 00:38:34 +02:00
|
|
|
#include "sys_memory.h"
|
|
|
|
|
|
2016-05-13 15:55:34 +02:00
|
|
|
logs::channel sys_memory("sys_memory", logs::level::notice);
|
2015-07-10 16:45:16 +02:00
|
|
|
|
|
|
|
|
s32 sys_memory_allocate(u32 size, u64 flags, vm::ptr<u32> alloc_addr)
|
|
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.warning("sys_memory_allocate(size=0x%x, flags=0x%llx, alloc_addr=*0x%x)", size, flags, alloc_addr);
|
2015-07-10 16:45:16 +02:00
|
|
|
|
|
|
|
|
LV2_LOCK;
|
|
|
|
|
|
|
|
|
|
// Check allocation size
|
2014-06-25 00:38:34 +02:00
|
|
|
switch(flags)
|
|
|
|
|
{
|
|
|
|
|
case SYS_MEMORY_PAGE_SIZE_1M:
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
|
|
|
|
if (size % 0x100000)
|
|
|
|
|
{
|
|
|
|
|
return CELL_EALIGN;
|
|
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-25 00:38:34 +02:00
|
|
|
case SYS_MEMORY_PAGE_SIZE_64K:
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
|
|
|
|
if (size % 0x10000)
|
|
|
|
|
{
|
|
|
|
|
return CELL_EALIGN;
|
|
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
break;
|
2014-06-25 00:38:34 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
return CELL_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allocate memory
|
|
|
|
|
const u32 addr =
|
2015-07-12 13:52:55 +02:00
|
|
|
flags == SYS_MEMORY_PAGE_SIZE_1M ? vm::alloc(size, vm::user_space, 0x100000) :
|
|
|
|
|
flags == SYS_MEMORY_PAGE_SIZE_64K ? vm::alloc(size, vm::user_space, 0x10000) :
|
2015-07-10 16:45:16 +02:00
|
|
|
throw EXCEPTION("Unexpected flags");
|
|
|
|
|
|
2014-11-29 18:01:04 +01:00
|
|
|
if (!addr)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_ENOMEM;
|
2015-07-10 16:45:16 +02:00
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Write back the start address of the allocated area
|
|
|
|
|
*alloc_addr = addr;
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
s32 sys_memory_allocate_from_container(u32 size, u32 cid, u64 flags, vm::ptr<u32> alloc_addr)
|
2014-06-25 00:38:34 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.warning("sys_memory_allocate_from_container(size=0x%x, cid=0x%x, flags=0x%llx, alloc_addr=*0x%x)", size, cid, flags, alloc_addr);
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
LV2_LOCK;
|
|
|
|
|
|
|
|
|
|
// Check if this container ID is valid
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto ct = idm::get<lv2_memory_container_t>(cid);
|
2015-04-14 04:00:31 +02:00
|
|
|
|
|
|
|
|
if (!ct)
|
|
|
|
|
{
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_ESRCH;
|
2015-04-14 04:00:31 +02:00
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Check allocation size
|
|
|
|
|
switch (flags)
|
2014-06-25 00:38:34 +02:00
|
|
|
{
|
|
|
|
|
case SYS_MEMORY_PAGE_SIZE_1M:
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
|
|
|
|
if (size % 0x100000)
|
|
|
|
|
{
|
|
|
|
|
return CELL_EALIGN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
case SYS_MEMORY_PAGE_SIZE_64K:
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
|
|
|
|
if (size % 0x10000)
|
|
|
|
|
{
|
|
|
|
|
return CELL_EALIGN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
return CELL_EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-11 22:44:53 +02:00
|
|
|
if (ct->used > ct->size)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2015-07-11 22:44:53 +02:00
|
|
|
throw EXCEPTION("Unexpected amount of memory taken (0x%x, size=0x%x)", ct->used.load(), ct->size);
|
2014-06-25 00:38:34 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Check memory availability
|
2015-07-11 22:44:53 +02:00
|
|
|
if (size > ct->size - ct->used)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_ENOMEM;
|
2015-07-10 16:45:16 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-12 13:52:55 +02:00
|
|
|
const auto area = vm::get(vm::user_space);
|
|
|
|
|
|
|
|
|
|
// Return "physical" memory required for allocation
|
|
|
|
|
area->used -= size;
|
|
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Allocate memory
|
|
|
|
|
const u32 addr =
|
2015-07-12 13:52:55 +02:00
|
|
|
flags == SYS_MEMORY_PAGE_SIZE_1M ? area->alloc(size, 0x100000) :
|
|
|
|
|
flags == SYS_MEMORY_PAGE_SIZE_64K ? area->alloc(size, 0x10000) :
|
2015-07-10 16:45:16 +02:00
|
|
|
throw EXCEPTION("Unexpected flags");
|
|
|
|
|
|
|
|
|
|
if (!addr)
|
|
|
|
|
{
|
|
|
|
|
throw EXCEPTION("Memory not allocated (ct=0x%x, size=0x%x)", cid, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the address and size in the container
|
|
|
|
|
ct->allocs.emplace(addr, size);
|
2015-07-11 22:44:53 +02:00
|
|
|
ct->used += size;
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
// Write back the start address of the allocated area.
|
2015-07-10 16:45:16 +02:00
|
|
|
*alloc_addr = addr;
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
s32 sys_memory_free(u32 addr)
|
2014-06-25 00:38:34 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.warning("sys_memory_free(addr=0x%x)", addr);
|
2015-07-10 16:45:16 +02:00
|
|
|
|
|
|
|
|
LV2_LOCK;
|
|
|
|
|
|
2015-07-12 13:52:55 +02:00
|
|
|
const auto area = vm::get(vm::user_space);
|
|
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Check all memory containers
|
2016-05-13 15:55:34 +02:00
|
|
|
const auto ct = idm::select<lv2_memory_container_t>([&](u32, lv2_memory_container_t& ct)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2016-05-13 15:55:34 +02:00
|
|
|
return ct.allocs.count(addr) != 0;
|
|
|
|
|
});
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2016-05-13 15:55:34 +02:00
|
|
|
if (ct)
|
|
|
|
|
{
|
|
|
|
|
const u32 size = ct->allocs.at(addr);
|
2015-07-12 13:52:55 +02:00
|
|
|
|
2016-05-13 15:55:34 +02:00
|
|
|
if (!area->dealloc(addr))
|
|
|
|
|
{
|
|
|
|
|
throw EXCEPTION("Memory not deallocated (cid=0x%x, addr=0x%x, size=0x%x)", ct->id, addr, size);
|
|
|
|
|
}
|
2015-07-10 16:45:16 +02:00
|
|
|
|
2016-05-13 15:55:34 +02:00
|
|
|
ct->allocs.erase(addr);
|
2015-07-10 16:45:16 +02:00
|
|
|
|
2016-05-13 15:55:34 +02:00
|
|
|
// Return "memory"
|
|
|
|
|
ct->used -= size;
|
|
|
|
|
area->used += size;
|
2015-07-12 13:52:55 +02:00
|
|
|
|
2016-05-13 15:55:34 +02:00
|
|
|
return CELL_OK;
|
2015-07-10 16:45:16 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-12 13:52:55 +02:00
|
|
|
if (!area->dealloc(addr))
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2014-08-05 19:33:02 +02:00
|
|
|
return CELL_EINVAL;
|
2015-07-10 16:45:16 +02:00
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-02 03:05:13 +02:00
|
|
|
s32 sys_memory_get_page_attribute(u32 addr, vm::ptr<sys_page_attr_t> attr)
|
2014-06-25 00:38:34 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.error("sys_memory_get_page_attribute(addr=0x%x, attr=*0x%x)", addr, attr);
|
2015-07-10 16:45:16 +02:00
|
|
|
|
|
|
|
|
LV2_LOCK;
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
// TODO: Implement per thread page attribute setting.
|
2014-08-27 21:05:46 +02:00
|
|
|
attr->attribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITE
|
|
|
|
|
attr->access_right = 0xFull; // SYS_MEMORY_ACCESS_RIGHT_ANY
|
|
|
|
|
attr->page_size = 4096;
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-02 03:05:13 +02:00
|
|
|
s32 sys_memory_get_user_memory_size(vm::ptr<sys_memory_info_t> mem_info)
|
2014-06-25 00:38:34 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.warning("sys_memory_get_user_memory_size(mem_info=*0x%x)", mem_info);
|
2015-07-10 16:45:16 +02:00
|
|
|
|
|
|
|
|
LV2_LOCK;
|
|
|
|
|
|
|
|
|
|
u32 reserved = 0;
|
|
|
|
|
|
|
|
|
|
// Check all memory containers
|
2016-05-13 15:55:34 +02:00
|
|
|
idm::select<lv2_memory_container_t>([&](u32, lv2_memory_container_t& ct)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2016-05-13 15:55:34 +02:00
|
|
|
reserved += ct.size;
|
|
|
|
|
});
|
2015-07-11 22:44:53 +02:00
|
|
|
|
|
|
|
|
const auto area = vm::get(vm::user_space);
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Fetch the user memory available
|
2015-07-11 22:44:53 +02:00
|
|
|
mem_info->total_user_memory = area->size - reserved;
|
2015-09-18 00:41:14 +02:00
|
|
|
mem_info->available_user_memory = area->size - area->used;
|
2015-07-10 16:45:16 +02:00
|
|
|
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
s32 sys_memory_container_create(vm::ptr<u32> cid, u32 size)
|
2014-06-25 00:38:34 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.warning("sys_memory_container_create(cid=*0x%x, size=0x%x)", cid, size);
|
2015-07-10 16:45:16 +02:00
|
|
|
|
|
|
|
|
LV2_LOCK;
|
|
|
|
|
|
|
|
|
|
// Round down to 1 MB granularity
|
|
|
|
|
size &= ~0xfffff;
|
|
|
|
|
|
|
|
|
|
if (!size)
|
|
|
|
|
{
|
|
|
|
|
return CELL_ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 reserved = 0;
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Check all memory containers
|
2016-05-13 15:55:34 +02:00
|
|
|
idm::select<lv2_memory_container_t>([&](u32, lv2_memory_container_t& ct)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2016-05-13 15:55:34 +02:00
|
|
|
reserved += ct.size;
|
|
|
|
|
});
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-11 22:44:53 +02:00
|
|
|
const auto area = vm::get(vm::user_space);
|
|
|
|
|
|
2015-09-18 00:41:14 +02:00
|
|
|
if (area->size < reserved + size || area->size - area->used < size)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_ENOMEM;
|
2015-07-10 16:45:16 +02:00
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Create the memory container
|
2015-08-05 17:30:32 +02:00
|
|
|
*cid = idm::make<lv2_memory_container_t>(size);
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s32 sys_memory_container_destroy(u32 cid)
|
|
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.warning("sys_memory_container_destroy(cid=0x%x)", cid);
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
LV2_LOCK;
|
|
|
|
|
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto ct = idm::get<lv2_memory_container_t>(cid);
|
2015-04-14 04:00:31 +02:00
|
|
|
|
|
|
|
|
if (!ct)
|
|
|
|
|
{
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_ESRCH;
|
2015-04-14 04:00:31 +02:00
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
// Check if some memory is not deallocated (the container cannot be destroyed in this case)
|
2015-09-18 00:41:14 +02:00
|
|
|
if (ct->used)
|
2015-07-10 16:45:16 +02:00
|
|
|
{
|
|
|
|
|
return CELL_EBUSY;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-05 17:30:32 +02:00
|
|
|
idm::remove<lv2_memory_container_t>(cid);
|
2014-06-25 00:38:34 +02:00
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-02 03:05:13 +02:00
|
|
|
s32 sys_memory_container_get_size(vm::ptr<sys_memory_info_t> mem_info, u32 cid)
|
2014-06-25 00:38:34 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_memory.warning("sys_memory_container_get_size(mem_info=*0x%x, cid=0x%x)", mem_info, cid);
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
LV2_LOCK;
|
|
|
|
|
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto ct = idm::get<lv2_memory_container_t>(cid);
|
2015-04-14 04:00:31 +02:00
|
|
|
|
|
|
|
|
if (!ct)
|
|
|
|
|
{
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_ESRCH;
|
2015-04-14 04:00:31 +02:00
|
|
|
}
|
2014-06-25 00:38:34 +02:00
|
|
|
|
2015-07-10 16:45:16 +02:00
|
|
|
mem_info->total_user_memory = ct->size; // total container memory
|
2015-09-18 00:41:14 +02:00
|
|
|
mem_info->available_user_memory = ct->size - ct->used; // available container memory
|
2015-07-10 16:45:16 +02:00
|
|
|
|
2014-06-25 00:38:34 +02:00
|
|
|
return CELL_OK;
|
|
|
|
|
}
|