shared_memory.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <cstring>
  5. #include "common/logging/log.h"
  6. #include "core/hle/kernel/memory.h"
  7. #include "core/hle/kernel/shared_memory.h"
  8. #include "core/memory.h"
  9. namespace Kernel {
  10. SharedMemory::SharedMemory() {}
  11. SharedMemory::~SharedMemory() {}
  12. SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size,
  13. MemoryPermission permissions,
  14. MemoryPermission other_permissions, VAddr address,
  15. MemoryRegion region, std::string name) {
  16. SharedPtr<SharedMemory> shared_memory(new SharedMemory);
  17. shared_memory->owner_process = owner_process;
  18. shared_memory->name = std::move(name);
  19. shared_memory->size = size;
  20. shared_memory->permissions = permissions;
  21. shared_memory->other_permissions = other_permissions;
  22. if (address == 0) {
  23. // We need to allocate a block from the Linear Heap ourselves.
  24. // We'll manually allocate some memory from the linear heap in the specified region.
  25. MemoryRegionInfo* memory_region = GetMemoryRegion(region);
  26. auto& linheap_memory = memory_region->linear_heap_memory;
  27. ASSERT_MSG(linheap_memory->size() + size <= memory_region->size,
  28. "Not enough space in region to allocate shared memory!");
  29. shared_memory->backing_block = linheap_memory;
  30. shared_memory->backing_block_offset = linheap_memory->size();
  31. // Allocate some memory from the end of the linear heap for this region.
  32. linheap_memory->insert(linheap_memory->end(), size, 0);
  33. memory_region->used += size;
  34. shared_memory->linear_heap_phys_address =
  35. Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset;
  36. // Increase the amount of used linear heap memory for the owner process.
  37. if (shared_memory->owner_process != nullptr) {
  38. shared_memory->owner_process->linear_heap_used += size;
  39. }
  40. // Refresh the address mappings for the current process.
  41. if (Kernel::g_current_process != nullptr) {
  42. Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
  43. }
  44. } else {
  45. // TODO(Subv): What happens if an application tries to create multiple memory blocks
  46. // pointing to the same address?
  47. auto& vm_manager = shared_memory->owner_process->vm_manager;
  48. // The memory is already available and mapped in the owner process.
  49. auto vma = vm_manager.FindVMA(address)->second;
  50. // Copy it over to our own storage
  51. shared_memory->backing_block = std::make_shared<std::vector<u8>>(
  52. vma.backing_block->data() + vma.offset, vma.backing_block->data() + vma.offset + size);
  53. shared_memory->backing_block_offset = 0;
  54. // Unmap the existing pages
  55. vm_manager.UnmapRange(address, size);
  56. // Map our own block into the address space
  57. vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size,
  58. MemoryState::Shared);
  59. // Reprotect the block with the new permissions
  60. vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
  61. }
  62. shared_memory->base_address = address;
  63. return shared_memory;
  64. }
  65. SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block,
  66. u32 offset, u32 size,
  67. MemoryPermission permissions,
  68. MemoryPermission other_permissions,
  69. std::string name) {
  70. SharedPtr<SharedMemory> shared_memory(new SharedMemory);
  71. shared_memory->owner_process = nullptr;
  72. shared_memory->name = std::move(name);
  73. shared_memory->size = size;
  74. shared_memory->permissions = permissions;
  75. shared_memory->other_permissions = other_permissions;
  76. shared_memory->backing_block = heap_block;
  77. shared_memory->backing_block_offset = offset;
  78. shared_memory->base_address = Memory::HEAP_VADDR + offset;
  79. return shared_memory;
  80. }
  81. ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
  82. MemoryPermission other_permissions) {
  83. MemoryPermission own_other_permissions =
  84. target_process == owner_process ? this->permissions : this->other_permissions;
  85. // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
  86. if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
  87. return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
  88. ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  89. }
  90. // Error out if the requested permissions don't match what the creator process allows.
  91. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
  92. LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
  93. GetObjectId(), address, name.c_str());
  94. return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
  95. ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  96. }
  97. // Heap-backed memory blocks can not be mapped with other_permissions = DontCare
  98. if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
  99. LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
  100. GetObjectId(), address, name.c_str());
  101. return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
  102. ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  103. }
  104. // Error out if the provided permissions are not compatible with what the creator process needs.
  105. if (other_permissions != MemoryPermission::DontCare &&
  106. static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
  107. LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
  108. GetObjectId(), address, name.c_str());
  109. return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS,
  110. ErrorSummary::WrongArgument, ErrorLevel::Permanent);
  111. }
  112. // TODO(Subv): Check for the Shared Device Mem flag in the creator process.
  113. /*if (was_created_with_shared_device_mem && address != 0) {
  114. return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
  115. ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  116. }*/
  117. // TODO(Subv): The same process that created a SharedMemory object
  118. // can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
  119. if (address != 0) {
  120. if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
  121. LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address",
  122. GetObjectId(), address, name.c_str());
  123. return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
  124. ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  125. }
  126. }
  127. VAddr target_address = address;
  128. if (base_address == 0 && target_address == 0) {
  129. // Calculate the address at which to map the memory block.
  130. target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address);
  131. }
  132. // Map the memory block into the target process
  133. auto result = target_process->vm_manager.MapMemoryBlock(
  134. target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
  135. if (result.Failed()) {
  136. LOG_ERROR(
  137. Kernel,
  138. "cannot map id=%u, target_address=0x%08X name=%s, error mapping to virtual memory",
  139. GetObjectId(), target_address, name.c_str());
  140. return result.Code();
  141. }
  142. return target_process->vm_manager.ReprotectRange(target_address, size,
  143. ConvertPermissions(permissions));
  144. }
  145. ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
  146. // TODO(Subv): Verify what happens if the application tries to unmap an address that is not
  147. // mapped to a SharedMemory.
  148. return target_process->vm_manager.UnmapRange(address, size);
  149. }
  150. VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
  151. u32 masked_permissions =
  152. static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute);
  153. return static_cast<VMAPermission>(masked_permissions);
  154. };
  155. u8* SharedMemory::GetPointer(u32 offset) {
  156. return backing_block->data() + backing_block_offset + offset;
  157. }
  158. } // namespace