kern: implement IPC KPageTable functionality

This commit is contained in:
Michael Scire
2020-07-12 15:42:47 -07:00
parent 3c8d8161cc
commit a8d4b10b17
3 changed files with 635 additions and 6 deletions

View File

@@ -155,6 +155,8 @@ namespace ams::kern {
KMemoryPermission_UserReadExecute = KMemoryPermission_UserRead | KMemoryPermission_UserExecute,
KMemoryPermission_UserMask = ams::svc::MemoryPermission_Read | ams::svc::MemoryPermission_Write | ams::svc::MemoryPermission_Execute,
KMemoryPermission_IpcLockChangeMask = KMemoryPermission_NotMapped | KMemoryPermission_UserReadWrite,
};
constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) {
@@ -215,6 +217,22 @@ namespace ams::kern {
constexpr uintptr_t GetLastAddress() const {
return this->GetEndAddress() - 1;
}
constexpr u16 GetIpcLockCount() const {
return this->ipc_lock_count;
}
constexpr KMemoryPermission GetPermission() const {
return this->perm;
}
constexpr KMemoryPermission GetOriginalPermission() const {
return this->original_perm;
}
constexpr KMemoryAttribute GetAttribute() const {
return this->attribute;
}
};
class KMemoryBlock : public util::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> {
@@ -258,6 +276,22 @@ namespace ams::kern {
return this->GetEndAddress() - 1;
}
constexpr u16 GetIpcLockCount() const {
return this->ipc_lock_count;
}
constexpr KMemoryPermission GetPermission() const {
return this->perm;
}
constexpr KMemoryPermission GetOriginalPermission() const {
return this->original_perm;
}
constexpr KMemoryAttribute GetAttribute() const {
return this->attribute;
}
constexpr KMemoryInfo GetMemoryInfo() const {
return {
.address = GetInteger(this->GetAddress()),
@@ -354,6 +388,42 @@ namespace ams::kern {
this->address = addr;
this->num_pages -= block->num_pages;
}
constexpr void LockForIpc(KMemoryPermission new_perm) {
/* We must either be locked or have a zero lock count. */
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked || this->ipc_lock_count == 0);
/* Lock. */
const u16 new_lock_count = ++this->ipc_lock_count;
MESOSPHERE_ABORT_UNLESS(new_lock_count > 0);
/* If this is our first lock, update our permissions. */
if (new_lock_count == 1) {
MESOSPHERE_ASSERT(this->original_perm == KMemoryPermission_None);
MESOSPHERE_ASSERT((this->perm | new_perm) == this->perm);
MESOSPHERE_ASSERT((this->perm & KMemoryPermission_UserExecute) != KMemoryPermission_UserExecute || (new_perm == KMemoryPermission_UserRead));
this->original_perm = this->perm;
this->perm = static_cast<KMemoryPermission>((new_perm & KMemoryPermission_IpcLockChangeMask) | (this->original_perm & ~KMemoryPermission_IpcLockChangeMask));
}
this->attribute = static_cast<KMemoryAttribute>(this->attribute | KMemoryAttribute_IpcLocked);
}
constexpr void UnlockForIpc(KMemoryPermission new_perm) {
/* We must be locked. */
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked);
/* Unlock. */
const u16 old_lock_count = this->ipc_lock_count--;
MESOSPHERE_ABORT_UNLESS(old_lock_count > 0);
/* If this is our last unlock, update our permissions. */
if (old_lock_count == 1) {
MESOSPHERE_ASSERT(this->original_perm != KMemoryPermission_None);
this->perm = this->original_perm;
this->original_perm = KMemoryPermission_None;
this->attribute = static_cast<KMemoryAttribute>(this->attribute & ~KMemoryAttribute_IpcLocked);
}
}
};
static_assert(std::is_trivially_destructible<KMemoryBlock>::value);

View File

@@ -255,6 +255,10 @@ namespace ams::kern {
bool IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages);
NOINLINE Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
Result SetupForIpcClient(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state);
Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send);
Result CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission test_perm);
public:
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const {
return this->GetImpl().GetPhysicalAddress(out, virt_addr);