diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs index a8965d36d3..ef4d535018 100644 --- a/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/ARMeilleure/CodeGen/X86/Assembler.cs @@ -97,7 +97,7 @@ namespace ARMeilleure.CodeGen.X86 Add(X86Instruction.Cvtpd2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Cvtps2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Cvtps2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex)); - Add(X86Instruction.Cvtsd2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2c, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Cvtsd2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF2)); Add(X86Instruction.Cvtsd2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); Add(X86Instruction.Cvtsi2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); Add(X86Instruction.Cvtsi2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF3)); diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index f56f91b6e0..09918b9f91 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -265,7 +265,21 @@ namespace ARMeilleure.CodeGen.X86 Debug.Assert(dest.Type.IsInteger() && !source.Type.IsInteger()); - context.Assembler.WriteInstruction(info.Inst, dest, source, dest.Type); + if (intrinOp.Intrinsic == Intrinsic.X86Cvtsi2si) + { + if (dest.Type == OperandType.I32) + { + context.Assembler.Movd(dest, source); // int _mm_cvtsi128_si32 + } + else /* if (dest.Type == OperandType.I64) */ + { + context.Assembler.Movq(dest, source); // __int64 _mm_cvtsi128_si64 + } + } + else + { + context.Assembler.WriteInstruction(info.Inst, dest, source, dest.Type); + } break; } diff --git a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index 7f930d6b9c..ed81482928 100644 --- a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -26,7 +26,7 @@ namespace ARMeilleure.CodeGen.X86 public static bool ForceLegacySse { get; set; } - public static bool SupportsVexEncoding => !ForceLegacySse && SupportsAvx; + public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse; static HardwareCapabilities() { diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs index e225f2542c..73fb5fd1b6 100644 --- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs +++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs @@ -37,6 +37,7 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary)); Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr)); Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary)); + Add(Intrinsic.X86Cvtsi2si, new IntrinsicInfo(X86Instruction.Movd, IntrinsicType.UnaryToGpr)); Add(Intrinsic.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary)); Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary)); Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary)); diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/ARMeilleure/Instructions/InstEmitSimdCmp.cs index 55b62001e0..2c8828ba48 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCmp.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCmp.cs @@ -322,7 +322,7 @@ namespace ARMeilleure.Instructions public static void Fcmge_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true); } @@ -334,7 +334,7 @@ namespace ARMeilleure.Instructions public static void Fcmge_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false); } @@ -346,7 +346,7 @@ namespace ARMeilleure.Instructions public static void Fcmgt_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true); } @@ -358,7 +358,7 @@ namespace ARMeilleure.Instructions public static void Fcmgt_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false); } @@ -372,7 +372,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: true); } else { @@ -384,7 +384,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: false); } else { @@ -396,7 +396,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: true); } else { @@ -408,7 +408,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: false); } else { @@ -426,7 +426,7 @@ namespace ARMeilleure.Instructions EmitFcmpOrFcmpe(context, signalNaNs: true); } - public static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs) + private static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs) { OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp; @@ -435,7 +435,7 @@ namespace ARMeilleure.Instructions context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond)); - EmitSetNzcv(context, Const(op.Nzcv)); + EmitSetNzcv(context, op.Nzcv); context.Branch(lblEnd); @@ -446,27 +446,47 @@ namespace ARMeilleure.Instructions context.MarkLabel(lblEnd); } + private static void EmitSetNzcv(ArmEmitterContext context, int nzcv) + { + Operand Extract(int value, int bit) + { + if (bit != 0) + { + value >>= bit; + } + + value &= 1; + + return Const(value); + } + + SetFlag(context, PState.VFlag, Extract(nzcv, 0)); + SetFlag(context, PState.CFlag, Extract(nzcv, 1)); + SetFlag(context, PState.ZFlag, Extract(nzcv, 2)); + SetFlag(context, PState.NFlag, Extract(nzcv, 3)); + } + private static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - const int cmpOrdered = 7; - bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false; - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2)) { Operand n = GetVec(op.Rn); Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm); + CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ; + Operand lblNaN = Label(); Operand lblEnd = Label(); if (op.Size == 0) { - Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const(cmpOrdered)); + Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered)); - Operand isOrdered = context.VectorExtract16(ordMask, 0); + Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); @@ -481,9 +501,9 @@ namespace ARMeilleure.Instructions } else /* if (op.Size == 1) */ { - Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const(cmpOrdered)); + Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered)); - Operand isOrdered = context.VectorExtract16(ordMask, 0); + Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); @@ -645,18 +665,7 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - private enum CmpCondition - { - Equal = 0, - GreaterThanOrEqual = 5, - GreaterThan = 6 - } - - private static void EmitCmpSseOrSse2OpF( - ArmEmitterContext context, - CmpCondition cond, - bool scalar, - bool isLeOrLt = false) + private static void EmitCmpSseOrSse2OpF(ArmEmitterContext context, CmpCondition cond, bool scalar) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -669,9 +678,7 @@ namespace ARMeilleure.Instructions { Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps; - Operand res = isLeOrLt - ? context.AddIntrinsic(inst, m, n, Const((int)cond)) - : context.AddIntrinsic(inst, n, m, Const((int)cond)); + Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond)); if (scalar) { @@ -688,9 +695,7 @@ namespace ARMeilleure.Instructions { Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd; - Operand res = isLeOrLt - ? context.AddIntrinsic(inst, m, n, Const((int)cond)) - : context.AddIntrinsic(inst, n, m, Const((int)cond)); + Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond)); if (scalar) { @@ -701,4 +706,4 @@ namespace ARMeilleure.Instructions } } } -} \ No newline at end of file +} diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt.cs b/ARMeilleure/Instructions/InstEmitSimdCvt.cs index d7925dea6e..462f79311e 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt.cs @@ -716,8 +716,7 @@ namespace ARMeilleure.Instructions Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64); Debug.Assert((uint)size < 2); - OperandType type = size == 0 ? OperandType.FP32 - : OperandType.FP64; + OperandType type = size == 0 ? OperandType.FP32 : OperandType.FP64; if (signed) { @@ -813,15 +812,12 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); - const int cmpGreaterThanOrEqual = 5; - const int cmpOrdered = 7; - // sizeF == ((OpCodeSimdShImm64)op).Size - 2 int sizeF = op.Size & 1; if (sizeF == 0) { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -843,7 +839,7 @@ namespace ARMeilleure.Instructions Operand mask = X86GetAllElements(context, 0x4F000000); // 2.14748365E9f (2147483648) - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, mask, Const(cmpGreaterThanOrEqual)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, mask, Const((int)CmpCondition.NotLessThan)); Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, nInt, mask2); @@ -860,7 +856,7 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -896,7 +892,7 @@ namespace ARMeilleure.Instructions Operand mask = X86GetAllElements(context, 0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808) - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, mask, Const(cmpGreaterThanOrEqual)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, mask, Const((int)CmpCondition.NotLessThan)); Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, nInt, mask2); @@ -915,16 +911,12 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); - const int cmpGreaterThanOrEqual = 5; - const int cmpGreaterThan = 6; - const int cmpOrdered = 7; - // sizeF == ((OpCodeSimdShImm)op).Size - 2 int sizeF = op.Size & 1; if (sizeF == 0) { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -942,7 +934,7 @@ namespace ARMeilleure.Instructions Operand nRnd = context.AddIntrinsic(Intrinsic.X86Roundps, nScaled, Const(X86GetRoundControl(roundMode))); - Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, context.VectorZero(), Const(cmpGreaterThan)); + Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand nRndMasked = context.AddIntrinsic(Intrinsic.X86Pand, nRnd, nRndMask); @@ -952,13 +944,13 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Subps, nRndMasked, mask); - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, res, context.VectorZero(), Const(cmpGreaterThan)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, res, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand resMasked = context.AddIntrinsic(Intrinsic.X86Pand, res, mask2); res = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, resMasked); - Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmpps, resMasked, mask, Const(cmpGreaterThanOrEqual)); + Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmpps, resMasked, mask, Const((int)CmpCondition.NotLessThan)); res = context.AddIntrinsic(Intrinsic.X86Pxor, res, mask3); res = context.AddIntrinsic(Intrinsic.X86Paddd, res, nInt); @@ -976,7 +968,7 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -994,7 +986,7 @@ namespace ARMeilleure.Instructions Operand nRnd = context.AddIntrinsic(Intrinsic.X86Roundpd, nScaled, Const(X86GetRoundControl(roundMode))); - Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, context.VectorZero(), Const(cmpGreaterThan)); + Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand nRndMasked = context.AddIntrinsic(Intrinsic.X86Pand, nRnd, nRndMask); @@ -1018,7 +1010,7 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Subpd, nRndMasked, mask); - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, res, context.VectorZero(), Const(cmpGreaterThan)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, res, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand resMasked = context.AddIntrinsic(Intrinsic.X86Pand, res, mask2); @@ -1032,7 +1024,7 @@ namespace ARMeilleure.Instructions res = EmitVectorLongCreate(context, low, high); - Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmppd, resMasked, mask, Const(cmpGreaterThanOrEqual)); + Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmppd, resMasked, mask, Const((int)CmpCondition.NotLessThan)); res = context.AddIntrinsic(Intrinsic.X86Pxor, res, mask3); res = context.AddIntrinsic(Intrinsic.X86Paddq, res, nInt); diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs index 3105c1a97f..2159579bbd 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -1065,6 +1065,21 @@ namespace ARMeilleure.Instructions } } + public enum CmpCondition + { + // Legacy Sse. + Equal = 0, // Ordered, non-signaling. + LessThan = 1, // Ordered, signaling. + LessThanOrEqual = 2, // Ordered, signaling. + NotLessThan = 5, // Unordered, signaling. + NotLessThanOrEqual = 6, // Unordered, signaling. + OrderedQ = 7, // Non-signaling. + + // Vex. + GreaterThanOrEqual = 13, // Ordered, signaling. + GreaterThan = 14, // Ordered, signaling. + OrderedS = 23 // Signaling. + } [Flags] public enum SaturatingFlags diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index 1fe29e8558..e2d3c6dbaf 100644 --- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -26,6 +26,7 @@ namespace ARMeilleure.IntermediateRepresentation X86Cvtps2pd, X86Cvtsd2si, X86Cvtsd2ss, + X86Cvtsi2si, X86Cvtss2sd, X86Divpd, X86Divps, diff --git a/ARMeilleure/Optimizations.cs b/ARMeilleure/Optimizations.cs index 0b9885dc95..28af0936c8 100644 --- a/ARMeilleure/Optimizations.cs +++ b/ARMeilleure/Optimizations.cs @@ -15,6 +15,7 @@ namespace ARMeilleure public static bool UseSse41IfAvailable { get; set; } = true; public static bool UseSse42IfAvailable { get; set; } = true; public static bool UsePopCntIfAvailable { get; set; } = true; + public static bool UseAvxIfAvailable { get; set; } = true; public static bool ForceLegacySse { @@ -29,5 +30,6 @@ namespace ARMeilleure internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41; internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42; internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt; + internal static bool UseAvx => UseAvxIfAvailable && HardwareCapabilities.SupportsAvx && !ForceLegacySse; } } \ No newline at end of file diff --git a/KEYS.md b/KEYS.md index a2867ddca2..2250cd3e27 100644 --- a/KEYS.md +++ b/KEYS.md @@ -2,103 +2,39 @@ Keys are required for decrypting most of the file formats used by the Nintendo Switch. -Keysets are stored as text files. These 3 filenames are automatically read: -`prod.keys` - Contains common keys usedy by all Switch devices. -`console.keys` - Contains console-unique keys. -`title.keys` - Contains game-specific keys. + Keysets are stored as text files. These 2 filenames are automatically read: +* `prod.keys` - Contains common keys used by all Nintendo Switch devices. +* `title.keys` - Contains game-specific keys. Ryujinx will first look for keys in `RyuFS/system`, and if it doesn't find any there it will look in `$HOME/.switch`. - -A guide to assist with dumping your own keys can be found [here](https://gist.github.com/roblabla/d8358ab058bbe3b00614740dcba4f208). - -## Common keys - -Here is a template for a key file containing the main keys Ryujinx uses to read content files. -Both `prod.keys` and `console.keys` use this format. - -``` -master_key_00 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_01 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_02 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_03 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_04 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_05 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -titlekek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -key_area_key_application_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -key_area_key_ocean_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -key_area_key_system_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -aes_kek_generation_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -aes_key_generation_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -header_kek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -header_key_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -``` +To dump your `prod.keys` and `title.keys` please follow these following steps. +1. First off learn how to boot into RCM mode and inject payloads if you haven't already. This can be done [here](https://nh-server.github.io/switch-guide/). +2. Make sure you have an SD card with the latest release of [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere/releases) inserted into your Nintendo Switch. +3. Download the latest release of [Lockpick_RCM](https://github.com/shchmue/Lockpick_RCM/releases). +4. Boot into RCM mode. +5. Inject the `Lockpick_RCM.bin` that you have downloaded at `Step 3.` using your preferred payload injector. We recommend [TegraRCMGUI](https://github.com/eliboa/TegraRcmGUI/releases) as it is easy to use and has a decent feature set. +6. Using the `Vol+/-` buttons to navigate and the `Power` button to select, select `Dump from SysNAND | Key generation: X` ("X" depends on your Nintendo Switch's firmware version) +7. The dumping process may take a while depending on how many titles you have installed. +8. After its completion press any button to return to the main menu of Lockpick_RCM. +9. Navigate to and select `Power off` if you have an SD card reader. Or you could Navigate and select `Reboot (RCM)` if you want to mount your SD card using `TegraRCMGUI > Tools > Memloader V3 > MMC - SD Card`. +10. You can find your keys in `sd:/switch/prod.keys` and `sd:/switch/title.keys` respectively. +11. Copy these files and paste them in `RyuFS/system`. +And you're done! ## Title keys -Title keys are stored in the format `rights_id,key`. +These are only used for games that are not dumped from cartridges but from games downloaded from the Nintendo eShop, these are also only used if the eShop dump does *not* have a `ticket`. If the game does have a ticket, Ryujinx will read the key directly from that ticket. + +Title keys are stored in the format `rights_id = key`. For example: ``` -01000000000100000000000000000003,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -01000000000108000000000000000003,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -01000000000108000000000000000004,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +01000000000100000000000000000003 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +01000000000108000000000000000003 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +01000000000108000000000000000004 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ``` -## Complete key list -Below is a complete list of keys that are currently recognized. -\## represents a hexadecimal number between 00 and 1F -@@ represents a hexadecimal number between 00 and 03 +## Prod keys -### Common keys - -``` -master_key_source -keyblob_mac_key_source -package2_key_source -aes_kek_generation_source -aes_key_generation_source -key_area_key_application_source -key_area_key_ocean_source -key_area_key_system_source -titlekek_source -header_kek_source -header_key_source -sd_card_kek_source -sd_card_nca_key_source -sd_card_save_key_source -retail_specific_aes_key_source -per_console_key_source -bis_kek_source -bis_key_source_@@ - -header_key -xci_header_key -eticket_rsa_kek - -master_key_## -package1_key_## -package2_key_## -titlekek_## -key_area_key_application_## -key_area_key_ocean_## -key_area_key_system_## -keyblob_key_source_## -keyblob_## -``` - -### Console-unique keys - -``` -secure_boot_key -tsec_key -device_key -bis_key_@@ - -keyblob_key_## -keyblob_mac_key_## -encrypted_keyblob_## - -sd_seed -``` +These are typically used to decrypt system files and encrypted game files. These keys get changed in about every major system update, so make sure to keep your keys up-to-date if you want to play newer games! \ No newline at end of file diff --git a/README.md b/README.md index 2af97c3cf3..9fdfadc079 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of - **Switch Keys** - Everything on the Switch is encrypted, so if you want to run anything other than homebrew, you have to dump encryption keys from your console. To get more information please take a look at our [Keys Documentation](KEYS.md) *(Outdated)*. + Everything on the Switch is encrypted, so if you want to run anything other than homebrew, you have to dump encryption keys from your console. To get more information please take a look at our [Keys Documentation](KEYS.md). - **FFmpeg Dependencies** @@ -57,7 +57,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of Homebrew is available on many websites, such as the [Switch Appstore](https://www.switchbru.com/appstore/). - A hacked Switch is needed to dump games, which you can learn how to do [here](https://nh-server.github.io/switch-guide/). Once you've hacked your Switch, you need to dump your own games with [NxDumpTool](https://github.com/DarkMatterCore/nxdumptool) to get an XCI dump or [SwitchSDTool](https://github.com/CaitSith2/SwitchSDTool) to get an NSP dump. + A hacked Nintendo Switch is needed to dump games, which you can learn how to do [here](https://nh-server.github.io/switch-guide/). Once you have hacked your Nintendo Switch, you will need to dump your own games with [NxDumpTool](https://github.com/DarkMatterCore/nxdumptool/releases) to get an XCI or NSP dump. ## Features diff --git a/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs index 4746ecc596..35fd4dcb54 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs @@ -6,18 +6,5 @@ ErrorCodeShift = 9, Success = 0, - - InvalidMemoryState = (51 << ErrorCodeShift) | ModuleId, - InvalidNro = (52 << ErrorCodeShift) | ModuleId, - InvalidNrr = (53 << ErrorCodeShift) | ModuleId, - MaxNro = (55 << ErrorCodeShift) | ModuleId, - MaxNrr = (56 << ErrorCodeShift) | ModuleId, - NroAlreadyLoaded = (57 << ErrorCodeShift) | ModuleId, - NroHashNotPresent = (54 << ErrorCodeShift) | ModuleId, - UnalignedAddress = (81 << ErrorCodeShift) | ModuleId, - BadSize = (82 << ErrorCodeShift) | ModuleId, - BadNroAddress = (84 << ErrorCodeShift) | ModuleId, - BadNrrAddress = (85 << ErrorCodeShift) | ModuleId, - BadInitialization = (87 << ErrorCodeShift) | ModuleId } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 9e22d17e77..2b9f09fa2f 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (isRead && isWrite) { - if (outputDataPosition < inputDataSize) + if (outputDataSize < inputDataSize) { arguments = null; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs index e21e437e82..ed74cc2631 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs @@ -1,10 +1,49 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types { + [StructLayout(LayoutKind.Sequential)] + struct ZbcColorArray + { + private uint element0; + private uint element1; + private uint element2; + private uint element3; + + public uint this[int index] + { + get + { + if (index == 0) + { + return element0; + } + else if (index == 1) + { + return element1; + } + else if (index == 2) + { + return element2; + } + else if (index == 2) + { + return element3; + } + + throw new IndexOutOfRangeException(); + } + } + } + [StructLayout(LayoutKind.Sequential)] struct ZbcSetTableArguments { - // TODO + public ZbcColorArray ColorDs; + public ZbcColorArray ColorL2; + public uint Depth; + public uint Format; + public uint Type; } } diff --git a/Ryujinx.HLE/HOS/Services/Loader/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs similarity index 57% rename from Ryujinx.HLE/HOS/Services/Loader/IRoInterface.cs rename to Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index 3d58594b05..0153f4dd19 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -5,19 +5,22 @@ using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Utilities; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { [Service("ldr:ro")] [Service("ro:1")] // 7.0.0+ - class IRoInterface : IpcService + class IRoInterface : IpcService, IDisposable { - private const int MaxNrr = 0x40; - private const int MaxNro = 0x40; + private const int MaxNrr = 0x40; + private const int MaxNro = 0x40; + private const int MaxMapRetries = 0x200; + private const int GuardPagesSize = 0x4000; private const uint NrrMagic = 0x3052524E; private const uint NroMagic = 0x304F524E; @@ -25,12 +28,15 @@ namespace Ryujinx.HLE.HOS.Services.Loader private List _nrrInfos; private List _nroInfos; - private bool _isInitialized; + private KProcess _owner; + + private static Random _random = new Random(); public IRoInterface(ServiceCtx context) { _nrrInfos = new List(MaxNrr); _nroInfos = new List(MaxNro); + _owner = null; } private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, long nrrAddress, long nrrSize) @@ -39,11 +45,11 @@ namespace Ryujinx.HLE.HOS.Services.Loader if (nrrSize == 0 || nrrAddress + nrrSize <= nrrAddress || (nrrSize & 0xFFF) != 0) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } else if ((nrrAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } StructReader reader = new StructReader(context.Memory, nrrAddress); @@ -55,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Loader } else if (header.NrrSize != nrrSize) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } List hashes = new List(); @@ -105,19 +111,19 @@ namespace Ryujinx.HLE.HOS.Services.Loader if (_nroInfos.Count >= MaxNro) { - return ResultCode.MaxNro; + return ResultCode.TooManyNro; } else if (nroSize == 0 || nroAddress + nroSize <= nroAddress || (nroSize & 0xFFF) != 0) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } else if (bssSize != 0 && bssAddress + bssSize <= bssAddress) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } else if ((nroAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } uint magic = context.Memory.ReadUInt32((long)nroAddress + 0x10); @@ -140,12 +146,12 @@ namespace Ryujinx.HLE.HOS.Services.Loader if (!IsNroHashPresent(nroHash)) { - return ResultCode.NroHashNotPresent; + return ResultCode.NotRegistered; } if (IsNroLoaded(nroHash)) { - return ResultCode.NroAlreadyLoaded; + return ResultCode.AlreadyLoaded; } stream.Position = 0; @@ -187,77 +193,120 @@ namespace Ryujinx.HLE.HOS.Services.Loader return ResultCode.Success; } - private ResultCode MapNro(ServiceCtx context, NroInfo info, out ulong nroMappedAddress) + private ResultCode MapNro(KProcess process, NroInfo info, out ulong nroMappedAddress) { + KMemoryManager memMgr = process.MemoryManager; + + int retryCount = 0; + nroMappedAddress = 0; - KMemoryManager memMgr = context.Process.MemoryManager; - - ulong targetAddress = memMgr.GetAddrSpaceBaseAddr(); - - while (true) + while (retryCount++ < MaxMapRetries) { - if (targetAddress + info.TotalSize >= memMgr.AddrSpaceEnd) + ResultCode result = MapCodeMemoryInProcess(process, info.NroAddress, info.NroSize, out nroMappedAddress); + + if (result != ResultCode.Success) { - return ResultCode.InvalidMemoryState; + return result; } - KMemoryInfo memInfo = memMgr.QueryMemory(targetAddress); - - if (memInfo.State == MemoryState.Unmapped && memInfo.Size >= info.TotalSize) + if (info.BssSize > 0) { - if (!memMgr.InsideHeapRegion (targetAddress, info.TotalSize) && - !memMgr.InsideAliasRegion(targetAddress, info.TotalSize)) + KernelResult bssMappingResult = memMgr.MapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); + + if (bssMappingResult == KernelResult.InvalidMemState) + { + memMgr.UnmapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); + memMgr.UnmapProcessCodeMemory(nroMappedAddress, info.NroAddress, info.NroSize); + + continue; + } + else if (bssMappingResult != KernelResult.Success) + { + memMgr.UnmapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); + memMgr.UnmapProcessCodeMemory(nroMappedAddress, info.NroAddress, info.NroSize); + + return (ResultCode)bssMappingResult; + } + } + + if (CanAddGuardRegionsInProcess(process, nroMappedAddress, info.TotalSize)) + { + return ResultCode.Success; + } + } + + return ResultCode.InsufficientAddressSpace; + } + + private bool CanAddGuardRegionsInProcess(KProcess process, ulong baseAddress, ulong size) + { + KMemoryManager memMgr = process.MemoryManager; + + KMemoryInfo memInfo = memMgr.QueryMemory(baseAddress - 1); + + if (memInfo.State == MemoryState.Unmapped && baseAddress - GuardPagesSize >= memInfo.Address) + { + memInfo = memMgr.QueryMemory(baseAddress + size); + + if (memInfo.State == MemoryState.Unmapped) + { + return baseAddress + size + GuardPagesSize <= memInfo.Address + memInfo.Size; + } + } + return false; + } + + private ResultCode MapCodeMemoryInProcess(KProcess process, ulong baseAddress, ulong size, out ulong targetAddress) + { + KMemoryManager memMgr = process.MemoryManager; + + targetAddress = 0; + + int retryCount; + + int addressSpacePageLimit = (int)((memMgr.GetAddrSpaceSize() - size) >> 12); + + for (retryCount = 0; retryCount < MaxMapRetries; retryCount++) + { + while (true) + { + targetAddress = memMgr.GetAddrSpaceBaseAddr() + (ulong)(_random.Next(addressSpacePageLimit) << 12); + + if (memMgr.InsideAddrSpace(targetAddress, size) && !memMgr.InsideHeapRegion(targetAddress, size) && !memMgr.InsideAliasRegion(targetAddress, size)) { break; } } - targetAddress += memInfo.Size; - } + KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, baseAddress, size); - KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize); - - if (result != KernelResult.Success) - { - return ResultCode.InvalidMemoryState; - } - - ulong bssTargetAddress = targetAddress + info.NroSize; - - if (info.BssSize != 0) - { - result = memMgr.MapProcessCodeMemory(bssTargetAddress, info.BssAddress, info.BssSize); - - if (result != KernelResult.Success) + if (result == KernelResult.InvalidMemState) { - memMgr.UnmapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize); - - return ResultCode.InvalidMemoryState; + continue; } - } - - result = LoadNroIntoMemory(context.Process, info.Executable, targetAddress); - - if (result != KernelResult.Success) - { - memMgr.UnmapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize); - - if (info.BssSize != 0) + else if (result != KernelResult.Success) { - memMgr.UnmapProcessCodeMemory(bssTargetAddress, info.BssAddress, info.BssSize); + return (ResultCode)result; + } + + if (!CanAddGuardRegionsInProcess(process, targetAddress, size)) + { + continue; } return ResultCode.Success; } - info.NroMappedAddress = targetAddress; - nroMappedAddress = targetAddress; + if (retryCount == MaxMapRetries) + { + return ResultCode.InsufficientAddressSpace; + } return ResultCode.Success; } - private KernelResult LoadNroIntoMemory(KProcess process, IExecutable relocatableObject, ulong baseAddress) + private KernelResult SetNroMemoryPermissions(KProcess process, IExecutable relocatableObject, ulong baseAddress) { ulong textStart = baseAddress + (ulong)relocatableObject.TextOffset; ulong roStart = baseAddress + (ulong)relocatableObject.RoOffset; @@ -304,10 +353,10 @@ namespace Ryujinx.HLE.HOS.Services.Loader } } - return ResultCode.BadNrrAddress; + return ResultCode.NotLoaded; } - private ResultCode RemoveNroInfo(ServiceCtx context, ulong nroMappedAddress) + private ResultCode RemoveNroInfo(ulong nroMappedAddress) { foreach (NroInfo info in _nroInfos) { @@ -315,49 +364,64 @@ namespace Ryujinx.HLE.HOS.Services.Loader { _nroInfos.Remove(info); - ulong textSize = (ulong)info.Executable.Text.Length; - ulong roSize = (ulong)info.Executable.Ro.Length; - ulong dataSize = (ulong)info.Executable.Data.Length; - ulong bssSize = (ulong)info.Executable.BssSize; - - KernelResult result = KernelResult.Success; - - if (info.Executable.BssSize != 0) - { - result = context.Process.MemoryManager.UnmapProcessCodeMemory( - info.NroMappedAddress + textSize + roSize + dataSize, - info.Executable.BssAddress, - bssSize); - } - - if (result == KernelResult.Success) - { - result = context.Process.MemoryManager.UnmapProcessCodeMemory( - info.NroMappedAddress + textSize + roSize, - info.Executable.SourceAddress + textSize + roSize, - dataSize); - - if (result == KernelResult.Success) - { - result = context.Process.MemoryManager.UnmapProcessCodeMemory( - info.NroMappedAddress, - info.Executable.SourceAddress, - textSize + roSize); - } - } - - return (ResultCode)result; + return UnmapNroFromInfo(info); } } - return ResultCode.BadNroAddress; + return ResultCode.NotLoaded; + } + + private ResultCode UnmapNroFromInfo(NroInfo info) + { + ulong textSize = (ulong)info.Executable.Text.Length; + ulong roSize = (ulong)info.Executable.Ro.Length; + ulong dataSize = (ulong)info.Executable.Data.Length; + ulong bssSize = (ulong)info.Executable.BssSize; + + KernelResult result = KernelResult.Success; + + if (info.Executable.BssSize != 0) + { + result = _owner.MemoryManager.UnmapProcessCodeMemory( + info.NroMappedAddress + textSize + roSize + dataSize, + info.Executable.BssAddress, + bssSize); + } + + if (result == KernelResult.Success) + { + result = _owner.MemoryManager.UnmapProcessCodeMemory( + info.NroMappedAddress + textSize + roSize, + info.Executable.SourceAddress + textSize + roSize, + dataSize); + + if (result == KernelResult.Success) + { + result = _owner.MemoryManager.UnmapProcessCodeMemory( + info.NroMappedAddress, + info.Executable.SourceAddress, + textSize + roSize); + } + } + + return (ResultCode)result; + } + + private ResultCode IsInitialized(KProcess process) + { + if (_owner != null && _owner.Pid == process.Pid) + { + return ResultCode.Success; + } + + return ResultCode.InvalidProcess; } [Command(0)] // LoadNro(u64, u64, u64, u64, u64, pid) -> u64 public ResultCode LoadNro(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); // Zero context.RequestData.ReadUInt64(); @@ -369,19 +433,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader ulong nroMappedAddress = 0; - if (_isInitialized) + if (result == ResultCode.Success) { NroInfo info; result = ParseNro(out info, context, nroHeapAddress, nroSize, bssHeapAddress, bssSize); - if (result == 0) + if (result == ResultCode.Success) { - result = MapNro(context, info, out nroMappedAddress); + result = MapNro(context.Process, info, out nroMappedAddress); - if (result == 0) + if (result == ResultCode.Success) { - _nroInfos.Add(info); + result = (ResultCode)SetNroMemoryPermissions(context.Process, info.Executable, nroMappedAddress); + + if (result == ResultCode.Success) + { + _nroInfos.Add(info); + } } } } @@ -395,21 +464,21 @@ namespace Ryujinx.HLE.HOS.Services.Loader // UnloadNro(u64, u64, pid) public ResultCode UnloadNro(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); // Zero context.RequestData.ReadUInt64(); ulong nroMappedAddress = context.RequestData.ReadUInt64(); - if (_isInitialized) + if (result == ResultCode.Success) { if ((nroMappedAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } - result = RemoveNroInfo(context, nroMappedAddress); + result = RemoveNroInfo(nroMappedAddress); } return result; @@ -419,24 +488,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader // LoadNrr(u64, u64, u64, pid) public ResultCode LoadNrr(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); - // Zero + // pid placeholder, zero context.RequestData.ReadUInt64(); long nrrAddress = context.RequestData.ReadInt64(); long nrrSize = context.RequestData.ReadInt64(); - if (_isInitialized) + if (result == ResultCode.Success) { NrrInfo info; result = ParseNrr(out info, context, nrrAddress, nrrSize); - if (result == 0) + if (result == ResultCode.Success) { if (_nrrInfos.Count >= MaxNrr) { - result = ResultCode.MaxNrr; + result = ResultCode.NotLoaded; } else { @@ -452,18 +521,18 @@ namespace Ryujinx.HLE.HOS.Services.Loader // UnloadNrr(u64, u64, pid) public ResultCode UnloadNrr(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); - // Zero + // pid placeholder, zero context.RequestData.ReadUInt64(); long nrrHeapAddress = context.RequestData.ReadInt64(); - if (_isInitialized) + if (result == ResultCode.Success) { if ((nrrHeapAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } result = RemoveNrrInfo(nrrHeapAddress); @@ -476,10 +545,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader // Initialize(u64, pid, KObject) public ResultCode Initialize(ServiceCtx context) { - // TODO: we actually ignore the pid and process handle receive, we will need to use them when we will have multi process support. - _isInitialized = true; + if (_owner != null) + { + return ResultCode.InvalidSession; + } + + _owner = context.Process; return ResultCode.Success; } + + public void Dispose() + { + foreach (NroInfo info in _nroInfos) + { + UnmapNroFromInfo(info); + } + + _nroInfos.Clear(); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs new file mode 100644 index 0000000000..ab1c6ac8c4 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.HLE.HOS.Services.Ro +{ + enum ResultCode + { + ModuleId = 22, + ErrorCodeShift = 22, + + Success = 0, + + InsufficientAddressSpace = (2 << ErrorCodeShift) | ModuleId, + AlreadyLoaded = (3 << ErrorCodeShift) | ModuleId, + InvalidNro = (4 << ErrorCodeShift) | ModuleId, + InvalidNrr = (6 << ErrorCodeShift) | ModuleId, + TooManyNro = (7 << ErrorCodeShift) | ModuleId, + TooManyNrr = (8 << ErrorCodeShift) | ModuleId, + NotAuthorized = (9 << ErrorCodeShift) | ModuleId, + + InvalidNrrType = (10 << ErrorCodeShift) | ModuleId, + + InvalidAddress = (1025 << ErrorCodeShift) | ModuleId, + InvalidSize = (1026 << ErrorCodeShift) | ModuleId, + NotLoaded = (1028 << ErrorCodeShift) | ModuleId, + NotRegistered = (1029 << ErrorCodeShift) | ModuleId, + InvalidSession = (1030 << ErrorCodeShift) | ModuleId, + InvalidProcess = (1031 << ErrorCodeShift) | ModuleId, + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Loader/Types/NroInfo.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs similarity index 96% rename from Ryujinx.HLE/HOS/Services/Loader/Types/NroInfo.cs rename to Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs index a71d4c0829..a09116272c 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/Types/NroInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.Loaders.Executables; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { class NroInfo { diff --git a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrHeader.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs similarity index 94% rename from Ryujinx.HLE/HOS/Services/Loader/Types/NrrHeader.cs rename to Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs index 1521719666..ec06feb436 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrHeader.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { [StructLayout(LayoutKind.Explicit, Size = 0x350)] unsafe struct NrrHeader diff --git a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrInfo.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs similarity index 91% rename from Ryujinx.HLE/HOS/Services/Loader/Types/NrrInfo.cs rename to Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs index 2c60360a1f..8e038fcb6b 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { class NrrInfo { diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Fence.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Fence.cs deleted file mode 100644 index 356a123227..0000000000 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Fence.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger -{ - [StructLayout(LayoutKind.Sequential, Size = 0x8)] - struct Fence - { - public int Id; - public int Value; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs index 97ad3e20d3..20e0723b88 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using Ryujinx.HLE.HOS.Services.Nv.Types; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { @@ -9,15 +10,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public int FenceCount; [FieldOffset(0x4)] - public Fence Fence0; + public NvFence Fence0; [FieldOffset(0xC)] - public Fence Fence1; + public NvFence Fence1; [FieldOffset(0x14)] - public Fence Fence2; + public NvFence Fence2; [FieldOffset(0x1C)] - public Fence Fence3; + public NvFence Fence3; } } \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj index f01e8ced5f..36310f3d2a 100644 --- a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj +++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj @@ -22,7 +22,7 @@ - + diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 143c4cec84..83ec2e9647 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -27,7 +27,7 @@ - +