mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-06-04 08:29:44 -04:00
kern: implement enough of KPageTable to initialize a thread
This commit is contained in:
parent
c6d1579265
commit
8c93eb5712
31 changed files with 1475 additions and 270 deletions
|
@ -18,169 +18,9 @@
|
|||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_typed_address.hpp>
|
||||
#include <mesosphere/kern_select_cpu.hpp>
|
||||
#include <mesosphere/arch/arm64/kern_k_page_table_entry.hpp>
|
||||
|
||||
namespace ams::kern::init {
|
||||
|
||||
constexpr size_t L1BlockSize = 1_GB;
|
||||
constexpr size_t L2BlockSize = 2_MB;
|
||||
constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize;
|
||||
constexpr size_t L3BlockSize = PageSize;
|
||||
constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize;
|
||||
|
||||
class PageTableEntry {
|
||||
public:
|
||||
enum Permission : u64 {
|
||||
Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)),
|
||||
Permission_KernelRX = ((0ul << 53) | (1ul << 54) | (2ul << 6)),
|
||||
Permission_KernelR = ((1ul << 53) | (1ul << 54) | (2ul << 6)),
|
||||
Permission_KernelRW = ((1ul << 53) | (1ul << 54) | (0ul << 6)),
|
||||
|
||||
Permission_UserRX = ((1ul << 53) | (0ul << 54) | (3ul << 6)),
|
||||
Permission_UserR = ((1ul << 53) | (1ul << 54) | (3ul << 6)),
|
||||
Permission_UserRW = ((1ul << 53) | (1ul << 54) | (1ul << 6)),
|
||||
};
|
||||
|
||||
enum Shareable : u64 {
|
||||
Shareable_NonShareable = (0 << 8),
|
||||
Shareable_OuterShareable = (2 << 8),
|
||||
Shareable_InnerShareable = (3 << 8),
|
||||
};
|
||||
|
||||
/* Official attributes are: */
|
||||
/* 0x00, 0x04, 0xFF, 0x44. 4-7 are unused. */
|
||||
enum PageAttribute : u64 {
|
||||
PageAttribute_Device_nGnRnE = (0 << 2),
|
||||
PageAttribute_Device_nGnRE = (1 << 2),
|
||||
PageAttribute_NormalMemory = (2 << 2),
|
||||
PageAttribute_NormalMemoryNotCacheable = (3 << 2),
|
||||
};
|
||||
|
||||
enum AccessFlag : u64 {
|
||||
AccessFlag_NotAccessed = (0 << 10),
|
||||
AccessFlag_Accessed = (1 << 10),
|
||||
};
|
||||
protected:
|
||||
u64 attributes;
|
||||
public:
|
||||
/* Take in a raw attribute. */
|
||||
constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
|
||||
|
||||
/* Extend a previous attribute. */
|
||||
constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
|
||||
|
||||
/* Construct a new attribute. */
|
||||
constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share)
|
||||
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share))
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
protected:
|
||||
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
|
||||
return (this->attributes >> offset) & ((1ul << count) - 1);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u64 SelectBits(size_t offset, size_t count) const {
|
||||
return this->attributes & (((1ul << count) - 1) << offset);
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->GetBits(10, 1)); }
|
||||
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
|
||||
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->GetBits(2, 3)); }
|
||||
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x1; }
|
||||
constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; }
|
||||
|
||||
/* Should not be called except by derived classes. */
|
||||
constexpr ALWAYS_INLINE u64 GetRawAttributes() const {
|
||||
return this->attributes;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(PageTableEntry) == sizeof(u64));
|
||||
|
||||
constexpr PageTableEntry InvalidPageTableEntry = PageTableEntry(0);
|
||||
|
||||
constexpr size_t MaxPageTableEntries = PageSize / sizeof(PageTableEntry);
|
||||
|
||||
class L1PageTableEntry : public PageTableEntry {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
|
||||
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
|
||||
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
|
||||
return this->SelectBits(30, 18);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
|
||||
return this->SelectBits(12, 36);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
|
||||
/* Check whether this has the same permission/etc as the desired attributes. */
|
||||
return L1PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
class L2PageTableEntry : public PageTableEntry {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
|
||||
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
|
||||
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
|
||||
return this->SelectBits(21, 27);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
|
||||
return this->SelectBits(12, 36);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
|
||||
/* Check whether this has the same permission/etc as the desired attributes. */
|
||||
return L2PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
class L3PageTableEntry : public PageTableEntry {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
|
||||
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x3; }
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
|
||||
return this->SelectBits(12, 36);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
|
||||
/* Check whether this has the same permission/etc as the desired attributes. */
|
||||
return L3PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
namespace ams::kern::arm64::init {
|
||||
|
||||
class KInitialPageTable {
|
||||
public:
|
||||
|
|
|
@ -124,11 +124,28 @@ namespace ams::kern::arm64::cpu {
|
|||
ClearPageToZeroImpl(page);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InvalidateTlbByAsid(u32 asid) {
|
||||
const u64 value = (static_cast<u64>(asid) << 48);
|
||||
__asm__ __volatile__("tlbi aside1is, %[value]" :: [value]"r"(static_cast<u64>(value) << 48) : "memory");
|
||||
EnsureInstructionConsistency();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InvalidateTlbByAsidAndVa(u32 asid, KProcessAddress virt_addr) {
|
||||
const u64 value = (static_cast<u64>(asid) << 48) | ((GetInteger(virt_addr) >> 12) & 0xFFFFFFFFFFFul);
|
||||
__asm__ __volatile__("tlbi aside1is, %[value]" :: [value]"r"(value) : "memory");
|
||||
EnsureInstructionConsistency();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InvalidateEntireTlb() {
|
||||
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||
EnsureInstructionConsistency();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
|
||||
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||
DataSynchronizationBarrier();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE uintptr_t GetCoreLocalRegionAddress() {
|
||||
register uintptr_t x18 asm("x18");
|
||||
__asm__ __volatile__("" : [x18]"=r"(x18));
|
||||
|
|
|
@ -97,7 +97,13 @@ namespace ams::kern::arm64::cpu {
|
|||
constexpr ALWAYS_INLINE void SetBits(size_t offset, size_t count, u64 value) {
|
||||
const u64 mask = ((1ul << count) - 1) << offset;
|
||||
this->value &= ~mask;
|
||||
this->value |= (value & mask) << offset;
|
||||
this->value |= (value & (mask >> offset)) << offset;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetBitsDirect(size_t offset, size_t count, u64 value) {
|
||||
const u64 mask = ((1ul << count) - 1) << offset;
|
||||
this->value &= ~mask;
|
||||
this->value |= (value & mask);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetBit(size_t offset, bool enabled) {
|
||||
|
|
|
@ -29,6 +29,127 @@ namespace ams::kern::arm64 {
|
|||
KPageTableManager *manager;
|
||||
u64 ttbr;
|
||||
u8 asid;
|
||||
private:
|
||||
enum BlockType {
|
||||
BlockType_L3Block,
|
||||
BlockType_L3ContiguousBlock,
|
||||
BlockType_L2Block,
|
||||
|
||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_SWITCH
|
||||
BlockType_L2TegraSmmuBlock,
|
||||
#endif
|
||||
|
||||
BlockType_L2ContiguousBlock,
|
||||
BlockType_L1Block,
|
||||
|
||||
BlockType_Count,
|
||||
};
|
||||
|
||||
static_assert(L3BlockSize == PageSize);
|
||||
static constexpr size_t ContiguousPageSize = L3ContiguousBlockSize;
|
||||
|
||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_SWITCH
|
||||
static constexpr size_t L2TegraSmmuBlockSize = 2 * L2BlockSize;
|
||||
#endif
|
||||
static constexpr size_t BlockSizes[BlockType_Count] = {
|
||||
[BlockType_L3Block] = L3BlockSize,
|
||||
[BlockType_L3ContiguousBlock] = L3ContiguousBlockSize,
|
||||
[BlockType_L2Block] = L2BlockSize,
|
||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_SWITCH
|
||||
[BlockType_L2TegraSmmuBlock] = L2TegraSmmuBlockSize,
|
||||
#endif
|
||||
[BlockType_L2ContiguousBlock] = L2ContiguousBlockSize,
|
||||
[BlockType_L1Block] = L1BlockSize,
|
||||
};
|
||||
|
||||
static constexpr size_t GetBlockSize(BlockType type) {
|
||||
return BlockSizes[type];
|
||||
}
|
||||
|
||||
static constexpr BlockType GetBlockType(size_t size) {
|
||||
switch (size) {
|
||||
case L3BlockSize: return BlockType_L3Block;
|
||||
case L3ContiguousBlockSize: return BlockType_L3ContiguousBlock;
|
||||
case L2BlockSize: return BlockType_L2Block;
|
||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_SWITCH
|
||||
case L2TegraSmmuBlockSize: return BlockType_L2TegraSmmuBlock;
|
||||
#endif
|
||||
case L2ContiguousBlockSize: return BlockType_L2ContiguousBlock;
|
||||
case L1BlockSize: return BlockType_L1Block;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties, OperationType operation, bool reuse_ll) override;
|
||||
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup *page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) override;
|
||||
virtual void FinalizeUpdate(PageLinkedList *page_list) override;
|
||||
|
||||
KPageTableManager &GetPageTableManager() { return *this->manager; }
|
||||
const KPageTableManager &GetPageTableManager() const { return *this->manager; }
|
||||
private:
|
||||
constexpr PageTableEntry GetEntryTemplate(const KPageProperties properties) const {
|
||||
/* Set basic attributes. */
|
||||
PageTableEntry entry;
|
||||
entry.SetPrivilegedExecuteNever(true);
|
||||
entry.SetAccessFlag(PageTableEntry::AccessFlag_Accessed);
|
||||
entry.SetShareable(PageTableEntry::Shareable_InnerShareable);
|
||||
|
||||
if (!this->IsKernel()) {
|
||||
entry.SetGlobal(false);
|
||||
}
|
||||
|
||||
/* Set page attribute. */
|
||||
if (properties.io) {
|
||||
MESOSPHERE_ABORT_UNLESS(!properties.io);
|
||||
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
|
||||
|
||||
entry.SetPageAttribute(PageTableEntry::PageAttribute_Device_nGnRnE)
|
||||
.SetUserExecuteNever(true);
|
||||
} else if (properties.uncached) {
|
||||
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
|
||||
|
||||
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemoryNotCacheable);
|
||||
} else {
|
||||
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemory);
|
||||
}
|
||||
|
||||
/* Set user execute never bit. */
|
||||
if (properties.perm != KMemoryPermission_UserReadExecute) {
|
||||
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
|
||||
entry.SetUserExecuteNever(true);
|
||||
}
|
||||
|
||||
/* Set can be contiguous. */
|
||||
entry.SetContiguousAllowed(!properties.non_contiguous);
|
||||
|
||||
/* Set AP[1] based on perm. */
|
||||
switch (properties.perm & KMemoryPermission_UserReadWrite) {
|
||||
case KMemoryPermission_UserReadWrite:
|
||||
case KMemoryPermission_UserRead:
|
||||
entry.SetUserAccessible(true);
|
||||
break;
|
||||
case KMemoryPermission_KernelReadWrite:
|
||||
case KMemoryPermission_KernelRead:
|
||||
entry.SetUserAccessible(false);
|
||||
break;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Set AP[2] based on perm. */
|
||||
switch (properties.perm & KMemoryPermission_UserReadWrite) {
|
||||
case KMemoryPermission_UserReadWrite:
|
||||
case KMemoryPermission_KernelReadWrite:
|
||||
entry.SetReadOnly(false);
|
||||
break;
|
||||
case KMemoryPermission_KernelRead:
|
||||
case KMemoryPermission_UserRead:
|
||||
entry.SetReadOnly(true);
|
||||
break;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
public:
|
||||
constexpr KPageTable() : KPageTableBase(), manager(), ttbr(), asid() { /* ... */ }
|
||||
|
||||
|
@ -41,6 +162,63 @@ namespace ams::kern::arm64 {
|
|||
|
||||
NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end);
|
||||
Result Finalize();
|
||||
private:
|
||||
Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
|
||||
Result Unmap(KProcessAddress virt_addr, size_t num_pages, KPageGroup *pg, PageLinkedList *page_list, bool force, bool reuse_ll);
|
||||
|
||||
Result MapContiguous(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
|
||||
|
||||
bool MergePages(KProcessAddress virt_addr, PageLinkedList *page_list);
|
||||
|
||||
static void PteDataSynchronizationBarrier() {
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
|
||||
static void ClearPageTable(KVirtualAddress table) {
|
||||
cpu::ClearPageToZero(GetVoidPointer(table));
|
||||
}
|
||||
|
||||
void OnTableUpdated() const {
|
||||
cpu::InvalidateTlbByAsid(this->asid);
|
||||
}
|
||||
|
||||
void OnKernelTableUpdated() const {
|
||||
cpu::InvalidateEntireTlbDataOnly();
|
||||
}
|
||||
|
||||
void NoteUpdated() const {
|
||||
cpu::DataSynchronizationBarrier();
|
||||
|
||||
if (this->IsKernel()) {
|
||||
this->OnKernelTableUpdated();
|
||||
} else {
|
||||
this->OnTableUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
KVirtualAddress AllocatePageTable(PageLinkedList *page_list, bool reuse_ll) {
|
||||
KVirtualAddress table = this->GetPageTableManager().Allocate();
|
||||
|
||||
if (table == Null<KVirtualAddress>) {
|
||||
if (reuse_ll && page_list->Peek()) {
|
||||
table = KVirtualAddress(reinterpret_cast<uintptr_t>(page_list->Pop()));
|
||||
} else {
|
||||
return Null<KVirtualAddress>;
|
||||
}
|
||||
}
|
||||
|
||||
ClearPageTable(table);
|
||||
|
||||
MESOSPHERE_ASSERT(this->GetPageTableManager().GetRefCount(table) == 0);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void FreePageTable(PageLinkedList *page_list, KVirtualAddress table) const {
|
||||
MESOSPHERE_ASSERT(this->GetPageTableManager().IsInPageTableHeap(table));
|
||||
MESOSPHERE_ASSERT(this->GetPageTableManager().GetRefCount(table) == 0);
|
||||
page_list->Push(table);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* 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 <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_select_cpu.hpp>
|
||||
#include <mesosphere/kern_k_typed_address.hpp>
|
||||
|
||||
namespace ams::kern::arm64 {
|
||||
|
||||
constexpr size_t L1BlockSize = 1_GB;
|
||||
constexpr size_t L2BlockSize = 2_MB;
|
||||
constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize;
|
||||
constexpr size_t L3BlockSize = PageSize;
|
||||
constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize;
|
||||
|
||||
class PageTableEntry {
|
||||
public:
|
||||
struct InvalidTag{};
|
||||
|
||||
enum Permission : u64 {
|
||||
Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)),
|
||||
Permission_KernelRX = ((0ul << 53) | (1ul << 54) | (2ul << 6)),
|
||||
Permission_KernelR = ((1ul << 53) | (1ul << 54) | (2ul << 6)),
|
||||
Permission_KernelRW = ((1ul << 53) | (1ul << 54) | (0ul << 6)),
|
||||
|
||||
Permission_UserRX = ((1ul << 53) | (0ul << 54) | (3ul << 6)),
|
||||
Permission_UserR = ((1ul << 53) | (1ul << 54) | (3ul << 6)),
|
||||
Permission_UserRW = ((1ul << 53) | (1ul << 54) | (1ul << 6)),
|
||||
};
|
||||
|
||||
enum Shareable : u64 {
|
||||
Shareable_NonShareable = (0 << 8),
|
||||
Shareable_OuterShareable = (2 << 8),
|
||||
Shareable_InnerShareable = (3 << 8),
|
||||
};
|
||||
|
||||
/* Official attributes are: */
|
||||
/* 0x00, 0x04, 0xFF, 0x44. 4-7 are unused. */
|
||||
enum PageAttribute : u64 {
|
||||
PageAttribute_Device_nGnRnE = (0 << 2),
|
||||
PageAttribute_Device_nGnRE = (1 << 2),
|
||||
PageAttribute_NormalMemory = (2 << 2),
|
||||
PageAttribute_NormalMemoryNotCacheable = (3 << 2),
|
||||
};
|
||||
|
||||
enum AccessFlag : u64 {
|
||||
AccessFlag_NotAccessed = (0 << 10),
|
||||
AccessFlag_Accessed = (1 << 10),
|
||||
};
|
||||
|
||||
enum Type : u64 {
|
||||
Type_None = 0x0,
|
||||
Type_L1Block = 0x1,
|
||||
Type_L1Table = 0x3,
|
||||
Type_L2Block = 0x1,
|
||||
Type_L2Table = 0x3,
|
||||
Type_L3Block = 0x3,
|
||||
};
|
||||
|
||||
enum ContigType : u64 {
|
||||
ContigType_NotContiguous = (0x0ul << 52),
|
||||
ContigType_Contiguous = (0x1ul << 52),
|
||||
};
|
||||
protected:
|
||||
u64 attributes;
|
||||
public:
|
||||
/* Take in a raw attribute. */
|
||||
constexpr ALWAYS_INLINE PageTableEntry() : attributes() { /* ... */ }
|
||||
constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE PageTableEntry(InvalidTag) : attributes(0) { /* ... */ }
|
||||
|
||||
/* Extend a previous attribute. */
|
||||
constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
|
||||
|
||||
/* Construct a new attribute. */
|
||||
constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share)
|
||||
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share))
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
protected:
|
||||
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
|
||||
return (this->attributes >> offset) & ((1ul << count) - 1);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u64 SelectBits(size_t offset, size_t count) const {
|
||||
return this->attributes & (((1ul << count) - 1) << offset);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetBits(size_t offset, size_t count, u64 value) {
|
||||
const u64 mask = ((1ul << count) - 1) << offset;
|
||||
this->attributes &= ~mask;
|
||||
this->attributes |= (value & (mask >> offset)) << offset;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetBitsDirect(size_t offset, size_t count, u64 value) {
|
||||
const u64 mask = ((1ul << count) - 1) << offset;
|
||||
this->attributes &= ~mask;
|
||||
this->attributes |= (value & mask);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetBit(size_t offset, bool enabled) {
|
||||
const u64 mask = 1ul << offset;
|
||||
if (enabled) {
|
||||
this->attributes |= mask;
|
||||
} else {
|
||||
this->attributes &= ~mask;
|
||||
}
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE bool IsContiguousAllowed() const { return this->GetBits(55, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsGlobal() const { return this->GetBits(11, 1) == 0; }
|
||||
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->GetBits(10, 1)); }
|
||||
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
|
||||
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->GetBits(2, 3)); }
|
||||
constexpr ALWAYS_INLINE bool IsReadOnly() const { return this->GetBits(7, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsUserAccessible() const { return this->GetBits(6, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x1; }
|
||||
constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; }
|
||||
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetContiguousAllowed(bool en) { this->SetBit(55, !en); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetUserExecuteNever(bool en) { this->SetBit(54, en); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetPrivilegedExecuteNever(bool en) { this->SetBit(53, en); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetContiguous(bool en) { this->SetBit(52, en); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetGlobal(bool en) { this->SetBit(11, !en); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetAccessFlag(AccessFlag f) { this->SetBitsDirect(10, 1, f); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetShareable(Shareable s) { this->SetBitsDirect(8, 2, s); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetReadOnly(bool en) { this->SetBit(7, en); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetUserAccessible(bool en) { this->SetBit(7, en); return *this; }
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetPageAttribute(PageAttribute a) { this->SetBitsDirect(2, 3, a); return *this; }
|
||||
|
||||
constexpr ALWAYS_INLINE u64 GetEntryTemplate() const {
|
||||
constexpr u64 Mask = (0xFFF0000000000FFFul & ~u64(0x3ul | (0x1ul << 52)));
|
||||
return this->attributes & Mask;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool Is(u64 attr) const {
|
||||
return this->attributes == attr;
|
||||
}
|
||||
|
||||
protected:
|
||||
constexpr ALWAYS_INLINE u64 GetRawAttributes() const {
|
||||
return this->attributes;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(PageTableEntry) == sizeof(u64));
|
||||
|
||||
constexpr inline PageTableEntry InvalidPageTableEntry = PageTableEntry(PageTableEntry::InvalidTag{});
|
||||
|
||||
constexpr inline size_t MaxPageTableEntries = PageSize / sizeof(PageTableEntry);
|
||||
|
||||
class L1PageTableEntry : public PageTableEntry {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
|
||||
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
|
||||
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
|
||||
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
|
||||
return this->SelectBits(30, 18);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
|
||||
return this->SelectBits(12, 36);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool GetTable(KPhysicalAddress &out) const {
|
||||
if (this->IsTable()) {
|
||||
out = this->GetTable();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
|
||||
/* Check whether this has the same permission/etc as the desired attributes. */
|
||||
return L1PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
class L2PageTableEntry : public PageTableEntry {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE L2PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
|
||||
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
|
||||
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
|
||||
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
|
||||
return this->SelectBits(21, 27);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
|
||||
return this->SelectBits(12, 36);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool GetTable(KPhysicalAddress &out) const {
|
||||
if (this->IsTable()) {
|
||||
out = this->GetTable();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
|
||||
/* Check whether this has the same permission/etc as the desired attributes. */
|
||||
return L2PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
class L3PageTableEntry : public PageTableEntry {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE L3PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
|
||||
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x3)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x3; }
|
||||
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
|
||||
return this->SelectBits(12, 36);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
|
||||
/* Check whether this has the same permission/etc as the desired attributes. */
|
||||
return L3PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline L1PageTableEntry InvalidL1PageTableEntry = L1PageTableEntry(PageTableEntry::InvalidTag{});
|
||||
constexpr inline L2PageTableEntry InvalidL2PageTableEntry = L2PageTableEntry(PageTableEntry::InvalidTag{});
|
||||
constexpr inline L3PageTableEntry InvalidL3PageTableEntry = L3PageTableEntry(PageTableEntry::InvalidTag{});
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
#include <mesosphere/kern_select_cpu.hpp>
|
||||
#include <mesosphere/kern_k_typed_address.hpp>
|
||||
#include <mesosphere/kern_k_memory_layout.hpp>
|
||||
#include <mesosphere/arch/arm64/kern_k_page_table_entry.hpp>
|
||||
|
||||
namespace ams::kern::arm64 {
|
||||
|
||||
|
@ -28,15 +29,48 @@ namespace ams::kern::arm64 {
|
|||
NON_COPYABLE(KPageTableImpl);
|
||||
NON_MOVEABLE(KPageTableImpl);
|
||||
private:
|
||||
u64 *table;
|
||||
static constexpr size_t PageBits = __builtin_ctzll(PageSize);
|
||||
static constexpr size_t NumLevels = 3;
|
||||
static constexpr size_t LevelBits = 9;
|
||||
static_assert(NumLevels > 0);
|
||||
|
||||
static constexpr size_t AddressBits = (NumLevels - 1) * LevelBits + PageBits;
|
||||
static_assert(AddressBits <= BITSIZEOF(u64));
|
||||
static constexpr size_t AddressSpaceSize = (1ull << AddressBits);
|
||||
private:
|
||||
L1PageTableEntry *table;
|
||||
bool is_kernel;
|
||||
u32 num_entries;
|
||||
public:
|
||||
ALWAYS_INLINE KVirtualAddress GetTableEntry(KVirtualAddress table, size_t index) {
|
||||
return table + index * sizeof(PageTableEntry);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KProcessAddress address) {
|
||||
return GetPointer<L1PageTableEntry>(GetTableEntry(KVirtualAddress(this->table), (GetInteger(address) >> (PageBits + LevelBits * 2)) & (this->num_entries - 1)));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE L2PageTableEntry *GetL2EntryFromTable(KVirtualAddress table, KProcessAddress address) {
|
||||
return GetPointer<L2PageTableEntry>(GetTableEntry(table, (GetInteger(address) >> (PageBits + LevelBits * 1)) & ((1ul << LevelBits) - 1)));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KProcessAddress address) {
|
||||
return GetL2EntryFromTable(KMemoryLayout::GetLinearVirtualAddress(entry->GetTable()), address);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE L3PageTableEntry *GetL3EntryFromTable(KVirtualAddress table, KProcessAddress address) {
|
||||
return GetPointer<L3PageTableEntry>(GetTableEntry(table, (GetInteger(address) >> (PageBits + LevelBits * 0)) & ((1ul << LevelBits) - 1)));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KProcessAddress address) {
|
||||
return GetL3EntryFromTable(KMemoryLayout::GetLinearVirtualAddress(entry->GetTable()), address);
|
||||
}
|
||||
public:
|
||||
constexpr KPageTableImpl() : table(), is_kernel(), num_entries() { /* ... */ }
|
||||
|
||||
NOINLINE void InitializeForKernel(void *tb, KVirtualAddress start, KVirtualAddress end);
|
||||
|
||||
u64 *Finalize();
|
||||
L1PageTableEntry *Finalize();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ namespace ams::kern::arm64 {
|
|||
NOINLINE void Initialize(s32 core_id);
|
||||
NOINLINE void Activate();
|
||||
void Finalize(s32 core_id);
|
||||
|
||||
Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||
return this->page_table.MapPages(out_addr, num_pages, alignment, phys_addr, region_start, region_num_pages, state, perm);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue