exo2: implement the atmosphere extension mappers

This commit is contained in:
Michael Scire 2020-05-20 00:34:01 -07:00 committed by SciresM
parent 1e0124fb67
commit 36754e2c38
12 changed files with 370 additions and 102 deletions

View file

@ -16,7 +16,9 @@
#include <exosphere.hpp> #include <exosphere.hpp>
#include "secmon_cache.hpp" #include "secmon_cache.hpp"
#include "secmon_setup.hpp" #include "secmon_setup.hpp"
#include "secmon_spinlock.hpp"
#include "secmon_map.hpp" #include "secmon_map.hpp"
#include "smc/secmon_smc_info.hpp"
namespace ams::secmon { namespace ams::secmon {
@ -26,10 +28,16 @@ namespace ams::secmon {
constexpr inline const size_t BootCodeSize = MemoryRegionVirtualTzramBootCode.GetSize(); constexpr inline const size_t BootCodeSize = MemoryRegionVirtualTzramBootCode.GetSize();
constinit uintptr_t g_smc_user_page_physical_address = 0; 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; using namespace ams::mmu;
constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal); 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) { 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. */ /* Unmap the L3 entries corresponding to the boot code. */
@ -57,6 +65,26 @@ namespace ams::secmon {
InvalidateL3Entries(l3, MemoryRegionVirtualSmcUserPage.GetAddress(), MemoryRegionVirtualSmcUserPage.GetSize()); 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) { void ClearLow(uintptr_t address, size_t size) {
/* Clear the low part. */ /* Clear the low part. */
util::ClearMemory(reinterpret_cast<void *>(address), size / 2); util::ClearMemory(reinterpret_cast<void *>(address), size / 2);
@ -67,6 +95,10 @@ namespace ams::secmon {
util::ClearMemory(reinterpret_cast<void *>(address + size / 2), size / 2); util::ClearMemory(reinterpret_cast<void *>(address + size / 2), size / 2);
} }
bool IsPhysicalMemoryAddress(uintptr_t address) {
return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize();
}
} }
void ClearBootCodeHigh() { void ClearBootCodeHigh() {
@ -88,6 +120,15 @@ namespace ams::secmon {
secmon::EnsureMappingConsistency(); 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() { void UnmapTzram() {
/* Get the tables. */ /* Get the tables. */
u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>(); u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>();
@ -102,9 +143,10 @@ namespace ams::secmon {
uintptr_t MapSmcUserPage(uintptr_t address) { uintptr_t MapSmcUserPage(uintptr_t address) {
if (g_smc_user_page_physical_address == 0) { if (g_smc_user_page_physical_address == 0) {
if (!(MemoryRegionDram.GetAddress() <= address && address <= MemoryRegionDramHigh.GetEndAddress() - MemoryRegionVirtualSmcUserPage.GetSize())) { if (!IsPhysicalMemoryAddress(address)) {
return 0; return 0;
} }
if (!util::IsAligned(address, 4_KB)) { if (!util::IsAligned(address, 4_KB)) {
return 0; return 0;
} }
@ -139,4 +181,106 @@ namespace ams::secmon {
g_smc_user_page_physical_address = 0; 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<u64>();
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<u64>();
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<u64>();
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<u64>();
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);
}
} }

View file

@ -18,9 +18,17 @@
namespace ams::secmon { namespace ams::secmon {
size_t GetPhysicalMemorySize();
void UnmapTzram(); void UnmapTzram();
uintptr_t MapSmcUserPage(uintptr_t address); uintptr_t MapSmcUserPage(uintptr_t address);
void UnmapSmcUserPage(); void UnmapSmcUserPage();
uintptr_t MapAtmosphereIramPage(uintptr_t address);
void UnmapAtmosphereIramPage();
uintptr_t MapAtmosphereUserPage(uintptr_t address);
void UnmapAtmosphereUserPage();
} }

View file

@ -15,21 +15,12 @@
*/ */
#pragma once #pragma once
#include <exosphere.hpp> #include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc { namespace ams::secmon {
class UserPageMapper { using SpinLockType = u32;
private:
uintptr_t physical_address;
uintptr_t virtual_address;
public:
constexpr UserPageMapper(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ }
bool Map(); void AcquireSpinLock(SpinLockType &lock);
void *GetPointerTo(uintptr_t phys, size_t size) const; void ReleaseSpinLock(SpinLockType &lock);
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;
};
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
.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

View file

@ -148,20 +148,8 @@ _ZN3ams6secmon25AcquireCommonSmcStackLockEv:
/* Get the address of the lock. */ /* Get the address of the lock. */
ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE
/* Prepare to try to take the spinlock. */ /* Take the lock. */
mov w1, #1 b _ZN3ams6secmon15AcquireSpinLockERj
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._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits .section .text._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits
.align 4 .align 4
@ -170,11 +158,8 @@ _ZN3ams6secmon25ReleaseCommonSmcStackLockEv:
/* Get the address of the lock. */ /* Get the address of the lock. */
ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE
/* Release the spinlock. */ /* Release the lock. */
stlr wzr, [x0] b _ZN3ams6secmon15ReleaseSpinLockERj
/* Return. */
ret
.section .text._ZN3ams6secmon26ReleaseCommonWarmbootStackEv, "ax", %progbits .section .text._ZN3ams6secmon26ReleaseCommonWarmbootStackEv, "ax", %progbits
.align 4 .align 4

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<void *>(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<MapSmcUserPage>();
}
bool AtmosphereIramPageMapper::Map() {
return this->MapImpl<MapAtmosphereIramPage>();
}
bool AtmosphereUserPageMapper::Map() {
return this->MapImpl<MapAtmosphereUserPage>();
}
AtmosphereIramPageMapper::~AtmosphereIramPageMapper() {
this->UnmapImpl<UnmapAtmosphereIramPage>();
}
AtmosphereUserPageMapper::~AtmosphereUserPageMapper() {
this->UnmapImpl<UnmapAtmosphereUserPage>();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#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<auto F>
bool MapImpl() {
this->virtual_address = F(this->physical_address);
return this->virtual_address != 0;
}
template<auto F>
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();
};
}

View file

@ -20,7 +20,7 @@
#include "secmon_smc_aes.hpp" #include "secmon_smc_aes.hpp"
#include "secmon_smc_device_unique_data.hpp" #include "secmon_smc_device_unique_data.hpp"
#include "secmon_smc_se_lock.hpp" #include "secmon_smc_se_lock.hpp"
#include "secmon_user_page_mapper.hpp" #include "secmon_page_mapper.hpp"
namespace ams::secmon::smc { namespace ams::secmon::smc {

View file

@ -97,12 +97,6 @@ namespace ams::secmon::smc {
return pkg1::MemoryMode_Auto; 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) { pkg1::MemorySize GetAvailableMemorySize(pkg1::MemorySize size) {
return std::min(GetPhysicalMemorySize(), size); return std::min(GetPhysicalMemorySize(), size);
} }
@ -294,4 +288,11 @@ namespace ams::secmon::smc {
return SmcResult::NotImplemented; 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];
}
} }

View file

@ -56,4 +56,7 @@ namespace ams::secmon::smc {
/* This is an atmosphere extension smc. */ /* This is an atmosphere extension smc. */
SmcResult SmcGetEmummcConfig(SmcArguments &args); SmcResult SmcGetEmummcConfig(SmcArguments &args);
/* For other parts of exosphere. */
pkg1::MemorySize GetPhysicalMemorySize();
} }

View file

@ -16,7 +16,7 @@
#include <exosphere.hpp> #include <exosphere.hpp>
#include "../secmon_error.hpp" #include "../secmon_error.hpp"
#include "secmon_smc_result.hpp" #include "secmon_smc_result.hpp"
#include "secmon_user_page_mapper.hpp" #include "secmon_page_mapper.hpp"
namespace ams::secmon::smc { namespace ams::secmon::smc {

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<void *>(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;
}
}