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:
parent
bc0a619bbc
commit
8be0a1dbcf
25 changed files with 772 additions and 146 deletions
|
@ -233,7 +233,6 @@ namespace ChocolArm64.Memory
|
|||
return (ulong)ReadUInt32(position + 0) << 0 |
|
||||
(ulong)ReadUInt32(position + 4) << 32;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Vector128<float> ReadVector8(long position)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
42
Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
Normal file
42
Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx.HLE/HOS/Kernel/KClientPort.cs
Normal file
31
Ryujinx.HLE/HOS/Kernel/KClientPort.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
26
Ryujinx.HLE/HOS/Kernel/KPort.cs
Normal file
26
Ryujinx.HLE/HOS/Kernel/KPort.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}.");
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
|
||||
int PrevCap = Cap;
|
||||
|
||||
Cap = Caps[Index + 1];
|
||||
Cap = Caps[++Index];
|
||||
|
||||
if (((Cap + 1) & ~Cap) != 0x40)
|
||||
{
|
||||
|
|
14
Ryujinx.HLE/HOS/Kernel/KServerPort.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/KServerPort.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>();
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
71
Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
Normal file
71
Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue