mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-30 23:49:01 -05:00 
			
		
		
		
	Merge pull request #1689 from Subv/shmem
Kernel: Implemented shared memory.
This commit is contained in:
		| @@ -52,6 +52,7 @@ set(SRCS | ||||
|             hle/service/apt/apt_a.cpp | ||||
|             hle/service/apt/apt_s.cpp | ||||
|             hle/service/apt/apt_u.cpp | ||||
|             hle/service/apt/bcfnt/bcfnt.cpp | ||||
|             hle/service/boss/boss.cpp | ||||
|             hle/service/boss/boss_p.cpp | ||||
|             hle/service/boss/boss_u.cpp | ||||
| @@ -185,6 +186,7 @@ set(HEADERS | ||||
|             hle/service/apt/apt_a.h | ||||
|             hle/service/apt/apt_s.h | ||||
|             hle/service/apt/apt_u.h | ||||
|             hle/service/apt/bcfnt/bcfnt.h | ||||
|             hle/service/boss/boss.h | ||||
|             hle/service/boss/boss_p.h | ||||
|             hle/service/boss/boss_u.h | ||||
|   | ||||
| @@ -65,6 +65,7 @@ protected: | ||||
|     virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; | ||||
|  | ||||
|     Service::APT::AppletId id; ///< Id of this Applet | ||||
|     std::shared_ptr<std::vector<u8>> heap_memory; ///< Heap memory for this Applet | ||||
| }; | ||||
|  | ||||
| /// Returns whether a library applet is currently running | ||||
|   | ||||
| @@ -35,9 +35,14 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p | ||||
|     ASSERT(sizeof(capture_info) == parameter.buffer_size); | ||||
|  | ||||
|     memcpy(&capture_info, parameter.data, sizeof(capture_info)); | ||||
|  | ||||
|     using Kernel::MemoryPermission; | ||||
|     framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite, | ||||
|                                                       MemoryPermission::ReadWrite, "MiiSelector Memory"); | ||||
|     // Allocate a heap block of the required size for this applet. | ||||
|     heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); | ||||
|     // Create a SharedMemory that directly points to this heap block. | ||||
|     framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(), | ||||
|                                                                MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, | ||||
|                                                                "MiiSelector Memory"); | ||||
|  | ||||
|     // Send the response message with the newly created SharedMemory | ||||
|     Service::APT::MessageParameter result; | ||||
|   | ||||
| @@ -40,8 +40,12 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con | ||||
|     memcpy(&capture_info, parameter.data, sizeof(capture_info)); | ||||
|  | ||||
|     using Kernel::MemoryPermission; | ||||
|     framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite, | ||||
|                                                       MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); | ||||
|     // Allocate a heap block of the required size for this applet. | ||||
|     heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); | ||||
|     // Create a SharedMemory that directly points to this heap block. | ||||
|     framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(), | ||||
|                                                                MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, | ||||
|                                                                "SoftwareKeyboard Memory"); | ||||
|  | ||||
|     // Send the response message with the newly created SharedMemory | ||||
|     Service::APT::MessageParameter result; | ||||
|   | ||||
| @@ -170,7 +170,8 @@ template<ResultCode func(s64*, u32, s32)> void Wrap() { | ||||
|  | ||||
| template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() { | ||||
|     u32 param_1 = 0; | ||||
|     u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; | ||||
|     // The last parameter is passed in R0 instead of R4 | ||||
|     u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(0)).raw; | ||||
|     Core::g_app_core->SetReg(1, param_1); | ||||
|     FuncReturn(retval); | ||||
| } | ||||
|   | ||||
| @@ -107,7 +107,6 @@ struct MemoryArea { | ||||
|  | ||||
| // We don't declare the IO regions in here since its handled by other means. | ||||
| static MemoryArea memory_areas[] = { | ||||
|     {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE,     "Shared Memory"}, // Shared memory | ||||
|     {VRAM_VADDR,          VRAM_SIZE,              "VRAM"},          // Video memory (VRAM) | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -209,7 +209,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     // Expansion of the linear heap is only allowed if you do an allocation immediatelly at its | ||||
|     // Expansion of the linear heap is only allowed if you do an allocation immediately at its | ||||
|     // end. It's possible to free gaps in the middle of the heap and then reallocate them later, | ||||
|     // but expansions are only allowed at the end. | ||||
|     if (target == heap_end) { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "common/logging/log.h" | ||||
|  | ||||
| #include "core/memory.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| @@ -14,93 +15,157 @@ namespace Kernel { | ||||
| SharedMemory::SharedMemory() {} | ||||
| SharedMemory::~SharedMemory() {} | ||||
|  | ||||
| SharedPtr<SharedMemory> SharedMemory::Create(u32 size, MemoryPermission permissions, | ||||
|         MemoryPermission other_permissions, std::string name) { | ||||
| SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions, | ||||
|         MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) { | ||||
|     SharedPtr<SharedMemory> shared_memory(new SharedMemory); | ||||
|  | ||||
|     shared_memory->owner_process = owner_process; | ||||
|     shared_memory->name = std::move(name); | ||||
|     shared_memory->base_address = 0x0; | ||||
|     shared_memory->fixed_address = 0x0; | ||||
|     shared_memory->size = size; | ||||
|     shared_memory->permissions = permissions; | ||||
|     shared_memory->other_permissions = other_permissions; | ||||
|  | ||||
|     if (address == 0) { | ||||
|         // We need to allocate a block from the Linear Heap ourselves. | ||||
|         // We'll manually allocate some memory from the linear heap in the specified region. | ||||
|         MemoryRegionInfo* memory_region = GetMemoryRegion(region); | ||||
|         auto& linheap_memory = memory_region->linear_heap_memory; | ||||
|  | ||||
|         ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, "Not enough space in region to allocate shared memory!"); | ||||
|  | ||||
|         shared_memory->backing_block = linheap_memory; | ||||
|         shared_memory->backing_block_offset = linheap_memory->size(); | ||||
|         // Allocate some memory from the end of the linear heap for this region. | ||||
|         linheap_memory->insert(linheap_memory->end(), size, 0); | ||||
|         memory_region->used += size; | ||||
|  | ||||
|         shared_memory->linear_heap_phys_address = Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset; | ||||
|  | ||||
|         // Increase the amount of used linear heap memory for the owner process. | ||||
|         if (shared_memory->owner_process != nullptr) { | ||||
|             shared_memory->owner_process->linear_heap_used += size; | ||||
|         } | ||||
|  | ||||
|         // Refresh the address mappings for the current process. | ||||
|         if (Kernel::g_current_process != nullptr) { | ||||
|             Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); | ||||
|         } | ||||
|     } else { | ||||
|         // TODO(Subv): What happens if an application tries to create multiple memory blocks pointing to the same address? | ||||
|         auto& vm_manager = shared_memory->owner_process->vm_manager; | ||||
|         // The memory is already available and mapped in the owner process. | ||||
|         auto vma = vm_manager.FindVMA(address)->second; | ||||
|         // Copy it over to our own storage | ||||
|         shared_memory->backing_block = std::make_shared<std::vector<u8>>(vma.backing_block->data() + vma.offset, | ||||
|                                                                          vma.backing_block->data() + vma.offset + size); | ||||
|         shared_memory->backing_block_offset = 0; | ||||
|         // Unmap the existing pages | ||||
|         vm_manager.UnmapRange(address, size); | ||||
|         // Map our own block into the address space | ||||
|         vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, MemoryState::Shared); | ||||
|         // Reprotect the block with the new permissions | ||||
|         vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions)); | ||||
|     } | ||||
|  | ||||
|     shared_memory->base_address = address; | ||||
|     return shared_memory; | ||||
| } | ||||
|  | ||||
| ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, | ||||
| SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size, | ||||
|                                                       MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { | ||||
|     SharedPtr<SharedMemory> shared_memory(new SharedMemory); | ||||
|  | ||||
|     shared_memory->owner_process = nullptr; | ||||
|     shared_memory->name = std::move(name); | ||||
|     shared_memory->size = size; | ||||
|     shared_memory->permissions = permissions; | ||||
|     shared_memory->other_permissions = other_permissions; | ||||
|     shared_memory->backing_block = heap_block; | ||||
|     shared_memory->backing_block_offset = offset; | ||||
|     shared_memory->base_address = Memory::HEAP_VADDR + offset; | ||||
|  | ||||
|     return shared_memory; | ||||
| } | ||||
|  | ||||
| ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, | ||||
|         MemoryPermission other_permissions) { | ||||
|  | ||||
|     if (base_address != 0) { | ||||
|         LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: already mapped at 0x%08X!", | ||||
|             GetObjectId(), address, name.c_str(), base_address); | ||||
|         // TODO: Verify error code with hardware | ||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | ||||
|             ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||||
|     MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions; | ||||
|  | ||||
|     // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare | ||||
|     if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { | ||||
|         return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|     } | ||||
|  | ||||
|     // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't | ||||
|     // match what was specified when the memory block was created. | ||||
|     // Error out if the requested permissions don't match what the creator process allows. | ||||
|     if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { | ||||
|         LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", | ||||
|                   GetObjectId(), address, name.c_str()); | ||||
|         return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|     } | ||||
|  | ||||
|     // TODO(Subv): Return E0E01BEE when address should be 0. | ||||
|     // Note: Find out when that's the case. | ||||
|     // Heap-backed memory blocks can not be mapped with other_permissions = DontCare | ||||
|     if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { | ||||
|         LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", | ||||
|                   GetObjectId(), address, name.c_str()); | ||||
|         return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|     } | ||||
|  | ||||
|     if (fixed_address != 0) { | ||||
|          if (address != 0 && address != fixed_address) { | ||||
|             LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: fixed_addres is 0x%08X!", | ||||
|                     GetObjectId(), address, name.c_str(), fixed_address); | ||||
|             // TODO: Verify error code with hardware | ||||
|             return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | ||||
|                 ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||||
|     // Error out if the provided permissions are not compatible with what the creator process needs. | ||||
|     if (other_permissions != MemoryPermission::DontCare && | ||||
|         static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { | ||||
|         LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", | ||||
|                   GetObjectId(), address, name.c_str()); | ||||
|         return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||||
|     } | ||||
|  | ||||
|     // TODO(Subv): Check for the Shared Device Mem flag in the creator process. | ||||
|     /*if (was_created_with_shared_device_mem && address != 0) { | ||||
|         return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|     }*/ | ||||
|  | ||||
|     // TODO(Subv): The same process that created a SharedMemory object | ||||
|     // can not map it in its own address space unless it was created with addr=0, result 0xD900182C. | ||||
|  | ||||
|     if (address != 0) { | ||||
|         if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) { | ||||
|             LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address", | ||||
|                       GetObjectId(), address, name.c_str()); | ||||
|             return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, | ||||
|                               ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|         } | ||||
|  | ||||
|         // HACK(yuriks): This is only here to support the APT shared font mapping right now. | ||||
|         // Later, this should actually map the memory block onto the address space. | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
|  | ||||
|     if (address < Memory::SHARED_MEMORY_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) { | ||||
|         LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s outside of shared mem bounds!", | ||||
|                 GetObjectId(), address, name.c_str()); | ||||
|         // TODO: Verify error code with hardware | ||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | ||||
|                 ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||||
|     VAddr target_address = address; | ||||
|  | ||||
|     if (base_address == 0 && target_address == 0) { | ||||
|         // Calculate the address at which to map the memory block. | ||||
|         target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address); | ||||
|     } | ||||
|  | ||||
|     // TODO: Test permissions | ||||
|  | ||||
|     // HACK: Since there's no way to write to the memory block without mapping it onto the game | ||||
|     // process yet, at least initialize memory the first time it's mapped. | ||||
|     if (address != this->base_address) { | ||||
|         std::memset(Memory::GetPointer(address), 0, size); | ||||
|     // Map the memory block into the target process | ||||
|     auto result = target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared); | ||||
|     if (result.Failed()) { | ||||
|         LOG_ERROR(Kernel, "cannot map id=%u, target_address=0x%08X name=%s, error mapping to virtual memory", | ||||
|                   GetObjectId(), target_address, name.c_str()); | ||||
|         return result.Code(); | ||||
|     } | ||||
|  | ||||
|     this->base_address = address; | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
|     return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions)); | ||||
| } | ||||
|  | ||||
| ResultCode SharedMemory::Unmap(VAddr address) { | ||||
|     if (base_address == 0) { | ||||
|         // TODO(Subv): Verify what actually happens when you want to unmap a memory block that | ||||
|         // was originally mapped with address = 0 | ||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|     } | ||||
|  | ||||
|     if (base_address != address) | ||||
|         return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage); | ||||
|  | ||||
|     base_address = 0; | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { | ||||
|     // TODO(Subv): Verify what happens if the application tries to unmap an address that is not mapped to a SharedMemory. | ||||
|     return target_process->vm_manager.UnmapRange(address, size); | ||||
| } | ||||
|  | ||||
| VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { | ||||
|     u32 masked_permissions = static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute); | ||||
|     return static_cast<VMAPermission>(masked_permissions); | ||||
| }; | ||||
|  | ||||
| u8* SharedMemory::GetPointer(u32 offset) { | ||||
|     if (base_address != 0) | ||||
|         return Memory::GetPointer(base_address + offset); | ||||
|  | ||||
|     LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); | ||||
|     return nullptr; | ||||
|     return backing_block->data() + backing_block_offset + offset; | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/result.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| @@ -29,14 +30,29 @@ enum class MemoryPermission : u32 { | ||||
| class SharedMemory final : public Object { | ||||
| public: | ||||
|     /** | ||||
|      * Creates a shared memory object | ||||
|      * Creates a shared memory object. | ||||
|      * @param owner_process Process that created this shared memory object. | ||||
|      * @param size Size of the memory block. Must be page-aligned. | ||||
|      * @param permissions Permission restrictions applied to the process which created the block. | ||||
|      * @param other_permissions Permission restrictions applied to other processes mapping the block. | ||||
|      * @param address The address from which to map the Shared Memory. | ||||
|      * @param region If the address is 0, the shared memory will be allocated in this region of the linear heap. | ||||
|      * @param name Optional object name, used for debugging purposes. | ||||
|      */ | ||||
|     static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions, | ||||
|             MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown"); | ||||
|  | ||||
|     /** | ||||
|      * Creates a shared memory object from a block of memory managed by an HLE applet. | ||||
|      * @param heap_block Heap block of the HLE applet. | ||||
|      * @param offset The offset into the heap block that the SharedMemory will map. | ||||
|      * @param size Size of the memory block. Must be page-aligned. | ||||
|      * @param permissions Permission restrictions applied to the process which created the block. | ||||
|      * @param other_permissions Permission restrictions applied to other processes mapping the block. | ||||
|      * @param name Optional object name, used for debugging purposes. | ||||
|      */ | ||||
|     static SharedPtr<SharedMemory> Create(u32 size, MemoryPermission permissions, | ||||
|             MemoryPermission other_permissions, std::string name = "Unknown"); | ||||
|     static SharedPtr<SharedMemory> CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size, | ||||
|                                                    MemoryPermission permissions, MemoryPermission other_permissions, std::string name = "Unknown Applet"); | ||||
|  | ||||
|     std::string GetTypeName() const override { return "SharedMemory"; } | ||||
|     std::string GetName() const override { return name; } | ||||
| @@ -45,19 +61,27 @@ public: | ||||
|     HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||||
|  | ||||
|     /** | ||||
|      * Maps a shared memory block to an address in system memory | ||||
|      * Converts the specified MemoryPermission into the equivalent VMAPermission. | ||||
|      * @param permission The MemoryPermission to convert. | ||||
|      */ | ||||
|     static VMAPermission ConvertPermissions(MemoryPermission permission); | ||||
|  | ||||
|     /** | ||||
|      * Maps a shared memory block to an address in the target process' address space | ||||
|      * @param target_process Process on which to map the memory block. | ||||
|      * @param address Address in system memory to map shared memory block to | ||||
|      * @param permissions Memory block map permissions (specified by SVC field) | ||||
|      * @param other_permissions Memory block map other permissions (specified by SVC field) | ||||
|      */ | ||||
|     ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions); | ||||
|     ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions); | ||||
|  | ||||
|     /** | ||||
|      * Unmaps a shared memory block from the specified address in system memory | ||||
|      * @param target_process Process from which to umap the memory block. | ||||
|      * @param address Address in system memory where the shared memory block is mapped | ||||
|      * @return Result code of the unmap operation | ||||
|      */ | ||||
|     ResultCode Unmap(VAddr address); | ||||
|     ResultCode Unmap(Process* target_process, VAddr address); | ||||
|  | ||||
|     /** | ||||
|     * Gets a pointer to the shared memory block | ||||
| @@ -66,10 +90,16 @@ public: | ||||
|     */ | ||||
|     u8* GetPointer(u32 offset = 0); | ||||
|  | ||||
|     /// Address of shared memory block in the process. | ||||
|     /// Process that created this shared memory block. | ||||
|     SharedPtr<Process> owner_process; | ||||
|     /// Address of shared memory block in the owner process if specified. | ||||
|     VAddr base_address; | ||||
|     /// Fixed address to allow mapping to. Used for blocks created from the linear heap. | ||||
|     VAddr fixed_address; | ||||
|     /// Physical address of the shared memory block in the linear heap if no address was specified during creation. | ||||
|     PAddr linear_heap_phys_address; | ||||
|     /// Backing memory for this shared memory block. | ||||
|     std::shared_ptr<std::vector<u8>> backing_block; | ||||
|     /// Offset into the backing block for this shared memory. | ||||
|     u32 backing_block_offset; | ||||
|     /// Size of the memory block. Page-aligned. | ||||
|     u32 size; | ||||
|     /// Permission restrictions applied to the process which created the block. | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| /// Detailed description of the error. This listing is likely incomplete. | ||||
| enum class ErrorDescription : u32 { | ||||
|     Success = 0, | ||||
|     WrongPermission = 46, | ||||
|     OS_InvalidBufferDescriptor = 48, | ||||
|     WrongAddress = 53, | ||||
|     FS_NotFound = 120, | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include "core/hle/service/apt/apt_a.h" | ||||
| #include "core/hle/service/apt/apt_s.h" | ||||
| #include "core/hle/service/apt/apt_u.h" | ||||
| #include "core/hle/service/apt/bcfnt/bcfnt.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
|  | ||||
| #include "core/hle/kernel/event.h" | ||||
| @@ -22,23 +23,14 @@ | ||||
| namespace Service { | ||||
| namespace APT { | ||||
|  | ||||
| // Address used for shared font (as observed on HW) | ||||
| // TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via | ||||
| // https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any | ||||
| // address other than 0x18000000 due to internal pointers in the shared font dump that would need to | ||||
| // be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then | ||||
| // correctly mapping it in Citra, however we still do not understand how the mapping is determined. | ||||
| static const VAddr SHARED_FONT_VADDR = 0x18000000; | ||||
|  | ||||
| /// Handle to shared memory region designated to for shared system font | ||||
| static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; | ||||
| static bool shared_font_relocated = false; | ||||
|  | ||||
| static Kernel::SharedPtr<Kernel::Mutex> lock; | ||||
| static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event | ||||
| static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event | ||||
|  | ||||
| static std::shared_ptr<std::vector<u8>> shared_font; | ||||
|  | ||||
| static u32 cpu_percent; ///< CPU time available to the running application | ||||
|  | ||||
| /// Parameter data to be returned in the next call to Glance/ReceiveParameter | ||||
| @@ -74,23 +66,25 @@ void Initialize(Service::Interface* self) { | ||||
| void GetSharedFont(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|  | ||||
|     if (shared_font != nullptr) { | ||||
|         // TODO(yuriks): This is a hack to keep this working right now even with our completely | ||||
|         // broken shared memory system. | ||||
|         shared_font_mem->fixed_address = SHARED_FONT_VADDR; | ||||
|         Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->fixed_address, | ||||
|                 shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared); | ||||
|  | ||||
|         cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); | ||||
|         cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||||
|         cmd_buff[2] = SHARED_FONT_VADDR; | ||||
|         cmd_buff[3] = IPC::MoveHandleDesc(); | ||||
|         cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); | ||||
|     } else { | ||||
|         cmd_buff[0] = IPC::MakeHeader(0x44, 1, 0); | ||||
|         cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware) | ||||
|         LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT); | ||||
|     // The shared font has to be relocated to the new address before being passed to the application. | ||||
|     VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address); | ||||
|     // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base, | ||||
|     // so we relocate it from there to our real address. | ||||
|     // TODO(Subv): This address is wrong if the shared font is dumped from a n3DS, | ||||
|     // we need a way to automatically calculate the original address of the font from the file. | ||||
|     static const VAddr SHARED_FONT_VADDR = 0x18000000; | ||||
|     if (!shared_font_relocated) { | ||||
|         BCFNT::RelocateSharedFont(shared_font_mem, SHARED_FONT_VADDR, target_address); | ||||
|         shared_font_relocated = true; | ||||
|     } | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||||
|     // Since the SharedMemory interface doesn't provide the address at which the memory was allocated, | ||||
|     // the real APT service calculates this address by scanning the entire address space (using svcQueryMemory) | ||||
|     // and searches for an allocation of the same size as the Shared Font. | ||||
|     cmd_buff[2] = target_address; | ||||
|     cmd_buff[3] = IPC::MoveHandleDesc(); | ||||
|     cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); | ||||
| } | ||||
|  | ||||
| void NotifyToWait(Service::Interface* self) { | ||||
| @@ -433,14 +427,12 @@ void Init() { | ||||
|     FileUtil::IOFile file(filepath, "rb"); | ||||
|  | ||||
|     if (file.IsOpen()) { | ||||
|         // Read shared font data | ||||
|         shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize()); | ||||
|         file.ReadBytes(shared_font->data(), shared_font->size()); | ||||
|  | ||||
|         // Create shared font memory object | ||||
|         using Kernel::MemoryPermission; | ||||
|         shared_font_mem = Kernel::SharedMemory::Create(3 * 1024 * 1024, // 3MB | ||||
|                 MemoryPermission::ReadWrite, MemoryPermission::Read, "APT_U:shared_font_mem"); | ||||
|         shared_font_mem = Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB | ||||
|                 MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); | ||||
|         // Read shared font data | ||||
|         file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); | ||||
|     } else { | ||||
|         LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); | ||||
|         shared_font_mem = nullptr; | ||||
| @@ -459,8 +451,8 @@ void Init() { | ||||
| } | ||||
|  | ||||
| void Shutdown() { | ||||
|     shared_font = nullptr; | ||||
|     shared_font_mem = nullptr; | ||||
|     shared_font_relocated = false; | ||||
|     lock = nullptr; | ||||
|     notification_event = nullptr; | ||||
|     parameter_event = nullptr; | ||||
|   | ||||
							
								
								
									
										71
									
								
								src/core/hle/service/apt/bcfnt/bcfnt.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/core/hle/service/apt/bcfnt/bcfnt.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| // Copyright 2016 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "core/hle/service/apt/bcfnt/bcfnt.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| namespace Service { | ||||
| namespace APT { | ||||
| namespace BCFNT { | ||||
|  | ||||
| void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address) { | ||||
|     static const u32 SharedFontStartOffset = 0x80; | ||||
|     u8* data = shared_font->GetPointer(SharedFontStartOffset); | ||||
|  | ||||
|     CFNT cfnt; | ||||
|     memcpy(&cfnt, data, sizeof(cfnt)); | ||||
|  | ||||
|     // Advance past the header | ||||
|     data = shared_font->GetPointer(SharedFontStartOffset + cfnt.header_size); | ||||
|  | ||||
|     for (unsigned block = 0; block < cfnt.num_blocks; ++block) { | ||||
|  | ||||
|         u32 section_size = 0; | ||||
|         if (memcmp(data, "FINF", 4) == 0) { | ||||
|             BCFNT::FINF finf; | ||||
|             memcpy(&finf, data, sizeof(finf)); | ||||
|             section_size = finf.section_size; | ||||
|  | ||||
|             // Relocate the offsets in the FINF section | ||||
|             finf.cmap_offset += new_address - previous_address; | ||||
|             finf.cwdh_offset += new_address - previous_address; | ||||
|             finf.tglp_offset += new_address - previous_address; | ||||
|  | ||||
|             memcpy(data, &finf, sizeof(finf)); | ||||
|         } else if (memcmp(data, "CMAP", 4) == 0) { | ||||
|             BCFNT::CMAP cmap; | ||||
|             memcpy(&cmap, data, sizeof(cmap)); | ||||
|             section_size = cmap.section_size; | ||||
|  | ||||
|             // Relocate the offsets in the CMAP section | ||||
|             cmap.next_cmap_offset += new_address - previous_address; | ||||
|  | ||||
|             memcpy(data, &cmap, sizeof(cmap)); | ||||
|         } else if (memcmp(data, "CWDH", 4) == 0) { | ||||
|             BCFNT::CWDH cwdh; | ||||
|             memcpy(&cwdh, data, sizeof(cwdh)); | ||||
|             section_size = cwdh.section_size; | ||||
|  | ||||
|             // Relocate the offsets in the CWDH section | ||||
|             cwdh.next_cwdh_offset += new_address - previous_address; | ||||
|  | ||||
|             memcpy(data, &cwdh, sizeof(cwdh)); | ||||
|         } else if (memcmp(data, "TGLP", 4) == 0) { | ||||
|             BCFNT::TGLP tglp; | ||||
|             memcpy(&tglp, data, sizeof(tglp)); | ||||
|             section_size = tglp.section_size; | ||||
|  | ||||
|             // Relocate the offsets in the TGLP section | ||||
|             tglp.sheet_data_offset += new_address - previous_address; | ||||
|  | ||||
|             memcpy(data, &tglp, sizeof(tglp)); | ||||
|         } | ||||
|  | ||||
|         data += section_size; | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace BCFNT | ||||
| } // namespace APT | ||||
| } // namespace Service | ||||
							
								
								
									
										87
									
								
								src/core/hle/service/apt/bcfnt/bcfnt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/core/hle/service/apt/bcfnt/bcfnt.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| // Copyright 2016 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/swap.h" | ||||
|  | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| namespace Service { | ||||
| namespace APT { | ||||
| namespace BCFNT { ///< BCFNT Shared Font file structures | ||||
|  | ||||
| struct CFNT { | ||||
|     u8 magic[4]; | ||||
|     u16_le endianness; | ||||
|     u16_le header_size; | ||||
|     u32_le version; | ||||
|     u32_le file_size; | ||||
|     u32_le num_blocks; | ||||
| }; | ||||
|  | ||||
| struct FINF { | ||||
|     u8 magic[4]; | ||||
|     u32_le section_size; | ||||
|     u8 font_type; | ||||
|     u8 line_feed; | ||||
|     u16_le alter_char_index; | ||||
|     u8 default_width[3]; | ||||
|     u8 encoding; | ||||
|     u32_le tglp_offset; | ||||
|     u32_le cwdh_offset; | ||||
|     u32_le cmap_offset; | ||||
|     u8 height; | ||||
|     u8 width; | ||||
|     u8 ascent; | ||||
|     u8 reserved; | ||||
| }; | ||||
|  | ||||
| struct TGLP { | ||||
|     u8 magic[4]; | ||||
|     u32_le section_size; | ||||
|     u8 cell_width; | ||||
|     u8 cell_height; | ||||
|     u8 baseline_position; | ||||
|     u8 max_character_width; | ||||
|     u32_le sheet_size; | ||||
|     u16_le num_sheets; | ||||
|     u16_le sheet_image_format; | ||||
|     u16_le num_columns; | ||||
|     u16_le num_rows; | ||||
|     u16_le sheet_width; | ||||
|     u16_le sheet_height; | ||||
|     u32_le sheet_data_offset; | ||||
| }; | ||||
|  | ||||
| struct CMAP { | ||||
|     u8 magic[4]; | ||||
|     u32_le section_size; | ||||
|     u16_le code_begin; | ||||
|     u16_le code_end; | ||||
|     u16_le mapping_method; | ||||
|     u16_le reserved; | ||||
|     u32_le next_cmap_offset; | ||||
| }; | ||||
|  | ||||
| struct CWDH { | ||||
|     u8 magic[4]; | ||||
|     u32_le section_size; | ||||
|     u16_le start_index; | ||||
|     u16_le end_index; | ||||
|     u32_le next_cwdh_offset; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Relocates the internal addresses of the BCFNT Shared Font to the new base. | ||||
|  * @param shared_font SharedMemory object that contains the Shared Font | ||||
|  * @param previous_address Previous address at which the offsets in the structure were based. | ||||
|  * @param new_address New base for the offsets in the structure. | ||||
|  */ | ||||
| void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address); | ||||
|  | ||||
| } // namespace BCFNT | ||||
| } // namespace APT | ||||
| } // namespace Service | ||||
| @@ -3,6 +3,7 @@ | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <cstring> | ||||
| #include "common/alignment.h" | ||||
| #include "core/hle/hle.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| @@ -41,14 +42,16 @@ static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr; | ||||
| void Initialize(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|  | ||||
|     shared_memory = Kernel::SharedMemory::Create(cmd_buff[1], | ||||
|             Kernel::MemoryPermission::ReadWrite, | ||||
|             Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem"); | ||||
|     u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE); | ||||
|     using Kernel::MemoryPermission; | ||||
|     shared_memory = Kernel::SharedMemory::Create(nullptr, size, | ||||
|                                                  MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, | ||||
|                                                  0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); | ||||
|  | ||||
|     mutex = Kernel::Mutex::Create(false); | ||||
|  | ||||
|     cmd_buff[1] = 0; | ||||
|     cmd_buff[2] = 0x4000000; | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = IPC::MoveHandleDesc(2); | ||||
|     cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); | ||||
|     cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); | ||||
| } | ||||
|   | ||||
| @@ -335,8 +335,9 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { | ||||
|     g_interrupt_event->name = "GSP_GPU::interrupt_event"; | ||||
|  | ||||
|     using Kernel::MemoryPermission; | ||||
|     g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, | ||||
|         MemoryPermission::ReadWrite, "GSPSharedMem"); | ||||
|     g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000, | ||||
|                                                    MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, | ||||
|                                                    0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); | ||||
|  | ||||
|     Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); | ||||
|  | ||||
|   | ||||
| @@ -280,8 +280,9 @@ void Init() { | ||||
|     AddService(new HID_SPVR_Interface); | ||||
|  | ||||
|     using Kernel::MemoryPermission; | ||||
|     shared_mem = SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, | ||||
|             MemoryPermission::Read, "HID:SharedMem"); | ||||
|     shared_mem = SharedMemory::Create(nullptr, 0x1000, | ||||
|                                       MemoryPermission::ReadWrite, MemoryPermission::Read, | ||||
|                                       0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); | ||||
|  | ||||
|     next_pad_index = 0; | ||||
|     next_touch_index = 0; | ||||
|   | ||||
| @@ -94,8 +94,9 @@ void Init() { | ||||
|     AddService(new IR_User_Interface); | ||||
|  | ||||
|     using Kernel::MemoryPermission; | ||||
|     shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite, | ||||
|                                          Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory"); | ||||
|     shared_memory = SharedMemory::Create(nullptr, 0x1000, | ||||
|                                          Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::ReadWrite, | ||||
|                                          0, Kernel::MemoryRegion::BASE, "IR:SharedMemory"); | ||||
|     transfer_shared_memory = nullptr; | ||||
|  | ||||
|     // Create event handle(s) | ||||
|   | ||||
| @@ -99,6 +99,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add | ||||
|     switch (operation & MEMOP_OPERATION_MASK) { | ||||
|     case MEMOP_FREE: | ||||
|     { | ||||
|         // TODO(Subv): What happens if an application tries to FREE a block of memory that has a SharedMemory pointing to it? | ||||
|         if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) { | ||||
|             ResultCode result = process.HeapFree(addr0, size); | ||||
|             if (result.IsError()) return result; | ||||
| @@ -160,8 +161,6 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o | ||||
|     LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", | ||||
|         handle, addr, permissions, other_permissions); | ||||
|  | ||||
|     // TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space | ||||
|  | ||||
|     SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); | ||||
|     if (shared_memory == nullptr) | ||||
|         return ERR_INVALID_HANDLE; | ||||
| @@ -176,7 +175,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o | ||||
|     case MemoryPermission::WriteExecute: | ||||
|     case MemoryPermission::ReadWriteExecute: | ||||
|     case MemoryPermission::DontCare: | ||||
|         return shared_memory->Map(addr, permissions_type, | ||||
|         return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type, | ||||
|                 static_cast<MemoryPermission>(other_permissions)); | ||||
|     default: | ||||
|         LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); | ||||
| @@ -196,7 +195,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) { | ||||
|     if (shared_memory == nullptr) | ||||
|         return ERR_INVALID_HANDLE; | ||||
|  | ||||
|     return shared_memory->Unmap(addr); | ||||
|     return shared_memory->Unmap(Kernel::g_current_process.get(), addr); | ||||
| } | ||||
|  | ||||
| /// Connect to an OS service given the port name, returns the handle to the port to out | ||||
| @@ -790,18 +789,44 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 | ||||
|     if (size % Memory::PAGE_SIZE != 0) | ||||
|         return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|  | ||||
|     // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap | ||||
|  | ||||
|     // TODO(Subv): Implement this function properly | ||||
|     SharedPtr<SharedMemory> shared_memory = nullptr; | ||||
|  | ||||
|     using Kernel::MemoryPermission; | ||||
|     SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size, | ||||
|             (MemoryPermission)my_permission, (MemoryPermission)other_permission); | ||||
|     // Map the SharedMemory to the specified address | ||||
|     shared_memory->base_address = addr; | ||||
|     auto VerifyPermissions = [](MemoryPermission permission) { | ||||
|         // SharedMemory blocks can not be created with Execute permissions | ||||
|         switch (permission) { | ||||
|         case MemoryPermission::None: | ||||
|         case MemoryPermission::Read: | ||||
|         case MemoryPermission::Write: | ||||
|         case MemoryPermission::ReadWrite: | ||||
|         case MemoryPermission::DontCare: | ||||
|             return true; | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) || | ||||
|         !VerifyPermissions(static_cast<MemoryPermission>(other_permission))) | ||||
|         return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, | ||||
|                           ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|  | ||||
|     if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) { | ||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||
|     } | ||||
|  | ||||
|     // When trying to create a memory block with address = 0, | ||||
|     // if the process has the Shared Device Memory flag in the exheader, | ||||
|     // then we have to allocate from the same region as the caller process instead of the BASE region. | ||||
|     Kernel::MemoryRegion region = Kernel::MemoryRegion::BASE; | ||||
|     if (addr == 0 && Kernel::g_current_process->flags.shared_device_mem) | ||||
|         region = Kernel::g_current_process->flags.memory_region; | ||||
|  | ||||
|     shared_memory = SharedMemory::Create(Kernel::g_current_process, size, | ||||
|                                 static_cast<MemoryPermission>(my_permission), static_cast<MemoryPermission>(other_permission), addr, region); | ||||
|     CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory))); | ||||
|  | ||||
|     LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr); | ||||
|     LOG_WARNING(Kernel_SVC, "called addr=0x%08X", addr); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei