From 8c65433ab5a70870111b4462d54871f3d349ee7a Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Sun, 4 Nov 2018 12:11:08 -0500 Subject: [PATCH 1/3] Kernel, APT: SharedFont/SharedMemoryOnSharedDevice should always use old linear heap VAddr --- src/core/hle/kernel/shared_memory.cpp | 5 ++++- src/core/hle/service/apt/apt.cpp | 15 +++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 560d3f3ac..f32651d49 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -142,7 +142,10 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi if (base_address == 0 && target_address == 0) { // Calculate the address at which to map the memory block. - target_address = linear_heap_phys_offset + target_process->GetLinearHeapAreaAddress(); + // Note: even on new firmware versions, the target address is still in the old linear heap + // region. This exception is made to keep the shared font compatibility. See + // APT:GetSharedFont for detail. + target_address = linear_heap_phys_offset + Memory::LINEAR_HEAP_VADDR; } auto vma = target_process->vm_manager.FindVMA(target_address); diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 741a288e8..44b81a0fd 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -207,10 +207,17 @@ void Module::Interface::GetSharedFont(Kernel::HLERequestContext& ctx) { // The shared font has to be relocated to the new address before being passed to the // application. - auto maybe_vaddr = Memory::PhysicalToVirtualAddress( - apt->shared_font_mem->linear_heap_phys_offset + Memory::FCRAM_PADDR); - ASSERT(maybe_vaddr); - VAddr target_address = *maybe_vaddr; + + // Note: the target address is still in the old linear heap region even on new firmware + // versions. This exception is made for shared font to resolve the following compatibility + // issue: + // The linear heap region changes depending on the kernel version marked in application's + // exheader (not the actual version the application is running on). If an application with old + // kernel version and an applet with new kernel version run at the same time, and they both use + // shared font, different linear heap region would have required shared font to relocate + // according to two different addresses at the same time, which is impossible. + VAddr target_address = + apt->shared_font_mem->linear_heap_phys_offset + Memory::LINEAR_HEAP_VADDR; if (!apt->shared_font_relocated) { BCFNT::RelocateSharedFont(apt->shared_font_mem, target_address); apt->shared_font_relocated = true; From 2654a679b3d1a6fb3c36ca00b44dce713b9a98b4 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Sun, 4 Nov 2018 12:15:55 -0500 Subject: [PATCH 2/3] Memory: replace PhysicalToVirtualAddress with a more dedicated function There is no external use of PhysicalToVirtualAddress any more, so it there is no need to have a generic function that handles all physical regions. Also, the previous APT change makes it possible that linear heap has some regions mapped to old and new VAddr regions at the same time, so we need to check both region and mark cached for the mapped one. RasterizerMarkRegionCached would skip the unmapped one in its loop --- src/core/memory.cpp | 122 ++++++++++++++++++++------------------------ src/core/memory.h | 5 -- 2 files changed, 54 insertions(+), 73 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 697345ea7..af37ff471 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -318,6 +318,22 @@ u8* GetPhysicalPointer(PAddr address) { return target_pointer; } +/// For a rasterizer-accessible PAddr, gets a list of all possible VAddr +static std::vector PhysicalToVirtualAddressForRasterizer(PAddr addr) { + if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { + return {addr - VRAM_PADDR + VRAM_VADDR}; + } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { + return {addr - FCRAM_PADDR + LINEAR_HEAP_VADDR, addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR}; + } else { + // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache, + // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond + // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing + // parts of the texture. + LOG_ERROR(HW_Memory, "Trying to use invalid physical address for rasterizer: {:08X}", addr); + return {}; + } +} + void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached) { if (start == 0) { return; @@ -327,57 +343,46 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached) { PAddr paddr = start; for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { - std::optional maybe_vaddr = PhysicalToVirtualAddress(paddr); - // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache, - // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond - // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing - // parts of the texture. - if (!maybe_vaddr) { - LOG_ERROR(HW_Memory, - "Trying to flush a cached region to an invalid physical address {:08X}", - paddr); - continue; - } - VAddr vaddr = *maybe_vaddr; + for (VAddr vaddr : PhysicalToVirtualAddressForRasterizer(paddr)) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; - PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; - - if (cached) { - // Switch page type to cached if now cached - switch (page_type) { - case PageType::Unmapped: - // It is not necessary for a process to have this region mapped into its address - // space, for example, a system module need not have a VRAM mapping. - break; - case PageType::Memory: - page_type = PageType::RasterizerCachedMemory; - current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; - break; - default: - UNREACHABLE(); - } - } else { - // Switch page type to uncached if now uncached - switch (page_type) { - case PageType::Unmapped: - // It is not necessary for a process to have this region mapped into its address - // space, for example, a system module need not have a VRAM mapping. - break; - case PageType::RasterizerCachedMemory: { - u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); - if (pointer == nullptr) { - // It's possible that this function has been called while updating the pagetable - // after unmapping a VMA. In that case the underlying VMA will no longer exist, - // and we should just leave the pagetable entry blank. - page_type = PageType::Unmapped; - } else { - page_type = PageType::Memory; - current_page_table->pointers[vaddr >> PAGE_BITS] = pointer; + if (cached) { + // Switch page type to cached if now cached + switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its address + // space, for example, a system module need not have a VRAM mapping. + break; + case PageType::Memory: + page_type = PageType::RasterizerCachedMemory; + current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; + break; + default: + UNREACHABLE(); + } + } else { + // Switch page type to uncached if now uncached + switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its address + // space, for example, a system module need not have a VRAM mapping. + break; + case PageType::RasterizerCachedMemory: { + u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); + if (pointer == nullptr) { + // It's possible that this function has been called while updating the + // pagetable after unmapping a VMA. In that case the underlying VMA will no + // longer exist, and we should just leave the pagetable entry blank. + page_type = PageType::Unmapped; + } else { + page_type = PageType::Memory; + current_page_table->pointers[vaddr >> PAGE_BITS] = pointer; + } + break; + } + default: + UNREACHABLE(); } - break; - } - default: - UNREACHABLE(); } } } @@ -820,25 +825,6 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) { return *paddr; } -std::optional PhysicalToVirtualAddress(const PAddr addr) { - if (addr == 0) { - return 0; - } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { - return addr - VRAM_PADDR + VRAM_VADDR; - } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { - return addr - FCRAM_PADDR + - Core::System::GetInstance().Kernel().GetCurrentProcess()->GetLinearHeapAreaAddress(); - } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { - return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; - } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { - return addr - IO_AREA_PADDR + IO_AREA_VADDR; - } else if (addr >= N3DS_EXTRA_RAM_PADDR && addr < N3DS_EXTRA_RAM_PADDR_END) { - return addr - N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_VADDR; - } - - return {}; -} - u32 GetFCRAMOffset(u8* pointer) { ASSERT(pointer >= fcram.data() && pointer < fcram.data() + fcram.size()); return pointer - fcram.data(); diff --git a/src/core/memory.h b/src/core/memory.h index bad47375a..a1e4383df 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -228,11 +228,6 @@ std::optional TryVirtualToPhysicalAddress(VAddr addr); */ PAddr VirtualToPhysicalAddress(VAddr addr); -/** - * Undoes a mapping performed by VirtualToPhysicalAddress(). - */ -std::optional PhysicalToVirtualAddress(PAddr paddr); - /** * Gets a pointer to the memory region beginning at the specified physical address. */ From d0edb81182ae430f5d5f1ababa1f6e593edaadb9 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Fri, 9 Nov 2018 10:40:04 -0500 Subject: [PATCH 3/3] Memory: convert PAddr for N3DS FCRAM extension --- src/core/memory.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index af37ff471..ed5a04c9a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -322,16 +322,19 @@ u8* GetPhysicalPointer(PAddr address) { static std::vector PhysicalToVirtualAddressForRasterizer(PAddr addr) { if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { return {addr - VRAM_PADDR + VRAM_VADDR}; - } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { - return {addr - FCRAM_PADDR + LINEAR_HEAP_VADDR, addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR}; - } else { - // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache, - // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond - // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing - // parts of the texture. - LOG_ERROR(HW_Memory, "Trying to use invalid physical address for rasterizer: {:08X}", addr); - return {}; } + if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { + return {addr - FCRAM_PADDR + LINEAR_HEAP_VADDR, addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR}; + } + if (addr >= FCRAM_PADDR_END && addr < FCRAM_N3DS_PADDR_END) { + return {addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR}; + } + // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache, + // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond + // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing + // parts of the texture. + LOG_ERROR(HW_Memory, "Trying to use invalid physical address for rasterizer: {:08X}", addr); + return {}; } void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached) {