Some improvements to the memory manager, implement back guest stack trace printing

This commit is contained in:
gdkchan 2018-11-10 11:29:00 -03:00
parent e6f2b1940a
commit f79ce1271c
26 changed files with 1421 additions and 422 deletions

View file

@ -22,6 +22,11 @@ namespace Ryujinx.Common
return Value & -(long)Size;
}
public static long DivRoundUp(long Value, int Dividend)
{
return (Value + Dividend - 1) / Dividend;
}
public static bool IsPowerOfTwo32(int Value)
{
return Value != 0 && (Value & (Value - 1)) == 0;

View file

@ -78,13 +78,17 @@ namespace Ryujinx.HLE.HOS
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
internal long HidBaseAddress { get; private set; }
public Horizon(Switch Device)
{
this.Device = Device;
State = new SystemStateMgr();
ResourceLimit = new KResourceLimit();
ResourceLimit = new KResourceLimit(this);
KernelInit.InitializeResourceLimit(ResourceLimit);
MemoryRegions = KernelInit.GetMemoryRegions();
@ -112,20 +116,27 @@ namespace Ryujinx.HLE.HOS
KernelInitialized = true;
if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) ||
!Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
{
throw new InvalidOperationException();
}
KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.Service];
HidSharedMem = new KSharedMemory(HidPA, HidSize);
FontSharedMem = new KSharedMemory(FontPA, FontSize);
long HidPA = Region.Address;
long FontPA = Region.Address + HidSize;
HidBaseAddress = HidPA - DramMemoryMap.DramBase;
KPageList HidPageList = new KPageList();
KPageList FontPageList = new KPageList();
HidPageList .AddRange(HidPA, HidSize / KMemoryManager.PageSize);
FontPageList.AddRange(FontPA, FontSize / KMemoryManager.PageSize);
HidSharedMem = new KSharedMemory(HidPageList, 0, 0, MemoryPermission.Read);
FontSharedMem = new KSharedMemory(FontPageList, 0, 0, MemoryPermission.Read);
AppletState = new AppletStateMgr(this);
AppletState.SetFocus(true);
Font = new SharedFontManager(Device, FontSharedMem.PA);
Font = new SharedFontManager(Device, FontPA - DramMemoryMap.DramBase);
VsyncEvent = new KEvent(this);

View file

@ -10,5 +10,6 @@ namespace Ryujinx.HLE.HOS.Kernel
public const long SlabHeapBase = KernelReserveBase + 0x85000;
public const long SlapHeapSize = 0xa21000;
public const long SlabHeapEnd = SlabHeapBase + SlapHeapSize;
}
}

View file

@ -0,0 +1,303 @@
using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
using Ryujinx.HLE.Loaders;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
class HleProcessDebugger
{
private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24;
private KProcess Owner;
private class Image
{
public long BaseAddress { get; private set; }
public ElfSym[] Symbols { get; private set; }
public Image(long BaseAddress, ElfSym[] Symbols)
{
this.BaseAddress = BaseAddress;
this.Symbols = Symbols;
}
}
private List<Image> Images;
private int Loaded;
public HleProcessDebugger(KProcess Owner)
{
this.Owner = Owner;
Images = new List<Image>();
}
public void PrintGuestStackTrace(CpuThreadState ThreadState)
{
EnsureLoaded();
StringBuilder Trace = new StringBuilder();
Trace.AppendLine("Guest stack trace:");
void AppendTrace(long Address)
{
Image Image = GetImage(Address, out int ImageIndex);
if (Image == null || !TryGetSubName(Image, Address, out string SubName))
{
SubName = $"Sub{Address:x16}";
}
else if (SubName.StartsWith("_Z"))
{
SubName = Demangler.Parse(SubName);
}
if (Image != null)
{
long Offset = Address - Image.BaseAddress;
string ImageName = GetGuessedNsoNameFromIndex(ImageIndex);
string ImageNameAndOffset = $"[{Owner.Name}] {ImageName}:0x{Offset:x8}";
Trace.AppendLine($" {ImageNameAndOffset} {SubName}");
}
else
{
Trace.AppendLine($" [{Owner.Name}] ??? {SubName}");
}
}
long FramePointer = (long)ThreadState.X29;
while (FramePointer != 0)
{
if (!Owner.CpuMemory.IsMapped(FramePointer))
{
break;
}
//Note: This is the return address, we need to subtract one instruction
//worth of bytes to get the branch instruction address.
AppendTrace(Owner.CpuMemory.ReadInt64(FramePointer + 8) - 4);
FramePointer = Owner.CpuMemory.ReadInt64(FramePointer);
}
Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
}
private bool TryGetSubName(Image Image, long Address, out string Name)
{
Address -= Image.BaseAddress;
int Left = 0;
int Right = Image.Symbols.Length - 1;
while (Left <= Right)
{
int Size = Right - Left;
int Middle = Left + (Size >> 1);
ElfSym Symbol = Image.Symbols[Middle];
long EndAddr = Symbol.Value + Symbol.Size;
if ((ulong)Address >= (ulong)Symbol.Value && (ulong)Address < (ulong)EndAddr)
{
Name = Symbol.Name;
return true;
}
if ((ulong)Address < (ulong)Symbol.Value)
{
Right = Middle - 1;
}
else
{
Left = Middle + 1;
}
}
Name = null;
return false;
}
private Image GetImage(long Address, out int Index)
{
lock (Images)
{
for (Index = Images.Count - 1; Index >= 0; Index--)
{
if ((ulong)Address >= (ulong)Images[Index].BaseAddress)
{
return Images[Index];
}
}
}
return null;
}
private string GetGuessedNsoNameFromIndex(int Index)
{
if ((uint)Index > 11)
{
return "???";
}
if (Index == 0)
{
return "rtld";
}
else if (Index == 1)
{
return "main";
}
else if (Index == GetImagesCount() - 1)
{
return "sdk";
}
else
{
return "subsdk" + (Index - 2);
}
}
private int GetImagesCount()
{
lock (Images)
{
return Images.Count;
}
}
private void EnsureLoaded()
{
if (Interlocked.CompareExchange(ref Loaded, 1, 0) == 0)
{
ScanMemoryForTextSegments();
}
}
private void ScanMemoryForTextSegments()
{
long OldAddress = 0;
long Address = 0;
while ((ulong)Address >= (ulong)OldAddress)
{
KMemoryInfo Info = Owner.MemoryManager.QueryMemory(Address);
if (Info.State == MemoryState.Reserved)
{
break;
}
if (Info.State == MemoryState.CodeStatic && Info.Permission == MemoryPermission.ReadAndExecute)
{
LoadMod0Symbols(Owner.CpuMemory, Info.Address);
}
OldAddress = Address;
Address = Info.Address + Info.Size;
}
}
private void LoadMod0Symbols(MemoryManager Memory, long TextOffset)
{
long Mod0Offset = TextOffset + Memory.ReadUInt32(TextOffset + 4);
if (Mod0Offset < TextOffset || !Memory.IsMapped(Mod0Offset) || (Mod0Offset & 3) != 0)
{
return;
}
Dictionary<ElfDynTag, long> Dynamic = new Dictionary<ElfDynTag, long>();
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
if (Mod0Magic != Mod0)
{
return;
}
long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset;
long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset;
long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset;
long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
while (true)
{
long TagVal = Memory.ReadInt64(DynamicOffset + 0);
long Value = Memory.ReadInt64(DynamicOffset + 8);
DynamicOffset += 0x10;
ElfDynTag Tag = (ElfDynTag)TagVal;
if (Tag == ElfDynTag.DT_NULL)
{
break;
}
Dynamic[Tag] = Value;
}
long StrTblAddr = TextOffset + Dynamic[ElfDynTag.DT_STRTAB];
long SymTblAddr = TextOffset + Dynamic[ElfDynTag.DT_SYMTAB];
long SymEntSize = Dynamic[ElfDynTag.DT_SYMENT];
List<ElfSym> Symbols = new List<ElfSym>();
while ((ulong)SymTblAddr < (ulong)StrTblAddr)
{
ElfSym Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
Symbols.Add(Sym);
SymTblAddr += SymEntSize;
}
lock (Images)
{
Images.Add(new Image(TextOffset, Symbols.OrderBy(x => x.Value).ToArray()));
}
}
private ElfSym GetSymbol(MemoryManager Memory, long Address, long StrTblAddr)
{
int NameIndex = Memory.ReadInt32(Address + 0);
int Info = Memory.ReadByte (Address + 4);
int Other = Memory.ReadByte (Address + 5);
int SHIdx = Memory.ReadInt16(Address + 6);
long Value = Memory.ReadInt64(Address + 8);
long Size = Memory.ReadInt64(Address + 16);
string Name = string.Empty;
for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
{
Name += (char)Chr;
}
return new ElfSym(Name, Info, Other, SHIdx, Value, Size);
}
}
}

View file

@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
static class KConditionVariable
{
public static void Wait(Horizon System, LinkedList<KThread> ThreadList, object Mutex, long Timeout)
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
System.CriticalSection.Enter();
Monitor.Exit(Mutex);
CurrentThread.Withholder = ThreadList;
CurrentThread.Reschedule(ThreadSchedState.Paused);
CurrentThread.WithholderNode = ThreadList.AddLast(CurrentThread);
if (CurrentThread.ShallBeTerminated ||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
{
ThreadList.Remove(CurrentThread.WithholderNode);
CurrentThread.Reschedule(ThreadSchedState.Running);
CurrentThread.Withholder = null;
System.CriticalSection.Leave();
}
else
{
if (Timeout > 0)
{
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
}
System.CriticalSection.Leave();
if (Timeout > 0)
{
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
}
Monitor.Enter(Mutex);
}
public static void NotifyAll(Horizon System, LinkedList<KThread> ThreadList)
{
System.CriticalSection.Enter();
LinkedListNode<KThread> Node = ThreadList.First;
for (; Node != null; Node = ThreadList.First)
{
KThread Thread = Node.Value;
ThreadList.Remove(Thread.WithholderNode);
Thread.Withholder = null;
Thread.Reschedule(ThreadSchedState.Running);
}
System.CriticalSection.Leave();
}
}
}

View file

@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryBlock
{
public long BasePosition { get; set; }
public long PagesCount { get; set; }
public long BaseAddress { get; set; }
public long PagesCount { get; set; }
public MemoryState State { get; set; }
public MemoryPermission Permission { get; set; }
@ -13,17 +13,17 @@ namespace Ryujinx.HLE.HOS.Kernel
public int DeviceRefCount { get; set; }
public KMemoryBlock(
long BasePosition,
long BaseAddress,
long PagesCount,
MemoryState State,
MemoryPermission Permission,
MemoryAttribute Attribute)
{
this.BasePosition = BasePosition;
this.PagesCount = PagesCount;
this.State = State;
this.Attribute = Attribute;
this.Permission = Permission;
this.BaseAddress = BaseAddress;
this.PagesCount = PagesCount;
this.State = State;
this.Attribute = Attribute;
this.Permission = Permission;
}
public KMemoryInfo GetInfo()
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel
long Size = PagesCount * KMemoryManager.PageSize;
return new KMemoryInfo(
BasePosition,
BaseAddress,
Size,
State,
Permission,

View file

@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryInfo
{
public long Position { get; private set; }
public long Size { get; private set; }
public long Address { get; private set; }
public long Size { get; private set; }
public MemoryState State { get; private set; }
public MemoryPermission Permission { get; private set; }
@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public int DeviceRefCount { get; private set; }
public KMemoryInfo(
long Position,
long Address,
long Size,
MemoryState State,
MemoryPermission Permission,
@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Kernel
int IpcRefCount,
int DeviceRefCount)
{
this.Position = Position;
this.Address = Address;
this.Size = Size;
this.State = State;
this.Attribute = Attribute;

File diff suppressed because it is too large Load diff

View file

@ -6,10 +6,11 @@ namespace Ryujinx.HLE.HOS.Kernel
{
private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
private long Address;
private long EndAddr;
private long Size;
private int BlockOrdersCount;
public long Address { get; private set; }
public long EndAddr { get; private set; }
public long Size { get; private set; }
private int BlockOrdersCount;
private KMemoryRegionBlock[] Blocks;
@ -292,7 +293,18 @@ namespace Ryujinx.HLE.HOS.Kernel
return KernelResult.OutOfMemory;
}
public void FreePages(long Address, long PagesCount)
public void FreePages(KPageList PageList)
{
lock (Blocks)
{
foreach (KPageNode PageNode in PageList)
{
FreePages(PageNode.Address, PageNode.PagesCount);
}
}
}
private void FreePages(long Address, long PagesCount)
{
long EndAddr = Address + PagesCount * KMemoryManager.PageSize;

View file

@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KPageList : IEnumerable<KPageNode>
{
private LinkedList<KPageNode> Nodes;
public LinkedList<KPageNode> Nodes { get; private set; }
public KPageList()
{
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KPageNode LastNode = Nodes.Last.Value;
if (LastNode.Address + LastNode.PagesCount * 4096 == Address)
if (LastNode.Address + LastNode.PagesCount * KMemoryManager.PageSize == Address)
{
Address = LastNode.Address;
PagesCount += LastNode.PagesCount;
@ -47,6 +47,26 @@ namespace Ryujinx.HLE.HOS.Kernel
return Sum;
}
public bool IsEqual(KPageList Other)
{
LinkedListNode<KPageNode> ThisNode = Nodes.First;
LinkedListNode<KPageNode> OtherNode = Other.Nodes.First;
while (ThisNode != null && OtherNode != null)
{
if (ThisNode.Value.Address != OtherNode.Value.Address ||
ThisNode.Value.PagesCount != OtherNode.Value.PagesCount)
{
return false;
}
ThisNode = ThisNode.Next;
OtherNode = OtherNode.Next;
}
return ThisNode == null && OtherNode == null;
}
public IEnumerator<KPageNode> GetEnumerator()
{
return Nodes.GetEnumerator();

View file

@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private bool Signaled;
private bool UseSystemMemBlocks;
private string Name;
public string Name { get; private set; }
private int ThreadCount;
@ -79,6 +79,8 @@ namespace Ryujinx.HLE.HOS.Kernel
private SvcHandler SvcHandler;
public HleProcessDebugger Debugger { get; private set; }
public KProcess(Horizon System) : base(System)
{
ProcessLock = new object();
@ -93,8 +95,6 @@ namespace Ryujinx.HLE.HOS.Kernel
FullTlsPages = new SortedDictionary<long, KTlsPageInfo>();
FreeTlsPages = new SortedDictionary<long, KTlsPageInfo>();
ResourceLimit = new KResourceLimit();
Capabilities = new KProcessCapabilities();
RandomEntropy = new long[KScheduler.CpuCoresCount];
@ -103,7 +103,11 @@ namespace Ryujinx.HLE.HOS.Kernel
Translator = new Translator();
Translator.CpuTrace += CpuTraceHandler;
SvcHandler = new SvcHandler(System.Device, this);
Debugger = new HleProcessDebugger(this);
}
public KernelResult InitializeKip(
@ -175,7 +179,10 @@ namespace Ryujinx.HLE.HOS.Kernel
KResourceLimit ResourceLimit,
MemoryRegion MemRegion)
{
long PersonalMmHeapSize = CreationInfo.PersonalMmHeapPagesCount * KMemoryManager.PageSize;
this.ResourceLimit = ResourceLimit;
this.MemRegion = MemRegion;
long PersonalMmHeapSize = GetPersonalMmHeapSize(CreationInfo.PersonalMmHeapPagesCount, MemRegion);
long CodePagesCount = (long)(uint)CreationInfo.CodePagesCount;
@ -273,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return Result;
}
private bool ValidateCodeAddressAndSize(long CodeAddress, long CodeSize)
private bool ValidateCodeAddressAndSize(long Address, long Size)
{
long CodeRegionStart;
long CodeRegionSize;
@ -298,18 +305,18 @@ namespace Ryujinx.HLE.HOS.Kernel
default: throw new InvalidOperationException("Invalid address space width on memory manager.");
}
long CodeEndAddr = CodeAddress + CodeSize;
long EndAddr = Address + Size;
long CodeRegionEnd = CodeRegionStart + CodeRegionSize;
if ((ulong)CodeEndAddr <= (ulong)CodeAddress ||
(ulong)CodeEndAddr - 1 > (ulong)CodeRegionEnd - 1)
if ((ulong)EndAddr <= (ulong)Address ||
(ulong)EndAddr - 1 > (ulong)CodeRegionEnd - 1)
{
return false;
}
if (MemoryManager.InsideHeapRegion(CodeAddress, CodeSize) ||
MemoryManager.InsideMapRegion (CodeAddress, CodeSize))
if (MemoryManager.InsideHeapRegion(Address, Size) ||
MemoryManager.InsideMapRegion (Address, Size))
{
return false;
}
@ -445,8 +452,9 @@ namespace Ryujinx.HLE.HOS.Kernel
}
long RegionStart = MemoryManager.TlsIoRegionStart;
long RegionSize = MemoryManager.TlsIoRegionEnd - RegionStart;
long RegionPagesCount = (MemoryManager.TlsIoRegionEnd - RegionStart) / KMemoryManager.PageSize;
long RegionPagesCount = (long)((ulong)RegionSize / KMemoryManager.PageSize);
KernelResult Result = MemoryManager.AllocateOrMapPa(
1,
@ -606,7 +614,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
long StackBottom = StackTop - MainThreadStackSize;
long StackPagesCount = MainThreadStackSize / KMemoryManager.PageSize;
long StackPagesCount = (long)((ulong)MainThreadStackSize / KMemoryManager.PageSize);
MemoryManager.UnmapForKernel(StackBottom, StackPagesCount, MemoryState.Stack);
}
@ -617,10 +625,12 @@ namespace Ryujinx.HLE.HOS.Kernel
if (StackSizeRounded != 0)
{
long StackPagesCount = StackSizeRounded / KMemoryManager.PageSize;
long StackPagesCount = (long)((ulong)StackSizeRounded / KMemoryManager.PageSize);
long RegionStart = MemoryManager.StackRegionStart;
long RegionPagesCount = (MemoryManager.StackRegionEnd - RegionStart) / KMemoryManager.PageSize;
long RegionStart = MemoryManager.StackRegionStart;
long RegionSize = MemoryManager.StackRegionEnd - RegionStart;
long RegionPagesCount = (long)((ulong)RegionSize / KMemoryManager.PageSize);
Result = MemoryManager.AllocateOrMapPa(
StackPagesCount,
@ -715,7 +725,7 @@ namespace Ryujinx.HLE.HOS.Kernel
CleanUpForError();
} */
MainThread.TimeUp();
MainThread.Reschedule(ThreadSchedState.Running);
return Result;
}
@ -772,8 +782,20 @@ namespace Ryujinx.HLE.HOS.Kernel
public long GetMemoryCapacity()
{
//TODO: Personal Mm Heap.
return 0xcd500000;
long TotalCapacity = ResourceLimit.GetRemainingValue(LimitableResource.Memory);
TotalCapacity += MemoryManager.GetTotalHeapSize();
TotalCapacity += GetPersonalMmHeapSize();
TotalCapacity += ImageSize + MainThreadStackSize;
if ((ulong)TotalCapacity <= (ulong)MemoryUsageCapacity)
{
return TotalCapacity;
}
return MemoryUsageCapacity;
}
public long GetMemoryUsage()
@ -782,6 +804,21 @@ namespace Ryujinx.HLE.HOS.Kernel
return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize();
}
private long GetPersonalMmHeapSize()
{
return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, MemRegion);
}
private static long GetPersonalMmHeapSize(long PersonalMmHeapPagesCount, MemoryRegion MemRegion)
{
if (MemRegion == MemoryRegion.Applet)
{
return 0;
}
return PersonalMmHeapPagesCount * KMemoryManager.PageSize;
}
public void AddThread(KThread Thread)
{
lock (ThreadingLock)
@ -881,5 +918,10 @@ namespace Ryujinx.HLE.HOS.Kernel
System.CriticalSection.Leave();
}
private void CpuTraceHandler(object sender, EventArgs e)
{
System.Scheduler.GetCurrentThread().PrintGuestStackTrace();
}
}
}

View file

@ -1,16 +1,135 @@
using Ryujinx.Common;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel
{
class KResourceLimit
{
private long[] Current;
private long[] Limit;
private long[] Available;
private object LockObj;
private LinkedList<KThread> WaitingThreads;
private int WaitingThreadsCount;
private Horizon System;
public KResourceLimit(Horizon System)
{
Current = new long[(int)LimitableResource.Count];
Limit = new long[(int)LimitableResource.Count];
Available = new long[(int)LimitableResource.Count];
LockObj = new object();
WaitingThreads = new LinkedList<KThread>();
this.System = System;
}
public bool Reserve(LimitableResource Resource, long Amount)
{
//TODO.
return true;
//Wait 10 seconds for the resource if no timeout is specified.
return Reserve(Resource, Amount, KTimeManager.ConvertMillisecondsToNanoseconds(10000));
}
public bool Reserve(LimitableResource Resource, long Amount, long Timeout)
{
long EndTimePoint = KTimeManager.ConvertNanosecondsToMilliseconds(Timeout);
EndTimePoint += PerformanceCounter.ElapsedMilliseconds;
bool Success = false;
int Index = GetIndex(Resource);
lock (LockObj)
{
long NewCurrent = Current[Index] + Amount;
while (NewCurrent > Limit[Index] && Available[Index] + Amount <= Limit[Index])
{
WaitingThreadsCount++;
KConditionVariable.Wait(System, WaitingThreads, LockObj, Timeout);
WaitingThreadsCount--;
NewCurrent = Current[Index] + Amount;
if (Timeout >= 0 && PerformanceCounter.ElapsedMilliseconds > EndTimePoint)
{
break;
}
}
if (NewCurrent <= Limit[Index])
{
Current[Index] = NewCurrent;
Success = true;
}
}
return Success;
}
public void Release(LimitableResource Resource, long Amount)
{
//TODO.
Release(Resource, Amount, Amount);
}
private void Release(LimitableResource Resource, long UsedAmount, long AvailableAmount)
{
int Index = GetIndex(Resource);
lock (LockObj)
{
Current [Index] -= UsedAmount;
Available[Index] -= AvailableAmount;
if (WaitingThreadsCount > 0)
{
KConditionVariable.NotifyAll(System, WaitingThreads);
}
}
}
public long GetRemainingValue(LimitableResource Resource)
{
int Index = GetIndex(Resource);
lock (LockObj)
{
return Limit[Index] - Current[Index];
}
}
public KernelResult SetLimitValue(LimitableResource Resource, long Limit)
{
int Index = GetIndex(Resource);
lock (LockObj)
{
if (Current[Index] <= Limit)
{
this.Limit[Index] = Limit;
return KernelResult.Success;
}
else
{
return KernelResult.InvalidState;
}
}
}
private static int GetIndex(LimitableResource Resource)
{
return (int)Resource;
}
}
}

View file

@ -1,14 +1,68 @@
using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Kernel
{
class KSharedMemory
{
public long PA { get; private set; }
public long Size { get; private set; }
private KPageList PageList;
public KSharedMemory(long PA, long Size)
private long OwnerPid;
private MemoryPermission OwnerPermission;
private MemoryPermission UserPermission;
public KSharedMemory(
KPageList PageList,
long OwnerPid,
MemoryPermission OwnerPermission,
MemoryPermission UserPermission)
{
this.PA = PA;
this.Size = Size;
this.PageList = PageList;
this.OwnerPid = OwnerPid;
this.OwnerPermission = OwnerPermission;
this.UserPermission = UserPermission;
}
public KernelResult MapIntoProcess(
KMemoryManager MemoryManager,
long Address,
long Size,
KProcess Process,
MemoryPermission Permission)
{
long PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
if (PageList.GetPagesCount() != PagesCountRounded)
{
return KernelResult.InvalidSize;
}
MemoryPermission ExpectedPermission = Process.Pid == OwnerPid
? OwnerPermission
: UserPermission;
if (Permission != ExpectedPermission)
{
return KernelResult.InvalidPermission;
}
return MemoryManager.MapPages(Address, PageList, MemoryState.SharedMemory, Permission);
}
public KernelResult UnmapFromProcess(
KMemoryManager MemoryManager,
long Address,
long Size,
KProcess Process)
{
long PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
if (PageList.GetPagesCount() != PagesCountRounded)
{
return KernelResult.InvalidSize;
}
return MemoryManager.UnmapPages(Address, PageList, MemoryState.SharedMemory);
}
}
}

View file

@ -32,7 +32,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
private LinkedListNode<KThread> WithholderNode;
public LinkedList<KThread> Withholder { get; set; }
public LinkedListNode<KThread> WithholderNode { get; set; }
public LinkedListNode<KThread> ProcessListNode { get; set; }
@ -223,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Kernel
SetNewSchedFlags(ThreadSchedState.Running);
Result = 0;
Result = KernelResult.Success;
break;
}
@ -980,11 +981,12 @@ namespace Ryujinx.HLE.HOS.Kernel
public void TimeUp()
{
System.CriticalSection.Enter();
ReleaseAndResume();
}
SetNewSchedFlags(ThreadSchedState.Running);
System.CriticalSection.Leave();
public void PrintGuestStackTrace()
{
Owner.Debugger.PrintGuestStackTrace(Context.ThreadState);
}
private void ThreadFinishedHandler(object sender, EventArgs e)

View file

@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel
WaitEvent.Set();
}
private long ConvertNanosecondsToMilliseconds(long Timeout)
public static long ConvertNanosecondsToMilliseconds(long Timeout)
{
Timeout /= 1000000;
@ -68,6 +68,11 @@ namespace Ryujinx.HLE.HOS.Kernel
return Timeout;
}
public static long ConvertMillisecondsToNanoseconds(long Timeout)
{
return Timeout * 1000000;
}
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
{
lock (WaitingObjects)

View file

@ -1,7 +1,36 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel
{
static class KernelInit
{
public static void InitializeResourceLimit(KResourceLimit ResourceLimit)
{
void EnsureSuccess(KernelResult Result)
{
if (Result != KernelResult.Success)
{
throw new InvalidOperationException($"Unexpected result \"{Result}\".");
}
}
int KernelMemoryCfg = 0;
long RamSize = GetRamSize(KernelMemoryCfg);
EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Memory, RamSize));
EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Thread, 800));
EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Event, 700));
EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.TransferMemory, 200));
EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Session, 900));
if (!ResourceLimit.Reserve(LimitableResource.Memory, 0) ||
!ResourceLimit.Reserve(LimitableResource.Memory, 0x60000))
{
throw new InvalidOperationException("Unexpected failure reserving memory on resource limit.");
}
}
public static KMemoryRegionManager[] GetMemoryRegions()
{
KMemoryArrange Arrange = GetMemoryArrange();
@ -17,10 +46,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion Region)
{
return new KMemoryRegionManager(
Region.Address,
Region.Size,
Region.EndAddr);
return new KMemoryRegionManager(Region.Address, Region.Size, Region.EndAddr);
}
private static KMemoryArrange GetMemoryArrange()
@ -31,14 +57,7 @@ namespace Ryujinx.HLE.HOS.Kernel
int KernelMemoryCfg = 0;
ulong RamSize;
switch ((KernelMemoryCfg >> 16) & 3)
{
case 1: RamSize = 0x180000000; break;
case 2: RamSize = 0x200000000; break;
default: RamSize = 0x100000000; break;
}
ulong RamSize = (ulong)GetRamSize(KernelMemoryCfg);
long RamPart0;
long RamPart1;
@ -97,11 +116,21 @@ namespace Ryujinx.HLE.HOS.Kernel
//Note: There is an extra region used by the kernel, however
//since we are doing HLE we are not going to use that memory, so give all
//the remaining memory space to services.
long ServiceRgSize = NvServicesRg.Address - DramMemoryMap.KernelReserveBase;
long ServiceRgSize = NvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
ServiceRg = new KMemoryArrangeRegion(DramMemoryMap.KernelReserveBase, ServiceRgSize);
ServiceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, ServiceRgSize);
return new KMemoryArrange(ServiceRg, NvServicesRg, AppletRg, ApplicationRg);
}
private static long GetRamSize(int KernelMemoryCfg)
{
switch ((KernelMemoryCfg >> 16) & 3)
{
case 1: return 0x180000000;
case 2: return 0x200000000;
default: return 0x100000000;
}
}
}
}

View file

@ -10,6 +10,7 @@ namespace Ryujinx.HLE.HOS.Kernel
OutOfMemory = 0xd001,
HandleTableFull = 0xd201,
InvalidMemState = 0xd401,
InvalidPermission = 0xd801,
InvalidMemRange = 0xdc01,
InvalidPriority = 0xe001,
InvalidCpuCore = 0xe201,

View file

@ -2,10 +2,12 @@ namespace Ryujinx.HLE.HOS.Kernel
{
enum LimitableResource : byte
{
Memory,
Thread,
Event,
TransferMemory,
Session
Memory = 0,
Thread = 1,
Event = 2,
TransferMemory = 3,
Session = 4,
Count = 5
}
}

View file

@ -105,8 +105,6 @@ namespace Ryujinx.HLE.HOS.Kernel
{
CpuThreadState ThreadState = (CpuThreadState)sender;
//Process.GetThread(ThreadState.Tpidr).LastPc = e.Position;
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{
Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");

View file

@ -141,9 +141,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.Map(Src, Dst, Size);
KernelResult Result = Process.MemoryManager.Map(Dst, Src, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@ -202,9 +202,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.Unmap(Src, Dst, Size);
KernelResult Result = Process.MemoryManager.Unmap(Dst, Src, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@ -219,7 +219,7 @@ namespace Ryujinx.HLE.HOS.Kernel
KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Address);
Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
@ -234,13 +234,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcMapSharedMemory(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
long Position = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Handle = (int)ThreadState.X0;
long Address = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
if (!PageAligned(Position))
if (!PageAligned(Address))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -256,9 +256,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Position + Size) <= (ulong)Position)
if ((ulong)(Address + Size) <= (ulong)Address)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -276,7 +276,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
@ -287,29 +289,25 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
if (!InsideAddrSpace(Address, Size) || InsideMapRegion(Address, Size) || InsideHeapRegion(Address, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
if (SharedMemory.Size != Size)
KernelResult Result = SharedMemory.MapIntoProcess(
CurrentProcess.MemoryManager,
Address,
Size,
CurrentProcess,
Permission);
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
return;
}
long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position);
if (Result != 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@ -348,7 +346,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
@ -368,11 +368,15 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size);
KernelResult Result = SharedMemory.UnmapFromProcess(
CurrentProcess.MemoryManager,
Position,
Size,
CurrentProcess);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@ -472,9 +476,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size);
KernelResult Result = Process.MemoryManager.MapPhysicalMemory(Position, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@ -523,9 +527,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size);
KernelResult Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}

View file

@ -1,9 +1,9 @@
using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Services;
using Ryujinx.Common.Logging;
using System;
using System.Threading;
@ -243,9 +243,20 @@ namespace Ryujinx.HLE.HOS.Kernel
long Unknown = (long)ThreadState.X1;
long Info = (long)ThreadState.X2;
//Process.PrintStackTrace(ThreadState);
KThread CurrentThread = System.Scheduler.GetCurrentThread();
throw new GuestBrokeExecutionException();
if ((Reason & (1 << 31)) == 0)
{
CurrentThread.PrintGuestStackTrace();
throw new GuestBrokeExecutionException();
}
else
{
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
CurrentThread.PrintGuestStackTrace();
}
}
private void SvcOutputDebugString(CpuThreadState ThreadState)

View file

@ -48,8 +48,10 @@ namespace Ryujinx.HLE.HOS.Kernel
return KernelResult.InvalidPriority;
}
long Timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
if (CurrentProcess.ResourceLimit != null &&
!CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1))
!CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, Timeout))
{
return KernelResult.ResLimitExceeded;
}

View file

@ -78,15 +78,34 @@ namespace Ryujinx.HLE.HOS
0,
PersonalMmHeapPagesCount);
KernelResult Result = Process.Initialize(
KernelResult Result;
KResourceLimit ResourceLimit = new KResourceLimit(System);
long ApplicationRgSize = System.MemoryRegions[(int)MemoryRegion.Application].Size;
Result = ResourceLimit.SetLimitValue(LimitableResource.Memory, ApplicationRgSize);
Result |= ResourceLimit.SetLimitValue(LimitableResource.Thread, 608);
Result |= ResourceLimit.SetLimitValue(LimitableResource.Event, 700);
Result |= ResourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128);
Result |= ResourceLimit.SetLimitValue(LimitableResource.Session, 894);
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.Loader, $"Resource limit initialization returned error \"{Result}\".");
return false;
}
Result = Process.Initialize(
CreationInfo,
MetaData.ACI0.KernelAccessControl.Capabilities,
System.ResourceLimit,
ResourceLimit,
MemoryRegion.Application);
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.KernelSvc, $"Process initialization returned error \"{Result}\".");
Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
return false;
}
@ -107,7 +126,7 @@ namespace Ryujinx.HLE.HOS
Process.CpuMemory.WriteBytes(ROStart, StaticObject.RO);
Process.CpuMemory.WriteBytes(DataStart, StaticObject.Data);
MemoryHelper.FillWithZeros(Process.CpuMemory, BssStart, (int)(BssEnd - BssStart));
MemoryHelper.FillWithZeros(Process.CpuMemory, BssStart, (int)(BssEnd - BssStart));
Process.MemoryManager.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute);
Process.MemoryManager.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read);
@ -118,7 +137,7 @@ namespace Ryujinx.HLE.HOS
if (Result != KernelResult.Success)
{
Logger.PrintError(LogClass.KernelSvc, $"Process start returned error \"{Result}\".");
Logger.PrintError(LogClass.Loader, $"Process start returned error \"{Result}\".");
return false;
}

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Utilities;
using System;
@ -318,14 +319,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
//Context.Process.RemoveProgram(Info.NroMappedAddress);
long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize);
KernelResult Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize);
if (Result == 0 && Info.Executable.BssSize != 0)
if (Result == KernelResult.Success && Info.Executable.BssSize != 0)
{
Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress + Info.TotalSize - Info.Executable.BssSize, Info.Executable.BssAddress, Info.Executable.BssSize);
}
return Result;
return (long)Result;
}
}

View file

@ -56,7 +56,7 @@ namespace Ryujinx.HLE
Statistics = new PerformanceStatistics();
Hid = new Hid(this, System.HidSharedMem.PA);
Hid = new Hid(this, System.HidBaseAddress);
VsyncEvent = new AutoResetEvent(true);
}

View file

@ -26,7 +26,7 @@ namespace Ryujinx
{ LogLevel.Error, ConsoleColor.Red }
};
_messageQueue = new BlockingCollection<LogEventArgs>();
_messageQueue = new BlockingCollection<LogEventArgs>(10);
_consoleLock = new object();
@ -58,7 +58,7 @@ namespace Ryujinx
string formattedTime = e.Time.ToString(@"hh\:mm\:ss\.fff");
string currentThread = Thread.CurrentThread.ManagedThreadId.ToString("d4");
string message = formattedTime + " | " + currentThread + " " + e.Message;
if (_logColors.TryGetValue(e.Level, out ConsoleColor color))