GSP: Return proper error codes for register writes
This commit is contained in:
		| @@ -24,6 +24,7 @@ enum class ErrorDescription : u32 { | |||||||
|     FS_InvalidOpenFlags = 230, |     FS_InvalidOpenFlags = 230, | ||||||
|     FS_NotAFile = 250, |     FS_NotAFile = 250, | ||||||
|     FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive |     FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive | ||||||
|  |     OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage | ||||||
|     FS_InvalidPath = 702, |     FS_InvalidPath = 702, | ||||||
|     InvalidSection = 1000, |     InvalidSection = 1000, | ||||||
|     TooLarge = 1001, |     TooLarge = 1001, | ||||||
|   | |||||||
| @@ -31,6 +31,13 @@ const static u32 REGS_BEGIN = 0x1EB00000; | |||||||
|  |  | ||||||
| namespace GSP_GPU { | namespace GSP_GPU { | ||||||
|  |  | ||||||
|  | const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED(ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, | ||||||
|  |     ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02A01 | ||||||
|  | const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, | ||||||
|  |     ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2 | ||||||
|  | const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, | ||||||
|  |     ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC | ||||||
|  |  | ||||||
| /// Event triggered when GSP interrupt has been signalled | /// Event triggered when GSP interrupt has been signalled | ||||||
| Kernel::SharedPtr<Kernel::Event> g_interrupt_event; | Kernel::SharedPtr<Kernel::Event> g_interrupt_event; | ||||||
| /// GSP shared memoryings | /// GSP shared memoryings | ||||||
| @@ -58,48 +65,88 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { | |||||||
|     return reinterpret_cast<InterruptRelayQueue*>(ptr); |     return reinterpret_cast<InterruptRelayQueue*>(ptr); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Checks if the parameters in a register write call are valid and logs in the case that |  | ||||||
|  * they are not |  | ||||||
|  * @param base_address The first address in the sequence of registers that will be written |  | ||||||
|  * @param size_in_bytes The number of registers that will be written |  | ||||||
|  * @return true if the parameters are valid, false otherwise |  | ||||||
|  */ |  | ||||||
| static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { |  | ||||||
|     // TODO: Return proper error codes |  | ||||||
|     if (base_address + size_in_bytes >= 0x420000) { |  | ||||||
|         LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", |  | ||||||
|                   base_address, size_in_bytes); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // size should be word-aligned |  | ||||||
|     if ((size_in_bytes % 4) != 0) { |  | ||||||
|         LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Writes sequential GSP GPU hardware registers using an array of source data |  * Writes sequential GSP GPU hardware registers using an array of source data | ||||||
|  * |  * | ||||||
|  * @param base_address The address of the first register in the sequence |  * @param base_address The address of the first register in the sequence | ||||||
|  * @param size_in_bytes The number of registers to update (size of data) |  * @param size_in_bytes The number of registers to update (size of data) | ||||||
|  * @param data A pointer to the source data |  * @param data A pointer to the source data | ||||||
|  |  * @return RESULT_SUCCESS if the parameters are valid, error code otherwise | ||||||
|  */ |  */ | ||||||
| static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | ||||||
|     // TODO: Return proper error codes |     // This magic number is verified to be done by the gsp module | ||||||
|     if (!CheckWriteParameters(base_address, size_in_bytes)) |     const u32 max_size_in_bytes = 0x80; | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     while (size_in_bytes > 0) { |     if (base_address & 3 || base_address >= 0x420000) { | ||||||
|         HW::Write<u32>(base_address + REGS_BEGIN, *data); |         LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", | ||||||
|  |                   base_address, size_in_bytes); | ||||||
|  |         return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; | ||||||
|  |     } else if (size_in_bytes <= max_size_in_bytes) { | ||||||
|  |         if (size_in_bytes & 3) { | ||||||
|  |             LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); | ||||||
|  |             return ERR_GSP_REGS_MISALIGNED; | ||||||
|  |         } else { | ||||||
|  |             while (size_in_bytes > 0) { | ||||||
|  |                 HW::Write<u32>(base_address + REGS_BEGIN, *data); | ||||||
|  |  | ||||||
|         size_in_bytes -= 4; |                 size_in_bytes -= 4; | ||||||
|         ++data; |                 ++data; | ||||||
|         base_address += 4; |                 base_address += 4; | ||||||
|  |             } | ||||||
|  |             return RESULT_SUCCESS; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } else { | ||||||
|  |         LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); | ||||||
|  |         return ERR_GSP_REGS_INVALID_SIZE; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. | ||||||
|  |  * For each register, the value is updated only where the mask is high | ||||||
|  |  * | ||||||
|  |  * @param base_address The address of the first register in the sequence | ||||||
|  |  * @param size_in_bytes The number of registers to update (size of data) | ||||||
|  |  * @param data A pointer to the source data to use for updates | ||||||
|  |  * @param masks A pointer to the masks | ||||||
|  |  * @return RESULT_SUCCESS if the parameters are valid, error code otherwise | ||||||
|  |  */ | ||||||
|  | static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { | ||||||
|  |     // This magic number is verified to be done by the gsp module | ||||||
|  |     const u32 max_size_in_bytes = 0x80; | ||||||
|  |  | ||||||
|  |     if (base_address & 3 || base_address >= 0x420000) { | ||||||
|  |         LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", | ||||||
|  |                   base_address, size_in_bytes); | ||||||
|  |         return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; | ||||||
|  |     } else if (size_in_bytes <= max_size_in_bytes) { | ||||||
|  |         if (size_in_bytes & 3) { | ||||||
|  |             LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); | ||||||
|  |             return ERR_GSP_REGS_MISALIGNED; | ||||||
|  |         } else { | ||||||
|  |             while (size_in_bytes > 0) { | ||||||
|  |                 const u32 reg_address = base_address + REGS_BEGIN; | ||||||
|  |  | ||||||
|  |                 u32 reg_value; | ||||||
|  |                 HW::Read<u32>(reg_value, reg_address); | ||||||
|  |  | ||||||
|  |                 // Update the current value of the register only for set mask bits | ||||||
|  |                 reg_value = (reg_value & ~*masks) | (*data | *masks); | ||||||
|  |  | ||||||
|  |                 HW::Write<u32>(reg_address, reg_value); | ||||||
|  |  | ||||||
|  |                 size_in_bytes -= 4; | ||||||
|  |                 ++data; | ||||||
|  |                 ++masks; | ||||||
|  |                 base_address += 4; | ||||||
|  |             } | ||||||
|  |             return RESULT_SUCCESS; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } else { | ||||||
|  |         LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); | ||||||
|  |         return ERR_GSP_REGS_INVALID_SIZE; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -120,39 +167,7 @@ static void WriteHWRegs(Service::Interface* self) { | |||||||
|  |  | ||||||
|     u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); |     u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); | ||||||
|  |  | ||||||
|     WriteHWRegs(reg_addr, size, src); |     cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw; | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. |  | ||||||
|  * For each register, the value is updated only where the mask is high |  | ||||||
|  * |  | ||||||
|  * @param base_address The address of the first register in the sequence |  | ||||||
|  * @param size_in_bytes The number of registers to update (size of data) |  | ||||||
|  * @param data A pointer to the source data to use for updates |  | ||||||
|  * @param masks A pointer to the masks |  | ||||||
|  */ |  | ||||||
| static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { |  | ||||||
|     // TODO: Return proper error codes |  | ||||||
|     if (!CheckWriteParameters(base_address, size_in_bytes)) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     while (size_in_bytes > 0) { |  | ||||||
|         const u32 reg_address = base_address + REGS_BEGIN; |  | ||||||
|  |  | ||||||
|         u32 reg_value; |  | ||||||
|         HW::Read<u32>(reg_value, reg_address); |  | ||||||
|  |  | ||||||
|         // Update the current value of the register only for set mask bits |  | ||||||
|         reg_value = (reg_value & ~*masks) | (*data | *masks); |  | ||||||
|  |  | ||||||
|         HW::Write<u32>(reg_address, reg_value); |  | ||||||
|  |  | ||||||
|         size_in_bytes -= 4; |  | ||||||
|         ++data; |  | ||||||
|         ++masks; |  | ||||||
|         base_address += 4; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -174,7 +189,7 @@ static void WriteHWRegsWithMask(Service::Interface* self) { | |||||||
|     u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); |     u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); | ||||||
|     u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); |     u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); | ||||||
|  |  | ||||||
|     WriteHWRegsWithMask(reg_addr, size, src_data, mask_data); |     cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Read a GSP GPU hardware register | /// Read a GSP GPU hardware register | ||||||
| @@ -206,27 +221,27 @@ static void ReadHWRegs(Service::Interface* self) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | ||||||
|     u32 base_address = 0x400000; |     u32 base_address = 0x400000; | ||||||
|     PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); |     PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); | ||||||
|     PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); |     PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); | ||||||
|     if (info.active_fb == 0) { |     if (info.active_fb == 0) { | ||||||
|         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), 4, |         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), | ||||||
|                 &phys_address_left); |                     4, &phys_address_left); | ||||||
|         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), 4, |         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), | ||||||
|                 &phys_address_right); |                     4, &phys_address_right); | ||||||
|     } else { |     } else { | ||||||
|         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), 4, |         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), | ||||||
|                 &phys_address_left); |                     4, &phys_address_left); | ||||||
|         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), 4, |         WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), | ||||||
|                 &phys_address_right); |                     4, &phys_address_right); | ||||||
|     } |     } | ||||||
|     WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), 4, |     WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), | ||||||
|             &info.stride); |                 4, &info.stride); | ||||||
|     WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), 4, |     WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), | ||||||
|             &info.format); |                 4, &info.format); | ||||||
|     WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, |     WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), | ||||||
|             &info.shown_fb); |                 4, &info.shown_fb); | ||||||
|  |  | ||||||
|     if (Pica::g_debug_context) |     if (Pica::g_debug_context) | ||||||
|         Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); |         Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); | ||||||
| @@ -234,6 +249,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | |||||||
|     if (screen_id == 0) { |     if (screen_id == 0) { | ||||||
|         MicroProfileFlip(); |         MicroProfileFlip(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -251,9 +268,8 @@ static void SetBufferSwap(Service::Interface* self) { | |||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|     u32 screen_id = cmd_buff[1]; |     u32 screen_id = cmd_buff[1]; | ||||||
|     FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; |     FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; | ||||||
|     SetBufferSwap(screen_id, *fb_info); |  | ||||||
|  |  | ||||||
|     cmd_buff[1] = 0; // No error |     cmd_buff[1] = SetBufferSwap(screen_id, *fb_info).raw; | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -194,7 +194,7 @@ public: | |||||||
|  */ |  */ | ||||||
| void SignalInterrupt(InterruptId interrupt_id); | void SignalInterrupt(InterruptId interrupt_id); | ||||||
|  |  | ||||||
| void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); | ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Retrieves the framebuffer info stored in the GSP shared memory for the |  * Retrieves the framebuffer info stored in the GSP shared memory for the | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user