diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index f8e5d9b1d..3f6c5b4e0 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -16,7 +16,9 @@ #include #include "secmon_cache.hpp" #include "secmon_setup.hpp" +#include "secmon_spinlock.hpp" #include "secmon_map.hpp" +#include "smc/secmon_smc_info.hpp" namespace ams::secmon { @@ -26,10 +28,16 @@ namespace ams::secmon { constexpr inline const size_t BootCodeSize = MemoryRegionVirtualTzramBootCode.GetSize(); constinit uintptr_t g_smc_user_page_physical_address = 0; + constinit uintptr_t g_ams_iram_page_physical_address = 0; + constinit uintptr_t g_ams_user_page_physical_address = 0; + + constinit SpinLockType g_ams_iram_page_spin_lock = {}; + constinit SpinLockType g_ams_user_page_spin_lock = {}; using namespace ams::mmu; constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexDevice); constexpr void UnmapBootCodeImpl(u64 *l1, u64 *l2, u64 *l3, uintptr_t boot_code, size_t boot_code_size) { /* Unmap the L3 entries corresponding to the boot code. */ @@ -57,6 +65,26 @@ namespace ams::secmon { InvalidateL3Entries(l3, MemoryRegionVirtualSmcUserPage.GetAddress(), MemoryRegionVirtualSmcUserPage.GetSize()); } + constexpr void MapAtmosphereIramPageImpl(u64 *l3, uintptr_t address) { + /* Set the L3 entry. */ + SetL3BlockEntry(l3, MemoryRegionVirtualAtmosphereIramPage.GetAddress(), address, MemoryRegionVirtualAtmosphereIramPage.GetSize(), MappingAttributesEl3NonSecureDevice); + } + + constexpr void UnmapAtmosphereIramPageImpl(u64 *l3) { + /* Unmap the L3 entry. */ + InvalidateL3Entries(l3, MemoryRegionVirtualAtmosphereIramPage.GetAddress(), MemoryRegionVirtualAtmosphereIramPage.GetSize()); + } + + constexpr void MapAtmosphereUserPageImpl(u64 *l3, uintptr_t address) { + /* Set the L3 entry. */ + SetL3BlockEntry(l3, MemoryRegionVirtualAtmosphereUserPage.GetAddress(), address, MemoryRegionVirtualAtmosphereUserPage.GetSize(), MappingAttributesEl3NonSecureRwData); + } + + constexpr void UnmapAtmosphereUserPageImpl(u64 *l3) { + /* Unmap the L3 entry. */ + InvalidateL3Entries(l3, MemoryRegionVirtualAtmosphereUserPage.GetAddress(), MemoryRegionVirtualAtmosphereUserPage.GetSize()); + } + void ClearLow(uintptr_t address, size_t size) { /* Clear the low part. */ util::ClearMemory(reinterpret_cast(address), size / 2); @@ -67,6 +95,10 @@ namespace ams::secmon { util::ClearMemory(reinterpret_cast(address + size / 2), size / 2); } + bool IsPhysicalMemoryAddress(uintptr_t address) { + return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize(); + } + } void ClearBootCodeHigh() { @@ -88,6 +120,15 @@ namespace ams::secmon { secmon::EnsureMappingConsistency(); } + size_t GetPhysicalMemorySize() { + switch (smc::GetPhysicalMemorySize()) { + case pkg1::MemorySize_4GB: return 4_GB; + case pkg1::MemorySize_6GB: return 6_GB; + case pkg1::MemorySize_8GB: return 8_GB; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + void UnmapTzram() { /* Get the tables. */ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer(); @@ -102,9 +143,10 @@ namespace ams::secmon { uintptr_t MapSmcUserPage(uintptr_t address) { if (g_smc_user_page_physical_address == 0) { - if (!(MemoryRegionDram.GetAddress() <= address && address <= MemoryRegionDramHigh.GetEndAddress() - MemoryRegionVirtualSmcUserPage.GetSize())) { + if (!IsPhysicalMemoryAddress(address)) { return 0; } + if (!util::IsAligned(address, 4_KB)) { return 0; } @@ -139,4 +181,106 @@ namespace ams::secmon { g_smc_user_page_physical_address = 0; } + uintptr_t MapAtmosphereIramPage(uintptr_t address) { + /* Acquire the ams iram spinlock. */ + AcquireSpinLock(g_ams_iram_page_spin_lock); + auto lock_guard = SCOPE_GUARD { ReleaseSpinLock(g_ams_iram_page_spin_lock); }; + + /* Validate that the page is an IRAM page. */ + if (!MemoryRegionPhysicalIram.Contains(address, 1)) { + return 0; + } + + /* Validate that the page is aligned. */ + if (!util::IsAligned(address, 4_KB)) { + return 0; + } + + /* Map the page. */ + g_ams_iram_page_physical_address = address; + + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer(); + + MapAtmosphereIramPageImpl(l2_l3, address); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereIramPage.GetAddress()); + + /* Hold the lock. */ + lock_guard.Cancel(); + + return true; + } + + void UnmapAtmosphereIramPage() { + /* Can't unmap if nothing's unmapped. */ + if (g_ams_iram_page_physical_address == 0) { + return; + } + + /* Unmap the page. */ + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer(); + + UnmapAtmosphereIramPageImpl(l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereIramPage.GetAddress()); + + /* Release the page. */ + g_ams_iram_page_physical_address = 0; + + ReleaseSpinLock(g_ams_iram_page_spin_lock); + } + + uintptr_t MapAtmosphereUserPage(uintptr_t address) { + /* Acquire the ams user spinlock. */ + AcquireSpinLock(g_ams_user_page_spin_lock); + auto lock_guard = SCOPE_GUARD { ReleaseSpinLock(g_ams_user_page_spin_lock); }; + + /* Validate that the page is a dram page. */ + if (!IsPhysicalMemoryAddress(address)) { + return 0; + } + + /* Validate that the page is aligned. */ + if (!util::IsAligned(address, 4_KB)) { + return 0; + } + + /* Map the page. */ + g_ams_user_page_physical_address = address; + + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer(); + + MapAtmosphereUserPageImpl(l2_l3, address); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereUserPage.GetAddress()); + + /* Hold the lock. */ + lock_guard.Cancel(); + + return true; + } + + void UnmapAtmosphereUserPage() { + /* Can't unmap if nothing's unmapped. */ + if (g_ams_user_page_physical_address == 0) { + return; + } + + /* Unmap the page. */ + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer(); + + UnmapAtmosphereUserPageImpl(l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereUserPage.GetAddress()); + + /* Release the page. */ + g_ams_user_page_physical_address = 0; + + ReleaseSpinLock(g_ams_user_page_spin_lock); + } + } diff --git a/exosphere2/program/source/secmon_map.hpp b/exosphere2/program/source/secmon_map.hpp index 2d74d4a8e..a6bdbc405 100644 --- a/exosphere2/program/source/secmon_map.hpp +++ b/exosphere2/program/source/secmon_map.hpp @@ -18,9 +18,17 @@ namespace ams::secmon { + size_t GetPhysicalMemorySize(); + void UnmapTzram(); uintptr_t MapSmcUserPage(uintptr_t address); void UnmapSmcUserPage(); + uintptr_t MapAtmosphereIramPage(uintptr_t address); + void UnmapAtmosphereIramPage(); + + uintptr_t MapAtmosphereUserPage(uintptr_t address); + void UnmapAtmosphereUserPage(); + } \ No newline at end of file diff --git a/exosphere2/program/source/smc/secmon_user_page_mapper.hpp b/exosphere2/program/source/secmon_spinlock.hpp similarity index 53% rename from exosphere2/program/source/smc/secmon_user_page_mapper.hpp rename to exosphere2/program/source/secmon_spinlock.hpp index cec6eb96d..3c095da72 100644 --- a/exosphere2/program/source/smc/secmon_user_page_mapper.hpp +++ b/exosphere2/program/source/secmon_spinlock.hpp @@ -15,21 +15,12 @@ */ #pragma once #include -#include "secmon_smc_common.hpp" -namespace ams::secmon::smc { +namespace ams::secmon { - class UserPageMapper { - private: - uintptr_t physical_address; - uintptr_t virtual_address; - public: - constexpr UserPageMapper(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ } + using SpinLockType = u32; - bool Map(); - void *GetPointerTo(uintptr_t phys, size_t size) const; - bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const; - bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const; - }; + void AcquireSpinLock(SpinLockType &lock); + void ReleaseSpinLock(SpinLockType &lock); } diff --git a/exosphere2/program/source/secmon_spinlock.s b/exosphere2/program/source/secmon_spinlock.s new file mode 100644 index 000000000..0e4ef7f64 --- /dev/null +++ b/exosphere2/program/source/secmon_spinlock.s @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .text._ZN3ams6secmon15AcquireSpinLockERj, "ax", %progbits +.align 4 +.global _ZN3ams6secmon15AcquireSpinLockERj +_ZN3ams6secmon15AcquireSpinLockERj: + /* Prepare to try to take the spinlock. */ + mov w1, #1 + sevl + prfm pstl1keep, [x0] + +1: /* Repeatedly try to take the lock. */ + wfe + ldaxr w2, [x0] + cbnz w2, 1b + stxr w2, w1, [x0] + cbnz w2, 1b + + /* Return. */ + ret + +.section .text._ZN3ams6secmon15ReleaseSpinLockERj, "ax", %progbits +.align 4 +.global _ZN3ams6secmon15ReleaseSpinLockERj +_ZN3ams6secmon15ReleaseSpinLockERj: + /* Release the spinlock. */ + stlr wzr, [x0] + + /* Return. */ + ret diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index 37f81f118..8d23a66df 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -148,20 +148,8 @@ _ZN3ams6secmon25AcquireCommonSmcStackLockEv: /* Get the address of the lock. */ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE - /* Prepare to try to take the spinlock. */ - mov w1, #1 - sevl - prfm pstl1keep, [x0] - -1: /* Repeatedly try to take the lock. */ - wfe - ldaxr w2, [x0] - cbnz w2, 1b - stxr w2, w1, [x0] - cbnz w2, 1b - - /* Return. */ - ret + /* Take the lock. */ + b _ZN3ams6secmon15AcquireSpinLockERj .section .text._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits .align 4 @@ -170,11 +158,8 @@ _ZN3ams6secmon25ReleaseCommonSmcStackLockEv: /* Get the address of the lock. */ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE - /* Release the spinlock. */ - stlr wzr, [x0] - - /* Return. */ - ret + /* Release the lock. */ + b _ZN3ams6secmon15ReleaseSpinLockERj .section .text._ZN3ams6secmon26ReleaseCommonWarmbootStackEv, "ax", %progbits .align 4 diff --git a/exosphere2/program/source/smc/secmon_page_mapper.cpp b/exosphere2/program/source/smc/secmon_page_mapper.cpp new file mode 100644 index 000000000..8d2008907 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_page_mapper.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_map.hpp" +#include "secmon_page_mapper.hpp" + +namespace ams::secmon::smc { + + namespace impl { + + void *PageMapperImpl::GetPointerTo(uintptr_t phys, size_t size) const { + /* Ensure we stay within the page. */ + if (util::AlignDown(phys, 4_KB) != this->physical_address) { + return nullptr; + } + if (size != 0) { + if (util::AlignDown(phys + size - 1, 4_KB) != this->physical_address) { + return nullptr; + } + } + + return reinterpret_cast(phys + (this->virtual_address - this->physical_address)); + } + + bool PageMapperImpl::CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { + void * const dst = this->GetPointerTo(dst_phys, size); + if (dst == nullptr) { + return false; + } + + std::memcpy(dst, src, size); + return true; + } + + bool PageMapperImpl::CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { + const void * const src = this->GetPointerTo(src_phys, size); + if (src == nullptr) { + return false; + } + + std::memcpy(dst, src, size); + return true; + } + + } + + bool UserPageMapper::Map() { + return this->MapImpl(); + } + + bool AtmosphereIramPageMapper::Map() { + return this->MapImpl(); + } + + bool AtmosphereUserPageMapper::Map() { + return this->MapImpl(); + } + + AtmosphereIramPageMapper::~AtmosphereIramPageMapper() { + this->UnmapImpl(); + } + + AtmosphereUserPageMapper::~AtmosphereUserPageMapper() { + this->UnmapImpl(); + } + +} diff --git a/exosphere2/program/source/smc/secmon_page_mapper.hpp b/exosphere2/program/source/smc/secmon_page_mapper.hpp new file mode 100644 index 000000000..a57f70812 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_page_mapper.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + namespace impl { + + class PageMapperImpl { + private: + uintptr_t physical_address; + uintptr_t virtual_address; + public: + constexpr PageMapperImpl(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ } + + void *GetPointerTo(uintptr_t phys, size_t size) const; + bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const; + bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const; + + template + bool MapImpl() { + this->virtual_address = F(this->physical_address); + return this->virtual_address != 0; + } + + template + void UnmapImpl() { + F(); + this->virtual_address = 0; + } + }; + + } + + class UserPageMapper : public impl::PageMapperImpl { + public: + constexpr UserPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ } + + bool Map(); + }; + + class AtmosphereIramPageMapper : public impl::PageMapperImpl { + public: + constexpr AtmosphereIramPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ } + ~AtmosphereIramPageMapper(); + + bool Map(); + }; + + class AtmosphereUserPageMapper : public impl::PageMapperImpl { + public: + constexpr AtmosphereUserPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ } + ~AtmosphereUserPageMapper(); + + bool Map(); + }; + +} diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 65af2f083..03b2b1d49 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -20,7 +20,7 @@ #include "secmon_smc_aes.hpp" #include "secmon_smc_device_unique_data.hpp" #include "secmon_smc_se_lock.hpp" -#include "secmon_user_page_mapper.hpp" +#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp index 08540e09e..ae6f0ccc3 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -97,12 +97,6 @@ namespace ams::secmon::smc { return pkg1::MemoryMode_Auto; } - pkg1::MemorySize GetPhysicalMemorySize() { - const auto dram_id = fuse::GetDramId(); - AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); - return DramIdToMemorySize[dram_id]; - } - pkg1::MemorySize GetAvailableMemorySize(pkg1::MemorySize size) { return std::min(GetPhysicalMemorySize(), size); } @@ -294,4 +288,11 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + /* For exosphere's usage. */ + pkg1::MemorySize GetPhysicalMemorySize() { + const auto dram_id = fuse::GetDramId(); + AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); + return DramIdToMemorySize[dram_id]; + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp index 26d28e17b..d45bf57d2 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.hpp +++ b/exosphere2/program/source/smc/secmon_smc_info.hpp @@ -56,4 +56,7 @@ namespace ams::secmon::smc { /* This is an atmosphere extension smc. */ SmcResult SmcGetEmummcConfig(SmcArguments &args); + /* For other parts of exosphere. */ + pkg1::MemorySize GetPhysicalMemorySize(); + } diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp index 702f12ff2..83fa1b404 100644 --- a/exosphere2/program/source/smc/secmon_smc_result.cpp +++ b/exosphere2/program/source/smc/secmon_smc_result.cpp @@ -16,7 +16,7 @@ #include #include "../secmon_error.hpp" #include "secmon_smc_result.hpp" -#include "secmon_user_page_mapper.hpp" +#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { diff --git a/exosphere2/program/source/smc/secmon_user_page_mapper.cpp b/exosphere2/program/source/smc/secmon_user_page_mapper.cpp deleted file mode 100644 index 3748592db..000000000 --- a/exosphere2/program/source/smc/secmon_user_page_mapper.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_map.hpp" -#include "secmon_user_page_mapper.hpp" - -namespace ams::secmon::smc { - - bool UserPageMapper::Map() { - this->virtual_address = MapSmcUserPage(this->physical_address); - return this->virtual_address != 0; - } - - void *UserPageMapper::GetPointerTo(uintptr_t phys, size_t size) const { - /* Ensure we stay within the page. */ - if (util::AlignDown(phys, 4_KB) != this->physical_address) { - return nullptr; - } - if (size != 0) { - if (util::AlignDown(phys + size - 1, 4_KB) != this->physical_address) { - return nullptr; - } - } - - return reinterpret_cast(phys + (this->virtual_address - this->physical_address)); - } - - bool UserPageMapper::CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { - void * const dst = this->GetPointerTo(dst_phys, size); - if (dst == nullptr) { - return false; - } - - std::memcpy(dst, src, size); - return true; - } - - bool UserPageMapper::CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { - const void * const src = this->GetPointerTo(src_phys, size); - if (src == nullptr) { - return false; - } - - std::memcpy(dst, src, size); - return true; - } - -}