mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	ldr_ro: create per-client storage; pass process where needed
This commit is contained in:
		
							parent
							
								
									8e4e663113
								
							
						
					
					
						commit
						9749a43cc4
					
				
					 4 changed files with 77 additions and 56 deletions
				
			
		|  | @ -40,9 +40,9 @@ static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D | |||
|     ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState, | ||||
|                ErrorLevel::Permanent); | ||||
| 
 | ||||
| static bool VerifyBufferState(VAddr buffer_ptr, u32 size) { | ||||
|     auto vma = Kernel::g_current_process->vm_manager.FindVMA(buffer_ptr); | ||||
|     return vma != Kernel::g_current_process->vm_manager.vma_map.end() && | ||||
| static bool VerifyBufferState(Kernel::Process& process, VAddr buffer_ptr, u32 size) { | ||||
|     auto vma = process.vm_manager.FindVMA(buffer_ptr); | ||||
|     return vma != process.vm_manager.vma_map.end() && | ||||
|            vma->second.base + vma->second.size >= buffer_ptr + size && | ||||
|            vma->second.permissions == Kernel::VMAPermission::ReadWrite && | ||||
|            vma->second.meminfo_state == Kernel::MemoryState::Private; | ||||
|  | @ -63,12 +63,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     if (loaded_crs != 0) { | ||||
|     auto insert_result = slots.insert({process->process_id, {}}); | ||||
|     if (!insert_result.second) { | ||||
|         LOG_ERROR(Service_LDR, "Already initialized"); | ||||
|         rb.Push(ERROR_ALREADY_INITIALIZED); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto& slot = insert_result.first->second; | ||||
| 
 | ||||
|     if (crs_size < CRO_HEADER_SIZE) { | ||||
|         LOG_ERROR(Service_LDR, "CRS is too small"); | ||||
|         rb.Push(ERROR_BUFFER_TOO_SMALL); | ||||
|  | @ -93,7 +96,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!VerifyBufferState(crs_buffer_ptr, crs_size)) { | ||||
|     if (!VerifyBufferState(*process, crs_buffer_ptr, crs_size)) { | ||||
|         LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state"); | ||||
|         rb.Push(ERROR_INVALID_MEMORY_STATE); | ||||
|         return; | ||||
|  | @ -112,7 +115,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
|         // TODO(wwylele): should be memory aliasing
 | ||||
|         std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size); | ||||
|         Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size); | ||||
|         result = Kernel::g_current_process->vm_manager | ||||
|         result = process->vm_manager | ||||
|                      .MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code) | ||||
|                      .Code(); | ||||
|         if (result.IsError()) { | ||||
|  | @ -121,15 +124,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size, | ||||
|                                                                       Kernel::VMAPermission::Read); | ||||
|         result = | ||||
|             process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); | ||||
|             rb.Push(result); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size); | ||||
|         slot.memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size); | ||||
|     } else { | ||||
|         // Do nothing if buffer_ptr == address
 | ||||
|         // TODO(wwylele): verify this behaviour. This is only seen in the web browser app,
 | ||||
|  | @ -148,9 +151,9 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     memory_synchronizer.SynchronizeOriginalMemory(); | ||||
|     slot.memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     loaded_crs = crs_address; | ||||
|     slot.loaded_crs = crs_address; | ||||
| 
 | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| } | ||||
|  | @ -204,12 +207,14 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
| 
 | ||||
|     if (loaded_crs == 0) { | ||||
|     auto find_result = slots.find(process->process_id); | ||||
|     if (find_result == slots.end()) { | ||||
|         LOG_ERROR(Service_LDR, "Not initialized"); | ||||
|         rb.Push(ERROR_NOT_INITIALIZED); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|     } | ||||
|     auto& slot = find_result->second; | ||||
| 
 | ||||
|     if (cro_size < CRO_HEADER_SIZE) { | ||||
|         LOG_ERROR(Service_LDR, "CRO too small"); | ||||
|  | @ -239,7 +244,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!VerifyBufferState(cro_buffer_ptr, cro_size)) { | ||||
|     if (!VerifyBufferState(*process, cro_buffer_ptr, cro_size)) { | ||||
|         LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state"); | ||||
|         rb.Push(ERROR_INVALID_MEMORY_STATE); | ||||
|         rb.Push<u32>(0); | ||||
|  | @ -268,7 +273,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
|         // TODO(wwylele): should be memory aliasing
 | ||||
|         std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size); | ||||
|         Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size); | ||||
|         result = Kernel::g_current_process->vm_manager | ||||
|         result = process->vm_manager | ||||
|                      .MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code) | ||||
|                      .Code(); | ||||
|         if (result.IsError()) { | ||||
|  | @ -278,17 +283,17 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size, | ||||
|                                                                       Kernel::VMAPermission::Read); | ||||
|         result = | ||||
|             process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); | ||||
|             Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|             process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|             rb.Push(result); | ||||
|             rb.Push<u32>(0); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size); | ||||
|         slot.memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size); | ||||
|     } else { | ||||
|         // Do nothing if buffer_ptr == address
 | ||||
|         // TODO(wwylele): verify this behaviour.
 | ||||
|  | @ -303,45 +308,44 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
|     result = cro.VerifyHash(cro_size, crr_address); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw); | ||||
|         Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         rb.Push(result); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size, | ||||
|     result = cro.Rebase(slot.loaded_crs, cro_size, data_segment_address, data_segment_size, | ||||
|                         bss_segment_address, bss_segment_size, false); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw); | ||||
|         Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         rb.Push(result); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     result = cro.Link(loaded_crs, link_on_load_bug_fix); | ||||
|     result = cro.Link(slot.loaded_crs, link_on_load_bug_fix); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); | ||||
|         Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         rb.Push(result); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     cro.Register(loaded_crs, auto_link); | ||||
|     cro.Register(slot.loaded_crs, auto_link); | ||||
| 
 | ||||
