From 9f79710cb72e901ff12806117a2b6740e18ed32a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 14 Jul 2020 01:53:32 -0700 Subject: [PATCH] kern: add SvcReadWriteRegister --- .../nintendo/nx/kern_k_system_control.hpp | 2 +- .../nintendo/nx/kern_k_system_control.cpp | 98 +++++++++++++++++-- .../source/svc/kern_svc_register.cpp | 8 +- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index 569caf964..2f51e5d88 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -48,7 +48,7 @@ namespace ams::kern::board::nintendo::nx { /* Privileged Access. */ static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); - static void ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); + static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); static ALWAYS_INLINE u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address) { u32 v; diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index e8835b43f..a56809cd9 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -125,12 +125,95 @@ namespace ams::kern::board::nintendo::nx { return GetConfigU64(which) != 0; } + ALWAYS_INLINE bool CheckRegisterAllowedTable(const u8 *table, const size_t offset) { + return (table[(offset / sizeof(u32)) / BITSIZEOF(u8)] & (1u << ((offset / sizeof(u32)) % BITSIZEOF(u8)))) != 0; + } + + /* TODO: Generate this from a list of register names (see similar logic in exosphere)? */ + constexpr inline const u8 McKernelRegisterWhitelist[(PageSize / sizeof(u32)) / BITSIZEOF(u8)] = { + 0x9F, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0x73, 0x3E, 0x6F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + /* TODO: Generate this from a list of register names (see similar logic in exosphere)? */ + constexpr inline const u8 McUserRegisterWhitelist[(PageSize / sizeof(u32)) / BITSIZEOF(u8)] = { + 0x00, 0x00, 0x20, 0x00, 0xF0, 0xFF, 0xF7, 0x01, + 0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, + 0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04, + 0x80, 0xFF, 0x08, 0x80, 0x03, 0x38, 0x8E, 0x1F, + 0xC8, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0xF0, 0x1F, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F, + 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + bool IsRegisterAccessibleToPrivileged(ams::svc::PhysicalAddress address) { - if (!KMemoryLayout::GetMemoryControllerRegion().Contains(address)) { - return false; + /* Find the region for the address. */ + KMemoryRegionTree::const_iterator it = KMemoryLayout::FindContainingRegion(KPhysicalAddress(address)); + if (AMS_LIKELY(it != KMemoryLayout::GetPhysicalMemoryRegionTree().end())) { + if (AMS_LIKELY(it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController))) { + /* Get the offset within the region. */ + const size_t offset = address - it->GetAddress(); + MESOSPHERE_ABORT_UNLESS(offset < it->GetSize()); + + /* Check the whitelist. */ + if (AMS_LIKELY(CheckRegisterAllowedTable(McKernelRegisterWhitelist, offset))) { + return true; + } + } } - /* TODO: Validate specific offsets. */ - return true; + + return false; + } + + bool IsRegisterAccessibleToUser(ams::svc::PhysicalAddress address) { + /* Find the region for the address. */ + KMemoryRegionTree::const_iterator it = KMemoryLayout::FindContainingRegion(KPhysicalAddress(address)); + if (AMS_LIKELY(it != KMemoryLayout::GetPhysicalMemoryRegionTree().end())) { + /* The PMC is always allowed. */ + if (it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_PowerManagementController)) { + return true; + } + + /* Memory controller is allowed if the register is whitelisted. */ + if (it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController ) || + it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController0) || + it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController1)) + { + /* Get the offset within the region. */ + const size_t offset = address - it->GetAddress(); + MESOSPHERE_ABORT_UNLESS(offset < it->GetSize()); + + /* Check the whitelist. */ + if (AMS_LIKELY(CheckRegisterAllowedTable(McUserRegisterWhitelist, offset))) { + return true; + } + } + } + + return false; } } @@ -299,8 +382,11 @@ namespace ams::kern::board::nintendo::nx { MESOSPHERE_ABORT_UNLESS(smc::ReadWriteRegister(out, address, mask, value)); } - void KSystemControl::ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) { - MESOSPHERE_UNIMPLEMENTED(); + Result KSystemControl::ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) { + R_UNLESS(AMS_LIKELY(util::IsAligned(address, sizeof(u32))), svc::ResultInvalidAddress()); + R_UNLESS(AMS_LIKELY(IsRegisterAccessibleToUser(address)), svc::ResultInvalidAddress()); + R_UNLESS(AMS_LIKELY(smc::ReadWriteRegister(out, address, mask, value)), svc::ResultInvalidAddress()); + return ResultSuccess(); } /* Randomness. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_register.cpp b/libraries/libmesosphere/source/svc/kern_svc_register.cpp index fedf164b1..4e4fca545 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_register.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_register.cpp @@ -21,20 +21,22 @@ namespace ams::kern::svc { namespace { - + Result ReadWriteRegister(uint32_t *out, ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) { + return KSystemControl::ReadWriteRegister(out, address, mask, value); + } } /* ============================= 64 ABI ============================= */ Result ReadWriteRegister64(uint32_t *out_value, ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) { - MESOSPHERE_PANIC("Stubbed SvcReadWriteRegister64 was called."); + return ReadWriteRegister(out_value, address, mask, value); } /* ============================= 64From32 ABI ============================= */ Result ReadWriteRegister64From32(uint32_t *out_value, ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) { - MESOSPHERE_PANIC("Stubbed SvcReadWriteRegister64From32 was called."); + return ReadWriteRegister(out_value, address, mask, value); } }