diff --git a/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp b/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp index ebfe49c40c..87e7458cf4 100644 --- a/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp @@ -46,7 +46,7 @@ u32 local_addr = 0; /* * Get usable local memory size for a specific game SDK version * Example: For 0x00446000 (FW 4.46) we get a localSize of 0x0F900000 (249MB) - */ + */ u32 gcmGetLocalMemorySize(u32 sdk_version) { if (sdk_version >= 0x00220000) @@ -397,16 +397,8 @@ s32 _cellGcmInitBody(vm::pptr context, u32 cmdSize, u32 ioSi m_config->current_config.coreFrequency = 500000000; // Create contexts - - u32 rsx_ctxaddr = 0; - for (u32 addr = 0x30000000; addr < 0xC0000000; addr += 0x10000000) - { - if (vm::map(addr, 0x10000000, 0x400)) - { - rsx_ctxaddr = addr; - break; - } - } + auto ctx_area = vm::find_map(0x10000000, 0x10000000, 0x403); + u32 rsx_ctxaddr = ctx_area ? ctx_area->addr : 0; if (!rsx_ctxaddr || vm::falloc(rsx_ctxaddr, 0x400000) != rsx_ctxaddr) fmt::throw_exception("Failed to alloc rsx context."); @@ -898,7 +890,7 @@ s32 cellGcmAddressToOffset(u32 address, vm::ptr offset) { result = address - 0xC0000000; } - // Address in main memory else check + // Address in main memory else check else { const u32 upper12Bits = offsetTable.ioAddress[address >> 20]; diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index fe9beb7ea2..466a1ae060 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -44,7 +44,7 @@ error_code sys_memory_allocate(u32 size, u64 flags, vm::ptr alloc_addr) } // Allocate memory, write back the start address of the allocated area - *alloc_addr = verify(HERE, vm::alloc(size, vm::user_space, align)); + *alloc_addr = verify(HERE, vm::alloc(size, align == 0x10000 ? vm::user64k : vm::user1m, align)); return CELL_OK; } @@ -94,7 +94,7 @@ error_code sys_memory_allocate_from_container(u32 size, u32 cid, u64 flags, vm:: const auto mem = idm::make_ptr(size, align, flags, ct.ptr); // Allocate memory - *alloc_addr = verify(HERE, vm::get(vm::user_space)->alloc(size, mem->align, &mem->shm)); + *alloc_addr = verify(HERE, vm::get(align == 0x10000 ? vm::user64k : vm::user1m)->alloc(size, mem->align, &mem->shm)); return CELL_OK; } @@ -103,7 +103,12 @@ error_code sys_memory_free(u32 addr) { sys_memory.warning("sys_memory_free(addr=0x%x)", addr); - const auto area = vm::get(vm::user_space); + const auto area = vm::get(vm::any, addr); + + if ((area->flags & 3) != 1) + { + return {CELL_EINVAL, addr}; + } const auto shm = area->get(addr); diff --git a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp index 2936e867eb..5c33825b4f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp @@ -45,13 +45,10 @@ error_code sys_mmapper_allocate_address(u64 size, u64 flags, u64 alignment, vm:: case 0x40000000: case 0x80000000: { - for (u64 addr = ::align(0x30000000, alignment); addr < 0xC0000000; addr += alignment) + if (const auto area = vm::find_map(static_cast(size), static_cast(alignment), flags & SYS_MEMORY_PAGE_SIZE_MASK)) { - if (const auto area = vm::map(static_cast(addr), static_cast(size), flags)) - { - *alloc_addr = static_cast(addr); - return CELL_OK; - } + *alloc_addr = area->addr; + return CELL_OK; } return CELL_ENOMEM; @@ -191,6 +188,11 @@ error_code sys_mmapper_free_address(u32 addr) { sys_mmapper.error("sys_mmapper_free_address(addr=0x%x)", addr); + if (addr < 0x20000000 || addr >= 0xC0000000) + { + return {CELL_EINVAL, addr}; + } + // If page fault notify exists and an address in this area is faulted, we can't free the memory. auto pf_events = fxm::get_always(); semaphore_lock pf_lock(pf_events->pf_mutex); @@ -209,7 +211,7 @@ error_code sys_mmapper_free_address(u32 addr) if (!area) { - return CELL_EINVAL; + return {CELL_EINVAL, addr}; } if (!area.unique()) @@ -272,7 +274,7 @@ error_code sys_mmapper_map_shared_memory(u32 addr, u32 mem_id, u64 flags) const auto area = vm::get(vm::any, addr); - if (!area || addr < 0x30000000 || addr >= 0xC0000000) + if (!area || addr < 0x20000000 || addr >= 0xC0000000) { return CELL_EINVAL; } @@ -320,7 +322,7 @@ error_code sys_mmapper_search_and_map(u32 start_addr, u32 mem_id, u64 flags, vm: const auto area = vm::get(vm::any, start_addr); - if (!area || start_addr < 0x30000000 || start_addr >= 0xC0000000) + if (!area || start_addr < 0x20000000 || start_addr >= 0xC0000000) { return {CELL_EINVAL, start_addr}; } @@ -353,7 +355,7 @@ error_code sys_mmapper_unmap_shared_memory(u32 addr, vm::ptr mem_id) const auto area = vm::get(vm::any, addr); - if (!area || addr < 0x30000000 || addr >= 0xC0000000) + if (!area || addr < 0x20000000 || addr >= 0xC0000000) { return {CELL_EINVAL, addr}; } diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 13185cd951..1e2967a725 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -211,7 +211,7 @@ s32 sys_rsx_context_iounmap(u32 context_id, u32 io, u32 size) } const u32 end = (io >>= 20) + (size >>= 20); - for (u32 ea = RSXIOMem.ea[io]; io < end;) + for (u32 ea = RSXIOMem.ea[io]; io < end;) { RSXIOMem.io[ea++] = 0xFFFF; RSXIOMem.ea[io++] = 0xFFFF; @@ -446,16 +446,11 @@ s32 sys_rsx_device_map(vm::ptr dev_addr, vm::ptr a2, u32 dev_id) return CELL_EINVAL; // sys_rsx_device_map called twice } - for (u32 addr = 0x30000000; addr < 0xC0000000; addr += 0x10000000) + if (const auto area = vm::find_map(0x10000000, 0x10000000, 0x403)) { - if (vm::map(addr, 0x10000000, 0x400)) - { - vm::falloc(addr, 0x400000); - - m_sysrsx->rsx_context_addr = *dev_addr = addr; - - return CELL_OK; - } + vm::falloc(area->addr, 0x400000); + m_sysrsx->rsx_context_addr = *dev_addr = area->addr; + return CELL_OK; } return CELL_ENOMEM; diff --git a/rpcs3/Emu/Cell/lv2/sys_vm.cpp b/rpcs3/Emu/Cell/lv2/sys_vm.cpp index 41fcd3420a..e5bf6fe607 100644 --- a/rpcs3/Emu/Cell/lv2/sys_vm.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_vm.cpp @@ -20,19 +20,14 @@ error_code sys_vm_memory_map(u32 vsize, u32 psize, u32 cid, u64 flag, u64 policy return CELL_ESRCH; } - // Look for unmapped space (roughly) - for (u32 found = 0x30000000; found <= 0xC0000000 - vsize; found += 0x1000000) + // Look for unmapped space + if (const auto area = vm::find_map(vsize, vsize == 0x10000000 ? 0x10000000 : 0x1000000, 2 | (flag & SYS_MEMORY_PAGE_SIZE_MASK))) { - // Try to map - if (const auto area = vm::map(found, vsize, flag)) - { - // Alloc all memory (shall not fail) - verify(HERE), area->alloc(vsize); + // Alloc all memory (shall not fail) + verify(HERE), area->alloc(vsize); - // Write a pointer for the allocated memory - *addr = found; - return CELL_OK; - } + // Write a pointer for the allocated memory + *addr = area->addr; } return CELL_ENOMEM; @@ -52,7 +47,7 @@ error_code sys_vm_unmap(u32 addr) if (!vm::unmap(addr)) { - return CELL_EINVAL; + return {CELL_EINVAL, addr}; } return CELL_OK; diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index ac36c8485b..9314bbf646 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -711,6 +711,41 @@ namespace vm return imp_used(lock); } + static bool _test_map(u32 addr, u32 size) + { + for (auto& block : g_locations) + { + if (block && block->addr >= addr && block->addr <= addr + size - 1) + { + return false; + } + + if (block && addr >= block->addr && addr <= block->addr + block->size - 1) + { + return false; + } + } + + return true; + } + + static std::shared_ptr _find_map(u32 size, u32 align, u64 flags) + { + for (u32 addr = ::align(0x20000000, align); addr < 0xC0000000; addr += align) + { + if (_test_map(addr, size)) + { + auto block = std::make_shared(addr, size, flags); + + g_locations.emplace_back(block); + + return block; + } + } + + return nullptr; + } + std::shared_ptr map(u32 addr, u32 size, u64 flags) { vm::writer_lock lock(0); @@ -720,17 +755,9 @@ namespace vm fmt::throw_exception("Invalid arguments (addr=0x%x, size=0x%x)" HERE, addr, size); } - for (auto& block : g_locations) + if (!_test_map(addr, size)) { - if (block->addr >= addr && block->addr <= addr + size - 1) - { - return nullptr; - } - - if (addr >= block->addr && addr <= block->addr + block->size - 1) - { - return nullptr; - } + return nullptr; } for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++) @@ -748,6 +775,28 @@ namespace vm return block; } + std::shared_ptr find_map(u32 orig_size, u32 align, u64 flags) + { + vm::writer_lock lock(0); + + // Align to minimal page size + const u32 size = ::align(orig_size, 0x10000); + + // Check alignment + if (align < 0x10000 || align != (0x80000000u >> ::cntlz32(align, true))) + { + fmt::throw_exception("Invalid alignment (size=0x%x, align=0x%x)" HERE, size, align); + } + + // Return if size is invalid + if (!size || size > 0x40000000) + { + return nullptr; + } + + return _find_map(size, align, flags); + } + std::shared_ptr unmap(u32 addr, bool must_be_empty) { vm::writer_lock lock(0); @@ -756,6 +805,16 @@ namespace vm { if (*it && (*it)->addr == addr) { + if (must_be_empty && (*it)->flags & 0x3) + { + continue; + } + + if (!must_be_empty && ((*it)->flags & 0x3) != 2) + { + continue; + } + if (must_be_empty && (!it->unique() || (*it)->imp_used(lock))) { return *it; @@ -779,7 +838,25 @@ namespace vm // return selected location if (location < g_locations.size()) { - return g_locations[location]; + auto& loc = g_locations[location]; + + if (!loc) + { + if (location == vm::user64k || location == vm::user1m) + { + g_mutex.lock_upgrade(); + + if (!loc) + { + // Deferred allocation + loc = _find_map(0x10000000, 0x10000000, location == vm::user64k ? 0x201 : 0x401); + } + + g_mutex.lock_degrade(); + } + } + + return loc; } return nullptr; @@ -788,7 +865,7 @@ namespace vm // search location by address for (auto& block : g_locations) { - if (addr >= block->addr && addr <= block->addr + block->size - 1) + if (block && addr >= block->addr && addr <= block->addr + block->size - 1) { return block; } @@ -804,9 +881,10 @@ namespace vm g_locations = { std::make_shared(0x00010000, 0x1FFF0000), // main - std::make_shared(0x20000000, 0x10000000), // user std::make_shared(0xC0000000, 0x10000000), // video std::make_shared(0xD0000000, 0x10000000), // stack + nullptr, // user 64k pages + nullptr, // user 1m pages std::make_shared(0xE0000000, 0x20000000), // SPU reserved }; } diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 5c3815d0ad..c77d519d20 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -21,9 +21,10 @@ namespace vm enum memory_location_t : uint { main, - user_space, video, stack, + user64k, + user1m, memory_location_max, any = 0xffffffff, @@ -177,6 +178,9 @@ namespace vm // Create new memory block with specified parameters and return it std::shared_ptr map(u32 addr, u32 size, u64 flags = 0); + // Create new memory block with at arbitrary position with specified alignment + std::shared_ptr find_map(u32 size, u32 align, u64 flags = 0); + // Delete existing memory block with specified start address, return it std::shared_ptr unmap(u32 addr, bool must_be_empty = false); diff --git a/rpcs3/Emu/RSX/Capture/rsx_replay.cpp b/rpcs3/Emu/RSX/Capture/rsx_replay.cpp index 963a0293f6..9ade6368ee 100644 --- a/rpcs3/Emu/RSX/Capture/rsx_replay.cpp +++ b/rpcs3/Emu/RSX/Capture/rsx_replay.cpp @@ -28,7 +28,8 @@ namespace rsx // 'fake' initialize usermemory // todo: seriously, need to probly watch the replay memory map and just make sure its mapped before we copy rather than do this - vm::falloc(0x20000000, 0x10000000, vm::user_space); + const auto user_mem = vm::get(vm::user64k); + vm::falloc(user_mem->addr, 0x10000000); return contextInfo.context_id; } @@ -37,7 +38,7 @@ namespace rsx { u32 fifo_size = 4; - // run through replay commands to figure out how big command buffer needs to be + // run through replay commands to figure out how big command buffer needs to be // technically we could do this in batches if it gets too big, but we should be fine // as we aren't allocating anything on main memory, although it may make issues with iooffset later for (const auto& rc : frame->replay_commands) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index cf15fbb1c6..1eed52c461 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -1304,7 +1304,7 @@ void Emulator::Resume() std::string dump; - for (u32 i = 0x10000; i < 0x30000000;) + for (u32 i = 0x10000; i < 0x20000000;) { if (vm::check_addr(i)) { diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 8b2cf3ccb6..69f2ce20ae 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -63,14 +63,14 @@ void kernel_explorer::Update() { m_tree->clear(); - const auto vm_block = vm::get(vm::user_space); + const auto dct = fxm::get_always(); - if (!vm_block) + if (!dct) { return; } - const u32 total_memory_usage = vm_block->used(); + const u32 total_memory_usage = dct->used; QTreeWidgetItem* root = new QTreeWidgetItem(); root->setText(0, qstr(fmt::format("Process, ID = 0x00000001, Total Memory Usage = 0x%x (%0.2f MB)", total_memory_usage, (float)total_memory_usage / (1024 * 1024))));