3 changed files with 0 additions and 1973 deletions
-
2src/core/CMakeLists.txt
-
1175src/core/hle/kernel/vm_manager.cpp
-
796src/core/hle/kernel/vm_manager.h
1175
src/core/hle/kernel/vm_manager.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,796 +0,0 @@ |
|||||
// Copyright 2015 Citra Emulator Project |
|
||||
// Licensed under GPLv2 or any later version |
|
||||
// Refer to the license.txt file included. |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <map> |
|
||||
#include <memory> |
|
||||
#include <tuple> |
|
||||
#include <vector> |
|
||||
#include "common/common_types.h" |
|
||||
#include "common/memory_hook.h" |
|
||||
#include "common/page_table.h" |
|
||||
#include "core/hle/kernel/physical_memory.h" |
|
||||
#include "core/hle/result.h" |
|
||||
#include "core/memory.h" |
|
||||
|
|
||||
namespace Core { |
|
||||
class System; |
|
||||
} |
|
||||
|
|
||||
namespace FileSys { |
|
||||
enum class ProgramAddressSpaceType : u8; |
|
||||
} |
|
||||
|
|
||||
namespace Kernel { |
|
||||
|
|
||||
enum class VMAType : u8 { |
|
||||
/// VMA represents an unmapped region of the address space. |
|
||||
Free, |
|
||||
/// VMA is backed by a ref-counted allocate memory block. |
|
||||
AllocatedMemoryBlock, |
|
||||
/// VMA is backed by a raw, unmanaged pointer. |
|
||||
BackingMemory, |
|
||||
/// VMA is mapped to MMIO registers at a fixed PAddr. |
|
||||
MMIO, |
|
||||
// TODO(yuriks): Implement MemoryAlias to support MAP/UNMAP |
|
||||
}; |
|
||||
|
|
||||
/// Permissions for mapped memory blocks |
|
||||
enum class VMAPermission : u8 { |
|
||||
None = 0, |
|
||||
Read = 1, |
|
||||
Write = 2, |
|
||||
Execute = 4, |
|
||||
|
|
||||
ReadWrite = Read | Write, |
|
||||
ReadExecute = Read | Execute, |
|
||||
WriteExecute = Write | Execute, |
|
||||
ReadWriteExecute = Read | Write | Execute, |
|
||||
|
|
||||
// Used as a wildcard when checking permissions across memory ranges |
|
||||
All = 0xFF, |
|
||||
}; |
|
||||
|
|
||||
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, |
|
||||
|
|
||||
IpcAndDeviceMapped = LockedForIPC | DeviceMapped, |
|
||||
}; |
|
||||
|
|
||||
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 |
|
||||
/// bits when storing memory state into a MemoryInfo instance. |
|
||||
enum class MemoryState : u32 { |
|
||||
Mask = 0xFF, |
|
||||
FlagProtect = 1U << 8, |
|
||||
FlagDebug = 1U << 9, |
|
||||
FlagIPC0 = 1U << 10, |
|
||||
FlagIPC3 = 1U << 11, |
|
||||
FlagIPC1 = 1U << 12, |
|
||||
FlagMapped = 1U << 13, |
|
||||
FlagCode = 1U << 14, |
|
||||
FlagAlias = 1U << 15, |
|
||||
FlagModule = 1U << 16, |
|
||||
FlagTransfer = 1U << 17, |
|
||||
FlagQueryPhysicalAddressAllowed = 1U << 18, |
|
||||
FlagSharedDevice = 1U << 19, |
|
||||
FlagSharedDeviceAligned = 1U << 20, |
|
||||
FlagIPCBuffer = 1U << 21, |
|
||||
FlagMemoryPoolAllocated = 1U << 22, |
|
||||
FlagMapProcess = 1U << 23, |
|
||||
FlagUncached = 1U << 24, |
|
||||
FlagCodeMemory = 1U << 25, |
|
||||
|
|
||||
// Wildcard used in range checking to indicate all states. |
|
||||
All = 0xFFFFFFFF, |
|
||||
|
|
||||
// Convenience flag sets to reduce repetition |
|
||||
IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, |
|
||||
|
|
||||
CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed | |
|
||||
FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, |
|
||||
|
|
||||
DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer | |
|
||||
FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned | |
|
||||
FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached, |
|
||||
|
|
||||
Unmapped = 0x00, |
|
||||
Io = 0x01 | FlagMapped, |
|
||||
Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, |
|
||||
Code = 0x03 | CodeFlags | FlagMapProcess, |
|
||||
CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory, |
|
||||
Heap = 0x05 | DataFlags | FlagCodeMemory, |
|
||||
Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated, |
|
||||
ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess, |
|
||||
ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory, |
|
||||
|
|
||||
IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | |
|
||||
IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, |
|
||||
|
|
||||
Stack = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed | |
|
||||
FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, |
|
||||
|
|
||||
ThreadLocal = 0x0C | FlagMapped | FlagMemoryPoolAllocated, |
|
||||
|
|
||||
TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed | |
|
||||
FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated | |
|
||||
FlagUncached, |
|
||||
|
|
||||
TransferMemory = 0x0E | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed | |
|
||||
FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, |
|
||||
|
|
||||
ProcessMemory = 0x0F | FlagIPC3 | FlagIPC1 | FlagMapped | FlagMemoryPoolAllocated, |
|
||||
|
|
||||
// Used to signify an inaccessible or invalid memory region with memory queries |
|
||||
Inaccessible = 0x10, |
|
||||
|
|
||||
IpcBuffer1 = 0x11 | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed | |
|
||||
FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, |
|
||||
|
|
||||
IpcBuffer3 = 0x12 | FlagIPC3 | FlagMapped | FlagQueryPhysicalAddressAllowed | |
|
||||
FlagSharedDeviceAligned | FlagMemoryPoolAllocated, |
|
||||
|
|
||||
KernelStack = 0x13 | FlagMapped, |
|
||||
}; |
|
||||
// clang-format on |
|
||||
|
|
||||
constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) { |
|
||||
return static_cast<MemoryState>(u32(lhs) | u32(rhs)); |
|
||||
} |
|
||||
|
|
||||
constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) { |
|
||||
return static_cast<MemoryState>(u32(lhs) & u32(rhs)); |
|
||||
} |
|
||||
|
|
||||
constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) { |
|
||||
return static_cast<MemoryState>(u32(lhs) ^ u32(rhs)); |
|
||||
} |
|
||||
|
|
||||
constexpr MemoryState operator~(MemoryState lhs) { |
|
||||
return static_cast<MemoryState>(~u32(lhs)); |
|
||||
} |
|
||||
|
|
||||
constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) { |
|
||||
lhs = lhs | rhs; |
|
||||
return lhs; |
|
||||
} |
|
||||
|
|
||||
constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) { |
|
||||
lhs = lhs & rhs; |
|
||||
return lhs; |
|
||||
} |
|
||||
|
|
||||
constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) { |
|
||||
lhs = lhs ^ rhs; |
|
||||
return lhs; |
|
||||
} |
|
||||
|
|
||||
constexpr u32 ToSvcMemoryState(MemoryState state) { |
|
||||
return static_cast<u32>(state & MemoryState::Mask); |
|
||||
} |
|
||||
|
|
||||
struct MemoryInfo { |
|
||||
u64 base_address; |
|
||||
u64 size; |
|
||||
u32 state; |
|
||||
u32 attributes; |
|
||||
u32 permission; |
|
||||
u32 ipc_ref_count; |
|
||||
u32 device_ref_count; |
|
||||
}; |
|
||||
static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); |
|
||||
|
|
||||
struct PageInfo { |
|
||||
u32 flags; |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space |
|
||||
* with homogeneous attributes across its extents. In this particular implementation each VMA is |
|
||||
* 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. |
|
||||
u64 size = 0; |
|
||||
|
|
||||
VMAType type = VMAType::Free; |
|
||||
VMAPermission permissions = VMAPermission::None; |
|
||||
MemoryState state = MemoryState::Unmapped; |
|
||||
MemoryAttribute attribute = MemoryAttribute::None; |
|
||||
|
|
||||
// Settings for type = AllocatedMemoryBlock |
|
||||
/// Memory block backing this VMA. |
|
||||
std::shared_ptr<PhysicalMemory> backing_block = nullptr; |
|
||||
/// Offset into the backing_memory the mapping starts from. |
|
||||
std::size_t offset = 0; |
|
||||
|
|
||||
// Settings for type = BackingMemory |
|
||||
/// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. |
|
||||
u8* backing_memory = nullptr; |
|
||||
|
|
||||
// Settings for type = MMIO |
|
||||
/// Physical address of the register area this VMA maps to. |
|
||||
PAddr paddr = 0; |
|
||||
Common::MemoryHookPointer mmio_handler = nullptr; |
|
||||
|
|
||||
/// Tests if this area can be merged to the right with `next`. |
|
||||
bool CanBeMergedWith(const VirtualMemoryArea& next) const; |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Manages a process' virtual addressing space. This class maintains a list of allocated and free |
|
||||
* regions in the address space, along with their attributes, and allows kernel clients to |
|
||||
* manipulate it, adjusting the page table to match. |
|
||||
* |
|
||||
* This is similar in idea and purpose to the VM manager present in operating system kernels, with |
|
||||
* the main difference being that it doesn't have to support swapping or memory mapping of files. |
|
||||
* The implementation is also simplified by not having to allocate page frames. See these articles |
|
||||
* about the Linux kernel for an explantion of the concept and implementation: |
|
||||
* - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/ |
|
||||
* - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ |
|
||||
*/ |
|
||||
class VMManager final { |
|
||||
using VMAMap = std::map<VAddr, VirtualMemoryArea>; |
|
||||
|
|
||||
public: |
|
||||
using VMAHandle = VMAMap::const_iterator; |
|
||||
|
|
||||
explicit VMManager(Core::System& system); |
|
||||
~VMManager(); |
|
||||
|
|
||||
/// Clears the address space map, re-initializing with a single free area. |
|
||||
void Reset(FileSys::ProgramAddressSpaceType type); |
|
||||
|
|
||||
/// Finds the VMA in which the given address is included in, or `vma_map.end()`. |
|
||||
VMAHandle FindVMA(VAddr target) const; |
|
||||
|
|
||||
/// Indicates whether or not the given handle is within the VMA map. |
|
||||
bool IsValidHandle(VMAHandle handle) const; |
|
||||
|
|
||||
// TODO(yuriks): Should these functions actually return the handle? |
|
||||
|
|
||||
/** |
|
||||
* Maps part of a ref-counted block of memory at a given address. |
|
||||
* |
|
||||
* @param target The guest address to start the mapping at. |
|
||||
* @param block The block to be mapped. |
|
||||
* @param offset Offset into `block` to map from. |
|
||||
* @param size Size of the mapping. |
|
||||
* @param state MemoryState tag to attach to the VMA. |
|
||||
*/ |
|
||||
ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block, |
|
||||
std::size_t offset, u64 size, MemoryState state, |
|
||||
VMAPermission perm = VMAPermission::ReadWrite); |
|
||||
|
|
||||
/** |
|
||||
* Maps an unmanaged host memory pointer at a given address. |
|
||||
* |
|
||||
* @param target The guest address to start the mapping at. |
|
||||
* @param memory The memory to be mapped. |
|
||||
* @param size Size of the mapping. |
|
||||
* @param state MemoryState tag to attach to the VMA. |
|
||||
*/ |
|
||||
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); |
|
||||
|
|
||||
/** |
|
||||
* Finds the first free memory region of the given size within |
|
||||
* the user-addressable ASLR memory region. |
|
||||
* |
|
||||
* @param size The size of the desired region in bytes. |
|
||||
* |
|
||||
* @returns If successful, the base address of the free region with |
|
||||
* the given size. |
|
||||
*/ |
|
||||
ResultVal<VAddr> FindFreeRegion(u64 size) const; |
|
||||
|
|
||||
/** |
|
||||
* Finds the first free address range that can hold a region of the desired size |
|
||||
* |
|
||||
* @param begin The starting address of the range. |
|
||||
* This is treated as an inclusive beginning address. |
|
||||
* |
|
||||
* @param end The ending address of the range. |
|
||||
* This is treated as an exclusive ending address. |
|
||||
* |
|
||||
* @param size The size of the free region to attempt to locate, |
|
||||
* in bytes. |
|
||||
* |
|
||||
* @returns If successful, the base address of the free region with |
|
||||
* the given size. |
|
||||
* |
|
||||
* @returns If unsuccessful, a result containing an error code. |
|
||||
* |
|
||||
* @pre The starting address must be less than the ending address. |
|
||||
* @pre The size must not exceed the address range itself. |
|
||||
*/ |
|
||||
ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const; |
|
||||
|
|
||||
/** |
|
||||
* Maps a memory-mapped IO region at a given address. |
|
||||
* |
|
||||
* @param target The guest address to start the mapping at. |
|
||||
* @param paddr The physical address where the registers are present. |
|
||||
* @param size Size of the mapping. |
|
||||
* @param state MemoryState tag to attach to the VMA. |
|
||||
* @param mmio_handler The handler that will implement read and write for this MMIO region. |
|
||||
*/ |
|
||||
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, |
|
||||
Common::MemoryHookPointer mmio_handler); |
|
||||
|
|
||||
/// Unmaps a range of addresses, splitting VMAs as necessary. |
|
||||
ResultCode UnmapRange(VAddr target, u64 size); |
|
||||
|
|
||||
/// Changes the permissions of the given VMA. |
|
||||
VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms); |
|
||||
|
|
||||
/// Changes the permissions of a range of addresses, splitting VMAs as necessary. |
|
||||
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); |
|
||||
|
|
||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); |
|
||||
|
|
||||
/// Attempts to allocate a heap with the given size. |
|
||||
/// |
|
||||
/// @param size The size of the heap to allocate in bytes. |
|
||||
/// |
|
||||
/// @note If a heap is currently allocated, and this is called |
|
||||
/// with a size that is equal to the size of the current heap, |
|
||||
/// then this function will do nothing and return the current |
|
||||
/// heap's starting address, as there's no need to perform |
|
||||
/// any additional heap allocation work. |
|
||||
/// |
|
||||
/// @note If a heap is currently allocated, and this is called |
|
||||
/// with a size less than the current heap's size, then |
|
||||
/// this function will attempt to shrink the heap. |
|
||||
/// |
|
||||
/// @note If a heap is currently allocated, and this is called |
|
||||
/// with a size larger than the current heap's size, then |
|
||||
/// this function will attempt to extend the size of the heap. |
|
||||
/// |
|
||||
/// @returns A result indicating either success or failure. |
|
||||
/// <p> |
|
||||
/// If successful, this function will return a result |
|
||||
/// containing the starting address to the allocated heap. |
|
||||
/// <p> |
|
||||
/// If unsuccessful, this function will return a result |
|
||||
/// containing an error code. |
|
||||
/// |
|
||||
/// @pre The given size must lie within the allowable heap |
|
||||
/// memory region managed by this VMManager instance. |
|
||||
/// Failure to abide by this will result in ERR_OUT_OF_MEMORY |
|
||||
/// being returned as the result. |
|
||||
/// |
|
||||
ResultVal<VAddr> SetHeapSize(u64 size); |
|
||||
|
|
||||
/// Maps memory at a given address. |
|
||||
/// |
|
||||
/// @param target The virtual address to map memory at. |
|
||||
/// @param size The amount of memory to map. |
|
||||
/// |
|
||||
/// @note The destination address must lie within the Map region. |
|
||||
/// |
|
||||
/// @note This function requires that SystemResourceSize be non-zero, |
|
||||
/// however, this is just because if it were not then the |
|
||||
/// resulting page tables could be exploited on hardware by |
|
||||
/// a malicious program. SystemResource usage does not need |
|
||||
/// to be explicitly checked or updated here. |
|
||||
ResultCode MapPhysicalMemory(VAddr target, u64 size); |
|
||||
|
|
||||
/// Unmaps memory at a given address. |
|
||||
/// |
|
||||
/// @param target The virtual address to unmap memory at. |
|
||||
/// @param size The amount of memory to unmap. |
|
||||
/// |
|
||||
/// @note The destination address must lie within the Map region. |
|
||||
/// |
|
||||
/// @note This function requires that SystemResourceSize be non-zero, |
|
||||
/// however, this is just because if it were not then the |
|
||||
/// resulting page tables could be exploited on hardware by |
|
||||
/// a malicious program. SystemResource usage does not need |
|
||||
/// to be explicitly checked or updated here. |
|
||||
ResultCode UnmapPhysicalMemory(VAddr target, u64 size); |
|
||||
|
|
||||
/// Maps a region of memory as code memory. |
|
||||
/// |
|
||||
/// @param dst_address The base address of the region to create the aliasing memory region. |
|
||||
/// @param src_address The base address of the region to be aliased. |
|
||||
/// @param size The total amount of memory to map in bytes. |
|
||||
/// |
|
||||
/// @pre Both memory regions lie within the actual addressable address space. |
|
||||
/// |
|
||||
/// @post After this function finishes execution, assuming success, then the address range |
|
||||
/// [dst_address, dst_address+size) will alias the memory region, |
|
||||
/// [src_address, src_address+size). |
|
||||
/// <p> |
|
||||
/// What this also entails is as follows: |
|
||||
/// 1. The aliased region gains the Locked memory attribute. |
|
||||
/// 2. The aliased region becomes read-only. |
|
||||
/// 3. The aliasing region becomes read-only. |
|
||||
/// 4. The aliasing region is created with a memory state of MemoryState::CodeModule. |
|
||||
/// |
|
||||
ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); |
|
||||
|
|
||||
/// Unmaps a region of memory designated as code module memory. |
|
||||
/// |
|
||||
/// @param dst_address The base address of the memory region aliasing the source memory region. |
|
||||
/// @param src_address The base address of the memory region being aliased. |
|
||||
/// @param size The size of the memory region to unmap in bytes. |
|
||||
/// |
|
||||
/// @pre Both memory ranges lie within the actual addressable address space. |
|
||||
/// |
|
||||
/// @pre The memory region being unmapped has been previously been mapped |
|
||||
/// by a call to MapCodeMemory. |
|
||||
/// |
|
||||
/// @post After execution of the function, if successful. the aliasing memory region |
|
||||
/// will be unmapped and the aliased region will have various traits about it |
|
||||
/// restored to what they were prior to the original mapping call preceding |
|
||||
/// this function call. |
|
||||
/// <p> |
|
||||
/// What this also entails is as follows: |
|
||||
/// 1. The state of the memory region will now indicate a general heap region. |
|
||||
/// 2. All memory attributes for the memory region are cleared. |
|
||||
/// 3. Memory permissions for the region are restored to user read/write. |
|
||||
/// |
|
||||
ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); |
|
||||
|
|
||||
/// Queries the memory manager for information about the given address. |
|
||||
/// |
|
||||
/// @param address The address to query the memory manager about for information. |
|
||||
/// |
|
||||
/// @return A MemoryInfo instance containing information about the given address. |
|
||||
/// |
|
||||
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. |
|
||||
*/ |
|
||||
void RefreshMemoryBlockMappings(const PhysicalMemory* block); |
|
||||
|
|
||||
/// Dumps the address space layout to the log, for debugging |
|
||||
void LogLayout() const; |
|
||||
|
|
||||
/// Gets the total memory usage, used by svcGetInfo |
|
||||
u64 GetTotalPhysicalMemoryAvailable() const; |
|
||||
|
|
||||
/// Gets the address space base address |
|
||||
VAddr GetAddressSpaceBaseAddress() const; |
|
||||
|
|
||||
/// Gets the address space end address |
|
||||
VAddr GetAddressSpaceEndAddress() const; |
|
||||
|
|
||||
/// Gets the total address space address size in bytes |
|
||||
u64 GetAddressSpaceSize() const; |
|
||||
|
|
||||
/// Gets the address space width in bits. |
|
||||
u64 GetAddressSpaceWidth() const; |
|
||||
|
|
||||
/// Determines whether or not the given address range lies within the address space. |
|
||||
bool IsWithinAddressSpace(VAddr address, u64 size) const; |
|
||||
|
|
||||
/// Gets the base address of the ASLR region. |
|
||||
VAddr GetASLRRegionBaseAddress() const; |
|
||||
|
|
||||
/// Gets the end address of the ASLR region. |
|
||||
VAddr GetASLRRegionEndAddress() const; |
|
||||
|
|
||||
/// Gets the size of the ASLR region |
|
||||
u64 GetASLRRegionSize() const; |
|
||||
|
|
||||
/// Determines whether or not the specified address range is within the ASLR region. |
|
||||
bool IsWithinASLRRegion(VAddr address, u64 size) const; |
|
||||
|
|
||||
/// Gets the base address of the code region. |
|
||||
VAddr GetCodeRegionBaseAddress() const; |
|
||||
|
|
||||
/// Gets the end address of the code region. |
|
||||
VAddr GetCodeRegionEndAddress() const; |
|
||||
|
|
||||
/// Gets the total size of the code region in bytes. |
|
||||
u64 GetCodeRegionSize() const; |
|
||||
|
|
||||
/// Determines whether or not the specified range is within the code region. |
|
||||
bool IsWithinCodeRegion(VAddr address, u64 size) const; |
|
||||
|
|
||||
/// Gets the base address of the heap region. |
|
||||
VAddr GetHeapRegionBaseAddress() const; |
|
||||
|
|
||||
/// Gets the end address of the heap region; |
|
||||
VAddr GetHeapRegionEndAddress() const; |
|
||||
|
|
||||
/// Gets the total size of the heap region in bytes. |
|
||||
u64 GetHeapRegionSize() const; |
|
||||
|
|
||||
/// Gets the total size of the current heap in bytes. |
|
||||
/// |
|
||||
/// @note This is the current allocated heap size, not the size |
|
||||
/// of the region it's allowed to exist within. |
|
||||
/// |
|
||||
u64 GetCurrentHeapSize() const; |
|
||||
|
|
||||
/// Determines whether or not the specified range is within the heap region. |
|
||||
bool IsWithinHeapRegion(VAddr address, u64 size) const; |
|
||||
|
|
||||
/// Gets the base address of the map region. |
|
||||
VAddr GetMapRegionBaseAddress() const; |
|
||||
|
|
||||
/// Gets the end address of the map region. |
|
||||
VAddr GetMapRegionEndAddress() const; |
|
||||
|
|
||||
/// Gets the total size of the map region in bytes. |
|
||||
u64 GetMapRegionSize() const; |
|
||||
|
|
||||
/// Determines whether or not the specified range is within the map region. |
|
||||
bool IsWithinMapRegion(VAddr address, u64 size) const; |
|
||||
|
|
||||
/// Gets the base address of the stack region. |
|
||||
VAddr GetStackRegionBaseAddress() const; |
|
||||
|
|
||||
/// Gets the end address of the stack region. |
|
||||
VAddr GetStackRegionEndAddress() const; |
|
||||
|
|
||||
/// Gets the total size of the stack region in bytes. |
|
||||
u64 GetStackRegionSize() const; |
|
||||
|
|
||||
/// Determines whether or not the given address range is within the stack region |
|
||||
bool IsWithinStackRegion(VAddr address, u64 size) const; |
|
||||
|
|
||||
/// Gets the base address of the TLS IO region. |
|
||||
VAddr GetTLSIORegionBaseAddress() const; |
|
||||
|
|
||||
/// Gets the end address of the TLS IO region. |
|
||||
VAddr GetTLSIORegionEndAddress() const; |
|
||||
|
|
||||
/// Gets the total size of the TLS IO region in bytes. |
|
||||
u64 GetTLSIORegionSize() const; |
|
||||
|
|
||||
/// Determines if the given address range is within the TLS IO region. |
|
||||
bool IsWithinTLSIORegion(VAddr address, u64 size) const; |
|
||||
|
|
||||
/// Each VMManager has its own page table, which is set as the main one when the owning process |
|
||||
/// is scheduled. |
|
||||
Common::PageTable page_table{Memory::PAGE_BITS}; |
|
||||
|
|
||||
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; |
|
||||
|
|
||||
private: |
|
||||
using VMAIter = VMAMap::iterator; |
|
||||
|
|
||||
/// Converts a VMAHandle to a mutable VMAIter. |
|
||||
VMAIter StripIterConstness(const VMAHandle& iter); |
|
||||
|
|
||||
/// Unmaps the given VMA. |
|
||||
VMAIter Unmap(VMAIter vma); |
|
||||
|
|
||||
/** |
|
||||
* Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing |
|
||||
* the appropriate error checking. |
|
||||
*/ |
|
||||
ResultVal<VMAIter> CarveVMA(VAddr base, u64 size); |
|
||||
|
|
||||
/** |
|
||||
* Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each |
|
||||
* end of the range. |
|
||||
*/ |
|
||||
ResultVal<VMAIter> CarveVMARange(VAddr base, u64 size); |
|
||||
|
|
||||
/** |
|
||||
* Splits a VMA in two, at the specified offset. |
|
||||
* @returns the right side of the split, with the original iterator becoming the left side. |
|
||||
*/ |
|
||||
VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); |
|
||||
|
|
||||
/** |
|
||||
* Checks for and merges the specified VMA with adjacent ones if possible. |
|
||||
* @returns the merged VMA or the original if no merging was possible. |
|
||||
*/ |
|
||||
VMAIter MergeAdjacent(VMAIter vma); |
|
||||
|
|
||||
/** |
|
||||
* Merges two adjacent VMAs. |
|
||||
*/ |
|
||||
void MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right); |
|
||||
|
|
||||
/// Updates the pages corresponding to this VMA so they match the VMA's attributes. |
|
||||
void UpdatePageTableForVMA(const VirtualMemoryArea& vma); |
|
||||
|
|
||||
/// Initializes memory region ranges to adhere to a given address space type. |
|
||||
void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type); |
|
||||
|
|
||||
/// Clears the underlying map and page table. |
|
||||
void Clear(); |
|
||||
|
|
||||
/// Clears out the VMA map, unmapping any previously mapped ranges. |
|
||||
void ClearVMAMap(); |
|
||||
|
|
||||
/// Clears out the page table |
|
||||
void ClearPageTable(); |
|
||||
|
|
||||
/// Gets the amount of memory currently mapped (state != Unmapped) in a range. |
|
||||
ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; |
|
||||
|
|
||||
/// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range. |
|
||||
ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address, |
|
||||
std::size_t size) 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 |
|
||||
* `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be |
|
||||
* merged when possible so that no two similar and adjacent regions exist that have not been |
|
||||
* merged. |
|
||||
*/ |
|
||||
VMAMap vma_map; |
|
||||
|
|
||||
u32 address_space_width = 0; |
|
||||
VAddr address_space_base = 0; |
|
||||
VAddr address_space_end = 0; |
|
||||
|
|
||||
VAddr aslr_region_base = 0; |
|
||||
VAddr aslr_region_end = 0; |
|
||||
|
|
||||
VAddr code_region_base = 0; |
|
||||
VAddr code_region_end = 0; |
|
||||
|
|
||||
VAddr heap_region_base = 0; |
|
||||
VAddr heap_region_end = 0; |
|
||||
|
|
||||
VAddr map_region_base = 0; |
|
||||
VAddr map_region_end = 0; |
|
||||
|
|
||||
VAddr stack_region_base = 0; |
|
||||
VAddr stack_region_end = 0; |
|
||||
|
|
||||
VAddr tls_io_region_base = 0; |
|
||||
VAddr tls_io_region_end = 0; |
|
||||
|
|
||||
// Memory used to back the allocations in the regular heap. A single vector is used to cover |
|
||||
// the entire virtual address space extents that bound the allocations, including any holes. |
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous |
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe. |
|
||||
std::shared_ptr<PhysicalMemory> heap_memory; |
|
||||
|
|
||||
// The end of the currently allocated heap. This is not an inclusive |
|
||||
// end of the range. This is essentially 'base_address + current_size'. |
|
||||
VAddr heap_end = 0; |
|
||||
|
|
||||
// The current amount of memory mapped via MapPhysicalMemory. |
|
||||
// This is used here (and in Nintendo's kernel) only for debugging, and does not impact |
|
||||
// any behavior. |
|
||||
u64 physical_memory_mapped = 0; |
|
||||
|
|
||||
Core::System& system; |
|
||||
}; |
|
||||
} // namespace Kernel |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue