mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 04:41:12 +00:00
kern: implement KCodeMemory (and SVCs)
This commit is contained in:
parent
3fb3beeaff
commit
39b22cee8c
6 changed files with 152 additions and 10 deletions
|
@ -156,8 +156,8 @@ namespace ams::kern::arch::arm64 {
|
||||||
return this->page_table.UnlockForTransferMemory(address, size, pg);
|
return this->page_table.UnlockForTransferMemory(address, size, pg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm) {
|
Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size) {
|
||||||
return this->page_table.LockForCodeMemory(out, address, size, perm);
|
return this->page_table.LockForCodeMemory(out, address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg) {
|
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg) {
|
||||||
|
|
|
@ -22,8 +22,35 @@ namespace ams::kern {
|
||||||
|
|
||||||
class KCodeMemory final : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
|
class KCodeMemory final : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
|
||||||
MESOSPHERE_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
|
MESOSPHERE_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
|
||||||
|
private:
|
||||||
|
TYPED_STORAGE(KPageGroup) page_group;
|
||||||
|
KProcess *owner;
|
||||||
|
KProcessAddress address;
|
||||||
|
KLightLock lock;
|
||||||
|
bool is_initialized;
|
||||||
|
bool is_owner_mapped;
|
||||||
|
bool is_mapped;
|
||||||
public:
|
public:
|
||||||
/* TODO: This is a placeholder definition. */
|
explicit KCodeMemory() : owner(nullptr), address(Null<KProcessAddress>), is_initialized(false), is_owner_mapped(false), is_mapped(false) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~KCodeMemory() { /* ... */ }
|
||||||
|
|
||||||
|
Result Initialize(KProcessAddress address, size_t size);
|
||||||
|
virtual void Finalize() override;
|
||||||
|
|
||||||
|
Result Map(KProcessAddress address, size_t size);
|
||||||
|
Result Unmap(KProcessAddress address, size_t size);
|
||||||
|
Result MapToOwner(KProcessAddress address, size_t size, ams::svc::MemoryPermission perm);
|
||||||
|
Result UnmapFromOwner(KProcessAddress address, size_t size);
|
||||||
|
|
||||||
|
virtual bool IsInitialized() const override { return this->is_initialized; }
|
||||||
|
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||||
|
|
||||||
|
KProcess *GetOwner() const { return this->owner; }
|
||||||
|
KProcessAddress GetSourceAddress() { return this->address; }
|
||||||
|
size_t GetSize() const { return this->is_initialized ? GetReference(this->page_group).GetNumPages() * PageSize : 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -331,7 +331,7 @@ namespace ams::kern {
|
||||||
|
|
||||||
Result LockForTransferMemory(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm);
|
Result LockForTransferMemory(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm);
|
||||||
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup &pg);
|
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup &pg);
|
||||||
Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm);
|
Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size);
|
||||||
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg);
|
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg);
|
||||||
|
|
||||||
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
|
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
|
||||||
|
|
|
@ -1981,7 +1981,7 @@ namespace ams::kern {
|
||||||
KMemoryAttribute_AnyLocked | KMemoryAttribute_Locked, std::addressof(pg));
|
KMemoryAttribute_AnyLocked | KMemoryAttribute_Locked, std::addressof(pg));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KPageTableBase::LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm) {
|
Result KPageTableBase::LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size) {
|
||||||
return this->LockMemoryAndOpen(out, nullptr, address, size,
|
return this->LockMemoryAndOpen(out, nullptr, address, size,
|
||||||
KMemoryState_FlagCanCodeMemory, KMemoryState_FlagCanCodeMemory,
|
KMemoryState_FlagCanCodeMemory, KMemoryState_FlagCanCodeMemory,
|
||||||
KMemoryPermission_All, KMemoryPermission_UserReadWrite,
|
KMemoryPermission_All, KMemoryPermission_UserReadWrite,
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace ams::kern {
|
||||||
void KTransferMemory::Finalize() {
|
void KTransferMemory::Finalize() {
|
||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
/* Unmap. */
|
/* Unlock. */
|
||||||
if (!this->is_mapped) {
|
if (!this->is_mapped) {
|
||||||
const size_t size = GetReference(this->page_group).GetNumPages() * PageSize;
|
const size_t size = GetReference(this->page_group).GetNumPages() * PageSize;
|
||||||
MESOSPHERE_R_ABORT_UNLESS(this->owner->GetPageTable().UnlockForTransferMemory(this->address, size, GetReference(this->page_group)));
|
MESOSPHERE_R_ABORT_UNLESS(this->owner->GetPageTable().UnlockForTransferMemory(this->address, size, GetReference(this->page_group)));
|
||||||
|
|
|
@ -21,28 +21,143 @@ namespace ams::kern::svc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr bool IsValidMapCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||||
|
return perm == ams::svc::MemoryPermission_ReadWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsValidMapToOwnerCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||||
|
return perm == ams::svc::MemoryPermission_Read || perm == ams::svc::MemoryPermission_ReadExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsValidUnmapCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||||
|
return perm == ams::svc::MemoryPermission_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||||
|
return perm == ams::svc::MemoryPermission_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateCodeMemory(ams::svc::Handle *out, uintptr_t address, size_t size) {
|
||||||
|
/* Validate address / size. */
|
||||||
|
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||||
|
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||||
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||||
|
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Create the code memory. */
|
||||||
|
KCodeMemory *code_mem = KCodeMemory::Create();
|
||||||
|
R_UNLESS(code_mem != nullptr, svc::ResultOutOfResource());
|
||||||
|
ON_SCOPE_EXIT { code_mem->Close(); };
|
||||||
|
|
||||||
|
/* Verify that the region is in range. */
|
||||||
|
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Initialize the code memory. */
|
||||||
|
R_TRY(code_mem->Initialize(address, size));
|
||||||
|
|
||||||
|
/* Register the code memory. */
|
||||||
|
R_TRY(KCodeMemory::Register(code_mem));
|
||||||
|
|
||||||
|
/* Add the code memory to the handle table. */
|
||||||
|
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, code_mem));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ControlCodeMemory(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||||
|
/* Validate the address / size. */
|
||||||
|
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||||
|
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||||
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||||
|
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||||
|
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
||||||
|
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Get the code memory from its handle. */
|
||||||
|
KScopedAutoObject code_mem = GetCurrentProcess().GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
|
||||||
|
R_UNLESS(code_mem.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
||||||
|
/* NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. */
|
||||||
|
/* This enables homebrew usage of these SVCs for JIT. */
|
||||||
|
/* R_UNLESS(code_mem->GetOwner() != GetCurrentProcessPointer(), svc::ResultInvalidHandle()); */
|
||||||
|
|
||||||
|
/* Perform the operation. */
|
||||||
|
switch (operation) {
|
||||||
|
case ams::svc::CodeMemoryOperation_Map:
|
||||||
|
{
|
||||||
|
/* Check that the region is in range. */
|
||||||
|
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_CodeOut), svc::ResultInvalidMemoryRegion());
|
||||||
|
|
||||||
|
/* Check the memory permission. */
|
||||||
|
R_UNLESS(IsValidMapCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||||
|
|
||||||
|
/* Map the memory. */
|
||||||
|
R_TRY(code_mem->Map(address, size));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ams::svc::CodeMemoryOperation_Unmap:
|
||||||
|
{
|
||||||
|
/* Check that the region is in range. */
|
||||||
|
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_CodeOut), svc::ResultInvalidMemoryRegion());
|
||||||
|
|
||||||
|
/* Check the memory permission. */
|
||||||
|
R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||||
|
|
||||||
|
/* Unmap the memory. */
|
||||||
|
R_TRY(code_mem->Unmap(address, size));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ams::svc::CodeMemoryOperation_MapToOwner:
|
||||||
|
{
|
||||||
|
/* Check that the region is in range. */
|
||||||
|
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion());
|
||||||
|
|
||||||
|
/* Check the memory permission. */
|
||||||
|
R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||||
|
|
||||||
|
/* Map the memory to its owner. */
|
||||||
|
R_TRY(code_mem->MapToOwner(address, size, perm));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ams::svc::CodeMemoryOperation_UnmapFromOwner:
|
||||||
|
{
|
||||||
|
/* Check that the region is in range. */
|
||||||
|
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion());
|
||||||
|
|
||||||
|
/* Check the memory permission. */
|
||||||
|
R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||||
|
|
||||||
|
/* Unmap the memory from its owner. */
|
||||||
|
R_TRY(code_mem->UnmapFromOwner(address, size));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return svc::ResultInvalidEnumValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================= 64 ABI ============================= */
|
/* ============================= 64 ABI ============================= */
|
||||||
|
|
||||||
Result CreateCodeMemory64(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size) {
|
Result CreateCodeMemory64(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcCreateCodeMemory64 was called.");
|
return CreateCodeMemory(out_handle, address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ControlCodeMemory64(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
Result ControlCodeMemory64(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcControlCodeMemory64 was called.");
|
return ControlCodeMemory(code_memory_handle, operation, address, size, perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================= 64From32 ABI ============================= */
|
/* ============================= 64From32 ABI ============================= */
|
||||||
|
|
||||||
Result CreateCodeMemory64From32(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size) {
|
Result CreateCodeMemory64From32(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcCreateCodeMemory64From32 was called.");
|
return CreateCodeMemory(out_handle, address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ControlCodeMemory64From32(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
Result ControlCodeMemory64From32(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcControlCodeMemory64From32 was called.");
|
return ControlCodeMemory(code_memory_handle, operation, address, size, perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue