Merge pull request #7684 from bunnei/set-mem-perm-attr

Kernel Memory Updates (Part 1): SetMemoryAttribute, and other minor changes.
This commit is contained in:
bunnei 2022-01-11 16:26:17 -08:00 committed by GitHub
commit 599c0763e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 229 additions and 178 deletions

View file

@ -70,11 +70,11 @@ enum class KMemoryState : u32 {
ThreadLocal = ThreadLocal =
static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
@ -93,6 +93,8 @@ enum class KMemoryState : u32 {
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped | GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
FlagReferenceCounted | FlagCanDebug, FlagReferenceCounted | FlagCanDebug,
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
}; };
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState); DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
@ -108,8 +110,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A); static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B); static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C); static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D); static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E); static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F); static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010); static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811); static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
@ -117,6 +119,7 @@ static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013); static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214); static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015); static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
enum class KMemoryPermission : u8 { enum class KMemoryPermission : u8 {
None = 0, None = 0,
@ -155,7 +158,13 @@ enum class KMemoryPermission : u8 {
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) { constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) {
return static_cast<KMemoryPermission>(perm); return static_cast<KMemoryPermission>(
(static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserMask) |
KMemoryPermission::KernelRead |
((static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserWrite)
<< KMemoryPermission::KernelShift) |
(perm == Svc::MemoryPermission::None ? KMemoryPermission::NotMapped
: KMemoryPermission::None));
} }
enum class KMemoryAttribute : u8 { enum class KMemoryAttribute : u8 {
@ -169,6 +178,8 @@ enum class KMemoryAttribute : u8 {
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared), DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
SetMask = Uncached,
IpcAndDeviceMapped = IpcLocked | DeviceShared, IpcAndDeviceMapped = IpcLocked | DeviceShared,
LockedAndIpcLocked = Locked | IpcLocked, LockedAndIpcLocked = Locked | IpcLocked,
DeviceSharedAndUncached = DeviceShared | Uncached DeviceSharedAndUncached = DeviceShared | Uncached
@ -215,6 +226,15 @@ struct KMemoryInfo {
constexpr VAddr GetLastAddress() const { constexpr VAddr GetLastAddress() const {
return GetEndAddress() - 1; return GetEndAddress() - 1;
} }
constexpr KMemoryState GetState() const {
return state;
}
constexpr KMemoryAttribute GetAttribute() const {
return attribute;
}
constexpr KMemoryPermission GetPermission() const {
return perm;
}
}; };
class KMemoryBlock final { class KMemoryBlock final {

View file

@ -305,8 +305,8 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
KMemoryState state{}; KMemoryState state{};
KMemoryPermission perm{}; KMemoryPermission perm{};
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All, CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
KMemoryState::Normal, KMemoryPermission::All, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
@ -344,14 +344,14 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
const std::size_t num_pages{size / PageSize}; const std::size_t num_pages{size / PageSize};
CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, src_addr, size, KMemoryState::All, CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
KMemoryState::Normal, KMemoryPermission::None, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::Mask, KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
KMemoryState state{}; KMemoryState state{};
CASCADE_CODE(CheckMemoryState( CASCADE_CODE(CheckMemoryState(
&state, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias, &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None, CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
@ -553,7 +553,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KMemoryState src_state{}; KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState( CASCADE_CODE(CheckMemoryState(
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite, KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
@ -592,13 +592,13 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KMemoryState src_state{}; KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState( CASCADE_CODE(CheckMemoryState(
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None, KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
KMemoryPermission dst_perm{}; KMemoryPermission dst_perm{};
CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, dst_addr, size, KMemoryState::All, CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size,
KMemoryState::Stack, KMemoryPermission::None, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::Mask, KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
@ -721,7 +721,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
KMemoryPermission prev_perm{}; KMemoryPermission prev_perm{};
CASCADE_CODE(CheckMemoryState( CASCADE_CODE(CheckMemoryState(
&prev_state, &prev_perm, nullptr, addr, size, KMemoryState::FlagCode, &prev_state, &prev_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCode,
KMemoryState::FlagCode, KMemoryPermission::None, KMemoryPermission::None, KMemoryState::FlagCode, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
@ -782,7 +782,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
KMemoryAttribute attribute{}; KMemoryAttribute attribute{};
CASCADE_CODE(CheckMemoryState( CASCADE_CODE(CheckMemoryState(
&state, nullptr, &attribute, addr, size, &state, nullptr, &attribute, nullptr, addr, size,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All, KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
@ -799,7 +799,7 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
KMemoryState state{}; KMemoryState state{};
CASCADE_CODE( CASCADE_CODE(
CheckMemoryState(&state, nullptr, nullptr, addr, size, CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
@ -820,7 +820,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
KMemoryState old_state; KMemoryState old_state;
KMemoryPermission old_perm; KMemoryPermission old_perm;
R_TRY(this->CheckMemoryState( R_TRY(this->CheckMemoryState(
std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size, std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size,
KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None, KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
@ -837,24 +837,36 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
return ResultSuccess; return ResultSuccess;
} }
ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask, ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
KMemoryAttribute value) { const size_t num_pages = size / PageSize;
ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
KMemoryAttribute::SetMask);
// Lock the table.
std::lock_guard lock{page_table_lock}; std::lock_guard lock{page_table_lock};
KMemoryState state{}; // Verify we can change the memory attribute.
KMemoryPermission perm{}; KMemoryState old_state;
KMemoryAttribute attribute{}; KMemoryPermission old_perm;
KMemoryAttribute old_attr;
CASCADE_CODE(CheckMemoryState( size_t num_allocator_blocks;
&state, &perm, &attribute, addr, size, KMemoryState::FlagCanChangeAttribute, constexpr auto AttributeTestMask =
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
R_TRY(this->CheckMemoryState(
std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
KMemoryAttribute::DeviceSharedAndUncached));
attribute = attribute & ~mask; // Determine the new attribute.
attribute = attribute | (mask & value); const auto new_attr = ((old_attr & static_cast<KMemoryAttribute>(~mask)) |
static_cast<KMemoryAttribute>(attr & mask));
block_manager->Update(addr, size / PageSize, state, perm, attribute); // Perform operation.
this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
// Update the blocks.
block_manager->Update(addr, num_pages, old_state, old_perm, new_attr);
return ResultSuccess; return ResultSuccess;
} }
@ -1019,7 +1031,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
KMemoryPermission perm{}; KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState( if (const ResultCode result{CheckMemoryState(
nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
KMemoryAttribute::DeviceSharedAndUncached)}; KMemoryAttribute::DeviceSharedAndUncached)};
@ -1042,7 +1054,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
KMemoryPermission perm{}; KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState( if (const ResultCode result{CheckMemoryState(
nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
KMemoryAttribute::DeviceSharedAndUncached)}; KMemoryAttribute::DeviceSharedAndUncached)};
@ -1068,7 +1080,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
KMemoryPermission old_perm{}; KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState( if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
result.IsError()) { result.IsError()) {
@ -1095,7 +1107,7 @@ ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
KMemoryPermission old_perm{}; KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState( if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::All, KMemoryAttribute::Locked)}; KMemoryAttribute::All, KMemoryAttribute::Locked)};
result.IsError()) { result.IsError()) {
@ -1225,18 +1237,19 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
return alias_region_start; return alias_region_start;
case KMemoryState::Stack: case KMemoryState::Stack:
return stack_region_start; return stack_region_start;
case KMemoryState::Io:
case KMemoryState::Static: case KMemoryState::Static:
case KMemoryState::ThreadLocal: case KMemoryState::ThreadLocal:
return kernel_map_region_start; return kernel_map_region_start;
case KMemoryState::Io:
case KMemoryState::Shared: case KMemoryState::Shared:
case KMemoryState::AliasCode: case KMemoryState::AliasCode:
case KMemoryState::AliasCodeData: case KMemoryState::AliasCodeData:
case KMemoryState::Transferred: case KMemoryState::Transfered:
case KMemoryState::SharedTransferred: case KMemoryState::SharedTransfered:
case KMemoryState::SharedCode: case KMemoryState::SharedCode:
case KMemoryState::GeneratedCode: case KMemoryState::GeneratedCode:
case KMemoryState::CodeOut: case KMemoryState::CodeOut:
case KMemoryState::Coverage:
return alias_code_region_start; return alias_code_region_start;
case KMemoryState::Code: case KMemoryState::Code:
case KMemoryState::CodeData: case KMemoryState::CodeData:
@ -1260,18 +1273,19 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
return alias_region_end - alias_region_start; return alias_region_end - alias_region_start;
case KMemoryState::Stack: case KMemoryState::Stack:
return stack_region_end - stack_region_start; return stack_region_end - stack_region_start;
case KMemoryState::Io:
case KMemoryState::Static: case KMemoryState::Static:
case KMemoryState::ThreadLocal: case KMemoryState::ThreadLocal:
return kernel_map_region_end - kernel_map_region_start; return kernel_map_region_end - kernel_map_region_start;
case KMemoryState::Io:
case KMemoryState::Shared: case KMemoryState::Shared:
case KMemoryState::AliasCode: case KMemoryState::AliasCode:
case KMemoryState::AliasCodeData: case KMemoryState::AliasCodeData:
case KMemoryState::Transferred: case KMemoryState::Transfered:
case KMemoryState::SharedTransferred: case KMemoryState::SharedTransfered:
case KMemoryState::SharedCode: case KMemoryState::SharedCode:
case KMemoryState::GeneratedCode: case KMemoryState::GeneratedCode:
case KMemoryState::CodeOut: case KMemoryState::CodeOut:
case KMemoryState::Coverage:
return alias_code_region_end - alias_code_region_start; return alias_code_region_end - alias_code_region_start;
case KMemoryState::Code: case KMemoryState::Code:
case KMemoryState::CodeData: case KMemoryState::CodeData:
@ -1283,15 +1297,18 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
} }
bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const { bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const {
const VAddr end{addr + size}; const VAddr end = addr + size;
const VAddr last{end - 1}; const VAddr last = end - 1;
const VAddr region_start{GetRegionAddress(state)};
const std::size_t region_size{GetRegionSize(state)};
const bool is_in_region{region_start <= addr && addr < end &&
last <= region_start + region_size - 1};
const bool is_in_heap{!(end <= heap_region_start || heap_region_end <= addr)};
const bool is_in_alias{!(end <= alias_region_start || alias_region_end <= addr)};
const VAddr region_start = this->GetRegionAddress(state);
const size_t region_size = this->GetRegionSize(state);
const bool is_in_region =
region_start <= addr && addr < end && last <= region_start + region_size - 1;
const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr ||
heap_region_start == heap_region_end);
const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr ||
alias_region_start == alias_region_end);
switch (state) { switch (state) {
case KMemoryState::Free: case KMemoryState::Free:
case KMemoryState::Kernel: case KMemoryState::Kernel:
@ -1305,11 +1322,12 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
case KMemoryState::AliasCodeData: case KMemoryState::AliasCodeData:
case KMemoryState::Stack: case KMemoryState::Stack:
case KMemoryState::ThreadLocal: case KMemoryState::ThreadLocal:
case KMemoryState::Transferred: case KMemoryState::Transfered:
case KMemoryState::SharedTransferred: case KMemoryState::SharedTransfered:
case KMemoryState::SharedCode: case KMemoryState::SharedCode:
case KMemoryState::GeneratedCode: case KMemoryState::GeneratedCode:
case KMemoryState::CodeOut: case KMemoryState::CodeOut:
case KMemoryState::Coverage:
return is_in_region && !is_in_heap && !is_in_alias; return is_in_region && !is_in_heap && !is_in_alias;
case KMemoryState::Normal: case KMemoryState::Normal:
ASSERT(is_in_heap); ASSERT(is_in_heap);
@ -1324,91 +1342,29 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
} }
} }
constexpr ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
// Validate the states match expectation.
R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory);
R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory);
R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const { KMemoryAttribute attr) const {
// Validate the states match expectation ASSERT(this->IsLockedByCurrentThread());
if ((info.state & state_mask) != state) {
return ResultInvalidCurrentMemory;
}
if ((info.perm & perm_mask) != perm) {
return ResultInvalidCurrentMemory;
}
if ((info.attribute & attr_mask) != attr) {
return ResultInvalidCurrentMemory;
}
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr) {
std::lock_guard lock{page_table_lock};
// Get information about the first block
const VAddr last_addr{addr + size - 1};
KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
KMemoryInfo info{it->GetMemoryInfo()};
// Validate all blocks in the range have correct state
const KMemoryState first_state{info.state};
const KMemoryPermission first_perm{info.perm};
const KMemoryAttribute first_attr{info.attribute};
while (true) {
// Validate the current block
if (!(info.state == first_state)) {
return ResultInvalidCurrentMemory;
}
if (!(info.perm == first_perm)) {
return ResultInvalidCurrentMemory;
}
if (!((info.attribute | static_cast<KMemoryAttribute>(ignore_attr)) ==
(first_attr | static_cast<KMemoryAttribute>(ignore_attr)))) {
return ResultInvalidCurrentMemory;
}
// Validate against the provided masks
CASCADE_CODE(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
// Break once we're done
if (last_addr <= info.GetLastAddress()) {
break;
}
// Advance our iterator
it++;
ASSERT(it != block_manager->cend());
info = it->GetMemoryInfo();
}
// Write output state
if (out_state) {
*out_state = first_state;
}
if (out_perm) {
*out_perm = first_perm;
}
if (out_attr) {
*out_attr = first_attr & static_cast<KMemoryAttribute>(~ignore_attr);
}
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
// Get information about the first block. // Get information about the first block.
const VAddr last_addr = addr + size - 1; const VAddr last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)}; KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
KMemoryInfo info = it->GetMemoryInfo(); KMemoryInfo info = it->GetMemoryInfo();
// If the start address isn't aligned, we need a block. // If the start address isn't aligned, we need a block.
@ -1417,7 +1373,7 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
while (true) { while (true) {
// Validate against the provided masks. // Validate against the provided masks.
R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
// Break once we're done. // Break once we're done.
if (last_addr <= info.GetLastAddress()) { if (last_addr <= info.GetLastAddress()) {
@ -1426,6 +1382,7 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
// Advance our iterator. // Advance our iterator.
it++; it++;
ASSERT(it != block_manager->cend());
info = it->GetMemoryInfo(); info = it->GetMemoryInfo();
} }
@ -1440,4 +1397,66 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
return ResultSuccess; return ResultSuccess;
} }
ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
ASSERT(this->IsLockedByCurrentThread());
// Get information about the first block.
const VAddr last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
KMemoryInfo info = it->GetMemoryInfo();
// If the start address isn't aligned, we need a block.
const size_t blocks_for_start_align =
(Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
// Validate all blocks in the range have correct state.
const KMemoryState first_state = info.state;
const KMemoryPermission first_perm = info.perm;
const KMemoryAttribute first_attr = info.attribute;
while (true) {
// Validate the current block.
R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory);
R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory);
R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr),
ResultInvalidCurrentMemory);
// Validate against the provided masks.
R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
// Break once we're done.
if (last_addr <= info.GetLastAddress()) {
break;
}
// Advance our iterator.
it++;
ASSERT(it != block_manager->cend());
info = it->GetMemoryInfo();
}
// If the end address isn't aligned, we need a block.
const size_t blocks_for_end_align =
(Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
// Write output state.
if (out_state != nullptr) {
*out_state = first_state;
}
if (out_perm != nullptr) {
*out_perm = first_perm;
}
if (out_attr != nullptr) {
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
}
if (out_blocks_needed != nullptr) {
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
}
return ResultSuccess;
}
} // namespace Kernel } // namespace Kernel

View file

@ -48,8 +48,7 @@ public:
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode ResetTransferMemory(VAddr addr, std::size_t size); ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask, ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
KMemoryAttribute value);
ResultCode SetMaxHeapSize(std::size_t size); ResultCode SetMaxHeapSize(std::size_t size);
ResultCode SetHeapSize(VAddr* out, std::size_t size); ResultCode SetHeapSize(VAddr* out, std::size_t size);
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
@ -102,28 +101,50 @@ private:
constexpr VAddr GetRegionAddress(KMemoryState state) const; constexpr VAddr GetRegionAddress(KMemoryState state) const;
constexpr std::size_t GetRegionSize(KMemoryState state) const; constexpr std::size_t GetRegionSize(KMemoryState state) const;
constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const;
ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr);
}
ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const; KMemoryAttribute attr) const;
ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, VAddr addr, std::size_t size, KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
KMemoryState state_mask, KMemoryState state, VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) { KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
perm, attr_mask, attr, ignore_attr);
}
ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const; KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
}
ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr);
}
bool IsLockedByCurrentThread() const {
return true;
}
std::recursive_mutex page_table_lock; std::recursive_mutex page_table_lock;
std::unique_ptr<KMemoryBlockManager> block_manager; std::unique_ptr<KMemoryBlockManager> block_manager;

View file

@ -168,6 +168,9 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size, static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
MemoryPermission perm) { MemoryPermission perm) {
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
perm);
// Validate address / size. // Validate address / size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
@ -186,46 +189,33 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s
} }
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
u32 attribute) { u32 attr) {
LOG_DEBUG(Kernel_SVC, LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attribute); size, mask, attr);
if (!Common::Is4KBAligned(address)) { // Validate address / size.
LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address); R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
return ResultInvalidAddress; R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
} R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
if (size == 0 || !Common::Is4KBAligned(size)) { // Validate the attribute and mask.
LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.", constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
size); R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
return ResultInvalidAddress; R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
}
if (!IsValidAddressRange(address, size)) {
LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
address, size);
return ResultInvalidCurrentMemory;
}
const auto attributes{static_cast<MemoryAttribute>(mask | attribute)};
if (attributes != static_cast<MemoryAttribute>(mask) ||
(attributes | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
LOG_ERROR(Kernel_SVC,
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
attribute, mask);
return ResultInvalidCombination;
}
// Validate that the region is in range for the current process.
auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
return page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask), // Set the memory attribute.
static_cast<KMemoryAttribute>(attribute)); return page_table.SetMemoryAttribute(address, size, mask, attr);
} }
static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
u32 attribute) { u32 attr) {
return SetMemoryAttribute(system, address, size, mask, attribute); return SetMemoryAttribute(system, address, size, mask, attr);
} }
/// Maps a memory range into a different range. /// Maps a memory range into a different range.

View file

@ -32,6 +32,7 @@ enum class MemoryState : u32 {
Kernel = 0x13, Kernel = 0x13,
GeneratedCode = 0x14, GeneratedCode = 0x14,
CodeOut = 0x15, CodeOut = 0x15,
Coverage = 0x16,
}; };
DECLARE_ENUM_FLAG_OPERATORS(MemoryState); DECLARE_ENUM_FLAG_OPERATORS(MemoryState);