Address PR feedback, additionally fix some small issues related to the KIP loader and implement SVCs GetProcessId, GetProcessList, GetSystemInfo, CreatePort and ManageNamedPort

This commit is contained in:
gdkchan 2018-11-25 20:50:39 -03:00
parent bc0a619bbc
commit 8be0a1dbcf
25 changed files with 772 additions and 146 deletions

View file

@ -233,7 +233,6 @@ namespace ChocolArm64.Memory
return (ulong)ReadUInt32(position + 0) << 0 |
(ulong)ReadUInt32(position + 4) << 32;
}
}
public Vector128<float> ReadVector8(long position)

View file

@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Npdm;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -25,10 +26,15 @@ namespace Ryujinx.HLE.HOS
internal const int HidSize = 0x40000;
internal const int FontSize = 0x1100000;
private const int MemoryBlockAllocatorSize = 0x2710;
private const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
private const ulong UserSlabHeapItemSize = KMemoryManager.PageSize;
private const ulong UserSlabHeapSize = 0x3de000;
internal long PrivilegedProcessLowestId { get; set; } = 1;
internal long PrivilegedProcessHighestId { get; set; } = 8;
internal Switch Device { get; private set; }
public SystemStateMgr State { get; private set; }
@ -39,8 +45,8 @@ namespace Ryujinx.HLE.HOS
internal KMemoryRegionManager[] MemoryRegions { get; private set; }
internal KMemoryBlockAllocator MemoryBlockAllocatorSys { get; private set; }
internal KMemoryBlockAllocator MemoryBlockAllocator2 { get; private set; }
internal KMemoryBlockAllocator LargeMemoryBlockAllocator { get; private set; }
internal KMemoryBlockAllocator SmallMemoryBlockAllocator { get; private set; }
internal KSlabHeap UserSlabHeapPages { get; private set; }
@ -60,7 +66,9 @@ namespace Ryujinx.HLE.HOS
internal CountdownEvent ThreadCounter;
internal LinkedList<KProcess> Processes;
internal SortedDictionary<long, KProcess> Processes;
internal ConcurrentDictionary<string, KAutoObject> AutoObjectNames;
internal bool EnableVersionChecks { get; private set; }
@ -99,8 +107,8 @@ namespace Ryujinx.HLE.HOS
MemoryRegions = KernelInit.GetMemoryRegions();
MemoryBlockAllocatorSys = new KMemoryBlockAllocator(0x4e20000);
MemoryBlockAllocator2 = new KMemoryBlockAllocator(0x2710000);
LargeMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize * 2);
SmallMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize);
UserSlabHeapPages = new KSlabHeap(
UserSlabHeapBase,
@ -126,9 +134,13 @@ namespace Ryujinx.HLE.HOS
ThreadCounter = new CountdownEvent(1);
Processes = new LinkedList<KProcess>();
Processes = new SortedDictionary<long, KProcess>();
KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.Service];
AutoObjectNames = new ConcurrentDictionary<string, KAutoObject>();
//Note: This is not really correct, but with HLE of services, the only memory
//region used that is used is Application, so we can use the other ones for anything.
KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.NvServices];
ulong HidPa = Region.Address;
ulong FontPa = Region.Address + HidSize;
@ -588,9 +600,12 @@ namespace Ryujinx.HLE.HOS
if (Disposing)
{
//Force all threads to exit.
foreach (KProcess Process in Processes)
lock (Processes)
{
Process.StopAllThreads();
foreach (KProcess Process in Processes.Values)
{
Process.StopAllThreads();
}
}
//It's only safe to release resources once all threads

View file

@ -261,10 +261,15 @@ namespace Ryujinx.HLE.HOS.Kernel
Dynamic[Tag] = Value;
}
long StrTblAddr = TextOffset + Dynamic[ElfDynamicTag.DT_STRTAB];
long SymTblAddr = TextOffset + Dynamic[ElfDynamicTag.DT_SYMTAB];
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 SymEntSize = Dynamic[ElfDynamicTag.DT_SYMENT];
long StrTblAddr = TextOffset + StrTab;
long SymTblAddr = TextOffset + SymTab;
List<ElfSymbol> Symbols = new List<ElfSymbol>();

View file

@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (!UserToKernelInt32(MutexAddress, out int MutexValue))
if (!KernelTransfer.UserToKernelInt32(System, MutexAddress, out int MutexValue))
{
System.CriticalSection.Leave();
@ -185,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel
long Result = 0;
if (!KernelToUserInt32(MutexAddress, MutexValue))
if (!KernelTransfer.KernelToUserInt32(System, MutexAddress, MutexValue))
{
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
@ -230,7 +230,7 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentProcess.CpuMemory.SetExclusive(0, Address);
if (!UserToKernelInt32(Address, out int MutexValue))
if (!KernelTransfer.UserToKernelInt32(System, Address, out int MutexValue))
{
//Invalid address.
CurrentProcess.CpuMemory.ClearExclusive(0);
@ -315,7 +315,7 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
if (!UserToKernelInt32(Address, out int CurrentValue))
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
System.CriticalSection.Leave();
@ -391,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Kernel
//If ShouldDecrement is true, do atomic decrement of the value at Address.
CurrentProcess.CpuMemory.SetExclusive(0, Address);
if (!UserToKernelInt32(Address, out int CurrentValue))
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
System.CriticalSection.Leave();
@ -509,7 +509,7 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentProcess.CpuMemory.SetExclusive(0, Address);
if (!UserToKernelInt32(Address, out int CurrentValue))
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
System.CriticalSection.Leave();
@ -580,7 +580,7 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentProcess.CpuMemory.SetExclusive(0, Address);
if (!UserToKernelInt32(Address, out int CurrentValue))
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
System.CriticalSection.Leave();
@ -646,35 +646,5 @@ namespace Ryujinx.HLE.HOS.Kernel
ArbiterThreads.Remove(Thread);
}
}
private bool UserToKernelInt32(long Address, out int Value)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (CurrentProcess.CpuMemory.IsMapped(Address))
{
Value = CurrentProcess.CpuMemory.ReadInt32(Address);
return true;
}
Value = 0;
return false;
}
private bool KernelToUserInt32(long Address, int Value)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (CurrentProcess.CpuMemory.IsMapped(Address))
{
CurrentProcess.CpuMemory.WriteInt32ToSharedAddr(Address, Value);
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,42 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KAutoObject
{
protected Horizon System;
public KAutoObject(Horizon System)
{
this.System = System;
}
public virtual KernelResult SetName(string Name)
{
if (!System.AutoObjectNames.TryAdd(Name, this))
{
return KernelResult.InvalidState;
}
return KernelResult.Success;
}
public static KernelResult RemoveName(Horizon System, string Name)
{
if (!System.AutoObjectNames.TryRemove(Name, out _))
{
return KernelResult.NotFound;
}
return KernelResult.Success;
}
public static KAutoObject FindNamedObject(Horizon System, string Name)
{
if (System.AutoObjectNames.TryGetValue(Name, out KAutoObject Obj))
{
return Obj;
}
return null;
}
}
}

View file

@ -0,0 +1,31 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KClientPort : KSynchronizationObject
{
private int SessionsCount;
private int CurrentCapacity;
private int MaxSessions;
private KPort Parent;
public KClientPort(Horizon System) : base(System) { }
public void Initialize(KPort Parent, int MaxSessions)
{
this.MaxSessions = MaxSessions;
this.Parent = Parent;
}
public new static KernelResult RemoveName(Horizon System, string Name)
{
KAutoObject FoundObj = KAutoObject.FindNamedObject(System, Name);
if (!(FoundObj is KClientPort))
{
return KernelResult.NotFound;
}
return KAutoObject.RemoveName(System, Name);
}
}
}

View file

@ -2,18 +2,18 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryBlockAllocator
{
private ulong Size;
private ulong CapacityElements;
public int Count { get; set; }
public KMemoryBlockAllocator(ulong Size)
public KMemoryBlockAllocator(ulong CapacityElements)
{
this.Size = Size;
this.CapacityElements = CapacityElements;
}
public bool CanAllocate(int Count)
{
return (ulong)(this.Count + Count) * KMemoryManager.KMemoryBlockSize <= Size;
return (ulong)(this.Count + Count) <= CapacityElements;
}
}
}

View file

@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
public const int PageSize = 0x1000;
public const int KMemoryBlockSize = 0x40;
private const int KMemoryBlockSize = 0x40;
//We need 2 blocks for the case where a big block
//needs to be split in 2, plus one block that will be the new one inserted.
@ -2396,6 +2396,11 @@ namespace Ryujinx.HLE.HOS.Kernel
return AliasRegionStart > Address || Address + Size - 1 > AliasRegionEnd - 1;
}
public bool OutsideAddrSpace(ulong Address, ulong Size)
{
return AddrSpaceStart > Address || Address + Size - 1 > AddrSpaceEnd - 1;
}
public bool OutsideStackRegion(ulong Address, ulong Size)
{
return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;

View file

@ -99,18 +99,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (BlockOrdersCount > 0)
{
ulong AvailablePages = 0;
for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
{
KMemoryRegionBlock Block = Blocks[BlockIndex];
ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
AvailablePages += BlockPagesCount * Block.FreeCount;
}
if (AvailablePages < PagesCount)
if (GetFreePagesImpl() < PagesCount)
{
return KernelResult.OutOfMemory;
}
@ -411,5 +400,29 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
}
public ulong GetFreePages()
{
lock (Blocks)
{
return GetFreePagesImpl();
}
}
private ulong GetFreePagesImpl()
{
ulong AvailablePages = 0;
for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
{
KMemoryRegionBlock Block = Blocks[BlockIndex];
ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
AvailablePages += BlockPagesCount * Block.FreeCount;
}
return AvailablePages;
}
}
}

View file

@ -0,0 +1,26 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KPort : KAutoObject
{
public KServerPort ServerPort { get; private set; }
public KClientPort ClientPort { get; private set; }
private long NameAddress;
private bool IsLight;
public KPort(Horizon System) : base(System)
{
ServerPort = new KServerPort(System);
ClientPort = new KClientPort(System);
}
public void Initialize(int MaxSessions, bool IsLight, long NameAddress)
{
ServerPort.Initialize(this);
ClientPort.Initialize(this, MaxSessions);
this.IsLight = IsLight;
this.NameAddress = NameAddress;
}
}
}

View file

@ -89,6 +89,8 @@ namespace Ryujinx.HLE.HOS.Kernel
CpuMemory = new MemoryManager(System.Device.Memory.RamPointer);
CpuMemory.InvalidAccess += InvalidAccessHandler;
AddressArbiter = new KAddressArbiter(System);
MemoryManager = new KMemoryManager(System, CpuMemory);
@ -118,6 +120,9 @@ namespace Ryujinx.HLE.HOS.Kernel
KResourceLimit ResourceLimit,
MemoryRegion MemRegion)
{
this.ResourceLimit = ResourceLimit;
this.MemRegion = MemRegion;
AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
@ -127,8 +132,8 @@ namespace Ryujinx.HLE.HOS.Kernel
ulong CodeSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
KMemoryBlockAllocator MemoryBlockAllocator = (MmuFlags & 0x40) != 0
? System.MemoryBlockAllocatorSys
: System.MemoryBlockAllocator2;
? System.LargeMemoryBlockAllocator
: System.SmallMemoryBlockAllocator;
KernelResult Result = MemoryManager.InitializeForProcess(
AddrSpaceType,
@ -221,8 +226,8 @@ namespace Ryujinx.HLE.HOS.Kernel
else
{
MemoryBlockAllocator = (MmuFlags & 0x40) != 0
? System.MemoryBlockAllocatorSys
: System.MemoryBlockAllocator2;
? System.LargeMemoryBlockAllocator
: System.SmallMemoryBlockAllocator;
}
AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
@ -990,6 +995,16 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
private void InvalidAccessHandler(object sender, InvalidAccessEventArgs e)
{
PrintCurrentThreadStackTrace();
}
public void PrintCurrentThreadStackTrace()
{
System.Scheduler.GetCurrentThread().PrintGuestStackTrace();
}
private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
{
Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");

View file

@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Kernel
int PrevCap = Cap;
Cap = Caps[Index + 1];
Cap = Caps[++Index];
if (((Cap + 1) & ~Cap) != 0x40)
{

View file

@ -0,0 +1,14 @@
namespace Ryujinx.HLE.HOS.Kernel
{
class KServerPort : KSynchronizationObject
{
private KPort Parent;
public KServerPort(Horizon System) : base(System) { }
public void Initialize(KPort Parent)
{
this.Parent = Parent;
}
}
}

View file

@ -2,16 +2,12 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel
{
class KSynchronizationObject
class KSynchronizationObject : KAutoObject
{
public LinkedList<KThread> WaitingThreads;
protected Horizon System;
public KSynchronizationObject(Horizon System)
public KSynchronizationObject(Horizon System) : base(System)
{
this.System = System;
WaitingThreads = new LinkedList<KThread>();
}

View file

@ -16,11 +16,13 @@ namespace Ryujinx.HLE.HOS.Kernel
InvalidPriority = 0xe001,
InvalidCpuCore = 0xe201,
InvalidHandle = 0xe401,
UserCopyFailed = 0xe601,
InvalidCombination = 0xe801,
TimedOut = 0xea01,
Cancelled = 0xec01,
MaximumExceeded = 0xee01,
InvalidEnumValue = 0xf001,
NotFound = 0xf201,
InvalidThread = 0xf401,
InvalidState = 0xfa01,
ReservedValue = 0xfc01,

View file

@ -0,0 +1,71 @@
using ChocolArm64.Memory;
namespace Ryujinx.HLE.HOS.Kernel
{
static class KernelTransfer
{
public static bool UserToKernelInt32(Horizon System, long Address, out int Value)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (CurrentProcess.CpuMemory.IsMapped(Address) &&
CurrentProcess.CpuMemory.IsMapped(Address + 3))
{
Value = CurrentProcess.CpuMemory.ReadInt32(Address);
return true;
}
Value = 0;
return false;
}
public static bool UserToKernelString(Horizon System, long Address, int Size, out string Value)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (CurrentProcess.CpuMemory.IsMapped(Address) &&
CurrentProcess.CpuMemory.IsMapped(Address + Size - 1))
{
Value = MemoryHelper.ReadAsciiString(CurrentProcess.CpuMemory, Address, Size);
return true;
}
Value = null;
return false;
}
public static bool KernelToUserInt32(Horizon System, long Address, int Value)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (CurrentProcess.CpuMemory.IsMapped(Address) &&
CurrentProcess.CpuMemory.IsMapped(Address + 3))
{
CurrentProcess.CpuMemory.WriteInt32ToSharedAddr(Address, Value);
return true;
}
return false;
}
public static bool KernelToUserInt64(Horizon System, long Address, long Value)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (CurrentProcess.CpuMemory.IsMapped(Address) &&
CurrentProcess.CpuMemory.IsMapped(Address + 7))
{
CurrentProcess.CpuMemory.WriteInt64(Address, Value);
return true;
}
return false;
}
}
}

View file

@ -39,8 +39,6 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
private static Random Rng;
public SvcHandler(Switch Device, KProcess Process)
{
SvcFuncs = new Dictionary<int, SvcFunc>()
@ -77,6 +75,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x1f, SvcConnectToNamedPort },
{ 0x21, SvcSendSyncRequest },
{ 0x22, SvcSendSyncRequestWithUserBuffer },
{ 0x24, GetProcessId64 },
{ 0x25, SvcGetThreadId },
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
@ -87,7 +86,11 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x33, SvcGetThreadContext3 },
{ 0x34, SvcWaitForAddress },
{ 0x35, SvcSignalToAddress },
{ 0x45, CreateEvent64 }
{ 0x45, CreateEvent64 },
{ 0x65, GetProcessList64 },
{ 0x6f, GetSystemInfo64 },
{ 0x70, CreatePort64 },
{ 0x71, ManageNamedPort64 }
};
this.Device = Device;
@ -96,11 +99,6 @@ namespace Ryujinx.HLE.HOS.Kernel
this.Memory = Process.CpuMemory;
}
static SvcHandler()
{
Rng = new Random();
}
public void SvcCall(object sender, InstExceptionEventArgs e)
{
CpuThreadState ThreadState = (CpuThreadState)sender;

View file

@ -14,10 +14,6 @@ namespace Ryujinx.HLE.HOS.Kernel
{
partial class SvcHandler
{
private const int AllowedCpuIdBitmask = 0b1111;
private const bool EnableProcessDebugging = false;
private void SvcExitProcess(CpuThreadState ThreadState)
{
System.Scheduler.GetCurrentProcess().Terminate();
@ -252,6 +248,41 @@ namespace Ryujinx.HLE.HOS.Kernel
IpcMessage.Thread.Reschedule(ThreadSchedState.Running);
}
private void GetProcessId64(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X1;
KernelResult Result = GetProcessId(Handle, out long Pid);
ThreadState.X0 = (ulong)Result;
ThreadState.X1 = (ulong)Pid;
}
private KernelResult GetProcessId(int Handle, out long Pid)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
if (Process == null)
{
KThread Thread = CurrentProcess.HandleTable.GetKThread(Handle);
if (Thread != null)
{
Process = Thread.Owner;
}
//TODO: KDebugEvent.
}
Pid = Process?.Pid ?? 0;
return Process != null
? KernelResult.Success
: KernelResult.InvalidHandle;
}
private void SvcBreak(CpuThreadState ThreadState)
{
long Reason = (long)ThreadState.X0;
@ -289,7 +320,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private void GetInfo64(CpuThreadState ThreadState)
{
long StackPtr = (long)ThreadState.X0;
int Id = (int)ThreadState.X1;
uint Id = (uint)ThreadState.X1;
int Handle = (int)ThreadState.X2;
long SubId = (long)ThreadState.X3;
@ -299,7 +330,7 @@ namespace Ryujinx.HLE.HOS.Kernel
ThreadState.X1 = (ulong)Value;
}
private KernelResult GetInfo(int Id, int Handle, long SubId, out long Value)
private KernelResult GetInfo(uint Id, int Handle, long SubId, out long Value)
{
Value = 0;
@ -465,12 +496,15 @@ namespace Ryujinx.HLE.HOS.Kernel
return KernelResult.InvalidCombination;
}
Value = System.Scheduler.GetCurrentProcess().RandomEntropy[SubId];
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
Value = CurrentProcess.RandomEntropy[SubId];
break;
}
case unchecked((int)0xf0000002u):
case 0xf0000002u:
{
if (SubId < -1 || SubId > 3)
{
@ -553,5 +587,241 @@ namespace Ryujinx.HLE.HOS.Kernel
return Result;
}
private void GetProcessList64(CpuThreadState State)
{
ulong Address = State.X1;
int MaxOut = (int)State.X2;
KernelResult Result = GetProcessList(Address, MaxOut, out int Count);
State.X0 = (ulong)Result;
State.X1 = (ulong)Count;
}
private KernelResult GetProcessList(ulong Address, int MaxCount, out int Count)
{
Count = 0;
if ((MaxCount >> 28) != 0)
{
return KernelResult.MaximumExceeded;
}
if (MaxCount != 0)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
ulong CopySize = (ulong)MaxCount * 8;
if (Address + CopySize <= Address)
{
return KernelResult.InvalidMemState;
}
if (CurrentProcess.MemoryManager.OutsideAddrSpace(Address, CopySize))
{
return KernelResult.InvalidMemState;
}
}
int CopyCount = 0;
lock (System.Processes)
{
foreach (KProcess Process in System.Processes.Values)
{
if (CopyCount < MaxCount)
{
if (!KernelTransfer.KernelToUserInt64(System, (long)Address + CopyCount * 8, Process.Pid))
{
return KernelResult.UserCopyFailed;
}
}
CopyCount++;
}
}
Count = CopyCount;
return KernelResult.Success;
}
private void GetSystemInfo64(CpuThreadState State)
{
uint Id = (uint)State.X1;
int Handle = (int)State.X2;
long SubId = (long)State.X3;
KernelResult Result = GetSystemInfo(Id, Handle, SubId, out long Value);
State.X0 = (ulong)Result;
State.X1 = (ulong)Value;
}
private KernelResult GetSystemInfo(uint Id, int Handle, long SubId, out long Value)
{
Value = 0;
if (Id > 2)
{
return KernelResult.InvalidEnumValue;
}
if (Handle != 0)
{
return KernelResult.InvalidHandle;
}
if (Id < 2)
{
if ((ulong)SubId > 3)
{
return KernelResult.InvalidCombination;
}
KMemoryRegionManager Region = System.MemoryRegions[SubId];
switch (Id)
{
//Memory region capacity.
case 0: Value = (long)Region.Size; break;
//Memory region free space.
case 1:
{
ulong FreePagesCount = Region.GetFreePages();
Value = (long)(FreePagesCount * KMemoryManager.PageSize);
break;
}
}
}
else /* if (Id == 2) */
{
if ((ulong)SubId > 1)
{
return KernelResult.InvalidCombination;
}
switch (SubId)
{
case 0: Value = System.PrivilegedProcessLowestId; break;
case 1: Value = System.PrivilegedProcessHighestId; break;
}
}
return KernelResult.Success;
}
private void CreatePort64(CpuThreadState State)
{
int MaxSessions = (int)State.X2;
bool IsLight = (State.X3 & 1) != 0;
long NameAddress = (long)State.X4;
KernelResult Result = CreatePort(
MaxSessions,
IsLight,
NameAddress,
out int ServerPortHandle,
out int ClientPortHandle);
State.X0 = (ulong)Result;
State.X1 = (ulong)ServerPortHandle;
State.X2 = (ulong)ClientPortHandle;
}
private KernelResult CreatePort(
int MaxSessions,
bool IsLight,
long NameAddress,
out int ServerPortHandle,
out int ClientPortHandle)
{
ServerPortHandle = ClientPortHandle = 0;
if (MaxSessions < 1)
{
return KernelResult.MaximumExceeded;
}
KPort Port = new KPort(System);
Port.Initialize(MaxSessions, IsLight, NameAddress);
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KernelResult Result = CurrentProcess.HandleTable.GenerateHandle(Port.ClientPort, out ClientPortHandle);
if (Result != KernelResult.Success)
{
return Result;
}
Result = CurrentProcess.HandleTable.GenerateHandle(Port.ServerPort, out ServerPortHandle);
if (Result != KernelResult.Success)
{
CurrentProcess.HandleTable.CloseHandle(ClientPortHandle);
}
return Result;
}
private void ManageNamedPort64(CpuThreadState State)
{
long NameAddress = (long)State.X1;
int MaxSessions = (int)State.X2;
KernelResult Result = ManageNamedPort(NameAddress, MaxSessions, out int Handle);
State.X0 = (ulong)Result;
State.X1 = (ulong)Handle;
}
private KernelResult ManageNamedPort(long NameAddress, int MaxSessions, out int Handle)
{
Handle = 0;
if (!KernelTransfer.UserToKernelString(System, NameAddress, 12, out string Name))
{
return KernelResult.UserCopyFailed;
}
if (MaxSessions < 0 || Name.Length > 11)
{
return KernelResult.MaximumExceeded;
}
if (MaxSessions == 0)
{
return KClientPort.RemoveName(System, Name);
}
KPort Port = new KPort(System);
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KernelResult Result = CurrentProcess.HandleTable.GenerateHandle(Port.ServerPort, out Handle);
if (Result != KernelResult.Success)
{
return Result;
}
Port.Initialize(MaxSessions, false, 0);
Result = Port.SetName(Name);
if (Result != KernelResult.Success)
{
CurrentProcess.HandleTable.CloseHandle(Handle);
}
return Result;
}
}
}

View file

@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{Handle:x8}");
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
if (SyncObj == null)

View file

@ -9,10 +9,112 @@ namespace Ryujinx.HLE.HOS
{
class ProgramLoader
{
private const bool AslrEnabled = true;
private const int ArgsHeaderSize = 8;
private const int ArgsDataSize = 0x9000;
private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize;
public static bool LoadKernelInitalProcess(Horizon System, KernelInitialProcess Kip)
{
int EndOffset = Kip.DataOffset + Kip.Data.Length;
if (Kip.BssSize != 0)
{
EndOffset = Kip.BssOffset + Kip.BssSize;
}
int CodeSize = BitUtils.AlignUp(Kip.TextOffset + EndOffset, KMemoryManager.PageSize);
int CodePagesCount = CodeSize / KMemoryManager.PageSize;
ulong CodeBaseAddress = Kip.Addr39Bits ? 0x8000000UL : 0x200000UL;
ulong CodeAddress = CodeBaseAddress + (ulong)Kip.TextOffset;
int MmuFlags = 0;
if (AslrEnabled)
{
//TODO: Randomization.
MmuFlags |= 0x20;
}
if (Kip.Addr39Bits)
{
MmuFlags |= (int)AddressSpaceType.Addr39Bits << 1;
}
if (Kip.Is64Bits)
{
MmuFlags |= 1;
}
ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
Kip.Name,
Kip.ProcessCategory,
Kip.TitleId,
CodeAddress,
CodePagesCount,
MmuFlags,
0,
0);
MemoryRegion MemRegion = Kip.IsService
? MemoryRegion.Service
: MemoryRegion.Application;
KMemoryRegionManager Region = System.MemoryRegions[(int)MemRegion];
KernelResult Result = Region.AllocatePages((ulong)CodePagesCount, false, out KPageList PageList);
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
return false;
}
KProcess Process = new KProcess(System);
Result = Process.InitializeKip(
CreationInfo,
Kip.Capabilities,
PageList,
System.ResourceLimit,
MemRegion);
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
return false;
}
Result = LoadIntoMemory(Process, Kip, CodeBaseAddress);
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
return false;
}
Result = Process.Start(Kip.MainThreadPriority, (ulong)Kip.MainThreadStackSize);
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.Loader, $"Process start returned error \"{Result}\".");
return false;
}
System.Processes.Add(Process.Pid, Process);
return true;
}
public static bool LoadStaticObjects(
Horizon System,
Npdm MetaData,
@ -66,8 +168,6 @@ namespace Ryujinx.HLE.HOS
int PersonalMmHeapPagesCount = MetaData.PersonalMmHeapSize / KMemoryManager.PageSize;
KProcess Process = new KProcess(System);
ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
MetaData.TitleName,
MetaData.ProcessCategory,
@ -97,6 +197,8 @@ namespace Ryujinx.HLE.HOS
return false;
}
KProcess Process = new KProcess(System);
Result = Process.Initialize(
CreationInfo,
MetaData.ACI0.KernelAccessControl.Capabilities,
@ -114,31 +216,11 @@ namespace Ryujinx.HLE.HOS
{
Logger.PrintInfo(LogClass.Loader, $"Loading image {Index} at 0x{NsoBase[Index]:x16}...");
IExecutable StaticObject = StaticObjects[Index];
ulong TextStart = NsoBase[Index] + (ulong)StaticObject.TextOffset;
ulong ROStart = NsoBase[Index] + (ulong)StaticObject.ROOffset;
ulong DataStart = NsoBase[Index] + (ulong)StaticObject.DataOffset;
ulong BssStart = DataStart + (ulong)StaticObject.Data.Length;
ulong BssEnd = BitUtils.AlignUp(BssStart + (ulong)StaticObject.BssSize, KMemoryManager.PageSize);
Process.CpuMemory.WriteBytes((long)TextStart, StaticObject.Text);
Process.CpuMemory.WriteBytes((long)ROStart, StaticObject.RO);
Process.CpuMemory.WriteBytes((long)DataStart, StaticObject.Data);
MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, (int)(BssEnd - BssStart));
KMemoryManager MemMgr = Process.MemoryManager;
Result = MemMgr.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute);
Result |= MemMgr.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read);
Result |= MemMgr.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite);
Result = LoadIntoMemory(Process, StaticObjects[Index], NsoBase[Index]);
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.Loader, $"Process initialization failed setting memory permissions.");
Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
return false;
}
@ -153,9 +235,58 @@ namespace Ryujinx.HLE.HOS
return false;
}
System.Processes.AddLast(Process);
System.Processes.Add(Process.Pid, Process);
return true;
}
private static KernelResult LoadIntoMemory(KProcess Process, IExecutable Image, ulong BaseAddress)
{
ulong TextStart = BaseAddress + (ulong)Image.TextOffset;
ulong ROStart = BaseAddress + (ulong)Image.ROOffset;
ulong DataStart = BaseAddress + (ulong)Image.DataOffset;
ulong BssStart = BaseAddress + (ulong)Image.BssOffset;
ulong End = DataStart + (ulong)Image.Data.Length;
if (Image.BssSize != 0)
{
End = BssStart + (ulong)Image.BssSize;
}
Process.CpuMemory.WriteBytes((long)TextStart, Image.Text);
Process.CpuMemory.WriteBytes((long)ROStart, Image.RO);
Process.CpuMemory.WriteBytes((long)DataStart, Image.Data);
MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, Image.BssSize);
KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
{
if (Size == 0)
{
return KernelResult.Success;
}
Size = BitUtils.AlignUp(Size, KMemoryManager.PageSize);
return Process.MemoryManager.SetProcessMemoryPermission(Address, Size, Permission);
}
KernelResult Result = SetProcessMemoryPermission(TextStart, (ulong)Image.Text.Length, MemoryPermission.ReadAndExecute);
if (Result != KernelResult.Success)
{
return Result;
}
Result = SetProcessMemoryPermission(ROStart, (ulong)Image.RO.Length, MemoryPermission.Read);
if (Result != KernelResult.Success)
{
return Result;
}
return SetProcessMemoryPermission(DataStart, End - DataStart, MemoryPermission.ReadAndWrite);
}
}
}

View file

@ -39,8 +39,10 @@ namespace Ryujinx.HLE.Loaders.Compression
}
}
public static byte[] Decompress(Stream Input)
public static byte[] Decompress(Stream Input, int DecompressedLength)
{
long End = Input.Position;
BackwardsReader Reader = new BackwardsReader(Input);
int AdditionalDecLength = Reader.ReadInt32();
@ -49,14 +51,18 @@ namespace Ryujinx.HLE.Loaders.Compression
Input.Seek(12 - StartOffset, SeekOrigin.Current);
byte[] Dec = new byte[CompressedLength + AdditionalDecLength];
byte[] Dec = new byte[DecompressedLength];
int DecompressedLengthUnpadded = CompressedLength + AdditionalDecLength;
int DecompressionStart = DecompressedLength - DecompressedLengthUnpadded;
int DecPos = Dec.Length;
byte Mask = 0;
byte Header = 0;
while (DecPos > 0)
while (DecPos > DecompressionStart)
{
if ((Mask >>= 1) == 0)
{
@ -75,19 +81,19 @@ namespace Ryujinx.HLE.Loaders.Compression
int Length = (Pair >> 12) + 3;
int Position = (Pair & 0xfff) + 3;
if (Position - Length >= DecPos)
DecPos -= Length;
if (Length <= Position)
{
int SrcPos = DecPos + Position;
DecPos -= Length;
Buffer.BlockCopy(Dec, SrcPos, Dec, DecPos, Length);
}
else
{
while (Length-- > 0)
for (int Offset = 0; Offset < Length; Offset++)
{
Dec[--DecPos] = Dec[DecPos + Position + 1];
Dec[DecPos + Offset] = Dec[DecPos + Position + Offset];
}
}
}

View file

@ -6,10 +6,10 @@ namespace Ryujinx.HLE.Loaders.Executables
byte[] RO { get; }
byte[] Data { get; }
int Mod0Offset { get; }
int TextOffset { get; }
int ROOffset { get; }
int DataOffset { get; }
int BssOffset { get; }
int BssSize { get; }
}
}

View file

@ -3,7 +3,7 @@ using System.IO;
namespace Ryujinx.HLE.Loaders.Executables
{
class KernelInitialProcess
class KernelInitialProcess : IExecutable
{
public string Name { get; private set; }
@ -14,6 +14,10 @@ namespace Ryujinx.HLE.Loaders.Executables
public byte MainThreadPriority { get; private set; }
public byte DefaultProcessorId { get; private set; }
public bool Is64Bits { get; private set; }
public bool Addr39Bits { get; private set; }
public bool IsService { get; private set; }
public byte[] Text { get; private set; }
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
@ -24,6 +28,10 @@ namespace Ryujinx.HLE.Loaders.Executables
public int BssOffset { get; private set; }
public int BssSize { get; private set; }
public int MainThreadStackSize { get; private set; }
public int[] Capabilities { get; private set; }
private struct SegmentHeader
{
public int Offset { get; private set; }
@ -67,6 +75,10 @@ namespace Ryujinx.HLE.Loaders.Executables
byte Reserved = Reader.ReadByte();
byte Flags = Reader.ReadByte();
Is64Bits = (Flags & 0x08) != 0;
Addr39Bits = (Flags & 0x10) != 0;
IsService = (Flags & 0x20) != 0;
SegmentHeader[] Segments = new SegmentHeader[6];
for (int Index = 0; Index < Segments.Length; Index++)
@ -78,17 +90,26 @@ namespace Ryujinx.HLE.Loaders.Executables
Reader.ReadInt32());
}
Reader.ReadBytes(0x20);
Text = ReadSegment(Segments[0], Input);
RO = ReadSegment(Segments[1], Input);
Data = ReadSegment(Segments[2], Input);
TextOffset = Segments[0].Offset;
ROOffset = Segments[1].Offset;
DataOffset = Segments[2].Offset;
BssOffset = Segments[3].Offset;
BssSize = Segments[3].DecompressedSize;
MainThreadStackSize = Segments[1].Attribute;
Capabilities = new int[8];
for (int Index = 0; Index < Capabilities.Length; Index++)
{
Capabilities[Index] = Reader.ReadInt32();
}
Input.Seek(0x100, SeekOrigin.Begin);
Text = ReadSegment(Segments[0], Input);
RO = ReadSegment(Segments[1], Input);
Data = ReadSegment(Segments[2], Input);
}
private byte[] ReadSegment(SegmentHeader Header, Stream Input)
@ -97,7 +118,7 @@ namespace Ryujinx.HLE.Loaders.Executables
Input.Seek(End, SeekOrigin.Begin);
byte[] Data = BackwardsLz.Decompress(Input);
byte[] Data = BackwardsLz.Decompress(Input, Header.DecompressedSize);
Input.Seek(End, SeekOrigin.Begin);

View file

@ -14,6 +14,8 @@ namespace Ryujinx.HLE.Loaders.Executables
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
public int BssOffset => DataOffset + Data.Length;
public ulong SourceAddress { get; private set; }
public ulong BssAddress { get; private set; }

View file

@ -10,12 +10,13 @@ namespace Ryujinx.HLE.Loaders.Executables
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
public int Mod0Offset { get; private set; }
public int TextOffset { get; private set; }
public int ROOffset { get; private set; }
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
public int BssOffset => DataOffset + Data.Length;
[Flags]
private enum NsoFlags
{
@ -103,15 +104,6 @@ namespace Ryujinx.HLE.Loaders.Executables
{
Data = Lz4.Decompress(Data, DataDecSize);
}
using (MemoryStream TextMS = new MemoryStream(Text))
{
BinaryReader TextReader = new BinaryReader(TextMS);
TextMS.Seek(4, SeekOrigin.Begin);
Mod0Offset = TextReader.ReadInt32();
}
}
}
}