Refactor SVC handler (#540)
* Refactor SVC handler * Get rid of KernelErr * Split kernel code files into multiple folders
This commit is contained in:
parent
2534a7f10c
commit
0039bb6394
105 changed files with 1894 additions and 1982 deletions
311
Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
Normal file
311
Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
Normal file
|
@ -0,0 +1,311 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.Loaders.Elf;
|
||||
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 long BaseAddress { get; private set; }
|
||||
|
||||
public ElfSymbol[] Symbols { get; private set; }
|
||||
|
||||
public Image(long baseAddress, ElfSymbol[] symbols)
|
||||
{
|
||||
BaseAddress = baseAddress;
|
||||
Symbols = symbols;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Image> _images;
|
||||
|
||||
private int _loaded;
|
||||
|
||||
public HleProcessDebugger(KProcess owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
_images = new List<Image>();
|
||||
}
|
||||
|
||||
public void PrintGuestStackTrace(CpuThreadState threadState)
|
||||
{
|
||||
EnsureLoaded();
|
||||
|
||||
StringBuilder trace = new StringBuilder();
|
||||
|
||||
trace.AppendLine("Guest stack trace:");
|
||||
|
||||
void AppendTrace(long address)
|
||||
{
|
||||
Image image = GetImage(address, out int imageIndex);
|
||||
|
||||
if (image == null || !TryGetSubName(image, address, out string subName))
|
||||
{
|
||||
subName = $"Sub{address:x16}";
|
||||
}
|
||||
else if (subName.StartsWith("_Z"))
|
||||
{
|
||||
subName = Demangler.Parse(subName);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
long offset = address - image.BaseAddress;
|
||||
|
||||
string imageName = GetGuessedNsoNameFromIndex(imageIndex);
|
||||
|
||||
string imageNameAndOffset = $"[{_owner.Name}] {imageName}:0x{offset:x8}";
|
||||
|
||||
trace.AppendLine($" {imageNameAndOffset} {subName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
trace.AppendLine($" [{_owner.Name}] ??? {subName}");
|
||||
}
|
||||
}
|
||||
|
||||
long framePointer = (long)threadState.X29;
|
||||
|
||||
while (framePointer != 0)
|
||||
{
|
||||
if ((framePointer & 7) != 0 ||
|
||||
!_owner.CpuMemory.IsMapped(framePointer) ||
|
||||
!_owner.CpuMemory.IsMapped(framePointer + 8))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//Note: This is the return address, we need to subtract one instruction
|
||||
//worth of bytes to get the branch instruction address.
|
||||
AppendTrace(_owner.CpuMemory.ReadInt64(framePointer + 8) - 4);
|
||||
|
||||
framePointer = _owner.CpuMemory.ReadInt64(framePointer);
|
||||
}
|
||||
|
||||
Logger.PrintInfo(LogClass.Cpu, trace.ToString());
|
||||
}
|
||||
|
||||
private bool TryGetSubName(Image image, long address, out string name)
|
||||
{
|
||||
address -= image.BaseAddress;
|
||||
|
||||
int left = 0;
|
||||
int right = image.Symbols.Length - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
int middle = left + (size >> 1);
|
||||
|
||||
ElfSymbol symbol = image.Symbols[middle];
|
||||
|
||||
long endAddr = symbol.Value + symbol.Size;
|
||||
|
||||
if ((ulong)address >= (ulong)symbol.Value && (ulong)address < (ulong)endAddr)
|
||||
{
|
||||
name = symbol.Name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((ulong)address < (ulong)symbol.Value)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
name = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Image GetImage(long address, out int index)
|
||||
{
|
||||
lock (_images)
|
||||
{
|
||||
for (index = _images.Count - 1; index >= 0; index--)
|
||||
{
|
||||
if ((ulong)address >= (ulong)_images[index].BaseAddress)
|
||||
{
|
||||
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 == MemoryPermission.ReadAndExecute)
|
||||
{
|
||||
LoadMod0Symbols(_owner.CpuMemory, (long)info.Address);
|
||||
}
|
||||
|
||||
oldAddress = address;
|
||||
|
||||
address = info.Address + info.Size;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadMod0Symbols(MemoryManager memory, long textOffset)
|
||||
{
|
||||
long mod0Offset = textOffset + memory.ReadUInt32(textOffset + 4);
|
||||
|
||||
if (mod0Offset < textOffset || !memory.IsMapped(mod0Offset) || (mod0Offset & 3) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ElfDynamicTag, long> dynamic = new Dictionary<ElfDynamicTag, long>();
|
||||
|
||||
int mod0Magic = memory.ReadInt32(mod0Offset + 0x0);
|
||||
|
||||
if (mod0Magic != Mod0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ElfDynamicTag tag = (ElfDynamicTag)tagVal;
|
||||
|
||||
if (tag == ElfDynamicTag.DT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
dynamic[tag] = value;
|
||||
}
|
||||
|
||||
if (!dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long strTab) ||
|
||||
!dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out long symTab) ||
|
||||
!dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out long symEntSize))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long strTblAddr = textOffset + strTab;
|
||||
long symTblAddr = textOffset + symTab;
|
||||
|
||||
List<ElfSymbol> symbols = new List<ElfSymbol>();
|
||||
|
||||
while ((ulong)symTblAddr < (ulong)strTblAddr)
|
||||
{
|
||||
ElfSymbol sym = GetSymbol(memory, symTblAddr, strTblAddr);
|
||||
|
||||
symbols.Add(sym);
|
||||
|
||||
symTblAddr += symEntSize;
|
||||
}
|
||||
|
||||
lock (_images)
|
||||
{
|
||||
_images.Add(new Image(textOffset, symbols.OrderBy(x => x.Value).ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
private ElfSymbol GetSymbol(MemoryManager memory, long address, long strTblAddr)
|
||||
{
|
||||
int nameIndex = memory.ReadInt32(address + 0);
|
||||
int info = memory.ReadByte (address + 4);
|
||||
int other = memory.ReadByte (address + 5);
|
||||
int shIdx = memory.ReadInt16(address + 6);
|
||||
long value = memory.ReadInt64(address + 8);
|
||||
long size = memory.ReadInt64(address + 16);
|
||||
|
||||
string name = string.Empty;
|
||||
|
||||
for (int chr; (chr = memory.ReadByte(strTblAddr + nameIndex++)) != 0;)
|
||||
{
|
||||
name += (char)chr;
|
||||
}
|
||||
|
||||
return new ElfSymbol(name, info, other, shIdx, value, size);
|
||||
}
|
||||
}
|
||||
}
|
83
Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs
Normal file
83
Ryujinx.HLE/HOS/Kernel/Process/KContextIdManager.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
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 = BitUtils.CountLeadingZeros32((mask + 1) & ~mask);
|
||||
|
||||
if (firstFreeBit < 32)
|
||||
{
|
||||
int baseBit = index * 32 + 31;
|
||||
|
||||
id = baseBit - firstFreeBit;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (index == IdMasksCount - 1)
|
||||
{
|
||||
throw new InvalidOperationException("Maximum number of Ids reached!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_nextFreeBitHint = id + 1;
|
||||
|
||||
SetBit(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public void PutId(int id)
|
||||
{
|
||||
lock (_idMasks)
|
||||
{
|
||||
ClearBit(id);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TestBit(int bit)
|
||||
{
|
||||
return (_idMasks[_nextFreeBitHint / 32] & (1 << (_nextFreeBitHint & 31))) != 0;
|
||||
}
|
||||
|
||||
private void SetBit(int bit)
|
||||
{
|
||||
_idMasks[_nextFreeBitHint / 32] |= (1 << (_nextFreeBitHint & 31));
|
||||
}
|
||||
|
||||
private void ClearBit(int bit)
|
||||
{
|
||||
_idMasks[_nextFreeBitHint / 32] &= ~(1 << (_nextFreeBitHint & 31));
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs
Normal file
17
Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
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 object Obj { get; set; }
|
||||
|
||||
public KHandleEntry(int index)
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
}
|
209
Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
Normal file
209
Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
Normal file
|
@ -0,0 +1,209 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
class KHandleTable
|
||||
{
|
||||
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
||||
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
||||
|
||||
private Horizon _system;
|
||||
|
||||
private KHandleEntry[] _table;
|
||||
|
||||
private KHandleEntry _tableHead;
|
||||
private KHandleEntry _nextFreeEntry;
|
||||
|
||||
private int _activeSlotsCount;
|
||||
|
||||
private int _size;
|
||||
|
||||
private ushort _idCounter;
|
||||
|
||||
public KHandleTable(Horizon system)
|
||||
{
|
||||
_system = system;
|
||||
}
|
||||
|
||||
public KernelResult Initialize(int size)
|
||||
{
|
||||
if ((uint)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 KernelResult.Success;
|
||||
}
|
||||
|
||||
public KernelResult GenerateHandle(object 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 = (int)((_idCounter << 15) & 0xffff8000) | entry.Index;
|
||||
|
||||
if ((short)(_idCounter + 1) >= 0)
|
||||
{
|
||||
_idCounter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_idCounter = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
public bool CloseHandle(int handle)
|
||||
{
|
||||
if ((handle >> 30) != 0 ||
|
||||
handle == SelfThreadHandle ||
|
||||
handle == SelfProcessHandle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = (handle >> 0) & 0x7fff;
|
||||
int handleId = (handle >> 15);
|
||||
|
||||
bool result = false;
|
||||
|
||||
lock (_table)
|
||||
{
|
||||
if (handleId != 0 && index < _size)
|
||||
{
|
||||
KHandleEntry entry = _table[index];
|
||||
|
||||
if (entry.Obj != null && entry.HandleId == handleId)
|
||||
{
|
||||
entry.Obj = null;
|
||||
entry.Next = _nextFreeEntry;
|
||||
|
||||
_nextFreeEntry = entry;
|
||||
|
||||
_activeSlotsCount--;
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public T GetObject<T>(int handle)
|
||||
{
|
||||
int index = (handle >> 0) & 0x7fff;
|
||||
int handleId = (handle >> 15);
|
||||
|
||||
lock (_table)
|
||||
{
|
||||
if ((handle >> 30) == 0 && handleId != 0)
|
||||
{
|
||||
KHandleEntry entry = _table[index];
|
||||
|
||||
if (entry.HandleId == handleId && entry.Obj is T obj)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public KThread GetKThread(int handle)
|
||||
{
|
||||
if (handle == SelfThreadHandle)
|
||||
{
|
||||
return _system.Scheduler.GetCurrentThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetObject<KThread>(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public KProcess GetKProcess(int handle)
|
||||
{
|
||||
if (handle == SelfProcessHandle)
|
||||
{
|
||||
return _system.Scheduler.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 = null;
|
||||
entry.Next = _nextFreeEntry;
|
||||
|
||||
_nextFreeEntry = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1017
Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
Normal file
1017
Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
Normal file
File diff suppressed because it is too large
Load diff
314
Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
Normal file
314
Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
Normal file
|
@ -0,0 +1,314 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
class KProcessCapabilities
|
||||
{
|
||||
public byte[] SvcAccessMask { get; private set; }
|
||||
public byte[] IrqAccessMask { get; private set; }
|
||||
|
||||
public long AllowedCpuCoresMask { get; private set; }
|
||||
public long AllowedThreadPriosMask { get; private set; }
|
||||
|
||||
public int DebuggingFlags { get; private set; }
|
||||
public int HandleTableSize { get; private set; }
|
||||
public int KernelReleaseVersion { get; private set; }
|
||||
public int ApplicationType { get; private set; }
|
||||
|
||||
public KProcessCapabilities()
|
||||
{
|
||||
SvcAccessMask = new byte[0x10];
|
||||
IrqAccessMask = new byte[0x80];
|
||||
}
|
||||
|
||||
public KernelResult InitializeForKernel(int[] caps, KMemoryManager memoryManager)
|
||||
{
|
||||
AllowedCpuCoresMask = 0xf;
|
||||
AllowedThreadPriosMask = -1;
|
||||
DebuggingFlags &= ~3;
|
||||
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
||||
|
||||
return Parse(caps, memoryManager);
|
||||
}
|
||||
|
||||
public KernelResult InitializeForUser(int[] caps, KMemoryManager memoryManager)
|
||||
{
|
||||
return Parse(caps, memoryManager);
|
||||
}
|
||||
|
||||
private KernelResult Parse(int[] caps, KMemoryManager memoryManager)
|
||||
{
|
||||
int mask0 = 0;
|
||||
int mask1 = 0;
|
||||
|
||||
for (int index = 0; index < caps.Length; index++)
|
||||
{
|
||||
int cap = caps[index];
|
||||
|
||||
if (((cap + 1) & ~cap) != 0x40)
|
||||
{
|
||||
KernelResult result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((uint)index + 1 >= caps.Length)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
int prevCap = cap;
|
||||
|
||||
cap = caps[++index];
|
||||
|
||||
if (((cap + 1) & ~cap) != 0x40)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
if ((cap & 0x78000000) != 0)
|
||||
{
|
||||
return KernelResult.MaximumExceeded;
|
||||
}
|
||||
|
||||
if ((cap & 0x7ffff80) == 0)
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
long address = ((long)(uint)prevCap << 5) & 0xffffff000;
|
||||
long size = ((long)(uint)cap << 5) & 0xfffff000;
|
||||
|
||||
if (((ulong)(address + size - 1) >> 36) != 0)
|
||||
{
|
||||
return KernelResult.InvalidAddress;
|
||||
}
|
||||
|
||||
MemoryPermission perm = (prevCap >> 31) != 0
|
||||
? MemoryPermission.Read
|
||||
: MemoryPermission.ReadAndWrite;
|
||||
|
||||
KernelResult result;
|
||||
|
||||
if ((cap >> 31) != 0)
|
||||
{
|
||||
result = memoryManager.MapNormalMemory(address, size, perm);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = memoryManager.MapIoMemory(address, size, perm);
|
||||
}
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private KernelResult ParseCapability(int cap, ref int mask0, ref int mask1, KMemoryManager memoryManager)
|
||||
{
|
||||
int code = (cap + 1) & ~cap;
|
||||
|
||||
if (code == 1)
|
||||
{
|
||||
return KernelResult.InvalidCapability;
|
||||
}
|
||||
else if (code == 0)
|
||||
{
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
int codeMask = 1 << (32 - BitUtils.CountLeadingZeros32(code + 1));
|
||||
|
||||
//Check if the property was already set.
|
||||
if (((mask0 & codeMask) & 0x1e008) != 0)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
mask0 |= codeMask;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 8:
|
||||
{
|
||||
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
|
||||
{
|
||||
return KernelResult.InvalidCapability;
|
||||
}
|
||||
|
||||
int lowestCpuCore = (cap >> 16) & 0xff;
|
||||
int highestCpuCore = (cap >> 24) & 0xff;
|
||||
|
||||
if (lowestCpuCore > highestCpuCore)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
int highestThreadPrio = (cap >> 4) & 0x3f;
|
||||
int lowestThreadPrio = (cap >> 10) & 0x3f;
|
||||
|
||||
if (lowestThreadPrio > highestThreadPrio)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
if (highestCpuCore >= KScheduler.CpuCoresCount)
|
||||
{
|
||||
return KernelResult.InvalidCpuCore;
|
||||
}
|
||||
|
||||
AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore);
|
||||
AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x10:
|
||||
{
|
||||
int slot = (cap >> 29) & 7;
|
||||
|
||||
int svcSlotMask = 1 << slot;
|
||||
|
||||
if ((mask1 & svcSlotMask) != 0)
|
||||
{
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
mask1 |= svcSlotMask;
|
||||
|
||||
int svcMask = (cap >> 5) & 0xffffff;
|
||||
|
||||
int baseSvc = slot * 24;
|
||||
|
||||
for (int index = 0; index < 24; index++)
|
||||
{
|
||||
if (((svcMask >> index) & 1) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int svcId = baseSvc + index;
|
||||
|
||||
if (svcId > 0x7f)
|
||||
{
|
||||
return KernelResult.MaximumExceeded;
|
||||
}
|
||||
|
||||
SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
long address = ((long)(uint)cap << 4) & 0xffffff000;
|
||||
|
||||
memoryManager.MapIoMemory(address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x800:
|
||||
{
|
||||
//TODO: GIC distributor check.
|
||||
int irq0 = (cap >> 12) & 0x3ff;
|
||||
int irq1 = (cap >> 22) & 0x3ff;
|
||||
|
||||
if (irq0 != 0x3ff)
|
||||
{
|
||||
IrqAccessMask[irq0 / 8] |= (byte)(1 << (irq0 & 7));
|
||||
}
|
||||
|
||||
if (irq1 != 0x3ff)
|
||||
{
|
||||
IrqAccessMask[irq1 / 8] |= (byte)(1 << (irq1 & 7));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x2000:
|
||||
{
|
||||
int applicationType = cap >> 14;
|
||||
|
||||
if ((uint)applicationType > 7)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
ApplicationType = applicationType;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4000:
|
||||
{
|
||||
//Note: This check is bugged on kernel too, we are just replicating the bug here.
|
||||
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
KernelReleaseVersion = cap;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x8000:
|
||||
{
|
||||
int handleTableSize = cap >> 26;
|
||||
|
||||
if ((uint)handleTableSize > 0x3ff)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
HandleTableSize = handleTableSize;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x10000:
|
||||
{
|
||||
int debuggingFlags = cap >> 19;
|
||||
|
||||
if ((uint)debuggingFlags > 3)
|
||||
{
|
||||
return KernelResult.ReservedValue;
|
||||
}
|
||||
|
||||
DebuggingFlags &= ~3;
|
||||
DebuggingFlags |= debuggingFlags;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: return KernelResult.InvalidCapability;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private static long GetMaskFromMinMax(int min, int max)
|
||||
{
|
||||
int range = max - min + 1;
|
||||
|
||||
long mask = (1L << range) - 1;
|
||||
|
||||
return mask << min;
|
||||
}
|
||||
}
|
||||
}
|
75
Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs
Normal file
75
Ryujinx.HLE/HOS/Kernel/Process/KTlsPageInfo.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
class KTlsPageInfo
|
||||
{
|
||||
public const int TlsEntrySize = 0x200;
|
||||
|
||||
public ulong PageAddr { get; private set; }
|
||||
|
||||
private bool[] _isSlotFree;
|
||||
|
||||
public KTlsPageInfo(ulong pageAddress)
|
||||
{
|
||||
PageAddr = pageAddress;
|
||||
|
||||
_isSlotFree = new bool[KMemoryManager.PageSize / TlsEntrySize];
|
||||
|
||||
for (int index = 0; index < _isSlotFree.Length; index++)
|
||||
{
|
||||
_isSlotFree[index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetFreePage(out ulong address)
|
||||
{
|
||||
address = PageAddr;
|
||||
|
||||
for (int index = 0; index < _isSlotFree.Length; index++)
|
||||
{
|
||||
if (_isSlotFree[index])
|
||||
{
|
||||
_isSlotFree[index] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
address += TlsEntrySize;
|
||||
}
|
||||
|
||||
address = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFull()
|
||||
{
|
||||
bool hasFree = false;
|
||||
|
||||
for (int index = 0; index < _isSlotFree.Length; index++)
|
||||
{
|
||||
hasFree |= _isSlotFree[index];
|
||||
}
|
||||
|
||||
return !hasFree;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
bool allFree = true;
|
||||
|
||||
for (int index = 0; index < _isSlotFree.Length; index++)
|
||||
{
|
||||
allFree &= _isSlotFree[index];
|
||||
}
|
||||
|
||||
return allFree;
|
||||
}
|
||||
|
||||
public void FreeTlsSlot(ulong address)
|
||||
{
|
||||
_isSlotFree[(address - PageAddr) / TlsEntrySize] = true;
|
||||
}
|
||||
}
|
||||
}
|
61
Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs
Normal file
61
Ryujinx.HLE/HOS/Kernel/Process/KTlsPageManager.cs
Normal 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[KMemoryManager.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--;
|
||||
}
|
||||
}
|
||||
}
|
37
Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
Normal file
37
Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
struct ProcessCreationInfo
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public int Category { get; private set; }
|
||||
public long TitleId { get; private set; }
|
||||
|
||||
public ulong CodeAddress { get; private set; }
|
||||
public int CodePagesCount { get; private set; }
|
||||
|
||||
public int MmuFlags { get; private set; }
|
||||
public int ResourceLimitHandle { get; private set; }
|
||||
public int PersonalMmHeapPagesCount { get; private set; }
|
||||
|
||||
public ProcessCreationInfo(
|
||||
string name,
|
||||
int category,
|
||||
long titleId,
|
||||
ulong codeAddress,
|
||||
int codePagesCount,
|
||||
int mmuFlags,
|
||||
int resourceLimitHandle,
|
||||
int personalMmHeapPagesCount)
|
||||
{
|
||||
Name = name;
|
||||
Category = category;
|
||||
TitleId = titleId;
|
||||
CodeAddress = codeAddress;
|
||||
CodePagesCount = codePagesCount;
|
||||
MmuFlags = mmuFlags;
|
||||
ResourceLimitHandle = resourceLimitHandle;
|
||||
PersonalMmHeapPagesCount = personalMmHeapPagesCount;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/Process/ProcessState.cs
Normal 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
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue