Merge pull request #1907 from lioncash/attribute

kernel/svc: Implement svcSetMemoryAttribute
master
bunnei 2018-12-19 11:50:50 +07:00 committed by GitHub
commit e73dd39413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 279 additions and 14 deletions

@ -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