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)