kern: add infra (but not impl) for all DeviceAddressSpace svcs

This commit is contained in:
Michael Scire 2020-07-14 18:46:25 -07:00 committed by SciresM
parent 863515a3b5
commit 0d3aa13f70
11 changed files with 303 additions and 13 deletions

View file

@ -104,6 +104,18 @@ namespace ams::kern::arch::arm64 {
return this->page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr);
}
Result MakeAndOpenPageGroupContiguous(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) {
return this->page_table.MakeAndOpenPageGroupContiguous(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr);
}
Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) {
return this->page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned);
}
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
return this->page_table.UnlockForDeviceAddressSpace(address, size);
}
Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) {
return this->page_table.LockForIpcUserBuffer(out, address, size);
}

View file

@ -58,6 +58,9 @@ namespace ams::kern::board::nintendo::nx {
Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size);
Result Detach(ams::svc::DeviceName device_name);
Result Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings);
Result Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address);
public:
static void Initialize();
};

View file

@ -389,6 +389,30 @@ namespace ams::kern {
this->num_pages -= block->num_pages;
}
constexpr void ShareToDevice(KMemoryPermission new_perm) {
/* We must either be shared or have a zero lock count. */
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared || this->device_use_count == 0);
/* Share. */
const u16 new_count = ++this->device_use_count;
MESOSPHERE_ABORT_UNLESS(new_count > 0);
this->attribute = static_cast<KMemoryAttribute>(this->attribute | KMemoryAttribute_DeviceShared);
}
constexpr void UnshareToDevice(KMemoryPermission new_perm) {
/* We must be shared. */
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared);
/* Unhare. */
const u16 old_count = this->device_use_count--;
MESOSPHERE_ABORT_UNLESS(old_count > 0);
if (old_count == 1) {
this->attribute = static_cast<KMemoryAttribute>(this->attribute & ~KMemoryAttribute_DeviceShared);
}
}
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);

View file

@ -300,7 +300,10 @@ namespace ams::kern {
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state);
Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr);
Result MakeAndOpenPageGroupContiguous(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr);
Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned);
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size);
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);

View file

@ -37,7 +37,7 @@ namespace ams::kern::svc {
/* 105 */ using ::ams::svc::ResultOutOfHandles;
/* 106 */ using ::ams::svc::ResultInvalidCurrentMemory;
/* 108 */ using ::ams::svc::ResultInvalidNewMemoryPermissions;
/* 108 */ using ::ams::svc::ResultInvalidNewMemoryPermission;
/* 110 */ using ::ams::svc::ResultInvalidMemoryRegion;

View file

@ -583,4 +583,12 @@ namespace ams::kern::board::nintendo::nx {
MESOSPHERE_UNIMPLEMENTED();
}
Result KDevicePageTable::Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
MESOSPHERE_UNIMPLEMENTED();
}
Result KDevicePageTable::Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) {
MESOSPHERE_UNIMPLEMENTED();
}
}

View file

@ -57,15 +57,71 @@ namespace ams::kern {
}
Result KDeviceAddressSpace::Detach(ams::svc::DeviceName device_name) {
MESOSPHERE_UNIMPLEMENTED();
/* Lock the address space. */
KScopedLightLock lk(this->lock);
/* Detach. */
return this->table.Detach(device_name);
}
Result KDeviceAddressSpace::Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool refresh_mappings) {
MESOSPHERE_UNIMPLEMENTED();
/* Check that the address falls within the space. */
R_UNLESS((this->space_address <= device_address && device_address + size - 1 <= this->space_address + this->space_size - 1), svc::ResultInvalidCurrentMemory());
/* Lock the address space. */
KScopedLightLock lk(this->lock);
/* Lock the pages. */
KPageGroup pg(page_table->GetBlockInfoManager());
R_TRY(page_table->LockForDeviceAddressSpace(std::addressof(pg), process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned));
/* Ensure that if we fail, we don't keep unmapped pages locked. */
ON_SCOPE_EXIT {
if (*out_mapped_size != size) {
page_table->UnlockForDeviceAddressSpace(process_address + *out_mapped_size, size - *out_mapped_size);
};
};
/* Map the pages. */
{
/* Clear the output size to zero on failure. */
auto map_guard = SCOPE_GUARD { *out_mapped_size = 0; };
/* Perform the mapping. */
R_TRY(this->table.Map(out_mapped_size, pg, device_address, device_perm, refresh_mappings));
/* We succeeded, so cancel our guard. */
map_guard.Cancel();
}
return ResultSuccess();
}
Result KDeviceAddressSpace::Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address) {
MESOSPHERE_UNIMPLEMENTED();
/* Check that the address falls within the space. */
R_UNLESS((this->space_address <= device_address && device_address + size - 1 <= this->space_address + this->space_size - 1), svc::ResultInvalidCurrentMemory());
/* Lock the address space. */
KScopedLightLock lk(this->lock);
/* Make and open a page group for the unmapped region. */
KPageGroup pg(page_table->GetBlockInfoManager());
R_TRY(page_table->MakeAndOpenPageGroupContiguous(std::addressof(pg), process_address, size / PageSize,
KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap,
KMemoryPermission_None, KMemoryPermission_None,
KMemoryAttribute_AnyLocked | KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared));
/* Ensure the page group is closed on scope exit. */
ON_SCOPE_EXIT { pg.Close(); };
/* Unmap. */
R_TRY(this->table.Unmap(pg, device_address));
/* Unlock the pages. */
R_TRY(page_table->UnlockForDeviceAddressSpace(process_address, size));
return ResultSuccess();
}
}

View file

@ -1370,6 +1370,85 @@ namespace ams::kern {
return ResultSuccess();
}
Result KPageTableBase::MakeAndOpenPageGroupContiguous(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) {
/* Ensure that the page group isn't null. */
MESOSPHERE_ASSERT(out != nullptr);
/* Make sure that the region we're mapping is valid for the table. */
const size_t size = num_pages * PageSize;
R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory());
/* Lock the table. */
KScopedLightLock lk(this->general_lock);
/* Check if state allows us to create the group. */
R_TRY(this->CheckMemoryStateContiguous(address, size, state_mask | KMemoryState_FlagReferenceCounted, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr));
/* Create a new page group for the region. */
R_TRY(this->MakePageGroup(*out, address, num_pages));
/* Open a new reference to the pages in the group. */
out->Open();
return ResultSuccess();
}
Result KPageTableBase::LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) {
/* Lightly validate the range before doing anything else. */
const size_t num_pages = size / PageSize;
R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory());
/* Lock the table. */
KScopedLightLock lk(this->general_lock);
/* Check the memory state. */
const u32 test_state = (is_aligned ? KMemoryState_FlagCanAlignedDeviceMap : KMemoryState_FlagCanDeviceMap);
R_TRY(this->CheckMemoryState(address, size, test_state, test_state, perm, perm, KMemoryAttribute_AnyLocked | KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, 0, KMemoryAttribute_DeviceShared));
/* Make the page group, if we should. */
if (out != nullptr) {
R_TRY(this->MakePageGroup(*out, address, num_pages));
}
/* Create an update allocator. */
KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager);
R_TRY(allocator.GetResult());
/* Update the memory blocks. */
this->memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::ShareToDevice, KMemoryPermission_None);
/* Open the page group. */
if (out != nullptr) {
out->Open();
}
return ResultSuccess();
}
Result KPageTableBase::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
/* Lightly validate the range before doing anything else. */
const size_t num_pages = size / PageSize;
R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory());
/* Lock the table. */
KScopedLightLock lk(this->general_lock);
/* Check the memory state. */
R_TRY(this->CheckMemoryStateContiguous(address, size,
KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap,
KMemoryPermission_None, KMemoryPermission_None,
KMemoryAttribute_AnyLocked | KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared));
/* Create an update allocator. */
KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager);
R_TRY(allocator.GetResult());
/* Update the memory blocks. */
this->memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::UnshareToDevice, KMemoryPermission_None);
return ResultSuccess();
}
Result KPageTableBase::LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) {
return this->LockMemoryAndOpen(nullptr, out, address, size,
KMemoryState_FlagCanIpcUserBuffer, KMemoryState_FlagCanIpcUserBuffer,

View file

@ -21,6 +21,12 @@ namespace ams::kern::svc {
namespace {
constexpr inline u64 DeviceAddressSpaceAlignMask = (1ul << 22) - 1;
constexpr bool IsProcessAndDeviceAligned(uint64_t process_address, uint64_t device_address) {
return (process_address & DeviceAddressSpaceAlignMask) == (device_address & DeviceAddressSpaceAlignMask);
}
Result CreateDeviceAddressSpace(ams::svc::Handle *out, uint64_t das_address, uint64_t das_size) {
/* Validate input. */
R_UNLESS(util::IsAligned(das_address, PageSize), svc::ResultInvalidMemoryRegion());
@ -62,6 +68,97 @@ namespace ams::kern::svc {
return das->Detach(device_name);
}
constexpr bool IsValidDeviceMemoryPermission(ams::svc::MemoryPermission device_perm) {
switch (device_perm) {
case ams::svc::MemoryPermission_Read:
case ams::svc::MemoryPermission_Write:
case ams::svc::MemoryPermission_ReadWrite:
return true;
default:
return false;
}
}
Result MapDeviceAddressSpace(size_t *out_mapped_size, ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
/* Validate input. */
R_UNLESS(util::IsAligned(process_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(device_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS((process_address < process_address + size), svc::ResultInvalidCurrentMemory());
R_UNLESS((device_address < device_address + size), svc::ResultInvalidMemoryRegion());
R_UNLESS((process_address == static_cast<uintptr_t>(process_address)), svc::ResultInvalidCurrentMemory());
R_UNLESS(IsValidDeviceMemoryPermission(device_perm), svc::ResultInvalidNewMemoryPermission());
/* Get the device address space. */
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
/* Get the process. */
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
/* Validate that the process address is within range. */
auto &page_table = process->GetPageTable();
R_UNLESS(page_table.Contains(process_address, size), svc::ResultInvalidCurrentMemory());
/* Map. */
return das->Map(out_mapped_size, std::addressof(page_table), KProcessAddress(process_address), size, device_address, device_perm, refresh_mappings);
}
Result MapDeviceAddressSpaceAligned(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
/* Validate input. */
R_UNLESS(util::IsAligned(process_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(device_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(IsProcessAndDeviceAligned(process_address, device_address), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS((process_address < process_address + size), svc::ResultInvalidCurrentMemory());
R_UNLESS((device_address < device_address + size), svc::ResultInvalidMemoryRegion());
R_UNLESS((process_address == static_cast<uintptr_t>(process_address)), svc::ResultInvalidCurrentMemory());
R_UNLESS(IsValidDeviceMemoryPermission(device_perm), svc::ResultInvalidNewMemoryPermission());
/* Get the device address space. */
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
/* Get the process. */
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
/* Validate that the process address is within range. */
auto &page_table = process->GetPageTable();
R_UNLESS(page_table.Contains(process_address, size), svc::ResultInvalidCurrentMemory());
/* Map. */
return das->MapAligned(std::addressof(page_table), KProcessAddress(process_address), size, device_address, device_perm);
}
Result UnmapDeviceAddressSpace(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address) {
/* Validate input. */
R_UNLESS(util::IsAligned(process_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(device_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS((process_address < process_address + size), svc::ResultInvalidCurrentMemory());
R_UNLESS((device_address < device_address + size), svc::ResultInvalidMemoryRegion());
R_UNLESS((process_address == static_cast<uintptr_t>(process_address)), svc::ResultInvalidCurrentMemory());
/* Get the device address space. */
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
/* Get the process. */
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
/* Validate that the process address is within range. */
auto &page_table = process->GetPageTable();
R_UNLESS(page_table.Contains(process_address, size), svc::ResultInvalidCurrentMemory());
return das->Unmap(std::addressof(page_table), KProcessAddress(process_address), size, device_address);
}
}
/* ============================= 64 ABI ============================= */
@ -79,19 +176,21 @@ namespace ams::kern::svc {
}
Result MapDeviceAddressSpaceByForce64(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
MESOSPHERE_PANIC("Stubbed SvcMapDeviceAddressSpaceByForce64 was called.");
size_t dummy_map_size;
return MapDeviceAddressSpace(std::addressof(dummy_map_size), das_handle, process_handle, process_address, size, device_address, device_perm, false);
}
Result MapDeviceAddressSpaceAligned64(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
MESOSPHERE_PANIC("Stubbed SvcMapDeviceAddressSpaceAligned64 was called.");
return MapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, device_perm);
}
Result MapDeviceAddressSpace64(ams::svc::Size *out_mapped_size, ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
MESOSPHERE_PANIC("Stubbed SvcMapDeviceAddressSpace64 was called.");
static_assert(sizeof(*out_mapped_size) == sizeof(size_t));
return MapDeviceAddressSpace(reinterpret_cast<size_t *>(out_mapped_size), das_handle, process_handle, process_address, size, device_address, device_perm, true);
}
Result UnmapDeviceAddressSpace64(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address) {
MESOSPHERE_PANIC("Stubbed SvcUnmapDeviceAddressSpace64 was called.");
return UnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address);
}
/* ============================= 64From32 ABI ============================= */
@ -109,19 +208,21 @@ namespace ams::kern::svc {
}
Result MapDeviceAddressSpaceByForce64From32(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
MESOSPHERE_PANIC("Stubbed SvcMapDeviceAddressSpaceByForce64From32 was called.");
size_t dummy_map_size;
return MapDeviceAddressSpace(std::addressof(dummy_map_size), das_handle, process_handle, process_address, size, device_address, device_perm, false);
}
Result MapDeviceAddressSpaceAligned64From32(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
MESOSPHERE_PANIC("Stubbed SvcMapDeviceAddressSpaceAligned64From32 was called.");
return MapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, device_perm);
}
Result MapDeviceAddressSpace64From32(ams::svc::Size *out_mapped_size, ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
MESOSPHERE_PANIC("Stubbed SvcMapDeviceAddressSpace64From32 was called.");
static_assert(sizeof(*out_mapped_size) == sizeof(size_t));
return MapDeviceAddressSpace(reinterpret_cast<size_t *>(out_mapped_size), das_handle, process_handle, process_address, size, device_address, device_perm, true);
}
Result UnmapDeviceAddressSpace64From32(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address) {
MESOSPHERE_PANIC("Stubbed SvcUnmapDeviceAddressSpace64From32 was called.");
return UnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address);
}
}

View file

@ -22,6 +22,10 @@ namespace ams::kern::svc {
namespace {
Result ReadWriteRegister(uint32_t *out, ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) {
/* Clear the output unconditionally. */
*out = 0;
/* Read/write the register. */
return KSystemControl::ReadWriteRegister(out, address, mask, value);
}

View file

@ -40,7 +40,7 @@ namespace ams::svc {
R_DEFINE_ERROR_RESULT(OutOfHandles, 105);
R_DEFINE_ERROR_RESULT(InvalidCurrentMemory, 106);
R_DEFINE_ERROR_RESULT(InvalidNewMemoryPermissions, 108);
R_DEFINE_ERROR_RESULT(InvalidNewMemoryPermission, 108);
R_DEFINE_ERROR_RESULT(InvalidMemoryRegion, 110);