mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	- removed HLE mem "hack" and replaced with kernel mem region
- added a helper function for getting command buffer for services - fixed bug where GSP DMA was incorrectly being done in DataSynchronizationBarrier (instead of gsp_TriggerCmdReqQueue)
This commit is contained in:
		
							parent
							
								
									f23e99bb85
								
							
						
					
					
						commit
						72622a1b5a
					
				
					 8 changed files with 100 additions and 116 deletions
				
			
		|  | @ -9,39 +9,15 @@ | |||
| 
 | ||||
| namespace HLE { | ||||
| 
 | ||||
| enum { | ||||
|     CMD_GX_REQUEST_DMA  = 0x00000000, | ||||
| }; | ||||
| 
 | ||||
| /// Data synchronization barrier
 | ||||
| u32 DataSynchronizationBarrier(u32* command_buffer) { | ||||
|     u32 command = command_buffer[0]; | ||||
| 
 | ||||
|     switch (command) { | ||||
| 
 | ||||
|     case CMD_GX_REQUEST_DMA: | ||||
|         { | ||||
|             u32* src = (u32*)Memory::GetPointer(command_buffer[1]); | ||||
|             u32* dst = (u32*)Memory::GetPointer(command_buffer[2]); | ||||
|             u32 size = command_buffer[3]; | ||||
|             memcpy(dst, src, size); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         ERROR_LOG(OSHLE, "MRC::DataSynchronizationBarrier unknown command 0x%08X", command); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
| u32 DataSynchronizationBarrier() { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Returns the coprocessor (in this case, syscore) command buffer pointer
 | ||||
| Addr GetThreadCommandBuffer() { | ||||
|     // Called on insruction: mrc p15, 0, r0, c13, c0, 3
 | ||||
|     // Returns an address in OSHLE memory for the CPU to read/write to
 | ||||
|     RETURN(CMD_BUFFER_ADDR); | ||||
|     return CMD_BUFFER_ADDR; | ||||
|     return Memory::KERNEL_MEMORY_VADDR; | ||||
| } | ||||
| 
 | ||||
| /// Call an MCR (move to coprocessor from ARM register) instruction in HLE
 | ||||
|  | @ -49,7 +25,7 @@ s32 CallMCR(u32 instruction, u32 value) { | |||
|     CoprocessorOperation operation = (CoprocessorOperation)((instruction >> 20) & 0xFF); | ||||
|     ERROR_LOG(OSHLE, "unimplemented MCR instruction=0x%08X, operation=%02X, value=%08X",  | ||||
|         instruction, operation, value); | ||||
|     return -1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Call an MRC (move to ARM register from coprocessor) instruction in HLE
 | ||||
|  | @ -59,7 +35,7 @@ s32 CallMRC(u32 instruction) { | |||
|     switch (operation) { | ||||
| 
 | ||||
|     case DATA_SYNCHRONIZATION_BARRIER: | ||||
|         return DataSynchronizationBarrier((u32*)Memory::GetPointer(PARAM(0))); | ||||
|         return DataSynchronizationBarrier(); | ||||
| 
 | ||||
|     case CALL_GET_THREAD_COMMAND_BUFFER: | ||||
|         return GetThreadCommandBuffer(); | ||||
|  | @ -68,7 +44,7 @@ s32 CallMRC(u32 instruction) { | |||
|         ERROR_LOG(OSHLE, "unimplemented MRC instruction 0x%08X", instruction); | ||||
|         break; | ||||
|     } | ||||
|     return -1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  |  | |||
|  | @ -15,49 +15,6 @@ namespace HLE { | |||
| 
 | ||||
| static std::vector<ModuleDef> g_module_db; | ||||
| 
 | ||||
| u8* g_command_buffer = NULL;    ///< Command buffer used for sharing between appcore and syscore
 | ||||
| 
 | ||||
| // Read from memory used by CTROS HLE functions
 | ||||
| template <typename T> | ||||
| inline void Read(T &var, const u32 addr) { | ||||
|     if (addr >= HLE::CMD_BUFFER_ADDR && addr < HLE::CMD_BUFFER_ADDR_END) { | ||||
|         var = *((const T*)&g_command_buffer[addr & CMD_BUFFER_MASK]); | ||||
|     } else { | ||||
|         ERROR_LOG(HLE, "unknown read from address %08X", addr); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Write to memory used by CTROS HLE functions
 | ||||
| template <typename T> | ||||
| inline void Write(u32 addr, const T data) { | ||||
|     if (addr >= HLE::CMD_BUFFER_ADDR && addr < HLE::CMD_BUFFER_ADDR_END) { | ||||
|         *(T*)&g_command_buffer[addr & CMD_BUFFER_MASK] = data; | ||||
|     } else { | ||||
|         ERROR_LOG(HLE, "unknown write to address %08X", addr); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| u8 *GetPointer(const u32 addr) { | ||||
|     if (addr >= HLE::CMD_BUFFER_ADDR && addr < HLE::CMD_BUFFER_ADDR_END) { | ||||
|         return g_command_buffer + (addr & CMD_BUFFER_MASK); | ||||
|     } else { | ||||
|         ERROR_LOG(HLE, "unknown pointer from address %08X", addr); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Explicitly instantiate template functions because we aren't defining this in the header:
 | ||||
| 
 | ||||
| template void Read<u64>(u64 &var, const u32 addr); | ||||
| template void Read<u32>(u32 &var, const u32 addr); | ||||
| template void Read<u16>(u16 &var, const u32 addr); | ||||
| template void Read<u8>(u8 &var, const u32 addr); | ||||
| 
 | ||||
| template void Write<u64>(u32 addr, const u64 data); | ||||
| template void Write<u32>(u32 addr, const u32 data); | ||||
| template void Write<u16>(u32 addr, const u16 data); | ||||
| template void Write<u8>(u32 addr, const u8 data); | ||||
| 
 | ||||
| const FunctionDef* GetSyscallInfo(u32 opcode) { | ||||
|     u32 func_num = opcode & 0xFFFFFF; // 8 bits
 | ||||
|     if (func_num > 0xFF) { | ||||
|  | @ -91,8 +48,6 @@ void RegisterAllModules() { | |||
| 
 | ||||
| void Init() { | ||||
|     Service::Init(); | ||||
| 
 | ||||
|     g_command_buffer = new u8[CMD_BUFFER_SIZE]; | ||||
|      | ||||
|     RegisterAllModules(); | ||||
| 
 | ||||
|  | @ -102,8 +57,6 @@ void Init() { | |||
| void Shutdown() { | ||||
|     Service::Shutdown(); | ||||
| 
 | ||||
|     delete g_command_buffer; | ||||
| 
 | ||||
|     g_module_db.clear(); | ||||
| 
 | ||||
|     NOTICE_LOG(HLE, "shutdown OK"); | ||||
|  |  | |||
|  | @ -17,13 +17,6 @@ | |||
| 
 | ||||
| namespace HLE { | ||||
| 
 | ||||
| enum { | ||||
|     CMD_BUFFER_ADDR     = 0xA0010000,    ///< Totally arbitrary unused address space
 | ||||
|     CMD_BUFFER_SIZE     = 0x10000, | ||||
|     CMD_BUFFER_MASK     = (CMD_BUFFER_SIZE - 1), | ||||
|     CMD_BUFFER_ADDR_END = (CMD_BUFFER_ADDR + CMD_BUFFER_SIZE), | ||||
| }; | ||||
| 
 | ||||
| typedef u32 Addr; | ||||
| typedef void (*Func)(); | ||||
| 
 | ||||
|  | @ -39,20 +32,6 @@ struct ModuleDef { | |||
|     const FunctionDef*  func_table; | ||||
| }; | ||||
| 
 | ||||
| // Read from memory used by CTROS HLE functions
 | ||||
| template <typename T> | ||||
| inline void Read(T &var, const u32 addr); | ||||
| 
 | ||||
| // Write to memory used by CTROS HLE functions
 | ||||
| template <typename T> | ||||
| inline void Write(u32 addr, const T data); | ||||
| 
 | ||||
| u8* GetPointer(const u32 Address); | ||||
| 
 | ||||
| inline const char* GetCharPointer(const u32 address) { | ||||
|     return (const char *)GetPointer(address); | ||||
| } | ||||
| 
 | ||||
| void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table); | ||||
| 
 | ||||
| void CallSyscall(u32 opcode); | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ void Initialize(Service::Interface* self) { | |||
| } | ||||
| 
 | ||||
| void GetLockHandle(Service::Interface* self) { | ||||
|     u32* cmd_buff = (u32*)HLE::GetPointer(HLE::CMD_BUFFER_ADDR + Service::kCommandHeaderOffset); | ||||
|     u32* cmd_buff = Service::GetCommandBuffer(); | ||||
|     cmd_buff[5] = 0x00000000; // TODO: This should be an actual mutex handle
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| 
 | ||||
| #include "common/log.h" | ||||
| #include "common/bit_field.h" | ||||
| 
 | ||||
| #include "core/mem_map.h" | ||||
| #include "core/hle/hle.h" | ||||
|  | @ -11,11 +12,57 @@ | |||
| 
 | ||||
| #include "core/hw/lcd.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /// GSP shared memory GX command buffer header
 | ||||
| union GX_CmdBufferHeader { | ||||
|     u32 hex; | ||||
| 
 | ||||
|     // Current command index. This index is updated by GSP module after loading the command data, 
 | ||||
|     // right before the command is processed. When this index is updated by GSP module, the total 
 | ||||
|     // commands field is decreased by one as well.
 | ||||
|     BitField<0,8,u32>   index; | ||||
|      | ||||
|     // Total commands to process, must not be value 0 when GSP module handles commands. This must be
 | ||||
|     // <=15 when writing a command to shared memory. This is incremented by the application when 
 | ||||
|     // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only 
 | ||||
|     // used if this field is value 1.
 | ||||
|     BitField<8,8,u32>  number_commands; | ||||
|      | ||||
|     // Must not be value 1. When the error-code u32 is set, this u8 is set to value 0x80.
 | ||||
|     BitField<16,8,u32> unk_0; | ||||
| 
 | ||||
|     // Bit 0 must not be set
 | ||||
|     BitField<24,8,u32> unk_1; | ||||
| }; | ||||
| 
 | ||||
| /// Gets the address of the start (header) of a command buffer in GSP shared memory
 | ||||
| static inline u32 GX_GetCmdBufferAddress(u32 thread_id) { | ||||
|     return (0x10002000 + 0x800 + (thread_id * 0x200)); | ||||
| } | ||||
| 
 | ||||
| /// Gets a pointer to the start (header) of a command buffer in GSP shared memory
 | ||||
| static inline u8* GX_GetCmdBufferPointer(u32 thread_id, u32 offset=0) { | ||||
|     return Memory::GetPointer(GX_GetCmdBufferAddress(thread_id) + offset); | ||||
| } | ||||
| 
 | ||||
| /// Finishes execution of a GSP command
 | ||||
| void GX_FinishCommand(u32 thread_id) { | ||||
|     GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(thread_id); | ||||
|     header->number_commands = header->number_commands - 1; | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Namespace GSP_GPU
 | ||||
| 
 | ||||
| namespace GSP_GPU { | ||||
| 
 | ||||
| u32 g_thread_id = 0; | ||||
| 
 | ||||
| enum { | ||||
|     CMD_GX_REQUEST_DMA  = 0x00000000, | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
|     REG_FRAMEBUFFER_1   = 0x00400468, | ||||
|     REG_FRAMEBUFFER_2   = 0x00400494, | ||||
|  | @ -26,7 +73,7 @@ void ReadHWRegs(Service::Interface* self) { | |||
|     static const u32 framebuffer_1[] = {LCD::PADDR_VRAM_TOP_LEFT_FRAME1, LCD::PADDR_VRAM_TOP_RIGHT_FRAME1}; | ||||
|     static const u32 framebuffer_2[] = {LCD::PADDR_VRAM_TOP_LEFT_FRAME2, LCD::PADDR_VRAM_TOP_RIGHT_FRAME2}; | ||||
| 
 | ||||
|     u32* cmd_buff = (u32*)HLE::GetPointer(HLE::CMD_BUFFER_ADDR + Service::kCommandHeaderOffset); | ||||
|     u32* cmd_buff = Service::GetCommandBuffer(); | ||||
|     u32 reg_addr = cmd_buff[1]; | ||||
|     u32 size = cmd_buff[2]; | ||||
|     u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]); | ||||
|  | @ -50,18 +97,37 @@ void ReadHWRegs(Service::Interface* self) { | |||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         ERROR_LOG(OSHLE, "GSP_GPU::ReadHWRegs unknown register read at address %08X", reg_addr); | ||||
|         ERROR_LOG(GSP, "ReadHWRegs unknown register read at address %08X", reg_addr); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void RegisterInterruptRelayQueue(Service::Interface* self) { | ||||
|     u32* cmd_buff = (u32*)HLE::GetPointer(HLE::CMD_BUFFER_ADDR + Service::kCommandHeaderOffset); | ||||
|     u32* cmd_buff = Service::GetCommandBuffer(); | ||||
|     u32 flags = cmd_buff[1]; | ||||
|     u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling
 | ||||
|      | ||||
|     cmd_buff[2] = g_thread_id;          // ThreadID
 | ||||
|     cmd_buff[4] = self->NewHandle(); | ||||
| } | ||||
| 
 | ||||
|     return; | ||||
| /// This triggers handling of the GX command written to the command buffer in shared memory.
 | ||||
| void TriggerCmdReqQueue(Service::Interface* self) { | ||||
|     GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(g_thread_id); | ||||
|     u32* cmd_buff = (u32*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20)); | ||||
| 
 | ||||
|     switch (cmd_buff[0]) { | ||||
| 
 | ||||
|     // GX request DMA - typically used for copying memory from GSP heap to VRAM
 | ||||
|     case CMD_GX_REQUEST_DMA: | ||||
|         memcpy(Memory::GetPointer(cmd_buff[2]), Memory::GetPointer(cmd_buff[1]), cmd_buff[3]); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         ERROR_LOG(GSP, "TriggerCmdReqQueue unknown command 0x%08X", cmd_buff[0]); | ||||
|     } | ||||
|      | ||||
|     GX_FinishCommand(g_thread_id); | ||||
| } | ||||
| 
 | ||||
| const Interface::FunctionInfo FunctionTable[] = { | ||||
|  | @ -76,7 +142,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
|     {0x00090082, NULL,                          "InvalidateDataCache"}, | ||||
|     {0x000A0044, NULL,                          "RegisterInterruptEvents"}, | ||||
|     {0x000B0040, NULL,                          "SetLcdForceBlack"}, | ||||
|     {0x000C0000, NULL,                          "TriggerCmdReqQueue"}, | ||||
|     {0x000C0000, TriggerCmdReqQueue,            "TriggerCmdReqQueue"}, | ||||
|     {0x000D0140, NULL,                          "SetDisplayTransfer"}, | ||||
|     {0x000E0180, NULL,                          "SetTextureCopy"}, | ||||
|     {0x000F0200, NULL,                          "SetMemoryFill"}, | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/mem_map.h" | ||||
| #include "core/hle/syscall.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -22,6 +23,15 @@ typedef s32 NativeUID;                          ///< Native handle for a service | |||
| static const int kMaxPortSize           = 0x08; ///< Maximum size of a port name (8 characters)
 | ||||
| static const int kCommandHeaderOffset   = 0x80; ///< Offset into command buffer of header
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns a pointer to the command buffer in kernel memory | ||||
|  * @param offset Optional offset into command buffer | ||||
|  * @return Pointer to command buffer | ||||
|  */ | ||||
| inline static u32* GetCommandBuffer(const int offset=0) { | ||||
|     return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); | ||||
| } | ||||
| 
 | ||||
| class Manager; | ||||
| 
 | ||||
| /// Interface to a CTROS service
 | ||||
|  | @ -81,7 +91,7 @@ public: | |||
|      * @return Return result of svcSendSyncRequest passed back to user app | ||||
|      */ | ||||
|     Syscall::Result Sync() { | ||||
|         u32* cmd_buff = (u32*)HLE::GetPointer(HLE::CMD_BUFFER_ADDR + kCommandHeaderOffset); | ||||
|         u32* cmd_buff = GetCommandBuffer(); | ||||
|         auto itr = m_functions.find(cmd_buff[0]); | ||||
| 
 | ||||
|         if (itr == m_functions.end()) { | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ void Initialize(Service::Interface* self) { | |||
| 
 | ||||
| void GetServiceHandle(Service::Interface* self) { | ||||
|     Syscall::Result res = 0; | ||||
|     u32* cmd_buff = (u32*)HLE::GetPointer(HLE::CMD_BUFFER_ADDR + Service::kCommandHeaderOffset); | ||||
|     u32* cmd_buff = Service::GetCommandBuffer(); | ||||
| 
 | ||||
|     std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); | ||||
|     Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | ||||
|  |  | |||
|  | @ -48,11 +48,9 @@ inline void _Read(T &var, const u32 addr) { | |||
| 
 | ||||
|     const u32 vaddr = _VirtualAddress(addr); | ||||
| 
 | ||||
|     // Memory allocated for HLE use that can be addressed from the emulated application
 | ||||
|     // The primary use of this is sharing a commandbuffer between the HLE OS (syscore) and the LLE
 | ||||
|     // core running the user application (appcore)
 | ||||
|     if (vaddr >= HLE::CMD_BUFFER_ADDR && vaddr < HLE::CMD_BUFFER_ADDR_END) { | ||||
|         HLE::Read<T>(var, vaddr); | ||||
|     // Kernel memory command buffer
 | ||||
|     if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { | ||||
|         var = *((const T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK]); | ||||
| 
 | ||||
|     // Hardware I/O register reads
 | ||||
|     // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
 | ||||
|  | @ -92,11 +90,9 @@ template <typename T> | |||
| inline void _Write(u32 addr, const T data) { | ||||
|     u32 vaddr = _VirtualAddress(addr); | ||||
|      | ||||
|     // Memory allocated for HLE use that can be addressed from the emulated application
 | ||||
|     // The primary use of this is sharing a commandbuffer between the HLE OS (syscore) and the LLE
 | ||||
|     // core running the user application (appcore)
 | ||||
|     if (vaddr >= HLE::CMD_BUFFER_ADDR && vaddr < HLE::CMD_BUFFER_ADDR_END) { | ||||
|         HLE::Write<T>(vaddr, data); | ||||
|     // Kernel memory command buffer
 | ||||
|     if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { | ||||
|         *(T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK] = data; | ||||
| 
 | ||||
|     // Hardware I/O register writes
 | ||||
|     // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
 | ||||
|  | @ -140,8 +136,12 @@ inline void _Write(u32 addr, const T data) { | |||
| u8 *GetPointer(const u32 addr) { | ||||
|     const u32 vaddr = _VirtualAddress(addr); | ||||
| 
 | ||||
|     // Kernel memory command buffer
 | ||||
|     if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { | ||||
|         return g_kernel_mem + (vaddr & KERNEL_MEMORY_MASK); | ||||
| 
 | ||||
|     // ExeFS:/.code is loaded here
 | ||||
|     if ((vaddr >= EXEFS_CODE_VADDR)  && (vaddr < EXEFS_CODE_VADDR_END)) { | ||||
|     } else if ((vaddr >= EXEFS_CODE_VADDR)  && (vaddr < EXEFS_CODE_VADDR_END)) { | ||||
|         return g_exefs_code + (vaddr & EXEFS_CODE_MASK); | ||||
| 
 | ||||
|     // FCRAM - GSP heap
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue