diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/hle/kernel/memory.cpp | 1 | ||||
-rw-r--r-- | src/core/hle/kernel/process.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 177 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.h | 48 |
4 files changed, 161 insertions, 67 deletions
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 61a741e28..4be20db22 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -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) }; diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 0546f6e16..69302cc82 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -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) { diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d90f0f00f..6a22c8986 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -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, - MemoryPermission other_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); - 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); - } + 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; - // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't - // match what was specified when the memory block was created. + return shared_memory; +} - // TODO(Subv): Return E0E01BEE when address should be 0. - // Note: Find out when that's the case. +ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, + MemoryPermission other_permissions) { - 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); - } + MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions; - // 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; + // 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); } - 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); + // 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: Test permissions + // 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); + } - // 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); + // 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); } - this->base_address = address; + // 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); + }*/ - return RESULT_SUCCESS; -} + // 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. -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 (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); + } } - if (base_address != address) - return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage); + VAddr target_address = address; - base_address = 0; + 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); + } + + // 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(); + } - return RESULT_SUCCESS; + return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions)); } -u8* SharedMemory::GetPointer(u32 offset) { - if (base_address != 0) - return Memory::GetPointer(base_address + offset); +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); +}; - LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); - return nullptr; +u8* SharedMemory::GetPointer(u32 offset) { + return backing_block->data() + backing_block_offset + offset; } } // namespace diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index b51049ad0..0c404a9f8 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -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(u32 size, MemoryPermission permissions, - MemoryPermission other_permissions, std::string name = "Unknown"); + 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> 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. |