Better GetInfo implementation, improve checking in some places with information from process capabilities
This commit is contained in:
parent
f79ce1271c
commit
aa16232977
26 changed files with 529 additions and 433 deletions
|
@ -13,7 +13,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using Nso = Ryujinx.HLE.Loaders.Executables.Nso;
|
using NxStaticObject = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
|
@ -49,8 +49,6 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal KSynchronization Synchronization { get; private set; }
|
internal KSynchronization Synchronization { get; private set; }
|
||||||
|
|
||||||
internal LinkedList<KThread> Withholders { get; private set; }
|
|
||||||
|
|
||||||
internal KContextIdManager ContextIdManager { get; private set; }
|
internal KContextIdManager ContextIdManager { get; private set; }
|
||||||
|
|
||||||
private long KipId;
|
private long KipId;
|
||||||
|
@ -105,8 +103,6 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
Synchronization = new KSynchronization(this);
|
Synchronization = new KSynchronization(this);
|
||||||
|
|
||||||
Withholders = new LinkedList<KThread>();
|
|
||||||
|
|
||||||
ContextIdManager = new KContextIdManager();
|
ContextIdManager = new KContextIdManager();
|
||||||
|
|
||||||
KipId = InitialKipId;
|
KipId = InitialKipId;
|
||||||
|
@ -118,16 +114,16 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.Service];
|
KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.Service];
|
||||||
|
|
||||||
long HidPA = Region.Address;
|
long HidPa = Region.Address;
|
||||||
long FontPA = Region.Address + HidSize;
|
long FontPa = Region.Address + HidSize;
|
||||||
|
|
||||||
HidBaseAddress = HidPA - DramMemoryMap.DramBase;
|
HidBaseAddress = HidPa - DramMemoryMap.DramBase;
|
||||||
|
|
||||||
KPageList HidPageList = new KPageList();
|
KPageList HidPageList = new KPageList();
|
||||||
KPageList FontPageList = new KPageList();
|
KPageList FontPageList = new KPageList();
|
||||||
|
|
||||||
HidPageList .AddRange(HidPA, HidSize / KMemoryManager.PageSize);
|
HidPageList .AddRange(HidPa, HidSize / KMemoryManager.PageSize);
|
||||||
FontPageList.AddRange(FontPA, FontSize / KMemoryManager.PageSize);
|
FontPageList.AddRange(FontPa, FontSize / KMemoryManager.PageSize);
|
||||||
|
|
||||||
HidSharedMem = new KSharedMemory(HidPageList, 0, 0, MemoryPermission.Read);
|
HidSharedMem = new KSharedMemory(HidPageList, 0, 0, MemoryPermission.Read);
|
||||||
FontSharedMem = new KSharedMemory(FontPageList, 0, 0, MemoryPermission.Read);
|
FontSharedMem = new KSharedMemory(FontPageList, 0, 0, MemoryPermission.Read);
|
||||||
|
@ -136,7 +132,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
AppletState.SetFocus(true);
|
AppletState.SetFocus(true);
|
||||||
|
|
||||||
Font = new SharedFontManager(Device, FontPA - DramMemoryMap.DramBase);
|
Font = new SharedFontManager(Device, FontPa - DramMemoryMap.DramBase);
|
||||||
|
|
||||||
VsyncEvent = new KEvent(this);
|
VsyncEvent = new KEvent(this);
|
||||||
|
|
||||||
|
@ -187,9 +183,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||||
{
|
{
|
||||||
string Name = Path.GetFileNameWithoutExtension(File);
|
NxStaticObject StaticObject = new NxStaticObject(Input);
|
||||||
|
|
||||||
Nso StaticObject = new Nso(Input, Name);
|
|
||||||
|
|
||||||
StaticObjects.Add(StaticObject);
|
StaticObjects.Add(StaticObject);
|
||||||
}
|
}
|
||||||
|
@ -420,9 +414,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
|
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
|
||||||
|
|
||||||
string Name = Path.GetFileNameWithoutExtension(File.Name);
|
NxStaticObject StaticObject = new NxStaticObject(Exefs.OpenFile(File));
|
||||||
|
|
||||||
Nso StaticObject = new Nso(Exefs.OpenFile(File), Name);
|
|
||||||
|
|
||||||
StaticObjects.Add(StaticObject);
|
StaticObjects.Add(StaticObject);
|
||||||
}
|
}
|
||||||
|
@ -448,6 +440,15 @@ namespace Ryujinx.HLE.HOS
|
||||||
return ControlData;
|
return ControlData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ControlNca != null)
|
||||||
|
{
|
||||||
|
ReadControlData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
|
||||||
|
}
|
||||||
|
|
||||||
if (!MetaData.Is64Bits)
|
if (!MetaData.Is64Bits)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("32-bit titles are not supported!");
|
throw new NotImplementedException("32-bit titles are not supported!");
|
||||||
|
@ -472,8 +473,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
||||||
{
|
{
|
||||||
IExecutable StaticObject = IsNro
|
IExecutable StaticObject = IsNro
|
||||||
? (IExecutable)new Nro(Input, FilePath)
|
? (IExecutable)new NxRelocatableObject(Input)
|
||||||
: (IExecutable)new Nso(Input, FilePath);
|
: (IExecutable)new NxStaticObject(Input);
|
||||||
|
|
||||||
ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject });
|
ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject });
|
||||||
}
|
}
|
||||||
|
@ -489,11 +490,6 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream GetHomebrewNpdmStream()
|
|
||||||
{
|
|
||||||
return Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadKeySet()
|
public void LoadKeySet()
|
||||||
{
|
{
|
||||||
string KeyFile = null;
|
string KeyFile = null;
|
||||||
|
|
|
@ -2,7 +2,7 @@ using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
|
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
|
||||||
using Ryujinx.HLE.Loaders;
|
using Ryujinx.HLE.Loaders.Elf;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -20,9 +20,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
public long BaseAddress { get; private set; }
|
public long BaseAddress { get; private set; }
|
||||||
|
|
||||||
public ElfSym[] Symbols { get; private set; }
|
public ElfSymbol[] Symbols { get; private set; }
|
||||||
|
|
||||||
public Image(long BaseAddress, ElfSym[] Symbols)
|
public Image(long BaseAddress, ElfSymbol[] Symbols)
|
||||||
{
|
{
|
||||||
this.BaseAddress = BaseAddress;
|
this.BaseAddress = BaseAddress;
|
||||||
this.Symbols = Symbols;
|
this.Symbols = Symbols;
|
||||||
|
@ -81,7 +81,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
while (FramePointer != 0)
|
while (FramePointer != 0)
|
||||||
{
|
{
|
||||||
if (!Owner.CpuMemory.IsMapped(FramePointer))
|
if ((FramePointer & 7) != 0 ||
|
||||||
|
!Owner.CpuMemory.IsMapped(FramePointer) ||
|
||||||
|
!Owner.CpuMemory.IsMapped(FramePointer + 8))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
int Middle = Left + (Size >> 1);
|
int Middle = Left + (Size >> 1);
|
||||||
|
|
||||||
ElfSym Symbol = Image.Symbols[Middle];
|
ElfSymbol Symbol = Image.Symbols[Middle];
|
||||||
|
|
||||||
long EndAddr = Symbol.Value + Symbol.Size;
|
long EndAddr = Symbol.Value + Symbol.Size;
|
||||||
|
|
||||||
|
@ -226,7 +228,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<ElfDynTag, long> Dynamic = new Dictionary<ElfDynTag, long>();
|
Dictionary<ElfDynamicTag, long> Dynamic = new Dictionary<ElfDynamicTag, long>();
|
||||||
|
|
||||||
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
|
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
|
||||||
|
|
||||||
|
@ -249,9 +251,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
DynamicOffset += 0x10;
|
DynamicOffset += 0x10;
|
||||||
|
|
||||||
ElfDynTag Tag = (ElfDynTag)TagVal;
|
ElfDynamicTag Tag = (ElfDynamicTag)TagVal;
|
||||||
|
|
||||||
if (Tag == ElfDynTag.DT_NULL)
|
if (Tag == ElfDynamicTag.DT_NULL)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -259,16 +261,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Dynamic[Tag] = Value;
|
Dynamic[Tag] = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
long StrTblAddr = TextOffset + Dynamic[ElfDynTag.DT_STRTAB];
|
long StrTblAddr = TextOffset + Dynamic[ElfDynamicTag.DT_STRTAB];
|
||||||
long SymTblAddr = TextOffset + Dynamic[ElfDynTag.DT_SYMTAB];
|
long SymTblAddr = TextOffset + Dynamic[ElfDynamicTag.DT_SYMTAB];
|
||||||
|
|
||||||
long SymEntSize = Dynamic[ElfDynTag.DT_SYMENT];
|
long SymEntSize = Dynamic[ElfDynamicTag.DT_SYMENT];
|
||||||
|
|
||||||
List<ElfSym> Symbols = new List<ElfSym>();
|
List<ElfSymbol> Symbols = new List<ElfSymbol>();
|
||||||
|
|
||||||
while ((ulong)SymTblAddr < (ulong)StrTblAddr)
|
while ((ulong)SymTblAddr < (ulong)StrTblAddr)
|
||||||
{
|
{
|
||||||
ElfSym Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
|
ElfSymbol Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
|
||||||
|
|
||||||
Symbols.Add(Sym);
|
Symbols.Add(Sym);
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ElfSym GetSymbol(MemoryManager Memory, long Address, long StrTblAddr)
|
private ElfSymbol GetSymbol(MemoryManager Memory, long Address, long StrTblAddr)
|
||||||
{
|
{
|
||||||
int NameIndex = Memory.ReadInt32(Address + 0);
|
int NameIndex = Memory.ReadInt32(Address + 0);
|
||||||
int Info = Memory.ReadByte (Address + 4);
|
int Info = Memory.ReadByte (Address + 4);
|
||||||
|
@ -297,7 +299,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Name += (char)Chr;
|
Name += (char)Chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ElfSym(Name, Info, Other, SHIdx, Value, Size);
|
return new ElfSymbol(Name, Info, Other, SHIdx, Value, Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public bool ContextSwitchNeeded { get; private set; }
|
public bool ContextSwitchNeeded { get; private set; }
|
||||||
|
|
||||||
|
public long LastContextSwitchTime { get; private set; }
|
||||||
|
|
||||||
|
public long TotalIdleTimeTicks { get; private set; } //TODO
|
||||||
|
|
||||||
public KThread CurrentThread { get; private set; }
|
public KThread CurrentThread { get; private set; }
|
||||||
public KThread SelectedThread { get; private set; }
|
public KThread SelectedThread { get; private set; }
|
||||||
|
|
||||||
|
@ -23,11 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
SelectedThread = Thread;
|
SelectedThread = Thread;
|
||||||
|
|
||||||
if (Thread != null)
|
|
||||||
{
|
|
||||||
Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SelectedThread != CurrentThread)
|
if (SelectedThread != CurrentThread)
|
||||||
{
|
{
|
||||||
ContextSwitchNeeded = true;
|
ContextSwitchNeeded = true;
|
||||||
|
@ -38,13 +37,25 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
ContextSwitchNeeded = false;
|
ContextSwitchNeeded = false;
|
||||||
|
|
||||||
|
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
CurrentThread = SelectedThread;
|
CurrentThread = SelectedThread;
|
||||||
|
|
||||||
|
if (CurrentThread != null)
|
||||||
|
{
|
||||||
|
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||||
|
CurrentThread.LastScheduledTime = CurrentTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ContextSwitch()
|
public void ContextSwitch()
|
||||||
{
|
{
|
||||||
ContextSwitchNeeded = false;
|
ContextSwitchNeeded = false;
|
||||||
|
|
||||||
|
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
if (CurrentThread != null)
|
if (CurrentThread != null)
|
||||||
{
|
{
|
||||||
CoreManager.Reset(CurrentThread.Context.Work);
|
CoreManager.Reset(CurrentThread.Context.Work);
|
||||||
|
@ -54,6 +65,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (CurrentThread != null)
|
if (CurrentThread != null)
|
||||||
{
|
{
|
||||||
|
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||||
|
CurrentThread.LastScheduledTime = CurrentTime;
|
||||||
|
|
||||||
CurrentThread.ClearExclusive();
|
CurrentThread.ClearExclusive();
|
||||||
|
|
||||||
CoreManager.Set(CurrentThread.Context.Work);
|
CoreManager.Set(CurrentThread.Context.Work);
|
||||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KProcessHandleTable
|
class KHandleTable
|
||||||
{
|
{
|
||||||
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
||||||
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
||||||
|
@ -20,38 +20,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private ushort IdCounter;
|
private ushort IdCounter;
|
||||||
|
|
||||||
public KProcessHandleTable(Horizon System)
|
public KHandleTable(Horizon System)
|
||||||
{
|
{
|
||||||
this.System = System;
|
this.System = System;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KProcessHandleTable(Horizon System, int Size = 1024)
|
|
||||||
{
|
|
||||||
this.System = System;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult Initialize(int Size)
|
public KernelResult Initialize(int Size)
|
||||||
{
|
{
|
||||||
if ((uint)Size > 1024)
|
if ((uint)Size > 1024)
|
||||||
|
@ -195,6 +168,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KProcess GetKProcess(int Handle)
|
||||||
|
{
|
||||||
|
if (Handle == SelfProcessHandle)
|
||||||
|
{
|
||||||
|
return System.Scheduler.GetCurrentProcess();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetObject<KProcess>(Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Destroy()
|
public void Destroy()
|
||||||
{
|
{
|
||||||
lock (Table)
|
lock (Table)
|
|
@ -12,6 +12,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
public const int PageSize = 0x1000;
|
public const int PageSize = 0x1000;
|
||||||
|
|
||||||
|
private const int KMemoryBlockSize = 0x40;
|
||||||
|
|
||||||
private LinkedList<KMemoryBlock> Blocks;
|
private LinkedList<KMemoryBlock> Blocks;
|
||||||
|
|
||||||
private MemoryManager CpuMemory;
|
private MemoryManager CpuMemory;
|
||||||
|
@ -1953,7 +1955,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
throw new ArgumentException($"Invalid state value \"{State}\".");
|
throw new ArgumentException($"Invalid state value \"{State}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetAddrSpaceBaseAddr()
|
public long GetAddrSpaceBaseAddr()
|
||||||
{
|
{
|
||||||
if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
|
if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
|
||||||
{
|
{
|
||||||
|
@ -1969,7 +1971,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetAddrSpaceSize()
|
public long GetAddrSpaceSize()
|
||||||
{
|
{
|
||||||
if (AddrSpaceWidth == 36)
|
if (AddrSpaceWidth == 36)
|
||||||
{
|
{
|
||||||
|
@ -2147,6 +2149,19 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetMmUsedPages()
|
||||||
|
{
|
||||||
|
lock (Blocks)
|
||||||
|
{
|
||||||
|
return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long GetMmUsedSize()
|
||||||
|
{
|
||||||
|
return Blocks.Count * KMemoryBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
public bool InsideAddrSpace(long Address, long Size)
|
public bool InsideAddrSpace(long Address, long Size)
|
||||||
{
|
{
|
||||||
ulong Start = (ulong)Address;
|
ulong Start = (ulong)Address;
|
||||||
|
|
|
@ -28,10 +28,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public int DefaultCpuCore { get; private set; }
|
public int DefaultCpuCore { get; private set; }
|
||||||
|
|
||||||
|
public bool Debug { get; private set; }
|
||||||
|
|
||||||
public KResourceLimit ResourceLimit { get; private set; }
|
public KResourceLimit ResourceLimit { get; private set; }
|
||||||
|
|
||||||
private long PersonalMmHeapBase;
|
public long PersonalMmHeapPagesCount { get; private set; }
|
||||||
private long PersonalMmHeapPagesCount;
|
|
||||||
|
|
||||||
private ProcessState State;
|
private ProcessState State;
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public KAddressArbiter AddressArbiter { get; private set; }
|
public KAddressArbiter AddressArbiter { get; private set; }
|
||||||
|
|
||||||
private long[] RandomEntropy;
|
public long[] RandomEntropy { get; private set; }
|
||||||
|
|
||||||
private bool Signaled;
|
private bool Signaled;
|
||||||
private bool UseSystemMemBlocks;
|
private bool UseSystemMemBlocks;
|
||||||
|
@ -65,9 +66,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private long MemoryUsageCapacity;
|
private long MemoryUsageCapacity;
|
||||||
private int Category;
|
private int Category;
|
||||||
|
|
||||||
public KProcessHandleTable HandleTable { get; private set; }
|
public KHandleTable HandleTable { get; private set; }
|
||||||
|
|
||||||
private long TlsAddress;
|
public long UserExceptionContextAddress { get; private set; }
|
||||||
|
|
||||||
private LinkedList<KThread> Threads;
|
private LinkedList<KThread> Threads;
|
||||||
|
|
||||||
|
@ -345,14 +346,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelResult Result = AllocateThreadLocalStorage(out TlsAddress);
|
KernelResult Result = AllocateThreadLocalStorage(out long UserExceptionContextAddress);
|
||||||
|
|
||||||
if (Result != KernelResult.Success)
|
if (Result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryHelper.FillWithZeros(CpuMemory, TlsAddress, KTlsPageInfo.TlsEntrySize);
|
this.UserExceptionContextAddress = UserExceptionContextAddress;
|
||||||
|
|
||||||
|
MemoryHelper.FillWithZeros(CpuMemory, UserExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
|
||||||
|
|
||||||
Name = CreationInfo.Name;
|
Name = CreationInfo.Name;
|
||||||
|
|
||||||
|
@ -666,7 +669,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleTable = new KProcessHandleTable(System);
|
HandleTable = new KHandleTable(System);
|
||||||
|
|
||||||
Result = HandleTable.Initialize(Capabilities.HandleTableSize);
|
Result = HandleTable.Initialize(Capabilities.HandleTableSize);
|
||||||
|
|
||||||
|
@ -804,6 +807,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize();
|
return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetMemoryCapacityWithoutPersonalMmHeap()
|
||||||
|
{
|
||||||
|
return GetMemoryCapacity() - GetPersonalMmHeapSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetMemoryUsageWithoutPersonalMmHeap()
|
||||||
|
{
|
||||||
|
return GetMemoryUsage() - GetPersonalMmHeapSize();
|
||||||
|
}
|
||||||
|
|
||||||
private long GetPersonalMmHeapSize()
|
private long GetPersonalMmHeapSize()
|
||||||
{
|
{
|
||||||
return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, MemRegion);
|
return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, MemRegion);
|
||||||
|
@ -835,12 +848,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAllowedCpuCore(int Core)
|
public bool IsCpuCoreAllowed(int Core)
|
||||||
{
|
{
|
||||||
return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0;
|
return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAllowedPriority(int Priority)
|
public bool IsPriorityAllowed(int Priority)
|
||||||
{
|
{
|
||||||
return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0;
|
return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0;
|
||||||
}
|
}
|
||||||
|
@ -856,12 +869,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
bool ShallTerminate = false;
|
bool ShallTerminate = false;
|
||||||
|
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
lock (ProcessLock)
|
lock (ProcessLock)
|
||||||
{
|
{
|
||||||
if (State >= ProcessState.Started)
|
if (State >= ProcessState.Started)
|
||||||
{
|
{
|
||||||
System.CriticalSection.Enter();
|
|
||||||
|
|
||||||
if (State == ProcessState.Started ||
|
if (State == ProcessState.Started ||
|
||||||
State == ProcessState.Crashed ||
|
State == ProcessState.Crashed ||
|
||||||
State == ProcessState.Attached ||
|
State == ProcessState.Attached ||
|
||||||
|
@ -872,8 +885,6 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ShallTerminate = true;
|
ShallTerminate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSection.Leave();
|
|
||||||
|
|
||||||
Result = KernelResult.Success;
|
Result = KernelResult.Success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -882,6 +893,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (ShallTerminate)
|
if (ShallTerminate)
|
||||||
{
|
{
|
||||||
UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
||||||
|
@ -919,6 +932,31 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult ClearIfNotExited()
|
||||||
|
{
|
||||||
|
KernelResult Result;
|
||||||
|
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
lock (ProcessLock)
|
||||||
|
{
|
||||||
|
if (State != ProcessState.Exited && Signaled)
|
||||||
|
{
|
||||||
|
Signaled = false;
|
||||||
|
|
||||||
|
Result = KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = KernelResult.InvalidState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
private void CpuTraceHandler(object sender, EventArgs e)
|
private void CpuTraceHandler(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
System.Scheduler.GetCurrentThread().PrintGuestStackTrace();
|
System.Scheduler.GetCurrentThread().PrintGuestStackTrace();
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
|
|
||||||
//If the candidate was scheduled after the current thread, then it's not worth it.
|
//If the candidate was scheduled after the current thread, then it's not worth it.
|
||||||
if (SelectedThread == null || SelectedThread.LastScheduledTicks >= Thread.LastScheduledTicks)
|
if (SelectedThread == null || SelectedThread.LastScheduledTime >= Thread.LastScheduledTime)
|
||||||
{
|
{
|
||||||
yield return Thread;
|
yield return Thread;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public long ThreadUid { get; private set; }
|
public long ThreadUid { get; private set; }
|
||||||
|
|
||||||
public KSynchronizationObject SignaledObj;
|
public long TotalTimeRunning { get; set; }
|
||||||
|
|
||||||
|
public KSynchronizationObject SignaledObj { get; set; }
|
||||||
|
|
||||||
public long CondVarAddress { get; set; }
|
public long CondVarAddress { get; set; }
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private long TlsAddress;
|
private long TlsAddress;
|
||||||
|
|
||||||
public long LastScheduledTicks { get; set; }
|
public long LastScheduledTime { get; set; }
|
||||||
|
|
||||||
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
|
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
|
||||||
|
|
||||||
|
@ -372,7 +374,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
//If the candidate was scheduled after the current thread, then it's not worth it,
|
//If the candidate was scheduled after the current thread, then it's not worth it,
|
||||||
//unless the priority is higher than the current one.
|
//unless the priority is higher than the current one.
|
||||||
if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks ||
|
if (NextThreadOnCurrentQueue.LastScheduledTime >= Thread.LastScheduledTime ||
|
||||||
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
|
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
|
||||||
{
|
{
|
||||||
yield return Thread;
|
yield return Thread;
|
||||||
|
@ -538,13 +540,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
SyncCancelled = true;
|
SyncCancelled = true;
|
||||||
}
|
}
|
||||||
else if (WithholderNode != null)
|
else if (Withholder != null)
|
||||||
{
|
{
|
||||||
System.Withholders.Remove(WithholderNode);
|
Withholder.Remove(WithholderNode);
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.Running);
|
SetNewSchedFlags(ThreadSchedState.Running);
|
||||||
|
|
||||||
WithholderNode = null;
|
Withholder = null;
|
||||||
|
|
||||||
SyncCancelled = true;
|
SyncCancelled = true;
|
||||||
}
|
}
|
||||||
|
@ -561,7 +563,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
|
public KernelResult SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
|
||||||
{
|
{
|
||||||
System.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
@ -576,7 +578,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +616,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int HighestSetCore(long Mask)
|
private static int HighestSetCore(long Mask)
|
||||||
|
@ -662,13 +664,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
||||||
{
|
{
|
||||||
if (WithholderNode != null)
|
if (Withholder != null)
|
||||||
{
|
{
|
||||||
System.Withholders.Remove(WithholderNode);
|
Withholder.Remove(WithholderNode);
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.Running);
|
SetNewSchedFlags(ThreadSchedState.Running);
|
||||||
|
|
||||||
WithholderNode = null;
|
Withholder = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -25,18 +25,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private AutoResetEvent WaitEvent;
|
private AutoResetEvent WaitEvent;
|
||||||
|
|
||||||
private Stopwatch Counter;
|
|
||||||
|
|
||||||
private bool KeepRunning;
|
private bool KeepRunning;
|
||||||
|
|
||||||
public KTimeManager()
|
public KTimeManager()
|
||||||
{
|
{
|
||||||
WaitingObjects = new List<WaitingObject>();
|
WaitingObjects = new List<WaitingObject>();
|
||||||
|
|
||||||
Counter = new Stopwatch();
|
|
||||||
|
|
||||||
Counter.Start();
|
|
||||||
|
|
||||||
KeepRunning = true;
|
KeepRunning = true;
|
||||||
|
|
||||||
Thread Work = new Thread(WaitAndCheckScheduledObjects);
|
Thread Work = new Thread(WaitAndCheckScheduledObjects);
|
||||||
|
@ -46,31 +40,36 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
|
public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
|
||||||
{
|
{
|
||||||
|
long TimePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
|
||||||
|
|
||||||
lock (WaitingObjects)
|
lock (WaitingObjects)
|
||||||
{
|
{
|
||||||
long TimePoint = Counter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
|
|
||||||
|
|
||||||
WaitingObjects.Add(new WaitingObject(Object, TimePoint));
|
WaitingObjects.Add(new WaitingObject(Object, TimePoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitEvent.Set();
|
WaitEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long ConvertNanosecondsToMilliseconds(long Timeout)
|
public static long ConvertNanosecondsToMilliseconds(long Time)
|
||||||
{
|
{
|
||||||
Timeout /= 1000000;
|
Time /= 1000000;
|
||||||
|
|
||||||
if ((ulong)Timeout > int.MaxValue)
|
if ((ulong)Time > int.MaxValue)
|
||||||
{
|
{
|
||||||
return int.MaxValue;
|
return int.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Timeout;
|
return Time;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long ConvertMillisecondsToNanoseconds(long Timeout)
|
public static long ConvertMillisecondsToNanoseconds(long Time)
|
||||||
{
|
{
|
||||||
return Timeout * 1000000;
|
return Time * 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ConvertMillisecondsToTicks(long Time)
|
||||||
|
{
|
||||||
|
return Time * 19200;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
|
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
|
||||||
|
@ -87,26 +86,31 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
while (KeepRunning)
|
while (KeepRunning)
|
||||||
{
|
{
|
||||||
Monitor.Enter(WaitingObjects);
|
WaitingObject Next;
|
||||||
|
|
||||||
WaitingObject Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
|
lock (WaitingObjects)
|
||||||
|
{
|
||||||
Monitor.Exit(WaitingObjects);
|
Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
if (Next != null)
|
if (Next != null)
|
||||||
{
|
{
|
||||||
long TimePoint = Counter.ElapsedMilliseconds;
|
long TimePoint = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
if (Next.TimePoint > TimePoint)
|
if (Next.TimePoint > TimePoint)
|
||||||
{
|
{
|
||||||
WaitEvent.WaitOne((int)(Next.TimePoint - TimePoint));
|
WaitEvent.WaitOne((int)(Next.TimePoint - TimePoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
Monitor.Enter(WaitingObjects);
|
bool TimeUp = PerformanceCounter.ElapsedMilliseconds >= Next.TimePoint;
|
||||||
|
|
||||||
bool TimeUp = Counter.ElapsedMilliseconds >= Next.TimePoint && WaitingObjects.Remove(Next);
|
if (TimeUp)
|
||||||
|
{
|
||||||
Monitor.Exit(WaitingObjects);
|
lock (WaitingObjects)
|
||||||
|
{
|
||||||
|
TimeUp = WaitingObjects.Remove(Next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (TimeUp)
|
if (TimeUp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,6 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
TimedOut = 0xea01,
|
TimedOut = 0xea01,
|
||||||
Cancelled = 0xec01,
|
Cancelled = 0xec01,
|
||||||
MaximumExceeded = 0xee01,
|
MaximumExceeded = 0xee01,
|
||||||
|
InvalidEnumValue = 0xf001,
|
||||||
InvalidThread = 0xf401,
|
InvalidThread = 0xf401,
|
||||||
InvalidState = 0xfa01,
|
InvalidState = 0xfa01,
|
||||||
ReservedValue = 0xfc01,
|
ReservedValue = 0xfc01,
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{ 0x0c, SvcGetThreadPriority },
|
{ 0x0c, SvcGetThreadPriority },
|
||||||
{ 0x0d, SvcSetThreadPriority },
|
{ 0x0d, SvcSetThreadPriority },
|
||||||
{ 0x0e, SvcGetThreadCoreMask },
|
{ 0x0e, SvcGetThreadCoreMask },
|
||||||
{ 0x0f, SvcSetThreadCoreMask },
|
{ 0x0f, SetThreadCoreMask64 },
|
||||||
{ 0x10, SvcGetCurrentProcessorNumber },
|
{ 0x10, SvcGetCurrentProcessorNumber },
|
||||||
{ 0x11, SignalEvent64 },
|
{ 0x11, SignalEvent64 },
|
||||||
{ 0x12, ClearEvent64 },
|
{ 0x12, ClearEvent64 },
|
||||||
|
@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{ 0x25, SvcGetThreadId },
|
{ 0x25, SvcGetThreadId },
|
||||||
{ 0x26, SvcBreak },
|
{ 0x26, SvcBreak },
|
||||||
{ 0x27, SvcOutputDebugString },
|
{ 0x27, SvcOutputDebugString },
|
||||||
{ 0x29, SvcGetInfo },
|
{ 0x29, GetInfo64 },
|
||||||
{ 0x2c, SvcMapPhysicalMemory },
|
{ 0x2c, SvcMapPhysicalMemory },
|
||||||
{ 0x2d, SvcUnmapPhysicalMemory },
|
{ 0x2d, SvcUnmapPhysicalMemory },
|
||||||
{ 0x32, SvcSetThreadActivity },
|
{ 0x32, SvcSetThreadActivity },
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
@ -120,18 +121,28 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private KernelResult ResetSignal(int Handle)
|
private KernelResult ResetSignal(int Handle)
|
||||||
{
|
{
|
||||||
KReadableEvent ReadableEvent = Process.HandleTable.GetObject<KReadableEvent>(Handle);
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KReadableEvent ReadableEvent = CurrentProcess.HandleTable.GetObject<KReadableEvent>(Handle);
|
||||||
|
|
||||||
KernelResult Result;
|
KernelResult Result;
|
||||||
|
|
||||||
//TODO: KProcess support.
|
|
||||||
if (ReadableEvent != null)
|
if (ReadableEvent != null)
|
||||||
{
|
{
|
||||||
Result = ReadableEvent.ClearIfSignaled();
|
Result = ReadableEvent.ClearIfSignaled();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Result = KernelResult.InvalidHandle;
|
KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
|
||||||
|
|
||||||
|
if (Process != null)
|
||||||
|
{
|
||||||
|
Result = Process.ClearIfNotExited();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Result == KernelResult.InvalidState)
|
if (Result == KernelResult.InvalidState)
|
||||||
|
@ -271,98 +282,240 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetInfo(CpuThreadState ThreadState)
|
private void GetInfo64(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long StackPtr = (long)ThreadState.X0;
|
long StackPtr = (long)ThreadState.X0;
|
||||||
int InfoType = (int)ThreadState.X1;
|
int Id = (int)ThreadState.X1;
|
||||||
long Handle = (long)ThreadState.X2;
|
int Handle = (int)ThreadState.X2;
|
||||||
int InfoId = (int)ThreadState.X3;
|
long SubId = (long)ThreadState.X3;
|
||||||
|
|
||||||
//Fail for info not available on older Kernel versions.
|
KernelResult Result = GetInfo(Id, Handle, SubId, out long Value);
|
||||||
if (InfoType == 18 ||
|
|
||||||
InfoType == 19 ||
|
|
||||||
InfoType == 20 ||
|
|
||||||
InfoType == 21 ||
|
|
||||||
InfoType == 22)
|
|
||||||
{
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
|
|
||||||
|
|
||||||
return;
|
ThreadState.X0 = (ulong)Result;
|
||||||
}
|
ThreadState.X1 = (ulong)Value;
|
||||||
|
}
|
||||||
|
|
||||||
switch (InfoType)
|
private KernelResult GetInfo(int Id, int Handle, long SubId, out long Value)
|
||||||
|
{
|
||||||
|
Value = 0;
|
||||||
|
|
||||||
|
switch (Id)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
ThreadState.X1 = AllowedCpuIdBitmask;
|
case 1:
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionEnd -
|
|
||||||
(ulong)Process.MemoryManager.AliasRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
|
|
||||||
(ulong)Process.MemoryManager.HeapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
ThreadState.X1 = (ulong)Process.GetMemoryCapacity();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
ThreadState.X1 = (ulong)Process.GetMemoryUsage();
|
case 12:
|
||||||
|
case 13:
|
||||||
|
case 14:
|
||||||
|
case 15:
|
||||||
|
case 16:
|
||||||
|
case 17:
|
||||||
|
case 18:
|
||||||
|
case 20:
|
||||||
|
case 21:
|
||||||
|
case 22:
|
||||||
|
{
|
||||||
|
if (SubId != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
|
||||||
|
|
||||||
|
if (Process == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Id)
|
||||||
|
{
|
||||||
|
case 0: Value = Process.Capabilities.AllowedCpuCoresMask; break;
|
||||||
|
case 1: Value = Process.Capabilities.AllowedThreadPriosMask; break;
|
||||||
|
|
||||||
|
case 2: Value = Process.MemoryManager.AliasRegionStart; break;
|
||||||
|
case 3: Value = Process.MemoryManager.AliasRegionEnd -
|
||||||
|
Process.MemoryManager.AliasRegionStart; break;
|
||||||
|
|
||||||
|
case 4: Value = Process.MemoryManager.HeapRegionStart; break;
|
||||||
|
case 5: Value = Process.MemoryManager.HeapRegionEnd -
|
||||||
|
Process.MemoryManager.HeapRegionStart; break;
|
||||||
|
|
||||||
|
case 6: Value = Process.GetMemoryCapacity(); break;
|
||||||
|
|
||||||
|
case 7: Value = Process.GetMemoryUsage(); break;
|
||||||
|
|
||||||
|
case 12: Value = Process.MemoryManager.GetAddrSpaceBaseAddr(); break;
|
||||||
|
|
||||||
|
case 13: Value = Process.MemoryManager.GetAddrSpaceSize(); break;
|
||||||
|
|
||||||
|
case 14: Value = Process.MemoryManager.StackRegionStart; break;
|
||||||
|
case 15: Value = Process.MemoryManager.StackRegionEnd -
|
||||||
|
Process.MemoryManager.StackRegionStart; break;
|
||||||
|
|
||||||
|
case 16: Value = Process.PersonalMmHeapPagesCount * KMemoryManager.PageSize; break;
|
||||||
|
|
||||||
|
case 17:
|
||||||
|
if (Process.PersonalMmHeapPagesCount != 0)
|
||||||
|
{
|
||||||
|
Value = Process.MemoryManager.GetMmUsedPages() * KMemoryManager.PageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 18: Value = Process.TitleId; break;
|
||||||
|
|
||||||
|
case 20: Value = Process.UserExceptionContextAddress; break;
|
||||||
|
|
||||||
|
case 21: Value = Process.GetMemoryCapacityWithoutPersonalMmHeap(); break;
|
||||||
|
|
||||||
|
case 22: Value = Process.GetMemoryUsageWithoutPersonalMmHeap(); break;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 8:
|
case 8:
|
||||||
ThreadState.X1 = EnableProcessDebugging ? 1 : 0;
|
{
|
||||||
|
if (Handle != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SubId != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = System.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 9:
|
||||||
|
{
|
||||||
|
if (Handle != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SubId != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (CurrentProcess.ResourceLimit != null)
|
||||||
|
{
|
||||||
|
KHandleTable HandleTable = CurrentProcess.HandleTable;
|
||||||
|
KResourceLimit ResourceLimit = CurrentProcess.ResourceLimit;
|
||||||
|
|
||||||
|
KernelResult Result = HandleTable.GenerateHandle(ResourceLimit, out int ResLimHandle);
|
||||||
|
|
||||||
|
if (Result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = (uint)ResLimHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
{
|
||||||
|
if (Handle != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CurrentCore = System.Scheduler.GetCurrentThread().CurrentCore;
|
||||||
|
|
||||||
|
if (SubId != -1 && SubId != CurrentCore)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = System.Scheduler.CoreContexts[CurrentCore].TotalIdleTimeTicks;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 11:
|
case 11:
|
||||||
ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
|
{
|
||||||
|
if (Handle != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)SubId > 3)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = System.Scheduler.GetCurrentProcess().RandomEntropy[SubId];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case unchecked((int)0xf0000002u):
|
||||||
|
{
|
||||||
|
if (SubId < -1 || SubId > 3)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
KThread Thread = System.Scheduler.GetCurrentProcess().HandleTable.GetKThread(Handle);
|
||||||
|
|
||||||
|
if (Thread == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
int CurrentCore = CurrentThread.CurrentCore;
|
||||||
|
|
||||||
|
if (SubId != -1 && SubId != CurrentCore)
|
||||||
|
{
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
KCoreContext CoreContext = System.Scheduler.CoreContexts[CurrentCore];
|
||||||
|
|
||||||
|
long TimeDelta = PerformanceCounter.ElapsedMilliseconds - CoreContext.LastContextSwitchTime;
|
||||||
|
|
||||||
|
if (SubId != -1)
|
||||||
|
{
|
||||||
|
Value = KTimeManager.ConvertMillisecondsToTicks(TimeDelta);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long TotalTimeRunning = Thread.TotalTimeRunning;
|
||||||
|
|
||||||
|
if (Thread == CurrentThread)
|
||||||
|
{
|
||||||
|
TotalTimeRunning += TimeDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = KTimeManager.ConvertMillisecondsToTicks(TotalTimeRunning);
|
||||||
|
}
|
||||||
|
|
||||||
case 12:
|
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 13:
|
default: return KernelResult.InvalidEnumValue;
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
|
|
||||||
(ulong)Process.MemoryManager.AddrSpaceStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 14:
|
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.StackRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 15:
|
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.StackRegionEnd -
|
|
||||||
(ulong)Process.MemoryManager.StackRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 16:
|
|
||||||
//ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 17:
|
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
//Process.PrintStackTrace(ThreadState);
|
|
||||||
|
|
||||||
throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateEvent64(CpuThreadState State)
|
private void CreateEvent64(CpuThreadState State)
|
||||||
|
|
|
@ -38,12 +38,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CpuCore = CurrentProcess.DefaultCpuCore;
|
CpuCore = CurrentProcess.DefaultCpuCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsAllowedCpuCore(CpuCore))
|
if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsCpuCoreAllowed(CpuCore))
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCpuCore;
|
return KernelResult.InvalidCpuCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsAllowedPriority(Priority))
|
if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsPriorityAllowed(Priority))
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidPriority;
|
return KernelResult.InvalidPriority;
|
||||||
}
|
}
|
||||||
|
@ -212,46 +212,60 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSetThreadCoreMask(CpuThreadState ThreadState)
|
private void SetThreadCoreMask64(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
int PrefferedCore = (int)ThreadState.X1;
|
int PreferredCore = (int)ThreadState.X1;
|
||||||
long AffinityMask = (long)ThreadState.X2;
|
long AffinityMask = (long)ThreadState.X2;
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
Logger.PrintDebug(LogClass.KernelSvc,
|
||||||
"Handle = 0x" + Handle .ToString("x8") + ", " +
|
"Handle = 0x" + Handle .ToString("x8") + ", " +
|
||||||
"PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
|
"PreferredCore = 0x" + PreferredCore.ToString("x8") + ", " +
|
||||||
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
|
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
|
||||||
|
|
||||||
if (PrefferedCore == -2)
|
KernelResult Result = SetThreadCoreMask(Handle, PreferredCore, AffinityMask);
|
||||||
{
|
|
||||||
//TODO: Get this value from the NPDM file.
|
|
||||||
PrefferedCore = 0;
|
|
||||||
|
|
||||||
AffinityMask = 1 << PrefferedCore;
|
if (Result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SetThreadCoreMask(int Handle, int PreferredCore, long AffinityMask)
|
||||||
|
{
|
||||||
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (PreferredCore == -2)
|
||||||
|
{
|
||||||
|
PreferredCore = CurrentProcess.DefaultCpuCore;
|
||||||
|
|
||||||
|
AffinityMask = 1 << PreferredCore;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TODO: Check allowed cores from NPDM file.
|
if ((CurrentProcess.Capabilities.AllowedCpuCoresMask | AffinityMask) !=
|
||||||
|
CurrentProcess.Capabilities.AllowedCpuCoresMask)
|
||||||
if ((uint)PrefferedCore > 3)
|
|
||||||
{
|
{
|
||||||
if ((PrefferedCore | 2) != -1)
|
return KernelResult.InvalidCpuCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AffinityMask == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint)PreferredCore > 3)
|
||||||
|
{
|
||||||
|
if ((PreferredCore | 2) != -1)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
|
return KernelResult.InvalidCpuCore;
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((AffinityMask & (1 << PrefferedCore)) == 0)
|
else if ((AffinityMask & (1 << PreferredCore)) == 0)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
|
return KernelResult.InvalidCombination;
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,21 +273,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (Thread == null)
|
if (Thread == null)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
return KernelResult.InvalidHandle;
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask);
|
return Thread.SetCoreAndAffinityMask(PreferredCore, AffinityMask);
|
||||||
|
|
||||||
if (Result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadState.X0 = (ulong)Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
|
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
|
||||||
|
|
|
@ -63,13 +63,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
|
|
||||||
class NroInfo
|
class NroInfo
|
||||||
{
|
{
|
||||||
public Nro Executable { get; private set; }
|
public NxRelocatableObject Executable { get; private set; }
|
||||||
public byte[] Hash { get; private set; }
|
public byte[] Hash { get; private set; }
|
||||||
public long NroAddress { get; private set; }
|
public long NroAddress { get; private set; }
|
||||||
public long TotalSize { get; private set; }
|
public long TotalSize { get; private set; }
|
||||||
public long NroMappedAddress { get; set; }
|
public long NroMappedAddress { get; set; }
|
||||||
|
|
||||||
public NroInfo(Nro Executable, byte[] Hash, long TotalSize)
|
public NroInfo(NxRelocatableObject Executable, byte[] Hash, long TotalSize)
|
||||||
{
|
{
|
||||||
this.Executable = Executable;
|
this.Executable = Executable;
|
||||||
this.Hash = Hash;
|
this.Hash = Hash;
|
||||||
|
@ -226,19 +226,19 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
|
|
||||||
Stream.Position = 0;
|
Stream.Position = 0;
|
||||||
|
|
||||||
Nro Executable = new Nro(Stream, "memory", NroHeapAddress, BssHeapAddress);
|
NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroHeapAddress, BssHeapAddress);
|
||||||
|
|
||||||
// check if everything is page align.
|
// check if everything is page align.
|
||||||
if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0
|
if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 ||
|
||||||
|| (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
|
(Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if everything is contiguous.
|
// check if everything is contiguous.
|
||||||
if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length
|
if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length ||
|
||||||
|| Executable.DataOffset != Executable.ROOffset + Executable.RO.Length
|
Executable.DataOffset != Executable.ROOffset + Executable.RO.Length ||
|
||||||
|| NroFileSize != Executable.DataOffset + Executable.Data.Length)
|
NroFileSize != Executable.DataOffset + Executable.Data.Length)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
||||||
}
|
}
|
||||||
|
|
15
Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
Normal file
15
Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
|
{
|
||||||
|
struct ElfDynamic
|
||||||
|
{
|
||||||
|
public ElfDynamicTag Tag { get; private set; }
|
||||||
|
|
||||||
|
public long Value { get; private set; }
|
||||||
|
|
||||||
|
public ElfDynamic(ElfDynamicTag Tag, long Value)
|
||||||
|
{
|
||||||
|
this.Tag = Tag;
|
||||||
|
this.Value = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfDynTag
|
enum ElfDynamicTag
|
||||||
{
|
{
|
||||||
DT_NULL = 0,
|
DT_NULL = 0,
|
||||||
DT_NEEDED = 1,
|
DT_NEEDED = 1,
|
40
Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
Normal file
40
Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
|
{
|
||||||
|
struct ElfSymbol
|
||||||
|
{
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public ElfSymbolType Type { get; private set; }
|
||||||
|
public ElfSymbolBinding Binding { get; private set; }
|
||||||
|
public ElfSymbolVisibility Visibility { get; private set; }
|
||||||
|
|
||||||
|
public bool IsFuncOrObject =>
|
||||||
|
Type == ElfSymbolType.STT_FUNC ||
|
||||||
|
Type == ElfSymbolType.STT_OBJECT;
|
||||||
|
|
||||||
|
public bool IsGlobalOrWeak =>
|
||||||
|
Binding == ElfSymbolBinding.STB_GLOBAL ||
|
||||||
|
Binding == ElfSymbolBinding.STB_WEAK;
|
||||||
|
|
||||||
|
public int SHIdx { get; private set; }
|
||||||
|
public long Value { get; private set; }
|
||||||
|
public long Size { get; private set; }
|
||||||
|
|
||||||
|
public ElfSymbol(
|
||||||
|
string Name,
|
||||||
|
int Info,
|
||||||
|
int Other,
|
||||||
|
int SHIdx,
|
||||||
|
long Value,
|
||||||
|
long Size)
|
||||||
|
{
|
||||||
|
this.Name = Name;
|
||||||
|
this.Type = (ElfSymbolType)(Info & 0xf);
|
||||||
|
this.Binding = (ElfSymbolBinding)(Info >> 4);
|
||||||
|
this.Visibility = (ElfSymbolVisibility)Other;
|
||||||
|
this.SHIdx = SHIdx;
|
||||||
|
this.Value = Value;
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfSymBinding
|
enum ElfSymbolBinding
|
||||||
{
|
{
|
||||||
STB_LOCAL = 0,
|
STB_LOCAL = 0,
|
||||||
STB_GLOBAL = 1,
|
STB_GLOBAL = 1,
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfSymType
|
enum ElfSymbolType
|
||||||
{
|
{
|
||||||
STT_NOTYPE = 0,
|
STT_NOTYPE = 0,
|
||||||
STT_OBJECT = 1,
|
STT_OBJECT = 1,
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfSymVisibility
|
enum ElfSymbolVisibility
|
||||||
{
|
{
|
||||||
STV_DEFAULT = 0,
|
STV_DEFAULT = 0,
|
||||||
STV_INTERNAL = 1,
|
STV_INTERNAL = 1,
|
|
@ -1,15 +0,0 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
|
||||||
{
|
|
||||||
struct ElfDyn
|
|
||||||
{
|
|
||||||
public ElfDynTag Tag { get; private set; }
|
|
||||||
|
|
||||||
public long Value { get; private set; }
|
|
||||||
|
|
||||||
public ElfDyn(ElfDynTag Tag, long Value)
|
|
||||||
{
|
|
||||||
this.Tag = Tag;
|
|
||||||
this.Value = Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
|
||||||
{
|
|
||||||
struct ElfSym
|
|
||||||
{
|
|
||||||
public string Name { get; private set; }
|
|
||||||
|
|
||||||
public ElfSymType Type { get; private set; }
|
|
||||||
public ElfSymBinding Binding { get; private set; }
|
|
||||||
public ElfSymVisibility Visibility { get; private set; }
|
|
||||||
|
|
||||||
public bool IsFuncOrObject =>
|
|
||||||
Type == ElfSymType.STT_FUNC ||
|
|
||||||
Type == ElfSymType.STT_OBJECT;
|
|
||||||
|
|
||||||
public bool IsGlobalOrWeak =>
|
|
||||||
Binding == ElfSymBinding.STB_GLOBAL ||
|
|
||||||
Binding == ElfSymBinding.STB_WEAK;
|
|
||||||
|
|
||||||
public int SHIdx { get; private set; }
|
|
||||||
public long Value { get; private set; }
|
|
||||||
public long Size { get; private set; }
|
|
||||||
|
|
||||||
public ElfSym(
|
|
||||||
string Name,
|
|
||||||
int Info,
|
|
||||||
int Other,
|
|
||||||
int SHIdx,
|
|
||||||
long Value,
|
|
||||||
long Size)
|
|
||||||
{
|
|
||||||
this.Name = Name;
|
|
||||||
this.Type = (ElfSymType)(Info & 0xf);
|
|
||||||
this.Binding = (ElfSymBinding)(Info >> 4);
|
|
||||||
this.Visibility = (ElfSymVisibility)Other;
|
|
||||||
this.SHIdx = SHIdx;
|
|
||||||
this.Value = Value;
|
|
||||||
this.Size = Size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders
|
|
||||||
{
|
|
||||||
class Executable
|
|
||||||
{
|
|
||||||
private MemoryManager Memory;
|
|
||||||
|
|
||||||
private List<ElfDyn> Dynamic;
|
|
||||||
|
|
||||||
public ReadOnlyCollection<ElfSym> SymbolTable;
|
|
||||||
|
|
||||||
public string Name { get; private set; }
|
|
||||||
|
|
||||||
public string FilePath { get; private set; }
|
|
||||||
|
|
||||||
public long ImageBase { get; private set; }
|
|
||||||
public long ImageEnd { get; private set; }
|
|
||||||
|
|
||||||
private KMemoryManager MemoryManager;
|
|
||||||
|
|
||||||
public Executable(IExecutable Exe, KMemoryManager MemoryManager, MemoryManager Memory, long ImageBase)
|
|
||||||
{
|
|
||||||
Dynamic = new List<ElfDyn>();
|
|
||||||
|
|
||||||
long Mod0Offset = ImageBase + Exe.Mod0Offset;
|
|
||||||
|
|
||||||
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
|
|
||||||
long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset;
|
|
||||||
long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset;
|
|
||||||
long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset;
|
|
||||||
long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
|
|
||||||
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
|
|
||||||
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
long TagVal = Memory.ReadInt64(DynamicOffset + 0);
|
|
||||||
long Value = Memory.ReadInt64(DynamicOffset + 8);
|
|
||||||
|
|
||||||
DynamicOffset += 0x10;
|
|
||||||
|
|
||||||
ElfDynTag Tag = (ElfDynTag)TagVal;
|
|
||||||
|
|
||||||
if (Tag == ElfDynTag.DT_NULL)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dynamic.Add(new ElfDyn(Tag, Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB);
|
|
||||||
long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB);
|
|
||||||
|
|
||||||
long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
|
|
||||||
|
|
||||||
List<ElfSym> Symbols = new List<ElfSym>();
|
|
||||||
|
|
||||||
while ((ulong)SymTblAddr < (ulong)StrTblAddr)
|
|
||||||
{
|
|
||||||
ElfSym Sym = GetSymbol(SymTblAddr, StrTblAddr);
|
|
||||||
|
|
||||||
Symbols.Add(Sym);
|
|
||||||
|
|
||||||
SymTblAddr += SymEntSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ElfSym GetSymbol(long Position, long StrTblAddr)
|
|
||||||
{
|
|
||||||
int NameIndex = Memory.ReadInt32(Position + 0);
|
|
||||||
int Info = Memory.ReadByte(Position + 4);
|
|
||||||
int Other = Memory.ReadByte(Position + 5);
|
|
||||||
int SHIdx = Memory.ReadInt16(Position + 6);
|
|
||||||
long Value = Memory.ReadInt64(Position + 8);
|
|
||||||
long Size = Memory.ReadInt64(Position + 16);
|
|
||||||
|
|
||||||
string Name = string.Empty;
|
|
||||||
|
|
||||||
for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
|
|
||||||
{
|
|
||||||
Name += (char)Chr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ElfSym(Name, Info, Other, SHIdx, Value, Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long GetFirstValue(ElfDynTag Tag)
|
|
||||||
{
|
|
||||||
foreach (ElfDyn Entry in Dynamic)
|
|
||||||
{
|
|
||||||
if (Entry.Tag == Tag)
|
|
||||||
{
|
|
||||||
return Entry.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,8 +2,6 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
interface IExecutable
|
interface IExecutable
|
||||||
{
|
{
|
||||||
string FilePath { get; }
|
|
||||||
|
|
||||||
byte[] Text { get; }
|
byte[] Text { get; }
|
||||||
byte[] RO { get; }
|
byte[] RO { get; }
|
||||||
byte[] Data { get; }
|
byte[] Data { get; }
|
||||||
|
|
|
@ -2,10 +2,8 @@ using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nro : IExecutable
|
class NxRelocatableObject : IExecutable
|
||||||
{
|
{
|
||||||
public string FilePath { get; private set; }
|
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
public byte[] Data { get; private set; }
|
public byte[] Data { get; private set; }
|
||||||
|
@ -19,9 +17,8 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
public long SourceAddress { get; private set; }
|
public long SourceAddress { get; private set; }
|
||||||
public long BssAddress { get; private set; }
|
public long BssAddress { get; private set; }
|
||||||
|
|
||||||
public Nro(Stream Input, string FilePath, long SourceAddress = 0, long BssAddress = 0)
|
public NxRelocatableObject(Stream Input, long SourceAddress = 0, long BssAddress = 0)
|
||||||
{
|
{
|
||||||
this.FilePath = FilePath;
|
|
||||||
this.SourceAddress = SourceAddress;
|
this.SourceAddress = SourceAddress;
|
||||||
this.BssAddress = BssAddress;
|
this.BssAddress = BssAddress;
|
||||||
|
|
|
@ -4,10 +4,8 @@ using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nso : IExecutable
|
class NxStaticObject : IExecutable
|
||||||
{
|
{
|
||||||
public string FilePath { get; private set; }
|
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
public byte[] Data { get; private set; }
|
public byte[] Data { get; private set; }
|
||||||
|
@ -29,10 +27,8 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
HasDataHash = 1 << 5
|
HasDataHash = 1 << 5
|
||||||
}
|
}
|
||||||
|
|
||||||
public Nso(Stream Input, string FilePath)
|
public NxStaticObject(Stream Input)
|
||||||
{
|
{
|
||||||
this.FilePath = FilePath;
|
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Input);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
Input.Seek(0, SeekOrigin.Begin);
|
Input.Seek(0, SeekOrigin.Begin);
|
||||||
|
@ -83,7 +79,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
|
||||||
Text = Reader.ReadBytes(TextSize);
|
Text = Reader.ReadBytes(TextSize);
|
||||||
|
|
||||||
if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
|
if (Flags.HasFlag(NsoFlags.IsTextCompressed))
|
||||||
{
|
{
|
||||||
Text = Lz4.Decompress(Text, TextDecSize);
|
Text = Lz4.Decompress(Text, TextDecSize);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +89,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
|
||||||
RO = Reader.ReadBytes(ROSize);
|
RO = Reader.ReadBytes(ROSize);
|
||||||
|
|
||||||
if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
|
if (Flags.HasFlag(NsoFlags.IsROCompressed))
|
||||||
{
|
{
|
||||||
RO = Lz4.Decompress(RO, RODecSize);
|
RO = Lz4.Decompress(RO, RODecSize);
|
||||||
}
|
}
|
||||||
|
@ -103,7 +99,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
|
||||||
Data = Reader.ReadBytes(DataSize);
|
Data = Reader.ReadBytes(DataSize);
|
||||||
|
|
||||||
if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
|
if (Flags.HasFlag(NsoFlags.IsDataCompressed))
|
||||||
{
|
{
|
||||||
Data = Lz4.Decompress(Data, DataDecSize);
|
Data = Lz4.Decompress(Data, DataDecSize);
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue