diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp index 48e5c8e99..1c39d7772 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp @@ -53,6 +53,12 @@ namespace ams::kern::board::nintendo::nx { public: constexpr KDevicePageTable() : tables(), table_asids(), attached_device(), attached_value(), detached_value(), hs_attached_value(), hs_detached_value() { /* ... */ } + Result Initialize(u64 space_address, u64 space_size); + void Finalize(); + + Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size); + Result Detach(ams::svc::DeviceName device_name); + public: static void Initialize(); }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp index 74fe90922..03b302f9a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp @@ -23,10 +23,39 @@ namespace ams::kern { class KDeviceAddressSpace final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject); + private: + KLightLock lock; + KDevicePageTable table; + u64 space_address; + u64 space_size; + bool is_initialized; + public: + constexpr KDeviceAddressSpace() : lock(), table(), space_address(), space_size(), is_initialized() { /* ... */ } + virtual ~KDeviceAddressSpace() { /* ... */ } + + Result Initialize(u64 address, u64 size); + virtual void Finalize() override; + + virtual bool IsInitialized() const override { return this->is_initialized; } + static void PostDestroy(uintptr_t arg) { /* ... */ } + + Result Attach(ams::svc::DeviceName device_name); + Result Detach(ams::svc::DeviceName device_name); + + Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) { + return this->Map(out_mapped_size, page_table, process_address, size, device_address, device_perm, false, refresh_mappings); + } + + Result MapAligned(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm) { + size_t dummy; + return this->Map(std::addressof(dummy), page_table, process_address, size, device_address, device_perm, true, false); + } + + Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address); + private: + Result 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); public: static void Initialize(); - - /* TODO: This is a placeholder definition. */ }; } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index 28eb097d3..a0ad01bb0 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -37,6 +37,8 @@ namespace ams::kern::board::nintendo::nx { constexpr size_t DeviceLargePageSize = (1ul << DevicePageBits); static_assert(DevicePageSize == PageSize); + constexpr size_t DeviceRegionSize = (1ul << 32); + constexpr size_t DeviceAsidRegisterOffsets[] = { [ams::svc::DeviceName_Afi] = MC_SMMU_AFI_ASID, [ams::svc::DeviceName_Avpc] = MC_SMMU_AVPC_ASID, @@ -140,6 +142,57 @@ namespace ams::kern::board::nintendo::nx { return (static_cast(GetInteger(addr)) & ~PhysicalAddressMask) == 0; } + constexpr struct { u64 start; u64 end; } SmmuSupportedRanges[] = { + [ams::svc::DeviceName_Afi] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Avpc] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Dc] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Dcb] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Hc] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Hda] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Isp2] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_MsencNvenc] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Nv] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Nv2] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Ppcs] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Sata] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Vi] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Vic] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_XusbHost] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_XusbDev] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Tsec] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Ppcs1] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Dc1] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Sdmmc1a] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Sdmmc2a] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Sdmmc3a] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Sdmmc4a] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Isp2b] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Gpu] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Gpub] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Ppcs2] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Nvdec] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Ape] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Se] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Nvjpg] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Hc1] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Se1] = { 0x00000000ul, 0x0FFFFFFFFul }, + [ams::svc::DeviceName_Axiap] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Etr] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Tsecb] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Tsec1] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Tsecb1] = { 0x00000000ul, 0x3FFFFFFFFul }, + [ams::svc::DeviceName_Nvdec1] = { 0x00000000ul, 0x0FFFFFFFFul }, + }; + static_assert(util::size(SmmuSupportedRanges) == ams::svc::DeviceName_Count); + + constexpr bool IsAttachable(ams::svc::DeviceName device_name, u64 space_address, u64 space_size) { + if (0 <= device_name && device_name < ams::svc::DeviceName_Count) { + const auto &range = SmmuSupportedRanges[device_name]; + return range.start <= space_address && (space_address + space_size - 1) <= range.end; + } + return false; + } + /* Types. */ class EntryBase { protected: @@ -404,4 +457,130 @@ namespace ams::kern::board::nintendo::nx { /* TODO: Install interrupt handler. */ } + /* Member functions. */ + + Result KDevicePageTable::Initialize(u64 space_address, u64 space_size) { + /* Ensure space is valid. */ + R_UNLESS(((space_address + space_size - 1) & ~DeviceVirtualAddressMask) == 0, svc::ResultInvalidMemoryRegion()); + + /* Determine extents. */ + const size_t start_index = space_address / DeviceRegionSize; + const size_t end_index = (space_address + space_size - 1) / DeviceRegionSize; + + /* Get the page table manager. */ + auto &ptm = Kernel::GetPageTableManager(); + + /* Clear the tables. */ + static_assert(TableCount == (1ul << DeviceVirtualAddressBits) / DeviceRegionSize); + for (size_t i = 0; i < TableCount; ++i) { + this->tables[i] = Null; + } + + /* Ensure that we clean up the tables on failure. */ + auto table_guard = SCOPE_GUARD { + for (size_t i = start_index; i <= end_index; ++i) { + if (this->tables[i] != Null && ptm.Close(this->tables[i], 1)) { + ptm.Free(this->tables[i]); + } + } + }; + + /* Allocate a table for all required indices. */ + for (size_t i = start_index; i <= end_index; ++i) { + const KVirtualAddress table_vaddr = ptm.Allocate(); + R_UNLESS(table_vaddr != Null, svc::ResultOutOfMemory()); + + MESOSPHERE_ASSERT((static_cast(GetInteger(GetPageTablePhysicalAddress(table_vaddr))) & ~PhysicalAddressMask) == 0); + + ptm.Open(table_vaddr, 1); + cpu::StoreDataCache(GetVoidPointer(table_vaddr), PageDirectorySize); + this->tables[i] = table_vaddr; + } + + /* Clear asids. */ + for (size_t i = 0; i < TableCount; ++i) { + this->table_asids[i] = g_reserved_asid; + } + + /* Reserve asids for the tables. */ + R_TRY(g_asid_manager.Reserve(std::addressof(this->table_asids[start_index]), end_index - start_index + 1)); + + /* Associate tables with asids. */ + for (size_t i = start_index; i <= end_index; ++i) { + SetTable(this->table_asids[i], GetPageTablePhysicalAddress(this->tables[i])); + } + + /* Set member variables. */ + this->attached_device = 0; + this->attached_value = (1u << 31) | this->table_asids[0]; + this->detached_value = (1u << 31) | g_reserved_asid; + + this->hs_attached_value = (1u << 31); + this->hs_detached_value = (1u << 31); + for (size_t i = 0; i < TableCount; ++i) { + this->hs_attached_value |= (this->table_asids[i] << (i * BITSIZEOF(u8))); + this->hs_detached_value |= (g_reserved_asid << (i * BITSIZEOF(u8))); + } + + /* We succeeded. */ + table_guard.Cancel(); + return ResultSuccess(); + } + + void KDevicePageTable::Finalize() { + MESOSPHERE_UNIMPLEMENTED(); + } + + Result KDevicePageTable::Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size) { + /* Validate the device name. */ + R_UNLESS(0 <= device_name, svc::ResultNotFound()); + R_UNLESS(device_name < ams::svc::DeviceName_Count, svc::ResultNotFound()); + + /* Check that the device isn't already attached. */ + R_UNLESS((this->attached_device & (1ul << device_name)) == 0, svc::ResultBusy()); + + /* Validate that the space is allowed for the device. */ + const size_t end_index = (space_address + space_size - 1) / DeviceRegionSize; + R_UNLESS(end_index == 0 || IsHsSupported(device_name), svc::ResultInvalidCombination()); + + /* Validate that the device can be attached. */ + R_UNLESS(IsAttachable(device_name, space_address, space_size), svc::ResultInvalidCombination()); + + /* Get the device asid register offset. */ + const int reg_offset = GetDeviceAsidRegisterOffset(device_name); + R_UNLESS(reg_offset >= 0, svc::ResultNotFound()); + + /* Determine the old/new values. */ + const u32 old_val = IsHsSupported(device_name) ? this->hs_detached_value : this->detached_value; + const u32 new_val = IsHsSupported(device_name) ? this->hs_attached_value : this->attached_value; + + /* Attach the device. */ + { + KScopedLightLock lk(g_lock); + + /* Validate that the device is unclaimed. */ + R_UNLESS((ReadMcRegister(reg_offset) | (1u << 31)) == (old_val | (1u << 31)), svc::ResultBusy()); + + /* Claim the device. */ + WriteMcRegister(reg_offset, new_val); + SmmuSynchronizationBarrier(); + + /* Ensure that we claimed it successfully. */ + if (ReadMcRegister(reg_offset) != new_val) { + WriteMcRegister(reg_offset, old_val); + SmmuSynchronizationBarrier(); + return svc::ResultNotFound(); + } + } + + /* Mark the device as attached. */ + this->attached_device |= (1ul << device_name); + + return ResultSuccess(); + } + + Result KDevicePageTable::Detach(ams::svc::DeviceName device_name) { + MESOSPHERE_UNIMPLEMENTED(); + } + } diff --git a/libraries/libmesosphere/source/kern_k_device_address_space.cpp b/libraries/libmesosphere/source/kern_k_device_address_space.cpp index ffe27b765..175ef8319 100644 --- a/libraries/libmesosphere/source/kern_k_device_address_space.cpp +++ b/libraries/libmesosphere/source/kern_k_device_address_space.cpp @@ -17,9 +17,55 @@ namespace ams::kern { + /* Static initializer. */ void KDeviceAddressSpace::Initialize() { /* This just forwards to the device page table manager. */ KDevicePageTable::Initialize(); } + /* Member functions. */ + Result KDeviceAddressSpace::Initialize(u64 address, u64 size) { + MESOSPHERE_ASSERT_THIS(); + + /* Initialize the device page table. */ + R_TRY(this->table.Initialize(address, size)); + + /* Set member variables. */ + this->space_address = address; + this->space_size = size; + this->is_initialized = true; + + return ResultSuccess(); + } + + void KDeviceAddressSpace::Finalize() { + MESOSPHERE_ASSERT_THIS(); + + /* Finalize the table. */ + this->table.Finalize(); + + /* Finalize base. */ + KAutoObjectWithSlabHeapAndContainer::Finalize(); + } + + Result KDeviceAddressSpace::Attach(ams::svc::DeviceName device_name) { + /* Lock the address space. */ + KScopedLightLock lk(this->lock); + + /* Attach. */ + return this->table.Attach(device_name, this->space_address, this->space_size); + } + + Result KDeviceAddressSpace::Detach(ams::svc::DeviceName device_name) { + MESOSPHERE_UNIMPLEMENTED(); + } + + 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(); + } + + Result KDeviceAddressSpace::Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address) { + MESOSPHERE_UNIMPLEMENTED(); + } + } diff --git a/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp b/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp index 60cbebde9..9a8686719 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp @@ -21,22 +21,61 @@ namespace ams::kern::svc { namespace { + 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()); + R_UNLESS(util::IsAligned(das_size, PageSize), svc::ResultInvalidMemoryRegion()); + R_UNLESS(das_size > 0, svc::ResultInvalidMemoryRegion()); + R_UNLESS((das_address < das_address + das_size), svc::ResultInvalidMemoryRegion()); + /* Create the device address space. */ + KScopedAutoObject das = KDeviceAddressSpace::Create(); + R_UNLESS(das.IsNotNull(), svc::ResultOutOfResource()); + + /* Initialize the device address space. */ + R_TRY(das->Initialize(das_address, das_size)); + + /* Register the device address space. */ + R_TRY(KDeviceAddressSpace::Register(das.GetPointerUnsafe())); + + /* Add to the handle table. */ + R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das.GetPointerUnsafe())); + + return ResultSuccess(); + } + + Result AttachDeviceAddressSpace(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) { + /* Get the device address space. */ + KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject(das_handle); + R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle()); + + /* Attach. */ + return das->Attach(device_name); + } + + Result DetachDeviceAddressSpace(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) { + /* Get the device address space. */ + KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject(das_handle); + R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle()); + + /* Detach. */ + return das->Detach(device_name); + } } /* ============================= 64 ABI ============================= */ Result CreateDeviceAddressSpace64(ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) { - MESOSPHERE_PANIC("Stubbed SvcCreateDeviceAddressSpace64 was called."); + return CreateDeviceAddressSpace(out_handle, das_address, das_size); } Result AttachDeviceAddressSpace64(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) { - MESOSPHERE_PANIC("Stubbed SvcAttachDeviceAddressSpace64 was called."); + return AttachDeviceAddressSpace(device_name, das_handle); } Result DetachDeviceAddressSpace64(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) { - MESOSPHERE_PANIC("Stubbed SvcDetachDeviceAddressSpace64 was called."); + return DetachDeviceAddressSpace(device_name, das_handle); } 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) { @@ -58,15 +97,15 @@ namespace ams::kern::svc { /* ============================= 64From32 ABI ============================= */ Result CreateDeviceAddressSpace64From32(ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) { - MESOSPHERE_PANIC("Stubbed SvcCreateDeviceAddressSpace64From32 was called."); + return CreateDeviceAddressSpace(out_handle, das_address, das_size); } Result AttachDeviceAddressSpace64From32(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) { - MESOSPHERE_PANIC("Stubbed SvcAttachDeviceAddressSpace64From32 was called."); + return AttachDeviceAddressSpace(device_name, das_handle); } Result DetachDeviceAddressSpace64From32(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) { - MESOSPHERE_PANIC("Stubbed SvcDetachDeviceAddressSpace64From32 was called."); + return DetachDeviceAddressSpace(device_name, das_handle); } 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) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp b/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp index bc9975019..bcc414017 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp @@ -43,20 +43,17 @@ namespace ams::kern::svc { auto &handle_table = process.GetHandleTable(); /* Create the interrupt event. */ - KInterruptEvent *event = KInterruptEvent::Create(); - R_UNLESS(event != nullptr, svc::ResultOutOfResource()); - - /* Ensure that we cleanup the event on exit, leaving the only reference as the one in the handle table. */ - ON_SCOPE_EXIT { event->Close(); }; + KScopedAutoObject event = KInterruptEvent::Create(); + R_UNLESS(event.IsNotNull(), svc::ResultOutOfResource()); /* Initialize the event. */ R_TRY(event->Initialize(interrupt_id, type)); /* Register the event. */ - R_TRY(KInterruptEvent::Register(event)); + R_TRY(KInterruptEvent::Register(event.GetPointerUnsafe())); /* Add the event to the handle table. */ - R_TRY(handle_table.Add(out, event)); + R_TRY(handle_table.Add(out, event.GetPointerUnsafe())); return ResultSuccess(); }