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.Threading;
|
||||
|
||||
using Nso = Ryujinx.HLE.Loaders.Executables.Nso;
|
||||
using NxStaticObject = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
|
||||
|
||||
namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
|
@ -49,8 +49,6 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
internal KSynchronization Synchronization { get; private set; }
|
||||
|
||||
internal LinkedList<KThread> Withholders { get; private set; }
|
||||
|
||||
internal KContextIdManager ContextIdManager { get; private set; }
|
||||
|
||||
private long KipId;
|
||||
|
@ -105,8 +103,6 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
Synchronization = new KSynchronization(this);
|
||||
|
||||
Withholders = new LinkedList<KThread>();
|
||||
|
||||
ContextIdManager = new KContextIdManager();
|
||||
|
||||
KipId = InitialKipId;
|
||||
|
@ -118,16 +114,16 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.Service];
|
||||
|
||||
long HidPA = Region.Address;
|
||||
long FontPA = Region.Address + HidSize;
|
||||
long HidPa = Region.Address;
|
||||
long FontPa = Region.Address + HidSize;
|
||||
|
||||
HidBaseAddress = HidPA - DramMemoryMap.DramBase;
|
||||
HidBaseAddress = HidPa - DramMemoryMap.DramBase;
|
||||
|
||||
KPageList HidPageList = new KPageList();
|
||||
KPageList FontPageList = new KPageList();
|
||||
|
||||
HidPageList .AddRange(HidPA, HidSize / KMemoryManager.PageSize);
|
||||
FontPageList.AddRange(FontPA, FontSize / KMemoryManager.PageSize);
|
||||
HidPageList .AddRange(HidPa, HidSize / KMemoryManager.PageSize);
|
||||
FontPageList.AddRange(FontPa, FontSize / KMemoryManager.PageSize);
|
||||
|
||||
HidSharedMem = new KSharedMemory(HidPageList, 0, 0, MemoryPermission.Read);
|
||||
FontSharedMem = new KSharedMemory(FontPageList, 0, 0, MemoryPermission.Read);
|
||||
|
@ -136,7 +132,7 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
AppletState.SetFocus(true);
|
||||
|
||||
Font = new SharedFontManager(Device, FontPA - DramMemoryMap.DramBase);
|
||||
Font = new SharedFontManager(Device, FontPa - DramMemoryMap.DramBase);
|
||||
|
||||
VsyncEvent = new KEvent(this);
|
||||
|
||||
|
@ -187,9 +183,7 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||
{
|
||||
string Name = Path.GetFileNameWithoutExtension(File);
|
||||
|
||||
Nso StaticObject = new Nso(Input, Name);
|
||||
NxStaticObject StaticObject = new NxStaticObject(Input);
|
||||
|
||||
StaticObjects.Add(StaticObject);
|
||||
}
|
||||
|
@ -420,9 +414,7 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
|
||||
|
||||
string Name = Path.GetFileNameWithoutExtension(File.Name);
|
||||
|
||||
Nso StaticObject = new Nso(Exefs.OpenFile(File), Name);
|
||||
NxStaticObject StaticObject = new NxStaticObject(Exefs.OpenFile(File));
|
||||
|
||||
StaticObjects.Add(StaticObject);
|
||||
}
|
||||
|
@ -448,6 +440,15 @@ namespace Ryujinx.HLE.HOS
|
|||
return ControlData;
|
||||
}
|
||||
|
||||
if (ControlNca != null)
|
||||
{
|
||||
ReadControlData();
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
|
||||
}
|
||||
|
||||
if (!MetaData.Is64Bits)
|
||||
{
|
||||
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))
|
||||
{
|
||||
IExecutable StaticObject = IsNro
|
||||
? (IExecutable)new Nro(Input, FilePath)
|
||||
: (IExecutable)new Nso(Input, FilePath);
|
||||
? (IExecutable)new NxRelocatableObject(Input)
|
||||
: (IExecutable)new NxStaticObject(Input);
|
||||
|
||||
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()
|
||||
{
|
||||
string KeyFile = null;
|
||||
|
|
|
@ -2,7 +2,7 @@ using ChocolArm64.Memory;
|
|||
using ChocolArm64.State;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
|
||||
using Ryujinx.HLE.Loaders;
|
||||
using Ryujinx.HLE.Loaders.Elf;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -20,9 +20,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
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.Symbols = Symbols;
|
||||
|
@ -81,7 +81,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
while (FramePointer != 0)
|
||||
{
|
||||
if (!Owner.CpuMemory.IsMapped(FramePointer))
|
||||
if ((FramePointer & 7) != 0 ||
|
||||
!Owner.CpuMemory.IsMapped(FramePointer) ||
|
||||
!Owner.CpuMemory.IsMapped(FramePointer + 8))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -109,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
int Middle = Left + (Size >> 1);
|
||||
|
||||
ElfSym Symbol = Image.Symbols[Middle];
|
||||
ElfSymbol Symbol = Image.Symbols[Middle];
|
||||
|
||||
long EndAddr = Symbol.Value + Symbol.Size;
|
||||
|
||||
|
@ -226,7 +228,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
Dictionary<ElfDynTag, long> Dynamic = new Dictionary<ElfDynTag, long>();
|
||||
Dictionary<ElfDynamicTag, long> Dynamic = new Dictionary<ElfDynamicTag, long>();
|
||||
|
||||
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
|
||||
|
||||
|
@ -249,9 +251,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
DynamicOffset += 0x10;
|
||||
|
||||
ElfDynTag Tag = (ElfDynTag)TagVal;
|
||||
ElfDynamicTag Tag = (ElfDynamicTag)TagVal;
|
||||
|
||||
if (Tag == ElfDynTag.DT_NULL)
|
||||
if (Tag == ElfDynamicTag.DT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -259,16 +261,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
Dynamic[Tag] = Value;
|
||||
}
|
||||
|
||||
long StrTblAddr = TextOffset + Dynamic[ElfDynTag.DT_STRTAB];
|
||||
long SymTblAddr = TextOffset + Dynamic[ElfDynTag.DT_SYMTAB];
|
||||
long StrTblAddr = TextOffset + Dynamic[ElfDynamicTag.DT_STRTAB];
|
||||
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)
|
||||
{
|
||||
ElfSym Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
|
||||
ElfSymbol Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
|
||||
|
||||
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 Info = Memory.ReadByte (Address + 4);
|
||||
|
@ -297,7 +299,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
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 long LastContextSwitchTime { get; private set; }
|
||||
|
||||
public long TotalIdleTimeTicks { get; private set; } //TODO
|
||||
|
||||
public KThread CurrentThread { get; private set; }
|
||||
public KThread SelectedThread { get; private set; }
|
||||
|
||||
|
@ -23,11 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
SelectedThread = Thread;
|
||||
|
||||
if (Thread != null)
|
||||
{
|
||||
Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds;
|
||||
}
|
||||
|
||||
if (SelectedThread != CurrentThread)
|
||||
{
|
||||
ContextSwitchNeeded = true;
|
||||
|
@ -38,13 +37,25 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
ContextSwitchNeeded = false;
|
||||
|
||||
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
CurrentThread = SelectedThread;
|
||||
|
||||
if (CurrentThread != null)
|
||||
{
|
||||
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||
CurrentThread.LastScheduledTime = CurrentTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void ContextSwitch()
|
||||
{
|
||||
ContextSwitchNeeded = false;
|
||||
|
||||
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
if (CurrentThread != null)
|
||||
{
|
||||
CoreManager.Reset(CurrentThread.Context.Work);
|
||||
|
@ -54,6 +65,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
if (CurrentThread != null)
|
||||
{
|
||||
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||
CurrentThread.LastScheduledTime = CurrentTime;
|
||||
|
||||
CurrentThread.ClearExclusive();
|
||||
|
||||
CoreManager.Set(CurrentThread.Context.Work);
|
||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Kernel
|
||||
{
|
||||
class KProcessHandleTable
|
||||
class KHandleTable
|
||||
{
|
||||
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
||||
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
||||
|
@ -20,38 +20,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private ushort IdCounter;
|
||||
|
||||
public KProcessHandleTable(Horizon System)
|
||||
public KHandleTable(Horizon 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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
lock (Table)
|
|
@ -12,6 +12,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
public const int PageSize = 0x1000;
|
||||
|
||||
private const int KMemoryBlockSize = 0x40;
|
||||
|
||||
private LinkedList<KMemoryBlock> Blocks;
|
||||
|
||||
private MemoryManager CpuMemory;
|
||||
|
@ -1953,7 +1955,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
throw new ArgumentException($"Invalid state value \"{State}\".");
|
||||
}
|
||||
|
||||
private long GetAddrSpaceBaseAddr()
|
||||
public long GetAddrSpaceBaseAddr()
|
||||
{
|
||||
if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
|
||||
{
|
||||
|
@ -1969,7 +1971,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
private long GetAddrSpaceSize()
|
||||
public long GetAddrSpaceSize()
|
||||
{
|
||||
if (AddrSpaceWidth == 36)
|
||||
{
|
||||
|
@ -2147,6 +2149,19 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
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)
|
||||
{
|
||||
ulong Start = (ulong)Address;
|
||||
|
|
|
@ -28,10 +28,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public int DefaultCpuCore { get; private set; }
|
||||
|
||||
public bool Debug { get; private set; }
|
||||
|
||||
public KResourceLimit ResourceLimit { get; private set; }
|
||||
|
||||
private long PersonalMmHeapBase;
|
||||
private long PersonalMmHeapPagesCount;
|
||||
public long PersonalMmHeapPagesCount { get; private set; }
|
||||
|
||||
private ProcessState State;
|
||||
|
||||
|
@ -40,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public KAddressArbiter AddressArbiter { get; private set; }
|
||||
|
||||
private long[] RandomEntropy;
|
||||
public long[] RandomEntropy { get; private set; }
|
||||
|
||||
private bool Signaled;
|
||||
private bool UseSystemMemBlocks;
|
||||
|
@ -65,9 +66,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
private long MemoryUsageCapacity;
|
||||
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;
|
||||
|
||||
|
@ -345,14 +346,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
KernelResult Result = AllocateThreadLocalStorage(out TlsAddress);
|
||||
KernelResult Result = AllocateThreadLocalStorage(out long UserExceptionContextAddress);
|
||||
|
||||
if (Result != KernelResult.Success)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
MemoryHelper.FillWithZeros(CpuMemory, TlsAddress, KTlsPageInfo.TlsEntrySize);
|
||||
this.UserExceptionContextAddress = UserExceptionContextAddress;
|
||||
|
||||
MemoryHelper.FillWithZeros(CpuMemory, UserExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
|
||||
|
||||
Name = CreationInfo.Name;
|
||||
|
||||
|
@ -666,7 +669,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return Result;
|
||||
}
|
||||
|
||||
HandleTable = new KProcessHandleTable(System);
|
||||
HandleTable = new KHandleTable(System);
|
||||
|
||||
Result = HandleTable.Initialize(Capabilities.HandleTableSize);
|
||||
|
||||
|
@ -804,6 +807,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize();
|
||||
}
|
||||
|
||||
public long GetMemoryCapacityWithoutPersonalMmHeap()
|
||||
{
|
||||
return GetMemoryCapacity() - GetPersonalMmHeapSize();
|
||||
}
|
||||
|
||||
public long GetMemoryUsageWithoutPersonalMmHeap()
|
||||
{
|
||||
return GetMemoryUsage() - GetPersonalMmHeapSize();
|
||||
}
|
||||
|
||||
private long GetPersonalMmHeapSize()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public bool IsAllowedPriority(int Priority)
|
||||
public bool IsPriorityAllowed(int Priority)
|
||||
{
|
||||
return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0;
|
||||
}
|
||||
|
@ -856,12 +869,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
bool ShallTerminate = false;
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
lock (ProcessLock)
|
||||
{
|
||||
if (State >= ProcessState.Started)
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (State == ProcessState.Started ||
|
||||
State == ProcessState.Crashed ||
|
||||
State == ProcessState.Attached ||
|
||||
|
@ -872,8 +885,6 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
ShallTerminate = true;
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
Result = KernelResult.Success;
|
||||
}
|
||||
else
|
||||
|
@ -882,6 +893,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
}
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
if (ShallTerminate)
|
||||
{
|
||||
UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
||||
|
@ -919,6 +932,31 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
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)
|
||||
{
|
||||
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 (SelectedThread == null || SelectedThread.LastScheduledTicks >= Thread.LastScheduledTicks)
|
||||
if (SelectedThread == null || SelectedThread.LastScheduledTime >= Thread.LastScheduledTime)
|
||||
{
|
||||
yield return Thread;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public long ThreadUid { get; private set; }
|
||||
|
||||
public KSynchronizationObject SignaledObj;
|
||||
public long TotalTimeRunning { get; set; }
|
||||
|
||||
public KSynchronizationObject SignaledObj { get; set; }
|
||||
|
||||
public long CondVarAddress { get; set; }
|
||||
|
||||
|
@ -28,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private long TlsAddress;
|
||||
|
||||
public long LastScheduledTicks { get; set; }
|
||||
public long LastScheduledTime { get; 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,
|
||||
//unless the priority is higher than the current one.
|
||||
if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks ||
|
||||
if (NextThreadOnCurrentQueue.LastScheduledTime >= Thread.LastScheduledTime ||
|
||||
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
|
||||
{
|
||||
yield return Thread;
|
||||
|
@ -538,13 +540,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
SyncCancelled = true;
|
||||
}
|
||||
else if (WithholderNode != null)
|
||||
else if (Withholder != null)
|
||||
{
|
||||
System.Withholders.Remove(WithholderNode);
|
||||
Withholder.Remove(WithholderNode);
|
||||
|
||||
SetNewSchedFlags(ThreadSchedState.Running);
|
||||
|
||||
WithholderNode = null;
|
||||
Withholder = null;
|
||||
|
||||
SyncCancelled = true;
|
||||
}
|
||||
|
@ -561,7 +563,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
|
||||
public KernelResult SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
|
@ -576,7 +578,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,7 +616,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return 0;
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private static int HighestSetCore(long Mask)
|
||||
|
@ -662,13 +664,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
||||
{
|
||||
if (WithholderNode != null)
|
||||
if (Withholder != null)
|
||||
{
|
||||
System.Withholders.Remove(WithholderNode);
|
||||
Withholder.Remove(WithholderNode);
|
||||
|
||||
SetNewSchedFlags(ThreadSchedState.Running);
|
||||
|
||||
WithholderNode = null;
|
||||
Withholder = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -25,18 +25,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
private AutoResetEvent WaitEvent;
|
||||
|
||||
private Stopwatch Counter;
|
||||
|
||||
private bool KeepRunning;
|
||||
|
||||
public KTimeManager()
|
||||
{
|
||||
WaitingObjects = new List<WaitingObject>();
|
||||
|
||||
Counter = new Stopwatch();
|
||||
|
||||
Counter.Start();
|
||||
|
||||
KeepRunning = true;
|
||||
|
||||
Thread Work = new Thread(WaitAndCheckScheduledObjects);
|
||||
|
@ -46,31 +40,36 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
|
||||
{
|
||||
long TimePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
|
||||
|
||||
lock (WaitingObjects)
|
||||
{
|
||||
long TimePoint = Counter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
|
||||
|
||||
WaitingObjects.Add(new WaitingObject(Object, TimePoint));
|
||||
}
|
||||
|
||||
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 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)
|
||||
|
@ -87,26 +86,31 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
while (KeepRunning)
|
||||
{
|
||||
Monitor.Enter(WaitingObjects);
|
||||
WaitingObject Next;
|
||||
|
||||
WaitingObject Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
|
||||
|
||||
Monitor.Exit(WaitingObjects);
|
||||
lock (WaitingObjects)
|
||||
{
|
||||
Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
|
||||
}
|
||||
|
||||
if (Next != null)
|
||||
{
|
||||
long TimePoint = Counter.ElapsedMilliseconds;
|
||||
long TimePoint = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
if (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);
|
||||
|
||||
Monitor.Exit(WaitingObjects);
|
||||
if (TimeUp)
|
||||
{
|
||||
lock (WaitingObjects)
|
||||
{
|
||||
TimeUp = WaitingObjects.Remove(Next);
|
||||
}
|
||||
}
|
||||
|
||||
if (TimeUp)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
TimedOut = 0xea01,
|
||||
Cancelled = 0xec01,
|
||||
MaximumExceeded = 0xee01,
|
||||
InvalidEnumValue = 0xf001,
|
||||
InvalidThread = 0xf401,
|
||||
InvalidState = 0xfa01,
|
||||
ReservedValue = 0xfc01,
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{ 0x0c, SvcGetThreadPriority },
|
||||
{ 0x0d, SvcSetThreadPriority },
|
||||
{ 0x0e, SvcGetThreadCoreMask },
|
||||
{ 0x0f, SvcSetThreadCoreMask },
|
||||
{ 0x0f, SetThreadCoreMask64 },
|
||||
{ 0x10, SvcGetCurrentProcessorNumber },
|
||||
{ 0x11, SignalEvent64 },
|
||||
{ 0x12, ClearEvent64 },
|
||||
|
@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{ 0x25, SvcGetThreadId },
|
||||
{ 0x26, SvcBreak },
|
||||
{ 0x27, SvcOutputDebugString },
|
||||
{ 0x29, SvcGetInfo },
|
||||
{ 0x29, GetInfo64 },
|
||||
{ 0x2c, SvcMapPhysicalMemory },
|
||||
{ 0x2d, SvcUnmapPhysicalMemory },
|
||||
{ 0x32, SvcSetThreadActivity },
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
|
@ -120,18 +121,28 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
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;
|
||||
|
||||
//TODO: KProcess support.
|
||||
if (ReadableEvent != null)
|
||||
{
|
||||
Result = ReadableEvent.ClearIfSignaled();
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = KernelResult.InvalidHandle;
|
||||
KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
|
||||
|
||||
if (Process != null)
|
||||
{
|
||||
Result = Process.ClearIfNotExited();
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = KernelResult.InvalidHandle;
|
||||
}
|
||||
}
|
||||
|
||||
if (Result == KernelResult.InvalidState)
|
||||
|
@ -271,98 +282,240 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcGetInfo(CpuThreadState ThreadState)
|
||||
private void GetInfo64(CpuThreadState ThreadState)
|
||||
{
|
||||
long StackPtr = (long)ThreadState.X0;
|
||||
int InfoType = (int)ThreadState.X1;
|
||||
long Handle = (long)ThreadState.X2;
|
||||
int InfoId = (int)ThreadState.X3;
|
||||
int Id = (int)ThreadState.X1;
|
||||
int Handle = (int)ThreadState.X2;
|
||||
long SubId = (long)ThreadState.X3;
|
||||
|
||||
//Fail for info not available on older Kernel versions.
|
||||
if (InfoType == 18 ||
|
||||
InfoType == 19 ||
|
||||
InfoType == 20 ||
|
||||
InfoType == 21 ||
|
||||
InfoType == 22)
|
||||
{
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
|
||||
KernelResult Result = GetInfo(Id, Handle, SubId, out long Value);
|
||||
|
||||
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:
|
||||
ThreadState.X1 = AllowedCpuIdBitmask;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionStart;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionEnd -
|
||||
(ulong)Process.MemoryManager.AliasRegionStart;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
|
||||
(ulong)Process.MemoryManager.HeapRegionStart;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
ThreadState.X1 = (ulong)Process.GetMemoryCapacity();
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
case 13:
|
||||
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}");
|
||||
default: return KernelResult.InvalidEnumValue;
|
||||
}
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private void CreateEvent64(CpuThreadState State)
|
||||
|
|
|
@ -38,12 +38,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
CpuCore = CurrentProcess.DefaultCpuCore;
|
||||
}
|
||||
|
||||
if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsAllowedCpuCore(CpuCore))
|
||||
if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsCpuCoreAllowed(CpuCore))
|
||||
{
|
||||
return KernelResult.InvalidCpuCore;
|
||||
}
|
||||
|
||||
if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsAllowedPriority(Priority))
|
||||
if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsPriorityAllowed(Priority))
|
||||
{
|
||||
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 PrefferedCore = (int)ThreadState.X1;
|
||||
int PreferredCore = (int)ThreadState.X1;
|
||||
long AffinityMask = (long)ThreadState.X2;
|
||||
|
||||
Logger.PrintDebug(LogClass.KernelSvc,
|
||||
"Handle = 0x" + Handle .ToString("x8") + ", " +
|
||||
"PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
|
||||
"PreferredCore = 0x" + PreferredCore.ToString("x8") + ", " +
|
||||
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
|
||||
|
||||
if (PrefferedCore == -2)
|
||||
{
|
||||
//TODO: Get this value from the NPDM file.
|
||||
PrefferedCore = 0;
|
||||
KernelResult Result = SetThreadCoreMask(Handle, PreferredCore, AffinityMask);
|
||||
|
||||
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
|
||||
{
|
||||
//TODO: Check allowed cores from NPDM file.
|
||||
|
||||
if ((uint)PrefferedCore > 3)
|
||||
if ((CurrentProcess.Capabilities.AllowedCpuCoresMask | AffinityMask) !=
|
||||
CurrentProcess.Capabilities.AllowedCpuCoresMask)
|
||||
{
|
||||
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}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
|
||||
|
||||
return;
|
||||
return KernelResult.InvalidCpuCore;
|
||||
}
|
||||
}
|
||||
else if ((AffinityMask & (1 << PrefferedCore)) == 0)
|
||||
else if ((AffinityMask & (1 << PreferredCore)) == 0)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
||||
|
||||
return;
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,21 +273,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
if (Thread == null)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
|
||||
long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||
}
|
||||
|
||||
ThreadState.X0 = (ulong)Result;
|
||||
return Thread.SetCoreAndAffinityMask(PreferredCore, AffinityMask);
|
||||
}
|
||||
|
||||
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
|
||||
|
|
|
@ -63,13 +63,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
|||
|
||||
class NroInfo
|
||||
{
|
||||
public Nro Executable { get; private set; }
|
||||
public NxRelocatableObject Executable { get; private set; }
|
||||
public byte[] Hash { get; private set; }
|
||||
public long NroAddress { get; private set; }
|
||||
public long TotalSize { get; private 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.Hash = Hash;
|
||||
|
@ -226,19 +226,19 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
|||
|
||||
Stream.Position = 0;
|
||||
|
||||
Nro Executable = new Nro(Stream, "memory", NroHeapAddress, BssHeapAddress);
|
||||
NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroHeapAddress, BssHeapAddress);
|
||||
|
||||
// check if everything is page align.
|
||||
if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0
|
||||
|| (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
|
||||
if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 ||
|
||||
(Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
|
||||
{
|
||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
||||
}
|
||||
|
||||
// check if everything is contiguous.
|
||||
if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length
|
||||
|| Executable.DataOffset != Executable.ROOffset + Executable.RO.Length
|
||||
|| NroFileSize != Executable.DataOffset + Executable.Data.Length)
|
||||
if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length ||
|
||||
Executable.DataOffset != Executable.ROOffset + Executable.RO.Length ||
|
||||
NroFileSize != Executable.DataOffset + Executable.Data.Length)
|
||||
{
|
||||
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_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_GLOBAL = 1,
|
|
@ -1,6 +1,6 @@
|
|||
namespace Ryujinx.HLE.Loaders
|
||||
namespace Ryujinx.HLE.Loaders.Elf
|
||||
{
|
||||
enum ElfSymType
|
||||
enum ElfSymbolType
|
||||
{
|
||||
STT_NOTYPE = 0,
|
||||
STT_OBJECT = 1,
|
|
@ -1,6 +1,6 @@
|
|||
namespace Ryujinx.HLE.Loaders
|
||||
namespace Ryujinx.HLE.Loaders.Elf
|
||||
{
|
||||
enum ElfSymVisibility
|
||||
enum ElfSymbolVisibility
|
||||
{
|
||||
STV_DEFAULT = 0,
|
||||
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
|
||||
{
|
||||
string FilePath { get; }
|
||||
|
||||
byte[] Text { get; }
|
||||
byte[] RO { get; }
|
||||
byte[] Data { get; }
|
||||
|
|
|
@ -2,10 +2,8 @@ using System.IO;
|
|||
|
||||
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[] RO { 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 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.BssAddress = BssAddress;
|
||||
|
|
@ -4,10 +4,8 @@ using System.IO;
|
|||
|
||||
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[] RO { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
|
@ -29,10 +27,8 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||
HasDataHash = 1 << 5
|
||||
}
|
||||
|
||||
public Nso(Stream Input, string FilePath)
|
||||
public NxStaticObject(Stream Input)
|
||||
{
|
||||
this.FilePath = FilePath;
|
||||
|
||||
BinaryReader Reader = new BinaryReader(Input);
|
||||
|
||||
Input.Seek(0, SeekOrigin.Begin);
|
||||
|
@ -83,7 +79,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||
|
||||
Text = Reader.ReadBytes(TextSize);
|
||||
|
||||
if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
|
||||
if (Flags.HasFlag(NsoFlags.IsTextCompressed))
|
||||
{
|
||||
Text = Lz4.Decompress(Text, TextDecSize);
|
||||
}
|
||||
|
@ -93,7 +89,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||
|
||||
RO = Reader.ReadBytes(ROSize);
|
||||
|
||||
if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
|
||||
if (Flags.HasFlag(NsoFlags.IsROCompressed))
|
||||
{
|
||||
RO = Lz4.Decompress(RO, RODecSize);
|
||||
}
|
||||
|
@ -103,7 +99,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||
|
||||
Data = Reader.ReadBytes(DataSize);
|
||||
|
||||
if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
|
||||
if (Flags.HasFlag(NsoFlags.IsDataCompressed))
|
||||
{
|
||||
Data = Lz4.Decompress(Data, DataDecSize);
|
||||
}
|
Loading…
Add table
Reference in a new issue