From 34c2b8a55e612a95995327b8dbc5cef89d3e14aa Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 21 Mar 2017 14:58:25 +0300 Subject: [PATCH] rsx: recover from FIFO parse errors - Validate FIFO registers before access -- Validate the args ptr separate from the get ptr --- rpcs3/Emu/RSX/RSXThread.cpp | 38 ++++++++++++++++++++++++++++++++++- rpcs3/Emu/RSX/RSXThread.h | 1 + rpcs3/Emu/RSX/gcm_enums.h | 5 +++++ rpcs3/Emu/RSX/rsx_methods.cpp | 14 +++++++++++-- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index b38b9dd89d..da9aa9d799 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -403,6 +403,19 @@ namespace rsx continue; } + //Validate put and get registers + //TODO: Who should handle graphics exceptions?? + const u32 get_address = RSXIOMem.RealAddr(get); + + if (!get_address) + { + LOG_ERROR(RSX, "Invalid FIFO queue get/put registers found, get=0x%X, put=0x%X", get, put); + + invalid_command_interrupt_raised = true; + ctrl->get = put; + continue; + } + const u32 cmd = ReadIO32(get); const u32 count = (cmd >> 18) & 0x7ff; @@ -442,7 +455,20 @@ namespace rsx continue; } - auto args = vm::ptr::make((u32)RSXIOMem.RealAddr(get + 4)); + //Validate the args ptr if the command attempts to read from it + const u32 args_address = RSXIOMem.RealAddr(get + 4); + + if (!args_address && count) + { + LOG_ERROR(RSX, "Invalid FIFO queue args ptr found, get=0x%X, cmd=0x%X, count=%d", get, cmd, count); + + invalid_command_interrupt_raised = true; + ctrl->get = put; + continue; + } + + auto args = vm::ptr::make(args_address); + invalid_command_interrupt_raised = false; u32 first_cmd = (cmd & 0xfffc) >> 2; @@ -469,8 +495,18 @@ namespace rsx { method(this, reg, value); } + + if (invalid_command_interrupt_raised) + { + //Ignore processing the rest of the chain + ctrl->get = put; + break; + } } + if (invalid_command_interrupt_raised) + continue; + ctrl->get = get + (count + 1) * 4; } } diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 4627828a66..902f266cc6 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -232,6 +232,7 @@ namespace rsx public: std::set m_used_gcm_commands; + bool invalid_command_interrupt_raised = false; protected: thread(); diff --git a/rpcs3/Emu/RSX/gcm_enums.h b/rpcs3/Emu/RSX/gcm_enums.h index b74f05d3b0..286607e343 100644 --- a/rpcs3/Emu/RSX/gcm_enums.h +++ b/rpcs3/Emu/RSX/gcm_enums.h @@ -1000,7 +1000,12 @@ enum NV3089_IMAGE_IN_OFFSET = 0x0000C408 >> 2, NV3089_IMAGE_IN = 0x0000C40C >> 2, + GCM_FLIP_HEAD0 = 0X0000E920 >> 2, + GCM_FLIP_HEAD1 = 0X0000E924 >> 2, + GCM_PREPARE_DISPLAY_BUFFER_HEAD0 = 0X0000E940 >> 2, //0X940 + [HEAD << 2] + GCM_PREPARE_DISPLAY_BUFFER_HEAD1 = 0X0000E944 >> 2, GCM_SET_USER_COMMAND = 0x0000EB00 >> 2, + GCM_SET_USER_COMMAND2 = 0x0000EB04 >> 2, GCM_FLIP_COMMAND = 0x0000FEAC >> 2 }; diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index 21be2933be..5b51301aff 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -29,9 +29,12 @@ namespace rsx std::array methods{}; - [[noreturn]] void invalid_method(thread*, u32 _reg, u32 arg) + void invalid_method(thread* rsx, u32 _reg, u32 arg) { - fmt::throw_exception("Invalid RSX method 0x%x (arg=0x%x)" HERE, _reg << 2, arg); + //Don't throw, gather information and ignore broken/garbage commands + //TODO: Investigate why these commands are executed at all. (Heap corruption? Alignment padding?) + LOG_ERROR(RSX, "Invalid RSX method 0x%x (arg=0x%x)" HERE, _reg << 2, arg); + rsx->invalid_command_interrupt_raised = true; } template struct vertex_data_type_from_element_type; @@ -1244,6 +1247,10 @@ namespace rsx methods[NV3089_IMAGE_IN_OFFSET] = nullptr; methods[NV3089_IMAGE_IN] = nullptr; + //Some custom GCM methods + methods[GCM_PREPARE_DISPLAY_BUFFER_HEAD0] = nullptr; + methods[GCM_PREPARE_DISPLAY_BUFFER_HEAD1] = nullptr; + bind_array(); bind_array(); bind_array(); @@ -1333,7 +1340,10 @@ namespace rsx // custom methods bind(); + bind(); + bind(); bind(); + bind(); return true; }();