diff options
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/memory/memory_block.h | 23 | ||||
| -rw-r--r-- | src/core/hle/kernel/memory/memory_block_manager.cpp | 36 | ||||
| -rw-r--r-- | src/core/hle/kernel/memory/memory_block_manager.h | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/memory/page_table.cpp | 44 | ||||
| -rw-r--r-- | src/core/hle/kernel/memory/page_table.h | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.h | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 143 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 3 |
9 files changed, 249 insertions, 15 deletions
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h index e11043b60..9db1f7b39 100644 --- a/src/core/hle/kernel/memory/memory_block.h +++ b/src/core/hle/kernel/memory/memory_block.h @@ -17,7 +17,7 @@ namespace Kernel::Memory { enum class MemoryState : u32 { None = 0, - Mask = 0xFFFFFFFF, // TODO(bunnei): This should probable be 0xFF + Mask = 0xFF, All = ~None, FlagCanReprotect = (1 << 8), @@ -253,6 +253,23 @@ public: }; } + void ShareToDevice(MemoryPermission /*new_perm*/) { + ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared || + device_use_count == 0); + attribute |= MemoryAttribute::DeviceShared; + const u16 new_use_count{++device_use_count}; + ASSERT(new_use_count > 0); + } + + void UnshareToDevice(MemoryPermission /*new_perm*/) { + ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared); + const u16 prev_use_count{device_use_count--}; + ASSERT(prev_use_count > 0); + if (prev_use_count == 1) { + attribute &= ~MemoryAttribute::DeviceShared; + } + } + private: constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const { constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask | @@ -287,9 +304,9 @@ private: state = new_state; perm = new_perm; - // TODO(bunnei): Is this right? attribute = static_cast<MemoryAttribute>( - new_attribute /*| (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))*/); + new_attribute | + (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))); } constexpr MemoryBlock Split(VAddr split_addr) { diff --git a/src/core/hle/kernel/memory/memory_block_manager.cpp b/src/core/hle/kernel/memory/memory_block_manager.cpp index 1ebc126c0..900395c37 100644 --- a/src/core/hle/kernel/memory/memory_block_manager.cpp +++ b/src/core/hle/kernel/memory/memory_block_manager.cpp @@ -143,6 +143,42 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState s } } +void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, + MemoryPermission perm) { + const std::size_t prev_count{memory_block_tree.size()}; + const VAddr end_addr{addr + num_pages * PageSize}; + iterator node{memory_block_tree.begin()}; + + while (node != memory_block_tree.end()) { + MemoryBlock* block{&(*node)}; + iterator next_node{std::next(node)}; + const VAddr cur_addr{block->GetAddress()}; + const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; + + if (addr < cur_end_addr && cur_addr < end_addr) { + iterator new_node{node}; + + if (addr > cur_addr) { + memory_block_tree.insert(node, block->Split(addr)); + } + + if (end_addr < cur_end_addr) { + new_node = memory_block_tree.insert(node, block->Split(end_addr)); + } + + lock_func(new_node, perm); + + MergeAdjacent(new_node, next_node); + } + + if (cur_end_addr - 1 >= end_addr - 1) { + break; + } + + node = next_node; + } +} + void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { const_iterator it{FindIterator(start)}; MemoryInfo info{}; diff --git a/src/core/hle/kernel/memory/memory_block_manager.h b/src/core/hle/kernel/memory/memory_block_manager.h index 0f2270f0f..9451b5df6 100644 --- a/src/core/hle/kernel/memory/memory_block_manager.h +++ b/src/core/hle/kernel/memory/memory_block_manager.h @@ -45,6 +45,9 @@ public: MemoryPermission perm = MemoryPermission::None, MemoryAttribute attribute = MemoryAttribute::None); + using LockFunc = std::function<void(iterator, MemoryPermission)>; + void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm); + using IterateFunc = std::function<void(const MemoryInfo&)>; void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index 091e52ca4..3281611f8 100644 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp @@ -840,6 +840,50 @@ ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, s return MakeResult<VAddr>(addr); } +ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { + std::lock_guard lock{page_table_lock}; + + MemoryPermission perm{}; + if (const ResultCode result{CheckMemoryState( + nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute, + MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None, + MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None, + MemoryAttribute::DeviceSharedAndUncached)}; + result.IsError()) { + return result; + } + + block_manager->UpdateLock(addr, size / PageSize, + [](MemoryBlockManager::iterator block, MemoryPermission perm) { + block->ShareToDevice(perm); + }, + perm); + + return RESULT_SUCCESS; +} + +ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { + std::lock_guard lock{page_table_lock}; + + MemoryPermission perm{}; + if (const ResultCode result{CheckMemoryState( + nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute, + MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None, + MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None, + MemoryAttribute::DeviceSharedAndUncached)}; + result.IsError()) { + return result; + } + + block_manager->UpdateLock(addr, size / PageSize, + [](MemoryBlockManager::iterator block, MemoryPermission perm) { + block->UnshareToDevice(perm); + }, + perm); + + return RESULT_SUCCESS; +} + ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) { block_manager = std::make_unique<MemoryBlockManager>(start, end); diff --git a/src/core/hle/kernel/memory/page_table.h b/src/core/hle/kernel/memory/page_table.h index 80384ab0f..a867aa050 100644 --- a/src/core/hle/kernel/memory/page_table.h +++ b/src/core/hle/kernel/memory/page_table.h @@ -53,6 +53,8 @@ public: bool is_map_only, VAddr region_start, std::size_t region_num_pages, MemoryState state, MemoryPermission perm, PAddr map_addr = 0); + ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); + ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); Common::PageTable& PageTableImpl() { return page_table_impl; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index c67696757..0cd467110 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -36,22 +36,22 @@ std::shared_ptr<SharedMemory> SharedMemory::Create( } ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size, - Memory::MemoryPermission permission) { + Memory::MemoryPermission permissions) { const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize}; if (page_list.GetNumPages() != page_count) { UNIMPLEMENTED_MSG("Page count does not match"); } - Memory::MemoryPermission expected = + const Memory::MemoryPermission expected = &target_process == owner_process ? owner_permission : user_permission; - if (permission != expected) { + if (permissions != expected) { UNIMPLEMENTED_MSG("Permission does not match"); } return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared, - permission); + permissions); } } // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index cd16d6412..0ef87235c 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -51,7 +51,7 @@ public: * @param permissions Memory block map permissions (specified by SVC field) */ ResultCode Map(Process& target_process, VAddr address, std::size_t size, - Memory::MemoryPermission permission); + Memory::MemoryPermission permissions); /** * Gets a pointer to the shared memory block diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4134acf65..25b4a23b4 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -55,9 +55,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) { return address + size > address; } -// 8 GiB -constexpr u64 MAIN_MEMORY_SIZE = 0x200000000; - // Helper function that performs the common sanity checks for svcMapMemory // and svcUnmapMemory. This is doable, as both functions perform their sanitizing // in the same order. @@ -1229,6 +1226,142 @@ static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address, return QueryMemory(system, memory_info_address, page_info_address, query_address); } +static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { + LOG_DEBUG(Kernel_SVC, + "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " + "src_address=0x{:016X}, size=0x{:016X}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto process = handle_table.Get<Process>(process_handle); + if (!process) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ERR_INVALID_HANDLE; + } + + auto& page_table = process->PageTable(); + if (!page_table.IsInsideAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!page_table.IsInsideASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return page_table.MapProcessCodeMemory(dst_address, src_address, size); +} + +static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, + u64 dst_address, u64 src_address, u64 size) { + LOG_DEBUG(Kernel_SVC, + "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " + "size=0x{:016X}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto process = handle_table.Get<Process>(process_handle); + if (!process) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ERR_INVALID_HANDLE; + } + + auto& page_table = process->PageTable(); + if (!page_table.IsInsideAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!page_table.IsInsideASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return page_table.UnmapProcessCodeMemory(dst_address, src_address, size); +} + /// Exits the current process static void ExitProcess(Core::System& system) { auto* current_process = system.Kernel().CurrentProcess(); @@ -2256,8 +2389,8 @@ static const FunctionDef SVC_Table_64[] = { {0x74, nullptr, "MapProcessMemory"}, {0x75, nullptr, "UnmapProcessMemory"}, {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, - {0x77, nullptr, "MapProcessCodeMemory"}, - {0x78, nullptr, "UnmapProcessCodeMemory"}, + {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, + {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, {0x79, nullptr, "CreateProcess"}, {0x7A, nullptr, "StartProcess"}, {0x7B, nullptr, "TerminateProcess"}, diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 4c0451c01..a919750a6 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -150,8 +150,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, context.pc = entry_point; context.sp = stack_top; // TODO(merry): Perform a hardware test to determine the below value. - // AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero - context.fpcr = 0x03C00000; + context.fpcr = 0; } ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, |
