diff options
| author | 2016-02-03 13:18:26 -0800 | |
|---|---|---|
| committer | 2016-03-30 18:31:49 -0700 | |
| commit | abe5c6efec7b05512a8fbf959feb7612c9fe56a0 (patch) | |
| tree | d78c2753234f2eff8db181e34f7333487f4e19a2 /src | |
| parent | Merge pull request #1572 from MerryMage/audio-filter (diff) | |
| download | yuzu-abe5c6efec7b05512a8fbf959feb7612c9fe56a0.tar.gz yuzu-abe5c6efec7b05512a8fbf959feb7612c9fe56a0.tar.xz yuzu-abe5c6efec7b05512a8fbf959feb7612c9fe56a0.zip | |
GSP: Return proper error codes for register writes
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/result.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 174 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_gpu.h | 2 |
3 files changed, 97 insertions, 80 deletions
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 0cb76ba1c..2d22652d9 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -24,6 +24,7 @@ enum class ErrorDescription : u32 { | |||
| 24 | FS_InvalidOpenFlags = 230, | 24 | FS_InvalidOpenFlags = 230, |
| 25 | FS_NotAFile = 250, | 25 | FS_NotAFile = 250, |
| 26 | FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive | 26 | FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive |
| 27 | OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage | ||
| 27 | FS_InvalidPath = 702, | 28 | FS_InvalidPath = 702, |
| 28 | InvalidSection = 1000, | 29 | InvalidSection = 1000, |
| 29 | TooLarge = 1001, | 30 | TooLarge = 1001, |
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 2ace2cade..16f2175ac 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -31,6 +31,13 @@ const static u32 REGS_BEGIN = 0x1EB00000; | |||
| 31 | 31 | ||
| 32 | namespace GSP_GPU { | 32 | namespace GSP_GPU { |
| 33 | 33 | ||
| 34 | const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED(ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, | ||
| 35 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02A01 | ||
| 36 | const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, | ||
| 37 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BF2 | ||
| 38 | const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, | ||
| 39 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E02BEC | ||
| 40 | |||
| 34 | /// Event triggered when GSP interrupt has been signalled | 41 | /// Event triggered when GSP interrupt has been signalled |
| 35 | Kernel::SharedPtr<Kernel::Event> g_interrupt_event; | 42 | Kernel::SharedPtr<Kernel::Event> g_interrupt_event; |
| 36 | /// GSP shared memoryings | 43 | /// GSP shared memoryings |
| @@ -59,47 +66,87 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { | |||
| 59 | } | 66 | } |
| 60 | 67 | ||
| 61 | /** | 68 | /** |
| 62 | * Checks if the parameters in a register write call are valid and logs in the case that | 69 | * Writes sequential GSP GPU hardware registers using an array of source data |
| 63 | * they are not | 70 | * |
| 64 | * @param base_address The first address in the sequence of registers that will be written | 71 | * @param base_address The address of the first register in the sequence |
| 65 | * @param size_in_bytes The number of registers that will be written | 72 | * @param size_in_bytes The number of registers to update (size of data) |
| 66 | * @return true if the parameters are valid, false otherwise | 73 | * @param data A pointer to the source data |
| 74 | * @return RESULT_SUCCESS if the parameters are valid, error code otherwise | ||
| 67 | */ | 75 | */ |
| 68 | static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { | 76 | static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { |
| 69 | // TODO: Return proper error codes | 77 | // This magic number is verified to be done by the gsp module |
| 70 | if (base_address + size_in_bytes >= 0x420000) { | 78 | const u32 max_size_in_bytes = 0x80; |
| 71 | LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", | 79 | |
| 80 | if (base_address & 3 || base_address >= 0x420000) { | ||
| 81 | LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", | ||
| 72 | base_address, size_in_bytes); | 82 | base_address, size_in_bytes); |
| 73 | return false; | 83 | return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; |
| 74 | } | 84 | } else if (size_in_bytes <= max_size_in_bytes) { |
| 85 | if (size_in_bytes & 3) { | ||
| 86 | LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); | ||
| 87 | return ERR_GSP_REGS_MISALIGNED; | ||
| 88 | } else { | ||
| 89 | while (size_in_bytes > 0) { | ||
| 90 | HW::Write<u32>(base_address + REGS_BEGIN, *data); | ||
| 91 | |||
| 92 | size_in_bytes -= 4; | ||
| 93 | ++data; | ||
| 94 | base_address += 4; | ||
| 95 | } | ||
| 96 | return RESULT_SUCCESS; | ||
| 97 | } | ||
| 75 | 98 | ||
| 76 | // size should be word-aligned | 99 | } else { |
| 77 | if ((size_in_bytes % 4) != 0) { | 100 | LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); |
| 78 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); | 101 | return ERR_GSP_REGS_INVALID_SIZE; |
| 79 | return false; | ||
| 80 | } | 102 | } |
| 81 | |||
| 82 | return true; | ||
| 83 | } | 103 | } |
| 84 | 104 | ||
| 85 | /** | 105 | /** |
| 86 | * Writes sequential GSP GPU hardware registers using an array of source data | 106 | * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. |
| 107 | * For each register, the value is updated only where the mask is high | ||
| 87 | * | 108 | * |
| 88 | * @param base_address The address of the first register in the sequence | 109 | * @param base_address The address of the first register in the sequence |
| 89 | * @param size_in_bytes The number of registers to update (size of data) | 110 | * @param size_in_bytes The number of registers to update (size of data) |
| 90 | * @param data A pointer to the source data | 111 | * @param data A pointer to the source data to use for updates |
| 112 | * @param masks A pointer to the masks | ||
| 113 | * @return RESULT_SUCCESS if the parameters are valid, error code otherwise | ||
| 91 | */ | 114 | */ |
| 92 | static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | 115 | static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { |
| 93 | // TODO: Return proper error codes | 116 | // This magic number is verified to be done by the gsp module |
| 94 | if (!CheckWriteParameters(base_address, size_in_bytes)) | 117 | const u32 max_size_in_bytes = 0x80; |
| 95 | return; | ||
| 96 | 118 | ||
| 97 | while (size_in_bytes > 0) { | 119 | if (base_address & 3 || base_address >= 0x420000) { |
| 98 | HW::Write<u32>(base_address + REGS_BEGIN, *data); | 120 | LOG_ERROR(Service_GSP, "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", |
| 121 | base_address, size_in_bytes); | ||
| 122 | return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; | ||
| 123 | } else if (size_in_bytes <= max_size_in_bytes) { | ||
| 124 | if (size_in_bytes & 3) { | ||
| 125 | LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); | ||
| 126 | return ERR_GSP_REGS_MISALIGNED; | ||
| 127 | } else { | ||
| 128 | while (size_in_bytes > 0) { | ||
| 129 | const u32 reg_address = base_address + REGS_BEGIN; | ||
| 130 | |||
| 131 | u32 reg_value; | ||
| 132 | HW::Read<u32>(reg_value, reg_address); | ||
| 133 | |||
| 134 | // Update the current value of the register only for set mask bits | ||
| 135 | reg_value = (reg_value & ~*masks) | (*data | *masks); | ||
| 136 | |||
| 137 | HW::Write<u32>(reg_address, reg_value); | ||
| 138 | |||
| 139 | size_in_bytes -= 4; | ||
| 140 | ++data; | ||
| 141 | ++masks; | ||
| 142 | base_address += 4; | ||
| 143 | } | ||
| 144 | return RESULT_SUCCESS; | ||
| 145 | } | ||
| 99 | 146 | ||
| 100 | size_in_bytes -= 4; | 147 | } else { |
| 101 | ++data; | 148 | LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); |
| 102 | base_address += 4; | 149 | return ERR_GSP_REGS_INVALID_SIZE; |
| 103 | } | 150 | } |
| 104 | } | 151 | } |
| 105 | 152 | ||
| @@ -120,39 +167,7 @@ static void WriteHWRegs(Service::Interface* self) { | |||
| 120 | 167 | ||
| 121 | u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); | 168 | u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); |
| 122 | 169 | ||
| 123 | WriteHWRegs(reg_addr, size, src); | 170 | cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw; |
| 124 | } | ||
| 125 | |||
| 126 | /** | ||
| 127 | * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. | ||
| 128 | * For each register, the value is updated only where the mask is high | ||
| 129 | * | ||
| 130 | * @param base_address The address of the first register in the sequence | ||
| 131 | * @param size_in_bytes The number of registers to update (size of data) | ||
| 132 | * @param data A pointer to the source data to use for updates | ||
| 133 | * @param masks A pointer to the masks | ||
| 134 | */ | ||
| 135 | static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { | ||
| 136 | // TODO: Return proper error codes | ||
| 137 | if (!CheckWriteParameters(base_address, size_in_bytes)) | ||
| 138 | return; | ||
| 139 | |||
| 140 | while (size_in_bytes > 0) { | ||
| 141 | const u32 reg_address = base_address + REGS_BEGIN; | ||
| 142 | |||
| 143 | u32 reg_value; | ||
| 144 | HW::Read<u32>(reg_value, reg_address); | ||
| 145 | |||
| 146 | // Update the current value of the register only for set mask bits | ||
| 147 | reg_value = (reg_value & ~*masks) | (*data | *masks); | ||
| 148 | |||
| 149 | HW::Write<u32>(reg_address, reg_value); | ||
| 150 | |||
| 151 | size_in_bytes -= 4; | ||
| 152 | ++data; | ||
| 153 | ++masks; | ||
| 154 | base_address += 4; | ||
| 155 | } | ||
| 156 | } | 171 | } |
| 157 | 172 | ||
| 158 | /** | 173 | /** |
| @@ -174,7 +189,7 @@ static void WriteHWRegsWithMask(Service::Interface* self) { | |||
| 174 | u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); | 189 | u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); |
| 175 | u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); | 190 | u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); |
| 176 | 191 | ||
| 177 | WriteHWRegsWithMask(reg_addr, size, src_data, mask_data); | 192 | cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw; |
| 178 | } | 193 | } |
| 179 | 194 | ||
| 180 | /// Read a GSP GPU hardware register | 195 | /// Read a GSP GPU hardware register |
| @@ -206,27 +221,27 @@ static void ReadHWRegs(Service::Interface* self) { | |||
| 206 | } | 221 | } |
| 207 | } | 222 | } |
| 208 | 223 | ||
| 209 | void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | 224 | ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { |
| 210 | u32 base_address = 0x400000; | 225 | u32 base_address = 0x400000; |
| 211 | PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); | 226 | PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); |
| 212 | PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); | 227 | PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); |
| 213 | if (info.active_fb == 0) { | 228 | if (info.active_fb == 0) { |
| 214 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), 4, | 229 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), |
| 215 | &phys_address_left); | 230 | 4, &phys_address_left); |
| 216 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), 4, | 231 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), |
| 217 | &phys_address_right); | 232 | 4, &phys_address_right); |
| 218 | } else { | 233 | } else { |
| 219 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), 4, | 234 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), |
| 220 | &phys_address_left); | 235 | 4, &phys_address_left); |
| 221 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), 4, | 236 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), |
| 222 | &phys_address_right); | 237 | 4, &phys_address_right); |
| 223 | } | 238 | } |
| 224 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), 4, | 239 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), |
| 225 | &info.stride); | 240 | 4, &info.stride); |
| 226 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), 4, | 241 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), |
| 227 | &info.format); | 242 | 4, &info.format); |
| 228 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, | 243 | WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), |
| 229 | &info.shown_fb); | 244 | 4, &info.shown_fb); |
| 230 | 245 | ||
| 231 | if (Pica::g_debug_context) | 246 | if (Pica::g_debug_context) |
| 232 | Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); | 247 | Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); |
| @@ -234,6 +249,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | |||
| 234 | if (screen_id == 0) { | 249 | if (screen_id == 0) { |
| 235 | MicroProfileFlip(); | 250 | MicroProfileFlip(); |
| 236 | } | 251 | } |
| 252 | |||
| 253 | return RESULT_SUCCESS; | ||
| 237 | } | 254 | } |
| 238 | 255 | ||
| 239 | /** | 256 | /** |
| @@ -251,9 +268,8 @@ static void SetBufferSwap(Service::Interface* self) { | |||
| 251 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 268 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 252 | u32 screen_id = cmd_buff[1]; | 269 | u32 screen_id = cmd_buff[1]; |
| 253 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; | 270 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; |
| 254 | SetBufferSwap(screen_id, *fb_info); | ||
| 255 | 271 | ||
| 256 | cmd_buff[1] = 0; // No error | 272 | cmd_buff[1] = SetBufferSwap(screen_id, *fb_info).raw; |
| 257 | } | 273 | } |
| 258 | 274 | ||
| 259 | /** | 275 | /** |
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 0e2f7a21e..55a993bb8 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h | |||
| @@ -194,7 +194,7 @@ public: | |||
| 194 | */ | 194 | */ |
| 195 | void SignalInterrupt(InterruptId interrupt_id); | 195 | void SignalInterrupt(InterruptId interrupt_id); |
| 196 | 196 | ||
| 197 | void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); | 197 | ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); |
| 198 | 198 | ||
| 199 | /** | 199 | /** |
| 200 | * Retrieves the framebuffer info stored in the GSP shared memory for the | 200 | * Retrieves the framebuffer info stored in the GSP shared memory for the |