|     u32 fix_size = cro.Fix(fix_level); | ||||
| 
 | ||||
|     memory_synchronizer.SynchronizeOriginalMemory(); | ||||
|     slot.memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | ||||
|     if (cro_buffer_ptr != cro_address) { | ||||
|         if (fix_size != cro_size) { | ||||
|             result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size, | ||||
|                                                                       cro_size - fix_size); | ||||
|             result = process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size); | ||||
|             if (result.IsError()) { | ||||
|                 LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw); | ||||
|                 Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|                 process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|                 rb.Push(result); | ||||
|                 rb.Push<u32>(0); | ||||
|                 return; | ||||
|  | @ -349,18 +353,18 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
|         } | ||||
| 
 | ||||
|         // Changes the block size
 | ||||
|         memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); | ||||
|         slot.memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); | ||||
|     } | ||||
| 
 | ||||
|     VAddr exe_begin; | ||||
|     u32 exe_size; | ||||
|     std::tie(exe_begin, exe_size) = cro.GetExecutablePages(); | ||||
|     if (exe_begin) { | ||||
|         result = Kernel::g_current_process->vm_manager.ReprotectRange( | ||||
|             exe_begin, exe_size, Kernel::VMAPermission::ReadExecute); | ||||
|         result = process->vm_manager.ReprotectRange(exe_begin, exe_size, | ||||
|                                                     Kernel::VMAPermission::ReadExecute); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); | ||||
|             Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size); | ||||
|             process->vm_manager.UnmapRange(cro_address, fix_size); | ||||
|             rb.Push(result); | ||||
|             rb.Push<u32>(0); | ||||
|             return; | ||||
|  | @ -389,11 +393,13 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     if (loaded_crs == 0) { | ||||
|     auto find_result = slots.find(process->process_id); | ||||
|     if (find_result == slots.end()) { | ||||
|         LOG_ERROR(Service_LDR, "Not initialized"); | ||||
|         rb.Push(ERROR_NOT_INITIALIZED); | ||||
|         return; | ||||
|     } | ||||
|     auto& slot = find_result->second; | ||||
| 
 | ||||
|     if (cro_address & Memory::PAGE_MASK) { | ||||
|         LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||||
|  | @ -411,9 +417,9 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     u32 fixed_size = cro.GetFixedSize(); | ||||
| 
 | ||||
|     cro.Unregister(loaded_crs); | ||||
|     cro.Unregister(slot.loaded_crs); | ||||
| 
 | ||||
|     ResultCode result = cro.Unlink(loaded_crs); | ||||
|     ResultCode result = cro.Unlink(slot.loaded_crs); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); | ||||
|         rb.Push(result); | ||||
|  | @ -433,15 +439,15 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     cro.Unrebase(false); | ||||
| 
 | ||||
|     memory_synchronizer.SynchronizeOriginalMemory(); | ||||
|     slot.memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | ||||
|     if (cro_address != cro_buffer_ptr) { | ||||
|         result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size); | ||||
|         result = process->vm_manager.UnmapRange(cro_address, fixed_size); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw); | ||||
|         } | ||||
|         memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); | ||||
|         slot.memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); | ||||
|     } | ||||
| 
 | ||||
|     Core::CPU().InvalidateCacheRange(cro_address, fixed_size); | ||||
|  | @ -460,11 +466,13 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     if (loaded_crs == 0) { | ||||
|     auto find_result = slots.find(process->process_id); | ||||
|     if (find_result == slots.end()) { | ||||
|         LOG_ERROR(Service_LDR, "Not initialized"); | ||||
|         rb.Push(ERROR_NOT_INITIALIZED); | ||||
|         return; | ||||
|     } | ||||
|     auto& slot = find_result->second; | ||||
| 
 | ||||
|     if (cro_address & Memory::PAGE_MASK) { | ||||
|         LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||||
|  | @ -480,12 +488,12 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data()); | ||||
| 
 | ||||
|     ResultCode result = cro.Link(loaded_crs, false); | ||||
|     ResultCode result = cro.Link(slot.loaded_crs, false); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); | ||||
|     } | ||||
| 
 | ||||
