1 // Copyright 2014 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <cstring>
6 #include "common/archives.h"
7 #include "common/logging/log.h"
8 #include "core/hle/kernel/errors.h"
9 #include "core/hle/kernel/memory.h"
10 #include "core/hle/kernel/shared_memory.h"
11 #include "core/memory.h"
12
13 SERIALIZE_EXPORT_IMPL(Kernel::SharedMemory)
14
15 namespace Kernel {
16
SharedMemory(KernelSystem & kernel)17 SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {}
~SharedMemory()18 SharedMemory::~SharedMemory() {
19 for (const auto& interval : holding_memory) {
20 kernel.GetMemoryRegion(MemoryRegion::SYSTEM)
21 ->Free(interval.lower(), interval.upper() - interval.lower());
22 }
23 if (base_address != 0 && owner_process != nullptr) {
24 owner_process->vm_manager.ChangeMemoryState(base_address, size, MemoryState::Locked,
25 VMAPermission::None, MemoryState::Private,
26 VMAPermission::ReadWrite);
27 }
28 }
29
CreateSharedMemory(Process * owner_process,u32 size,MemoryPermission permissions,MemoryPermission other_permissions,VAddr address,MemoryRegion region,std::string name)30 ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory(
31 Process* owner_process, u32 size, MemoryPermission permissions,
32 MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
33 auto shared_memory{std::make_shared<SharedMemory>(*this)};
34
35 shared_memory->owner_process = owner_process;
36 shared_memory->name = std::move(name);
37 shared_memory->size = size;
38 shared_memory->permissions = permissions;
39 shared_memory->other_permissions = other_permissions;
40
41 if (address == 0) {
42 // We need to allocate a block from the Linear Heap ourselves.
43 // We'll manually allocate some memory from the linear heap in the specified region.
44 auto memory_region = GetMemoryRegion(region);
45 auto offset = memory_region->LinearAllocate(size);
46
47 ASSERT_MSG(offset, "Not enough space in region to allocate shared memory!");
48
49 std::fill(memory.GetFCRAMPointer(*offset), memory.GetFCRAMPointer(*offset + size), 0);
50 shared_memory->backing_blocks = {{memory.GetFCRAMRef(*offset), size}};
51 shared_memory->holding_memory += MemoryRegionInfo::Interval(*offset, *offset + size);
52 shared_memory->linear_heap_phys_offset = *offset;
53
54 // Increase the amount of used linear heap memory for the owner process.
55 if (shared_memory->owner_process != nullptr) {
56 shared_memory->owner_process->memory_used += size;
57 }
58 } else {
59 auto& vm_manager = shared_memory->owner_process->vm_manager;
60 // The memory is already available and mapped in the owner process.
61
62 CASCADE_CODE(vm_manager.ChangeMemoryState(address, size, MemoryState::Private,
63 VMAPermission::ReadWrite, MemoryState::Locked,
64 SharedMemory::ConvertPermissions(permissions)));
65
66 auto backing_blocks = vm_manager.GetBackingBlocksForRange(address, size);
67 ASSERT(backing_blocks.Succeeded()); // should success after verifying memory state above
68 shared_memory->backing_blocks = std::move(backing_blocks).Unwrap();
69 }
70
71 shared_memory->base_address = address;
72 return MakeResult(shared_memory);
73 }
74
CreateSharedMemoryForApplet(u32 offset,u32 size,MemoryPermission permissions,MemoryPermission other_permissions,std::string name)75 std::shared_ptr<SharedMemory> KernelSystem::CreateSharedMemoryForApplet(
76 u32 offset, u32 size, MemoryPermission permissions, MemoryPermission other_permissions,
77 std::string name) {
78 auto shared_memory{std::make_shared<SharedMemory>(*this)};
79
80 // Allocate memory in heap
81 auto memory_region = GetMemoryRegion(MemoryRegion::SYSTEM);
82 auto backing_blocks = memory_region->HeapAllocate(size);
83 ASSERT_MSG(!backing_blocks.empty(), "Not enough space in region to allocate shared memory!");
84 shared_memory->holding_memory = backing_blocks;
85 shared_memory->owner_process = nullptr;
86 shared_memory->name = std::move(name);
87 shared_memory->size = size;
88 shared_memory->permissions = permissions;
89 shared_memory->other_permissions = other_permissions;
90 for (const auto& interval : backing_blocks) {
91 shared_memory->backing_blocks.push_back(
92 {memory.GetFCRAMRef(interval.lower()), interval.upper() - interval.lower()});
93 std::fill(memory.GetFCRAMPointer(interval.lower()),
94 memory.GetFCRAMPointer(interval.upper()), 0);
95 }
96 shared_memory->base_address = Memory::HEAP_VADDR + offset;
97
98 return shared_memory;
99 }
100
Map(Process & target_process,VAddr address,MemoryPermission permissions,MemoryPermission other_permissions)101 ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions,
102 MemoryPermission other_permissions) {
103
104 MemoryPermission own_other_permissions =
105 &target_process == owner_process ? this->permissions : this->other_permissions;
106
107 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
108 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
109 return ERR_INVALID_COMBINATION;
110 }
111
112 // Error out if the requested permissions don't match what the creator process allows.
113 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
114 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:08X} name={}, permissions don't match",
115 GetObjectId(), address, name);
116 return ERR_INVALID_COMBINATION;
117 }
118
119 // Heap-backed memory blocks can not be mapped with other_permissions = DontCare
120 if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
121 LOG_ERROR(Kernel, "cannot map id={}, address=0x{08X} name={}, permissions don't match",
122 GetObjectId(), address, name);
123 return ERR_INVALID_COMBINATION;
124 }
125
126 // Error out if the provided permissions are not compatible with what the creator process needs.
127 if (other_permissions != MemoryPermission::DontCare &&
128 static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
129 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:08X} name={}, permissions don't match",
130 GetObjectId(), address, name);
131 return ERR_WRONG_PERMISSION;
132 }
133
134 // TODO(Subv): Check for the Shared Device Mem flag in the creator process.
135 /*if (was_created_with_shared_device_mem && address != 0) {
136 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
137 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
138 }*/
139
140 // TODO(Subv): The same process that created a SharedMemory object
141 // can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
142
143 if (address != 0) {
144 if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
145 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:08X} name={}, invalid address",
146 GetObjectId(), address, name);
147 return ERR_INVALID_ADDRESS;
148 }
149 }
150
151 VAddr target_address = address;
152
153 if (base_address == 0 && target_address == 0) {
154 // Calculate the address at which to map the memory block.
155 // Note: even on new firmware versions, the target address is still in the old linear heap
156 // region. This exception is made to keep the shared font compatibility. See
157 // APT:GetSharedFont for detail.
158 target_address = linear_heap_phys_offset + Memory::LINEAR_HEAP_VADDR;
159 }
160
161 auto vma = target_process.vm_manager.FindVMA(target_address);
162 if (vma->second.type != VMAType::Free ||
163 vma->second.base + vma->second.size < target_address + size) {
164 LOG_ERROR(Kernel,
165 "cannot map id={}, address=0x{:08X} name={}, mapping to already allocated memory",
166 GetObjectId(), address, name);
167 return ERR_INVALID_ADDRESS_STATE;
168 }
169
170 // Map the memory block into the target process
171 VAddr interval_target = target_address;
172 for (const auto& interval : backing_blocks) {
173 auto vma = target_process.vm_manager.MapBackingMemory(interval_target, interval.first,
174 interval.second, MemoryState::Shared);
175 ASSERT(vma.Succeeded());
176 target_process.vm_manager.Reprotect(vma.Unwrap(), ConvertPermissions(permissions));
177 interval_target += interval.second;
178 }
179
180 return RESULT_SUCCESS;
181 }
182
Unmap(Process & target_process,VAddr address)183 ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
184 // TODO(Subv): Verify what happens if the application tries to unmap an address that is not
185 // mapped to a SharedMemory.
186 return target_process.vm_manager.UnmapRange(address, size);
187 }
188
ConvertPermissions(MemoryPermission permission)189 VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
190 u32 masked_permissions =
191 static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute);
192 return static_cast<VMAPermission>(masked_permissions);
193 };
194
GetPointer(u32 offset)195 u8* SharedMemory::GetPointer(u32 offset) {
196 if (backing_blocks.size() != 1) {
197 LOG_WARNING(Kernel, "Unsafe GetPointer on discontinuous SharedMemory");
198 }
199 return backing_blocks[0].first + offset;
200 }
201
GetPointer(u32 offset) const202 const u8* SharedMemory::GetPointer(u32 offset) const {
203 if (backing_blocks.size() != 1) {
204 LOG_WARNING(Kernel, "Unsafe GetPointer on discontinuous SharedMemory");
205 }
206 return backing_blocks[0].first + offset;
207 }
208
209 } // namespace Kernel
210