mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-01-18 15:21:34 +00:00
thermosphere: mmu table builder
This commit is contained in:
parent
d4bbb78a27
commit
987731ea43
2 changed files with 240 additions and 0 deletions
197
thermosphere/src/cpu/hvisor_cpu_mmu.hpp
Normal file
197
thermosphere/src/cpu/hvisor_cpu_mmu.hpp
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-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 "hvisor_cpu_sysreg_general.hpp"
|
||||||
|
|
||||||
|
namespace ams::hvisor::cpu {
|
||||||
|
|
||||||
|
// Assumes addr is valid, must be called with interrupts masked
|
||||||
|
inline uintptr_t Va2Pa(const void *vaddrEl2) {
|
||||||
|
uintptr_t va = reinterpret_cast<uintptr_t>(vaddrEl2);
|
||||||
|
__asm__ __volatile__("at s1e2r, %0" :: "r"(va) : "memory");
|
||||||
|
return (THERMOSPHERE_GET_SYSREG(par_el1) & MASK2L(47, 12)) | (va & MASKL(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MmuPteType : u64 {
|
||||||
|
MMU_ENTRY_FAULT = 0,
|
||||||
|
MMU_ENTRY_BLOCK = 1,
|
||||||
|
MMU_ENTRY_TABLE = 3,
|
||||||
|
|
||||||
|
// L3 (this definition allows for recursive page tables)
|
||||||
|
MMU_ENTRY_PAGE = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Multi-byte attributes...
|
||||||
|
constexpr u64 MMU_ATTRINDEX(u64 idx) { return (idx & 8) << 2; }
|
||||||
|
constexpr u64 MMU_MEMATTR(u64 attr) { return (attr & 0xF) << 2; }
|
||||||
|
|
||||||
|
// Attributes. They are defined in a way that allows recursive page tables (assuming PBHA isn't used)
|
||||||
|
enum MmuPteAttributes : u64 {
|
||||||
|
// Stage 1 Table only, the rest is block/page only
|
||||||
|
MMU_NS_TABLE = BITL(62),
|
||||||
|
MMU_AP_TABLE = BITL(61),
|
||||||
|
MMU_XN_TABLE = BITL(60),
|
||||||
|
MMU_PXN_TABLE = BITL(59),
|
||||||
|
|
||||||
|
MMU_UXN = BITL(54), // EL1&0 only
|
||||||
|
MMU_PXN = BITL(53), // EL1&0 only
|
||||||
|
MMU_XN = MMU_UXN,
|
||||||
|
MMU_XN0 = MMU_PXN, // Armv8.2, stage 2 only
|
||||||
|
MMU_CONTIGUOUS = BITL(52),
|
||||||
|
MMU_DBM = BITL(51), // stage 1 only
|
||||||
|
MMU_GP = BITL(50), // undocumented
|
||||||
|
|
||||||
|
// ARMv8.4-TTRem only
|
||||||
|
MMU_NT = BITL(16),
|
||||||
|
|
||||||
|
// EL1&0 only
|
||||||
|
MMU_NG = BITL(11),
|
||||||
|
|
||||||
|
MMU_AF = BITL(10),
|
||||||
|
|
||||||
|
// SH[1:0]
|
||||||
|
MMU_NON_SHAREABLE = 0 << 8,
|
||||||
|
MMU_OUTER_SHAREABLE = 2 << 8,
|
||||||
|
MMU_INNER_SHAREABLE = 2 << 8,
|
||||||
|
|
||||||
|
// AP[2:1], stage 1 only. AP[0] does not exist.
|
||||||
|
MMU_AP_PRIV_RW = 0 << 6,
|
||||||
|
MMU_AP_RW = 1 << 6,
|
||||||
|
MMU_AP_PRIV_RO = 2 << 6,
|
||||||
|
MMU_AP_RO = 3 << 6,
|
||||||
|
|
||||||
|
// S2AP[1:0], stage 2 only
|
||||||
|
MMU_S2AP_NONE = 0 << 6,
|
||||||
|
MMU_S2AP_RO = 1 << 6,
|
||||||
|
MMU_S2AP_WO = 2 << 6,
|
||||||
|
MMU_S2AP_RW = 3 << 6,
|
||||||
|
|
||||||
|
// NS, stage 1 only
|
||||||
|
MMU_NS = BITL(5),
|
||||||
|
|
||||||
|
// See above...
|
||||||
|
|
||||||
|
// MemAttr[3:0], stage 2 only (convenience defs). When combining, strongest memory type applies
|
||||||
|
MMU_MEMATTR_DEVICE_NGNRE = MMU_MEMATTR(2),
|
||||||
|
MMU_MEMATTR_UNCHANGED = MMU_MEMATTR(0xF),
|
||||||
|
|
||||||
|
// Other useful defines for stage 2:
|
||||||
|
MMU_SAME_SHAREABILITY = MMU_NON_SHAREABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
template<u32 Level, u32 AddressSpaceSize, bool IsMmuEnabled = false, TranslationGranuleSize GranuleSize = TranslationGranule_4K>
|
||||||
|
class MmuTableBuilder final {
|
||||||
|
private:
|
||||||
|
static constexpr u32 tgBitSize = GetTranslationGranuleBitSize(GranuleSize);
|
||||||
|
|
||||||
|
// tgBitSize - 3 = log2(tg / sizeof(u64))
|
||||||
|
static constexpr u32 levelShift = tgBitSize + (tgBitSize - 3) * (3 - Level);
|
||||||
|
static constexpr u32 levelBitSize = std::min(AddressSpaceSize - levelShift, tgBitSize - 3);
|
||||||
|
static constexpr u64 levelMask = MASKL(levelBitSize);
|
||||||
|
static constexpr size_t ComputeIndex(uintptr_t va)
|
||||||
|
{
|
||||||
|
return (va >> levelShift) & levelMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64 *m_pageTable = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using NextLevelBuilder = MmuTableBuilder<Level + 1, AddressSpaceSize, IsMmuEnabled, GranuleSize>;
|
||||||
|
static_assert(Level <= 3, "Invalid translation table level");
|
||||||
|
static_assert(AddressSpaceSize <= 48);
|
||||||
|
static_assert(AddressSpaceSize > levelShift, "Address space size mismatch with translation level");
|
||||||
|
static constexpr size_t blockSize = BITL(levelShift);
|
||||||
|
static constexpr size_t tableSize = BITL(levelBitSize);
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr MmuTableBuilder(u64 *pageTable = nullptr) : m_pageTable{pageTable} {}
|
||||||
|
|
||||||
|
constexpr MmuTableBuilder &InitializeTable()
|
||||||
|
{
|
||||||
|
std::memset(m_pageTable, 0, 8 * tableSize);
|
||||||
|
// Fails to optimize before GCC 10: std::fill_n(m_pageTable, tableSize, MMU_ENTRY_FAULT);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: va and pa bits in range
|
||||||
|
constexpr NextLevelBuilder MapTable(uintptr_t va, uintptr_t pa, u64 *table, u64 attribs = 0) const
|
||||||
|
{
|
||||||
|
static_assert(Level < 3, "Level 3 is the last level of translation");
|
||||||
|
|
||||||
|
m_pageTable[ComputeIndex(va)] = pa | attribs | MMU_ENTRY_TABLE;
|
||||||
|
return NextLevelBuilder{table};
|
||||||
|
}
|
||||||
|
|
||||||
|
NextLevelBuilder MapTable(uintptr_t va, u64 *table, u64 attribs = 0) const
|
||||||
|
{
|
||||||
|
if constexpr (IsMmuEnabled) {
|
||||||
|
return MapTable(va, Va2Pa(table), table, attribs);
|
||||||
|
} else {
|
||||||
|
return MapTable(va, reinterpret_cast<uintptr_t>(table), table, attribs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MmuTableBuilder &Unmap(uintptr_t va)
|
||||||
|
{
|
||||||
|
m_pageTable[ComputeIndex(va)] = MMU_ENTRY_FAULT;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: guardSize == 0 if Level == 0
|
||||||
|
constexpr MmuTableBuilder &UnmapRange(uintptr_t va, size_t size, size_t guardSize = 0)
|
||||||
|
{
|
||||||
|
for (size_t off = 0, offVa = 0; off < size; off += blockSize, offVa += blockSize + guardSize) {
|
||||||
|
Unmap(va + offVa);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: va and pa bits in range
|
||||||
|
constexpr MmuTableBuilder &MapBlock(uintptr_t va, uintptr_t pa, u64 attribs)
|
||||||
|
{
|
||||||
|
static_assert(Level > 0, "Can only map L1 tables at L0");
|
||||||
|
|
||||||
|
constexpr u64 entryType = Level == 3 ? MMU_ENTRY_PAGE : MMU_ENTRY_BLOCK;
|
||||||
|
m_pageTable[ComputeIndex(va)] = pa | attribs | MMU_AF | entryType;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MmuTableBuilder &MapBlock(uintptr_t pa, u64 attribs)
|
||||||
|
{
|
||||||
|
return MapBlock(pa, pa, attribs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: size and guardSize are multiples of blockSize
|
||||||
|
constexpr MmuTableBuilder &MapBlockRange(uintptr_t va, uintptr_t pa, size_t size, u64 attribs, size_t guardSize = 0)
|
||||||
|
{
|
||||||
|
for (size_t off = 0, offVa = 0; off < size; off += blockSize, offVa += blockSize + guardSize) {
|
||||||
|
MapBlock(va + offVa, pa + off, attribs);
|
||||||
|
UnmapRange(va + offVa + blockSize, guardSize, 0);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr MmuTableBuilder &MapBlockRange(uintptr_t pa, size_t size, u64 attribs)
|
||||||
|
{
|
||||||
|
return MapBlockRange(pa, pa, attribs, size, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -445,4 +445,47 @@ namespace ams::hvisor::cpu {
|
||||||
CNTCTL_ENABLE = BITL(0),
|
CNTCTL_ENABLE = BITL(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TCR_ELx flags
|
||||||
|
enum TcrFlags {
|
||||||
|
TCR_IRGN_NC = (0 << 8),
|
||||||
|
TCR_IRGN_WBWA = (1 << 8),
|
||||||
|
TCR_IRGN_WT = (2 << 8),
|
||||||
|
TCR_IRGN_WBNWA = (3 << 8),
|
||||||
|
TCR_IRGN_MASK = (3 << 8),
|
||||||
|
TCR_ORGN_NC = (0 << 10),
|
||||||
|
TCR_ORGN_WBWA = (1 << 10),
|
||||||
|
TCR_ORGN_WT = (2 << 10),
|
||||||
|
TCR_ORGN_WBNWA = (3 << 10),
|
||||||
|
TCR_ORGN_MASK = (3 << 10),
|
||||||
|
TCR_NOT_SHARED = (0 << 12),
|
||||||
|
TCR_SHARED_OUTER = (2 << 12),
|
||||||
|
TCR_SHARED_INNER = (3 << 12),
|
||||||
|
TCR_EPD1_DISABLE = BITL(23),
|
||||||
|
|
||||||
|
TCR_EL1_RSVD = BITL(31),
|
||||||
|
TCR_EL2_RSVD = (BITL(31) | BITL(23)),
|
||||||
|
VTCR_EL2_RSVD = BITL(31),
|
||||||
|
TCR_EL3_RSVD = (BITL(31) | BITL(23)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Could have used enum class here, but can't start identifiers with a digit...
|
||||||
|
enum TranslationGranuleSize {
|
||||||
|
TranslationGranule_4K = 0,
|
||||||
|
TranslationGranule_64K = 1,
|
||||||
|
TranslationGranule_16K = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t GetTranslationGranuleBitSize(TranslationGranuleSize granuleSize)
|
||||||
|
{
|
||||||
|
switch (granuleSize) {
|
||||||
|
case TranslationGranule_4K: return 12;
|
||||||
|
case TranslationGranule_64K: return 16;
|
||||||
|
case TranslationGranule_16K: return 14;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 TCR_T0SZ(size_t addressSpaceSize) { return (64ul - (addressSpaceSize & 0x3F)) << 0; }
|
||||||
|
constexpr u64 TCR_PS(u64 n) { return (n & 7) << 16; }
|
||||||
|
constexpr u64 VTCR_SL0(u64 n) { return (n & 3) << 6; }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue