diff --git a/Ryujinx.Common/BitUtils.cs b/Ryujinx.Common/BitUtils.cs index 84d2072052..fa49082bb8 100644 --- a/Ryujinx.Common/BitUtils.cs +++ b/Ryujinx.Common/BitUtils.cs @@ -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; diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 8741f99ac7..0b94a11abe 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -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); diff --git a/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs index f580d0a76e..50b33be2df 100644 --- a/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs +++ b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs @@ -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; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs new file mode 100644 index 0000000000..5207950c36 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs @@ -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 Images; + + private int Loaded; + + public HleProcessDebugger(KProcess Owner) + { + this.Owner = Owner; + + Images = new List(); + } + + 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 Dynamic = new Dictionary(); + + 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 Symbols = new List(); + + 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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs new file mode 100644 index 0000000000..1c95f81168 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs @@ -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 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 ThreadList) + { + System.CriticalSection.Enter(); + + LinkedListNode 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(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs index 6100741b5c..48fb7a723d 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs @@ -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, diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs index 9b73b32b1e..7a557bb866 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs @@ -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; diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs index 388002a44c..13552cf934 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs @@ -311,7 +311,7 @@ namespace Ryujinx.HLE.HOS.Kernel private void InitializeBlocks(long AddrSpaceStart, long AddrSpaceEnd) { - long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize; + long AddrSpacePagesCount = (long)((ulong)(AddrSpaceEnd - AddrSpaceStart) / PageSize); InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped); } @@ -324,19 +324,95 @@ namespace Ryujinx.HLE.HOS.Kernel { long PagesCount = PageList.GetPagesCount(); - if (!IsUnmapped(Address, PagesCount * PageSize)) + long Size = PagesCount * PageSize; + + if (!ValidateRegionForState(Address, Size, State)) { return KernelResult.InvalidMemState; } - KernelResult Result = MmuMapPages(Address, PageList); - - if (Result == KernelResult.Success) + lock (Blocks) { - InsertBlock(Address, PagesCount, State, Permission); + if (!IsUnmapped(Address, PagesCount * PageSize)) + { + return KernelResult.InvalidMemState; + } + + KernelResult Result = MapPages(Address, PageList, Permission); + + if (Result == KernelResult.Success) + { + InsertBlock(Address, PagesCount, State, Permission); + } + + return Result; + } + } + + public KernelResult UnmapPages(long Address, KPageList PageList, MemoryState StateExpected) + { + long PagesCount = PageList.GetPagesCount(); + + long Size = PagesCount * PageSize; + + long EndAddr = Address + Size; + + long AddrSpacePagesCount = (long)((ulong)(AddrSpaceEnd - AddrSpaceStart) / PageSize); + + if ((ulong)AddrSpaceStart > (ulong)Address) + { + return KernelResult.InvalidMemState; } - return Result; + if ((ulong)AddrSpacePagesCount < (ulong)PagesCount) + { + return KernelResult.InvalidMemState; + } + + if ((ulong)(EndAddr - 1) > (ulong)(AddrSpaceEnd - 1)) + { + return KernelResult.InvalidMemState; + } + + lock (Blocks) + { + KPageList CurrentPageList = new KPageList(); + + AddVaRangeToPageList(CurrentPageList, Address, Address + Size); + + if (!CurrentPageList.IsEqual(PageList)) + { + return KernelResult.InvalidMemRange; + } + + if (CheckRange( + Address, + Size, + MemoryState.Mask, + StateExpected, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out _, + out _)) + { + KernelResult Result = MmuUnmap(Address, PagesCount); + + if (Result == KernelResult.Success) + { + InsertBlock(Address, PagesCount, MemoryState.Unmapped); + } + + return Result; + } + else + { + return KernelResult.InvalidMemState; + } + } } public KernelResult MapNormalMemory(long Address, long Size, MemoryPermission Permission) @@ -388,7 +464,7 @@ namespace Ryujinx.HLE.HOS.Kernel long RemainingPages = RegionPagesCount - NeededPagesCount; - long AslrMaxOffset = ((RemainingPages + ReservedPagesCount) * PageSize) / Alignment; + long AslrMaxOffset = (long)((ulong)((RemainingPages + ReservedPagesCount) * PageSize) / (ulong)Alignment); for (int Attempt = 0; Attempt < 8; Attempt++) { @@ -403,14 +479,13 @@ namespace Ryujinx.HLE.HOS.Kernel continue; } - long BlkStartAddr = Info.Position + ReservedPagesCount * PageSize; - - long BlkEndAddr = Info.Position + Info.Size; + long CurrBaseAddr = Info.Address + ReservedPagesCount * PageSize; + long CurrEndAddr = Info.Address + Info.Size; if ((ulong)Address >= (ulong)RegionStart && - (ulong)Address >= (ulong)BlkStartAddr && + (ulong)Address >= (ulong)CurrBaseAddr && (ulong)EndAddr - 1 <= (ulong)RegionEndAddr - 1 && - (ulong)EndAddr - 1 <= (ulong)BlkEndAddr - 1) + (ulong)EndAddr - 1 <= (ulong)CurrEndAddr - 1) { break; } @@ -506,12 +581,12 @@ namespace Ryujinx.HLE.HOS.Kernel } } - public long UnmapProcessCodeMemory(long Dst, long Src, long Size) + public KernelResult UnmapProcessCodeMemory(long Dst, long Src, long Size) { + long PagesCount = (long)((ulong)Size / PageSize); + lock (Blocks) { - long PagesCount = Size / PageSize; - bool Success = CheckRange( Dst, Size, @@ -542,21 +617,28 @@ namespace Ryujinx.HLE.HOS.Kernel if (Success) { + KernelResult Result = MmuUnmap(Dst, PagesCount); + + if (Result != KernelResult.Success) + { + return Result; + } + InsertBlock(Dst, PagesCount, MemoryState.Unmapped); InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); - CpuMemory.Unmap(Dst, Size); - - return 0; + return KernelResult.Success; + } + else + { + return KernelResult.InvalidMemState; } } - - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } - public KernelResult SetHeapSize(long Size, out long Position) + public KernelResult SetHeapSize(long Size, out long Address) { - Position = 0; + Address = 0; if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart)) { @@ -572,7 +654,7 @@ namespace Ryujinx.HLE.HOS.Kernel lock (Blocks) { - long PagesCount = DiffSize / PageSize; + long PagesCount = (long)((ulong)DiffSize / PageSize); KMemoryRegionManager Region = GetMemoryRegionManager(); @@ -628,15 +710,9 @@ namespace Ryujinx.HLE.HOS.Kernel return KernelResult.InvalidMemState; } - long PagesCount = DiffSize / PageSize; + long PagesCount = (long)((ulong)DiffSize / PageSize); - KernelResult Result = DoMmuOperation( - FreeAddr, - PagesCount, - 0, - false, - MemoryPermission.None, - MemoryOperation.Unmap); + KernelResult Result = MmuUnmap(FreeAddr, PagesCount); if (Result != KernelResult.Success) { @@ -649,7 +725,7 @@ namespace Ryujinx.HLE.HOS.Kernel CurrentHeapAddr = HeapRegionStart + Size; - Position = HeapRegionStart; + Address = HeapRegionStart; return KernelResult.Success; } @@ -678,7 +754,7 @@ namespace Ryujinx.HLE.HOS.Kernel } public long SetMemoryAttribute( - long Position, + long Address, long Size, MemoryAttribute AttributeMask, MemoryAttribute AttributeValue) @@ -686,7 +762,7 @@ namespace Ryujinx.HLE.HOS.Kernel lock (Blocks) { if (CheckRange( - Position, + Address, Size, MemoryState.AttributeChangeAllowed, MemoryState.AttributeChangeAllowed, @@ -699,12 +775,12 @@ namespace Ryujinx.HLE.HOS.Kernel out MemoryPermission Permission, out MemoryAttribute Attribute)) { - long PagesCount = Size / PageSize; + long PagesCount = (long)((ulong)Size / PageSize); Attribute &= ~AttributeMask; Attribute |= AttributeMask & AttributeValue; - InsertBlock(Position, PagesCount, State, Permission, Attribute); + InsertBlock(Address, PagesCount, State, Permission, Attribute); return 0; } @@ -713,14 +789,14 @@ namespace Ryujinx.HLE.HOS.Kernel return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } - public KMemoryInfo QueryMemory(long Position) + public KMemoryInfo QueryMemory(long Address) { - if ((ulong)Position >= (ulong)AddrSpaceStart && - (ulong)Position < (ulong)AddrSpaceEnd) + if ((ulong)Address >= (ulong)AddrSpaceStart && + (ulong)Address < (ulong)AddrSpaceEnd) { lock (Blocks) { - return FindBlock(Position).GetInfo(); + return FindBlock(Address).GetInfo(); } } else @@ -736,7 +812,7 @@ namespace Ryujinx.HLE.HOS.Kernel } } - public long Map(long Src, long Dst, long Size) + public KernelResult Map(long Dst, long Src, long Size) { bool Success; @@ -760,18 +836,41 @@ namespace Ryujinx.HLE.HOS.Kernel if (Success) { - long PagesCount = Size / PageSize; + long PagesCount = (long)((ulong)Size / PageSize); + + KPageList PageList = new KPageList(); + + AddVaRangeToPageList(PageList, Src, Src + Size); + + KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None); + + if (Result != KernelResult.Success) + { + return Result; + } + + Result = MapPages(Dst, PageList, MemoryPermission.ReadAndWrite); + + if (Result != KernelResult.Success) + { + if (MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure reverting memory permission."); + } + + return Result; + } InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed); InsertBlock(Dst, PagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite); - long PA = CpuMemory.GetPhysicalAddress(Src); - - CpuMemory.Map(Dst, PA, Size); + return KernelResult.Success; + } + else + { + return KernelResult.InvalidMemState; } } - - return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } public KernelResult UnmapForKernel(long Address, long PagesCount, MemoryState StateExpected) @@ -794,18 +893,23 @@ namespace Ryujinx.HLE.HOS.Kernel out _, out _)) { - CpuMemory.Unmap(Address, Size); + KernelResult Result = MmuUnmap(Address, PagesCount); - InsertBlock(Address, PagesCount, MemoryState.Unmapped); + if (Result == KernelResult.Success) + { + InsertBlock(Address, PagesCount, MemoryState.Unmapped); + } return KernelResult.Success; } + else + { + return KernelResult.InvalidMemState; + } } - - return KernelResult.InvalidMemState; } - public long Unmap(long Src, long Dst, long Size) + public KernelResult Unmap(long Dst, long Src, long Size) { bool Success; @@ -836,79 +940,58 @@ namespace Ryujinx.HLE.HOS.Kernel MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, out _, - out _, + out MemoryPermission DstPermission, out _); if (Success) { - long PagesCount = Size / PageSize; + long PagesCount = (long)((ulong)Size / PageSize); + + KPageList SrcPageList = new KPageList(); + KPageList DstPageList = new KPageList(); + + AddVaRangeToPageList(SrcPageList, Src, Src + Size); + AddVaRangeToPageList(DstPageList, Dst, Dst + Size); + + if (!DstPageList.IsEqual(SrcPageList)) + { + return KernelResult.InvalidMemRange; + } + + KernelResult Result = MmuUnmap(Dst, PagesCount); + + if (Result != KernelResult.Success) + { + return Result; + } + + Result = MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite); + + if (Result != KernelResult.Success) + { + MapPages(Dst, DstPageList, DstPermission); + + return Result; + } InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite); InsertBlock(Dst, PagesCount, MemoryState.Unmapped); - CpuMemory.Unmap(Dst, Size); + return KernelResult.Success; } - } - - return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); - } - - public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position) - { - lock (Blocks) - { - if (IsUnmapped(Position, SharedMemory.Size)) + else { - long PagesCount = SharedMemory.Size / PageSize; - - InsertBlock(Position, PagesCount, MemoryState.SharedMemory, Permission); - - CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size); - - return 0; + return KernelResult.InvalidMemState; } } - - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } - public long UnmapSharedMemory(long Position, long Size) + public long ReserveTransferMemory(long Address, long Size, MemoryPermission Permission) { lock (Blocks) { if (CheckRange( - Position, - Size, - MemoryState.Mask, - MemoryState.SharedMemory, - MemoryPermission.None, - MemoryPermission.None, - MemoryAttribute.Mask, - MemoryAttribute.None, - MemoryAttribute.IpcAndDeviceMapped, - out MemoryState State, - out _, - out _)) - { - long PagesCount = Size / PageSize; - - InsertBlock(Position, PagesCount, MemoryState.Unmapped); - - CpuMemory.Unmap(Position, Size); - - return 0; - } - } - - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); - } - - public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission) - { - lock (Blocks) - { - if (CheckRange( - Position, + Address, Size, MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, @@ -921,11 +1004,11 @@ namespace Ryujinx.HLE.HOS.Kernel out _, out MemoryAttribute Attribute)) { - long PagesCount = Size / PageSize; + long PagesCount = (long)((ulong)Size / PageSize); Attribute |= MemoryAttribute.Borrowed; - InsertBlock(Position, PagesCount, State, Permission, Attribute); + InsertBlock(Address, PagesCount, State, Permission, Attribute); return 0; } @@ -934,12 +1017,12 @@ namespace Ryujinx.HLE.HOS.Kernel return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } - public long ResetTransferMemory(long Position, long Size) + public long ResetTransferMemory(long Address, long Size) { lock (Blocks) { if (CheckRange( - Position, + Address, Size, MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, @@ -952,9 +1035,9 @@ namespace Ryujinx.HLE.HOS.Kernel out _, out _)) { - long PagesCount = Size / PageSize; + long PagesCount = (long)((ulong)Size / PageSize); - InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite); + InsertBlock(Address, PagesCount, State, MemoryPermission.ReadAndWrite); return 0; } @@ -963,12 +1046,12 @@ namespace Ryujinx.HLE.HOS.Kernel return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } - public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission) + public KernelResult SetProcessMemoryPermission(long Address, long Size, MemoryPermission Permission) { lock (Blocks) { if (CheckRange( - Position, + Address, Size, MemoryState.ProcessPermissionChangeAllowed, MemoryState.ProcessPermissionChangeAllowed, @@ -1003,21 +1086,34 @@ namespace Ryujinx.HLE.HOS.Kernel if (NewState != OldState || Permission != OldPermission) { - long PagesCount = Size / PageSize; + long PagesCount = (long)((ulong)Size / PageSize); - InsertBlock(Position, PagesCount, NewState, Permission); + MemoryOperation Operation = (Permission & MemoryPermission.Execute) != 0 + ? MemoryOperation.ChangePermsAndAttributes + : MemoryOperation.ChangePermRw; + + KernelResult Result = DoMmuOperation(Address, PagesCount, 0, false, Permission, Operation); + + if (Result != KernelResult.Success) + { + return Result; + } + + InsertBlock(Address, PagesCount, NewState, Permission); } - return 0; + return KernelResult.Success; + } + else + { + return KernelResult.InvalidMemState; } } - - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } - public long MapPhysicalMemory(long Position, long Size) + public KernelResult MapPhysicalMemory(long Address, long Size) { - long End = Position + Size; + long EndAddr = Address + Size; lock (Blocks) { @@ -1025,9 +1121,7 @@ namespace Ryujinx.HLE.HOS.Kernel KMemoryInfo Info; - LinkedListNode BaseNode = FindBlockNode(Position); - - LinkedListNode Node = BaseNode; + LinkedListNode Node = FindBlockNode(Address); do { @@ -1035,57 +1129,49 @@ namespace Ryujinx.HLE.HOS.Kernel if (Info.State != MemoryState.Unmapped) { - MappedSize += GetSizeInRange(Info, Position, End); + MappedSize += GetSizeInRange(Info, Address, EndAddr); } Node = Node.Next; } - while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + while ((ulong)(Info.Address + Info.Size) < (ulong)EndAddr && Node != null); if (MappedSize == Size) { - return 0; + return KernelResult.Success; } long RemainingSize = Size - MappedSize; - if (!Allocator.TryAllocate(RemainingSize, out long PA)) + long RemainingPages = (long)((ulong)RemainingSize / PageSize); + + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + if (CurrentProcess.ResourceLimit != null && + !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, RemainingSize)) { - return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + return KernelResult.ResLimitExceeded; } - Node = BaseNode; + KMemoryRegionManager Region = GetMemoryRegionManager(); - do + KernelResult Result = Region.AllocatePages(RemainingPages, AslrDisabled, out KPageList PageList); + + if (Result != KernelResult.Success) { - Info = Node.Value.GetInfo(); + CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, RemainingSize); - if (Info.State == MemoryState.Unmapped) - { - long CurrSize = GetSizeInRange(Info, Position, End); - - long MapPosition = Info.Position; - - if ((ulong)MapPosition < (ulong)Position) - { - MapPosition = Position; - } - - CpuMemory.Map(MapPosition, PA, CurrSize); - - PA += CurrSize; - } - - Node = Node.Next; + return Result; } - while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + + MapPhysicalMemory(PageList, Address, EndAddr); PersonalMmHeapUsage += RemainingSize; - long PagesCount = Size / PageSize; + long PagesCount = (long)((ulong)Size / PageSize); InsertBlock( - Position, + Address, PagesCount, MemoryState.Unmapped, MemoryPermission.None, @@ -1095,22 +1181,26 @@ namespace Ryujinx.HLE.HOS.Kernel MemoryAttribute.None); } - return 0; + return KernelResult.Success; } - public long UnmapPhysicalMemory(long Position, long Size) + public KernelResult UnmapPhysicalMemory(long Address, long Size) { - long End = Position + Size; + long EndAddr = Address + Size; lock (Blocks) { + //Scan, ensure that the region can be unmapped (all blocks are heap or + //already unmapped), fill pages list for freeing memory. long HeapMappedSize = 0; - long CurrPosition = Position; + KPageList PageList = new KPageList(); KMemoryInfo Info; - LinkedListNode Node = FindBlockNode(CurrPosition); + LinkedListNode BaseNode = FindBlockNode(Address); + + LinkedListNode Node = BaseNode; do { @@ -1120,90 +1210,207 @@ namespace Ryujinx.HLE.HOS.Kernel { if (Info.Attribute != MemoryAttribute.None) { - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + return KernelResult.InvalidMemState; } - HeapMappedSize += GetSizeInRange(Info, Position, End); + long BlockSize = GetSizeInRange(Info, Address, EndAddr); + long BlockAddress = GetAddrInRange(Info, Address); + + AddVaRangeToPageList(PageList, BlockAddress, BlockAddress + BlockSize); + + HeapMappedSize += BlockSize; } else if (Info.State != MemoryState.Unmapped) { - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + return KernelResult.InvalidMemState; } Node = Node.Next; } - while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + while ((ulong)(Info.Address + Info.Size) < (ulong)EndAddr && Node != null); if (HeapMappedSize == 0) { - return 0; + return KernelResult.Success; } - PersonalMmHeapUsage -= HeapMappedSize; + //Try to unmap all the heap mapped memory inside range. + KernelResult Result = KernelResult.Success; - long PagesCount = Size / PageSize; + Node = BaseNode; - InsertBlock(Position, PagesCount, MemoryState.Unmapped); - - FreePages(Position, PagesCount); - - CpuMemory.Unmap(Position, Size); - - return 0; - } - } - - private long GetSizeInRange(KMemoryInfo Info, long Start, long End) - { - long CurrEnd = Info.Size + Info.Position; - long CurrSize = Info.Size; - - if ((ulong)Info.Position < (ulong)Start) - { - CurrSize -= Start - Info.Position; - } - - if ((ulong)CurrEnd > (ulong)End) - { - CurrSize -= CurrEnd - End; - } - - return CurrSize; - } - - private void FreePages(long Position, long PagesCount) - { - for (long Page = 0; Page < PagesCount; Page++) - { - long VA = Position + Page * PageSize; - - if (!CpuMemory.IsMapped(VA)) + do { - continue; + Info = Node.Value.GetInfo(); + + if (Info.State == MemoryState.Heap) + { + long BlockSize = GetSizeInRange(Info, Address, EndAddr); + long BlockAddress = GetAddrInRange(Info, Address); + + long BlockPagesCount = (long)((ulong)BlockSize / PageSize); + + Result = MmuUnmap(BlockAddress, BlockPagesCount); + + if (Result != KernelResult.Success) + { + //If we failed to unmap, we need to remap everything back again. + MapPhysicalMemory(PageList, Address, BlockAddress + BlockSize); + + break; + } + } + + Node = Node.Next; + } + while ((ulong)(Info.Address + Info.Size) < (ulong)EndAddr && Node != null); + + if (Result == KernelResult.Success) + { + GetMemoryRegionManager().FreePages(PageList); + + PersonalMmHeapUsage -= HeapMappedSize; + + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, HeapMappedSize); + + long PagesCount = (long)((ulong)Size / PageSize); + + InsertBlock(Address, PagesCount, MemoryState.Unmapped); } - long PA = CpuMemory.GetPhysicalAddress(VA); - - Allocator.Free(PA, PageSize); + return Result; } } - public bool HleIsUnmapped(long Position, long Size) + private void MapPhysicalMemory(KPageList PageList, long Address, long EndAddr) + { + KMemoryInfo Info; + + LinkedListNode Node = FindBlockNode(Address); + + LinkedListNode PageListNode = PageList.Nodes.First; + + KPageNode PageNode = PageListNode.Value; + + long SrcPa = PageNode.Address; + long SrcPaPages = PageNode.PagesCount; + + do + { + Info = Node.Value.GetInfo(); + + if (Info.State == MemoryState.Unmapped) + { + long BlockSize = GetSizeInRange(Info, Address, EndAddr); + + long DstVaPages = (long)((ulong)BlockSize / PageSize); + + long DstVa = GetAddrInRange(Info, Address); + + while (DstVaPages > 0) + { + if (PageNode.PagesCount == 0) + { + PageListNode = PageListNode.Next; + + PageNode = PageListNode.Value; + + SrcPa = PageNode.Address; + SrcPaPages = PageNode.PagesCount; + } + + long PagesCount = PageNode.PagesCount; + + if (PagesCount > DstVaPages) + { + PagesCount = DstVaPages; + } + + DoMmuOperation( + DstVa, + PagesCount, + SrcPa, + true, + MemoryPermission.ReadAndWrite, + MemoryOperation.MapPa); + + DstVa += PagesCount * PageSize; + SrcPa += PagesCount * PageSize; + SrcPaPages -= PagesCount; + DstVaPages -= PagesCount; + } + } + + Node = Node.Next; + } + while ((ulong)(Info.Address + Info.Size) < (ulong)EndAddr && Node != null); + } + + private static long GetSizeInRange(KMemoryInfo Info, long Start, long End) + { + long EndAddr = Info.Size + Info.Address; + long Size = Info.Size; + + if ((ulong)Info.Address < (ulong)Start) + { + Size -= Start - Info.Address; + } + + if ((ulong)EndAddr > (ulong)End) + { + Size -= EndAddr - End; + } + + return Size; + } + + private static long GetAddrInRange(KMemoryInfo Info, long Start) + { + if ((ulong)Info.Address < (ulong)Start) + { + return Start; + } + + return Info.Address; + } + + private void AddVaRangeToPageList(KPageList PageList, long Start, long End) + { + long Address = Start; + + while ((ulong)Address < (ulong)End) + { + KernelResult Result = ConvertVaToPa(Address, out long Pa); + + if (Result != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure translating virtual address."); + } + + PageList.AddRange(Pa, 1); + + Address += PageSize; + } + } + + public bool HleIsUnmapped(long Address, long Size) { bool Result = false; lock (Blocks) { - Result = IsUnmapped(Position, Size); + Result = IsUnmapped(Address, Size); } return Result; } - private bool IsUnmapped(long Position, long Size) + private bool IsUnmapped(long Address, long Size) { return CheckRange( - Position, + Address, Size, MemoryState.Mask, MemoryState.Unmapped, @@ -1218,7 +1425,7 @@ namespace Ryujinx.HLE.HOS.Kernel } private bool CheckRange( - long Position, + long Address, long Size, MemoryState StateMask, MemoryState StateExpected, @@ -1231,24 +1438,44 @@ namespace Ryujinx.HLE.HOS.Kernel out MemoryPermission OutPermission, out MemoryAttribute OutAttribute) { - KMemoryInfo BlkInfo = FindBlock(Position).GetInfo(); + long EndAddr = Address + Size - 1; - ulong Start = (ulong)Position; - ulong End = (ulong)Size + Start; + LinkedListNode Node = FindBlockNode(Address); - if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size)) + KMemoryInfo Info = Node.Value.GetInfo(); + + MemoryState FirstState = Info.State; + MemoryPermission FirstPermission = Info.Permission; + MemoryAttribute FirstAttribute = Info.Attribute; + + do { - if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected && - (BlkInfo.State & StateMask) == StateExpected && - (BlkInfo.Permission & PermissionMask) == PermissionExpected) + Info = Node.Value.GetInfo(); + + //Check if the block state matches what we expect. + if ( FirstState != Info.State || + FirstPermission != Info.Permission || + (Info.Attribute & AttributeMask) != AttributeExpected || + (FirstAttribute | AttributeIgnoreMask) != (Info.Attribute | AttributeIgnoreMask) || + (FirstState & StateMask) != StateExpected || + (FirstPermission & PermissionMask) != PermissionExpected) { - OutState = BlkInfo.State; - OutPermission = BlkInfo.Permission; - OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask; + break; + } + + //Check if this is the last block on the range, if so return success. + if ((ulong)EndAddr <= (ulong)(Info.Address + Info.Size - 1)) + { + OutState = FirstState; + OutPermission = FirstPermission; + OutAttribute = FirstAttribute & ~AttributeIgnoreMask; return true; } + + Node = Node.Next; } + while (Node != null); OutState = MemoryState.Unmapped; OutPermission = MemoryPermission.None; @@ -1258,7 +1485,7 @@ namespace Ryujinx.HLE.HOS.Kernel } private void InsertBlock( - long BasePosition, + long BaseAddress, long PagesCount, MemoryState OldState, MemoryPermission OldPermission, @@ -1272,8 +1499,8 @@ namespace Ryujinx.HLE.HOS.Kernel //arguments, otherwise leave it as is. OldAttribute |= MemoryAttribute.IpcAndDeviceMapped; - ulong Start = (ulong)BasePosition; - ulong End = (ulong)PagesCount * PageSize + Start; + ulong BaseAddr = (ulong)BaseAddress; + ulong EndAddr = (ulong)PagesCount * PageSize + BaseAddr; LinkedListNode Node = Blocks.First; @@ -1284,10 +1511,10 @@ namespace Ryujinx.HLE.HOS.Kernel KMemoryBlock CurrBlock = Node.Value; - ulong CurrStart = (ulong)CurrBlock.BasePosition; - ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart; + ulong CurrBaseAddr = (ulong)CurrBlock.BaseAddress; + ulong CurrEndAddr = (ulong)CurrBlock.PagesCount * PageSize + CurrBaseAddr; - if (Start < CurrEnd && CurrStart < End) + if (BaseAddr < CurrEndAddr && CurrBaseAddr < EndAddr) { MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped; @@ -1300,36 +1527,36 @@ namespace Ryujinx.HLE.HOS.Kernel continue; } - if (CurrStart >= Start && CurrEnd <= End) + if (CurrBaseAddr >= BaseAddr && CurrEndAddr <= EndAddr) { CurrBlock.State = NewState; CurrBlock.Permission = NewPermission; CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped; CurrBlock.Attribute |= NewAttribute; } - else if (CurrStart >= Start) + else if (CurrBaseAddr >= BaseAddr) { - CurrBlock.BasePosition = (long)End; + CurrBlock.BaseAddress = (long)EndAddr; - CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize); + CurrBlock.PagesCount = (long)((CurrEndAddr - EndAddr) / PageSize); - long NewPagesCount = (long)((End - CurrStart) / PageSize); + long NewPagesCount = (long)((EndAddr - CurrBaseAddr) / PageSize); NewNode = Blocks.AddBefore(Node, new KMemoryBlock( - (long)CurrStart, + (long)CurrBaseAddr, NewPagesCount, NewState, NewPermission, NewAttribute)); } - else if (CurrEnd <= End) + else if (CurrEndAddr <= EndAddr) { - CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + CurrBlock.PagesCount = (long)((BaseAddr - CurrBaseAddr) / PageSize); - long NewPagesCount = (long)((CurrEnd - Start) / PageSize); + long NewPagesCount = (long)((CurrEndAddr - BaseAddr) / PageSize); NewNode = Blocks.AddAfter(Node, new KMemoryBlock( - BasePosition, + BaseAddress, NewPagesCount, NewState, NewPermission, @@ -1337,19 +1564,19 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + CurrBlock.PagesCount = (long)((BaseAddr - CurrBaseAddr) / PageSize); - long NextPagesCount = (long)((CurrEnd - End) / PageSize); + long NextPagesCount = (long)((CurrEndAddr - EndAddr) / PageSize); NewNode = Blocks.AddAfter(Node, new KMemoryBlock( - BasePosition, + BaseAddress, PagesCount, NewState, NewPermission, NewAttribute)); Blocks.AddAfter(NewNode, new KMemoryBlock( - (long)End, + (long)EndAddr, NextPagesCount, CurrBlock.State, CurrBlock.Permission, @@ -1366,7 +1593,7 @@ namespace Ryujinx.HLE.HOS.Kernel } private void InsertBlock( - long BasePosition, + long BaseAddress, long PagesCount, MemoryState State, MemoryPermission Permission = MemoryPermission.None, @@ -1374,10 +1601,10 @@ namespace Ryujinx.HLE.HOS.Kernel { //Inserts new block at the list, replacing and spliting //existing blocks as needed. - KMemoryBlock Block = new KMemoryBlock(BasePosition, PagesCount, State, Permission, Attribute); + KMemoryBlock Block = new KMemoryBlock(BaseAddress, PagesCount, State, Permission, Attribute); - ulong Start = (ulong)BasePosition; - ulong End = (ulong)PagesCount * PageSize + Start; + ulong BaseAddr = (ulong)BaseAddress; + ulong EndAddr = (ulong)PagesCount * PageSize + BaseAddr; LinkedListNode NewNode = null; @@ -1389,26 +1616,26 @@ namespace Ryujinx.HLE.HOS.Kernel LinkedListNode NextNode = Node.Next; - ulong CurrStart = (ulong)CurrBlock.BasePosition; - ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart; + ulong CurrBaseAddr = (ulong)CurrBlock.BaseAddress; + ulong CurrEndAddr = (ulong)CurrBlock.PagesCount * PageSize + CurrBaseAddr; - if (Start < CurrEnd && CurrStart < End) + if (BaseAddr < CurrEndAddr && CurrBaseAddr < EndAddr) { - if (Start >= CurrStart && End <= CurrEnd) + if (BaseAddr >= CurrBaseAddr && EndAddr <= CurrEndAddr) { Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped; } - if (Start > CurrStart && End < CurrEnd) + if (BaseAddr > CurrBaseAddr && EndAddr < CurrEndAddr) { - CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + CurrBlock.PagesCount = (long)((BaseAddr - CurrBaseAddr) / PageSize); - long NextPagesCount = (long)((CurrEnd - End) / PageSize); + long NextPagesCount = (long)((CurrEndAddr - EndAddr) / PageSize); NewNode = Blocks.AddAfter(Node, Block); Blocks.AddAfter(NewNode, new KMemoryBlock( - (long)End, + (long)EndAddr, NextPagesCount, CurrBlock.State, CurrBlock.Permission, @@ -1416,20 +1643,20 @@ namespace Ryujinx.HLE.HOS.Kernel break; } - else if (Start <= CurrStart && End < CurrEnd) + else if (BaseAddr <= CurrBaseAddr && EndAddr < CurrEndAddr) { - CurrBlock.BasePosition = (long)End; + CurrBlock.BaseAddress = (long)EndAddr; - CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize); + CurrBlock.PagesCount = (long)((CurrEndAddr - EndAddr) / PageSize); if (NewNode == null) { NewNode = Blocks.AddBefore(Node, Block); } } - else if (Start > CurrStart && End >= CurrEnd) + else if (BaseAddr > CurrBaseAddr && EndAddr >= CurrEndAddr) { - CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + CurrBlock.PagesCount = (long)((BaseAddr - CurrBaseAddr) / PageSize); if (NewNode == null) { @@ -1462,8 +1689,8 @@ namespace Ryujinx.HLE.HOS.Kernel { KMemoryBlock Block = Node.Value; - ulong Start = (ulong)Block.BasePosition; - ulong End = (ulong)Block.PagesCount * PageSize + Start; + ulong BaseAddr = (ulong)Block.BaseAddress; + ulong EndAddr = (ulong)Block.PagesCount * PageSize + BaseAddr; if (Node.Previous != null) { @@ -1473,9 +1700,9 @@ namespace Ryujinx.HLE.HOS.Kernel { Blocks.Remove(Node.Previous); - Block.BasePosition = Previous.BasePosition; + Block.BaseAddress = Previous.BaseAddress; - Start = (ulong)Block.BasePosition; + BaseAddr = (ulong)Block.BaseAddress; } } @@ -1487,11 +1714,11 @@ namespace Ryujinx.HLE.HOS.Kernel { Blocks.Remove(Node.Next); - End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize); + EndAddr = (ulong)(Next.BaseAddress + Next.PagesCount * PageSize); } } - Block.PagesCount = (long)((End - Start) / PageSize); + Block.PagesCount = (long)((EndAddr - BaseAddr) / PageSize); } private static bool BlockStateEquals(KMemoryBlock Lhs, KMemoryBlock Rhs) @@ -1521,17 +1748,16 @@ namespace Ryujinx.HLE.HOS.Kernel KMemoryInfo Info = Node.Value.GetInfo(); - while ((ulong)RegionEndAddr >= (ulong)Info.Position) + while ((ulong)RegionEndAddr >= (ulong)Info.Address) { if (Info.State == MemoryState.Unmapped) { - long BlkStartAddr = Info.Position + ReservedSize; + long CurrBaseAddr = Info.Address + ReservedSize; + long CurrEndAddr = Info.Address + Info.Size - 1; - long BlkEndAddr = Info.Position + Info.Size - 1; + long Address = BitUtils.AlignDown(CurrBaseAddr, Alignment) + ReservedStart; - long Address = BitUtils.AlignDown(BlkStartAddr, Alignment) + ReservedStart; - - if ((ulong)BlkStartAddr > (ulong)Address) + if ((ulong)CurrBaseAddr > (ulong)Address) { Address += Alignment; } @@ -1539,7 +1765,7 @@ namespace Ryujinx.HLE.HOS.Kernel long AllocationEndAddr = Address + TotalNeededSize - 1; if ((ulong)AllocationEndAddr <= (ulong)RegionEndAddr && - (ulong)AllocationEndAddr <= (ulong)BlkEndAddr && + (ulong)AllocationEndAddr <= (ulong)CurrEndAddr && (ulong)Address < (ulong)AllocationEndAddr) { return Address; @@ -1559,15 +1785,13 @@ namespace Ryujinx.HLE.HOS.Kernel return 0; } - private KMemoryBlock FindBlock(long Position) + private KMemoryBlock FindBlock(long Address) { - return FindBlockNode(Position)?.Value; + return FindBlockNode(Address)?.Value; } - private LinkedListNode FindBlockNode(long Position) + private LinkedListNode FindBlockNode(long Address) { - ulong Addr = (ulong)Position; - lock (Blocks) { LinkedListNode Node = Blocks.First; @@ -1576,10 +1800,10 @@ namespace Ryujinx.HLE.HOS.Kernel { KMemoryBlock Block = Node.Value; - ulong Start = (ulong)Block.BasePosition; - ulong End = (ulong)Block.PagesCount * PageSize + Start; + ulong CurrBaseAddr = (ulong)Block.BaseAddress; + ulong CurrEndAddr = (ulong)Block.PagesCount * PageSize + CurrBaseAddr; - if (Start <= Addr && End - 1 >= Addr) + if (CurrBaseAddr <= (ulong)Address && CurrEndAddr - 1 >= (ulong)Address) { return Node; } @@ -1765,6 +1989,61 @@ namespace Ryujinx.HLE.HOS.Kernel } } + private KernelResult MapPages(long Address, KPageList PageList, MemoryPermission Permission) + { + long CurrAddr = Address; + + KernelResult Result = KernelResult.Success; + + foreach (KPageNode PageNode in PageList) + { + Result = DoMmuOperation( + CurrAddr, + PageNode.PagesCount, + PageNode.Address, + true, + Permission, + MemoryOperation.MapPa); + + if (Result != KernelResult.Success) + { + KMemoryInfo Info = FindBlock(CurrAddr).GetInfo(); + + long PagesCount = (long)((ulong)(Address - CurrAddr) / PageSize); + + Result = MmuUnmap(Address, PagesCount); + + break; + } + + CurrAddr += PageNode.PagesCount * PageSize; + } + + return Result; + } + + private KernelResult MmuUnmap(long Address, long PagesCount) + { + return DoMmuOperation( + Address, + PagesCount, + 0, + false, + MemoryPermission.None, + MemoryOperation.Unmap); + } + + private KernelResult MmuChangePermission(long Address, long PagesCount, MemoryPermission Permission) + { + return DoMmuOperation( + Address, + PagesCount, + 0, + false, + Permission, + MemoryOperation.ChangePermRw); + } + private KernelResult DoMmuOperation( long DstVa, long PagesCount, @@ -1773,6 +2052,11 @@ namespace Ryujinx.HLE.HOS.Kernel MemoryPermission Permission, MemoryOperation Operation) { + if (Map != (Operation == MemoryOperation.MapPa)) + { + throw new ArgumentException(nameof(Map) + " value is invalid for this operation."); + } + KernelResult Result; switch (Operation) @@ -1781,7 +2065,7 @@ namespace Ryujinx.HLE.HOS.Kernel { long Size = PagesCount * PageSize; - CpuMemory.Map(DstVa, SrcPa, Size); + CpuMemory.Map(DstVa, SrcPa - DramMemoryMap.DramBase, Size); Result = KernelResult.Success; @@ -1813,7 +2097,10 @@ namespace Ryujinx.HLE.HOS.Kernel break; } - default: throw new NotImplementedException($"Unsupported memory operation \"{Operation}\"."); + case MemoryOperation.ChangePermRw: Result = KernelResult.Success; break; + case MemoryOperation.ChangePermsAndAttributes: Result = KernelResult.Success; break; + + default: throw new ArgumentException($"Invalid operation \"{Operation}\"."); } return Result; @@ -1860,36 +2147,36 @@ namespace Ryujinx.HLE.HOS.Kernel return KernelResult.Success; } - public bool InsideAddrSpace(long Position, long Size) + public bool InsideAddrSpace(long Address, long Size) { - ulong Start = (ulong)Position; + ulong Start = (ulong)Address; ulong End = (ulong)Size + Start; return Start >= (ulong)AddrSpaceStart && End < (ulong)AddrSpaceEnd; } - public bool InsideMapRegion(long Position, long Size) + public bool InsideMapRegion(long Address, long Size) { - ulong Start = (ulong)Position; + ulong Start = (ulong)Address; ulong End = (ulong)Size + Start; return Start >= (ulong)AliasRegionStart && End < (ulong)AliasRegionEnd; } - public bool InsideHeapRegion(long Position, long Size) + public bool InsideHeapRegion(long Address, long Size) { - ulong Start = (ulong)Position; + ulong Start = (ulong)Address; ulong End = (ulong)Size + Start; return Start >= (ulong)HeapRegionStart && End < (ulong)HeapRegionEnd; } - public bool InsideNewMapRegion(long Position, long Size) + public bool InsideNewMapRegion(long Address, long Size) { - ulong Start = (ulong)Position; + ulong Start = (ulong)Address; ulong End = (ulong)Size + Start; return Start >= (ulong)StackRegionStart && diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs index 14f8f6f443..95ad302565 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs @@ -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; diff --git a/Ryujinx.HLE/HOS/Kernel/KPageList.cs b/Ryujinx.HLE/HOS/Kernel/KPageList.cs index 49f5d2cfb1..b750f33fe2 100644 --- a/Ryujinx.HLE/HOS/Kernel/KPageList.cs +++ b/Ryujinx.HLE/HOS/Kernel/KPageList.cs @@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel { class KPageList : IEnumerable { - private LinkedList Nodes; + public LinkedList 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 ThisNode = Nodes.First; + LinkedListNode 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 GetEnumerator() { return Nodes.GetEnumerator(); diff --git a/Ryujinx.HLE/HOS/Kernel/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/KProcess.cs index 3c96d4fc1e..2c349d9136 100644 --- a/Ryujinx.HLE/HOS/Kernel/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/KProcess.cs @@ -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(); FreeTlsPages = new SortedDictionary(); - 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(); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs index 0912acf608..0a8fd88dd5 100644 --- a/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs +++ b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs @@ -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 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(); + + 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; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs index cdd31667b8..5691e74a09 100644 --- a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs @@ -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); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KThread.cs b/Ryujinx.HLE/HOS/Kernel/KThread.cs index b55230ff28..f52c20d5c8 100644 --- a/Ryujinx.HLE/HOS/Kernel/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/KThread.cs @@ -32,7 +32,8 @@ namespace Ryujinx.HLE.HOS.Kernel public LinkedListNode[] SiblingsPerCore { get; private set; } - private LinkedListNode WithholderNode; + public LinkedList Withholder { get; set; } + public LinkedListNode WithholderNode { get; set; } public LinkedListNode 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) diff --git a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs index 47a3c86cfd..febd985419 100644 --- a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs @@ -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) diff --git a/Ryujinx.HLE/HOS/Kernel/KernelInit.cs b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs index 4e933f50b1..912b4b4904 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelInit.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs @@ -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; + } + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs index f33bf646a6..060fe5ccb5 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs @@ -10,6 +10,7 @@ namespace Ryujinx.HLE.HOS.Kernel OutOfMemory = 0xd001, HandleTableFull = 0xd201, InvalidMemState = 0xd401, + InvalidPermission = 0xd801, InvalidMemRange = 0xdc01, InvalidPriority = 0xe001, InvalidCpuCore = 0xe201, diff --git a/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs index 4055148361..baab422238 100644 --- a/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs +++ b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs @@ -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 } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs index c9c0ded90f..27fc377b43 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs @@ -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."); diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs index 5b5f3b8ed5..949f77b33c 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs @@ -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(Handle); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject(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(Handle); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject(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}!"); } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs index 5964fd28d7..bd6715a7f3 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs @@ -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) diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs index b40d252a0d..65bb908139 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs @@ -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; } diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 4b69b6d45b..15e50438c8 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -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; } diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs index fccbaac440..e571006436 100644 --- a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs @@ -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; } } diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 8de49ca4a8..ddcc6fddec 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -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); } diff --git a/Ryujinx/Ui/ConsoleLog.cs b/Ryujinx/Ui/ConsoleLog.cs index 1ecd4cde44..67eaa0e35f 100644 --- a/Ryujinx/Ui/ConsoleLog.cs +++ b/Ryujinx/Ui/ConsoleLog.cs @@ -26,7 +26,7 @@ namespace Ryujinx { LogLevel.Error, ConsoleColor.Red } }; - _messageQueue = new BlockingCollection(); + _messageQueue = new BlockingCollection(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))