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 |
|
return (ulong)ReadUInt32(position + 0) << 0 |
|
||||||
(ulong)ReadUInt32(position + 4) << 32;
|
(ulong)ReadUInt32(position + 4) << 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector128<float> ReadVector8(long position)
|
public Vector128<float> ReadVector8(long position)
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -25,10 +26,15 @@ namespace Ryujinx.HLE.HOS
|
||||||
internal const int HidSize = 0x40000;
|
internal const int HidSize = 0x40000;
|
||||||
internal const int FontSize = 0x1100000;
|
internal const int FontSize = 0x1100000;
|
||||||
|
|
||||||
|
private const int MemoryBlockAllocatorSize = 0x2710;
|
||||||
|
|
||||||
private const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
private const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
||||||
private const ulong UserSlabHeapItemSize = KMemoryManager.PageSize;
|
private const ulong UserSlabHeapItemSize = KMemoryManager.PageSize;
|
||||||
private const ulong UserSlabHeapSize = 0x3de000;
|
private const ulong UserSlabHeapSize = 0x3de000;
|
||||||
|
|
||||||
|
internal long PrivilegedProcessLowestId { get; set; } = 1;
|
||||||
|
internal long PrivilegedProcessHighestId { get; set; } = 8;
|
||||||
|
|
||||||
internal Switch Device { get; private set; }
|
internal Switch Device { get; private set; }
|
||||||
|
|
||||||
public SystemStateMgr State { get; private set; }
|
public SystemStateMgr State { get; private set; }
|
||||||
|
@ -39,8 +45,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal KMemoryRegionManager[] MemoryRegions { get; private set; }
|
internal KMemoryRegionManager[] MemoryRegions { get; private set; }
|
||||||
|
|
||||||
internal KMemoryBlockAllocator MemoryBlockAllocatorSys { get; private set; }
|
internal KMemoryBlockAllocator LargeMemoryBlockAllocator { get; private set; }
|
||||||
internal KMemoryBlockAllocator MemoryBlockAllocator2 { get; private set; }
|
internal KMemoryBlockAllocator SmallMemoryBlockAllocator { get; private set; }
|
||||||
|
|
||||||
internal KSlabHeap UserSlabHeapPages { get; private set; }
|
internal KSlabHeap UserSlabHeapPages { get; private set; }
|
||||||
|
|
||||||
|
@ -60,7 +66,9 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal CountdownEvent ThreadCounter;
|
internal CountdownEvent ThreadCounter;
|
||||||
|
|
||||||
internal LinkedList<KProcess> Processes;
|
internal SortedDictionary<long, KProcess> Processes;
|
||||||
|
|
||||||
|
internal ConcurrentDictionary<string, KAutoObject> AutoObjectNames;
|
||||||
|
|
||||||
internal bool EnableVersionChecks { get; private set; }
|
internal bool EnableVersionChecks { get; private set; }
|
||||||
|
|
||||||
|
@ -99,8 +107,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
MemoryRegions = KernelInit.GetMemoryRegions();
|
MemoryRegions = KernelInit.GetMemoryRegions();
|
||||||
|
|
||||||
MemoryBlockAllocatorSys = new KMemoryBlockAllocator(0x4e20000);
|
LargeMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize * 2);
|
||||||
MemoryBlockAllocator2 = new KMemoryBlockAllocator(0x2710000);
|
SmallMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize);
|
||||||
|
|
||||||
UserSlabHeapPages = new KSlabHeap(
|
UserSlabHeapPages = new KSlabHeap(
|
||||||
UserSlabHeapBase,
|
UserSlabHeapBase,
|
||||||
|
@ -126,9 +134,13 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
ThreadCounter = new CountdownEvent(1);
|
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 HidPa = Region.Address;
|
||||||
ulong FontPa = Region.Address + HidSize;
|
ulong FontPa = Region.Address + HidSize;
|
||||||
|
@ -588,9 +600,12 @@ namespace Ryujinx.HLE.HOS
|
||||||
if (Disposing)
|
if (Disposing)
|
||||||
{
|
{
|
||||||
//Force all threads to exit.
|
//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
|
//It's only safe to release resources once all threads
|
||||||
|
|
|
@ -261,10 +261,15 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Dynamic[Tag] = Value;
|
Dynamic[Tag] = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
long StrTblAddr = TextOffset + Dynamic[ElfDynamicTag.DT_STRTAB];
|
if (!Dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long StrTab) ||
|
||||||
long SymTblAddr = TextOffset + Dynamic[ElfDynamicTag.DT_SYMTAB];
|
!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>();
|
List<ElfSymbol> Symbols = new List<ElfSymbol>();
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
if (!UserToKernelInt32(MutexAddress, out int MutexValue))
|
if (!KernelTransfer.UserToKernelInt32(System, MutexAddress, out int MutexValue))
|
||||||
{
|
{
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
long Result = 0;
|
long Result = 0;
|
||||||
|
|
||||||
if (!KernelToUserInt32(MutexAddress, MutexValue))
|
if (!KernelTransfer.KernelToUserInt32(System, MutexAddress, MutexValue))
|
||||||
{
|
{
|
||||||
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!UserToKernelInt32(Address, out int MutexValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int MutexValue))
|
||||||
{
|
{
|
||||||
//Invalid address.
|
//Invalid address.
|
||||||
CurrentProcess.CpuMemory.ClearExclusive(0);
|
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
@ -315,7 +315,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CurrentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
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();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
@ -391,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
||||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!UserToKernelInt32(Address, out int CurrentValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
|
||||||
{
|
{
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
@ -509,7 +509,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!UserToKernelInt32(Address, out int CurrentValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
|
||||||
{
|
{
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
@ -580,7 +580,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!UserToKernelInt32(Address, out int CurrentValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
|
||||||
{
|
{
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
@ -646,35 +646,5 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ArbiterThreads.Remove(Thread);
|
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
|
class KMemoryBlockAllocator
|
||||||
{
|
{
|
||||||
private ulong Size;
|
private ulong CapacityElements;
|
||||||
|
|
||||||
public int Count { get; set; }
|
public int Count { get; set; }
|
||||||
|
|
||||||
public KMemoryBlockAllocator(ulong Size)
|
public KMemoryBlockAllocator(ulong CapacityElements)
|
||||||
{
|
{
|
||||||
this.Size = Size;
|
this.CapacityElements = CapacityElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanAllocate(int Count)
|
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 PageSize = 0x1000;
|
||||||
|
|
||||||
public const int KMemoryBlockSize = 0x40;
|
private const int KMemoryBlockSize = 0x40;
|
||||||
|
|
||||||
//We need 2 blocks for the case where a big block
|
//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.
|
//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;
|
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)
|
public bool OutsideStackRegion(ulong Address, ulong Size)
|
||||||
{
|
{
|
||||||
return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;
|
return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;
|
||||||
|
|
|
@ -99,18 +99,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (BlockOrdersCount > 0)
|
if (BlockOrdersCount > 0)
|
||||||
{
|
{
|
||||||
ulong AvailablePages = 0;
|
if (GetFreePagesImpl() < PagesCount)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfMemory;
|
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 = new MemoryManager(System.Device.Memory.RamPointer);
|
||||||
|
|
||||||
|
CpuMemory.InvalidAccess += InvalidAccessHandler;
|
||||||
|
|
||||||
AddressArbiter = new KAddressArbiter(System);
|
AddressArbiter = new KAddressArbiter(System);
|
||||||
|
|
||||||
MemoryManager = new KMemoryManager(System, CpuMemory);
|
MemoryManager = new KMemoryManager(System, CpuMemory);
|
||||||
|
@ -118,6 +120,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
KResourceLimit ResourceLimit,
|
KResourceLimit ResourceLimit,
|
||||||
MemoryRegion MemRegion)
|
MemoryRegion MemRegion)
|
||||||
{
|
{
|
||||||
|
this.ResourceLimit = ResourceLimit;
|
||||||
|
this.MemRegion = MemRegion;
|
||||||
|
|
||||||
AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
|
AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
|
||||||
|
|
||||||
bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
|
bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
|
||||||
|
@ -127,8 +132,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ulong CodeSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
|
ulong CodeSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
|
||||||
|
|
||||||
KMemoryBlockAllocator MemoryBlockAllocator = (MmuFlags & 0x40) != 0
|
KMemoryBlockAllocator MemoryBlockAllocator = (MmuFlags & 0x40) != 0
|
||||||
? System.MemoryBlockAllocatorSys
|
? System.LargeMemoryBlockAllocator
|
||||||
: System.MemoryBlockAllocator2;
|
: System.SmallMemoryBlockAllocator;
|
||||||
|
|
||||||
KernelResult Result = MemoryManager.InitializeForProcess(
|
KernelResult Result = MemoryManager.InitializeForProcess(
|
||||||
AddrSpaceType,
|
AddrSpaceType,
|
||||||
|
@ -221,8 +226,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MemoryBlockAllocator = (MmuFlags & 0x40) != 0
|
MemoryBlockAllocator = (MmuFlags & 0x40) != 0
|
||||||
? System.MemoryBlockAllocatorSys
|
? System.LargeMemoryBlockAllocator
|
||||||
: System.MemoryBlockAllocator2;
|
: System.SmallMemoryBlockAllocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
|
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)
|
private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
|
||||||
{
|
{
|
||||||
Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
|
Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
int PrevCap = Cap;
|
int PrevCap = Cap;
|
||||||
|
|
||||||
Cap = Caps[Index + 1];
|
Cap = Caps[++Index];
|
||||||
|
|
||||||
if (((Cap + 1) & ~Cap) != 0x40)
|
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
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KSynchronizationObject
|
class KSynchronizationObject : KAutoObject
|
||||||
{
|
{
|
||||||
public LinkedList<KThread> WaitingThreads;
|
public LinkedList<KThread> WaitingThreads;
|
||||||
|
|
||||||
protected Horizon System;
|
public KSynchronizationObject(Horizon System) : base(System)
|
||||||
|
|
||||||
public KSynchronizationObject(Horizon System)
|
|
||||||
{
|
{
|
||||||
this.System = System;
|
|
||||||
|
|
||||||
WaitingThreads = new LinkedList<KThread>();
|
WaitingThreads = new LinkedList<KThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
InvalidPriority = 0xe001,
|
InvalidPriority = 0xe001,
|
||||||
InvalidCpuCore = 0xe201,
|
InvalidCpuCore = 0xe201,
|
||||||
InvalidHandle = 0xe401,
|
InvalidHandle = 0xe401,
|
||||||
|
UserCopyFailed = 0xe601,
|
||||||
InvalidCombination = 0xe801,
|
InvalidCombination = 0xe801,
|
||||||
TimedOut = 0xea01,
|
TimedOut = 0xea01,
|
||||||
Cancelled = 0xec01,
|
Cancelled = 0xec01,
|
||||||
MaximumExceeded = 0xee01,
|
MaximumExceeded = 0xee01,
|
||||||
InvalidEnumValue = 0xf001,
|
InvalidEnumValue = 0xf001,
|
||||||
|
NotFound = 0xf201,
|
||||||
InvalidThread = 0xf401,
|
InvalidThread = 0xf401,
|
||||||
InvalidState = 0xfa01,
|
InvalidState = 0xfa01,
|
||||||
ReservedValue = 0xfc01,
|
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)
|
public SvcHandler(Switch Device, KProcess Process)
|
||||||
{
|
{
|
||||||
SvcFuncs = new Dictionary<int, SvcFunc>()
|
SvcFuncs = new Dictionary<int, SvcFunc>()
|
||||||
|
@ -77,6 +75,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{ 0x1f, SvcConnectToNamedPort },
|
{ 0x1f, SvcConnectToNamedPort },
|
||||||
{ 0x21, SvcSendSyncRequest },
|
{ 0x21, SvcSendSyncRequest },
|
||||||
{ 0x22, SvcSendSyncRequestWithUserBuffer },
|
{ 0x22, SvcSendSyncRequestWithUserBuffer },
|
||||||
|
{ 0x24, GetProcessId64 },
|
||||||
{ 0x25, SvcGetThreadId },
|
{ 0x25, SvcGetThreadId },
|
||||||
{ 0x26, SvcBreak },
|
{ 0x26, SvcBreak },
|
||||||
{ 0x27, SvcOutputDebugString },
|
{ 0x27, SvcOutputDebugString },
|
||||||
|
@ -87,7 +86,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{ 0x33, SvcGetThreadContext3 },
|
{ 0x33, SvcGetThreadContext3 },
|
||||||
{ 0x34, SvcWaitForAddress },
|
{ 0x34, SvcWaitForAddress },
|
||||||
{ 0x35, SvcSignalToAddress },
|
{ 0x35, SvcSignalToAddress },
|
||||||
{ 0x45, CreateEvent64 }
|
{ 0x45, CreateEvent64 },
|
||||||
|
{ 0x65, GetProcessList64 },
|
||||||
|
{ 0x6f, GetSystemInfo64 },
|
||||||
|
{ 0x70, CreatePort64 },
|
||||||
|
{ 0x71, ManageNamedPort64 }
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Device = Device;
|
this.Device = Device;
|
||||||
|
@ -96,11 +99,6 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
this.Memory = Process.CpuMemory;
|
this.Memory = Process.CpuMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SvcHandler()
|
|
||||||
{
|
|
||||||
Rng = new Random();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SvcCall(object sender, InstExceptionEventArgs e)
|
public void SvcCall(object sender, InstExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
CpuThreadState ThreadState = (CpuThreadState)sender;
|
CpuThreadState ThreadState = (CpuThreadState)sender;
|
||||||
|
|
|
@ -14,10 +14,6 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private const int AllowedCpuIdBitmask = 0b1111;
|
|
||||||
|
|
||||||
private const bool EnableProcessDebugging = false;
|
|
||||||
|
|
||||||
private void SvcExitProcess(CpuThreadState ThreadState)
|
private void SvcExitProcess(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
System.Scheduler.GetCurrentProcess().Terminate();
|
System.Scheduler.GetCurrentProcess().Terminate();
|
||||||
|
@ -252,6 +248,41 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
IpcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
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)
|
private void SvcBreak(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Reason = (long)ThreadState.X0;
|
long Reason = (long)ThreadState.X0;
|
||||||
|
@ -289,7 +320,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private void GetInfo64(CpuThreadState ThreadState)
|
private void GetInfo64(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long StackPtr = (long)ThreadState.X0;
|
long StackPtr = (long)ThreadState.X0;
|
||||||
int Id = (int)ThreadState.X1;
|
uint Id = (uint)ThreadState.X1;
|
||||||
int Handle = (int)ThreadState.X2;
|
int Handle = (int)ThreadState.X2;
|
||||||
long SubId = (long)ThreadState.X3;
|
long SubId = (long)ThreadState.X3;
|
||||||
|
|
||||||
|
@ -299,7 +330,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ThreadState.X1 = (ulong)Value;
|
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;
|
Value = 0;
|
||||||
|
|
||||||
|
@ -465,12 +496,15 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value = System.Scheduler.GetCurrentProcess().RandomEntropy[SubId];
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
|
||||||
|
Value = CurrentProcess.RandomEntropy[SubId];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case unchecked((int)0xf0000002u):
|
case 0xf0000002u:
|
||||||
{
|
{
|
||||||
if (SubId < -1 || SubId > 3)
|
if (SubId < -1 || SubId > 3)
|
||||||
{
|
{
|
||||||
|
@ -553,5 +587,241 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
return Result;
|
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);
|
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
|
||||||
|
|
||||||
|
Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{Handle:x8}");
|
||||||
|
|
||||||
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
|
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
|
||||||
|
|
||||||
if (SyncObj == null)
|
if (SyncObj == null)
|
||||||
|
|
|
@ -9,10 +9,112 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
class ProgramLoader
|
class ProgramLoader
|
||||||
{
|
{
|
||||||
|
private const bool AslrEnabled = true;
|
||||||
|
|
||||||
private const int ArgsHeaderSize = 8;
|
private const int ArgsHeaderSize = 8;
|
||||||
private const int ArgsDataSize = 0x9000;
|
private const int ArgsDataSize = 0x9000;
|
||||||
private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize;
|
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(
|
public static bool LoadStaticObjects(
|
||||||
Horizon System,
|
Horizon System,
|
||||||
Npdm MetaData,
|
Npdm MetaData,
|
||||||
|
@ -66,8 +168,6 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
int PersonalMmHeapPagesCount = MetaData.PersonalMmHeapSize / KMemoryManager.PageSize;
|
int PersonalMmHeapPagesCount = MetaData.PersonalMmHeapSize / KMemoryManager.PageSize;
|
||||||
|
|
||||||
KProcess Process = new KProcess(System);
|
|
||||||
|
|
||||||
ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
|
ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
|
||||||
MetaData.TitleName,
|
MetaData.TitleName,
|
||||||
MetaData.ProcessCategory,
|
MetaData.ProcessCategory,
|
||||||
|
@ -97,6 +197,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KProcess Process = new KProcess(System);
|
||||||
|
|
||||||
Result = Process.Initialize(
|
Result = Process.Initialize(
|
||||||
CreationInfo,
|
CreationInfo,
|
||||||
MetaData.ACI0.KernelAccessControl.Capabilities,
|
MetaData.ACI0.KernelAccessControl.Capabilities,
|
||||||
|
@ -114,31 +216,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading image {Index} at 0x{NsoBase[Index]:x16}...");
|
Logger.PrintInfo(LogClass.Loader, $"Loading image {Index} at 0x{NsoBase[Index]:x16}...");
|
||||||
|
|
||||||
IExecutable StaticObject = StaticObjects[Index];
|
Result = LoadIntoMemory(Process, StaticObjects[Index], NsoBase[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);
|
|
||||||
|
|
||||||
if (Result != KernelResult.Success)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -153,9 +235,58 @@ namespace Ryujinx.HLE.HOS
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Processes.AddLast(Process);
|
System.Processes.Add(Process.Pid, Process);
|
||||||
|
|
||||||
return true;
|
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);
|
BackwardsReader Reader = new BackwardsReader(Input);
|
||||||
|
|
||||||
int AdditionalDecLength = Reader.ReadInt32();
|
int AdditionalDecLength = Reader.ReadInt32();
|
||||||
|
@ -49,14 +51,18 @@ namespace Ryujinx.HLE.Loaders.Compression
|
||||||
|
|
||||||
Input.Seek(12 - StartOffset, SeekOrigin.Current);
|
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;
|
int DecPos = Dec.Length;
|
||||||
|
|
||||||
byte Mask = 0;
|
byte Mask = 0;
|
||||||
byte Header = 0;
|
byte Header = 0;
|
||||||
|
|
||||||
while (DecPos > 0)
|
while (DecPos > DecompressionStart)
|
||||||
{
|
{
|
||||||
if ((Mask >>= 1) == 0)
|
if ((Mask >>= 1) == 0)
|
||||||
{
|
{
|
||||||
|
@ -75,19 +81,19 @@ namespace Ryujinx.HLE.Loaders.Compression
|
||||||
int Length = (Pair >> 12) + 3;
|
int Length = (Pair >> 12) + 3;
|
||||||
int Position = (Pair & 0xfff) + 3;
|
int Position = (Pair & 0xfff) + 3;
|
||||||
|
|
||||||
if (Position - Length >= DecPos)
|
DecPos -= Length;
|
||||||
|
|
||||||
|
if (Length <= Position)
|
||||||
{
|
{
|
||||||
int SrcPos = DecPos + Position;
|
int SrcPos = DecPos + Position;
|
||||||
|
|
||||||
DecPos -= Length;
|
|
||||||
|
|
||||||
Buffer.BlockCopy(Dec, SrcPos, Dec, DecPos, Length);
|
Buffer.BlockCopy(Dec, SrcPos, Dec, DecPos, Length);
|
||||||
}
|
}
|
||||||
else
|
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[] RO { get; }
|
||||||
byte[] Data { get; }
|
byte[] Data { get; }
|
||||||
|
|
||||||
int Mod0Offset { get; }
|
|
||||||
int TextOffset { get; }
|
int TextOffset { get; }
|
||||||
int ROOffset { get; }
|
int ROOffset { get; }
|
||||||
int DataOffset { get; }
|
int DataOffset { get; }
|
||||||
|
int BssOffset { get; }
|
||||||
int BssSize { get; }
|
int BssSize { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class KernelInitialProcess
|
class KernelInitialProcess : IExecutable
|
||||||
{
|
{
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
@ -14,6 +14,10 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
public byte MainThreadPriority { get; private set; }
|
public byte MainThreadPriority { get; private set; }
|
||||||
public byte DefaultProcessorId { 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[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
public byte[] Data { 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 BssOffset { get; private set; }
|
||||||
public int BssSize { get; private set; }
|
public int BssSize { get; private set; }
|
||||||
|
|
||||||
|
public int MainThreadStackSize { get; private set; }
|
||||||
|
|
||||||
|
public int[] Capabilities { get; private set; }
|
||||||
|
|
||||||
private struct SegmentHeader
|
private struct SegmentHeader
|
||||||
{
|
{
|
||||||
public int Offset { get; private set; }
|
public int Offset { get; private set; }
|
||||||
|
@ -67,6 +75,10 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
byte Reserved = Reader.ReadByte();
|
byte Reserved = Reader.ReadByte();
|
||||||
byte Flags = Reader.ReadByte();
|
byte Flags = Reader.ReadByte();
|
||||||
|
|
||||||
|
Is64Bits = (Flags & 0x08) != 0;
|
||||||
|
Addr39Bits = (Flags & 0x10) != 0;
|
||||||
|
IsService = (Flags & 0x20) != 0;
|
||||||
|
|
||||||
SegmentHeader[] Segments = new SegmentHeader[6];
|
SegmentHeader[] Segments = new SegmentHeader[6];
|
||||||
|
|
||||||
for (int Index = 0; Index < Segments.Length; Index++)
|
for (int Index = 0; Index < Segments.Length; Index++)
|
||||||
|
@ -78,17 +90,26 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
Reader.ReadInt32());
|
Reader.ReadInt32());
|
||||||
}
|
}
|
||||||
|
|
||||||
Reader.ReadBytes(0x20);
|
|
||||||
|
|
||||||
Text = ReadSegment(Segments[0], Input);
|
|
||||||
RO = ReadSegment(Segments[1], Input);
|
|
||||||
Data = ReadSegment(Segments[2], Input);
|
|
||||||
|
|
||||||
TextOffset = Segments[0].Offset;
|
TextOffset = Segments[0].Offset;
|
||||||
ROOffset = Segments[1].Offset;
|
ROOffset = Segments[1].Offset;
|
||||||
DataOffset = Segments[2].Offset;
|
DataOffset = Segments[2].Offset;
|
||||||
BssOffset = Segments[3].Offset;
|
BssOffset = Segments[3].Offset;
|
||||||
BssSize = Segments[3].DecompressedSize;
|
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)
|
private byte[] ReadSegment(SegmentHeader Header, Stream Input)
|
||||||
|
@ -97,7 +118,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
|
||||||
Input.Seek(End, SeekOrigin.Begin);
|
Input.Seek(End, SeekOrigin.Begin);
|
||||||
|
|
||||||
byte[] Data = BackwardsLz.Decompress(Input);
|
byte[] Data = BackwardsLz.Decompress(Input, Header.DecompressedSize);
|
||||||
|
|
||||||
Input.Seek(End, SeekOrigin.Begin);
|
Input.Seek(End, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
public int DataOffset { get; private set; }
|
public int DataOffset { get; private set; }
|
||||||
public int BssSize { get; private set; }
|
public int BssSize { get; private set; }
|
||||||
|
|
||||||
|
public int BssOffset => DataOffset + Data.Length;
|
||||||
|
|
||||||
public ulong SourceAddress { get; private set; }
|
public ulong SourceAddress { get; private set; }
|
||||||
public ulong BssAddress { 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[] RO { get; private set; }
|
||||||
public byte[] Data { get; private set; }
|
public byte[] Data { get; private set; }
|
||||||
|
|
||||||
public int Mod0Offset { get; private set; }
|
|
||||||
public int TextOffset { get; private set; }
|
public int TextOffset { get; private set; }
|
||||||
public int ROOffset { get; private set; }
|
public int ROOffset { get; private set; }
|
||||||
public int DataOffset { get; private set; }
|
public int DataOffset { get; private set; }
|
||||||
public int BssSize { get; private set; }
|
public int BssSize { get; private set; }
|
||||||
|
|
||||||
|
public int BssOffset => DataOffset + Data.Length;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
private enum NsoFlags
|
private enum NsoFlags
|
||||||
{
|
{
|
||||||
|
@ -103,15 +104,6 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
Data = Lz4.Decompress(Data, DataDecSize);
|
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
Add a link
Reference in a new issue