mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-30 23:49:01 -05:00 
			
		
		
		
	Merge pull request #1907 from lioncash/attribute
kernel/svc: Implement svcSetMemoryAttribute
This commit is contained in:
		| @@ -254,11 +254,52 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | ||||
|     return vm_manager.ReprotectRange(addr, size, converted_permissions); | ||||
| } | ||||
|  | ||||
| static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { | ||||
|     LOG_WARNING(Kernel_SVC, | ||||
|                 "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr, | ||||
|                 size, state0, state1); | ||||
|     return RESULT_SUCCESS; | ||||
| static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) { | ||||
|     LOG_DEBUG(Kernel_SVC, | ||||
|               "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | ||||
|               size, mask, attribute); | ||||
|  | ||||
|     if (!Common::Is4KBAligned(address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     if (size == 0 || !Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.", | ||||
|                   size); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     if (!IsValidAddressRange(address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})", | ||||
|                   address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
|  | ||||
|     const auto mem_attribute = static_cast<MemoryAttribute>(attribute); | ||||
|     const auto mem_mask = static_cast<MemoryAttribute>(mask); | ||||
|     const auto attribute_with_mask = mem_attribute | mem_mask; | ||||
|  | ||||
|     if (attribute_with_mask != mem_mask) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}", | ||||
|                   attribute, mask); | ||||
|         return ERR_INVALID_COMBINATION; | ||||
|     } | ||||
|  | ||||
|     if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) { | ||||
|         LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8)."); | ||||
|         return ERR_INVALID_COMBINATION; | ||||
|     } | ||||
|  | ||||
|     auto& vm_manager = Core::CurrentProcess()->VMManager(); | ||||
|     if (!IsInsideAddressSpace(vm_manager, address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Given address (0x{:016X}) is outside the bounds of the address space.", address); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
|  | ||||
|     return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute); | ||||
| } | ||||
|  | ||||
| /// Maps a memory range into a different range. | ||||
|   | ||||
| @@ -37,7 +37,7 @@ static const char* GetMemoryStateName(MemoryState state) { | ||||
|  | ||||
| bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | ||||
|     ASSERT(base + size == next.base); | ||||
|     if (permissions != next.permissions || meminfo_state != next.meminfo_state || | ||||
|     if (permissions != next.permissions || state != next.state || attribute != next.attribute || | ||||
|         type != next.type) { | ||||
|         return false; | ||||
|     } | ||||
| @@ -115,7 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | ||||
|  | ||||
|     final_vma.type = VMAType::AllocatedMemoryBlock; | ||||
|     final_vma.permissions = VMAPermission::ReadWrite; | ||||
|     final_vma.meminfo_state = state; | ||||
|     final_vma.state = state; | ||||
|     final_vma.backing_block = std::move(block); | ||||
|     final_vma.offset = offset; | ||||
|     UpdatePageTableForVMA(final_vma); | ||||
| @@ -140,7 +140,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | ||||
|  | ||||
|     final_vma.type = VMAType::BackingMemory; | ||||
|     final_vma.permissions = VMAPermission::ReadWrite; | ||||
|     final_vma.meminfo_state = state; | ||||
|     final_vma.state = state; | ||||
|     final_vma.backing_memory = memory; | ||||
|     UpdatePageTableForVMA(final_vma); | ||||
|  | ||||
| @@ -177,7 +177,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u6 | ||||
|  | ||||
|     final_vma.type = VMAType::MMIO; | ||||
|     final_vma.permissions = VMAPermission::ReadWrite; | ||||
|     final_vma.meminfo_state = state; | ||||
|     final_vma.state = state; | ||||
|     final_vma.paddr = paddr; | ||||
|     final_vma.mmio_handler = std::move(mmio_handler); | ||||
|     UpdatePageTableForVMA(final_vma); | ||||
| @@ -189,7 +189,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { | ||||
|     VirtualMemoryArea& vma = vma_handle->second; | ||||
|     vma.type = VMAType::Free; | ||||
|     vma.permissions = VMAPermission::None; | ||||
|     vma.meminfo_state = MemoryState::Unmapped; | ||||
|     vma.state = MemoryState::Unmapped; | ||||
|  | ||||
|     vma.backing_block = nullptr; | ||||
|     vma.offset = 0; | ||||
| @@ -308,9 +308,10 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const { | ||||
|  | ||||
|     if (IsValidHandle(vma)) { | ||||
|         memory_info.base_address = vma->second.base; | ||||
|         memory_info.attributes = ToSvcMemoryAttribute(vma->second.attribute); | ||||
|         memory_info.permission = static_cast<u32>(vma->second.permissions); | ||||
|         memory_info.size = vma->second.size; | ||||
|         memory_info.state = ToSvcMemoryState(vma->second.meminfo_state); | ||||
|         memory_info.state = ToSvcMemoryState(vma->second.state); | ||||
|     } else { | ||||
|         memory_info.base_address = address_space_end; | ||||
|         memory_info.permission = static_cast<u32>(VMAPermission::None); | ||||
| @@ -321,6 +322,34 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const { | ||||
|     return memory_info; | ||||
| } | ||||
|  | ||||
| ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, | ||||
|                                          MemoryAttribute attribute) { | ||||
|     constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped; | ||||
|     constexpr auto attribute_mask = ~ignore_mask; | ||||
|  | ||||
|     const auto result = CheckRangeState( | ||||
|         address, size, MemoryState::FlagUncached, MemoryState::FlagUncached, VMAPermission::None, | ||||
|         VMAPermission::None, attribute_mask, MemoryAttribute::None, ignore_mask); | ||||
|  | ||||
|     if (result.Failed()) { | ||||
|         return result.Code(); | ||||
|     } | ||||
|  | ||||
|     const auto [prev_state, prev_permissions, prev_attributes] = *result; | ||||
|     const auto new_attribute = (prev_attributes & ~mask) | (mask & attribute); | ||||
|  | ||||
|     const auto carve_result = CarveVMARange(address, size); | ||||
|     if (carve_result.Failed()) { | ||||
|         return carve_result.Code(); | ||||
|     } | ||||
|  | ||||
|     auto vma_iter = *carve_result; | ||||
|     vma_iter->second.attribute = new_attribute; | ||||
|  | ||||
|     MergeAdjacent(vma_iter); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { | ||||
|     const auto vma = FindVMA(src_addr); | ||||
|  | ||||
| @@ -364,7 +393,7 @@ void VMManager::LogLayout() const { | ||||
|                   (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', | ||||
|                   (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', | ||||
|                   (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', | ||||
|                   GetMemoryStateName(vma.meminfo_state)); | ||||
|                   GetMemoryStateName(vma.state)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -591,6 +620,66 @@ void VMManager::ClearPageTable() { | ||||
|               Memory::PageType::Unmapped); | ||||
| } | ||||
|  | ||||
| VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask, | ||||
|                                                    MemoryState state, VMAPermission permission_mask, | ||||
|                                                    VMAPermission permissions, | ||||
|                                                    MemoryAttribute attribute_mask, | ||||
|                                                    MemoryAttribute attribute, | ||||
|                                                    MemoryAttribute ignore_mask) const { | ||||
|     auto iter = FindVMA(address); | ||||
|  | ||||
|     // If we don't have a valid VMA handle at this point, then it means this is | ||||
|     // being called with an address outside of the address space, which is definitely | ||||
|     // indicative of a bug, as this function only operates on mapped memory regions. | ||||
|     DEBUG_ASSERT(IsValidHandle(iter)); | ||||
|  | ||||
|     const VAddr end_address = address + size - 1; | ||||
|     const MemoryAttribute initial_attributes = iter->second.attribute; | ||||
|     const VMAPermission initial_permissions = iter->second.permissions; | ||||
|     const MemoryState initial_state = iter->second.state; | ||||
|  | ||||
|     while (true) { | ||||
|         // The iterator should be valid throughout the traversal. Hitting the end of | ||||
|         // the mapped VMA regions is unquestionably indicative of a bug. | ||||
|         DEBUG_ASSERT(IsValidHandle(iter)); | ||||
|  | ||||
|         const auto& vma = iter->second; | ||||
|  | ||||
|         if (vma.state != initial_state) { | ||||
|             return ERR_INVALID_ADDRESS_STATE; | ||||
|         } | ||||
|  | ||||
|         if ((vma.state & state_mask) != state) { | ||||
|             return ERR_INVALID_ADDRESS_STATE; | ||||
|         } | ||||
|  | ||||
|         if (vma.permissions != initial_permissions) { | ||||
|             return ERR_INVALID_ADDRESS_STATE; | ||||
|         } | ||||
|  | ||||
|         if ((vma.permissions & permission_mask) != permissions) { | ||||
|             return ERR_INVALID_ADDRESS_STATE; | ||||
|         } | ||||
|  | ||||
|         if ((vma.attribute | ignore_mask) != (initial_attributes | ignore_mask)) { | ||||
|             return ERR_INVALID_ADDRESS_STATE; | ||||
|         } | ||||
|  | ||||
|         if ((vma.attribute & attribute_mask) != attribute) { | ||||
|             return ERR_INVALID_ADDRESS_STATE; | ||||
|         } | ||||
|  | ||||
|         if (end_address <= vma.EndAddress()) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         ++iter; | ||||
|     } | ||||
|  | ||||
|     return MakeResult( | ||||
|         std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask)); | ||||
| } | ||||
|  | ||||
| u64 VMManager::GetTotalMemoryUsage() const { | ||||
|     LOG_WARNING(Kernel, "(STUBBED) called"); | ||||
|     return 0xF8000000; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <tuple> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/result.h" | ||||
| @@ -43,6 +44,88 @@ enum class VMAPermission : u8 { | ||||
|     ReadWriteExecute = Read | Write | Execute, | ||||
| }; | ||||
|  | ||||
| constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { | ||||
|     return static_cast<VMAPermission>(u32(lhs) | u32(rhs)); | ||||
| } | ||||
|  | ||||
| constexpr VMAPermission operator&(VMAPermission lhs, VMAPermission rhs) { | ||||
|     return static_cast<VMAPermission>(u32(lhs) & u32(rhs)); | ||||
| } | ||||
|  | ||||
| constexpr VMAPermission operator^(VMAPermission lhs, VMAPermission rhs) { | ||||
|     return static_cast<VMAPermission>(u32(lhs) ^ u32(rhs)); | ||||
| } | ||||
|  | ||||
| constexpr VMAPermission operator~(VMAPermission permission) { | ||||
|     return static_cast<VMAPermission>(~u32(permission)); | ||||
| } | ||||
|  | ||||
| constexpr VMAPermission& operator|=(VMAPermission& lhs, VMAPermission rhs) { | ||||
|     lhs = lhs | rhs; | ||||
|     return lhs; | ||||
| } | ||||
|  | ||||
| constexpr VMAPermission& operator&=(VMAPermission& lhs, VMAPermission rhs) { | ||||
|     lhs = lhs & rhs; | ||||
|     return lhs; | ||||
| } | ||||
|  | ||||
| constexpr VMAPermission& operator^=(VMAPermission& lhs, VMAPermission rhs) { | ||||
|     lhs = lhs ^ rhs; | ||||
|     return lhs; | ||||
| } | ||||
|  | ||||
| /// Attribute flags that can be applied to a VMA | ||||
| enum class MemoryAttribute : u32 { | ||||
|     Mask = 0xFF, | ||||
|  | ||||
|     /// No particular qualities | ||||
|     None = 0, | ||||
|     /// Memory locked/borrowed for use. e.g. This would be used by transfer memory. | ||||
|     Locked = 1, | ||||
|     /// Memory locked for use by IPC-related internals. | ||||
|     LockedForIPC = 2, | ||||
|     /// Mapped as part of the device address space. | ||||
|     DeviceMapped = 4, | ||||
|     /// Uncached memory | ||||
|     Uncached = 8, | ||||
| }; | ||||
|  | ||||
| constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { | ||||
|     return static_cast<MemoryAttribute>(u32(lhs) | u32(rhs)); | ||||
| } | ||||
|  | ||||
| constexpr MemoryAttribute operator&(MemoryAttribute lhs, MemoryAttribute rhs) { | ||||
|     return static_cast<MemoryAttribute>(u32(lhs) & u32(rhs)); | ||||
| } | ||||
|  | ||||
| constexpr MemoryAttribute operator^(MemoryAttribute lhs, MemoryAttribute rhs) { | ||||
|     return static_cast<MemoryAttribute>(u32(lhs) ^ u32(rhs)); | ||||
| } | ||||
|  | ||||
| constexpr MemoryAttribute operator~(MemoryAttribute attribute) { | ||||
|     return static_cast<MemoryAttribute>(~u32(attribute)); | ||||
| } | ||||
|  | ||||
| constexpr MemoryAttribute& operator|=(MemoryAttribute& lhs, MemoryAttribute rhs) { | ||||
|     lhs = lhs | rhs; | ||||
|     return lhs; | ||||
| } | ||||
|  | ||||
| constexpr MemoryAttribute& operator&=(MemoryAttribute& lhs, MemoryAttribute rhs) { | ||||
|     lhs = lhs & rhs; | ||||
|     return lhs; | ||||
| } | ||||
|  | ||||
| constexpr MemoryAttribute& operator^=(MemoryAttribute& lhs, MemoryAttribute rhs) { | ||||
|     lhs = lhs ^ rhs; | ||||
|     return lhs; | ||||
| } | ||||
|  | ||||
| constexpr u32 ToSvcMemoryAttribute(MemoryAttribute attribute) { | ||||
|     return static_cast<u32>(attribute & MemoryAttribute::Mask); | ||||
| } | ||||
|  | ||||
| // clang-format off | ||||
| /// Represents memory states and any relevant flags, as used by the kernel. | ||||
| /// svcQueryMemory interprets these by masking away all but the first eight | ||||
| @@ -174,6 +257,16 @@ struct PageInfo { | ||||
|  * also backed by a single host memory allocation. | ||||
|  */ | ||||
| struct VirtualMemoryArea { | ||||
|     /// Gets the starting (base) address of this VMA. | ||||
|     VAddr StartAddress() const { | ||||
|         return base; | ||||
|     } | ||||
|  | ||||
|     /// Gets the ending address of this VMA. | ||||
|     VAddr EndAddress() const { | ||||
|         return base + size - 1; | ||||
|     } | ||||
|  | ||||
|     /// Virtual base address of the region. | ||||
|     VAddr base = 0; | ||||
|     /// Size of the region. | ||||
| @@ -181,8 +274,8 @@ struct VirtualMemoryArea { | ||||
|  | ||||
|     VMAType type = VMAType::Free; | ||||
|     VMAPermission permissions = VMAPermission::None; | ||||
|     /// Tag returned by svcQueryMemory. Not otherwise used. | ||||
|     MemoryState meminfo_state = MemoryState::Unmapped; | ||||
|     MemoryState state = MemoryState::Unmapped; | ||||
|     MemoryAttribute attribute = MemoryAttribute::None; | ||||
|  | ||||
|     // Settings for type = AllocatedMemoryBlock | ||||
|     /// Memory block backing this VMA. | ||||
| @@ -299,6 +392,19 @@ public: | ||||
|     /// | ||||
|     MemoryInfo QueryMemory(VAddr address) const; | ||||
|  | ||||
|     /// Sets an attribute across the given address range. | ||||
|     /// | ||||
|     /// @param address   The starting address | ||||
|     /// @param size      The size of the range to set the attribute on. | ||||
|     /// @param mask      The attribute mask | ||||
|     /// @param attribute The attribute to set across the given address range | ||||
|     /// | ||||
|     /// @returns RESULT_SUCCESS if successful | ||||
|     /// @returns ERR_INVALID_ADDRESS_STATE if the attribute could not be set. | ||||
|     /// | ||||
|     ResultCode SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, | ||||
|                                   MemoryAttribute attribute); | ||||
|  | ||||
|     /** | ||||
|      * Scans all VMAs and updates the page table range of any that use the given vector as backing | ||||
|      * memory. This should be called after any operation that causes reallocation of the vector. | ||||
| @@ -435,6 +541,35 @@ private: | ||||
|     /// Clears out the page table | ||||
|     void ClearPageTable(); | ||||
|  | ||||
|     using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; | ||||
|  | ||||
|     /// Checks if an address range adheres to the specified states provided. | ||||
|     /// | ||||
|     /// @param address         The starting address of the address range. | ||||
|     /// @param size            The size of the address range. | ||||
|     /// @param state_mask      The memory state mask. | ||||
|     /// @param state           The state to compare the individual VMA states against, | ||||
|     ///                        which is done in the form of: (vma.state & state_mask) != state. | ||||
|     /// @param permission_mask The memory permissions mask. | ||||
|     /// @param permissions     The permission to compare the individual VMA permissions against, | ||||
|     ///                        which is done in the form of: | ||||
|     ///                        (vma.permission & permission_mask) != permission. | ||||
|     /// @param attribute_mask  The memory attribute mask. | ||||
|     /// @param attribute       The memory attributes to compare the individual VMA attributes | ||||
|     ///                        against, which is done in the form of: | ||||
|     ///                        (vma.attributes & attribute_mask) != attribute. | ||||
|     /// @param ignore_mask     The memory attributes to ignore during the check. | ||||
|     /// | ||||
|     /// @returns If successful, returns a tuple containing the memory attributes | ||||
|     ///          (with ignored bits specified by ignore_mask unset), memory permissions, and | ||||
|     ///          memory state across the memory range. | ||||
|     /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. | ||||
|     /// | ||||
|     CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, | ||||
|                                  VMAPermission permission_mask, VMAPermission permissions, | ||||
|                                  MemoryAttribute attribute_mask, MemoryAttribute attribute, | ||||
|                                  MemoryAttribute ignore_mask) const; | ||||
|  | ||||
|     /** | ||||
|      * A map covering the entirety of the managed address space, keyed by the `base` field of each | ||||
|      * VMA. It must always be modified by splitting or merging VMAs, so that the invariant | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei