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