From 50458b2472cf106b2fae9945867cf1e740ee6a80 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Thu, 7 Mar 2024 20:55:54 -0300
Subject: [PATCH] LightningJit: Disable some cache ops and CTR_EL0 access on
 Windows Arm (#6326)

* LightningJit: Disable some cache ops and CTR_EL0 access on Windows Arm

* Format whitespace

* Delete unused code

* Fix typo

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
---
 .../LightningJit/Arm64/InstName.cs            |  1 +
 .../LightningJit/Arm64/SysUtils.cs            | 48 +++++++++++++++++++
 .../Arm64/Target/Arm64/Decoder.cs             |  7 ++-
 .../Arm64/Target/Arm64/InstEmitMemory.cs      |  8 ++++
 .../Arm64/Target/Arm64/InstEmitSystem.cs      | 15 ++++--
 5 files changed, 74 insertions(+), 5 deletions(-)
 create mode 100644 src/Ryujinx.Cpu/LightningJit/Arm64/SysUtils.cs

diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs
index 58d78ae6e..365640645 100644
--- a/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs
+++ b/src/Ryujinx.Cpu/LightningJit/Arm64/InstName.cs
@@ -1106,6 +1106,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64
                 case InstName.Mrs:
                 case InstName.MsrImm:
                 case InstName.MsrReg:
+                case InstName.Sysl:
                     return true;
             }
 
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/SysUtils.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/SysUtils.cs
new file mode 100644
index 000000000..69689a391
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Arm64/SysUtils.cs
@@ -0,0 +1,48 @@
+using System.Diagnostics;
+
+namespace Ryujinx.Cpu.LightningJit.Arm64
+{
+    static class SysUtils
+    {
+        public static (uint, uint, uint, uint) UnpackOp1CRnCRmOp2(uint encoding)
+        {
+            uint op1 = (encoding >> 16) & 7;
+            uint crn = (encoding >> 12) & 0xf;
+            uint crm = (encoding >> 8) & 0xf;
+            uint op2 = (encoding >> 5) & 7;
+
+            return (op1, crn, crm, op2);
+        }
+
+        public static bool IsCacheInstEl0(uint encoding)
+        {
+            (uint op1, uint crn, uint crm, uint op2) = UnpackOp1CRnCRmOp2(encoding);
+
+            return ((op1 << 11) | (crn << 7) | (crm << 3) | op2) switch
+            {
+                0b011_0111_0100_001 => true, // DC ZVA
+                0b011_0111_1010_001 => true, // DC CVAC
+                0b011_0111_1100_001 => true, // DC CVAP
+                0b011_0111_1011_001 => true, // DC CVAU
+                0b011_0111_1110_001 => true, // DC CIVAC
+                0b011_0111_0101_001 => true, // IC IVAU
+                _ => false,
+            };
+        }
+
+        public static bool IsCacheInstUciTrapped(uint encoding)
+        {
+            (uint op1, uint crn, uint crm, uint op2) = UnpackOp1CRnCRmOp2(encoding);
+
+            return ((op1 << 11) | (crn << 7) | (crm << 3) | op2) switch
+            {
+                0b011_0111_1010_001 => true, // DC CVAC
+                0b011_0111_1100_001 => true, // DC CVAP
+                0b011_0111_1011_001 => true, // DC CVAU
+                0b011_0111_1110_001 => true, // DC CIVAC
+                0b011_0111_0101_001 => true, // IC IVAU
+                _ => false,
+            };
+        }
+    }
+}
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs
index e9ba8ba21..00a1758f2 100644
--- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs
+++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Decoder.cs
@@ -257,7 +257,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
 
                 (name, flags, AddressForm addressForm) = InstTable.GetInstNameAndFlags(encoding, cpuPreset.Version, cpuPreset.Features);
 
-                if (name.IsPrivileged())
+                if (name.IsPrivileged() || (name == InstName.Sys && IsPrivilegedSys(encoding)))
                 {
                     name = InstName.UdfPermUndef;
                     flags = InstFlags.None;
@@ -341,6 +341,11 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
             return new(startAddress, address, insts, !isTruncated && !name.IsException(), isTruncated, isLoopEnd);
         }
 
+        private static bool IsPrivilegedSys(uint encoding)
+        {
+            return !SysUtils.IsCacheInstEl0(encoding);
+        }
+
         private static bool IsMrsNzcv(uint encoding)
         {
             return (encoding & ~0x1fu) == 0xd53b4200u;
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs
index ece1520fd..e03d9373a 100644
--- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs
+++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitMemory.cs
@@ -13,6 +13,14 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
 
         public static void RewriteSysInstruction(int asBits, MemoryManagerType mmType, CodeWriter writer, RegisterAllocator regAlloc, uint encoding)
         {
+            // TODO: Handle IC instruction, it should invalidate the JIT cache.
+
+            if (InstEmitSystem.IsCacheInstForbidden(encoding))
+            {
+                // Current OS does not allow cache maintenance instructions from user mode, just do nothing.
+                return;
+            }
+
             int rtIndex = RegisterUtils.ExtractRt(encoding);
             if (rtIndex == RegisterUtils.ZrIndex)
             {
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs
index 3d4204fc1..82cb29d73 100644
--- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs
+++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs
@@ -69,7 +69,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
                     asm.LdrRiUn(Register((int)rd), Register(regAlloc.FixedContextRegister), NativeContextOffsets.TpidrEl0Offset);
                 }
             }
-            else if ((encoding & ~0x1f) == 0xd53b0020 && IsAppleOS()) // mrs x0, ctr_el0
+            else if ((encoding & ~0x1f) == 0xd53b0020 && IsCtrEl0AccessForbidden()) // mrs x0, ctr_el0
             {
                 uint rd = encoding & 0x1f;
 
@@ -115,7 +115,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
             {
                 return true;
             }
-            else if ((encoding & ~0x1f) == 0xd53b0020 && IsAppleOS()) // mrs x0, ctr_el0
+            else if ((encoding & ~0x1f) == 0xd53b0020 && IsCtrEl0AccessForbidden()) // mrs x0, ctr_el0
             {
                 return true;
             }
@@ -127,9 +127,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
             return false;
         }
 
-        private static bool IsAppleOS()
+        private static bool IsCtrEl0AccessForbidden()
         {
-            return OperatingSystem.IsMacOS() || OperatingSystem.IsIOS();
+            // Only Linux allows accessing CTR_EL0 from user mode.
+            return OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS();
+        }
+
+        public static bool IsCacheInstForbidden(uint encoding)
+        {
+            // Windows does not allow the cache maintenance instructions to be used from user mode.
+            return OperatingSystem.IsWindows() && SysUtils.IsCacheInstUciTrapped(encoding);
         }
 
         public static bool NeedsContextStoreLoad(InstName name)