|     memory_synchronizer.SynchronizeOriginalMemory(); | ||||
|     slot.memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | @ -501,11 +509,13 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     if (loaded_crs == 0) { | ||||
|     auto find_result = slots.find(process->process_id); | ||||
|     if (find_result == slots.end()) { | ||||
|         LOG_ERROR(Service_LDR, "Not initialized"); | ||||
|         rb.Push(ERROR_NOT_INITIALIZED); | ||||
|         return; | ||||
|     } | ||||
|     auto& slot = find_result->second; | ||||
| 
 | ||||
|     if (cro_address & Memory::PAGE_MASK) { | ||||
|         LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||||
|  | @ -521,12 +531,12 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data()); | ||||
| 
 | ||||
|     ResultCode result = cro.Unlink(loaded_crs); | ||||
|     ResultCode result = cro.Unlink(slot.loaded_crs); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); | ||||
|     } | ||||
| 
 | ||||
|     memory_synchronizer.SynchronizeOriginalMemory(); | ||||
|     slot.memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | @ -540,29 +550,31 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     if (loaded_crs == 0) { | ||||
|     auto find_result = slots.find(process->process_id); | ||||
|     if (find_result == slots.end()) { | ||||
|         LOG_ERROR(Service_LDR, "Not initialized"); | ||||
|         rb.Push(ERROR_NOT_INITIALIZED); | ||||
|         return; | ||||
|     } | ||||
|     auto& slot = find_result->second; | ||||
| 
 | ||||
|     CROHelper crs(loaded_crs); | ||||
|     CROHelper crs(slot.loaded_crs); | ||||
|     crs.Unrebase(true); | ||||
| 
 | ||||
|     memory_synchronizer.SynchronizeOriginalMemory(); | ||||
|     slot.memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     ResultCode result = RESULT_SUCCESS; | ||||
| 
 | ||||
|     // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | ||||
|     if (loaded_crs != crs_buffer_ptr) { | ||||
|         result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize()); | ||||
|     if (slot.loaded_crs != crs_buffer_ptr) { | ||||
|         result = process->vm_manager.UnmapRange(slot.loaded_crs, crs.GetFileSize()); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw); | ||||
|         } | ||||
|         memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr); | ||||
|         slot.memory_synchronizer.RemoveMemoryBlock(slot.loaded_crs, crs_buffer_ptr); | ||||
|     } | ||||
| 
 | ||||
|     loaded_crs = 0; | ||||
|     slots.erase(find_result); | ||||
|     rb.Push(result); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <unordered_map> | ||||
| #include "core/hle/service/ldr_ro/memory_synchronizer.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
|  | @ -144,9 +145,12 @@ private: | |||
|      */ | ||||
|     void Shutdown(Kernel::HLERequestContext& self); | ||||
| 
 | ||||
|     struct ClientSlot { | ||||
|         MemorySynchronizer memory_synchronizer; | ||||
|         VAddr loaded_crs = 0; ///< the virtual address of the static module
 | ||||
|     }; | ||||
|     std::unordered_map<u32, ClientSlot> slots; | ||||
| }; | ||||
| 
 | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager); | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| #include <algorithm> | ||||
| #include "common/assert.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/service/ldr_ro/memory_synchronizer.h" | ||||
| 
 | ||||
| namespace Service { | ||||
|  | @ -32,9 +33,9 @@ void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) { | |||
|     memory_blocks.erase(FindMemoryBlock(mapping, original)); | ||||
| } | ||||
| 
 | ||||
| void MemorySynchronizer::SynchronizeOriginalMemory() { | ||||
| void MemorySynchronizer::SynchronizeOriginalMemory(Kernel::Process& process) { | ||||
|     for (auto& block : memory_blocks) { | ||||
|         Memory::CopyBlock(block.original, block.mapping, block.size); | ||||
|         Memory::CopyBlock(process, block.original, block.mapping, block.size); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,10 @@ | |||
| #include <vector> | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| class Process; | ||||
| } | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace LDR { | ||||
| 
 | ||||
|  | @ -24,7 +28,7 @@ public: | |||
|     void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size); | ||||
|     void RemoveMemoryBlock(VAddr mapping, VAddr original); | ||||
| 
 | ||||
|     void SynchronizeOriginalMemory(); | ||||
|     void SynchronizeOriginalMemory(Kernel::Process& process); | ||||
| 
 | ||||
| private: | ||||
|     struct MemoryBlock { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue