2015-05-20 20:37:07 -07:00
|
|
|
// 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 <vector>
|
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "core/hle/result.h"
|
2016-01-30 10:41:04 -08:00
|
|
|
#include "core/mmio.h"
|
2015-05-20 20:37:07 -07:00
|
|
|
|
|
|
|
namespace Kernel {
|
|
|
|
|
2016-09-17 17:38:01 -07:00
|
|
|
const ResultCode ERR_INVALID_ADDRESS{// 0xE0E01BF5
|
|
|
|
ErrorDescription::InvalidAddress, ErrorModule::OS,
|
|
|
|
ErrorSummary::InvalidArgument, ErrorLevel::Usage};
|
2015-07-17 18:34:50 -07:00
|
|
|
|
2016-09-17 17:38:01 -07:00
|
|
|
const ResultCode ERR_INVALID_ADDRESS_STATE{// 0xE0A01BF5
|
|
|
|
ErrorDescription::InvalidAddress, ErrorModule::OS,
|
|
|
|
ErrorSummary::InvalidState, ErrorLevel::Usage};
|
2015-07-17 18:34:50 -07:00
|
|
|
|
2015-05-20 20:37:07 -07:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Set of values returned in MemoryInfo.state by svcQueryMemory.
|
|
|
|
enum class MemoryState : u8 {
|
|
|
|
Free = 0,
|
|
|
|
Reserved = 1,
|
|
|
|
IO = 2,
|
|
|
|
Static = 3,
|
|
|
|
Code = 4,
|
|
|
|
Private = 5,
|
|
|
|
Shared = 6,
|
|
|
|
Continuous = 7,
|
|
|
|
Aliased = 8,
|
|
|
|
Alias = 9,
|
|
|
|
AliasCode = 10,
|
|
|
|
Locked = 11,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 {
|
|
|
|
/// Virtual base address of the region.
|
|
|
|
VAddr base = 0;
|
|
|
|
/// Size of the region.
|
|
|
|
u32 size = 0;
|
|
|
|
|
|
|
|
VMAType type = VMAType::Free;
|
|
|
|
VMAPermission permissions = VMAPermission::None;
|
|
|
|
/// Tag returned by svcQueryMemory. Not otherwise used.
|
|
|
|
MemoryState meminfo_state = MemoryState::Free;
|
|
|
|
|
|
|
|
// Settings for type = AllocatedMemoryBlock
|
|
|
|
/// Memory block backing this VMA.
|
|
|
|
std::shared_ptr<std::vector<u8>> backing_block = nullptr;
|
|
|
|
/// Offset into the backing_memory the mapping starts from.
|
2015-07-13 19:28:13 -07:00
|
|
|
size_t offset = 0;
|
2015-05-20 20:37:07 -07:00
|
|
|
|
|
|
|
// 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;
|
2016-01-30 10:41:04 -08:00
|
|
|
Memory::MMIORegionPointer mmio_handler = nullptr;
|
2015-05-20 20:37:07 -07:00
|
|
|
|
|
|
|
/// 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/
|
|
|
|
*/
|
2015-07-09 18:52:15 -07:00
|
|
|
class VMManager final {
|
2015-05-20 20:37:07 -07:00
|
|
|
// TODO(yuriks): Make page tables switchable to support multiple VMManagers
|
|
|
|
public:
|
|
|
|
/**
|
2016-09-17 17:38:01 -07:00
|
|
|
* The maximum amount of address space managed by the kernel. Addresses above this are never
|
|
|
|
* used.
|
2015-05-20 20:37:07 -07:00
|
|
|
* @note This is the limit used by the New 3DS kernel. Old 3DS used 0x20000000.
|
|
|
|
*/
|
|
|
|
static const u32 MAX_ADDRESS = 0x40000000;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
std::map<VAddr, VirtualMemoryArea> vma_map;
|
|
|
|
using VMAHandle = decltype(vma_map)::const_iterator;
|
|
|
|
|
|
|
|
VMManager();
|
2015-07-09 18:52:15 -07:00
|
|
|
~VMManager();
|
2015-05-20 20:37:07 -07:00
|
|
|
|
|
|
|
/// Clears the address space map, re-initializing with a single free area.
|
|
|
|
void Reset();
|
|
|
|
|
|
|
|
/// Finds the VMA in which the given address is included in, or `vma_map.end()`.
|
|
|
|
VMAHandle FindVMA(VAddr target) 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<std::vector<u8>> block,
|
2016-09-17 17:38:01 -07:00
|
|
|
size_t offset, u32 size, MemoryState state);
|
2015-05-20 20:37:07 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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, u32 size, MemoryState state);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2016-01-30 10:41:04 -08:00
|
|
|
* @param mmio_handler The handler that will implement read and write for this MMIO region.
|
2015-05-20 20:37:07 -07:00
|
|
|
*/
|
2016-09-17 17:38:01 -07:00
|
|
|
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state,
|
|
|
|
Memory::MMIORegionPointer mmio_handler);
|
2015-05-20 20:37:07 -07:00
|
|
|
|
2015-07-17 19:19:16 -07:00
|
|
|
/// Unmaps a range of addresses, splitting VMAs as necessary.
|
|
|
|
ResultCode UnmapRange(VAddr target, u32 size);
|
2015-05-20 20:37:07 -07:00
|
|
|
|
|
|
|
/// Changes the permissions of the given VMA.
|
2015-07-17 19:19:16 -07:00
|
|
|
VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms);
|
|
|
|
|
|
|
|
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
|
|
|
|
ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 std::vector<u8>* block);
|
2015-05-20 20:37:07 -07:00
|
|
|
|
2015-07-09 18:52:15 -07:00
|
|
|
/// Dumps the address space layout to the log, for debugging
|
2015-07-17 17:55:48 -07:00
|
|
|
void LogLayout(Log::Level log_level) const;
|
2015-07-09 18:52:15 -07:00
|
|
|
|
2015-05-20 20:37:07 -07:00
|
|
|
private:
|
|
|
|
using VMAIter = decltype(vma_map)::iterator;
|
|
|
|
|
|
|
|
/// Converts a VMAHandle to a mutable VMAIter.
|
|
|
|
VMAIter StripIterConstness(const VMAHandle& iter);
|
|
|
|
|
2015-07-17 19:19:16 -07:00
|
|
|
/// Unmaps the given VMA.
|
|
|
|
VMAIter Unmap(VMAIter vma);
|
|
|
|
|
2015-05-20 20:37:07 -07:00
|
|
|
/**
|
|
|
|
* 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, u32 size);
|
|
|
|
|
2015-07-17 19:19:16 -07:00
|
|
|
/**
|
|
|
|
* 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, u32 size);
|
|
|
|
|
2015-05-20 20:37:07 -07:00
|
|
|
/**
|
|
|
|
* 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, u32 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);
|
|
|
|
|
|
|
|
/// Updates the pages corresponding to this VMA so they match the VMA's attributes.
|
|
|
|
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
|
|
|
|
};
|
|
|
|
}
|