Merge branch 'master' into aot
This commit is contained in:
commit
fb41969b89
23 changed files with 394 additions and 302 deletions
|
@ -97,7 +97,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
Add(X86Instruction.Cvtpd2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
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.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.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.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.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));
|
Add(X86Instruction.Cvtsi2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF3));
|
||||||
|
|
|
@ -265,7 +265,21 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
|
|
||||||
Debug.Assert(dest.Type.IsInteger() && !source.Type.IsInteger());
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
|
|
||||||
public static bool ForceLegacySse { get; set; }
|
public static bool ForceLegacySse { get; set; }
|
||||||
|
|
||||||
public static bool SupportsVexEncoding => !ForceLegacySse && SupportsAvx;
|
public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
|
||||||
|
|
||||||
static HardwareCapabilities()
|
static HardwareCapabilities()
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,6 +37,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary));
|
Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary));
|
||||||
Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr));
|
Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr));
|
||||||
Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary));
|
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.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary));
|
||||||
|
|
|
@ -322,7 +322,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
public static void Fcmge_S(ArmEmitterContext context)
|
public static void Fcmge_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
|
||||||
}
|
}
|
||||||
|
@ -334,7 +334,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
public static void Fcmge_V(ArmEmitterContext context)
|
public static void Fcmge_V(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
|
||||||
}
|
}
|
||||||
|
@ -346,7 +346,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
public static void Fcmgt_S(ArmEmitterContext context)
|
public static void Fcmgt_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true);
|
||||||
}
|
}
|
||||||
|
@ -358,7 +358,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
public static void Fcmgt_V(ArmEmitterContext context)
|
public static void Fcmgt_V(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false);
|
||||||
}
|
}
|
||||||
|
@ -372,7 +372,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, isLeOrLt: true);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -384,7 +384,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, isLeOrLt: true);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -396,7 +396,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true, isLeOrLt: true);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -408,7 +408,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false, isLeOrLt: true);
|
EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -426,7 +426,7 @@ namespace ARMeilleure.Instructions
|
||||||
EmitFcmpOrFcmpe(context, signalNaNs: true);
|
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;
|
OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp;
|
||||||
|
|
||||||
|
@ -435,7 +435,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond));
|
context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond));
|
||||||
|
|
||||||
EmitSetNzcv(context, Const(op.Nzcv));
|
EmitSetNzcv(context, op.Nzcv);
|
||||||
|
|
||||||
context.Branch(lblEnd);
|
context.Branch(lblEnd);
|
||||||
|
|
||||||
|
@ -446,27 +446,47 @@ namespace ARMeilleure.Instructions
|
||||||
context.MarkLabel(lblEnd);
|
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)
|
private static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs)
|
||||||
{
|
{
|
||||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||||
|
|
||||||
const int cmpOrdered = 7;
|
|
||||||
|
|
||||||
bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false;
|
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 n = GetVec(op.Rn);
|
||||||
Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm);
|
Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm);
|
||||||
|
|
||||||
|
CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ;
|
||||||
|
|
||||||
Operand lblNaN = Label();
|
Operand lblNaN = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
if (op.Size == 0)
|
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);
|
context.BranchIfFalse(lblNaN, isOrdered);
|
||||||
|
|
||||||
|
@ -481,9 +501,9 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
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);
|
context.BranchIfFalse(lblNaN, isOrdered);
|
||||||
|
|
||||||
|
@ -645,18 +665,7 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVec(op.Rd), res);
|
context.Copy(GetVec(op.Rd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum CmpCondition
|
private static void EmitCmpSseOrSse2OpF(ArmEmitterContext context, CmpCondition cond, bool scalar)
|
||||||
{
|
|
||||||
Equal = 0,
|
|
||||||
GreaterThanOrEqual = 5,
|
|
||||||
GreaterThan = 6
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitCmpSseOrSse2OpF(
|
|
||||||
ArmEmitterContext context,
|
|
||||||
CmpCondition cond,
|
|
||||||
bool scalar,
|
|
||||||
bool isLeOrLt = false)
|
|
||||||
{
|
{
|
||||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
|
@ -669,9 +678,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps;
|
Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps;
|
||||||
|
|
||||||
Operand res = isLeOrLt
|
Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond));
|
||||||
? context.AddIntrinsic(inst, m, n, Const((int)cond))
|
|
||||||
: context.AddIntrinsic(inst, n, m, Const((int)cond));
|
|
||||||
|
|
||||||
if (scalar)
|
if (scalar)
|
||||||
{
|
{
|
||||||
|
@ -688,9 +695,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd;
|
Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd;
|
||||||
|
|
||||||
Operand res = isLeOrLt
|
Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond));
|
||||||
? context.AddIntrinsic(inst, m, n, Const((int)cond))
|
|
||||||
: context.AddIntrinsic(inst, n, m, Const((int)cond));
|
|
||||||
|
|
||||||
if (scalar)
|
if (scalar)
|
||||||
{
|
{
|
||||||
|
@ -701,4 +706,4 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -716,8 +716,7 @@ namespace ARMeilleure.Instructions
|
||||||
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
||||||
Debug.Assert((uint)size < 2);
|
Debug.Assert((uint)size < 2);
|
||||||
|
|
||||||
OperandType type = size == 0 ? OperandType.FP32
|
OperandType type = size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||||
: OperandType.FP64;
|
|
||||||
|
|
||||||
if (signed)
|
if (signed)
|
||||||
{
|
{
|
||||||
|
@ -813,15 +812,12 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
|
|
||||||
const int cmpGreaterThanOrEqual = 5;
|
|
||||||
const int cmpOrdered = 7;
|
|
||||||
|
|
||||||
// sizeF == ((OpCodeSimdShImm64)op).Size - 2
|
// sizeF == ((OpCodeSimdShImm64)op).Size - 2
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
if (sizeF == 0)
|
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);
|
Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n);
|
||||||
|
|
||||||
|
@ -843,7 +839,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
Operand mask = X86GetAllElements(context, 0x4F000000); // 2.14748365E9f (2147483648)
|
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);
|
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, nInt, mask2);
|
||||||
|
|
||||||
|
@ -860,7 +856,7 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
else /* if (sizeF == 1) */
|
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);
|
Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n);
|
||||||
|
|
||||||
|
@ -896,7 +892,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
Operand mask = X86GetAllElements(context, 0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808)
|
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);
|
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, nInt, mask2);
|
||||||
|
|
||||||
|
@ -915,16 +911,12 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
|
|
||||||
const int cmpGreaterThanOrEqual = 5;
|
|
||||||
const int cmpGreaterThan = 6;
|
|
||||||
const int cmpOrdered = 7;
|
|
||||||
|
|
||||||
// sizeF == ((OpCodeSimdShImm)op).Size - 2
|
// sizeF == ((OpCodeSimdShImm)op).Size - 2
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
if (sizeF == 0)
|
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);
|
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 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);
|
Operand nRndMasked = context.AddIntrinsic(Intrinsic.X86Pand, nRnd, nRndMask);
|
||||||
|
|
||||||
|
@ -952,13 +944,13 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Subps, nRndMasked, mask);
|
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);
|
Operand resMasked = context.AddIntrinsic(Intrinsic.X86Pand, res, mask2);
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, resMasked);
|
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.X86Pxor, res, mask3);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Paddd, res, nInt);
|
res = context.AddIntrinsic(Intrinsic.X86Paddd, res, nInt);
|
||||||
|
@ -976,7 +968,7 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
else /* if (sizeF == 1) */
|
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);
|
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 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);
|
Operand nRndMasked = context.AddIntrinsic(Intrinsic.X86Pand, nRnd, nRndMask);
|
||||||
|
|
||||||
|
@ -1018,7 +1010,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Subpd, nRndMasked, mask);
|
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);
|
Operand resMasked = context.AddIntrinsic(Intrinsic.X86Pand, res, mask2);
|
||||||
|
|
||||||
|
@ -1032,7 +1024,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
res = EmitVectorLongCreate(context, low, high);
|
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.X86Pxor, res, mask3);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Paddq, res, nInt);
|
res = context.AddIntrinsic(Intrinsic.X86Paddq, res, nInt);
|
||||||
|
|
|
@ -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]
|
[Flags]
|
||||||
public enum SaturatingFlags
|
public enum SaturatingFlags
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||||
X86Cvtps2pd,
|
X86Cvtps2pd,
|
||||||
X86Cvtsd2si,
|
X86Cvtsd2si,
|
||||||
X86Cvtsd2ss,
|
X86Cvtsd2ss,
|
||||||
|
X86Cvtsi2si,
|
||||||
X86Cvtss2sd,
|
X86Cvtss2sd,
|
||||||
X86Divpd,
|
X86Divpd,
|
||||||
X86Divps,
|
X86Divps,
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace ARMeilleure
|
||||||
public static bool UseSse41IfAvailable { get; set; } = true;
|
public static bool UseSse41IfAvailable { get; set; } = true;
|
||||||
public static bool UseSse42IfAvailable { get; set; } = true;
|
public static bool UseSse42IfAvailable { get; set; } = true;
|
||||||
public static bool UsePopCntIfAvailable { get; set; } = true;
|
public static bool UsePopCntIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvxIfAvailable { get; set; } = true;
|
||||||
|
|
||||||
public static bool ForceLegacySse
|
public static bool ForceLegacySse
|
||||||
{
|
{
|
||||||
|
@ -29,5 +30,6 @@ namespace ARMeilleure
|
||||||
internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41;
|
internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41;
|
||||||
internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42;
|
internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42;
|
||||||
internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt;
|
internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt;
|
||||||
|
internal static bool UseAvx => UseAvxIfAvailable && HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
||||||
}
|
}
|
||||||
}
|
}
|
112
KEYS.md
112
KEYS.md
|
@ -2,103 +2,39 @@
|
||||||
|
|
||||||
Keys are required for decrypting most of the file formats used by the Nintendo Switch.
|
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:
|
Keysets are stored as text files. These 2 filenames are automatically read:
|
||||||
`prod.keys` - Contains common keys usedy by all Switch devices.
|
* `prod.keys` - Contains common keys used by all Nintendo Switch devices.
|
||||||
`console.keys` - Contains console-unique keys.
|
* `title.keys` - Contains game-specific keys.
|
||||||
`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`.
|
Ryujinx will first look for keys in `RyuFS/system`, and if it doesn't find any there it will look in `$HOME/.switch`.
|
||||||
|
To dump your `prod.keys` and `title.keys` please follow these following steps.
|
||||||
A guide to assist with dumping your own keys can be found [here](https://gist.github.com/roblabla/d8358ab058bbe3b00614740dcba4f208).
|
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.
|
||||||
## Common keys
|
3. Download the latest release of [Lockpick_RCM](https://github.com/shchmue/Lockpick_RCM/releases).
|
||||||
|
4. Boot into RCM mode.
|
||||||
Here is a template for a key file containing the main keys Ryujinx uses to read content files.
|
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.
|
||||||
Both `prod.keys` and `console.keys` use this format.
|
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.
|
||||||
master_key_00 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
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`.
|
||||||
master_key_01 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
10. You can find your keys in `sd:/switch/prod.keys` and `sd:/switch/title.keys` respectively.
|
||||||
master_key_02 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
11. Copy these files and paste them in `RyuFS/system`.
|
||||||
master_key_03 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
And you're done!
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
## Title keys
|
## 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:
|
For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
01000000000100000000000000000003,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
01000000000100000000000000000003 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
01000000000108000000000000000003,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
01000000000108000000000000000003 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
01000000000108000000000000000004,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
01000000000108000000000000000004 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
```
|
```
|
||||||
|
|
||||||
## Complete key list
|
## Prod keys
|
||||||
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
|
|
||||||
|
|
||||||
### Common keys
|
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!
|
||||||
|
|
||||||
```
|
|
||||||
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
|
|
||||||
```
|
|
|
@ -41,7 +41,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
|
||||||
|
|
||||||
- **Switch Keys**
|
- **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**
|
- **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/).
|
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
|
## Features
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,5 @@
|
||||||
ErrorCodeShift = 9,
|
ErrorCodeShift = 9,
|
||||||
|
|
||||||
Success = 0,
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
|
|
||||||
if (isRead && isWrite)
|
if (isRead && isWrite)
|
||||||
{
|
{
|
||||||
if (outputDataPosition < inputDataSize)
|
if (outputDataSize < inputDataSize)
|
||||||
{
|
{
|
||||||
arguments = null;
|
arguments = null;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,49 @@
|
||||||
using System.Runtime.InteropServices;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
|
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)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
struct ZbcSetTableArguments
|
struct ZbcSetTableArguments
|
||||||
{
|
{
|
||||||
// TODO
|
public ZbcColorArray ColorDs;
|
||||||
|
public ZbcColorArray ColorL2;
|
||||||
|
public uint Depth;
|
||||||
|
public uint Format;
|
||||||
|
public uint Type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,22 @@ using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Loader
|
namespace Ryujinx.HLE.HOS.Services.Ro
|
||||||
{
|
{
|
||||||
[Service("ldr:ro")]
|
[Service("ldr:ro")]
|
||||||
[Service("ro:1")] // 7.0.0+
|
[Service("ro:1")] // 7.0.0+
|
||||||
class IRoInterface : IpcService
|
class IRoInterface : IpcService, IDisposable
|
||||||
{
|
{
|
||||||
private const int MaxNrr = 0x40;
|
private const int MaxNrr = 0x40;
|
||||||
private const int MaxNro = 0x40;
|
private const int MaxNro = 0x40;
|
||||||
|
private const int MaxMapRetries = 0x200;
|
||||||
|
private const int GuardPagesSize = 0x4000;
|
||||||
|
|
||||||
private const uint NrrMagic = 0x3052524E;
|
private const uint NrrMagic = 0x3052524E;
|
||||||
private const uint NroMagic = 0x304F524E;
|
private const uint NroMagic = 0x304F524E;
|
||||||
|
@ -25,12 +28,15 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
private List<NrrInfo> _nrrInfos;
|
private List<NrrInfo> _nrrInfos;
|
||||||
private List<NroInfo> _nroInfos;
|
private List<NroInfo> _nroInfos;
|
||||||
|
|
||||||
private bool _isInitialized;
|
private KProcess _owner;
|
||||||
|
|
||||||
|
private static Random _random = new Random();
|
||||||
|
|
||||||
public IRoInterface(ServiceCtx context)
|
public IRoInterface(ServiceCtx context)
|
||||||
{
|
{
|
||||||
_nrrInfos = new List<NrrInfo>(MaxNrr);
|
_nrrInfos = new List<NrrInfo>(MaxNrr);
|
||||||
_nroInfos = new List<NroInfo>(MaxNro);
|
_nroInfos = new List<NroInfo>(MaxNro);
|
||||||
|
_owner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, long nrrAddress, long nrrSize)
|
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)
|
if (nrrSize == 0 || nrrAddress + nrrSize <= nrrAddress || (nrrSize & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return ResultCode.BadSize;
|
return ResultCode.InvalidSize;
|
||||||
}
|
}
|
||||||
else if ((nrrAddress & 0xFFF) != 0)
|
else if ((nrrAddress & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return ResultCode.UnalignedAddress;
|
return ResultCode.InvalidAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
StructReader reader = new StructReader(context.Memory, nrrAddress);
|
StructReader reader = new StructReader(context.Memory, nrrAddress);
|
||||||
|
@ -55,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
}
|
}
|
||||||
else if (header.NrrSize != nrrSize)
|
else if (header.NrrSize != nrrSize)
|
||||||
{
|
{
|
||||||
return ResultCode.BadSize;
|
return ResultCode.InvalidSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<byte[]> hashes = new List<byte[]>();
|
List<byte[]> hashes = new List<byte[]>();
|
||||||
|
@ -105,19 +111,19 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
|
|
||||||
if (_nroInfos.Count >= MaxNro)
|
if (_nroInfos.Count >= MaxNro)
|
||||||
{
|
{
|
||||||
return ResultCode.MaxNro;
|
return ResultCode.TooManyNro;
|
||||||
}
|
}
|
||||||
else if (nroSize == 0 || nroAddress + nroSize <= nroAddress || (nroSize & 0xFFF) != 0)
|
else if (nroSize == 0 || nroAddress + nroSize <= nroAddress || (nroSize & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return ResultCode.BadSize;
|
return ResultCode.InvalidSize;
|
||||||
}
|
}
|
||||||
else if (bssSize != 0 && bssAddress + bssSize <= bssAddress)
|
else if (bssSize != 0 && bssAddress + bssSize <= bssAddress)
|
||||||
{
|
{
|
||||||
return ResultCode.BadSize;
|
return ResultCode.InvalidSize;
|
||||||
}
|
}
|
||||||
else if ((nroAddress & 0xFFF) != 0)
|
else if ((nroAddress & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return ResultCode.UnalignedAddress;
|
return ResultCode.InvalidAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint magic = context.Memory.ReadUInt32((long)nroAddress + 0x10);
|
uint magic = context.Memory.ReadUInt32((long)nroAddress + 0x10);
|
||||||
|
@ -140,12 +146,12 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
|
|
||||||
if (!IsNroHashPresent(nroHash))
|
if (!IsNroHashPresent(nroHash))
|
||||||
{
|
{
|
||||||
return ResultCode.NroHashNotPresent;
|
return ResultCode.NotRegistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsNroLoaded(nroHash))
|
if (IsNroLoaded(nroHash))
|
||||||
{
|
{
|
||||||
return ResultCode.NroAlreadyLoaded;
|
return ResultCode.AlreadyLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
|
@ -187,77 +193,120 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
return ResultCode.Success;
|
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;
|
nroMappedAddress = 0;
|
||||||
|
|
||||||
KMemoryManager memMgr = context.Process.MemoryManager;
|
while (retryCount++ < MaxMapRetries)
|
||||||
|
|
||||||
ulong targetAddress = memMgr.GetAddrSpaceBaseAddr();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
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 (info.BssSize > 0)
|
||||||
|
|
||||||
if (memInfo.State == MemoryState.Unmapped && memInfo.Size >= info.TotalSize)
|
|
||||||
{
|
{
|
||||||
if (!memMgr.InsideHeapRegion (targetAddress, info.TotalSize) &&
|
KernelResult bssMappingResult = memMgr.MapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize);
|
||||||
!memMgr.InsideAliasRegion(targetAddress, info.TotalSize))
|
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAddress += memInfo.Size;
|
KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, baseAddress, size);
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize);
|
if (result == KernelResult.InvalidMemState)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
memMgr.UnmapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize);
|
continue;
|
||||||
|
|
||||||
return ResultCode.InvalidMemoryState;
|
|
||||||
}
|
}
|
||||||
}
|
else if (result != KernelResult.Success)
|
||||||
|
|
||||||
result = LoadNroIntoMemory(context.Process, info.Executable, targetAddress);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
memMgr.UnmapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize);
|
|
||||||
|
|
||||||
if (info.BssSize != 0)
|
|
||||||
{
|
{
|
||||||
memMgr.UnmapProcessCodeMemory(bssTargetAddress, info.BssAddress, info.BssSize);
|
return (ResultCode)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanAddGuardRegionsInProcess(process, targetAddress, size))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.NroMappedAddress = targetAddress;
|
if (retryCount == MaxMapRetries)
|
||||||
nroMappedAddress = targetAddress;
|
{
|
||||||
|
return ResultCode.InsufficientAddressSpace;
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
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 textStart = baseAddress + (ulong)relocatableObject.TextOffset;
|
||||||
ulong roStart = baseAddress + (ulong)relocatableObject.RoOffset;
|
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)
|
foreach (NroInfo info in _nroInfos)
|
||||||
{
|
{
|
||||||
|
@ -315,49 +364,64 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
{
|
{
|
||||||
_nroInfos.Remove(info);
|
_nroInfos.Remove(info);
|
||||||
|
|
||||||
ulong textSize = (ulong)info.Executable.Text.Length;
|
return UnmapNroFromInfo(info);
|
||||||
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 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)]
|
[Command(0)]
|
||||||
// LoadNro(u64, u64, u64, u64, u64, pid) -> u64
|
// LoadNro(u64, u64, u64, u64, u64, pid) -> u64
|
||||||
public ResultCode LoadNro(ServiceCtx context)
|
public ResultCode LoadNro(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result = ResultCode.BadInitialization;
|
ResultCode result = IsInitialized(context.Process);
|
||||||
|
|
||||||
// Zero
|
// Zero
|
||||||
context.RequestData.ReadUInt64();
|
context.RequestData.ReadUInt64();
|
||||||
|
@ -369,19 +433,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
|
|
||||||
ulong nroMappedAddress = 0;
|
ulong nroMappedAddress = 0;
|
||||||
|
|
||||||
if (_isInitialized)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
NroInfo info;
|
NroInfo info;
|
||||||
|
|
||||||
result = ParseNro(out info, context, nroHeapAddress, nroSize, bssHeapAddress, bssSize);
|
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)
|
// UnloadNro(u64, u64, pid)
|
||||||
public ResultCode UnloadNro(ServiceCtx context)
|
public ResultCode UnloadNro(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result = ResultCode.BadInitialization;
|
ResultCode result = IsInitialized(context.Process);
|
||||||
|
|
||||||
// Zero
|
// Zero
|
||||||
context.RequestData.ReadUInt64();
|
context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
ulong nroMappedAddress = context.RequestData.ReadUInt64();
|
ulong nroMappedAddress = context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
if (_isInitialized)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
if ((nroMappedAddress & 0xFFF) != 0)
|
if ((nroMappedAddress & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return ResultCode.UnalignedAddress;
|
return ResultCode.InvalidAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = RemoveNroInfo(context, nroMappedAddress);
|
result = RemoveNroInfo(nroMappedAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -419,24 +488,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
// LoadNrr(u64, u64, u64, pid)
|
// LoadNrr(u64, u64, u64, pid)
|
||||||
public ResultCode LoadNrr(ServiceCtx context)
|
public ResultCode LoadNrr(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result = ResultCode.BadInitialization;
|
ResultCode result = IsInitialized(context.Process);
|
||||||
|
|
||||||
// Zero
|
// pid placeholder, zero
|
||||||
context.RequestData.ReadUInt64();
|
context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
long nrrAddress = context.RequestData.ReadInt64();
|
long nrrAddress = context.RequestData.ReadInt64();
|
||||||
long nrrSize = context.RequestData.ReadInt64();
|
long nrrSize = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
if (_isInitialized)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
NrrInfo info;
|
NrrInfo info;
|
||||||
result = ParseNrr(out info, context, nrrAddress, nrrSize);
|
result = ParseNrr(out info, context, nrrAddress, nrrSize);
|
||||||
|
|
||||||
if (result == 0)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
if (_nrrInfos.Count >= MaxNrr)
|
if (_nrrInfos.Count >= MaxNrr)
|
||||||
{
|
{
|
||||||
result = ResultCode.MaxNrr;
|
result = ResultCode.NotLoaded;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -452,18 +521,18 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
// UnloadNrr(u64, u64, pid)
|
// UnloadNrr(u64, u64, pid)
|
||||||
public ResultCode UnloadNrr(ServiceCtx context)
|
public ResultCode UnloadNrr(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ResultCode result = ResultCode.BadInitialization;
|
ResultCode result = IsInitialized(context.Process);
|
||||||
|
|
||||||
// Zero
|
// pid placeholder, zero
|
||||||
context.RequestData.ReadUInt64();
|
context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
long nrrHeapAddress = context.RequestData.ReadInt64();
|
long nrrHeapAddress = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
if (_isInitialized)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
if ((nrrHeapAddress & 0xFFF) != 0)
|
if ((nrrHeapAddress & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return ResultCode.UnalignedAddress;
|
return ResultCode.InvalidAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = RemoveNrrInfo(nrrHeapAddress);
|
result = RemoveNrrInfo(nrrHeapAddress);
|
||||||
|
@ -476,10 +545,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader
|
||||||
// Initialize(u64, pid, KObject)
|
// Initialize(u64, pid, KObject)
|
||||||
public ResultCode Initialize(ServiceCtx context)
|
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.
|
if (_owner != null)
|
||||||
_isInitialized = true;
|
{
|
||||||
|
return ResultCode.InvalidSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
_owner = context.Process;
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (NroInfo info in _nroInfos)
|
||||||
|
{
|
||||||
|
UnmapNroFromInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
_nroInfos.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
27
Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs
Normal file
27
Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs
Normal file
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Loader
|
namespace Ryujinx.HLE.HOS.Services.Ro
|
||||||
{
|
{
|
||||||
class NroInfo
|
class NroInfo
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Loader
|
namespace Ryujinx.HLE.HOS.Services.Ro
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x350)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x350)]
|
||||||
unsafe struct NrrHeader
|
unsafe struct NrrHeader
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Loader
|
namespace Ryujinx.HLE.HOS.Services.Ro
|
||||||
{
|
{
|
||||||
class NrrInfo
|
class NrrInfo
|
||||||
{
|
{
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
{
|
{
|
||||||
|
@ -9,15 +10,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
public int FenceCount;
|
public int FenceCount;
|
||||||
|
|
||||||
[FieldOffset(0x4)]
|
[FieldOffset(0x4)]
|
||||||
public Fence Fence0;
|
public NvFence Fence0;
|
||||||
|
|
||||||
[FieldOffset(0xC)]
|
[FieldOffset(0xC)]
|
||||||
public Fence Fence1;
|
public NvFence Fence1;
|
||||||
|
|
||||||
[FieldOffset(0x14)]
|
[FieldOffset(0x14)]
|
||||||
public Fence Fence2;
|
public NvFence Fence2;
|
||||||
|
|
||||||
[FieldOffset(0x1C)]
|
[FieldOffset(0x1C)]
|
||||||
public Fence Fence3;
|
public NvFence Fence3;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,7 +22,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue