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.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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||
X86Cvtps2pd,
|
||||
X86Cvtsd2si,
|
||||
X86Cvtsd2ss,
|
||||
X86Cvtsi2si,
|
||||
X86Cvtss2sd,
|
||||
X86Divpd,
|
||||
X86Divps,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
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.
|
||||
|
||||
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!
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||
|
||||
if (isRead && isWrite)
|
||||
{
|
||||
if (outputDataPosition < inputDataSize)
|
||||
if (outputDataSize < inputDataSize)
|
||||
{
|
||||
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
|
||||
{
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<NrrInfo> _nrrInfos;
|
||||
private List<NroInfo> _nroInfos;
|
||||
|
||||
private bool _isInitialized;
|
||||
private KProcess _owner;
|
||||
|
||||
private static Random _random = new Random();
|
||||
|
||||
public IRoInterface(ServiceCtx context)
|
||||
{
|
||||
_nrrInfos = new List<NrrInfo>(MaxNrr);
|
||||
_nroInfos = new List<NroInfo>(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<byte[]> hashes = new List<byte[]>();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Loader
|
||||
namespace Ryujinx.HLE.HOS.Services.Ro
|
||||
{
|
||||
class NroInfo
|
||||
{
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Loader
|
||||
namespace Ryujinx.HLE.HOS.Services.Ro
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<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="NUnit3TestAdapter" Version="3.15.1" />
|
||||
</ItemGroup>
|
||||
|
|
Loading…
Add table
Reference in a new issue