Move solution and projects to src

This commit is contained in:
TSR Berry 2023-04-08 01:22:00 +02:00 committed by Mary
parent e210a51de6
commit ea8eaea6c2
3466 changed files with 55 additions and 55 deletions

View file

@ -0,0 +1,22 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
static class CapabilityExtensions
{
public static CapabilityType GetCapabilityType(this uint cap)
{
return (CapabilityType)(((cap + 1) & ~cap) - 1);
}
public static uint GetFlag(this CapabilityType type)
{
return (uint)type + 1;
}
public static uint GetId(this CapabilityType type)
{
return (uint)BitOperations.TrailingZeroCount(type.GetFlag());
}
}
}

View file

@ -0,0 +1,19 @@
namespace Ryujinx.HLE.HOS.Kernel.Process
{
enum CapabilityType : uint
{
CorePriority = (1u << 3) - 1,
SyscallMask = (1u << 4) - 1,
MapRange = (1u << 6) - 1,
MapIoPage = (1u << 7) - 1,
MapRegion = (1u << 10) - 1,
InterruptPair = (1u << 11) - 1,
ProgramType = (1u << 13) - 1,
KernelVersion = (1u << 14) - 1,
HandleTable = (1u << 15) - 1,
DebugFlags = (1u << 16) - 1,
Invalid = 0u,
Padding = ~0u
}
}

View file

@ -0,0 +1,465 @@
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.Loaders.Elf;
using Ryujinx.Memory;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class HleProcessDebugger
{
private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24;
private KProcess _owner;
private class Image
{
public ulong BaseAddress { get; }
public ulong Size { get; }
public ulong EndAddress => BaseAddress + Size;
public ElfSymbol[] Symbols { get; }
public Image(ulong baseAddress, ulong size, ElfSymbol[] symbols)
{
BaseAddress = baseAddress;
Size = size;
Symbols = symbols;
}
}
private List<Image> _images;
private int _loaded;
public HleProcessDebugger(KProcess owner)
{
_owner = owner;
_images = new List<Image>();
}
public string GetGuestStackTrace(KThread thread)
{
EnsureLoaded();
var context = thread.Context;
StringBuilder trace = new StringBuilder();
trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}");
void AppendTrace(ulong address)
{
if (AnalyzePointer(out PointerInfo info, address, thread))
{
trace.AppendLine($" 0x{address:x16}\t{info.ImageDisplay}\t{info.SubDisplay}");
}
else
{
trace.AppendLine($" 0x{address:x16}");
}
}
if (context.IsAarch32)
{
ulong framePointer = context.GetX(11);
while (framePointer != 0)
{
if ((framePointer & 3) != 0 ||
!_owner.CpuMemory.IsMapped(framePointer) ||
!_owner.CpuMemory.IsMapped(framePointer + 4))
{
break;
}
AppendTrace(_owner.CpuMemory.Read<uint>(framePointer + 4));
framePointer = _owner.CpuMemory.Read<uint>(framePointer);
}
}
else
{
ulong framePointer = context.GetX(29);
while (framePointer != 0)
{
if ((framePointer & 7) != 0 ||
!_owner.CpuMemory.IsMapped(framePointer) ||
!_owner.CpuMemory.IsMapped(framePointer + 8))
{
break;
}
AppendTrace(_owner.CpuMemory.Read<ulong>(framePointer + 8));
framePointer = _owner.CpuMemory.Read<ulong>(framePointer);
}
}
return trace.ToString();
}
public string GetCpuRegisterPrintout(KThread thread)
{
EnsureLoaded();
var context = thread.Context;
StringBuilder sb = new StringBuilder();
string GetReg(int x)
{
var v = x == 32 ? context.Pc : context.GetX(x);
if (!AnalyzePointer(out PointerInfo info, v, thread))
{
return $"0x{v:x16}";
}
else
{
if (!string.IsNullOrEmpty(info.ImageName))
{
return $"0x{v:x16} ({info.ImageDisplay})\t=> {info.SubDisplay}";
}
else
{
return $"0x{v:x16} ({info.SpDisplay})";
}
}
}
for (int i = 0; i <= 28; i++)
{
sb.AppendLine($"\tX[{i:d2}]:\t{GetReg(i)}");
}
sb.AppendLine($"\tFP:\t{GetReg(29)}");
sb.AppendLine($"\tLR:\t{GetReg(30)}");
sb.AppendLine($"\tSP:\t{GetReg(31)}");
sb.AppendLine($"\tPC:\t{GetReg(32)}");
return sb.ToString();
}
private bool TryGetSubName(Image image, ulong address, out ElfSymbol symbol)
{
address -= image.BaseAddress;
int left = 0;
int right = image.Symbols.Length - 1;
while (left <= right)
{
int size = right - left;
int middle = left + (size >> 1);
symbol = image.Symbols[middle];
ulong endAddr = symbol.Value + symbol.Size;
if (address >= symbol.Value && address < endAddr)
{
return true;
}
if (address < symbol.Value)
{
right = middle - 1;
}
else
{
left = middle + 1;
}
}
symbol = default;
return false;
}
struct PointerInfo
{
public string ImageName;
public string SubName;
public ulong Offset;
public ulong SubOffset;
public string ImageDisplay => $"{ImageName}:0x{Offset:x4}";
public string SubDisplay => SubOffset == 0 ? SubName : $"{SubName}:0x{SubOffset:x4}";
public string SpDisplay => SubOffset == 0 ? "SP" : $"SP:-0x{SubOffset:x4}";
}
private bool AnalyzePointer(out PointerInfo info, ulong address, KThread thread)
{
if (AnalyzePointerFromImages(out info, address))
{
return true;
}
if (AnalyzePointerFromStack(out info, address, thread))
{
return true;
}
return false;
}
private bool AnalyzePointerFromImages(out PointerInfo info, ulong address)
{
info = default;
Image image = GetImage(address, out int imageIndex);
if (image == null)
{
// Value isn't a pointer to a known image...
return false;
}
info.Offset = address - image.BaseAddress;
// Try to find what this pointer is referring to
if (TryGetSubName(image, address, out ElfSymbol symbol))
{
info.SubName = symbol.Name;
// Demangle string if possible
if (info.SubName.StartsWith("_Z"))
{
info.SubName = Demangler.Parse(info.SubName);
}
info.SubOffset = info.Offset - symbol.Value;
}
else
{
info.SubName = "";
}
info.ImageName = GetGuessedNsoNameFromIndex(imageIndex);
return true;
}
private bool AnalyzePointerFromStack(out PointerInfo info, ulong address, KThread thread)
{
info = default;
ulong sp = thread.Context.GetX(31);
var memoryInfo = _owner.MemoryManager.QueryMemory(address);
MemoryState memoryState = memoryInfo.State;
if (!memoryState.HasFlag(MemoryState.Stack)) // Is this pointer within the stack?
{
return false;
}
info.SubOffset = address - sp;
return true;
}
private Image GetImage(ulong address, out int index)
{
lock (_images)
{
for (index = _images.Count - 1; index >= 0; index--)
{
if (address >= _images[index].BaseAddress && address < _images[index].EndAddress)
{
return _images[index];
}
}
}
return null;
}
private string GetGuessedNsoNameFromIndex(int index)
{
if ((uint)index > 11)
{
return "???";
}
if (index == 0)
{
return "rtld";
}
else if (index == 1)
{
return "main";
}
else if (index == GetImagesCount() - 1)
{
return "sdk";
}
else
{
return "subsdk" + (index - 2);
}
}
private int GetImagesCount()
{
lock (_images)
{
return _images.Count;
}
}
private void EnsureLoaded()
{
if (Interlocked.CompareExchange(ref _loaded, 1, 0) == 0)
{
ScanMemoryForTextSegments();
}
}
private void ScanMemoryForTextSegments()
{
ulong oldAddress = 0;
ulong address = 0;
while (address >= oldAddress)
{
KMemoryInfo info = _owner.MemoryManager.QueryMemory(address);
if (info.State == MemoryState.Reserved)
{
break;
}
if (info.State == MemoryState.CodeStatic && info.Permission == KMemoryPermission.ReadAndExecute)
{
LoadMod0Symbols(_owner.CpuMemory, info.Address, info.Size);
}
oldAddress = address;
address = info.Address + info.Size;
}
}
private void LoadMod0Symbols(IVirtualMemoryManager memory, ulong textOffset, ulong textSize)
{
ulong mod0Offset = textOffset + memory.Read<uint>(textOffset + 4);
if (mod0Offset < textOffset || !memory.IsMapped(mod0Offset) || (mod0Offset & 3) != 0)
{
return;
}
Dictionary<ElfDynamicTag, ulong> dynamic = new Dictionary<ElfDynamicTag, ulong>();
int mod0Magic = memory.Read<int>(mod0Offset + 0x0);
if (mod0Magic != Mod0)
{
return;
}
ulong dynamicOffset = memory.Read<uint>(mod0Offset + 0x4) + mod0Offset;
ulong bssStartOffset = memory.Read<uint>(mod0Offset + 0x8) + mod0Offset;
ulong bssEndOffset = memory.Read<uint>(mod0Offset + 0xc) + mod0Offset;
ulong ehHdrStartOffset = memory.Read<uint>(mod0Offset + 0x10) + mod0Offset;
ulong ehHdrEndOffset = memory.Read<uint>(mod0Offset + 0x14) + mod0Offset;
ulong modObjOffset = memory.Read<uint>(mod0Offset + 0x18) + mod0Offset;
bool isAArch32 = memory.Read<ulong>(dynamicOffset) > 0xFFFFFFFF || memory.Read<ulong>(dynamicOffset + 0x10) > 0xFFFFFFFF;
while (true)
{
ulong tagVal;
ulong value;
if (isAArch32)
{
tagVal = memory.Read<uint>(dynamicOffset + 0);
value = memory.Read<uint>(dynamicOffset + 4);
dynamicOffset += 0x8;
}
else
{
tagVal = memory.Read<ulong>(dynamicOffset + 0);
value = memory.Read<ulong>(dynamicOffset + 8);
dynamicOffset += 0x10;
}
ElfDynamicTag tag = (ElfDynamicTag)tagVal;
if (tag == ElfDynamicTag.DT_NULL)
{
break;
}
dynamic[tag] = value;
}
if (!dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out ulong strTab) ||
!dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out ulong symTab) ||
!dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out ulong symEntSize))
{
return;
}
ulong strTblAddr = textOffset + strTab;
ulong symTblAddr = textOffset + symTab;
List<ElfSymbol> symbols = new List<ElfSymbol>();
while (symTblAddr < strTblAddr)
{
ElfSymbol sym = isAArch32 ? GetSymbol32(memory, symTblAddr, strTblAddr) : GetSymbol64(memory, symTblAddr, strTblAddr);
symbols.Add(sym);
symTblAddr += symEntSize;
}
lock (_images)
{
_images.Add(new Image(textOffset, textSize, symbols.OrderBy(x => x.Value).ToArray()));
}
}
private ElfSymbol GetSymbol64(IVirtualMemoryManager memory, ulong address, ulong strTblAddr)
{
ElfSymbol64 sym = memory.Read<ElfSymbol64>(address);
uint nameIndex = sym.NameOffset;
string name = string.Empty;
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
{
name += (char)chr;
}
return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
}
private ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr)
{
ElfSymbol32 sym = memory.Read<ElfSymbol32>(address);
uint nameIndex = sym.NameOffset;
string name = string.Empty;
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
{
name += (char)chr;
}
return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
}
}
}

View file

@ -0,0 +1,15 @@
using Ryujinx.Cpu;
using Ryujinx.Memory;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
interface IProcessContext : IDisposable
{
IVirtualMemoryManager AddressSpace { get; }
IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks);
void Execute(IExecutionContext context, ulong codeAddress);
void InvalidateCacheRegion(ulong address, ulong size);
}
}

View file

@ -0,0 +1,9 @@
using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
interface IProcessContextFactory
{
IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit);
}
}

View file

@ -0,0 +1,83 @@
using System;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KContextIdManager
{
private const int IdMasksCount = 8;
private int[] _idMasks;
private int _nextFreeBitHint;
public KContextIdManager()
{
_idMasks = new int[IdMasksCount];
}
public int GetId()
{
lock (_idMasks)
{
int id = 0;
if (!TestBit(_nextFreeBitHint))
{
id = _nextFreeBitHint;
}
else
{
for (int index = 0; index < IdMasksCount; index++)
{
int mask = _idMasks[index];
int firstFreeBit = BitOperations.LeadingZeroCount((uint)((mask + 1) & ~mask));
if (firstFreeBit < 32)
{
int baseBit = index * 32 + 31;
id = baseBit - firstFreeBit;
break;
}
else if (index == IdMasksCount - 1)
{
throw new InvalidOperationException("Maximum number of Ids reached!");
}
}
}
_nextFreeBitHint = id + 1;
SetBit(id);
return id;
}
}
public void PutId(int id)
{
lock (_idMasks)
{
ClearBit(id);
}
}
private bool TestBit(int bit)
{
return (_idMasks[_nextFreeBitHint / 32] & (1 << (_nextFreeBitHint & 31))) != 0;
}
private void SetBit(int bit)
{
_idMasks[_nextFreeBitHint / 32] |= (1 << (_nextFreeBitHint & 31));
}
private void ClearBit(int bit)
{
_idMasks[_nextFreeBitHint / 32] &= ~(1 << (_nextFreeBitHint & 31));
}
}
}

View file

@ -0,0 +1,19 @@
using Ryujinx.HLE.HOS.Kernel.Common;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KHandleEntry
{
public KHandleEntry Next { get; set; }
public int Index { get; private set; }
public ushort HandleId { get; set; }
public KAutoObject Obj { get; set; }
public KHandleEntry(int index)
{
Index = index;
}
}
}

View file

@ -0,0 +1,285 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KHandleTable
{
public const int SelfThreadHandle = (0x1ffff << 15) | 0;
public const int SelfProcessHandle = (0x1ffff << 15) | 1;
private readonly KernelContext _context;
private KHandleEntry[] _table;
private KHandleEntry _tableHead;
private KHandleEntry _nextFreeEntry;
private int _activeSlotsCount;
private uint _size;
private ushort _idCounter;
public KHandleTable(KernelContext context)
{
_context = context;
}
public Result Initialize(uint size)
{
if (size > 1024)
{
return KernelResult.OutOfMemory;
}
if (size < 1)
{
size = 1024;
}
_size = size;
_idCounter = 1;
_table = new KHandleEntry[size];
_tableHead = new KHandleEntry(0);
KHandleEntry entry = _tableHead;
for (int index = 0; index < size; index++)
{
_table[index] = entry;
entry.Next = new KHandleEntry(index + 1);
entry = entry.Next;
}
_table[size - 1].Next = null;
_nextFreeEntry = _tableHead;
return Result.Success;
}
public Result GenerateHandle(KAutoObject obj, out int handle)
{
handle = 0;
lock (_table)
{
if (_activeSlotsCount >= _size)
{
return KernelResult.HandleTableFull;
}
KHandleEntry entry = _nextFreeEntry;
_nextFreeEntry = entry.Next;
entry.Obj = obj;
entry.HandleId = _idCounter;
_activeSlotsCount++;
handle = (_idCounter << 15) | entry.Index;
obj.IncrementReferenceCount();
if ((short)(_idCounter + 1) >= 0)
{
_idCounter++;
}
else
{
_idCounter = 1;
}
}
return Result.Success;
}
public Result ReserveHandle(out int handle)
{
handle = 0;
lock (_table)
{
if (_activeSlotsCount >= _size)
{
return KernelResult.HandleTableFull;
}
KHandleEntry entry = _nextFreeEntry;
_nextFreeEntry = entry.Next;
_activeSlotsCount++;
handle = (_idCounter << 15) | entry.Index;
if ((short)(_idCounter + 1) >= 0)
{
_idCounter++;
}
else
{
_idCounter = 1;
}
}
return Result.Success;
}
public void CancelHandleReservation(int handle)
{
int index = (handle >> 0) & 0x7fff;
lock (_table)
{
KHandleEntry entry = _table[index];
entry.Obj = null;
entry.Next = _nextFreeEntry;
_nextFreeEntry = entry;
_activeSlotsCount--;
}
}
public void SetReservedHandleObj(int handle, KAutoObject obj)
{
int index = (handle >> 0) & 0x7fff;
int handleId = (handle >> 15);
lock (_table)
{
KHandleEntry entry = _table[index];
entry.Obj = obj;
entry.HandleId = (ushort)handleId;
obj.IncrementReferenceCount();
}
}
public bool CloseHandle(int handle)
{
if ((handle >> 30) != 0 ||
handle == SelfThreadHandle ||
handle == SelfProcessHandle)
{
return false;
}
int index = (handle >> 0) & 0x7fff;
int handleId = (handle >> 15);
KAutoObject obj = null;
bool result = false;
lock (_table)
{
if (handleId != 0 && index < _size)
{
KHandleEntry entry = _table[index];
if ((obj = entry.Obj) != null && entry.HandleId == handleId)
{
entry.Obj = null;
entry.Next = _nextFreeEntry;
_nextFreeEntry = entry;
_activeSlotsCount--;
result = true;
}
}
}
if (result)
{
obj.DecrementReferenceCount();
}
return result;
}
public T GetObject<T>(int handle) where T : KAutoObject
{
int index = (handle >> 0) & 0x7fff;
int handleId = (handle >> 15);
lock (_table)
{
if ((handle >> 30) == 0 && handleId != 0 && index < _size)
{
KHandleEntry entry = _table[index];
if (entry.HandleId == handleId && entry.Obj is T obj)
{
return obj;
}
}
}
return default;
}
public KThread GetKThread(int handle)
{
if (handle == SelfThreadHandle)
{
return KernelStatic.GetCurrentThread();
}
else
{
return GetObject<KThread>(handle);
}
}
public KProcess GetKProcess(int handle)
{
if (handle == SelfProcessHandle)
{
return KernelStatic.GetCurrentProcess();
}
else
{
return GetObject<KProcess>(handle);
}
}
public void Destroy()
{
lock (_table)
{
for (int index = 0; index < _size; index++)
{
KHandleEntry entry = _table[index];
if (entry.Obj != null)
{
if (entry.Obj is IDisposable disposableObj)
{
disposableObj.Dispose();
}
entry.Obj.DecrementReferenceCount();
entry.Obj = null;
entry.Next = _nextFreeEntry;
_nextFreeEntry = entry;
}
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,328 @@
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KProcessCapabilities
{
public byte[] SvcAccessMask { get; }
public byte[] IrqAccessMask { get; }
public ulong AllowedCpuCoresMask { get; private set; }
public ulong AllowedThreadPriosMask { get; private set; }
public uint DebuggingFlags { get; private set; }
public uint HandleTableSize { get; private set; }
public uint KernelReleaseVersion { get; private set; }
public uint ApplicationType { get; private set; }
public KProcessCapabilities()
{
// length / number of bits of the underlying type
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8];
IrqAccessMask = new byte[0x80];
}
public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{
AllowedCpuCoresMask = 0xf;
AllowedThreadPriosMask = ulong.MaxValue;
DebuggingFlags &= ~3u;
KernelReleaseVersion = KProcess.KernelVersionPacked;
return Parse(capabilities, memoryManager);
}
public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{
return Parse(capabilities, memoryManager);
}
private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{
int mask0 = 0;
int mask1 = 0;
for (int index = 0; index < capabilities.Length; index++)
{
uint cap = capabilities[index];
if (cap.GetCapabilityType() != CapabilityType.MapRange)
{
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
if (result != Result.Success)
{
return result;
}
}
else
{
if ((uint)index + 1 >= capabilities.Length)
{
return KernelResult.InvalidCombination;
}
uint prevCap = cap;
cap = capabilities[++index];
if (((cap + 1) & ~cap) != 0x40)
{
return KernelResult.InvalidCombination;
}
if ((cap & 0x78000000) != 0)
{
return KernelResult.MaximumExceeded;
}
if ((cap & 0x7ffff80) == 0)
{
return KernelResult.InvalidSize;
}
long address = ((long)prevCap << 5) & 0xffffff000;
long size = ((long)cap << 5) & 0xfffff000;
if (((ulong)(address + size - 1) >> 36) != 0)
{
return KernelResult.InvalidAddress;
}
KMemoryPermission perm = (prevCap >> 31) != 0
? KMemoryPermission.Read
: KMemoryPermission.ReadAndWrite;
Result result;
if ((cap >> 31) != 0)
{
result = memoryManager.MapNormalMemory(address, size, perm);
}
else
{
result = memoryManager.MapIoMemory(address, size, perm);
}
if (result != Result.Success)
{
return result;
}
}
}
return Result.Success;
}
private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
{
CapabilityType code = cap.GetCapabilityType();
if (code == CapabilityType.Invalid)
{
return KernelResult.InvalidCapability;
}
else if (code == CapabilityType.Padding)
{
return Result.Success;
}
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1));
// Check if the property was already set.
if (((mask0 & codeMask) & 0x1e008) != 0)
{
return KernelResult.InvalidCombination;
}
mask0 |= codeMask;
switch (code)
{
case CapabilityType.CorePriority:
{
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
{
return KernelResult.InvalidCapability;
}
uint lowestCpuCore = (cap >> 16) & 0xff;
uint highestCpuCore = (cap >> 24) & 0xff;
if (lowestCpuCore > highestCpuCore)
{
return KernelResult.InvalidCombination;
}
uint highestThreadPrio = (cap >> 4) & 0x3f;
uint lowestThreadPrio = (cap >> 10) & 0x3f;
if (lowestThreadPrio > highestThreadPrio)
{
return KernelResult.InvalidCombination;
}
if (highestCpuCore >= KScheduler.CpuCoresCount)
{
return KernelResult.InvalidCpuCore;
}
AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore);
AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio);
break;
}
case CapabilityType.SyscallMask:
{
int slot = ((int)cap >> 29) & 7;
int svcSlotMask = 1 << slot;
if ((mask1 & svcSlotMask) != 0)
{
return KernelResult.InvalidCombination;
}
mask1 |= svcSlotMask;
uint svcMask = (cap >> 5) & 0xffffff;
int baseSvc = slot * 24;
for (int index = 0; index < 24; index++)
{
if (((svcMask >> index) & 1) == 0)
{
continue;
}
int svcId = baseSvc + index;
if (svcId >= KernelConstants.SupervisorCallCount)
{
return KernelResult.MaximumExceeded;
}
SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7));
}
break;
}
case CapabilityType.MapIoPage:
{
long address = ((long)cap << 4) & 0xffffff000;
memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite);
break;
}
case CapabilityType.MapRegion:
{
// TODO: Implement capabilities for MapRegion
break;
}
case CapabilityType.InterruptPair:
{
// TODO: GIC distributor check.
int irq0 = ((int)cap >> 12) & 0x3ff;
int irq1 = ((int)cap >> 22) & 0x3ff;
if (irq0 != 0x3ff)
{
IrqAccessMask[irq0 / 8] |= (byte)(1 << (irq0 & 7));
}
if (irq1 != 0x3ff)
{
IrqAccessMask[irq1 / 8] |= (byte)(1 << (irq1 & 7));
}
break;
}
case CapabilityType.ProgramType:
{
uint applicationType = (cap >> 14);
if (applicationType > 7)
{
return KernelResult.ReservedValue;
}
ApplicationType = applicationType;
break;
}
case CapabilityType.KernelVersion:
{
// Note: This check is bugged on kernel too, we are just replicating the bug here.
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
{
return KernelResult.ReservedValue;
}
KernelReleaseVersion = cap;
break;
}
case CapabilityType.HandleTable:
{
uint handleTableSize = cap >> 26;
if (handleTableSize > 0x3ff)
{
return KernelResult.ReservedValue;
}
HandleTableSize = handleTableSize;
break;
}
case CapabilityType.DebugFlags:
{
uint debuggingFlags = cap >> 19;
if (debuggingFlags > 3)
{
return KernelResult.ReservedValue;
}
DebuggingFlags &= ~3u;
DebuggingFlags |= debuggingFlags;
break;
}
default: return KernelResult.InvalidCapability;
}
return Result.Success;
}
private static ulong GetMaskFromMinMax(uint min, uint max)
{
uint range = max - min + 1;
if (range == 64)
{
return ulong.MaxValue;
}
ulong mask = (1UL << (int)range) - 1;
return mask << (int)min;
}
}
}

View file

@ -0,0 +1,77 @@
using Ryujinx.HLE.HOS.Kernel.Memory;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KTlsPageInfo
{
public const int TlsEntrySize = 0x200;
public ulong PageVirtualAddress { get; }
public ulong PagePhysicalAddress { get; }
private readonly bool[] _isSlotFree;
public KTlsPageInfo(ulong pageVirtualAddress, ulong pagePhysicalAddress)
{
PageVirtualAddress = pageVirtualAddress;
PagePhysicalAddress = pagePhysicalAddress;
_isSlotFree = new bool[KPageTableBase.PageSize / TlsEntrySize];
for (int index = 0; index < _isSlotFree.Length; index++)
{
_isSlotFree[index] = true;
}
}
public bool TryGetFreePage(out ulong address)
{
address = PageVirtualAddress;
for (int index = 0; index < _isSlotFree.Length; index++)
{
if (_isSlotFree[index])
{
_isSlotFree[index] = false;
return true;
}
address += TlsEntrySize;
}
address = 0;
return false;
}
public bool IsFull()
{
bool hasFree = false;
for (int index = 0; index < _isSlotFree.Length; index++)
{
hasFree |= _isSlotFree[index];
}
return !hasFree;
}
public bool IsEmpty()
{
bool allFree = true;
for (int index = 0; index < _isSlotFree.Length; index++)
{
allFree &= _isSlotFree[index];
}
return allFree;
}
public void FreeTlsSlot(ulong address)
{
_isSlotFree[(address - PageVirtualAddress) / TlsEntrySize] = true;
}
}
}

View file

@ -0,0 +1,61 @@
using Ryujinx.HLE.HOS.Kernel.Memory;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KTlsPageManager
{
private const int TlsEntrySize = 0x200;
private long _pagePosition;
private int _usedSlots;
private bool[] _slots;
public bool IsEmpty => _usedSlots == 0;
public bool IsFull => _usedSlots == _slots.Length;
public KTlsPageManager(long pagePosition)
{
_pagePosition = pagePosition;
_slots = new bool[KPageTableBase.PageSize / TlsEntrySize];
}
public bool TryGetFreeTlsAddr(out long position)
{
position = _pagePosition;
for (int index = 0; index < _slots.Length; index++)
{
if (!_slots[index])
{
_slots[index] = true;
_usedSlots++;
return true;
}
position += TlsEntrySize;
}
position = 0;
return false;
}
public void FreeTlsSlot(int slot)
{
if ((uint)slot > _slots.Length)
{
throw new ArgumentOutOfRangeException(nameof(slot));
}
_slots[slot] = false;
_usedSlots--;
}
}
}

View file

@ -0,0 +1,34 @@
using Ryujinx.Cpu;
using Ryujinx.Memory;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class ProcessContext : IProcessContext
{
public IVirtualMemoryManager AddressSpace { get; }
public ProcessContext(IVirtualMemoryManager asManager)
{
AddressSpace = asManager;
}
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
{
return new ProcessExecutionContext();
}
public void Execute(IExecutionContext context, ulong codeAddress)
{
throw new NotSupportedException();
}
public void InvalidateCacheRegion(ulong address, ulong size)
{
}
public void Dispose()
{
}
}
}

View file

@ -0,0 +1,12 @@
using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class ProcessContextFactory : IProcessContextFactory
{
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
{
return new ProcessContext(new AddressSpaceManager(context.Memory, addressSpaceSize));
}
}
}

View file

@ -0,0 +1,41 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
[Flags]
enum ProcessCreationFlags
{
Is64Bit = 1 << 0,
AddressSpaceShift = 1,
AddressSpace32Bit = 0 << AddressSpaceShift,
AddressSpace64BitDeprecated = 1 << AddressSpaceShift,
AddressSpace32BitWithoutAlias = 2 << AddressSpaceShift,
AddressSpace64Bit = 3 << AddressSpaceShift,
AddressSpaceMask = 7 << AddressSpaceShift,
EnableDebug = 1 << 4,
EnableAslr = 1 << 5,
IsApplication = 1 << 6,
DeprecatedUseSecureMemory = 1 << 7,
PoolPartitionShift = 7,
PoolPartitionApplication = 0 << PoolPartitionShift,
PoolPartitionApplet = 1 << PoolPartitionShift,
PoolPartitionSystem = 2 << PoolPartitionShift,
PoolPartitionSystemNonSecure = 3 << PoolPartitionShift,
PoolPartitionMask = 0xf << PoolPartitionShift,
OptimizeMemoryAllocation = 1 << 11,
All =
Is64Bit |
AddressSpaceMask |
EnableDebug |
EnableAslr |
IsApplication |
DeprecatedUseSecureMemory |
PoolPartitionMask |
OptimizeMemoryAllocation
}
}

View file

@ -0,0 +1,37 @@
namespace Ryujinx.HLE.HOS.Kernel.Process
{
readonly struct ProcessCreationInfo
{
public string Name { get; }
public int Version { get; }
public ulong TitleId { get; }
public ulong CodeAddress { get; }
public int CodePagesCount { get; }
public ProcessCreationFlags Flags { get; }
public int ResourceLimitHandle { get; }
public int SystemResourcePagesCount { get; }
public ProcessCreationInfo(
string name,
int version,
ulong titleId,
ulong codeAddress,
int codePagesCount,
ProcessCreationFlags flags,
int resourceLimitHandle,
int systemResourcePagesCount)
{
Name = name;
Version = version;
TitleId = titleId;
CodeAddress = codeAddress;
CodePagesCount = codePagesCount;
Flags = flags;
ResourceLimitHandle = resourceLimitHandle;
SystemResourcePagesCount = systemResourcePagesCount;
}
}
}

View file

@ -0,0 +1,46 @@
using ARMeilleure.State;
using Ryujinx.Cpu;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class ProcessExecutionContext : IExecutionContext
{
public ulong Pc => 0UL;
public ulong CntfrqEl0 { get; set; }
public ulong CntpctEl0 => 0UL;
public long TpidrEl0 { get; set; }
public long TpidrroEl0 { get; set; }
public uint Pstate { get; set; }
public uint Fpcr { get; set; }
public uint Fpsr { get; set; }
public bool IsAarch32 { get => false; set { } }
public bool Running { get; private set; } = true;
private readonly ulong[] _x = new ulong[32];
public ulong GetX(int index) => _x[index];
public void SetX(int index, ulong value) => _x[index] = value;
public V128 GetV(int index) => default;
public void SetV(int index, V128 value) { }
public void RequestInterrupt()
{
}
public void StopRunning()
{
Running = false;
}
public void Dispose()
{
}
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.HLE.HOS.Kernel.Process
{
enum ProcessState : byte
{
Created = 0,
CreatedAttached = 1,
Started = 2,
Crashed = 3,
Attached = 4,
Exiting = 5,
Exited = 6,
DebugSuspended = 7
}
}

View file

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class ProcessTamperInfo
{
public KProcess Process { get; }
public IEnumerable<string> BuildIds { get; }
public IEnumerable<ulong> CodeAddresses { get; }
public ulong HeapAddress { get; }
public ulong AliasAddress { get; }
public ulong AslrAddress { get; }
public ProcessTamperInfo(KProcess process, IEnumerable<string> buildIds, IEnumerable<ulong> codeAddresses, ulong heapAddress, ulong aliasAddress, ulong aslrAddress)
{
Process = process;
BuildIds = buildIds;
CodeAddresses = codeAddresses;
HeapAddress = heapAddress;
AliasAddress = aliasAddress;
AslrAddress = aslrAddress;
}
}
}