Initial implementation of KProcess
This commit is contained in:
parent
dc02ac08ca
commit
e6f2b1940a
79 changed files with 4551 additions and 2006 deletions
|
@ -18,7 +18,7 @@ namespace ChocolArm64
|
|||
|
||||
private int _isExecuting;
|
||||
|
||||
public CpuThread(Translator translator, MemoryManager memory, long entryPoint)
|
||||
public CpuThread(Translator translator, MemoryManager memory, long entrypoint)
|
||||
{
|
||||
_translator = translator;
|
||||
Memory = memory;
|
||||
|
@ -31,7 +31,7 @@ namespace ChocolArm64
|
|||
|
||||
Work = new Thread(delegate()
|
||||
{
|
||||
translator.ExecuteSubroutine(this, entryPoint);
|
||||
translator.ExecuteSubroutine(this, entrypoint);
|
||||
|
||||
memory.RemoveMonitor(ThreadState.Core);
|
||||
|
||||
|
|
|
@ -276,16 +276,38 @@ namespace ChocolArm64.Memory
|
|||
|
||||
public byte[] ReadBytes(long position, long size)
|
||||
{
|
||||
if ((uint)size > int.MaxValue)
|
||||
long endAddr = position + size;
|
||||
|
||||
if ((ulong)size > int.MaxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
EnsureRangeIsValid(position, size);
|
||||
if ((ulong)endAddr < (ulong)position)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(position));
|
||||
}
|
||||
|
||||
byte[] data = new byte[size];
|
||||
|
||||
Marshal.Copy((IntPtr)Translate(position), data, 0, (int)size);
|
||||
int offset = 0;
|
||||
|
||||
while ((ulong)position < (ulong)endAddr)
|
||||
{
|
||||
long pageLimit = (position + PageSize) & ~(long)PageMask;
|
||||
|
||||
if ((ulong)pageLimit > (ulong)endAddr)
|
||||
{
|
||||
pageLimit = endAddr;
|
||||
}
|
||||
|
||||
int copySize = (int)(pageLimit - position);
|
||||
|
||||
Marshal.Copy((IntPtr)Translate(position), data, offset, copySize);
|
||||
|
||||
position += copySize;
|
||||
offset += copySize;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
|
84
Ryujinx.Common/BitUtils.cs
Normal file
84
Ryujinx.Common/BitUtils.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
namespace Ryujinx.Common
|
||||
{
|
||||
public static class BitUtils
|
||||
{
|
||||
public static int AlignUp(int Value, int Size)
|
||||
{
|
||||
return (Value + (Size - 1)) & -Size;
|
||||
}
|
||||
|
||||
public static long AlignUp(long Value, int Size)
|
||||
{
|
||||
return (Value + (Size - 1)) & -(long)Size;
|
||||
}
|
||||
|
||||
public static int AlignDown(int Value, int Size)
|
||||
{
|
||||
return Value & -Size;
|
||||
}
|
||||
|
||||
public static long AlignDown(long Value, int Size)
|
||||
{
|
||||
return Value & -(long)Size;
|
||||
}
|
||||
|
||||
public static bool IsPowerOfTwo32(int Value)
|
||||
{
|
||||
return Value != 0 && (Value & (Value - 1)) == 0;
|
||||
}
|
||||
|
||||
public static bool IsPowerOfTwo64(long Value)
|
||||
{
|
||||
return Value != 0 && (Value & (Value - 1)) == 0;
|
||||
}
|
||||
|
||||
public static int CountLeadingZeros32(int Value)
|
||||
{
|
||||
return (int)CountLeadingZeros((ulong)Value, 32);
|
||||
}
|
||||
|
||||
public static int CountLeadingZeros64(long Value)
|
||||
{
|
||||
return (int)CountLeadingZeros((ulong)Value, 64);
|
||||
}
|
||||
|
||||
private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
private static ulong CountLeadingZeros(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||
{
|
||||
if (Value == 0ul)
|
||||
{
|
||||
return (ulong)Size;
|
||||
}
|
||||
|
||||
int NibbleIdx = Size;
|
||||
int PreCount, Count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
NibbleIdx -= 4;
|
||||
PreCount = ClzNibbleTbl[(Value >> NibbleIdx) & 0b1111];
|
||||
Count += PreCount;
|
||||
}
|
||||
while (PreCount == 4);
|
||||
|
||||
return (ulong)Count;
|
||||
}
|
||||
|
||||
public static long ReverseBits64(long Value)
|
||||
{
|
||||
return (long)ReverseBits64((ulong)Value);
|
||||
}
|
||||
|
||||
private static ulong ReverseBits64(ulong Value)
|
||||
{
|
||||
Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((Value & 0x5555555555555555) << 1 );
|
||||
Value = ((Value & 0xcccccccccccccccc) >> 2 ) | ((Value & 0x3333333333333333) << 2 );
|
||||
Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4 );
|
||||
Value = ((Value & 0xff00ff00ff00ff00) >> 8 ) | ((Value & 0x00ff00ff00ff00ff) << 8 );
|
||||
Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
|
||||
|
||||
return (Value >> 32) | (Value << 32);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,10 +29,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData)
|
||||
{
|
||||
if (Context.Process.MetaData != null)
|
||||
{
|
||||
CurrentTitleId = Context.Process.MetaData.ACI0.TitleId;
|
||||
}
|
||||
CurrentTitleId = Context.Process.TitleId;
|
||||
}
|
||||
|
||||
string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -5,28 +6,28 @@ namespace Ryujinx.HLE.HOS
|
|||
{
|
||||
class GlobalStateTable
|
||||
{
|
||||
private ConcurrentDictionary<Process, IdDictionary> DictByProcess;
|
||||
private ConcurrentDictionary<KProcess, IdDictionary> DictByProcess;
|
||||
|
||||
public GlobalStateTable()
|
||||
{
|
||||
DictByProcess = new ConcurrentDictionary<Process, IdDictionary>();
|
||||
DictByProcess = new ConcurrentDictionary<KProcess, IdDictionary>();
|
||||
}
|
||||
|
||||
public bool Add(Process Process, int Id, object Data)
|
||||
public bool Add(KProcess Process, int Id, object Data)
|
||||
{
|
||||
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
|
||||
|
||||
return Dict.Add(Id, Data);
|
||||
}
|
||||
|
||||
public int Add(Process Process, object Data)
|
||||
public int Add(KProcess Process, object Data)
|
||||
{
|
||||
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
|
||||
|
||||
return Dict.Add(Data);
|
||||
}
|
||||
|
||||
public object GetData(Process Process, int Id)
|
||||
public object GetData(KProcess Process, int Id)
|
||||
{
|
||||
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||
{
|
||||
|
@ -36,7 +37,7 @@ namespace Ryujinx.HLE.HOS
|
|||
return null;
|
||||
}
|
||||
|
||||
public T GetData<T>(Process Process, int Id)
|
||||
public T GetData<T>(KProcess Process, int Id)
|
||||
{
|
||||
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||
{
|
||||
|
@ -46,7 +47,7 @@ namespace Ryujinx.HLE.HOS
|
|||
return default(T);
|
||||
}
|
||||
|
||||
public object Delete(Process Process, int Id)
|
||||
public object Delete(KProcess Process, int Id)
|
||||
{
|
||||
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||
{
|
||||
|
@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS
|
|||
return null;
|
||||
}
|
||||
|
||||
public ICollection<object> DeleteProcess(Process Process)
|
||||
public ICollection<object> DeleteProcess(KProcess Process)
|
||||
{
|
||||
if (DictByProcess.TryRemove(Process, out IdDictionary Dict))
|
||||
{
|
||||
|
|
|
@ -7,37 +7,58 @@ using Ryujinx.HLE.HOS.SystemState;
|
|||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.HLE.Loaders.Npdm;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
using Nso = Ryujinx.HLE.Loaders.Executables.Nso;
|
||||
|
||||
namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
public class Horizon : IDisposable
|
||||
{
|
||||
internal const int InitialKipId = 1;
|
||||
internal const int InitialProcessId = 0x51;
|
||||
|
||||
internal const int HidSize = 0x40000;
|
||||
internal const int FontSize = 0x1100000;
|
||||
|
||||
private Switch Device;
|
||||
private const long UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
||||
private const long UserSlabHeapItemSize = KMemoryManager.PageSize;
|
||||
private const long UserSlabHeapSize = 0x3de000;
|
||||
|
||||
private ConcurrentDictionary<int, Process> Processes;
|
||||
internal Switch Device { get; private set; }
|
||||
|
||||
public SystemStateMgr State { get; private set; }
|
||||
|
||||
internal KRecursiveLock CriticalSectionLock { get; private set; }
|
||||
internal bool KernelInitialized { get; private set; }
|
||||
|
||||
internal KResourceLimit ResourceLimit { get; private set; }
|
||||
|
||||
internal KMemoryRegionManager[] MemoryRegions { get; private set; }
|
||||
|
||||
internal KSlabHeap UserSlabHeapPages { get; private set; }
|
||||
|
||||
internal KCriticalSection CriticalSection { get; private set; }
|
||||
|
||||
internal KScheduler Scheduler { get; private set; }
|
||||
|
||||
internal KTimeManager TimeManager { get; private set; }
|
||||
|
||||
internal KAddressArbiter AddressArbiter { get; private set; }
|
||||
|
||||
internal KSynchronization Synchronization { get; private set; }
|
||||
|
||||
internal LinkedList<KThread> Withholders { get; private set; }
|
||||
|
||||
internal KContextIdManager ContextIdManager { get; private set; }
|
||||
|
||||
private long KipId;
|
||||
private long ProcessId;
|
||||
private long ThreadUid;
|
||||
|
||||
internal AppletStateMgr AppletState { get; private set; }
|
||||
|
||||
internal KSharedMemory HidSharedMem { get; private set; }
|
||||
internal KSharedMemory FontSharedMem { get; private set; }
|
||||
|
||||
|
@ -61,24 +82,36 @@ namespace Ryujinx.HLE.HOS
|
|||
{
|
||||
this.Device = Device;
|
||||
|
||||
Processes = new ConcurrentDictionary<int, Process>();
|
||||
|
||||
State = new SystemStateMgr();
|
||||
|
||||
CriticalSectionLock = new KRecursiveLock(this);
|
||||
ResourceLimit = new KResourceLimit();
|
||||
|
||||
MemoryRegions = KernelInit.GetMemoryRegions();
|
||||
|
||||
UserSlabHeapPages = new KSlabHeap(
|
||||
UserSlabHeapBase,
|
||||
UserSlabHeapItemSize,
|
||||
UserSlabHeapSize);
|
||||
|
||||
CriticalSection = new KCriticalSection(this);
|
||||
|
||||
Scheduler = new KScheduler(this);
|
||||
|
||||
TimeManager = new KTimeManager();
|
||||
|
||||
AddressArbiter = new KAddressArbiter(this);
|
||||
|
||||
Synchronization = new KSynchronization(this);
|
||||
|
||||
Withholders = new LinkedList<KThread>();
|
||||
|
||||
ContextIdManager = new KContextIdManager();
|
||||
|
||||
KipId = InitialKipId;
|
||||
ProcessId = InitialProcessId;
|
||||
|
||||
Scheduler.StartAutoPreemptionThread();
|
||||
|
||||
KernelInitialized = true;
|
||||
|
||||
if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) ||
|
||||
!Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
|
||||
{
|
||||
|
@ -88,6 +121,10 @@ namespace Ryujinx.HLE.HOS
|
|||
HidSharedMem = new KSharedMemory(HidPA, HidSize);
|
||||
FontSharedMem = new KSharedMemory(FontPA, FontSize);
|
||||
|
||||
AppletState = new AppletStateMgr(this);
|
||||
|
||||
AppletState.SetFocus(true);
|
||||
|
||||
Font = new SharedFontManager(Device, FontSharedMem.PA);
|
||||
|
||||
VsyncEvent = new KEvent(this);
|
||||
|
@ -120,13 +157,15 @@ namespace Ryujinx.HLE.HOS
|
|||
else
|
||||
{
|
||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
||||
|
||||
MetaData = GetDefaultNpdm();
|
||||
}
|
||||
|
||||
Process MainProcess = MakeProcess(MetaData);
|
||||
List<IExecutable> StaticObjects = new List<IExecutable>();
|
||||
|
||||
void LoadNso(string FileName)
|
||||
void LoadNso(string SearchPattern)
|
||||
{
|
||||
foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
|
||||
foreach (string File in Directory.GetFiles(ExeFsDir, SearchPattern))
|
||||
{
|
||||
if (Path.GetExtension(File) != string.Empty)
|
||||
{
|
||||
|
@ -139,31 +178,28 @@ namespace Ryujinx.HLE.HOS
|
|||
{
|
||||
string Name = Path.GetFileNameWithoutExtension(File);
|
||||
|
||||
Nso Program = new Nso(Input, Name);
|
||||
Nso StaticObject = new Nso(Input, Name);
|
||||
|
||||
MainProcess.LoadProgram(Program);
|
||||
StaticObjects.Add(StaticObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(MainProcess.MetaData?.Is64Bits ?? true))
|
||||
if (!MetaData.Is64Bits)
|
||||
{
|
||||
throw new NotImplementedException("32-bit titles are unsupported!");
|
||||
}
|
||||
|
||||
CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16");
|
||||
CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
|
||||
|
||||
LoadNso("rtld");
|
||||
|
||||
MainProcess.SetEmptyArgs();
|
||||
|
||||
LoadNso("main");
|
||||
LoadNso("subsdk*");
|
||||
LoadNso("sdk");
|
||||
|
||||
ContentManager.LoadEntries();
|
||||
|
||||
MainProcess.Run();
|
||||
ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
|
||||
}
|
||||
|
||||
public void LoadXci(string XciFile)
|
||||
|
@ -356,9 +392,11 @@ namespace Ryujinx.HLE.HOS
|
|||
else
|
||||
{
|
||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
||||
|
||||
MetaData = GetDefaultNpdm();
|
||||
}
|
||||
|
||||
Process MainProcess = MakeProcess(MetaData);
|
||||
List<IExecutable> StaticObjects = new List<IExecutable>();
|
||||
|
||||
void LoadNso(string Filename)
|
||||
{
|
||||
|
@ -373,9 +411,9 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
string Name = Path.GetFileNameWithoutExtension(File.Name);
|
||||
|
||||
Nso Program = new Nso(Exefs.OpenFile(File), Name);
|
||||
Nso StaticObject = new Nso(Exefs.OpenFile(File), Name);
|
||||
|
||||
MainProcess.LoadProgram(Program);
|
||||
StaticObjects.Add(StaticObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,71 +437,50 @@ namespace Ryujinx.HLE.HOS
|
|||
return ControlData;
|
||||
}
|
||||
|
||||
if (ControlNca != null)
|
||||
if (!MetaData.Is64Bits)
|
||||
{
|
||||
MainProcess.ControlData = ReadControlData();
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16");
|
||||
}
|
||||
|
||||
if (!MainProcess.MetaData.Is64Bits)
|
||||
{
|
||||
throw new NotImplementedException("32-bit titles are unsupported!");
|
||||
throw new NotImplementedException("32-bit titles are not supported!");
|
||||
}
|
||||
|
||||
LoadNso("rtld");
|
||||
|
||||
MainProcess.SetEmptyArgs();
|
||||
|
||||
LoadNso("main");
|
||||
LoadNso("subsdk");
|
||||
LoadNso("sdk");
|
||||
|
||||
ContentManager.LoadEntries();
|
||||
|
||||
MainProcess.Run();
|
||||
ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
|
||||
}
|
||||
|
||||
public void LoadProgram(string FilePath)
|
||||
{
|
||||
Npdm MetaData = GetDefaultNpdm();
|
||||
|
||||
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
||||
|
||||
string Name = Path.GetFileNameWithoutExtension(FilePath);
|
||||
string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath);
|
||||
|
||||
if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
|
||||
{
|
||||
string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}";
|
||||
string TempPath = Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
|
||||
|
||||
string SwitchDir = Path.GetDirectoryName(TempPath);
|
||||
|
||||
if (!Directory.Exists(SwitchDir))
|
||||
{
|
||||
Directory.CreateDirectory(SwitchDir);
|
||||
}
|
||||
|
||||
File.Copy(FilePath, TempPath, true);
|
||||
|
||||
FilePath = TempPath;
|
||||
}
|
||||
|
||||
Process MainProcess = MakeProcess();
|
||||
|
||||
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
||||
{
|
||||
MainProcess.LoadProgram(IsNro
|
||||
IExecutable StaticObject = IsNro
|
||||
? (IExecutable)new Nro(Input, FilePath)
|
||||
: (IExecutable)new Nso(Input, FilePath));
|
||||
: (IExecutable)new Nso(Input, FilePath);
|
||||
|
||||
ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject });
|
||||
}
|
||||
}
|
||||
|
||||
MainProcess.SetEmptyArgs();
|
||||
private Npdm GetDefaultNpdm()
|
||||
{
|
||||
Assembly Asm = Assembly.GetCallingAssembly();
|
||||
|
||||
ContentManager.LoadEntries();
|
||||
using (Stream NpdmStream = Asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
|
||||
{
|
||||
return new Npdm(NpdmStream);
|
||||
}
|
||||
}
|
||||
|
||||
MainProcess.Run(IsNro);
|
||||
private Stream GetHomebrewNpdmStream()
|
||||
{
|
||||
return Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm");
|
||||
}
|
||||
|
||||
public void LoadKeySet()
|
||||
|
@ -507,51 +524,19 @@ namespace Ryujinx.HLE.HOS
|
|||
VsyncEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
private Process MakeProcess(Npdm MetaData = null)
|
||||
internal long GetThreadUid()
|
||||
{
|
||||
HasStarted = true;
|
||||
|
||||
Process Process;
|
||||
|
||||
lock (Processes)
|
||||
{
|
||||
int ProcessId = 0;
|
||||
|
||||
while (Processes.ContainsKey(ProcessId))
|
||||
{
|
||||
ProcessId++;
|
||||
}
|
||||
|
||||
Process = new Process(Device, ProcessId, MetaData);
|
||||
|
||||
Processes.TryAdd(ProcessId, Process);
|
||||
}
|
||||
|
||||
InitializeProcess(Process);
|
||||
|
||||
return Process;
|
||||
return Interlocked.Increment(ref ThreadUid) - 1;
|
||||
}
|
||||
|
||||
private void InitializeProcess(Process Process)
|
||||
internal long GetKipId()
|
||||
{
|
||||
Process.AppletState.SetFocus(true);
|
||||
return Interlocked.Increment(ref KipId) - 1;
|
||||
}
|
||||
|
||||
internal void ExitProcess(int ProcessId)
|
||||
internal long GetProcessId()
|
||||
{
|
||||
if (Processes.TryRemove(ProcessId, out Process Process))
|
||||
{
|
||||
Process.Dispose();
|
||||
|
||||
if (Processes.Count == 0)
|
||||
{
|
||||
Scheduler.Dispose();
|
||||
|
||||
TimeManager.Dispose();
|
||||
|
||||
Device.Unload();
|
||||
}
|
||||
}
|
||||
return Interlocked.Increment(ref ProcessId) - 1;
|
||||
}
|
||||
|
||||
public void EnableMultiCoreScheduling()
|
||||
|
@ -579,10 +564,11 @@ namespace Ryujinx.HLE.HOS
|
|||
{
|
||||
if (Disposing)
|
||||
{
|
||||
foreach (Process Process in Processes.Values)
|
||||
{
|
||||
Process.Dispose();
|
||||
}
|
||||
Scheduler.Dispose();
|
||||
|
||||
TimeManager.Dispose();
|
||||
|
||||
Device.Unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||
static class IpcHandler
|
||||
{
|
||||
public static long IpcCall(
|
||||
Switch Ns,
|
||||
Process Process,
|
||||
Switch Device,
|
||||
KProcess Process,
|
||||
MemoryManager Memory,
|
||||
KSession Session,
|
||||
IpcMessage Request,
|
||||
|
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||
BinaryWriter ResWriter = new BinaryWriter(ResMS);
|
||||
|
||||
ServiceCtx Context = new ServiceCtx(
|
||||
Ns,
|
||||
Device,
|
||||
Process,
|
||||
Memory,
|
||||
Session,
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
Addr32Bits = 0,
|
||||
Addr36Bits = 1,
|
||||
Addr36BitsNoMap = 2,
|
||||
Addr32BitsNoMap = 2,
|
||||
Addr39Bits = 3
|
||||
}
|
||||
}
|
|
@ -6,4 +6,4 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
DecrementAndWaitIfLessThan = 1,
|
||||
WaitIfEqual = 2
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
static class DramMemoryMap
|
||||
{
|
||||
public const long DramBase = 0x80000000;
|
||||
public const long DramSize = 0x100000000;
|
||||
public const long DramEnd = DramBase + DramSize;
|
||||
|
||||
public const long KernelReserveBase = DramBase + 0x60000;
|
||||
|
||||
public const long SlabHeapBase = KernelReserveBase + 0x85000;
|
||||
public const long SlapHeapSize = 0xa21000;
|
||||
}
|
||||
}
|
|
@ -5,24 +5,61 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
class HleCoreManager
|
||||
{
|
||||
private ConcurrentDictionary<Thread, ManualResetEvent> Threads;
|
||||
private class PausableThread
|
||||
{
|
||||
public ManualResetEvent Event { get; private set; }
|
||||
|
||||
public bool IsExiting { get; set; }
|
||||
|
||||
public PausableThread()
|
||||
{
|
||||
Event = new ManualResetEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<Thread, PausableThread> Threads;
|
||||
|
||||
public HleCoreManager()
|
||||
{
|
||||
Threads = new ConcurrentDictionary<Thread, ManualResetEvent>();
|
||||
Threads = new ConcurrentDictionary<Thread, PausableThread>();
|
||||
}
|
||||
|
||||
public ManualResetEvent GetThread(Thread Thread)
|
||||
public void Set(Thread Thread)
|
||||
{
|
||||
return Threads.GetOrAdd(Thread, (Key) => new ManualResetEvent(false));
|
||||
GetThread(Thread).Event.Set();
|
||||
}
|
||||
|
||||
public void Reset(Thread Thread)
|
||||
{
|
||||
GetThread(Thread).Event.Reset();
|
||||
}
|
||||
|
||||
public void Wait(Thread Thread)
|
||||
{
|
||||
PausableThread PausableThread = GetThread(Thread);
|
||||
|
||||
if (!PausableThread.IsExiting)
|
||||
{
|
||||
PausableThread.Event.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
public void Exit(Thread Thread)
|
||||
{
|
||||
GetThread(Thread).IsExiting = true;
|
||||
}
|
||||
|
||||
private PausableThread GetThread(Thread Thread)
|
||||
{
|
||||
return Threads.GetOrAdd(Thread, (Key) => new PausableThread());
|
||||
}
|
||||
|
||||
public void RemoveThread(Thread Thread)
|
||||
{
|
||||
if (Threads.TryRemove(Thread, out ManualResetEvent Event))
|
||||
if (Threads.TryRemove(Thread, out PausableThread PausableThread))
|
||||
{
|
||||
Event.Set();
|
||||
Event.Dispose();
|
||||
PausableThread.Event.Set();
|
||||
PausableThread.Event.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,11 +49,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
if (SelectedCount == 0)
|
||||
{
|
||||
CoreManager.GetThread(Thread.CurrentThread).Reset();
|
||||
CoreManager.Reset(Thread.CurrentThread);
|
||||
}
|
||||
else if (SelectedCount == 1)
|
||||
{
|
||||
CoreManager.GetThread(Thread.CurrentThread).Set();
|
||||
CoreManager.Set(Thread.CurrentThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
CoreManager.GetThread(CurrentThread.Context.Work).Reset();
|
||||
CoreManager.Reset(CurrentThread.Context.Work);
|
||||
}
|
||||
|
||||
//Advance current core and try picking a thread,
|
||||
|
@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
CoreContext.CurrentThread.ClearExclusive();
|
||||
|
||||
CoreManager.GetThread(CoreContext.CurrentThread.Context.Work).Set();
|
||||
CoreManager.Set(CoreContext.CurrentThread.Context.Work);
|
||||
|
||||
CoreContext.CurrentThread.Context.Execute();
|
||||
|
||||
|
@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
CoreManager.GetThread(Thread.CurrentThread).WaitOne();
|
||||
CoreManager.Wait(Thread.CurrentThread);
|
||||
}
|
||||
|
||||
private void PreemptCurrentThread()
|
||||
|
@ -134,11 +134,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
public void StopThread(KThread Thread)
|
||||
public void ExitThread(KThread Thread)
|
||||
{
|
||||
Thread.Context.StopExecution();
|
||||
|
||||
CoreManager.GetThread(Thread.Context.Work).Set();
|
||||
CoreManager.Exit(Thread.Context.Work);
|
||||
}
|
||||
|
||||
public void RemoveThread(KThread Thread)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using ChocolArm64.Memory;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
@ -23,39 +22,36 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
ArbiterThreads = new List<KThread>();
|
||||
}
|
||||
|
||||
public long ArbitrateLock(
|
||||
Process Process,
|
||||
MemoryManager Memory,
|
||||
int OwnerHandle,
|
||||
long MutexAddress,
|
||||
int RequesterHandle)
|
||||
public long ArbitrateLock(int OwnerHandle, long MutexAddress, int RequesterHandle)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
CurrentThread.SignaledObj = null;
|
||||
CurrentThread.ObjSyncResult = 0;
|
||||
|
||||
if (!UserToKernelInt32(Memory, MutexAddress, out int MutexValue))
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (!UserToKernelInt32(MutexAddress, out int MutexValue))
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);;
|
||||
}
|
||||
|
||||
if (MutexValue != (OwnerHandle | HasListenersMask))
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
KThread MutexOwner = Process.HandleTable.GetObject<KThread>(OwnerHandle);
|
||||
KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(OwnerHandle);
|
||||
|
||||
if (MutexOwner == null)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
|
@ -67,26 +63,26 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Leave();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (CurrentThread.MutexOwner != null)
|
||||
{
|
||||
CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return (uint)CurrentThread.ObjSyncResult;
|
||||
}
|
||||
|
||||
public long ArbitrateUnlock(MemoryManager Memory, long MutexAddress)
|
||||
public long ArbitrateUnlock(long MutexAddress)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
(long Result, KThread NewOwnerThread) = MutexUnlock(Memory, CurrentThread, MutexAddress);
|
||||
(long Result, KThread NewOwnerThread) = MutexUnlock(CurrentThread, MutexAddress);
|
||||
|
||||
if (Result != 0 && NewOwnerThread != null)
|
||||
{
|
||||
|
@ -94,19 +90,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
NewOwnerThread.ObjSyncResult = (int)Result;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public long WaitProcessWideKeyAtomic(
|
||||
MemoryManager Memory,
|
||||
long MutexAddress,
|
||||
long CondVarAddress,
|
||||
int ThreadHandle,
|
||||
long Timeout)
|
||||
long MutexAddress,
|
||||
long CondVarAddress,
|
||||
int ThreadHandle,
|
||||
long Timeout)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
|
@ -116,16 +111,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
if (CurrentThread.ShallBeTerminated ||
|
||||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||
}
|
||||
|
||||
(long Result, _) = MutexUnlock(Memory, CurrentThread, MutexAddress);
|
||||
(long Result, _) = MutexUnlock(CurrentThread, MutexAddress);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
@ -146,14 +141,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
if (Timeout > 0)
|
||||
{
|
||||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (CurrentThread.MutexOwner != null)
|
||||
{
|
||||
|
@ -162,12 +157,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
CondVarThreads.Remove(CurrentThread);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return (uint)CurrentThread.ObjSyncResult;
|
||||
}
|
||||
|
||||
private (long, KThread) MutexUnlock(MemoryManager Memory, KThread CurrentThread, long MutexAddress)
|
||||
private (long, KThread) MutexUnlock(KThread CurrentThread, long MutexAddress)
|
||||
{
|
||||
KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count);
|
||||
|
||||
|
@ -190,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
long Result = 0;
|
||||
|
||||
if (!KernelToUserInt32(Memory, MutexAddress, MutexValue))
|
||||
if (!KernelToUserInt32(MutexAddress, MutexValue))
|
||||
{
|
||||
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||
}
|
||||
|
@ -198,17 +193,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return (Result, NewOwnerThread);
|
||||
}
|
||||
|
||||
public void SignalProcessWideKey(Process Process, MemoryManager Memory, long Address, int Count)
|
||||
public void SignalProcessWideKey(long Address, int Count)
|
||||
{
|
||||
Queue<KThread> SignaledThreads = new Queue<KThread>();
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
IOrderedEnumerable<KThread> SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
|
||||
|
||||
foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address))
|
||||
{
|
||||
TryAcquireMutex(Process, Memory, Thread);
|
||||
TryAcquireMutex(Thread);
|
||||
|
||||
SignaledThreads.Enqueue(Thread);
|
||||
|
||||
|
@ -224,19 +219,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
CondVarThreads.Remove(Thread);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
private KThread TryAcquireMutex(Process Process, MemoryManager Memory, KThread Requester)
|
||||
private KThread TryAcquireMutex(KThread Requester)
|
||||
{
|
||||
long Address = Requester.MutexAddress;
|
||||
|
||||
Memory.SetExclusive(0, Address);
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (!UserToKernelInt32(Memory, Address, out int MutexValue))
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
if (!UserToKernelInt32(Address, out int MutexValue))
|
||||
{
|
||||
//Invalid address.
|
||||
Memory.ClearExclusive(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||
|
||||
Requester.SignaledObj = null;
|
||||
Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||
|
@ -246,27 +243,27 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
while (true)
|
||||
{
|
||||
if (Memory.TestExclusive(0, Address))
|
||||
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||
{
|
||||
if (MutexValue != 0)
|
||||
{
|
||||
//Update value to indicate there is a mutex waiter now.
|
||||
Memory.WriteInt32(Address, MutexValue | HasListenersMask);
|
||||
CurrentProcess.CpuMemory.WriteInt32(Address, MutexValue | HasListenersMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
//No thread owning the mutex, assign to requesting thread.
|
||||
Memory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
|
||||
CurrentProcess.CpuMemory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
|
||||
}
|
||||
|
||||
Memory.ClearExclusiveForStore(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Memory.SetExclusive(0, Address);
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
MutexValue = Memory.ReadInt32(Address);
|
||||
MutexValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||
}
|
||||
|
||||
if (MutexValue == 0)
|
||||
|
@ -282,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
MutexValue &= ~HasListenersMask;
|
||||
|
||||
KThread MutexOwner = Process.HandleTable.GetObject<KThread>(MutexValue);
|
||||
KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(MutexValue);
|
||||
|
||||
if (MutexOwner != null)
|
||||
{
|
||||
|
@ -301,16 +298,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return MutexOwner;
|
||||
}
|
||||
|
||||
public long WaitForAddressIfEqual(MemoryManager Memory, long Address, int Value, long Timeout)
|
||||
public long WaitForAddressIfEqual(long Address, int Value, long Timeout)
|
||||
{
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (CurrentThread.ShallBeTerminated ||
|
||||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||
}
|
||||
|
@ -318,9 +315,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
CurrentThread.SignaledObj = null;
|
||||
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||
|
||||
if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
|
||||
if (!UserToKernelInt32(Address, out int CurrentValue))
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||
}
|
||||
|
@ -329,7 +326,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
if (Timeout == 0)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||
}
|
||||
|
@ -346,14 +343,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
if (Timeout > 0)
|
||||
{
|
||||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (CurrentThread.WaitingInArbitration)
|
||||
{
|
||||
|
@ -362,31 +359,26 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
CurrentThread.WaitingInArbitration = false;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return CurrentThread.ObjSyncResult;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||
}
|
||||
|
||||
public long WaitForAddressIfLessThan(
|
||||
MemoryManager Memory,
|
||||
long Address,
|
||||
int Value,
|
||||
bool ShouldDecrement,
|
||||
long Timeout)
|
||||
public long WaitForAddressIfLessThan(long Address, int Value, bool ShouldDecrement, long Timeout)
|
||||
{
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (CurrentThread.ShallBeTerminated ||
|
||||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||
}
|
||||
|
@ -394,12 +386,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
CurrentThread.SignaledObj = null;
|
||||
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||
|
||||
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
||||
Memory.SetExclusive(0, Address);
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
|
||||
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
if (!UserToKernelInt32(Address, out int CurrentValue))
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||
}
|
||||
|
@ -408,28 +402,28 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
while (CurrentValue < Value)
|
||||
{
|
||||
if (Memory.TestExclusive(0, Address))
|
||||
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||
{
|
||||
Memory.WriteInt32(Address, CurrentValue - 1);
|
||||
CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue - 1);
|
||||
|
||||
Memory.ClearExclusiveForStore(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Memory.SetExclusive(0, Address);
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
CurrentValue = Memory.ReadInt32(Address);
|
||||
CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||
}
|
||||
}
|
||||
|
||||
Memory.ClearExclusive(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||
|
||||
if (CurrentValue < Value)
|
||||
{
|
||||
if (Timeout == 0)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||
}
|
||||
|
@ -446,14 +440,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
if (Timeout > 0)
|
||||
{
|
||||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (CurrentThread.WaitingInArbitration)
|
||||
{
|
||||
|
@ -462,12 +456,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
CurrentThread.WaitingInArbitration = false;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return CurrentThread.ObjSyncResult;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||
}
|
||||
|
@ -498,63 +492,65 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public long Signal(long Address, int Count)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
WakeArbiterThreads(Address, Count);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long SignalAndIncrementIfEqual(MemoryManager Memory, long Address, int Value, int Count)
|
||||
public long SignalAndIncrementIfEqual(long Address, int Value, int Count)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
Memory.SetExclusive(0, Address);
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
if (!UserToKernelInt32(Address, out int CurrentValue))
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||
}
|
||||
|
||||
while (CurrentValue == Value)
|
||||
{
|
||||
if (Memory.TestExclusive(0, Address))
|
||||
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||
{
|
||||
Memory.WriteInt32(Address, CurrentValue + 1);
|
||||
CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + 1);
|
||||
|
||||
Memory.ClearExclusiveForStore(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Memory.SetExclusive(0, Address);
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
CurrentValue = Memory.ReadInt32(Address);
|
||||
CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||
}
|
||||
|
||||
Memory.ClearExclusive(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||
|
||||
if (CurrentValue != Value)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||
}
|
||||
|
||||
WakeArbiterThreads(Address, Count);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long SignalAndModifyIfEqual(MemoryManager Memory, long Address, int Value, int Count)
|
||||
public long SignalAndModifyIfEqual(long Address, int Value, int Count)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
int Offset;
|
||||
|
||||
|
@ -580,43 +576,45 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
Offset = 1;
|
||||
}
|
||||
|
||||
Memory.SetExclusive(0, Address);
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
if (!UserToKernelInt32(Address, out int CurrentValue))
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||
}
|
||||
|
||||
while (CurrentValue == Value)
|
||||
{
|
||||
if (Memory.TestExclusive(0, Address))
|
||||
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||
{
|
||||
Memory.WriteInt32(Address, CurrentValue + Offset);
|
||||
CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + Offset);
|
||||
|
||||
Memory.ClearExclusiveForStore(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Memory.SetExclusive(0, Address);
|
||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||
|
||||
CurrentValue = Memory.ReadInt32(Address);
|
||||
CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||
}
|
||||
|
||||
Memory.ClearExclusive(0);
|
||||
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||
|
||||
if (CurrentValue != Value)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||
}
|
||||
|
||||
WakeArbiterThreads(Address, Count);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -649,11 +647,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
private bool UserToKernelInt32(MemoryManager Memory, long Address, out int Value)
|
||||
private bool UserToKernelInt32(long Address, out int Value)
|
||||
{
|
||||
if (Memory.IsMapped(Address))
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (CurrentProcess.CpuMemory.IsMapped(Address))
|
||||
{
|
||||
Value = Memory.ReadInt32(Address);
|
||||
Value = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -663,11 +663,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return false;
|
||||
}
|
||||
|
||||
private bool KernelToUserInt32(MemoryManager Memory, long Address, int Value)
|
||||
private bool KernelToUserInt32(long Address, int Value)
|
||||
{
|
||||
if (Memory.IsMapped(Address))
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (CurrentProcess.CpuMemory.IsMapped(Address))
|
||||
{
|
||||
Memory.WriteInt32ToSharedAddr(Address, Value);
|
||||
CurrentProcess.CpuMemory.WriteInt32ToSharedAddr(Address, Value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
83
Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
Normal file
83
Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KContextIdManager
|
||||
{
|
||||
private const int IdMasksCount = 8;
|
||||
|
||||
private int[] IdMasks;
|
||||
|
||||
private int NextFreeBitHint;
|
||||
|
||||
public KContextIdManager()
|
||||
{
|
||||
IdMasks = new int[IdMasksCount];
|
||||
}
|
||||
|
||||
public int GetId()
|
||||
{
|
||||
lock (IdMasks)
|
||||
{
|
||||
int Id = 0;
|
||||
|
||||
if (!TestBit(NextFreeBitHint))
|
||||
{
|
||||
Id = NextFreeBitHint;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int Index = 0; Index < IdMasksCount; Index++)
|
||||
{
|
||||
int Mask = IdMasks[Index];
|
||||
|
||||
int FirstFreeBit = BitUtils.CountLeadingZeros32((Mask + 1) & ~Mask);
|
||||
|
||||
if (FirstFreeBit < 32)
|
||||
{
|
||||
int BaseBit = Index * 32 + 31;
|
||||
|
||||
Id = BaseBit - FirstFreeBit;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (Index == IdMasksCount - 1)
|
||||
{
|
||||
throw new InvalidOperationException("Maximum number of Ids reached!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NextFreeBitHint = Id + 1;
|
||||
|
||||
SetBit(Id);
|
||||
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
|
||||
public void PutId(int Id)
|
||||
{
|
||||
lock (IdMasks)
|
||||
{
|
||||
ClearBit(Id);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TestBit(int Bit)
|
||||
{
|
||||
return (IdMasks[NextFreeBitHint / 32] & (1 << (NextFreeBitHint & 31))) != 0;
|
||||
}
|
||||
|
||||
private void SetBit(int Bit)
|
||||
{
|
||||
IdMasks[NextFreeBitHint / 32] |= (1 << (NextFreeBitHint & 31));
|
||||
}
|
||||
|
||||
private void ClearBit(int Bit)
|
||||
{
|
||||
IdMasks[NextFreeBitHint / 32] &= ~(1 << (NextFreeBitHint & 31));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
|
@ -48,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
if (CurrentThread != null)
|
||||
{
|
||||
CoreManager.GetThread(CurrentThread.Context.Work).Reset();
|
||||
CoreManager.Reset(CurrentThread.Context.Work);
|
||||
}
|
||||
|
||||
CurrentThread = SelectedThread;
|
||||
|
@ -57,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
CurrentThread.ClearExclusive();
|
||||
|
||||
CoreManager.GetThread(CurrentThread.Context.Work).Set();
|
||||
CoreManager.Set(CurrentThread.Context.Work);
|
||||
|
||||
CurrentThread.Context.Execute();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Threading;
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KRecursiveLock
|
||||
class KCriticalSection
|
||||
{
|
||||
private Horizon System;
|
||||
|
||||
|
@ -11,21 +11,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private int RecursionCount;
|
||||
|
||||
public KRecursiveLock(Horizon System)
|
||||
public KCriticalSection(Horizon System)
|
||||
{
|
||||
this.System = System;
|
||||
|
||||
LockObj = new object();
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
public void Enter()
|
||||
{
|
||||
Monitor.Enter(LockObj);
|
||||
|
||||
RecursionCount++;
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
public void Leave()
|
||||
{
|
||||
if (RecursionCount == 0)
|
||||
{
|
22
Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
Normal file
22
Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KMemoryArrange
|
||||
{
|
||||
public KMemoryArrangeRegion Service { get; private set; }
|
||||
public KMemoryArrangeRegion NvServices { get; private set; }
|
||||
public KMemoryArrangeRegion Applet { get; private set; }
|
||||
public KMemoryArrangeRegion Application { get; private set; }
|
||||
|
||||
public KMemoryArrange(
|
||||
KMemoryArrangeRegion Service,
|
||||
KMemoryArrangeRegion NvServices,
|
||||
KMemoryArrangeRegion Applet,
|
||||
KMemoryArrangeRegion Application)
|
||||
{
|
||||
this.Service = Service;
|
||||
this.NvServices = NvServices;
|
||||
this.Applet = Applet;
|
||||
this.Application = Application;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
Normal file
16
Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
struct KMemoryArrangeRegion
|
||||
{
|
||||
public long Address { get; private set; }
|
||||
public long Size { get; private set; }
|
||||
|
||||
public long EndAddr => Address + Size;
|
||||
|
||||
public KMemoryArrangeRegion(long Address, long Size)
|
||||
{
|
||||
this.Address = Address;
|
||||
this.Size = Size;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
43
Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
Normal file
43
Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KMemoryRegionBlock
|
||||
{
|
||||
public long[][] Masks;
|
||||
|
||||
public long FreeCount;
|
||||
public int MaxLevel;
|
||||
public long StartAligned;
|
||||
public long SizeInBlocksTruncated;
|
||||
public long SizeInBlocksRounded;
|
||||
public int Order;
|
||||
public int NextOrder;
|
||||
|
||||
public bool TryCoalesce(int Index, int Size)
|
||||
{
|
||||
long Mask = ((1L << Size) - 1) << (Index & 63);
|
||||
|
||||
Index /= 64;
|
||||
|
||||
if ((Mask & ~Masks[MaxLevel - 1][Index]) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Masks[MaxLevel - 1][Index] &= ~Mask;
|
||||
|
||||
for (int Level = MaxLevel - 2; Level >= 0; Level--, Index /= 64)
|
||||
{
|
||||
Masks[Level][Index / 64] &= ~(1L << (Index & 63));
|
||||
|
||||
if (Masks[Level][Index / 64] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FreeCount -= Size;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
404
Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
Normal file
404
Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
Normal file
|
@ -0,0 +1,404 @@
|
|||
using Ryujinx.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KMemoryRegionManager
|
||||
{
|
||||
private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
|
||||
|
||||
private long Address;
|
||||
private long EndAddr;
|
||||
private long Size;
|
||||
private int BlockOrdersCount;
|
||||
|
||||
private KMemoryRegionBlock[] Blocks;
|
||||
|
||||
public KMemoryRegionManager(long Address, long Size, long EndAddr)
|
||||
{
|
||||
Blocks = new KMemoryRegionBlock[BlockOrders.Length];
|
||||
|
||||
this.Address = Address;
|
||||
this.Size = Size;
|
||||
this.EndAddr = EndAddr;
|
||||
|
||||
BlockOrdersCount = BlockOrders.Length;
|
||||
|
||||
for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
|
||||
{
|
||||
Blocks[BlockIndex] = new KMemoryRegionBlock();
|
||||
|
||||
Blocks[BlockIndex].Order = BlockOrders[BlockIndex];
|
||||
|
||||
int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1];
|
||||
|
||||
Blocks[BlockIndex].NextOrder = NextOrder;
|
||||
|
||||
int CurrBlockSize = 1 << BlockOrders[BlockIndex];
|
||||
int NextBlockSize = CurrBlockSize;
|
||||
|
||||
if (NextOrder != 0)
|
||||
{
|
||||
NextBlockSize = 1 << NextOrder;
|
||||
}
|
||||
|
||||
long StartAligned = BitUtils.AlignDown(Address, NextBlockSize);
|
||||
long EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize);
|
||||
|
||||
ulong SizeInBlocksTruncated = (ulong)(EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex];
|
||||
|
||||
long EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize);
|
||||
|
||||
ulong SizeInBlocksRounded = (ulong)(EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex];
|
||||
|
||||
Blocks[BlockIndex].StartAligned = StartAligned;
|
||||
Blocks[BlockIndex].SizeInBlocksTruncated = (long)SizeInBlocksTruncated;
|
||||
Blocks[BlockIndex].SizeInBlocksRounded = (long)SizeInBlocksRounded;
|
||||
|
||||
ulong CurrSizeInBlocks = SizeInBlocksRounded;
|
||||
|
||||
int MaxLevel = 0;
|
||||
|
||||
do
|
||||
{
|
||||
MaxLevel++;
|
||||
}
|
||||
while ((CurrSizeInBlocks /= 64) != 0);
|
||||
|
||||
Blocks[BlockIndex].MaxLevel = MaxLevel;
|
||||
|
||||
Blocks[BlockIndex].Masks = new long[MaxLevel][];
|
||||
|
||||
CurrSizeInBlocks = SizeInBlocksRounded;
|
||||
|
||||
for (int Level = MaxLevel - 1; Level >= 0; Level--)
|
||||
{
|
||||
CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64;
|
||||
|
||||
Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks];
|
||||
}
|
||||
}
|
||||
|
||||
if (Size != 0)
|
||||
{
|
||||
FreePages(Address, Size / KMemoryManager.PageSize);
|
||||
}
|
||||
}
|
||||
|
||||
public KernelResult AllocatePages(long PagesCount, bool Backwards, out KPageList PageList)
|
||||
{
|
||||
lock (Blocks)
|
||||
{
|
||||
return AllocatePagesImpl(PagesCount, Backwards, out PageList);
|
||||
}
|
||||
}
|
||||
|
||||
private KernelResult AllocatePagesImpl(long PagesCount, bool Backwards, out KPageList PageList)
|
||||
{
|
||||
Backwards = false;
|
||||
|
||||
PageList = new KPageList();
|
||||
|
||||
if (BlockOrdersCount > 0)
|
||||
{
|
||||
ulong AvailablePages = 0;
|
||||
|
||||
for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
|
||||
{
|
||||
KMemoryRegionBlock Block = Blocks[BlockIndex];
|
||||
|
||||
ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
|
||||
|
||||
AvailablePages += BlockPagesCount * (ulong)Block.FreeCount;
|
||||
}
|
||||
|
||||
if (AvailablePages < (ulong)PagesCount)
|
||||
{
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
}
|
||||
else if (PagesCount != 0)
|
||||
{
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
for (int BlockIndex = BlockOrdersCount - 1; BlockIndex >= 0; BlockIndex--)
|
||||
{
|
||||
KMemoryRegionBlock Block = Blocks[BlockIndex];
|
||||
|
||||
int BestFitBlockSize = 1 << Block.Order;
|
||||
|
||||
int BlockPagesCount = BestFitBlockSize / KMemoryManager.PageSize;
|
||||
|
||||
//Check if this is the best fit for this page size.
|
||||
//If so, try allocating as much requested pages as possible.
|
||||
while ((ulong)BlockPagesCount <= (ulong)PagesCount)
|
||||
{
|
||||
long Address = 0;
|
||||
|
||||
for (int CurrBlockIndex = BlockIndex;
|
||||
CurrBlockIndex < BlockOrdersCount && Address == 0;
|
||||
CurrBlockIndex++)
|
||||
{
|
||||
Block = Blocks[CurrBlockIndex];
|
||||
|
||||
int Index = 0;
|
||||
|
||||
bool ZeroMask = false;
|
||||
|
||||
for (int Level = 0; Level < Block.MaxLevel; Level++)
|
||||
{
|
||||
long Mask = Block.Masks[Level][Index];
|
||||
|
||||
if (Mask == 0)
|
||||
{
|
||||
ZeroMask = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (Backwards)
|
||||
{
|
||||
Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
|
||||
}
|
||||
}
|
||||
|
||||
if ((ulong)Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Block.FreeCount--;
|
||||
|
||||
int TempIdx = Index;
|
||||
|
||||
for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
|
||||
{
|
||||
Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
|
||||
|
||||
if (Block.Masks[Level][TempIdx / 64] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Address = Block.StartAligned + ((long)Index << Block.Order);
|
||||
}
|
||||
|
||||
for (int CurrBlockIndex = BlockIndex;
|
||||
CurrBlockIndex < BlockOrdersCount && Address == 0;
|
||||
CurrBlockIndex++)
|
||||
{
|
||||
Block = Blocks[CurrBlockIndex];
|
||||
|
||||
int Index = 0;
|
||||
|
||||
bool ZeroMask = false;
|
||||
|
||||
for (int Level = 0; Level < Block.MaxLevel; Level++)
|
||||
{
|
||||
long Mask = Block.Masks[Level][Index];
|
||||
|
||||
if (Mask == 0)
|
||||
{
|
||||
ZeroMask = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (Backwards)
|
||||
{
|
||||
Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
|
||||
}
|
||||
else
|
||||
{
|
||||
Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
|
||||
}
|
||||
}
|
||||
|
||||
if ((ulong)Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Block.FreeCount--;
|
||||
|
||||
int TempIdx = Index;
|
||||
|
||||
for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
|
||||
{
|
||||
Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
|
||||
|
||||
if (Block.Masks[Level][TempIdx / 64] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Address = Block.StartAligned + ((long)Index << Block.Order);
|
||||
}
|
||||
|
||||
//The address being zero means that no free space was found on that order,
|
||||
//just give up and try with the next one.
|
||||
if (Address == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//If we are using a larger order than best fit, then we should
|
||||
//split it into smaller blocks.
|
||||
int FirstFreeBlockSize = 1 << Block.Order;
|
||||
|
||||
if (FirstFreeBlockSize > BestFitBlockSize)
|
||||
{
|
||||
FreePages(Address + BestFitBlockSize, (FirstFreeBlockSize - BestFitBlockSize) / KMemoryManager.PageSize);
|
||||
}
|
||||
|
||||
//Add new allocated page(s) to the pages list.
|
||||
//If an error occurs, then free all allocated pages and fail.
|
||||
KernelResult Result = PageList.AddRange(Address, BlockPagesCount);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
FreePages(Address, BlockPagesCount);
|
||||
|
||||
foreach (KPageNode PageNode in PageList)
|
||||
{
|
||||
FreePages(PageNode.Address, PageNode.PagesCount);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
PagesCount -= BlockPagesCount;
|
||||
}
|
||||
}
|
||||
|
||||
//Success case, all requested pages were allocated successfully.
|
||||
if (PagesCount == 0)
|
||||
{
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
//Error case, free allocated pages and return out of memory.
|
||||
foreach (KPageNode PageNode in PageList)
|
||||
{
|
||||
FreePages(PageNode.Address, PageNode.PagesCount);
|
||||
}
|
||||
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
public void FreePages(long Address, long PagesCount)
|
||||
{
|
||||
long EndAddr = Address + PagesCount * KMemoryManager.PageSize;
|
||||
|
||||
int BlockIndex = BlockOrdersCount - 1;
|
||||
|
||||
long AddressRounded = 0;
|
||||
long EndAddrTruncated = 0;
|
||||
|
||||
for (; BlockIndex >= 0; BlockIndex--)
|
||||
{
|
||||
KMemoryRegionBlock AllocInfo = Blocks[BlockIndex];
|
||||
|
||||
long BlockSize = 1L << AllocInfo.Order;
|
||||
|
||||
AddressRounded = (Address + BlockSize - 1) & -BlockSize;
|
||||
|
||||
EndAddrTruncated = EndAddr & -BlockSize;
|
||||
|
||||
if ((ulong)AddressRounded < (ulong)EndAddrTruncated)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeRegion(long CurrAddress)
|
||||
{
|
||||
for (int CurrBlockIndex = BlockIndex;
|
||||
CurrBlockIndex < BlockOrdersCount && CurrAddress != 0;
|
||||
CurrBlockIndex++)
|
||||
{
|
||||
KMemoryRegionBlock Block = Blocks[CurrBlockIndex];
|
||||
|
||||
Block.FreeCount++;
|
||||
|
||||
ulong FreedBlocks = (ulong)(CurrAddress - Block.StartAligned) >> Block.Order;
|
||||
|
||||
int Index = (int)FreedBlocks;
|
||||
|
||||
for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64)
|
||||
{
|
||||
long Mask = Block.Masks[Level][Index / 64];
|
||||
|
||||
Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63));
|
||||
|
||||
if (Mask != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order);
|
||||
|
||||
int FreedBlocksTruncated = (int)FreedBlocks & -BlockSizeDelta;
|
||||
|
||||
if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
CurrAddress = Block.StartAligned + ((long)FreedBlocksTruncated << Block.Order);
|
||||
}
|
||||
}
|
||||
|
||||
//Free inside aligned region.
|
||||
long BaseAddress = AddressRounded;
|
||||
|
||||
while ((ulong)BaseAddress < (ulong)EndAddrTruncated)
|
||||
{
|
||||
long BlockSize = 1L << Blocks[BlockIndex].Order;
|
||||
|
||||
FreeRegion(BaseAddress);
|
||||
|
||||
BaseAddress += BlockSize;
|
||||
}
|
||||
|
||||
int NextBlockIndex = BlockIndex - 1;
|
||||
|
||||
//Free region between Address and aligned region start.
|
||||
BaseAddress = AddressRounded;
|
||||
|
||||
for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
|
||||
{
|
||||
long BlockSize = 1L << Blocks[BlockIndex].Order;
|
||||
|
||||
while ((ulong)(BaseAddress - BlockSize) >= (ulong)Address)
|
||||
{
|
||||
BaseAddress -= BlockSize;
|
||||
|
||||
FreeRegion(BaseAddress);
|
||||
}
|
||||
}
|
||||
|
||||
//Free region between aligned region end and End Address.
|
||||
BaseAddress = EndAddrTruncated;
|
||||
|
||||
for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
|
||||
{
|
||||
long BlockSize = 1L << Blocks[BlockIndex].Order;
|
||||
|
||||
while ((ulong)(BaseAddress + BlockSize) <= (ulong)EndAddr)
|
||||
{
|
||||
FreeRegion(BaseAddress);
|
||||
|
||||
BaseAddress += BlockSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
Ryujinx.HLE/HOS/Kernel/KPageList.cs
Normal file
60
Ryujinx.HLE/HOS/Kernel/KPageList.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KPageList : IEnumerable<KPageNode>
|
||||
{
|
||||
private LinkedList<KPageNode> Nodes;
|
||||
|
||||
public KPageList()
|
||||
{
|
||||
Nodes = new LinkedList<KPageNode>();
|
||||
}
|
||||
|
||||
public KernelResult AddRange(long Address, long PagesCount)
|
||||
{
|
||||
if (PagesCount != 0)
|
||||
{
|
||||
if (Nodes.Last != null)
|
||||
{
|
||||
KPageNode LastNode = Nodes.Last.Value;
|
||||
|
||||
if (LastNode.Address + LastNode.PagesCount * 4096 == Address)
|
||||
{
|
||||
Address = LastNode.Address;
|
||||
PagesCount += LastNode.PagesCount;
|
||||
|
||||
Nodes.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
Nodes.AddLast(new KPageNode(Address, PagesCount));
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
public long GetPagesCount()
|
||||
{
|
||||
long Sum = 0;
|
||||
|
||||
foreach (KPageNode Node in Nodes)
|
||||
{
|
||||
Sum += Node.PagesCount;
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
public IEnumerator<KPageNode> GetEnumerator()
|
||||
{
|
||||
return Nodes.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.HLE/HOS/Kernel/KPageNode.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/KPageNode.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
struct KPageNode
|
||||
{
|
||||
public long Address;
|
||||
public long PagesCount;
|
||||
|
||||
public KPageNode(long Address, long PagesCount)
|
||||
{
|
||||
this.Address = Address;
|
||||
this.PagesCount = PagesCount;
|
||||
}
|
||||
}
|
||||
}
|
885
Ryujinx.HLE/HOS/Kernel/KProcess.cs
Normal file
885
Ryujinx.HLE/HOS/Kernel/KProcess.cs
Normal file
|
@ -0,0 +1,885 @@
|
|||
using ChocolArm64;
|
||||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KProcess : KSynchronizationObject
|
||||
{
|
||||
public const int KernelVersionMajor = 10;
|
||||
public const int KernelVersionMinor = 4;
|
||||
public const int KernelVersionRevision = 0;
|
||||
|
||||
public const int KernelVersionPacked =
|
||||
(KernelVersionMajor << 19) |
|
||||
(KernelVersionMinor << 15) |
|
||||
(KernelVersionRevision << 0);
|
||||
|
||||
public KMemoryManager MemoryManager { get; private set; }
|
||||
|
||||
private long TotalMemoryUsage;
|
||||
|
||||
private SortedDictionary<long, KTlsPageInfo> FullTlsPages;
|
||||
private SortedDictionary<long, KTlsPageInfo> FreeTlsPages;
|
||||
|
||||
public int DefaultCpuCore { get; private set; }
|
||||
|
||||
public KResourceLimit ResourceLimit { get; private set; }
|
||||
|
||||
private long PersonalMmHeapBase;
|
||||
private long PersonalMmHeapPagesCount;
|
||||
|
||||
private ProcessState State;
|
||||
|
||||
private object ProcessLock;
|
||||
private object ThreadingLock;
|
||||
|
||||
public KAddressArbiter AddressArbiter { get; private set; }
|
||||
|
||||
private long[] RandomEntropy;
|
||||
|
||||
private bool Signaled;
|
||||
private bool UseSystemMemBlocks;
|
||||
|
||||
private string Name;
|
||||
|
||||
private int ThreadCount;
|
||||
|
||||
public int MmuFlags { get; private set; }
|
||||
|
||||
private MemoryRegion MemRegion;
|
||||
|
||||
public KProcessCapabilities Capabilities { get; private set; }
|
||||
|
||||
public long TitleId { get; private set; }
|
||||
public long Pid { get; private set; }
|
||||
|
||||
private long CreationTimestamp;
|
||||
private long Entrypoint;
|
||||
private long ImageSize;
|
||||
private long MainThreadStackSize;
|
||||
private long MemoryUsageCapacity;
|
||||
private int Category;
|
||||
|
||||
public KProcessHandleTable HandleTable { get; private set; }
|
||||
|
||||
private long TlsAddress;
|
||||
|
||||
private LinkedList<KThread> Threads;
|
||||
|
||||
public bool IsPaused { get; private set; }
|
||||
|
||||
public Translator Translator { get; private set; }
|
||||
|
||||
public MemoryManager CpuMemory { get; private set; }
|
||||
|
||||
private SvcHandler SvcHandler;
|
||||
|
||||
public KProcess(Horizon System) : base(System)
|
||||
{
|
||||
ProcessLock = new object();
|
||||
ThreadingLock = new object();
|
||||
|
||||
CpuMemory = new MemoryManager(System.Device.Memory.RamPointer);
|
||||
|
||||
AddressArbiter = new KAddressArbiter(System);
|
||||
|
||||
MemoryManager = new KMemoryManager(System, CpuMemory);
|
||||
|
||||
FullTlsPages = new SortedDictionary<long, KTlsPageInfo>();
|
||||
FreeTlsPages = new SortedDictionary<long, KTlsPageInfo>();
|
||||
|
||||
ResourceLimit = new KResourceLimit();
|
||||
|
||||
Capabilities = new KProcessCapabilities();
|
||||
|
||||
RandomEntropy = new long[KScheduler.CpuCoresCount];
|
||||
|
||||
Threads = new LinkedList<KThread>();
|
||||
|
||||
Translator = new Translator();
|
||||
|
||||
SvcHandler = new SvcHandler(System.Device, this);
|
||||
}
|
||||
|
||||
public KernelResult InitializeKip(
|
||||
ProcessCreationInfo CreationInfo,
|
||||
int[] Caps,
|
||||
KPageList PageList,
|
||||
KResourceLimit ResourceLimit,
|
||||
MemoryRegion MemRegion)
|
||||
{
|
||||
AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
|
||||
|
||||
bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
|
||||
|
||||
long CodeAddress = CreationInfo.CodeAddress;
|
||||
|
||||
long CodeSize = (long)(uint)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
|
||||
|
||||
KernelResult Result = MemoryManager.InitializeForProcess(
|
||||
AddrSpaceType,
|
||||
AslrEnabled,
|
||||
!AslrEnabled,
|
||||
MemRegion,
|
||||
CodeAddress,
|
||||
CodeSize);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
|
||||
{
|
||||
return KernelResult.InvalidMemRange;
|
||||
}
|
||||
|
||||
Result = MemoryManager.MapPages(
|
||||
CodeAddress,
|
||||
PageList,
|
||||
MemoryState.CodeStatic,
|
||||
MemoryPermission.None);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
Result = Capabilities.InitializeForKernel(Caps, MemoryManager);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
Pid = System.GetKipId();
|
||||
|
||||
if (Pid == 0 || (ulong)Pid >= Horizon.InitialProcessId)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
|
||||
}
|
||||
|
||||
Result = ParseProcessInfo(CreationInfo);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public KernelResult Initialize(
|
||||
ProcessCreationInfo CreationInfo,
|
||||
int[] Caps,
|
||||
KResourceLimit ResourceLimit,
|
||||
MemoryRegion MemRegion)
|
||||
{
|
||||
long PersonalMmHeapSize = CreationInfo.PersonalMmHeapPagesCount * KMemoryManager.PageSize;
|
||||
|
||||
long CodePagesCount = (long)(uint)CreationInfo.CodePagesCount;
|
||||
|
||||
long NeededSizeForProcess = PersonalMmHeapSize + CodePagesCount * KMemoryManager.PageSize;
|
||||
|
||||
if (NeededSizeForProcess != 0 && ResourceLimit != null)
|
||||
{
|
||||
if (!ResourceLimit.Reserve(LimitableResource.Memory, NeededSizeForProcess))
|
||||
{
|
||||
return KernelResult.ResLimitExceeded;
|
||||
}
|
||||
}
|
||||
|
||||
void CleanUpForError()
|
||||
{
|
||||
if (NeededSizeForProcess != 0 && ResourceLimit != null)
|
||||
{
|
||||
ResourceLimit.Release(LimitableResource.Memory, NeededSizeForProcess);
|
||||
}
|
||||
}
|
||||
|
||||
PersonalMmHeapPagesCount = CreationInfo.PersonalMmHeapPagesCount;
|
||||
|
||||
if (PersonalMmHeapPagesCount != 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
|
||||
|
||||
bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
|
||||
|
||||
long CodeAddress = CreationInfo.CodeAddress;
|
||||
|
||||
long CodeSize = CodePagesCount * KMemoryManager.PageSize;
|
||||
|
||||
KernelResult Result = MemoryManager.InitializeForProcess(
|
||||
AddrSpaceType,
|
||||
AslrEnabled,
|
||||
!AslrEnabled,
|
||||
MemRegion,
|
||||
CodeAddress,
|
||||
CodeSize);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return KernelResult.InvalidMemRange;
|
||||
}
|
||||
|
||||
Result = MemoryManager.MapNewProcessCode(
|
||||
CodeAddress,
|
||||
CodePagesCount,
|
||||
MemoryState.CodeStatic,
|
||||
MemoryPermission.None);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
Result = Capabilities.InitializeForUser(Caps, MemoryManager);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
Pid = System.GetProcessId();
|
||||
|
||||
if (Pid == -1 || (ulong)Pid < Horizon.InitialProcessId)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid Process Id {Pid}.");
|
||||
}
|
||||
|
||||
Result = ParseProcessInfo(CreationInfo);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private bool ValidateCodeAddressAndSize(long CodeAddress, long CodeSize)
|
||||
{
|
||||
long CodeRegionStart;
|
||||
long CodeRegionSize;
|
||||
|
||||
switch (MemoryManager.AddrSpaceWidth)
|
||||
{
|
||||
case 32:
|
||||
CodeRegionStart = 0x200000;
|
||||
CodeRegionSize = 0x3fe00000;
|
||||
break;
|
||||
|
||||
case 36:
|
||||
CodeRegionStart = 0x8000000;
|
||||
CodeRegionSize = 0x78000000;
|
||||
break;
|
||||
|
||||
case 39:
|
||||
CodeRegionStart = 0x8000000;
|
||||
CodeRegionSize = 0x7ff8000000;
|
||||
break;
|
||||
|
||||
default: throw new InvalidOperationException("Invalid address space width on memory manager.");
|
||||
}
|
||||
|
||||
long CodeEndAddr = CodeAddress + CodeSize;
|
||||
|
||||
long CodeRegionEnd = CodeRegionStart + CodeRegionSize;
|
||||
|
||||
if ((ulong)CodeEndAddr <= (ulong)CodeAddress ||
|
||||
(ulong)CodeEndAddr - 1 > (ulong)CodeRegionEnd - 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (MemoryManager.InsideHeapRegion(CodeAddress, CodeSize) ||
|
||||
MemoryManager.InsideMapRegion (CodeAddress, CodeSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private KernelResult ParseProcessInfo(ProcessCreationInfo CreationInfo)
|
||||
{
|
||||
//Ensure that the current kernel version is equal or above to the minimum required.
|
||||
uint RequiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
|
||||
uint RequiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
|
||||
|
||||
if (RequiredKernelVersionMajor > KernelVersionMajor)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
if (RequiredKernelVersionMajor != KernelVersionMajor && RequiredKernelVersionMajor < 3)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
if (RequiredKernelVersionMinor > KernelVersionMinor)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
KernelResult Result = AllocateThreadLocalStorage(out TlsAddress);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
MemoryHelper.FillWithZeros(CpuMemory, TlsAddress, KTlsPageInfo.TlsEntrySize);
|
||||
|
||||
Name = CreationInfo.Name;
|
||||
|
||||
State = ProcessState.Created;
|
||||
|
||||
CreationTimestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
MmuFlags = CreationInfo.MmuFlags;
|
||||
Category = CreationInfo.Category;
|
||||
TitleId = CreationInfo.TitleId;
|
||||
Entrypoint = CreationInfo.CodeAddress;
|
||||
ImageSize = CreationInfo.CodePagesCount * KMemoryManager.PageSize;
|
||||
|
||||
UseSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0;
|
||||
|
||||
switch ((AddressSpaceType)((MmuFlags >> 1) & 7))
|
||||
{
|
||||
case AddressSpaceType.Addr32Bits:
|
||||
case AddressSpaceType.Addr36Bits:
|
||||
case AddressSpaceType.Addr39Bits:
|
||||
MemoryUsageCapacity = MemoryManager.HeapRegionEnd -
|
||||
MemoryManager.HeapRegionStart;
|
||||
break;
|
||||
|
||||
case AddressSpaceType.Addr32BitsNoMap:
|
||||
MemoryUsageCapacity = MemoryManager.HeapRegionEnd -
|
||||
MemoryManager.HeapRegionStart +
|
||||
MemoryManager.AliasRegionEnd -
|
||||
MemoryManager.AliasRegionStart;
|
||||
break;
|
||||
|
||||
default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}.");
|
||||
}
|
||||
|
||||
GenerateRandomEntropy();
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
public KernelResult AllocateThreadLocalStorage(out long Address)
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
KernelResult Result;
|
||||
|
||||
if (FreeTlsPages.Count > 0)
|
||||
{
|
||||
//If we have free TLS pages available, just use the first one.
|
||||
KTlsPageInfo PageInfo = FreeTlsPages.Values.First();
|
||||
|
||||
if (!PageInfo.TryGetFreePage(out Address))
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure getting free TLS page!");
|
||||
}
|
||||
|
||||
if (PageInfo.IsFull())
|
||||
{
|
||||
FreeTlsPages.Remove(PageInfo.PageAddr);
|
||||
|
||||
FullTlsPages.Add(PageInfo.PageAddr, PageInfo);
|
||||
}
|
||||
|
||||
Result = KernelResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Otherwise, we need to create a new one.
|
||||
Result = AllocateTlsPage(out KTlsPageInfo PageInfo);
|
||||
|
||||
if (Result == KernelResult.Success)
|
||||
{
|
||||
if (!PageInfo.TryGetFreePage(out Address))
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure getting free TLS page!");
|
||||
}
|
||||
|
||||
FreeTlsPages.Add(PageInfo.PageAddr, PageInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Address = 0;
|
||||
}
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private KernelResult AllocateTlsPage(out KTlsPageInfo PageInfo)
|
||||
{
|
||||
PageInfo = default(KTlsPageInfo);
|
||||
|
||||
if (!System.UserSlabHeapPages.TryGetItem(out long TlsPagePa))
|
||||
{
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
long RegionStart = MemoryManager.TlsIoRegionStart;
|
||||
|
||||
long RegionPagesCount = (MemoryManager.TlsIoRegionEnd - RegionStart) / KMemoryManager.PageSize;
|
||||
|
||||
KernelResult Result = MemoryManager.AllocateOrMapPa(
|
||||
1,
|
||||
KMemoryManager.PageSize,
|
||||
TlsPagePa,
|
||||
true,
|
||||
RegionStart,
|
||||
RegionPagesCount,
|
||||
MemoryState.ThreadLocal,
|
||||
MemoryPermission.ReadAndWrite,
|
||||
out long TlsPageVa);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
System.UserSlabHeapPages.Free(TlsPagePa);
|
||||
}
|
||||
else
|
||||
{
|
||||
PageInfo = new KTlsPageInfo(TlsPageVa);
|
||||
|
||||
MemoryHelper.FillWithZeros(CpuMemory, TlsPageVa, KMemoryManager.PageSize);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public KernelResult FreeThreadLocalStorage(long TlsSlotAddr)
|
||||
{
|
||||
long TlsPageAddr = BitUtils.AlignDown(TlsSlotAddr, KMemoryManager.PageSize);
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
KernelResult Result = KernelResult.Success;
|
||||
|
||||
KTlsPageInfo PageInfo = null;
|
||||
|
||||
if (FullTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
|
||||
{
|
||||
//TLS page was full, free slot and move to free pages tree.
|
||||
FullTlsPages.Remove(TlsPageAddr);
|
||||
|
||||
FreeTlsPages.Add(TlsPageAddr, PageInfo);
|
||||
}
|
||||
else if (!FreeTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
|
||||
{
|
||||
Result = KernelResult.InvalidAddress;
|
||||
}
|
||||
|
||||
if (PageInfo != null)
|
||||
{
|
||||
PageInfo.FreeTlsSlot(TlsSlotAddr);
|
||||
|
||||
if (PageInfo.IsEmpty())
|
||||
{
|
||||
//TLS page is now empty, we should ensure it is removed
|
||||
//from all trees, and free the memory it was using.
|
||||
FreeTlsPages.Remove(TlsPageAddr);
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
FreeTlsPage(PageInfo);
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private KernelResult FreeTlsPage(KTlsPageInfo PageInfo)
|
||||
{
|
||||
KernelResult Result = MemoryManager.ConvertVaToPa(PageInfo.PageAddr, out long TlsPagePa);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
|
||||
}
|
||||
|
||||
Result = MemoryManager.UnmapForKernel(PageInfo.PageAddr, 1, MemoryState.ThreadLocal);
|
||||
|
||||
if (Result == KernelResult.Success)
|
||||
{
|
||||
System.UserSlabHeapPages.Free(TlsPagePa);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private void GenerateRandomEntropy()
|
||||
{
|
||||
//TODO.
|
||||
}
|
||||
|
||||
public KernelResult Start(int MainThreadPriority, long StackSize)
|
||||
{
|
||||
lock (ProcessLock)
|
||||
{
|
||||
if (State > ProcessState.CreatedAttached)
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1))
|
||||
{
|
||||
return KernelResult.ResLimitExceeded;
|
||||
}
|
||||
|
||||
KResourceLimit ThreadResourceLimit = ResourceLimit;
|
||||
KResourceLimit MemoryResourceLimit = null;
|
||||
|
||||
if (MainThreadStackSize != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Trying to start a process with a invalid state!");
|
||||
}
|
||||
|
||||
long StackSizeRounded = BitUtils.AlignUp(StackSize, KMemoryManager.PageSize);
|
||||
|
||||
long NeededSize = StackSizeRounded + ImageSize;
|
||||
|
||||
//Check if the needed size for the code and the stack will fit on the
|
||||
//memory usage capacity of this Process. Also check for possible overflow
|
||||
//on the above addition.
|
||||
if ((ulong)NeededSize > (ulong)MemoryUsageCapacity ||
|
||||
(ulong)NeededSize < (ulong)StackSizeRounded)
|
||||
{
|
||||
ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
if (StackSizeRounded != 0 && ResourceLimit != null)
|
||||
{
|
||||
MemoryResourceLimit = ResourceLimit;
|
||||
|
||||
if (!MemoryResourceLimit.Reserve(LimitableResource.Memory, StackSizeRounded))
|
||||
{
|
||||
ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||
|
||||
return KernelResult.ResLimitExceeded;
|
||||
}
|
||||
}
|
||||
|
||||
KernelResult Result;
|
||||
|
||||
KThread MainThread = null;
|
||||
|
||||
long StackTop = 0;
|
||||
|
||||
void CleanUpForError()
|
||||
{
|
||||
MainThread?.Terminate();
|
||||
HandleTable.Destroy();
|
||||
|
||||
if (MainThreadStackSize != 0)
|
||||
{
|
||||
long StackBottom = StackTop - MainThreadStackSize;
|
||||
|
||||
long StackPagesCount = MainThreadStackSize / KMemoryManager.PageSize;
|
||||
|
||||
MemoryManager.UnmapForKernel(StackBottom, StackPagesCount, MemoryState.Stack);
|
||||
}
|
||||
|
||||
MemoryResourceLimit?.Release(LimitableResource.Memory, StackSizeRounded);
|
||||
ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||
}
|
||||
|
||||
if (StackSizeRounded != 0)
|
||||
{
|
||||
long StackPagesCount = StackSizeRounded / KMemoryManager.PageSize;
|
||||
|
||||
long RegionStart = MemoryManager.StackRegionStart;
|
||||
long RegionPagesCount = (MemoryManager.StackRegionEnd - RegionStart) / KMemoryManager.PageSize;
|
||||
|
||||
Result = MemoryManager.AllocateOrMapPa(
|
||||
StackPagesCount,
|
||||
KMemoryManager.PageSize,
|
||||
0,
|
||||
false,
|
||||
RegionStart,
|
||||
RegionPagesCount,
|
||||
MemoryState.Stack,
|
||||
MemoryPermission.ReadAndWrite,
|
||||
out long StackBottom);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
MainThreadStackSize += StackSizeRounded;
|
||||
|
||||
StackTop = StackBottom + StackSizeRounded;
|
||||
}
|
||||
|
||||
long HeapCapacity = MemoryUsageCapacity - MainThreadStackSize - ImageSize;
|
||||
|
||||
Result = MemoryManager.SetHeapCapacity(HeapCapacity);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
HandleTable = new KProcessHandleTable(System);
|
||||
|
||||
Result = HandleTable.Initialize(Capabilities.HandleTableSize);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
MainThread = new KThread(System);
|
||||
|
||||
Result = MainThread.Initialize(
|
||||
Entrypoint,
|
||||
0,
|
||||
StackTop,
|
||||
MainThreadPriority,
|
||||
DefaultCpuCore,
|
||||
this);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
Result = HandleTable.GenerateHandle(MainThread, out int MainThreadHandle);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
MainThread.SetEntryArguments(0, MainThreadHandle);
|
||||
|
||||
ProcessState OldState = State;
|
||||
ProcessState NewState = State != ProcessState.Created
|
||||
? ProcessState.Attached
|
||||
: ProcessState.Started;
|
||||
|
||||
SetState(NewState);
|
||||
|
||||
//TODO: We can't call KThread.Start from a non-guest thread.
|
||||
//We will need to make some changes to allow the creation of
|
||||
//dummy threads that will be used to initialize the current
|
||||
//thread on KCoreContext so that GetCurrentThread doesn't fail.
|
||||
/* Result = MainThread.Start();
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
SetState(OldState);
|
||||
|
||||
CleanUpForError();
|
||||
} */
|
||||
|
||||
MainThread.TimeUp();
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetState(ProcessState NewState)
|
||||
{
|
||||
if (State != NewState)
|
||||
{
|
||||
State = NewState;
|
||||
Signaled = true;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
public KernelResult InitializeThread(
|
||||
KThread Thread,
|
||||
long Entrypoint,
|
||||
long ArgsPtr,
|
||||
long StackTop,
|
||||
int Priority,
|
||||
int CpuCore)
|
||||
{
|
||||
lock (ProcessLock)
|
||||
{
|
||||
return Thread.Initialize(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void SubscribeThreadEventHandlers(CpuThread Context)
|
||||
{
|
||||
Context.ThreadState.Interrupt += InterruptHandler;
|
||||
Context.ThreadState.SvcCall += SvcHandler.SvcCall;
|
||||
}
|
||||
|
||||
private void InterruptHandler(object sender, EventArgs e)
|
||||
{
|
||||
System.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
public void IncrementThreadCount()
|
||||
{
|
||||
Interlocked.Increment(ref ThreadCount);
|
||||
}
|
||||
|
||||
public void DecrementThreadCountAndTerminateIfZero()
|
||||
{
|
||||
if (Interlocked.Decrement(ref ThreadCount) == 0)
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
public long GetMemoryCapacity()
|
||||
{
|
||||
//TODO: Personal Mm Heap.
|
||||
return 0xcd500000;
|
||||
}
|
||||
|
||||
public long GetMemoryUsage()
|
||||
{
|
||||
//TODO: Personal Mm Heap.
|
||||
return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize();
|
||||
}
|
||||
|
||||
public void AddThread(KThread Thread)
|
||||
{
|
||||
lock (ThreadingLock)
|
||||
{
|
||||
Thread.ProcessListNode = Threads.AddLast(Thread);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveThread(KThread Thread)
|
||||
{
|
||||
lock (ThreadingLock)
|
||||
{
|
||||
Threads.Remove(Thread.ProcessListNode);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAllowedCpuCore(int Core)
|
||||
{
|
||||
return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0;
|
||||
}
|
||||
|
||||
public bool IsAllowedPriority(int Priority)
|
||||
{
|
||||
return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0;
|
||||
}
|
||||
|
||||
public override bool IsSignaled()
|
||||
{
|
||||
return Signaled;
|
||||
}
|
||||
|
||||
public KernelResult Terminate()
|
||||
{
|
||||
KernelResult Result;
|
||||
|
||||
bool ShallTerminate = false;
|
||||
|
||||
lock (ProcessLock)
|
||||
{
|
||||
if (State >= ProcessState.Started)
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (State == ProcessState.Started ||
|
||||
State == ProcessState.Crashed ||
|
||||
State == ProcessState.Attached ||
|
||||
State == ProcessState.DebugSuspended)
|
||||
{
|
||||
SetState(ProcessState.Exiting);
|
||||
|
||||
ShallTerminate = true;
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
Result = KernelResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = KernelResult.InvalidState;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShallTerminate)
|
||||
{
|
||||
UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
||||
|
||||
HandleTable.Destroy();
|
||||
|
||||
SignalExitForDebugEvent();
|
||||
SignalExit();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private void UnpauseAndTerminateAllThreadsExcept(KThread Thread)
|
||||
{
|
||||
//TODO.
|
||||
}
|
||||
|
||||
private void SignalExitForDebugEvent()
|
||||
{
|
||||
//TODO: Debug events.
|
||||
}
|
||||
|
||||
private void SignalExit()
|
||||
{
|
||||
if (ResourceLimit != null)
|
||||
{
|
||||
ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage());
|
||||
}
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
SetState(ProcessState.Exited);
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
}
|
||||
}
|
311
Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
Normal file
311
Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
Normal file
|
@ -0,0 +1,311 @@
|
|||
using Ryujinx.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KProcessCapabilities
|
||||
{
|
||||
public byte[] SvcAccessMask { get; private set; }
|
||||
public byte[] IrqAccessMask { get; private set; }
|
||||
|
||||
public long AllowedCpuCoresMask { get; private set; }
|
||||
public long AllowedThreadPriosMask { get; private set; }
|
||||
|
||||
public int DebuggingFlags { get; private set; }
|
||||
public int HandleTableSize { get; private set; }
|
||||
public int KernelReleaseVersion { get; private set; }
|
||||
public int ApplicationType { get; private set; }
|
||||
|
||||
public KProcessCapabilities()
|
||||
{
|
||||
SvcAccessMask = new byte[0x10];
|
||||
IrqAccessMask = new byte[0x80];
|
||||
}
|
||||
|
||||
public KernelResult InitializeForKernel(int[] Caps, KMemoryManager MemoryManager)
|
||||
{
|
||||
AllowedCpuCoresMask = 0xf;
|
||||
AllowedThreadPriosMask = -1;
|
||||
DebuggingFlags &= ~3;
|
||||
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
||||
|
||||
return Parse(Caps, MemoryManager);
|
||||
}
|
||||
|
||||
public KernelResult InitializeForUser(int[] Caps, KMemoryManager MemoryManager)
|
||||
{
|
||||
return Parse(Caps, MemoryManager);
|
||||
}
|
||||
|
||||
private KernelResult Parse(int[] Caps, KMemoryManager MemoryManager)
|
||||
{
|
||||
int Mask0 = 0;
|
||||
int Mask1 = 0;
|
||||
|
||||
for (int Index = 0; Index < Caps.Length; Index++)
|
||||
{
|
||||
int Cap = Caps[Index];
|
||||
|
||||
if (((Cap + 1) & ~Cap) != 0x40)
|
||||
{
|
||||
KernelResult Result = ParseCapability(Cap, ref Mask0, ref Mask1, MemoryManager);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((uint)Index + 1 >= Caps.Length)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
int PrevCap = Cap;
|
||||
|
||||
Cap = Caps[Index + 1];
|
||||
|
||||
if (((Cap + 1) & ~Cap) != 0x40)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
if ((Cap & 0x78000000) != 0)
|
||||
{
|
||||
return KernelResult.MaximumExceeded;
|
||||
}
|
||||
|
||||
if ((Cap & 0x7ffff80) == 0)
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
long Address = ((long)(uint)PrevCap << 5) & 0xffffff000;
|
||||
long Size = ((long)(uint)Cap << 5) & 0xfffff000;
|
||||
|
||||
if (((ulong)(Address + Size - 1) >> 36) != 0)
|
||||
{
|
||||
return KernelResult.InvalidAddress;
|
||||
}
|
||||
|
||||
MemoryPermission Perm = (PrevCap >> 31) != 0
|
||||
? MemoryPermission.Read
|
||||
: MemoryPermission.ReadAndWrite;
|
||||
|
||||
KernelResult Result;
|
||||
|
||||
if ((Cap >> 31) != 0)
|
||||
{
|
||||
Result = MemoryManager.MapNormalMemory(Address, Size, Perm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = MemoryManager.MapIoMemory(Address, Size, Perm);
|
||||
}
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private KernelResult ParseCapability(int Cap, ref int Mask0, ref int Mask1, KMemoryManager MemoryManager)
|
||||
{
|
||||
int Code = (Cap + 1) & ~Cap;
|
||||
|
||||
if (Code == 1)
|
||||
{
|
||||
return KernelResult.InvalidCapability;
|
||||
}
|
||||
else if (Code == 0)
|
||||
{
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
int CodeMask = 1 << (32 - BitUtils.CountLeadingZeros32(Code + 1));
|
||||
|
||||
//Check if the property was already set.
|
||||
if (((Mask0 & CodeMask) & 0x1e008) != 0)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
Mask0 |= CodeMask;
|
||||
|
||||
switch (Code)
|
||||
{
|
||||
case 8:
|
||||
{
|
||||
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
|
||||
{
|
||||
return KernelResult.InvalidCapability;
|
||||
}
|
||||
|
||||
int LowestCpuCore = (Cap >> 16) & 0xff;
|
||||
int HighestCpuCore = (Cap >> 24) & 0xff;
|
||||
|
||||
if (LowestCpuCore > HighestCpuCore)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
int HighestThreadPrio = (Cap >> 4) & 0x3f;
|
||||
int LowestThreadPrio = (Cap >> 10) & 0x3f;
|
||||
|
||||
if (LowestThreadPrio > HighestThreadPrio)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
if (HighestCpuCore >= KScheduler.CpuCoresCount)
|
||||
{
|
||||
return KernelResult.InvalidCpuCore;
|
||||
}
|
||||
|
||||
AllowedCpuCoresMask = GetMaskFromMinMax(LowestCpuCore, HighestCpuCore);
|
||||
AllowedThreadPriosMask = GetMaskFromMinMax(LowestThreadPrio, HighestThreadPrio);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x10:
|
||||
{
|
||||
int Slot = (Cap >> 29) & 7;
|
||||
|
||||
int SvcSlotMask = 1 << Slot;
|
||||
|
||||
if ((Mask1 & SvcSlotMask) != 0)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
Mask1 |= SvcSlotMask;
|
||||
|
||||
int SvcMask = (Cap >> 5) & 0xffffff;
|
||||
|
||||
int BaseSvc = Slot * 24;
|
||||
|
||||
for (int Index = 0; Index < 24; Index++)
|
||||
{
|
||||
if (((SvcMask >> Index) & 1) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int SvcId = BaseSvc + Index;
|
||||
|
||||
if (SvcId > 0x7f)
|
||||
{
|
||||
return KernelResult.MaximumExceeded;
|
||||
}
|
||||
|
||||
SvcAccessMask[SvcId / 8] |= (byte)(1 << (SvcId & 7));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
long Address = ((long)(uint)Cap << 4) & 0xffffff000;
|
||||
|
||||
MemoryManager.MapIoMemory(Address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x800:
|
||||
{
|
||||
//TODO: GIC distributor check.
|
||||
int Irq0 = (Cap >> 12) & 0x3ff;
|
||||
int Irq1 = (Cap >> 22) & 0x3ff;
|
||||
|
||||
if (Irq0 != 0x3ff)
|
||||
{
|
||||
IrqAccessMask[Irq0 / 8] |= (byte)(1 << (Irq0 & 7));
|
||||
}
|
||||
|
||||
if (Irq1 != 0x3ff)
|
||||
{
|
||||
IrqAccessMask[Irq1 / 8] |= (byte)(1 << (Irq1 & 7));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x2000:
|
||||
{
|
||||
int ApplicationType = Cap >> 14;
|
||||
|
||||
if ((uint)ApplicationType > 7)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
this.ApplicationType = ApplicationType;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4000:
|
||||
{
|
||||
//Note: This check is bugged on kernel too, we are just replicating the bug here.
|
||||
if ((KernelReleaseVersion >> 17) != 0 || Cap < 0x80000)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
KernelReleaseVersion = Cap;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x8000:
|
||||
{
|
||||
int HandleTableSize = Cap >> 26;
|
||||
|
||||
if ((uint)HandleTableSize > 0x3ff)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
this.HandleTableSize = HandleTableSize;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x10000:
|
||||
{
|
||||
int DebuggingFlags = Cap >> 19;
|
||||
|
||||
if ((uint)DebuggingFlags > 3)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
this.DebuggingFlags &= ~3;
|
||||
this.DebuggingFlags |= DebuggingFlags;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: return KernelResult.InvalidCapability;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private static long GetMaskFromMinMax(int Min, int Max)
|
||||
{
|
||||
int Range = Max - Min + 1;
|
||||
|
||||
long Mask = (1L << Range) - 1;
|
||||
|
||||
return Mask << Min;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private ushort IdCounter;
|
||||
|
||||
private object LockObj;
|
||||
public KProcessHandleTable(Horizon System)
|
||||
{
|
||||
this.System = System;
|
||||
}
|
||||
|
||||
public KProcessHandleTable(Horizon System, int Size = 1024)
|
||||
{
|
||||
|
@ -47,15 +50,51 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
Table[Size - 1].Next = null;
|
||||
|
||||
NextFreeEntry = TableHead;
|
||||
}
|
||||
|
||||
LockObj = new object();
|
||||
public KernelResult Initialize(int Size)
|
||||
{
|
||||
if ((uint)Size > 1024)
|
||||
{
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
if (Size < 1)
|
||||
{
|
||||
Size = 1024;
|
||||
}
|
||||
|
||||
this.Size = Size;
|
||||
|
||||
IdCounter = 1;
|
||||
|
||||
Table = new KHandleEntry[Size];
|
||||
|
||||
TableHead = new KHandleEntry(0);
|
||||
|
||||
KHandleEntry Entry = TableHead;
|
||||
|
||||
for (int Index = 0; Index < Size; Index++)
|
||||
{
|
||||
Table[Index] = Entry;
|
||||
|
||||
Entry.Next = new KHandleEntry(Index + 1);
|
||||
|
||||
Entry = Entry.Next;
|
||||
}
|
||||
|
||||
Table[Size - 1].Next = null;
|
||||
|
||||
NextFreeEntry = TableHead;
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
public KernelResult GenerateHandle(object Obj, out int Handle)
|
||||
{
|
||||
Handle = 0;
|
||||
|
||||
lock (LockObj)
|
||||
lock (Table)
|
||||
{
|
||||
if (ActiveSlotsCount >= Size)
|
||||
{
|
||||
|
@ -95,12 +134,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return false;
|
||||
}
|
||||
|
||||
int Index = (Handle >> 0) & 0x7fff;
|
||||
int Index = (Handle >> 0) & 0x7fff;
|
||||
int HandleId = (Handle >> 15);
|
||||
|
||||
bool Result = false;
|
||||
|
||||
lock (LockObj)
|
||||
lock (Table)
|
||||
{
|
||||
if (HandleId != 0 && Index < Size)
|
||||
{
|
||||
|
@ -125,10 +164,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public T GetObject<T>(int Handle)
|
||||
{
|
||||
int Index = (Handle >> 0) & 0x7fff;
|
||||
int Index = (Handle >> 0) & 0x7fff;
|
||||
int HandleId = (Handle >> 15);
|
||||
|
||||
lock (LockObj)
|
||||
lock (Table)
|
||||
{
|
||||
if ((Handle >> 30) == 0 && HandleId != 0)
|
||||
{
|
||||
|
@ -158,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public void Destroy()
|
||||
{
|
||||
lock (LockObj)
|
||||
lock (Table)
|
||||
{
|
||||
for (int Index = 0; Index < Size; Index++)
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public override void Signal()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (!Signaled)
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
base.Signal();
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public KernelResult Clear()
|
||||
|
@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
KernelResult Result;
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (Signaled)
|
||||
{
|
||||
|
@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
Result = KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
|
16
Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
Normal file
16
Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KResourceLimit
|
||||
{
|
||||
public bool Reserve(LimitableResource Resource, long Amount)
|
||||
{
|
||||
//TODO.
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Release(LimitableResource Resource, long Amount)
|
||||
{
|
||||
//TODO.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,14 +38,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private void PreemptThreads()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
PreemptThread(PreemptionPriorityCores012, 0);
|
||||
PreemptThread(PreemptionPriorityCores012, 1);
|
||||
PreemptThread(PreemptionPriorityCores012, 2);
|
||||
PreemptThread(PreemptionPriorityCore3, 3);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
private void PreemptThread(int Prio, int Core)
|
||||
|
@ -212,6 +212,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
throw new InvalidOperationException("Current thread is not scheduled!");
|
||||
}
|
||||
|
||||
public KProcess GetCurrentProcess()
|
||||
{
|
||||
return GetCurrentThread().Owner;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
|
50
Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
Normal file
50
Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KSlabHeap
|
||||
{
|
||||
private LinkedList<long> Items;
|
||||
|
||||
public KSlabHeap(long Pa, long ItemSize, long Size)
|
||||
{
|
||||
Items = new LinkedList<long>();
|
||||
|
||||
int ItemsCount = (int)(Size / ItemSize);
|
||||
|
||||
for (int Index = 0; Index < ItemsCount; Index++)
|
||||
{
|
||||
Items.AddLast(Pa);
|
||||
|
||||
Pa += ItemSize;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetItem(out long Pa)
|
||||
{
|
||||
lock (Items)
|
||||
{
|
||||
if (Items.First != null)
|
||||
{
|
||||
Pa = Items.First.Value;
|
||||
|
||||
Items.RemoveFirst();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Pa = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Free(long Pa)
|
||||
{
|
||||
lock (Items)
|
||||
{
|
||||
Items.AddFirst(Pa);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
//Check if objects are already signaled before waiting.
|
||||
for (int Index = 0; Index < SyncObjs.Length; Index++)
|
||||
|
@ -29,14 +29,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
HndIndex = Index;
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Timeout == 0)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
CurrentThread.WaitingSync = false;
|
||||
|
||||
|
@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
Result = (uint)CurrentThread.ObjSyncResult;
|
||||
|
||||
|
@ -100,14 +100,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public void SignalObject(KSynchronizationObject SyncObj)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (SyncObj.IsSignaled())
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
KThread Thread = Node.Value;
|
||||
|
||||
if ((Thread.SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
|
||||
if ((Thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
||||
{
|
||||
Thread.SignaledObj = SyncObj;
|
||||
Thread.ObjSyncResult = 0;
|
||||
|
@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using ChocolArm64;
|
||||
using ChocolArm64.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -13,14 +14,19 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public long AffinityMask { get; set; }
|
||||
|
||||
public int ThreadId { get; private set; }
|
||||
public long ThreadUid { get; private set; }
|
||||
|
||||
public KSynchronizationObject SignaledObj;
|
||||
|
||||
public long CondVarAddress { get; set; }
|
||||
public long MutexAddress { get; set; }
|
||||
|
||||
public Process Owner { get; private set; }
|
||||
private long Entrypoint;
|
||||
|
||||
public long MutexAddress { get; set; }
|
||||
|
||||
public KProcess Owner { get; private set; }
|
||||
|
||||
private long TlsAddress;
|
||||
|
||||
public long LastScheduledTicks { get; set; }
|
||||
|
||||
|
@ -28,6 +34,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private LinkedListNode<KThread> WithholderNode;
|
||||
|
||||
public LinkedListNode<KThread> ProcessListNode { get; set; }
|
||||
|
||||
private LinkedList<KThread> MutexWaiters;
|
||||
private LinkedListNode<KThread> MutexWaiterNode;
|
||||
|
||||
|
@ -65,38 +73,131 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public long LastPc { get; set; }
|
||||
|
||||
public KThread(
|
||||
CpuThread Thread,
|
||||
Process Process,
|
||||
Horizon System,
|
||||
int ProcessorId,
|
||||
int Priority,
|
||||
int ThreadId) : base(System)
|
||||
public KThread(Horizon System) : base(System)
|
||||
{
|
||||
this.ThreadId = ThreadId;
|
||||
|
||||
Context = Thread;
|
||||
Owner = Process;
|
||||
PreferredCore = ProcessorId;
|
||||
Scheduler = System.Scheduler;
|
||||
SchedulingData = System.Scheduler.SchedulingData;
|
||||
|
||||
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
|
||||
|
||||
MutexWaiters = new LinkedList<KThread>();
|
||||
|
||||
AffinityMask = 1 << ProcessorId;
|
||||
|
||||
DynamicPriority = BasePriority = Priority;
|
||||
|
||||
CurrentCore = PreferredCore;
|
||||
}
|
||||
|
||||
public long Start()
|
||||
public KernelResult Initialize(
|
||||
long Entrypoint,
|
||||
long ArgsPtr,
|
||||
long StackTop,
|
||||
int Priority,
|
||||
int DefaultCpuCore,
|
||||
KProcess Owner,
|
||||
ThreadType Type = ThreadType.User)
|
||||
{
|
||||
long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||
if ((uint)Type > 3)
|
||||
{
|
||||
throw new ArgumentException($"Invalid thread type \"{Type}\".");
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
PreferredCore = DefaultCpuCore;
|
||||
|
||||
AffinityMask |= 1L << DefaultCpuCore;
|
||||
|
||||
SchedFlags = Type == ThreadType.Dummy
|
||||
? ThreadSchedState.Running
|
||||
: ThreadSchedState.None;
|
||||
|
||||
CurrentCore = PreferredCore;
|
||||
|
||||
DynamicPriority = Priority;
|
||||
BasePriority = Priority;
|
||||
|
||||
ObjSyncResult = 0x7201;
|
||||
|
||||
this.Entrypoint = Entrypoint;
|
||||
|
||||
if (Type == ThreadType.User)
|
||||
{
|
||||
if (Owner.AllocateThreadLocalStorage(out TlsAddress) != KernelResult.Success)
|
||||
{
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
|
||||
MemoryHelper.FillWithZeros(Owner.CpuMemory, TlsAddress, KTlsPageInfo.TlsEntrySize);
|
||||
}
|
||||
|
||||
bool Is64Bits;
|
||||
|
||||
if (Owner != null)
|
||||
{
|
||||
this.Owner = Owner;
|
||||
|
||||
Owner.IncrementThreadCount();
|
||||
|
||||
Is64Bits = (Owner.MmuFlags & 1) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Is64Bits = true;
|
||||
}
|
||||
|
||||
Context = new CpuThread(Owner.Translator, Owner.CpuMemory, Entrypoint);
|
||||
|
||||
Context.ThreadState.X0 = (ulong)ArgsPtr;
|
||||
Context.ThreadState.X31 = (ulong)StackTop;
|
||||
|
||||
Context.ThreadState.CntfrqEl0 = 19200000;
|
||||
Context.ThreadState.Tpidr = TlsAddress;
|
||||
|
||||
Owner.SubscribeThreadEventHandlers(Context);
|
||||
|
||||
Context.WorkFinished += ThreadFinishedHandler;
|
||||
|
||||
ThreadUid = System.GetThreadUid();
|
||||
|
||||
if (Owner != null)
|
||||
{
|
||||
Owner.AddThread(this);
|
||||
|
||||
if (Owner.IsPaused)
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
ForcePauseFlags |= ThreadSchedState.ProcessPauseFlag;
|
||||
|
||||
CombineForcePauseFlags();
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
public KernelResult Start()
|
||||
{
|
||||
if (!System.KernelInitialized)
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
ForcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
|
||||
|
||||
CombineForcePauseFlags();
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
KernelResult Result = KernelResult.ThreadTerminating;
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (!ShallBeTerminated)
|
||||
{
|
||||
|
@ -106,9 +207,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
CurrentThread.SchedFlags != ThreadSchedState.TerminationPending &&
|
||||
!CurrentThread.ShallBeTerminated)
|
||||
{
|
||||
if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None)
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
|
||||
{
|
||||
Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||
Result = KernelResult.InvalidState;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -130,8 +231,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
CurrentThread.CombineForcePauseFlags();
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Leave();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (CurrentThread.ShallBeTerminated)
|
||||
{
|
||||
|
@ -141,25 +242,25 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask;
|
||||
ForcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
|
||||
|
||||
ExitImpl();
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
private void ExitImpl()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
SetNewSchedFlags(ThreadSchedState.TerminationPending);
|
||||
|
||||
|
@ -167,16 +268,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
Signal();
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public long Sleep(long Timeout)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||
}
|
||||
|
@ -188,7 +289,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
System.TimeManager.ScheduleFutureInvocation(this, Timeout);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
if (Timeout > 0)
|
||||
{
|
||||
|
@ -200,11 +301,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public void Yield()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (SchedFlags != ThreadSchedState.Running)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
System.Scheduler.ContextSwitch();
|
||||
|
||||
|
@ -219,27 +320,27 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
Scheduler.ThreadReselectionRequested = true;
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
System.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
public void YieldWithLoadBalancing()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
|
||||
int Prio = DynamicPriority;
|
||||
int Core = CurrentCore;
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (SchedFlags != ThreadSchedState.Running)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
System.Scheduler.ContextSwitch();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int Prio = DynamicPriority;
|
||||
int Core = CurrentCore;
|
||||
|
||||
KThread NextThreadOnCurrentQueue = null;
|
||||
|
||||
if (DynamicPriority < KScheduler.PrioritiesCount)
|
||||
|
@ -292,18 +393,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
Scheduler.ThreadReselectionRequested = true;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
System.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
public void YieldAndWaitForLoadBalancing()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (SchedFlags != ThreadSchedState.Running)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
System.Scheduler.ContextSwitch();
|
||||
|
||||
|
@ -348,47 +449,47 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
Scheduler.ThreadReselectionRequested = true;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
System.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
public void SetPriority(int Priority)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
BasePriority = Priority;
|
||||
|
||||
UpdatePriorityInheritance();
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public long SetActivity(bool Pause)
|
||||
{
|
||||
long Result = 0;
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
|
||||
ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
|
||||
|
||||
if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
if (Pause)
|
||||
{
|
||||
//Pause, the force pause flag should be clear (thread is NOT paused).
|
||||
if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) == 0)
|
||||
if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
|
||||
{
|
||||
ForcePauseFlags |= ThreadSchedState.ForcePauseFlag;
|
||||
ForcePauseFlags |= ThreadSchedState.ThreadPauseFlag;
|
||||
|
||||
CombineForcePauseFlags();
|
||||
}
|
||||
|
@ -400,17 +501,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
else
|
||||
{
|
||||
//Unpause, the force pause flag should be set (thread is paused).
|
||||
if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0)
|
||||
if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
|
||||
{
|
||||
ThreadSchedState OldForcePauseFlags = ForcePauseFlags;
|
||||
|
||||
ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag;
|
||||
ForcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
|
||||
|
||||
if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None)
|
||||
if ((OldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
|
||||
{
|
||||
ThreadSchedState OldSchedFlags = SchedFlags;
|
||||
|
||||
SchedFlags &= ThreadSchedState.LowNibbleMask;
|
||||
SchedFlags &= ThreadSchedState.LowMask;
|
||||
|
||||
AdjustScheduling(OldSchedFlags);
|
||||
}
|
||||
|
@ -422,17 +523,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public void CancelSynchronization()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.Paused || !WaitingSync)
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync)
|
||||
{
|
||||
SyncCancelled = true;
|
||||
}
|
||||
|
@ -456,12 +557,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
SyncCancelled = false;
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
bool UseOverride = AffinityOverrideCount != 0;
|
||||
|
||||
|
@ -472,7 +573,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
if ((NewAffinityMask & (1 << NewCore)) == 0)
|
||||
{
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
||||
}
|
||||
|
@ -510,7 +611,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -531,7 +632,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
private void CombineForcePauseFlags()
|
||||
{
|
||||
ThreadSchedState OldFlags = SchedFlags;
|
||||
ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
|
||||
ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
|
||||
|
||||
SchedFlags = LowNibble | ForcePauseFlags;
|
||||
|
||||
|
@ -540,25 +641,25 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private void SetNewSchedFlags(ThreadSchedState NewFlags)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
ThreadSchedState OldFlags = SchedFlags;
|
||||
|
||||
SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | NewFlags;
|
||||
SchedFlags = (OldFlags & ThreadSchedState.HighMask) | NewFlags;
|
||||
|
||||
if ((OldFlags & ThreadSchedState.LowNibbleMask) != NewFlags)
|
||||
if ((OldFlags & ThreadSchedState.LowMask) != NewFlags)
|
||||
{
|
||||
AdjustScheduling(OldFlags);
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public void ReleaseAndResume()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if ((SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
||||
{
|
||||
if (WithholderNode != null)
|
||||
{
|
||||
|
@ -574,21 +675,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public void Reschedule(ThreadSchedState NewFlags)
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
ThreadSchedState OldFlags = SchedFlags;
|
||||
|
||||
SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) |
|
||||
(NewFlags & ThreadSchedState.LowNibbleMask);
|
||||
SchedFlags = (OldFlags & ThreadSchedState.HighMask) |
|
||||
(NewFlags & ThreadSchedState.LowMask);
|
||||
|
||||
AdjustScheduling(OldFlags);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public void AddMutexWaiter(KThread Requester)
|
||||
|
@ -866,18 +967,60 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return HasExited;
|
||||
}
|
||||
|
||||
public void SetEntryArguments(long ArgsPtr, int ThreadHandle)
|
||||
{
|
||||
Context.ThreadState.X0 = (ulong)ArgsPtr;
|
||||
Context.ThreadState.X1 = (ulong)ThreadHandle;
|
||||
}
|
||||
|
||||
public void ClearExclusive()
|
||||
{
|
||||
Owner.Memory.ClearExclusive(CurrentCore);
|
||||
Owner.CpuMemory.ClearExclusive(CurrentCore);
|
||||
}
|
||||
|
||||
public void TimeUp()
|
||||
{
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
SetNewSchedFlags(ThreadSchedState.Running);
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
private void ThreadFinishedHandler(object sender, EventArgs e)
|
||||
{
|
||||
System.Scheduler.ExitThread(this);
|
||||
|
||||
Terminate();
|
||||
|
||||
System.Scheduler.RemoveThread(this);
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
Owner?.RemoveThread(this);
|
||||
|
||||
if (TlsAddress != 0 && Owner.FreeThreadLocalStorage(TlsAddress) != KernelResult.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure freeing thread local storage.");
|
||||
}
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
//Wake up all threads that may be waiting for a mutex being held
|
||||
//by this thread.
|
||||
foreach (KThread Thread in MutexWaiters)
|
||||
{
|
||||
Thread.MutexOwner = null;
|
||||
Thread.PreferredCoreOverride = 0;
|
||||
Thread.ObjSyncResult = 0xfa01;
|
||||
|
||||
Thread.ReleaseAndResume();
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
Owner?.DecrementThreadCountAndTerminateIfZero();
|
||||
}
|
||||
}
|
||||
}
|
73
Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
Normal file
73
Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KTlsPageInfo
|
||||
{
|
||||
public const int TlsEntrySize = 0x200;
|
||||
|
||||
public long PageAddr { get; private set; }
|
||||
|
||||
private bool[] IsSlotFree;
|
||||
|
||||
public KTlsPageInfo(long PageAddress)
|
||||
{
|
||||
this.PageAddr = PageAddress;
|
||||
|
||||
IsSlotFree = new bool[KMemoryManager.PageSize / TlsEntrySize];
|
||||
|
||||
for (int Index = 0; Index < IsSlotFree.Length; Index++)
|
||||
{
|
||||
IsSlotFree[Index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetFreePage(out long Address)
|
||||
{
|
||||
Address = PageAddr;
|
||||
|
||||
for (int Index = 0; Index < IsSlotFree.Length; Index++)
|
||||
{
|
||||
if (IsSlotFree[Index])
|
||||
{
|
||||
IsSlotFree[Index] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Address += TlsEntrySize;
|
||||
}
|
||||
|
||||
Address = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFull()
|
||||
{
|
||||
bool HasFree = false;
|
||||
|
||||
for (int Index = 0; Index < IsSlotFree.Length; Index++)
|
||||
{
|
||||
HasFree |= IsSlotFree[Index];
|
||||
}
|
||||
|
||||
return !HasFree;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
bool AllFree = true;
|
||||
|
||||
for (int Index = 0; Index < IsSlotFree.Length; Index++)
|
||||
{
|
||||
AllFree &= IsSlotFree[Index];
|
||||
}
|
||||
|
||||
return AllFree;
|
||||
}
|
||||
|
||||
public void FreeTlsSlot(long Address)
|
||||
{
|
||||
IsSlotFree[(Address - PageAddr) / TlsEntrySize] = true;
|
||||
}
|
||||
}
|
||||
}
|
107
Ryujinx.HLE/HOS/Kernel/KernelInit.cs
Normal file
107
Ryujinx.HLE/HOS/Kernel/KernelInit.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
static class KernelInit
|
||||
{
|
||||
public static KMemoryRegionManager[] GetMemoryRegions()
|
||||
{
|
||||
KMemoryArrange Arrange = GetMemoryArrange();
|
||||
|
||||
return new KMemoryRegionManager[]
|
||||
{
|
||||
GetMemoryRegion(Arrange.Application),
|
||||
GetMemoryRegion(Arrange.Applet),
|
||||
GetMemoryRegion(Arrange.Service),
|
||||
GetMemoryRegion(Arrange.NvServices)
|
||||
};
|
||||
}
|
||||
|
||||
private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion Region)
|
||||
{
|
||||
return new KMemoryRegionManager(
|
||||
Region.Address,
|
||||
Region.Size,
|
||||
Region.EndAddr);
|
||||
}
|
||||
|
||||
private static KMemoryArrange GetMemoryArrange()
|
||||
{
|
||||
int McEmemCfg = 0x1000;
|
||||
|
||||
ulong EmemApertureSize = (ulong)(McEmemCfg & 0x3fff) << 20;
|
||||
|
||||
int KernelMemoryCfg = 0;
|
||||
|
||||
ulong RamSize;
|
||||
|
||||
switch ((KernelMemoryCfg >> 16) & 3)
|
||||
{
|
||||
case 1: RamSize = 0x180000000; break;
|
||||
case 2: RamSize = 0x200000000; break;
|
||||
default: RamSize = 0x100000000; break;
|
||||
}
|
||||
|
||||
long RamPart0;
|
||||
long RamPart1;
|
||||
|
||||
if (RamSize * 2 > EmemApertureSize)
|
||||
{
|
||||
RamPart0 = (long)(EmemApertureSize / 2);
|
||||
RamPart1 = (long)(EmemApertureSize / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
RamPart0 = (long)EmemApertureSize;
|
||||
RamPart1 = 0;
|
||||
}
|
||||
|
||||
int MemoryArrange = 1;
|
||||
|
||||
long ApplicationRgSize;
|
||||
|
||||
switch (MemoryArrange)
|
||||
{
|
||||
case 2: ApplicationRgSize = 0x80000000; break;
|
||||
case 0x11:
|
||||
case 0x21: ApplicationRgSize = 0x133400000; break;
|
||||
default: ApplicationRgSize = 0xcd500000; break;
|
||||
}
|
||||
|
||||
long AppletRgSize;
|
||||
|
||||
switch (MemoryArrange)
|
||||
{
|
||||
case 2: AppletRgSize = 0x61200000; break;
|
||||
case 3: AppletRgSize = 0x1c000000; break;
|
||||
case 0x11: AppletRgSize = 0x23200000; break;
|
||||
case 0x12:
|
||||
case 0x21: AppletRgSize = 0x89100000; break;
|
||||
default: AppletRgSize = 0x1fb00000; break;
|
||||
}
|
||||
|
||||
KMemoryArrangeRegion ServiceRg;
|
||||
KMemoryArrangeRegion NvServicesRg;
|
||||
KMemoryArrangeRegion AppletRg;
|
||||
KMemoryArrangeRegion ApplicationRg;
|
||||
|
||||
const long NvServicesRgSize = 0x29ba000;
|
||||
|
||||
long ApplicationRgEnd = DramMemoryMap.DramEnd; //- RamPart0;
|
||||
|
||||
ApplicationRg = new KMemoryArrangeRegion(ApplicationRgEnd - ApplicationRgSize, ApplicationRgSize);
|
||||
|
||||
long NvServicesRgEnd = ApplicationRg.Address - AppletRgSize;
|
||||
|
||||
NvServicesRg = new KMemoryArrangeRegion(NvServicesRgEnd - NvServicesRgSize, NvServicesRgSize);
|
||||
AppletRg = new KMemoryArrangeRegion(NvServicesRgEnd, AppletRgSize);
|
||||
|
||||
//Note: There is an extra region used by the kernel, however
|
||||
//since we are doing HLE we are not going to use that memory, so give all
|
||||
//the remaining memory space to services.
|
||||
long ServiceRgSize = NvServicesRg.Address - DramMemoryMap.KernelReserveBase;
|
||||
|
||||
ServiceRg = new KMemoryArrangeRegion(DramMemoryMap.KernelReserveBase, ServiceRgSize);
|
||||
|
||||
return new KMemoryArrange(ServiceRg, NvServicesRg, AppletRg, ApplicationRg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,25 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
enum KernelResult
|
||||
{
|
||||
Success = 0,
|
||||
HandleTableFull = 0xd201,
|
||||
InvalidHandle = 0xe401,
|
||||
InvalidState = 0xfa01
|
||||
Success = 0,
|
||||
InvalidCapability = 0x1c01,
|
||||
ThreadTerminating = 0x7601,
|
||||
InvalidSize = 0xca01,
|
||||
InvalidAddress = 0xcc01,
|
||||
OutOfMemory = 0xd001,
|
||||
HandleTableFull = 0xd201,
|
||||
InvalidMemState = 0xd401,
|
||||
InvalidMemRange = 0xdc01,
|
||||
InvalidPriority = 0xe001,
|
||||
InvalidCpuCore = 0xe201,
|
||||
InvalidHandle = 0xe401,
|
||||
InvalidCombination = 0xe801,
|
||||
TimedOut = 0xea01,
|
||||
Cancelled = 0xec01,
|
||||
MaximumExceeded = 0xee01,
|
||||
InvalidThread = 0xf401,
|
||||
InvalidState = 0xfa01,
|
||||
ReservedValue = 0xfc01,
|
||||
ResLimitExceeded = 0x10801
|
||||
}
|
||||
}
|
11
Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
Normal file
11
Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
enum LimitableResource : byte
|
||||
{
|
||||
Memory,
|
||||
Thread,
|
||||
Event,
|
||||
TransferMemory,
|
||||
Session
|
||||
}
|
||||
}
|
12
Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
Normal file
12
Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
enum MemoryOperation
|
||||
{
|
||||
MapPa,
|
||||
MapVa,
|
||||
Allocate,
|
||||
Unmap,
|
||||
ChangePermRw,
|
||||
ChangePermsAndAttributes
|
||||
}
|
||||
}
|
10
Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
Normal file
10
Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
enum MemoryRegion
|
||||
{
|
||||
Application = 0,
|
||||
Applet = 1,
|
||||
Service = 2,
|
||||
NvServices = 3
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
ModCodeStatic = 0x00DD7E08,
|
||||
ModCodeMutable = 0x03FFBD09,
|
||||
IpcBuffer0 = 0x005C3C0A,
|
||||
MappedMemory = 0x005C3C0B,
|
||||
Stack = 0x005C3C0B,
|
||||
ThreadLocal = 0x0040200C,
|
||||
TransferMemoryIsolated = 0x015C3C0D,
|
||||
TransferMemory = 0x005C380E,
|
||||
|
|
128
Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
Normal file
128
Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
using Ryujinx.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class MersenneTwister
|
||||
{
|
||||
private int Index;
|
||||
private uint[] Mt;
|
||||
|
||||
public MersenneTwister(uint Seed)
|
||||
{
|
||||
Mt = new uint[624];
|
||||
|
||||
Mt[0] = Seed;
|
||||
|
||||
for (int MtIdx = 1; MtIdx < Mt.Length; MtIdx++)
|
||||
{
|
||||
uint Prev = Mt[MtIdx - 1];
|
||||
|
||||
Mt[MtIdx] = (uint)(0x6c078965 * (Prev ^ (Prev >> 30)) + MtIdx);
|
||||
}
|
||||
|
||||
Index = Mt.Length;
|
||||
}
|
||||
|
||||
public long GenRandomNumber(long Min, long Max)
|
||||
{
|
||||
long Range = Max - Min;
|
||||
|
||||
if (Min == Max)
|
||||
{
|
||||
return Min;
|
||||
}
|
||||
|
||||
if (Range == -1)
|
||||
{
|
||||
//Increment would cause a overflow, special case.
|
||||
return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu);
|
||||
}
|
||||
|
||||
Range++;
|
||||
|
||||
//This is log2(Range) plus one.
|
||||
int NextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(Range);
|
||||
|
||||
//If Range is already power of 2, subtract one to use log2(Range) directly.
|
||||
int RangeLog2 = NextRangeLog2 - (BitUtils.IsPowerOfTwo64(Range) ? 1 : 0);
|
||||
|
||||
int Parts = RangeLog2 > 32 ? 2 : 1;
|
||||
int BitsPerPart = RangeLog2 / Parts;
|
||||
|
||||
int FullParts = Parts - (RangeLog2 - Parts * BitsPerPart);
|
||||
|
||||
uint Mask = 0xffffffffu >> (32 - BitsPerPart);
|
||||
uint MaskPlus1 = 0xffffffffu >> (31 - BitsPerPart);
|
||||
|
||||
long RandomNumber;
|
||||
|
||||
do
|
||||
{
|
||||
RandomNumber = GenRandomNumber(Parts, FullParts, BitsPerPart, Mask, MaskPlus1);
|
||||
}
|
||||
while ((ulong)RandomNumber >= (ulong)Range);
|
||||
|
||||
return Min + RandomNumber;
|
||||
}
|
||||
|
||||
private long GenRandomNumber(
|
||||
int Parts,
|
||||
int FullParts,
|
||||
int BitsPerPart,
|
||||
uint Mask,
|
||||
uint MaskPlus1)
|
||||
{
|
||||
long RandomNumber = 0;
|
||||
|
||||
int Part = 0;
|
||||
|
||||
for (; Part < FullParts; Part++)
|
||||
{
|
||||
RandomNumber <<= BitsPerPart;
|
||||
RandomNumber |= GenRandomNumber() & Mask;
|
||||
}
|
||||
|
||||
for (; Part < Parts; Part++)
|
||||
{
|
||||
RandomNumber <<= BitsPerPart + 1;
|
||||
RandomNumber |= GenRandomNumber() & MaskPlus1;
|
||||
}
|
||||
|
||||
return RandomNumber;
|
||||
}
|
||||
|
||||
private uint GenRandomNumber()
|
||||
{
|
||||
if (Index >= Mt.Length)
|
||||
{
|
||||
Twist();
|
||||
}
|
||||
|
||||
uint Value = Mt[Index++];
|
||||
|
||||
Value ^= Value >> 11;
|
||||
Value ^= (Value << 7) & 0x9d2c5680;
|
||||
Value ^= (Value << 15) & 0xefc60000;
|
||||
Value ^= Value >> 18;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
private void Twist()
|
||||
{
|
||||
for (int MtIdx = 0; MtIdx < Mt.Length; MtIdx++)
|
||||
{
|
||||
uint Value = (Mt[MtIdx] & 0x80000000) + (Mt[(MtIdx + 1) % Mt.Length] & 0x7fffffff);
|
||||
|
||||
Mt[MtIdx] = Mt[(MtIdx + 397) % Mt.Length] ^ (Value >> 1);
|
||||
|
||||
if ((Value & 1) != 0)
|
||||
{
|
||||
Mt[MtIdx] ^= 0x9908b0df;
|
||||
}
|
||||
}
|
||||
|
||||
Index = 0;
|
||||
}
|
||||
}
|
||||
}
|
37
Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
Normal file
37
Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
struct ProcessCreationInfo
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public int Category { get; private set; }
|
||||
public long TitleId { get; private set; }
|
||||
|
||||
public long CodeAddress { get; private set; }
|
||||
public int CodePagesCount { get; private set; }
|
||||
|
||||
public int MmuFlags { get; private set; }
|
||||
public int ResourceLimitHandle { get; private set; }
|
||||
public int PersonalMmHeapPagesCount { get; private set; }
|
||||
|
||||
public ProcessCreationInfo(
|
||||
string Name,
|
||||
int Category,
|
||||
long TitleId,
|
||||
long CodeAddress,
|
||||
int CodePagesCount,
|
||||
int MmuFlags,
|
||||
int ResourceLimitHandle,
|
||||
int PersonalMmHeapPagesCount)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Category = Category;
|
||||
this.TitleId = TitleId;
|
||||
this.CodeAddress = CodeAddress;
|
||||
this.CodePagesCount = CodePagesCount;
|
||||
this.MmuFlags = MmuFlags;
|
||||
this.ResourceLimitHandle = ResourceLimitHandle;
|
||||
this.PersonalMmHeapPagesCount = PersonalMmHeapPagesCount;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.HLE/HOS/Kernel/ProcessState.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/ProcessState.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
enum ProcessState : byte
|
||||
{
|
||||
Created = 0,
|
||||
CreatedAttached = 1,
|
||||
Started = 2,
|
||||
Crashed = 3,
|
||||
Attached = 4,
|
||||
Exiting = 5,
|
||||
Exited = 6,
|
||||
DebugSuspended = 7
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
using ChocolArm64.Events;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
private Dictionary<int, SvcFunc> SvcFuncs;
|
||||
|
||||
private Switch Device;
|
||||
private Process Process;
|
||||
private KProcess Process;
|
||||
private Horizon System;
|
||||
private MemoryManager Memory;
|
||||
|
||||
|
@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private static Random Rng;
|
||||
|
||||
public SvcHandler(Switch Device, Process Process)
|
||||
public SvcHandler(Switch Device, KProcess Process)
|
||||
{
|
||||
SvcFuncs = new Dictionary<int, SvcFunc>()
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{ 0x05, SvcUnmapMemory },
|
||||
{ 0x06, SvcQueryMemory },
|
||||
{ 0x07, SvcExitProcess },
|
||||
{ 0x08, SvcCreateThread },
|
||||
{ 0x08, CreateThread64 },
|
||||
{ 0x09, SvcStartThread },
|
||||
{ 0x0a, SvcExitThread },
|
||||
{ 0x0b, SvcSleepThread },
|
||||
|
@ -92,8 +92,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
this.Device = Device;
|
||||
this.Process = Process;
|
||||
this.System = Process.Device.System;
|
||||
this.Memory = Process.Memory;
|
||||
this.System = Device.System;
|
||||
this.Memory = Process.CpuMemory;
|
||||
}
|
||||
|
||||
static SvcHandler()
|
||||
|
@ -105,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
CpuThreadState ThreadState = (CpuThreadState)sender;
|
||||
|
||||
Process.GetThread(ThreadState.Tpidr).LastPc = e.Position;
|
||||
//Process.GetThread(ThreadState.Tpidr).LastPc = e.Position;
|
||||
|
||||
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
else
|
||||
{
|
||||
Process.PrintStackTrace(ThreadState);
|
||||
//Process.PrintStackTrace(ThreadState);
|
||||
|
||||
throw new NotImplementedException($"0x{e.Id:x4}");
|
||||
}
|
||||
|
|
|
@ -20,17 +20,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position);
|
||||
KernelResult Result = Process.MemoryManager.SetHeapSize((long)Size, out long Position);
|
||||
|
||||
ThreadState.X0 = (ulong)Result;
|
||||
|
||||
if (Result == 0)
|
||||
if (Result == KernelResult.Success)
|
||||
{
|
||||
ThreadState.X1 = (ulong)Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,8 +552,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
ulong Start = (ulong)Position;
|
||||
ulong End = (ulong)Size + Start;
|
||||
|
||||
return Start >= (ulong)Process.MemoryManager.MapRegionStart &&
|
||||
End < (ulong)Process.MemoryManager.MapRegionEnd;
|
||||
return Start >= (ulong)Process.MemoryManager.AliasRegionStart &&
|
||||
End < (ulong)Process.MemoryManager.AliasRegionEnd;
|
||||
}
|
||||
|
||||
private bool InsideHeapRegion(long Position, long Size)
|
||||
|
@ -570,8 +570,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
ulong Start = (ulong)Position;
|
||||
ulong End = (ulong)Size + Start;
|
||||
|
||||
return Start >= (ulong)Process.MemoryManager.NewMapRegionStart &&
|
||||
End < (ulong)Process.MemoryManager.NewMapRegionEnd;
|
||||
return Start >= (ulong)Process.MemoryManager.StackRegionStart &&
|
||||
End < (ulong)Process.MemoryManager.StackRegionEnd;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Services;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private void SvcExitProcess(CpuThreadState ThreadState)
|
||||
{
|
||||
Device.System.ExitProcess(Process.ProcessId);
|
||||
System.Scheduler.GetCurrentProcess().Terminate();
|
||||
}
|
||||
|
||||
private void SignalEvent64(CpuThreadState ThreadState)
|
||||
|
@ -187,17 +187,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle)
|
||||
{
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
|
||||
|
||||
KSession Session = Process.HandleTable.GetObject<KSession>(Handle);
|
||||
|
||||
if (Session != null)
|
||||
{
|
||||
//Process.Scheduler.Suspend(CurrThread);
|
||||
|
||||
System.CriticalSectionLock.Lock();
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
|
@ -214,7 +210,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
Message,
|
||||
MessagePtr));
|
||||
|
||||
System.CriticalSectionLock.Unlock();
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult;
|
||||
}
|
||||
|
@ -247,17 +243,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
long Unknown = (long)ThreadState.X1;
|
||||
long Info = (long)ThreadState.X2;
|
||||
|
||||
if ((Reason & (1 << 31)) == 0)
|
||||
{
|
||||
Process.PrintStackTrace(ThreadState);
|
||||
//Process.PrintStackTrace(ThreadState);
|
||||
|
||||
throw new GuestBrokeExecutionException();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered");
|
||||
Process.PrintStackTrace(ThreadState);
|
||||
}
|
||||
throw new GuestBrokeExecutionException();
|
||||
}
|
||||
|
||||
private void SvcOutputDebugString(CpuThreadState ThreadState)
|
||||
|
@ -298,12 +286,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
break;
|
||||
|
||||
case 2:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart;
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionStart;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd -
|
||||
(ulong)Process.MemoryManager.MapRegionStart;
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionEnd -
|
||||
(ulong)Process.MemoryManager.AliasRegionStart;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
|
@ -316,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
break;
|
||||
|
||||
case 6:
|
||||
ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalAvailableSize;
|
||||
ThreadState.X1 = (ulong)Process.GetMemoryCapacity();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalUsedSize;
|
||||
ThreadState.X1 = (ulong)Process.GetMemoryUsage();
|
||||
break;
|
||||
|
||||
case 8:
|
||||
|
@ -341,16 +329,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
break;
|
||||
|
||||
case 14:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.StackRegionStart;
|
||||
break;
|
||||
|
||||
case 15:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
|
||||
(ulong)Process.MemoryManager.NewMapRegionStart;
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.StackRegionEnd -
|
||||
(ulong)Process.MemoryManager.StackRegionStart;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
|
||||
//ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
|
||||
break;
|
||||
|
||||
case 17:
|
||||
|
@ -358,7 +346,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
break;
|
||||
|
||||
default:
|
||||
Process.PrintStackTrace(ThreadState);
|
||||
//Process.PrintStackTrace(ThreadState);
|
||||
|
||||
throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}");
|
||||
}
|
||||
|
|
|
@ -7,48 +7,82 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
partial class SvcHandler
|
||||
{
|
||||
private void SvcCreateThread(CpuThreadState ThreadState)
|
||||
private void CreateThread64(CpuThreadState ThreadState)
|
||||
{
|
||||
long EntryPoint = (long)ThreadState.X1;
|
||||
long ArgsPtr = (long)ThreadState.X2;
|
||||
long StackTop = (long)ThreadState.X3;
|
||||
int Priority = (int)ThreadState.X4;
|
||||
int ProcessorId = (int)ThreadState.X5;
|
||||
long Entrypoint = (long)ThreadState.X1;
|
||||
long ArgsPtr = (long)ThreadState.X2;
|
||||
long StackTop = (long)ThreadState.X3;
|
||||
int Priority = (int)ThreadState.X4;
|
||||
int CpuCore = (int)ThreadState.X5;
|
||||
|
||||
if ((uint)Priority > 0x3f)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
|
||||
KernelResult Result = CreateThread(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, out int Handle);
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ProcessorId == -2)
|
||||
{
|
||||
//TODO: Get this value from the NPDM file.
|
||||
ProcessorId = 0;
|
||||
}
|
||||
else if ((uint)ProcessorId > 3)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int Handle = Process.MakeThread(
|
||||
EntryPoint,
|
||||
StackTop,
|
||||
ArgsPtr,
|
||||
Priority,
|
||||
ProcessorId);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X0 = (ulong)Result;
|
||||
ThreadState.X1 = (ulong)Handle;
|
||||
}
|
||||
|
||||
private KernelResult CreateThread(
|
||||
long Entrypoint,
|
||||
long ArgsPtr,
|
||||
long StackTop,
|
||||
int Priority,
|
||||
int CpuCore,
|
||||
out int Handle)
|
||||
{
|
||||
Handle = 0;
|
||||
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (CpuCore == -2)
|
||||
{
|
||||
CpuCore = CurrentProcess.DefaultCpuCore;
|
||||
}
|
||||
|
||||
if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsAllowedCpuCore(CpuCore))
|
||||
{
|
||||
return KernelResult.InvalidCpuCore;
|
||||
}
|
||||
|
||||
if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsAllowedPriority(Priority))
|
||||
{
|
||||
return KernelResult.InvalidPriority;
|
||||
}
|
||||
|
||||
if (CurrentProcess.ResourceLimit != null &&
|
||||
!CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1))
|
||||
{
|
||||
return KernelResult.ResLimitExceeded;
|
||||
}
|
||||
|
||||
KThread Thread = new KThread(System);
|
||||
|
||||
KernelResult Result = CurrentProcess.InitializeThread(
|
||||
Thread,
|
||||
Entrypoint,
|
||||
ArgsPtr,
|
||||
StackTop,
|
||||
Priority,
|
||||
CpuCore);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
Result = Process.HandleTable.GenerateHandle(Thread, out Handle);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
Thread.Terminate();
|
||||
|
||||
CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private void SvcStartThread(CpuThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
|
@ -57,11 +91,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
if (Thread != null)
|
||||
{
|
||||
long Result = Thread.Start();
|
||||
KernelResult Result = Thread.Start();
|
||||
|
||||
if (Result != 0)
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
|
||||
}
|
||||
|
||||
ThreadState.X0 = (ulong)Result;
|
||||
|
@ -78,9 +112,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
CurrentThread.Exit();
|
||||
System.Scheduler.ExitThread(CurrentThread);
|
||||
|
||||
System.Scheduler.StopThread(CurrentThread);
|
||||
CurrentThread.Exit();
|
||||
}
|
||||
|
||||
private void SvcSleepThread(CpuThreadState ThreadState)
|
||||
|
@ -242,7 +276,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
|
||||
{
|
||||
ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).CurrentCore;
|
||||
ThreadState.X0 = (ulong)System.Scheduler.GetCurrentThread().CurrentCore;
|
||||
}
|
||||
|
||||
private void SvcGetThreadId(CpuThreadState ThreadState)
|
||||
|
@ -254,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
if (Thread != null)
|
||||
{
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = (ulong)Thread.ThreadId;
|
||||
ThreadState.X1 = (ulong)Thread.ThreadUid;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -280,15 +314,24 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
if (Thread.Owner != Process)
|
||||
if (Thread.Owner != System.Scheduler.GetCurrentProcess())
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!");
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Thread == System.Scheduler.GetCurrentThread())
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
|
||||
|
||||
ThreadState.X0 = (ulong)KernelResult.InvalidThread;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
long Result = Thread.SetActivity(Pause);
|
||||
|
||||
if (Result != 0)
|
||||
|
@ -304,6 +347,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
long Position = (long)ThreadState.X0;
|
||||
int Handle = (int)ThreadState.X1;
|
||||
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||
|
||||
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
|
||||
|
||||
if (Thread == null)
|
||||
|
@ -315,9 +361,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
if (Process.GetThread(ThreadState.Tpidr) == Thread)
|
||||
if (Thread.Owner != CurrentProcess)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurrentThread == Thread)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
|
||||
|
||||
|
|
|
@ -116,12 +116,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
long Result = System.AddressArbiter.ArbitrateLock(
|
||||
Process,
|
||||
Memory,
|
||||
OwnerHandle,
|
||||
MutexAddress,
|
||||
RequesterHandle);
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
long Result = CurrentProcess.AddressArbiter.ArbitrateLock(OwnerHandle, MutexAddress, RequesterHandle);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
|
@ -155,7 +152,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress);
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
long Result = CurrentProcess.AddressArbiter.ArbitrateUnlock(MutexAddress);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
|
@ -196,8 +195,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
long Result = System.AddressArbiter.WaitProcessWideKeyAtomic(
|
||||
Memory,
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
long Result = CurrentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
|
||||
MutexAddress,
|
||||
CondVarAddress,
|
||||
ThreadHandle,
|
||||
|
@ -227,7 +227,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
"Address = 0x" + Address.ToString("x16") + ", " +
|
||||
"Count = 0x" + Count .ToString("x8"));
|
||||
|
||||
System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count);
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
CurrentProcess.AddressArbiter.SignalProcessWideKey(Address, Count);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
@ -263,20 +265,22 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
long Result;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ArbitrationType.WaitIfLessThan:
|
||||
Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout);
|
||||
Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, false, Timeout);
|
||||
break;
|
||||
|
||||
case ArbitrationType.DecrementAndWaitIfLessThan:
|
||||
Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout);
|
||||
Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, true, Timeout);
|
||||
break;
|
||||
|
||||
case ArbitrationType.WaitIfEqual:
|
||||
Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout);
|
||||
Result = CurrentProcess.AddressArbiter.WaitForAddressIfEqual(Address, Value, Timeout);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -323,20 +327,22 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||
|
||||
long Result;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case SignalType.Signal:
|
||||
Result = System.AddressArbiter.Signal(Address, Count);
|
||||
Result = CurrentProcess.AddressArbiter.Signal(Address, Count);
|
||||
break;
|
||||
|
||||
case SignalType.SignalAndIncrementIfEqual:
|
||||
Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count);
|
||||
Result = CurrentProcess.AddressArbiter.SignalAndIncrementIfEqual(Address, Value, Count);
|
||||
break;
|
||||
|
||||
case SignalType.SignalAndModifyIfEqual:
|
||||
Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count);
|
||||
Result = CurrentProcess.AddressArbiter.SignalAndModifyIfEqual(Address, Value, Count);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
enum ThreadSchedState : byte
|
||||
enum ThreadSchedState : ushort
|
||||
{
|
||||
LowNibbleMask = 0xf,
|
||||
HighNibbleMask = 0xf0,
|
||||
ExceptionalMask = 0x70,
|
||||
ForcePauseFlag = 0x20,
|
||||
LowMask = 0xf,
|
||||
HighMask = 0xfff0,
|
||||
ForcePauseMask = 0x70,
|
||||
|
||||
ProcessPauseFlag = 1 << 4,
|
||||
ThreadPauseFlag = 1 << 5,
|
||||
ProcessDebugPauseFlag = 1 << 6,
|
||||
KernelInitPauseFlag = 1 << 8,
|
||||
|
||||
None = 0,
|
||||
Paused = 1,
|
||||
|
|
10
Ryujinx.HLE/HOS/Kernel/ThreadType.cs
Normal file
10
Ryujinx.HLE/HOS/Kernel/ThreadType.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
enum ThreadType
|
||||
{
|
||||
Dummy,
|
||||
Kernel,
|
||||
Kernel2,
|
||||
User
|
||||
}
|
||||
}
|
|
@ -1,528 +0,0 @@
|
|||
using ChocolArm64;
|
||||
using ChocolArm64.Events;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using LibHac;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Services.Nv;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Loaders;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.HLE.Loaders.Npdm;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
class Process : IDisposable
|
||||
{
|
||||
private const int TickFreq = 19_200_000;
|
||||
|
||||
public Switch Device { get; private set; }
|
||||
|
||||
public bool NeedsHbAbi { get; private set; }
|
||||
|
||||
public long HbAbiDataPosition { get; private set; }
|
||||
|
||||
public int ProcessId { get; private set; }
|
||||
|
||||
private Translator Translator;
|
||||
|
||||
public MemoryManager Memory { get; private set; }
|
||||
|
||||
public KMemoryManager MemoryManager { get; private set; }
|
||||
|
||||
private List<KTlsPageManager> TlsPages;
|
||||
|
||||
public Npdm MetaData { get; private set; }
|
||||
|
||||
public Nacp ControlData { get; set; }
|
||||
|
||||
public KProcessHandleTable HandleTable { get; private set; }
|
||||
|
||||
public AppletStateMgr AppletState { get; private set; }
|
||||
|
||||
private SvcHandler SvcHandler;
|
||||
|
||||
private ConcurrentDictionary<long, KThread> Threads;
|
||||
|
||||
private List<Executable> Executables;
|
||||
|
||||
private long ImageBase;
|
||||
|
||||
private bool Disposed;
|
||||
|
||||
public Process(Switch Device, int ProcessId, Npdm MetaData)
|
||||
{
|
||||
this.Device = Device;
|
||||
this.MetaData = MetaData;
|
||||
this.ProcessId = ProcessId;
|
||||
|
||||
Memory = new MemoryManager(Device.Memory.RamPointer);
|
||||
|
||||
Memory.InvalidAccess += CpuInvalidAccessHandler;
|
||||
|
||||
MemoryManager = new KMemoryManager(this);
|
||||
|
||||
TlsPages = new List<KTlsPageManager>();
|
||||
|
||||
int HandleTableSize = 1024;
|
||||
|
||||
if (MetaData != null)
|
||||
{
|
||||
foreach (KernelAccessControlItem Item in MetaData.ACI0.KernelAccessControl.Items)
|
||||
{
|
||||
if (Item.HasHandleTableSize)
|
||||
{
|
||||
HandleTableSize = Item.HandleTableSize;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandleTable = new KProcessHandleTable(Device.System, HandleTableSize);
|
||||
|
||||
AppletState = new AppletStateMgr(Device.System);
|
||||
|
||||
SvcHandler = new SvcHandler(Device, this);
|
||||
|
||||
Threads = new ConcurrentDictionary<long, KThread>();
|
||||
|
||||
Executables = new List<Executable>();
|
||||
|
||||
ImageBase = MemoryManager.CodeRegionStart;
|
||||
}
|
||||
|
||||
public void LoadProgram(IExecutable Program)
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Process));
|
||||
}
|
||||
|
||||
long ImageEnd = LoadProgram(Program, ImageBase);
|
||||
|
||||
ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize);
|
||||
}
|
||||
|
||||
public long LoadProgram(IExecutable Program, long ExecutableBase)
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Process));
|
||||
}
|
||||
|
||||
Logger.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}.");
|
||||
|
||||
Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase);
|
||||
|
||||
Executables.Add(Executable);
|
||||
|
||||
return Executable.ImageEnd;
|
||||
}
|
||||
|
||||
public void RemoveProgram(long ExecutableBase)
|
||||
{
|
||||
foreach (Executable Executable in Executables)
|
||||
{
|
||||
if (Executable.ImageBase == ExecutableBase)
|
||||
{
|
||||
Executables.Remove(Executable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEmptyArgs()
|
||||
{
|
||||
//TODO: This should be part of Run.
|
||||
ImageBase += KMemoryManager.PageSize;
|
||||
}
|
||||
|
||||
public bool Run(bool NeedsHbAbi = false)
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Process));
|
||||
}
|
||||
|
||||
this.NeedsHbAbi = NeedsHbAbi;
|
||||
|
||||
if (Executables.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize;
|
||||
|
||||
long MainStackSize = 1 * 1024 * 1024;
|
||||
|
||||
long MainStackBottom = MainStackTop - MainStackSize;
|
||||
|
||||
MemoryManager.HleMapCustom(
|
||||
MainStackBottom,
|
||||
MainStackSize,
|
||||
MemoryState.MappedMemory,
|
||||
MemoryPermission.ReadAndWrite);
|
||||
|
||||
int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0);
|
||||
|
||||
if (Handle == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
KThread MainThread = HandleTable.GetKThread(Handle);
|
||||
|
||||
if (NeedsHbAbi)
|
||||
{
|
||||
HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize);
|
||||
|
||||
const long HbAbiDataSize = KMemoryManager.PageSize;
|
||||
|
||||
MemoryManager.HleMapCustom(
|
||||
HbAbiDataPosition,
|
||||
HbAbiDataSize,
|
||||
MemoryState.MappedMemory,
|
||||
MemoryPermission.ReadAndWrite);
|
||||
|
||||
string SwitchPath = Device.FileSystem.SystemPathToSwitchPath(Executables[0].FilePath);
|
||||
|
||||
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath);
|
||||
|
||||
MainThread.Context.ThreadState.X0 = (ulong)HbAbiDataPosition;
|
||||
MainThread.Context.ThreadState.X1 = ulong.MaxValue;
|
||||
}
|
||||
|
||||
MainThread.TimeUp();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int ThreadIdCtr = 1;
|
||||
|
||||
public int MakeThread(
|
||||
long EntryPoint,
|
||||
long StackTop,
|
||||
long ArgsPtr,
|
||||
int Priority,
|
||||
int ProcessorId)
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Process));
|
||||
}
|
||||
|
||||
CpuThread CpuThread = new CpuThread(GetTranslator(), Memory, EntryPoint);
|
||||
|
||||
long Tpidr = GetFreeTls();
|
||||
|
||||
int ThreadId = ThreadIdCtr++; //(int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1;
|
||||
|
||||
KThread Thread = new KThread(CpuThread, this, Device.System, ProcessorId, Priority, ThreadId);
|
||||
|
||||
Thread.LastPc = EntryPoint;
|
||||
|
||||
HandleTable.GenerateHandle(Thread, out int Handle);
|
||||
|
||||
CpuThread.ThreadState.CntfrqEl0 = TickFreq;
|
||||
CpuThread.ThreadState.Tpidr = Tpidr;
|
||||
|
||||
CpuThread.ThreadState.X0 = (ulong)ArgsPtr;
|
||||
CpuThread.ThreadState.X1 = (ulong)Handle;
|
||||
CpuThread.ThreadState.X31 = (ulong)StackTop;
|
||||
|
||||
CpuThread.ThreadState.Interrupt += InterruptHandler;
|
||||
CpuThread.ThreadState.Break += BreakHandler;
|
||||
CpuThread.ThreadState.SvcCall += SvcHandler.SvcCall;
|
||||
CpuThread.ThreadState.Undefined += UndefinedHandler;
|
||||
|
||||
CpuThread.WorkFinished += ThreadFinished;
|
||||
|
||||
Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread);
|
||||
|
||||
return Handle;
|
||||
}
|
||||
|
||||
private long GetFreeTls()
|
||||
{
|
||||
long Position;
|
||||
|
||||
lock (TlsPages)
|
||||
{
|
||||
for (int Index = 0; Index < TlsPages.Count; Index++)
|
||||
{
|
||||
if (TlsPages[Index].TryGetFreeTlsAddr(out Position))
|
||||
{
|
||||
return Position;
|
||||
}
|
||||
}
|
||||
|
||||
long PagePosition = MemoryManager.HleMapTlsPage();
|
||||
|
||||
KTlsPageManager TlsPage = new KTlsPageManager(PagePosition);
|
||||
|
||||
TlsPages.Add(TlsPage);
|
||||
|
||||
TlsPage.TryGetFreeTlsAddr(out Position);
|
||||
}
|
||||
|
||||
return Position;
|
||||
}
|
||||
|
||||
private void InterruptHandler(object sender, EventArgs e)
|
||||
{
|
||||
Device.System.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
private void BreakHandler(object sender, InstExceptionEventArgs e)
|
||||
{
|
||||
PrintStackTraceForCurrentThread();
|
||||
|
||||
throw new GuestBrokeExecutionException();
|
||||
}
|
||||
|
||||
private void UndefinedHandler(object sender, InstUndefinedEventArgs e)
|
||||
{
|
||||
PrintStackTraceForCurrentThread();
|
||||
|
||||
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
||||
}
|
||||
|
||||
public void EnableCpuTracing()
|
||||
{
|
||||
Translator.EnableCpuTrace = true;
|
||||
}
|
||||
|
||||
public void DisableCpuTracing()
|
||||
{
|
||||
Translator.EnableCpuTrace = false;
|
||||
}
|
||||
|
||||
private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
|
||||
{
|
||||
Executable Exe = GetExecutable(e.Position);
|
||||
|
||||
if (Exe == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetSubName(Exe, e.Position, out string SubName))
|
||||
{
|
||||
SubName = string.Empty;
|
||||
}
|
||||
|
||||
long Offset = e.Position - Exe.ImageBase;
|
||||
|
||||
string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
|
||||
|
||||
Logger.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName);
|
||||
}
|
||||
|
||||
private Translator GetTranslator()
|
||||
{
|
||||
if (Translator == null)
|
||||
{
|
||||
Translator = new Translator();
|
||||
|
||||
Translator.CpuTrace += CpuTraceHandler;
|
||||
}
|
||||
|
||||
return Translator;
|
||||
}
|
||||
|
||||
private void CpuInvalidAccessHandler(object sender, InvalidAccessEventArgs e)
|
||||
{
|
||||
PrintStackTraceForCurrentThread();
|
||||
}
|
||||
|
||||
private void PrintStackTraceForCurrentThread()
|
||||
{
|
||||
foreach (KThread Thread in Threads.Values)
|
||||
{
|
||||
if (Thread.Context.IsCurrentThread())
|
||||
{
|
||||
PrintStackTrace(Thread.Context.ThreadState);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintStackTrace(CpuThreadState ThreadState)
|
||||
{
|
||||
StringBuilder Trace = new StringBuilder();
|
||||
|
||||
Trace.AppendLine("Guest stack trace:");
|
||||
|
||||
void AppendTrace(long Position)
|
||||
{
|
||||
Executable Exe = GetExecutable(Position);
|
||||
|
||||
if (Exe == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetSubName(Exe, Position, out string SubName))
|
||||
{
|
||||
SubName = $"Sub{Position:x16}";
|
||||
}
|
||||
else if (SubName.StartsWith("_Z"))
|
||||
{
|
||||
SubName = Demangler.Parse(SubName);
|
||||
}
|
||||
|
||||
long Offset = Position - Exe.ImageBase;
|
||||
|
||||
string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
|
||||
|
||||
Trace.AppendLine(" " + ExeNameWithAddr + " " + SubName);
|
||||
}
|
||||
|
||||
long FramePointer = (long)ThreadState.X29;
|
||||
|
||||
while (FramePointer != 0)
|
||||
{
|
||||
AppendTrace(Memory.ReadInt64(FramePointer + 8));
|
||||
|
||||
FramePointer = Memory.ReadInt64(FramePointer);
|
||||
}
|
||||
|
||||
Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
|
||||
}
|
||||
|
||||
private bool TryGetSubName(Executable Exe, long Position, out string Name)
|
||||
{
|
||||
Position -= Exe.ImageBase;
|
||||
|
||||
int Left = 0;
|
||||
int Right = Exe.SymbolTable.Count - 1;
|
||||
|
||||
while (Left <= Right)
|
||||
{
|
||||
int Size = Right - Left;
|
||||
|
||||
int Middle = Left + (Size >> 1);
|
||||
|
||||
ElfSym Symbol = Exe.SymbolTable[Middle];
|
||||
|
||||
long EndPosition = Symbol.Value + Symbol.Size;
|
||||
|
||||
if ((ulong)Position >= (ulong)Symbol.Value && (ulong)Position < (ulong)EndPosition)
|
||||
{
|
||||
Name = Symbol.Name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((ulong)Position < (ulong)Symbol.Value)
|
||||
{
|
||||
Right = Middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Left = Middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Name = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Executable GetExecutable(long Position)
|
||||
{
|
||||
string Name = string.Empty;
|
||||
|
||||
for (int Index = Executables.Count - 1; Index >= 0; Index--)
|
||||
{
|
||||
if ((ulong)Position >= (ulong)Executables[Index].ImageBase)
|
||||
{
|
||||
return Executables[Index];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ThreadFinished(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is CpuThread Thread)
|
||||
{
|
||||
if (Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread))
|
||||
{
|
||||
Device.System.Scheduler.RemoveThread(KernelThread);
|
||||
}
|
||||
}
|
||||
|
||||
if (Threads.Count == 0)
|
||||
{
|
||||
Device.System.ExitProcess(ProcessId);
|
||||
}
|
||||
}
|
||||
|
||||
public KThread GetThread(long Tpidr)
|
||||
{
|
||||
if (!Threads.TryGetValue(Tpidr, out KThread Thread))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return Thread;
|
||||
}
|
||||
|
||||
private void Unload()
|
||||
{
|
||||
if (Disposed || Threads.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Disposed = true;
|
||||
|
||||
HandleTable.Destroy();
|
||||
|
||||
INvDrvServices.UnloadProcess(this);
|
||||
|
||||
if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix))
|
||||
{
|
||||
File.Delete(Executables[0].FilePath);
|
||||
}
|
||||
|
||||
Logger.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
{
|
||||
if (Disposing)
|
||||
{
|
||||
if (Threads.Count > 0)
|
||||
{
|
||||
foreach (KThread Thread in Threads.Values)
|
||||
{
|
||||
Device.System.Scheduler.StopThread(Thread);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
129
Ryujinx.HLE/HOS/ProgramLoader.cs
Normal file
129
Ryujinx.HLE/HOS/ProgramLoader.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.HLE.Loaders.Npdm;
|
||||
|
||||
namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
class ProgramLoader
|
||||
{
|
||||
private const int ArgsHeaderSize = 8;
|
||||
private const int ArgsDataSize = 0x9000;
|
||||
private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize;
|
||||
|
||||
public static bool LoadStaticObjects(
|
||||
Horizon System,
|
||||
Npdm MetaData,
|
||||
IExecutable[] StaticObjects,
|
||||
byte[] Arguments = null)
|
||||
{
|
||||
long ArgsStart = 0;
|
||||
int ArgsSize = 0;
|
||||
long CodeStart = 0x8000000;
|
||||
int CodeSize = 0;
|
||||
|
||||
long[] NsoBase = new long[StaticObjects.Length];
|
||||
|
||||
for (int Index = 0; Index < StaticObjects.Length; Index++)
|
||||
{
|
||||
IExecutable StaticObject = StaticObjects[Index];
|
||||
|
||||
int TextEnd = StaticObject.TextOffset + StaticObject.Text.Length;
|
||||
int ROEnd = StaticObject.ROOffset + StaticObject.RO.Length;
|
||||
int DataEnd = StaticObject.DataOffset + StaticObject.Data.Length + StaticObject.BssSize;
|
||||
|
||||
int NsoSize = TextEnd;
|
||||
|
||||
if ((uint)NsoSize < (uint)ROEnd)
|
||||
{
|
||||
NsoSize = ROEnd;
|
||||
}
|
||||
|
||||
if ((uint)NsoSize < (uint)DataEnd)
|
||||
{
|
||||
NsoSize = DataEnd;
|
||||
}
|
||||
|
||||
NsoSize = BitUtils.AlignUp(NsoSize, KMemoryManager.PageSize);
|
||||
|
||||
NsoBase[Index] = CodeStart + CodeSize;
|
||||
|
||||
CodeSize += NsoSize;
|
||||
|
||||
if (Arguments != null && ArgsSize == 0)
|
||||
{
|
||||
ArgsStart = CodeSize;
|
||||
|
||||
ArgsSize = BitUtils.AlignDown(Arguments.Length * 2 + ArgsTotalSize - 1, KMemoryManager.PageSize);
|
||||
|
||||
CodeSize += ArgsSize;
|
||||
}
|
||||
}
|
||||
|
||||
int CodePagesCount = CodeSize / KMemoryManager.PageSize;
|
||||
|
||||
int PersonalMmHeapPagesCount = MetaData.PersonalMmHeapSize / KMemoryManager.PageSize;
|
||||
|
||||
KProcess Process = new KProcess(System);
|
||||
|
||||
ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
|
||||
MetaData.TitleName,
|
||||
MetaData.ProcessCategory,
|
||||
MetaData.ACI0.TitleId,
|
||||
CodeStart,
|
||||
CodePagesCount,
|
||||
MetaData.MmuFlags,
|
||||
0,
|
||||
PersonalMmHeapPagesCount);
|
||||
|
||||
KernelResult Result = Process.Initialize(
|
||||
CreationInfo,
|
||||
MetaData.ACI0.KernelAccessControl.Capabilities,
|
||||
System.ResourceLimit,
|
||||
MemoryRegion.Application);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
Logger.PrintError(LogClass.KernelSvc, $"Process initialization returned error \"{Result}\".");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < StaticObjects.Length; Index++)
|
||||
{
|
||||
IExecutable StaticObject = StaticObjects[Index];
|
||||
|
||||
long TextStart = NsoBase[Index] + StaticObject.TextOffset;
|
||||
long ROStart = NsoBase[Index] + StaticObject.ROOffset;
|
||||
long DataStart = NsoBase[Index] + StaticObject.DataOffset;
|
||||
|
||||
long BssStart = DataStart + StaticObject.Data.Length;
|
||||
|
||||
long BssEnd = BitUtils.AlignUp(BssStart + StaticObject.BssSize, KMemoryManager.PageSize);
|
||||
|
||||
Process.CpuMemory.WriteBytes(TextStart, StaticObject.Text);
|
||||
Process.CpuMemory.WriteBytes(ROStart, StaticObject.RO);
|
||||
Process.CpuMemory.WriteBytes(DataStart, StaticObject.Data);
|
||||
|
||||
MemoryHelper.FillWithZeros(Process.CpuMemory, BssStart, (int)(BssEnd - BssStart));
|
||||
|
||||
Process.MemoryManager.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute);
|
||||
Process.MemoryManager.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read);
|
||||
Process.MemoryManager.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite);
|
||||
}
|
||||
|
||||
Result = Process.Start(MetaData.MainThreadPriority, MetaData.MainThreadStackSize);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
Logger.PrintError(LogClass.KernelSvc, $"Process start returned error \"{Result}\".");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS
|
|||
class ServiceCtx
|
||||
{
|
||||
public Switch Device { get; private set; }
|
||||
public Process Process { get; private set; }
|
||||
public KProcess Process { get; private set; }
|
||||
public MemoryManager Memory { get; private set; }
|
||||
public KSession Session { get; private set; }
|
||||
public IpcMessage Request { get; private set; }
|
||||
|
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
public ServiceCtx(
|
||||
Switch Device,
|
||||
Process Process,
|
||||
KProcess Process,
|
||||
MemoryManager Memory,
|
||||
KSession Session,
|
||||
IpcMessage Request,
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
|
|||
|
||||
public long GetEventHandle(ServiceCtx Context)
|
||||
{
|
||||
KEvent Event = Context.Process.AppletState.MessageEvent;
|
||||
KEvent Event = Context.Device.System.AppletState.MessageEvent;
|
||||
|
||||
if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success)
|
||||
{
|
||||
|
@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
|
|||
|
||||
public long ReceiveMessage(ServiceCtx Context)
|
||||
{
|
||||
if (!Context.Process.AppletState.TryDequeueMessage(out MessageInfo Message))
|
||||
if (!Context.Device.System.AppletState.TryDequeueMessage(out MessageInfo Message))
|
||||
{
|
||||
return MakeError(ErrorModule.Am, AmErr.NoMessages);
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
|
|||
|
||||
public long GetCurrentFocusState(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState);
|
||||
Context.ResponseData.Write((byte)Context.Device.System.AppletState.FocusState);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -261,8 +261,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
|||
long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart;
|
||||
long HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd;
|
||||
|
||||
long MapRegionStart = Context.Process.MemoryManager.MapRegionStart;
|
||||
long MapRegionEnd = Context.Process.MemoryManager.MapRegionEnd;
|
||||
long MapRegionStart = Context.Process.MemoryManager.AliasRegionStart;
|
||||
long MapRegionEnd = Context.Process.MemoryManager.AliasRegionEnd;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -285,7 +285,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
|||
TargetAddress += 0x1000;
|
||||
}
|
||||
|
||||
Context.Process.LoadProgram(Info.Executable, TargetAddress);
|
||||
//Context.Process.LoadProgram(Info.Executable, TargetAddress);
|
||||
|
||||
Info.NroMappedAddress = TargetAddress;
|
||||
NroMappedAddress = TargetAddress;
|
||||
|
@ -316,7 +316,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
|||
{
|
||||
NroInfos.Remove(Info);
|
||||
|
||||
Context.Process.RemoveProgram(Info.NroMappedAddress);
|
||||
//Context.Process.RemoveProgram(Info.NroMappedAddress);
|
||||
|
||||
long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize);
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||
return ((Cmd >> 31) & 1) != 0;
|
||||
}
|
||||
|
||||
public static void UnloadProcess(Process Process)
|
||||
public static void UnloadProcess(KProcess Process)
|
||||
{
|
||||
Fds.DeleteProcess(Process);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
@ -13,11 +14,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
|
|||
|
||||
private const int FlagRemapSubRange = 0x100;
|
||||
|
||||
private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs;
|
||||
private static ConcurrentDictionary<KProcess, NvGpuASCtx> ASCtxs;
|
||||
|
||||
static NvGpuASIoctl()
|
||||
{
|
||||
ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>();
|
||||
ASCtxs = new ConcurrentDictionary<KProcess, NvGpuASCtx>();
|
||||
}
|
||||
|
||||
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
|
||||
|
@ -321,7 +322,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
|
|||
return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context));
|
||||
}
|
||||
|
||||
public static void UnloadProcess(Process Process)
|
||||
public static void UnloadProcess(KProcess Process)
|
||||
{
|
||||
ASCtxs.TryRemove(Process, out _);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
@ -21,11 +22,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
|
|||
}
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<Process, ChannelsPerProcess> Channels;
|
||||
private static ConcurrentDictionary<KProcess, ChannelsPerProcess> Channels;
|
||||
|
||||
static NvHostChannelIoctl()
|
||||
{
|
||||
Channels = new ConcurrentDictionary<Process, ChannelsPerProcess>();
|
||||
Channels = new ConcurrentDictionary<KProcess, ChannelsPerProcess>();
|
||||
}
|
||||
|
||||
public static int ProcessIoctlGpu(ServiceCtx Context, int Cmd)
|
||||
|
@ -194,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
|
|||
return Cpp.Channels[Channel];
|
||||
}
|
||||
|
||||
public static void UnloadProcess(Process Process)
|
||||
public static void UnloadProcess(KProcess Process)
|
||||
{
|
||||
Channels.TryRemove(Process, out _);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
|
@ -9,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl
|
|||
{
|
||||
class NvHostCtrlIoctl
|
||||
{
|
||||
private static ConcurrentDictionary<Process, NvHostCtrlUserCtx> UserCtxs;
|
||||
private static ConcurrentDictionary<KProcess, NvHostCtrlUserCtx> UserCtxs;
|
||||
|
||||
private static bool IsProductionMode = true;
|
||||
|
||||
static NvHostCtrlIoctl()
|
||||
{
|
||||
UserCtxs = new ConcurrentDictionary<Process, NvHostCtrlUserCtx>();
|
||||
UserCtxs = new ConcurrentDictionary<KProcess, NvHostCtrlUserCtx>();
|
||||
|
||||
if (Set.NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object ProductionModeSetting))
|
||||
{
|
||||
|
@ -390,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl
|
|||
return UserCtxs.GetOrAdd(Context.Process, (Key) => new NvHostCtrlUserCtx());
|
||||
}
|
||||
|
||||
public static void UnloadProcess(Process Process)
|
||||
public static void UnloadProcess(KProcess Process)
|
||||
{
|
||||
UserCtxs.TryRemove(Process, out _);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
|
@ -10,11 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
|
|||
{
|
||||
private const int FlagNotFreedYet = 1;
|
||||
|
||||
private static ConcurrentDictionary<Process, IdDictionary> Maps;
|
||||
private static ConcurrentDictionary<KProcess, IdDictionary> Maps;
|
||||
|
||||
static NvMapIoctl()
|
||||
{
|
||||
Maps = new ConcurrentDictionary<Process, IdDictionary>();
|
||||
Maps = new ConcurrentDictionary<KProcess, IdDictionary>();
|
||||
}
|
||||
|
||||
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
|
||||
|
@ -294,7 +295,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
|
|||
return null;
|
||||
}
|
||||
|
||||
public static void UnloadProcess(Process Process)
|
||||
public static void UnloadProcess(KProcess Process)
|
||||
{
|
||||
Maps.TryRemove(Process, out _);
|
||||
}
|
||||
|
|
BIN
Ryujinx.HLE/Homebrew.npdm
Normal file
BIN
Ryujinx.HLE/Homebrew.npdm
Normal file
Binary file not shown.
99
Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
Normal file
99
Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE.Loaders.Compression
|
||||
{
|
||||
static class BackwardsLz
|
||||
{
|
||||
private class BackwardsReader
|
||||
{
|
||||
private Stream BaseStream;
|
||||
|
||||
public BackwardsReader(Stream BaseStream)
|
||||
{
|
||||
this.BaseStream = BaseStream;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
BaseStream.Seek(-1, SeekOrigin.Current);
|
||||
|
||||
byte Value = (byte)BaseStream.ReadByte();
|
||||
|
||||
BaseStream.Seek(-1, SeekOrigin.Current);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
public short ReadInt16()
|
||||
{
|
||||
return (short)((ReadByte() << 8) | (ReadByte() << 0));
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
return ((ReadByte() << 24) |
|
||||
(ReadByte() << 16) |
|
||||
(ReadByte() << 8) |
|
||||
(ReadByte() << 0));
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Decompress(Stream Input)
|
||||
{
|
||||
BackwardsReader Reader = new BackwardsReader(Input);
|
||||
|
||||
int AdditionalDecLength = Reader.ReadInt32();
|
||||
int StartOffset = Reader.ReadInt32();
|
||||
int CompressedLength = Reader.ReadInt32();
|
||||
|
||||
Input.Seek(12 - StartOffset, SeekOrigin.Current);
|
||||
|
||||
byte[] Dec = new byte[CompressedLength + AdditionalDecLength];
|
||||
|
||||
int DecPos = Dec.Length;
|
||||
|
||||
byte Mask = 0;
|
||||
byte Header = 0;
|
||||
|
||||
while (DecPos > 0)
|
||||
{
|
||||
if ((Mask >>= 1) == 0)
|
||||
{
|
||||
Header = Reader.ReadByte();
|
||||
Mask = 0x80;
|
||||
}
|
||||
|
||||
if ((Header & Mask) == 0)
|
||||
{
|
||||
Dec[--DecPos] = Reader.ReadByte();
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort Pair = (ushort)Reader.ReadInt16();
|
||||
|
||||
int Length = (Pair >> 12) + 3;
|
||||
int Position = (Pair & 0xfff) + 3;
|
||||
|
||||
if (Position - Length >= DecPos)
|
||||
{
|
||||
int SrcPos = DecPos + Position;
|
||||
|
||||
DecPos -= Length;
|
||||
|
||||
Buffer.BlockCopy(Dec, SrcPos, Dec, DecPos, Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (Length-- > 0)
|
||||
{
|
||||
Dec[--DecPos] = Dec[DecPos + Position + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Dec;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders
|
||||
{
|
||||
struct ElfRel
|
||||
{
|
||||
public long Offset { get; private set; }
|
||||
public long Addend { get; private set; }
|
||||
|
||||
public ElfSym Symbol { get; private set; }
|
||||
public ElfRelType Type { get; private set; }
|
||||
|
||||
public ElfRel(long Offset, long Addend, ElfSym Symbol, ElfRelType Type)
|
||||
{
|
||||
this.Offset = Offset;
|
||||
this.Addend = Addend;
|
||||
this.Symbol = Symbol;
|
||||
this.Type = Type;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders
|
||||
{
|
||||
enum ElfRelType
|
||||
{
|
||||
R_AARCH64_NONE = 0,
|
||||
R_AARCH64_ABS64 = 257,
|
||||
R_AARCH64_ABS32 = 258,
|
||||
R_AARCH64_ABS16 = 259,
|
||||
R_AARCH64_PREL64 = 260,
|
||||
R_AARCH64_PREL32 = 261,
|
||||
R_AARCH64_PREL16 = 262,
|
||||
R_AARCH64_MOVW_UABS_G0 = 263,
|
||||
R_AARCH64_MOVW_UABS_G0_NC = 264,
|
||||
R_AARCH64_MOVW_UABS_G1 = 265,
|
||||
R_AARCH64_MOVW_UABS_G1_NC = 266,
|
||||
R_AARCH64_MOVW_UABS_G2 = 267,
|
||||
R_AARCH64_MOVW_UABS_G2_NC = 268,
|
||||
R_AARCH64_MOVW_UABS_G3 = 269,
|
||||
R_AARCH64_MOVW_SABS_G0 = 270,
|
||||
R_AARCH64_MOVW_SABS_G1 = 271,
|
||||
R_AARCH64_MOVW_SABS_G2 = 272,
|
||||
R_AARCH64_LD_PREL_LO19 = 273,
|
||||
R_AARCH64_ADR_PREL_LO21 = 274,
|
||||
R_AARCH64_ADR_PREL_PG_HI21 = 275,
|
||||
R_AARCH64_ADR_PREL_PG_HI21_NC = 276,
|
||||
R_AARCH64_ADD_ABS_LO12_NC = 277,
|
||||
R_AARCH64_LDST8_ABS_LO12_NC = 278,
|
||||
R_AARCH64_TSTBR14 = 279,
|
||||
R_AARCH64_CONDBR19 = 280,
|
||||
R_AARCH64_JUMP26 = 282,
|
||||
R_AARCH64_CALL26 = 283,
|
||||
R_AARCH64_LDST16_ABS_LO12_NC = 284,
|
||||
R_AARCH64_LDST32_ABS_LO12_NC = 285,
|
||||
R_AARCH64_LDST64_ABS_LO12_NC = 286,
|
||||
R_AARCH64_MOVW_PREL_G0 = 287,
|
||||
R_AARCH64_MOVW_PREL_G0_NC = 288,
|
||||
R_AARCH64_MOVW_PREL_G1 = 289,
|
||||
R_AARCH64_MOVW_PREL_G1_NC = 290,
|
||||
R_AARCH64_MOVW_PREL_G2 = 291,
|
||||
R_AARCH64_MOVW_PREL_G2_NC = 292,
|
||||
R_AARCH64_MOVW_PREL_G3 = 293,
|
||||
R_AARCH64_LDST128_ABS_LO12_NC = 299,
|
||||
R_AARCH64_MOVW_GOTOFF_G0 = 300,
|
||||
R_AARCH64_MOVW_GOTOFF_G0_NC = 301,
|
||||
R_AARCH64_MOVW_GOTOFF_G1 = 302,
|
||||
R_AARCH64_MOVW_GOTOFF_G1_NC = 303,
|
||||
R_AARCH64_MOVW_GOTOFF_G2 = 304,
|
||||
R_AARCH64_MOVW_GOTOFF_G2_NC = 305,
|
||||
R_AARCH64_MOVW_GOTOFF_G3 = 306,
|
||||
R_AARCH64_GOTREL64 = 307,
|
||||
R_AARCH64_GOTREL32 = 308,
|
||||
R_AARCH64_GOT_LD_PREL19 = 309,
|
||||
R_AARCH64_LD64_GOTOFF_LO15 = 310,
|
||||
R_AARCH64_ADR_GOT_PAGE = 311,
|
||||
R_AARCH64_LD64_GOT_LO12_NC = 312,
|
||||
R_AARCH64_LD64_GOTPAGE_LO15 = 313,
|
||||
R_AARCH64_TLSGD_ADR_PREL21 = 512,
|
||||
R_AARCH64_TLSGD_ADR_PAGE21 = 513,
|
||||
R_AARCH64_TLSGD_ADD_LO12_NC = 514,
|
||||
R_AARCH64_TLSGD_MOVW_G1 = 515,
|
||||
R_AARCH64_TLSGD_MOVW_G0_NC = 516,
|
||||
R_AARCH64_TLSLD_ADR_PREL21 = 517,
|
||||
R_AARCH64_TLSLD_ADR_PAGE21 = 518,
|
||||
R_AARCH64_TLSLD_ADD_LO12_NC = 519,
|
||||
R_AARCH64_TLSLD_MOVW_G1 = 520,
|
||||
R_AARCH64_TLSLD_MOVW_G0_NC = 521,
|
||||
R_AARCH64_TLSLD_LD_PREL19 = 522,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527,
|
||||
R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528,
|
||||
R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529,
|
||||
R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530,
|
||||
R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531,
|
||||
R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532,
|
||||
R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533,
|
||||
R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534,
|
||||
R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535,
|
||||
R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536,
|
||||
R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537,
|
||||
R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538,
|
||||
R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539,
|
||||
R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540,
|
||||
R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541,
|
||||
R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542,
|
||||
R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548,
|
||||
R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549,
|
||||
R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550,
|
||||
R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551,
|
||||
R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552,
|
||||
R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553,
|
||||
R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554,
|
||||
R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555,
|
||||
R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556,
|
||||
R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557,
|
||||
R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558,
|
||||
R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559,
|
||||
R_AARCH64_TLSDESC_LD_PREL19 = 560,
|
||||
R_AARCH64_TLSDESC_ADR_PREL21 = 561,
|
||||
R_AARCH64_TLSDESC_ADR_PAGE21 = 562,
|
||||
R_AARCH64_TLSDESC_LD64_LO12 = 563,
|
||||
R_AARCH64_TLSDESC_ADD_LO12 = 564,
|
||||
R_AARCH64_TLSDESC_OFF_G1 = 565,
|
||||
R_AARCH64_TLSDESC_OFF_G0_NC = 566,
|
||||
R_AARCH64_TLSDESC_LDR = 567,
|
||||
R_AARCH64_TLSDESC_ADD = 568,
|
||||
R_AARCH64_TLSDESC_CALL = 569,
|
||||
R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570,
|
||||
R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571,
|
||||
R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572,
|
||||
R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573,
|
||||
R_AARCH64_COPY = 1024,
|
||||
R_AARCH64_GLOB_DAT = 1025,
|
||||
R_AARCH64_JUMP_SLOT = 1026,
|
||||
R_AARCH64_RELATIVE = 1027,
|
||||
R_AARCH64_TLS_DTPMOD64 = 1028,
|
||||
R_AARCH64_TLS_DTPREL64 = 1029,
|
||||
R_AARCH64_TLS_TPREL64 = 1030,
|
||||
R_AARCH64_TLSDESC = 1031
|
||||
}
|
||||
}
|
|
@ -1,12 +1,9 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.HLE.Loaders
|
||||
|
@ -32,72 +29,6 @@ namespace Ryujinx.HLE.Loaders
|
|||
{
|
||||
Dynamic = new List<ElfDyn>();
|
||||
|
||||
FilePath = Exe.FilePath;
|
||||
|
||||
if (FilePath != null)
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
|
||||
}
|
||||
|
||||
this.Memory = Memory;
|
||||
this.MemoryManager = MemoryManager;
|
||||
this.ImageBase = ImageBase;
|
||||
this.ImageEnd = ImageBase;
|
||||
|
||||
long TextPosition = ImageBase + (uint)Exe.TextOffset;
|
||||
long ROPosition = ImageBase + (uint)Exe.ROOffset;
|
||||
long DataPosition = ImageBase + (uint)Exe.DataOffset;
|
||||
|
||||
long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize);
|
||||
long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize);
|
||||
long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize);
|
||||
long BssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize);
|
||||
|
||||
long DataAndBssSize = BssSize + DataSize;
|
||||
|
||||
ImageEnd = DataPosition + DataAndBssSize;
|
||||
|
||||
if (Exe.SourceAddress == 0)
|
||||
{
|
||||
MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize);
|
||||
|
||||
MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read);
|
||||
MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite);
|
||||
|
||||
Memory.WriteBytes(TextPosition, Exe.Text);
|
||||
Memory.WriteBytes(ROPosition, Exe.RO);
|
||||
Memory.WriteBytes(DataPosition, Exe.Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
long Result = MemoryManager.MapProcessCodeMemory(TextPosition, Exe.SourceAddress, TextSize + ROSize + DataSize);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read);
|
||||
MemoryManager.SetProcessMemoryPermission(DataPosition, DataSize, MemoryPermission.ReadAndWrite);
|
||||
|
||||
if (Exe.BssAddress != 0 && Exe.BssSize != 0)
|
||||
{
|
||||
Result = MemoryManager.MapProcessCodeMemory(DataPosition + DataSize, Exe.BssAddress, BssSize);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
MemoryManager.SetProcessMemoryPermission(DataPosition + DataSize, BssSize, MemoryPermission.ReadAndWrite);
|
||||
}
|
||||
}
|
||||
|
||||
if (Exe.Mod0Offset == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long Mod0Offset = ImageBase + Exe.Mod0Offset;
|
||||
|
||||
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
|
||||
|
@ -144,32 +75,6 @@ namespace Ryujinx.HLE.Loaders
|
|||
SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray());
|
||||
}
|
||||
|
||||
private ElfRel GetRelocation(long Position)
|
||||
{
|
||||
long Offset = Memory.ReadInt64(Position + 0);
|
||||
long Info = Memory.ReadInt64(Position + 8);
|
||||
long Addend = Memory.ReadInt64(Position + 16);
|
||||
|
||||
int RelType = (int)(Info >> 0);
|
||||
int SymIdx = (int)(Info >> 32);
|
||||
|
||||
ElfSym Symbol = GetSymbol(SymIdx);
|
||||
|
||||
return new ElfRel(Offset, Addend, Symbol, (ElfRelType)RelType);
|
||||
}
|
||||
|
||||
private ElfSym GetSymbol(int Index)
|
||||
{
|
||||
long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB);
|
||||
long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB);
|
||||
|
||||
long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
|
||||
|
||||
long Position = SymTblAddr + Index * SymEntSize;
|
||||
|
||||
return GetSymbol(Position, StrTblAddr);
|
||||
}
|
||||
|
||||
private ElfSym GetSymbol(long Position, long StrTblAddr)
|
||||
{
|
||||
int NameIndex = Memory.ReadInt32(Position + 0);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace Ryujinx.HLE.Loaders.Executables
|
||||
{
|
||||
public interface IExecutable
|
||||
interface IExecutable
|
||||
{
|
||||
string FilePath { get; }
|
||||
|
||||
|
@ -8,9 +8,6 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||
byte[] RO { get; }
|
||||
byte[] Data { get; }
|
||||
|
||||
long SourceAddress { get; }
|
||||
long BssAddress { get; }
|
||||
|
||||
int Mod0Offset { get; }
|
||||
int TextOffset { get; }
|
||||
int ROOffset { get; }
|
||||
|
|
128
Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
Normal file
128
Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
using Ryujinx.HLE.Loaders.Compression;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE.Loaders.Executables
|
||||
{
|
||||
class KernelInitialProcess
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public long TitleId { get; private set; }
|
||||
|
||||
public int ProcessCategory { get; private set; }
|
||||
|
||||
public byte MainThreadPriority { get; private set; }
|
||||
public byte DefaultProcessorId { get; private set; }
|
||||
|
||||
public byte[] Text { get; private set; }
|
||||
public byte[] RO { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
public int TextOffset { get; private set; }
|
||||
public int ROOffset { get; private set; }
|
||||
public int DataOffset { get; private set; }
|
||||
public int BssOffset { get; private set; }
|
||||
public int BssSize { get; private set; }
|
||||
|
||||
private struct SegmentHeader
|
||||
{
|
||||
public int Offset { get; private set; }
|
||||
public int DecompressedSize { get; private set; }
|
||||
public int CompressedSize { get; private set; }
|
||||
public int Attribute { get; private set; }
|
||||
|
||||
public SegmentHeader(
|
||||
int Offset,
|
||||
int DecompressedSize,
|
||||
int CompressedSize,
|
||||
int Attribute)
|
||||
{
|
||||
this.Offset = Offset;
|
||||
this.DecompressedSize = DecompressedSize;
|
||||
this.CompressedSize = CompressedSize;
|
||||
this.Attribute = Attribute;
|
||||
}
|
||||
}
|
||||
|
||||
public KernelInitialProcess(Stream Input)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(Input);
|
||||
|
||||
string Magic = ReadString(Reader, 4);
|
||||
|
||||
if (Magic != "KIP1")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Name = ReadString(Reader, 12);
|
||||
|
||||
TitleId = Reader.ReadInt64();
|
||||
|
||||
ProcessCategory = Reader.ReadInt32();
|
||||
|
||||
MainThreadPriority = Reader.ReadByte();
|
||||
DefaultProcessorId = Reader.ReadByte();
|
||||
|
||||
byte Reserved = Reader.ReadByte();
|
||||
byte Flags = Reader.ReadByte();
|
||||
|
||||
SegmentHeader[] Segments = new SegmentHeader[6];
|
||||
|
||||
for (int Index = 0; Index < Segments.Length; Index++)
|
||||
{
|
||||
Segments[Index] = new SegmentHeader(
|
||||
Reader.ReadInt32(),
|
||||
Reader.ReadInt32(),
|
||||
Reader.ReadInt32(),
|
||||
Reader.ReadInt32());
|
||||
}
|
||||
|
||||
Reader.ReadBytes(0x20);
|
||||
|
||||
Text = ReadSegment(Segments[0], Input);
|
||||
RO = ReadSegment(Segments[1], Input);
|
||||
Data = ReadSegment(Segments[2], Input);
|
||||
|
||||
TextOffset = Segments[0].Offset;
|
||||
ROOffset = Segments[1].Offset;
|
||||
DataOffset = Segments[2].Offset;
|
||||
BssOffset = Segments[3].Offset;
|
||||
BssSize = Segments[3].DecompressedSize;
|
||||
}
|
||||
|
||||
private byte[] ReadSegment(SegmentHeader Header, Stream Input)
|
||||
{
|
||||
long End = Input.Position + Header.CompressedSize;
|
||||
|
||||
Input.Seek(End, SeekOrigin.Begin);
|
||||
|
||||
byte[] Data = BackwardsLz.Decompress(Input);
|
||||
|
||||
Input.Seek(End, SeekOrigin.Begin);
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
private static string ReadString(BinaryReader Reader, int MaxSize)
|
||||
{
|
||||
string Value = string.Empty;
|
||||
|
||||
for (int Index = 0; Index < MaxSize; Index++)
|
||||
{
|
||||
char Chr = (char)Reader.ReadByte();
|
||||
|
||||
if (Chr == '\0')
|
||||
{
|
||||
Reader.BaseStream.Seek(MaxSize - Index - 1, SeekOrigin.Current);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Value += Chr;
|
||||
}
|
||||
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,9 +18,6 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||
public int DataOffset { get; private set; }
|
||||
public int BssSize { get; private set; }
|
||||
|
||||
public long SourceAddress { get; private set; }
|
||||
public long BssAddress { get; private set; }
|
||||
|
||||
[Flags]
|
||||
private enum NsoFlags
|
||||
{
|
||||
|
@ -36,9 +33,6 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||
{
|
||||
this.FilePath = FilePath;
|
||||
|
||||
SourceAddress = 0;
|
||||
BssAddress = 0;
|
||||
|
||||
BinaryReader Reader = new BinaryReader(Input);
|
||||
|
||||
Input.Seek(0, SeekOrigin.Begin);
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
enum ApplicationType
|
||||
{
|
||||
SystemModule,
|
||||
Application,
|
||||
Applet
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
enum FsPermissionBool : ulong
|
||||
{
|
||||
BisCache = 0x8000000000000080,
|
||||
EraseMmc = 0x8000000000000080,
|
||||
GameCardCertificate = 0x8000000000000010,
|
||||
GameCardIdSet = 0x8000000000000010,
|
||||
GameCardDriver = 0x8000000000000200,
|
||||
GameCardAsic = 0x8000000000000200,
|
||||
SaveDataCreate = 0x8000000000002020,
|
||||
SaveDataDelete0 = 0x8000000000000060,
|
||||
SystemSaveDataCreate0 = 0x8000000000000028,
|
||||
SystemSaveDataCreate1 = 0x8000000000000020,
|
||||
SaveDataDelete1 = 0x8000000000004028,
|
||||
SaveDataIterators0 = 0x8000000000000060,
|
||||
SaveDataIterators1 = 0x8000000000004020,
|
||||
SaveThumbnails = 0x8000000000020000,
|
||||
PosixTime = 0x8000000000000400,
|
||||
SaveDataExtraData = 0x8000000000004060,
|
||||
GlobalMode = 0x8000000000080000,
|
||||
SpeedEmulation = 0x8000000000080000,
|
||||
NULL = 0,
|
||||
PaddingFiles = 0xC000000000800000,
|
||||
SaveData_Debug = 0xC000000001000000,
|
||||
SaveData_SystemManagement = 0xC000000002000000,
|
||||
Unknown0x16 = 0x8000000004000000,
|
||||
Unknown0x17 = 0x8000000008000000,
|
||||
Unknown0x18 = 0x8000000010000000,
|
||||
Unknown0x19 = 0x8000000000000800,
|
||||
Unknown0x1A = 0x8000000000004020
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
enum FsPermissionRw : ulong
|
||||
{
|
||||
MountContentType2 = 0x8000000000000801,
|
||||
MountContentType5 = 0x8000000000000801,
|
||||
MountContentType3 = 0x8000000000000801,
|
||||
MountContentType4 = 0x8000000000000801,
|
||||
MountContentType6 = 0x8000000000000801,
|
||||
MountContentType7 = 0x8000000000000801,
|
||||
Unknown0x6 = 0x8000000000000000,
|
||||
ContentStorageAccess = 0x8000000000000800,
|
||||
ImageDirectoryAccess = 0x8000000000001000,
|
||||
MountBisType28 = 0x8000000000000084,
|
||||
MountBisType29 = 0x8000000000000080,
|
||||
MountBisType30 = 0x8000000000008080,
|
||||
MountBisType31 = 0x8000000000008080,
|
||||
Unknown0xD = 0x8000000000000080,
|
||||
SdCardAccess = 0xC000000000200000,
|
||||
GameCardUser = 0x8000000000000010,
|
||||
SaveDataAccess0 = 0x8000000000040020,
|
||||
SystemSaveDataAccess0 = 0x8000000000000028,
|
||||
SaveDataAccess1 = 0x8000000000000020,
|
||||
SystemSaveDataAccess1 = 0x8000000000000020,
|
||||
BisPartition0 = 0x8000000000010082,
|
||||
BisPartition10 = 0x8000000000010080,
|
||||
BisPartition20 = 0x8000000000010080,
|
||||
BisPartition21 = 0x8000000000010080,
|
||||
BisPartition22 = 0x8000000000010080,
|
||||
BisPartition23 = 0x8000000000010080,
|
||||
BisPartition24 = 0x8000000000010080,
|
||||
BisPartition25 = 0x8000000000010080,
|
||||
BisPartition26 = 0x8000000000000080,
|
||||
BisPartition27 = 0x8000000000000084,
|
||||
BisPartition28 = 0x8000000000000084,
|
||||
BisPartition29 = 0x8000000000000080,
|
||||
BisPartition30 = 0x8000000000000080,
|
||||
BisPartition31 = 0x8000000000000080,
|
||||
BisPartition32 = 0x8000000000000080,
|
||||
Unknown0x23 = 0xC000000000200000,
|
||||
GameCard_System = 0x8000000000000100,
|
||||
MountContent_System = 0x8000000000100008,
|
||||
HostAccess = 0xC000000000400000
|
||||
}
|
||||
}
|
|
@ -1,173 +1,23 @@
|
|||
using Ryujinx.HLE.Exceptions;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
class KernelAccessControl
|
||||
{
|
||||
public ReadOnlyCollection<KernelAccessControlItem> Items;
|
||||
public int[] Capabilities { get; private set; }
|
||||
|
||||
public KernelAccessControl(Stream Stream, int Offset, int Size)
|
||||
{
|
||||
Stream.Seek(Offset, SeekOrigin.Begin);
|
||||
|
||||
Capabilities = new int[Size / 4];
|
||||
|
||||
BinaryReader Reader = new BinaryReader(Stream);
|
||||
|
||||
KernelAccessControlItem[] Items = new KernelAccessControlItem[Size / 4];
|
||||
|
||||
for (int Index = 0; Index < Size / 4; Index++)
|
||||
for (int Index = 0; Index < Capabilities.Length; Index++)
|
||||
{
|
||||
uint Descriptor = Reader.ReadUInt32();
|
||||
|
||||
//Ignore the descriptor.
|
||||
if (Descriptor == 0xffffffff)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Items[Index] = new KernelAccessControlItem();
|
||||
|
||||
int LowBits = 0;
|
||||
|
||||
while ((Descriptor & 1) != 0)
|
||||
{
|
||||
Descriptor >>= 1;
|
||||
|
||||
LowBits++;
|
||||
}
|
||||
|
||||
Descriptor >>= 1;
|
||||
|
||||
switch (LowBits)
|
||||
{
|
||||
//Kernel flags.
|
||||
case 3:
|
||||
{
|
||||
Items[Index].HasKernelFlags = true;
|
||||
|
||||
Items[Index].HighestThreadPriority = (Descriptor >> 0) & 0x3f;
|
||||
Items[Index].LowestThreadPriority = (Descriptor >> 6) & 0x3f;
|
||||
Items[Index].LowestCpuId = (Descriptor >> 12) & 0xff;
|
||||
Items[Index].HighestCpuId = (Descriptor >> 20) & 0xff;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Syscall mask.
|
||||
case 4:
|
||||
{
|
||||
Items[Index].HasSvcFlags = true;
|
||||
|
||||
Items[Index].AllowedSvcs = new bool[0x80];
|
||||
|
||||
int SysCallBase = (int)(Descriptor >> 24) * 0x18;
|
||||
|
||||
for (int SysCall = 0; SysCall < 0x18 && SysCallBase + SysCall < 0x80; SysCall++)
|
||||
{
|
||||
Items[Index].AllowedSvcs[SysCallBase + SysCall] = (Descriptor & 1) != 0;
|
||||
|
||||
Descriptor >>= 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Map IO/Normal.
|
||||
case 6:
|
||||
{
|
||||
ulong Address = (Descriptor & 0xffffff) << 12;
|
||||
bool IsRo = (Descriptor >> 24) != 0;
|
||||
|
||||
if (Index == Size / 4 - 1)
|
||||
{
|
||||
throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!");
|
||||
}
|
||||
|
||||
Descriptor = Reader.ReadUInt32();
|
||||
|
||||
if ((Descriptor & 0x7f) != 0x3f)
|
||||
{
|
||||
throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!");
|
||||
}
|
||||
|
||||
Descriptor >>= 7;
|
||||
|
||||
ulong MmioSize = (Descriptor & 0xffffff) << 12;
|
||||
bool IsNormal = (Descriptor >> 24) != 0;
|
||||
|
||||
Items[Index].NormalMmio.Add(new KernelAccessControlMmio(Address, MmioSize, IsRo, IsNormal));
|
||||
|
||||
Index++;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Map Normal Page.
|
||||
case 7:
|
||||
{
|
||||
ulong Address = Descriptor << 12;
|
||||
|
||||
Items[Index].PageMmio.Add(new KernelAccessControlMmio(Address, 0x1000, false, false));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//IRQ Pair.
|
||||
case 11:
|
||||
{
|
||||
Items[Index].Irq.Add(new KernelAccessControlIrq(
|
||||
(Descriptor >> 0) & 0x3ff,
|
||||
(Descriptor >> 10) & 0x3ff));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Application Type.
|
||||
case 13:
|
||||
{
|
||||
Items[Index].HasApplicationType = true;
|
||||
|
||||
Items[Index].ApplicationType = (int)Descriptor & 7;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Kernel Release Version.
|
||||
case 14:
|
||||
{
|
||||
Items[Index].HasKernelVersion = true;
|
||||
|
||||
Items[Index].KernelVersionRelease = (int)Descriptor;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Handle Table Size.
|
||||
case 15:
|
||||
{
|
||||
Items[Index].HasHandleTableSize = true;
|
||||
|
||||
Items[Index].HandleTableSize = (int)Descriptor;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Debug Flags.
|
||||
case 16:
|
||||
{
|
||||
Items[Index].HasDebugFlags = true;
|
||||
|
||||
Items[Index].AllowDebug = ((Descriptor >> 0) & 1) != 0;
|
||||
Items[Index].ForceDebug = ((Descriptor >> 1) & 1) != 0;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
Capabilities[Index] = Reader.ReadInt32();
|
||||
}
|
||||
|
||||
this.Items = Array.AsReadOnly(Items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
struct KernelAccessControlIrq
|
||||
{
|
||||
public uint Irq0 { get; private set; }
|
||||
public uint Irq1 { get; private set; }
|
||||
|
||||
public KernelAccessControlIrq(uint Irq0, uint Irq1)
|
||||
{
|
||||
this.Irq0 = Irq0;
|
||||
this.Irq1 = Irq1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
struct KernelAccessControlMmio
|
||||
{
|
||||
public ulong Address { get; private set; }
|
||||
public ulong Size { get; private set; }
|
||||
public bool IsRo { get; private set; }
|
||||
public bool IsNormal { get; private set; }
|
||||
|
||||
public KernelAccessControlMmio(
|
||||
ulong Address,
|
||||
ulong Size,
|
||||
bool IsRo,
|
||||
bool IsNormal)
|
||||
{
|
||||
this.Address = Address;
|
||||
this.Size = Size;
|
||||
this.IsRo = IsRo;
|
||||
this.IsNormal = IsNormal;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
struct KernelAccessControlItem
|
||||
{
|
||||
public bool HasKernelFlags { get; set; }
|
||||
public uint LowestThreadPriority { get; set; }
|
||||
public uint HighestThreadPriority { get; set; }
|
||||
public uint LowestCpuId { get; set; }
|
||||
public uint HighestCpuId { get; set; }
|
||||
|
||||
public bool HasSvcFlags { get; set; }
|
||||
public bool[] AllowedSvcs { get; set; }
|
||||
|
||||
public List<KernelAccessControlMmio> NormalMmio { get; set; }
|
||||
public List<KernelAccessControlMmio> PageMmio { get; set; }
|
||||
public List<KernelAccessControlIrq> Irq { get; set; }
|
||||
|
||||
public bool HasApplicationType { get; set; }
|
||||
public int ApplicationType { get; set; }
|
||||
|
||||
public bool HasKernelVersion { get; set; }
|
||||
public int KernelVersionRelease { get; set; }
|
||||
|
||||
public bool HasHandleTableSize { get; set; }
|
||||
public int HandleTableSize { get; set; }
|
||||
|
||||
public bool HasDebugFlags { get; set; }
|
||||
public bool AllowDebug { get; set; }
|
||||
public bool ForceDebug { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
@ -12,15 +11,15 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
{
|
||||
private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
|
||||
|
||||
public bool Is64Bits { get; private set; }
|
||||
public int AddressSpaceWidth { get; private set; }
|
||||
public byte MainThreadPriority { get; private set; }
|
||||
public byte DefaultCpuId { get; private set; }
|
||||
public int SystemResourceSize { get; private set; }
|
||||
public int ProcessCategory { get; private set; }
|
||||
public int MainEntrypointStackSize { get; private set; }
|
||||
public string TitleName { get; private set; }
|
||||
public byte[] ProductCode { get; private set; }
|
||||
public byte MmuFlags { get; private set; }
|
||||
public bool Is64Bits { get; private set; }
|
||||
public byte MainThreadPriority { get; private set; }
|
||||
public byte DefaultCpuId { get; private set; }
|
||||
public int PersonalMmHeapSize { get; private set; }
|
||||
public int ProcessCategory { get; private set; }
|
||||
public int MainThreadStackSize { get; private set; }
|
||||
public string TitleName { get; private set; }
|
||||
public byte[] ProductCode { get; private set; }
|
||||
|
||||
public ACI0 ACI0 { get; private set; }
|
||||
public ACID ACID { get; private set; }
|
||||
|
@ -36,27 +35,22 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
|
||||
Reader.ReadInt64();
|
||||
|
||||
//MmuFlags, bit0: 64-bit instructions, bits1-3: address space width (1=64-bit, 2=32-bit). Needs to be <= 0xF.
|
||||
byte MmuFlags = Reader.ReadByte();
|
||||
MmuFlags = Reader.ReadByte();
|
||||
|
||||
Is64Bits = (MmuFlags & 1) != 0;
|
||||
AddressSpaceWidth = (MmuFlags >> 1) & 7;
|
||||
Is64Bits = (MmuFlags & 1) != 0;
|
||||
|
||||
Reader.ReadByte();
|
||||
|
||||
MainThreadPriority = Reader.ReadByte(); //(0-63).
|
||||
MainThreadPriority = Reader.ReadByte();
|
||||
DefaultCpuId = Reader.ReadByte();
|
||||
|
||||
Reader.ReadInt32();
|
||||
|
||||
//System resource size (max size as of 5.x: 534773760).
|
||||
SystemResourceSize = EndianSwap.Swap32(Reader.ReadInt32());
|
||||
PersonalMmHeapSize = Reader.ReadInt32();
|
||||
|
||||
//ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here.
|
||||
ProcessCategory = EndianSwap.Swap32(Reader.ReadInt32());
|
||||
ProcessCategory = Reader.ReadInt32();
|
||||
|
||||
//Main entrypoint stack size.
|
||||
MainEntrypointStackSize = Reader.ReadInt32();
|
||||
MainThreadStackSize = Reader.ReadInt32();
|
||||
|
||||
byte[] TempTitleName = Reader.ReadBytes(0x10);
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
break;
|
||||
}
|
||||
|
||||
int Length = ((ControlByte & 0x07)) + 1;
|
||||
bool RegisterAllowed = ((ControlByte & 0x80) != 0);
|
||||
int Length = (ControlByte & 0x07) + 1;
|
||||
bool RegisterAllowed = (ControlByte & 0x80) != 0;
|
||||
|
||||
Services.Add(Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed);
|
||||
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
enum SvcName
|
||||
{
|
||||
Reserved0,
|
||||
SetHeapSize,
|
||||
SetMemoryPermission,
|
||||
SetMemoryAttribute,
|
||||
MapMemory,
|
||||
UnmapMemory,
|
||||
QueryMemory,
|
||||
ExitProcess,
|
||||
CreateThread,
|
||||
StartThread,
|
||||
ExitThread,
|
||||
SleepThread,
|
||||
GetThreadPriority,
|
||||
SetThreadPriority,
|
||||
GetThreadCoreMask,
|
||||
SetThreadCoreMask,
|
||||
GetCurrentProcessorNumber,
|
||||
SignalEvent,
|
||||
ClearEvent,
|
||||
MapSharedMemory,
|
||||
UnmapSharedMemory,
|
||||
CreateTransferMemory,
|
||||
CloseHandle,
|
||||
ResetSignal,
|
||||
WaitSynchronization,
|
||||
CancelSynchronization,
|
||||
ArbitrateLock,
|
||||
ArbitrateUnlock,
|
||||
WaitProcessWideKeyAtomic,
|
||||
SignalProcessWideKey,
|
||||
GetSystemTick,
|
||||
ConnectToNamedPort,
|
||||
SendSyncRequestLight,
|
||||
SendSyncRequest,
|
||||
SendSyncRequestWithUserBuffer,
|
||||
SendAsyncRequestWithUserBuffer,
|
||||
GetProcessId,
|
||||
GetThreadId,
|
||||
Break,
|
||||
OutputDebugString,
|
||||
ReturnFromException,
|
||||
GetInfo,
|
||||
FlushEntireDataCache,
|
||||
FlushDataCache,
|
||||
MapPhysicalMemory,
|
||||
UnmapPhysicalMemory,
|
||||
GetFutureThreadInfo,
|
||||
GetLastThreadInfo,
|
||||
GetResourceLimitLimitValue,
|
||||
GetResourceLimitCurrentValue,
|
||||
SetThreadActivity,
|
||||
GetThreadContext3,
|
||||
WaitForAddress,
|
||||
SignalToAddress,
|
||||
Reserved1,
|
||||
Reserved2,
|
||||
Reserved3,
|
||||
Reserved4,
|
||||
Reserved5,
|
||||
Reserved6,
|
||||
DumpInfo,
|
||||
DumpInfoNew,
|
||||
Reserved7,
|
||||
Reserved8,
|
||||
CreateSession,
|
||||
AcceptSession,
|
||||
ReplyAndReceiveLight,
|
||||
ReplyAndReceive,
|
||||
ReplyAndReceiveWithUserBuffer,
|
||||
CreateEvent,
|
||||
Reserved9,
|
||||
Reserved10,
|
||||
MapPhysicalMemoryUnsafe,
|
||||
UnmapPhysicalMemoryUnsafe,
|
||||
SetUnsafeLimit,
|
||||
CreateCodeMemory,
|
||||
ControlCodeMemory,
|
||||
SleepSystem,
|
||||
ReadWriteRegister,
|
||||
SetProcessActivity,
|
||||
CreateSharedMemory,
|
||||
MapTransferMemory,
|
||||
UnmapTransferMemory,
|
||||
CreateInterruptEvent,
|
||||
QueryPhysicalAddress,
|
||||
QueryIoMapping,
|
||||
CreateDeviceAddressSpace,
|
||||
AttachDeviceAddressSpace,
|
||||
DetachDeviceAddressSpace,
|
||||
MapDeviceAddressSpaceByForce,
|
||||
MapDeviceAddressSpaceAligned,
|
||||
MapDeviceAddressSpace,
|
||||
UnmapDeviceAddressSpace,
|
||||
InvalidateProcessDataCache,
|
||||
StoreProcessDataCache,
|
||||
FlushProcessDataCache,
|
||||
DebugActiveProcess,
|
||||
BreakDebugProcess,
|
||||
TerminateDebugProcess,
|
||||
GetDebugEvent,
|
||||
ContinueDebugEvent,
|
||||
GetProcessList,
|
||||
GetThreadList,
|
||||
GetDebugThreadContext,
|
||||
SetDebugThreadContext,
|
||||
QueryDebugProcessMemory,
|
||||
ReadDebugProcessMemory,
|
||||
WriteDebugProcessMemory,
|
||||
SetHardwareBreakPoint,
|
||||
GetDebugThreadParam,
|
||||
Reserved11,
|
||||
GetSystemInfo,
|
||||
CreatePort,
|
||||
ManageNamedPort,
|
||||
ConnectToPort,
|
||||
SetProcessMemoryPermission,
|
||||
MapProcessMemory,
|
||||
UnmapProcessMemory,
|
||||
QueryProcessMemory,
|
||||
MapProcessCodeMemory,
|
||||
UnmapProcessCodeMemory,
|
||||
CreateProcess,
|
||||
StartProcess,
|
||||
TerminateProcess,
|
||||
GetProcessInfo,
|
||||
CreateResourceLimit,
|
||||
SetResourceLimitLimitValue,
|
||||
CallSecureMonitor
|
||||
}
|
||||
}
|
|
@ -14,10 +14,12 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Homebrew.npdm" />
|
||||
<None Remove="RyujinxProfileImage.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Homebrew.npdm" />
|
||||
<EmbeddedResource Include="RyujinxProfileImage.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.Utilities
|
||||
namespace Ryujinx.HLE.Utilities
|
||||
{
|
||||
public enum WSAError
|
||||
enum WSAError
|
||||
{
|
||||
/*
|
||||
* All Windows Sockets error constants are biased by WSABASEERR from
|
||||
* the "normal"
|
||||
*/
|
||||
WSABASEERR = 10000,
|
||||
|
||||
|
||||
/*
|
||||
* Windows Sockets definitions of regular Microsoft C error constants
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue