diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-08-15 15:59:51 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-08-15 15:59:51 -0300 |
| commit | c393cdf8e3775bc95850e4d8c8e4c446b286d3b4 (patch) | |
| tree | 25035a244741d2daf3f7d6be8b23153ff061ea15 /Ryujinx.HLE/OsHle | |
| parent | 76d95dee05e3c51c18e1799f54cc407e0f633b4e (diff) | |
More flexible memory manager (#307)
* Keep track mapped buffers with fixed offsets
* Started rewriting the memory manager
* Initial support for MapPhysicalMemory and UnmapPhysicalMemory, other tweaks
* MapPhysicalMemory/UnmapPhysicalMemory support, other tweaks
* Rebased
* Optimize the map/unmap physical memory svcs
* Integrate shared font support
* Fix address space reserve alignment
* Some fixes related to gpu memory mapping
* Some cleanup
* Only try uploading const buffers that are really used
* Check if memory region is contiguous
* Rebased
* Add missing count increment on IsRegionModified
* Check for reads/writes outside of the address space, optimize translation with a tail call
Diffstat (limited to 'Ryujinx.HLE/OsHle')
36 files changed, 2470 insertions, 502 deletions
diff --git a/Ryujinx.HLE/OsHle/Font/SharedFontManager.cs b/Ryujinx.HLE/OsHle/Font/SharedFontManager.cs new file mode 100644 index 00000000..12b6973e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Font/SharedFontManager.cs @@ -0,0 +1,122 @@ +using Ryujinx.HLE.Memory; +using Ryujinx.HLE.OsHle.Utilities; +using Ryujinx.HLE.Resource; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.OsHle.Font +{ + class SharedFontManager + { + private DeviceMemory Memory; + + private long PhysicalAddress; + + private string FontsPath; + + private struct FontInfo + { + public int Offset; + public int Size; + + public FontInfo(int Offset, int Size) + { + this.Offset = Offset; + this.Size = Size; + } + } + + private Dictionary<SharedFontType, FontInfo> FontData; + + public SharedFontManager(Switch Device, long PhysicalAddress) + { + this.PhysicalAddress = PhysicalAddress; + + Memory = Device.Memory; + + FontsPath = Path.Combine(Device.VFs.GetSystemPath(), "fonts"); + } + + public void EnsureInitialized() + { + if (FontData == null) + { + Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize); + + uint FontOffset = 0; + + FontInfo CreateFont(string Name) + { + string FontFilePath = Path.Combine(FontsPath, Name + ".ttf"); + + if (File.Exists(FontFilePath)) + { + byte[] Data = File.ReadAllBytes(FontFilePath); + + FontInfo Info = new FontInfo((int)FontOffset, Data.Length); + + WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length); + + FontOffset += 8; + + uint Start = FontOffset; + + for (; FontOffset - Start < Data.Length; FontOffset++) + { + Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]); + } + + return Info; + } + else + { + throw new InvalidSystemResourceException($"Font \"{Name}.ttf\" not found. Please provide it in \"{FontsPath}\"."); + } + } + + FontData = new Dictionary<SharedFontType, FontInfo>() + { + { SharedFontType.JapanUsEurope, CreateFont("FontStandard") }, + { SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") }, + { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") }, + { SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") }, + { SharedFontType.Korean, CreateFont("FontKorean") }, + { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") } + }; + + if (FontOffset > Horizon.FontSize) + { + throw new InvalidSystemResourceException( + $"The sum of all fonts size exceed the shared memory size. " + + $"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. " + + $"(actual size: {FontOffset} bytes)."); + } + } + } + + private void WriteMagicAndSize(long Position, int Size) + { + const int DecMagic = 0x18029a7f; + const int Key = 0x49621806; + + int EncryptedSize = EndianSwap.Swap32(Size ^ Key); + + Memory.WriteInt32(Position + 0, DecMagic); + Memory.WriteInt32(Position + 4, EncryptedSize); + } + + public int GetFontSize(SharedFontType FontType) + { + EnsureInitialized(); + + return FontData[FontType].Size; + } + + public int GetSharedMemoryAddressOffset(SharedFontType FontType) + { + EnsureInitialized(); + + return FontData[FontType].Offset + 8; + } + } +} diff --git a/Ryujinx.HLE/OsHle/Font/SharedFontType.cs b/Ryujinx.HLE/OsHle/Font/SharedFontType.cs new file mode 100644 index 00000000..80f42a7d --- /dev/null +++ b/Ryujinx.HLE/OsHle/Font/SharedFontType.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.HLE.OsHle.Font +{ + public enum SharedFontType + { + JapanUsEurope = 0, + SimplifiedChinese = 1, + SimplifiedChineseEx = 2, + TraditionalChinese = 3, + Korean = 4, + NintendoEx = 5, + Count + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs b/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs new file mode 100644 index 00000000..946c51e1 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + enum AddressSpaceType + { + Addr32Bits = 0, + Addr36Bits = 1, + Addr36BitsNoMap = 2, + Addr39Bits = 3 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs deleted file mode 100644 index cd3d8223..00000000 --- a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs +++ /dev/null @@ -1,44 +0,0 @@ -using ChocolArm64.Memory; -using System; -using System.Collections.Generic; - -namespace Ryujinx.HLE.OsHle.Handles -{ - class HSharedMem - { - private List<(AMemory, long, long)> Positions; - - public EventHandler<EventArgs> MemoryMapped; - public EventHandler<EventArgs> MemoryUnmapped; - - public HSharedMem() - { - Positions = new List<(AMemory, long, long)>(); - } - - public void AddVirtualPosition(AMemory Memory, long Position, long Size) - { - lock (Positions) - { - Positions.Add((Memory, Position, Size)); - - MemoryMapped?.Invoke(this, EventArgs.Empty); - } - } - - public void RemoveVirtualPosition(AMemory Memory, long Position, long Size) - { - lock (Positions) - { - Positions.Remove((Memory, Position, Size)); - - MemoryUnmapped?.Invoke(this, EventArgs.Empty); - } - } - - public (AMemory, long, long)[] GetVirtualPositions() - { - return Positions.ToArray(); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs b/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs deleted file mode 100644 index 2969a2ba..00000000 --- a/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs +++ /dev/null @@ -1,21 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.HLE.OsHle.Handles -{ - class HTransferMem - { - public AMemory Memory { get; private set; } - public AMemoryPerm Perm { get; private set; } - - public long Position { get; private set; } - public long Size { get; private set; } - - public HTransferMem(AMemory Memory, AMemoryPerm Perm, long Position, long Size) - { - this.Memory = Memory; - this.Perm = Perm; - this.Position = Position; - this.Size = Size; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs new file mode 100644 index 00000000..e33b6cb9 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs @@ -0,0 +1,43 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KMemoryBlock + { + public long BasePosition { get; set; } + public long PagesCount { get; set; } + + public MemoryState State { get; set; } + public MemoryPermission Permission { get; set; } + public MemoryAttribute Attribute { get; set; } + + public int IpcRefCount { get; set; } + public int DeviceRefCount { get; set; } + + public KMemoryBlock( + long BasePosition, + long PagesCount, + MemoryState State, + MemoryPermission Permission, + MemoryAttribute Attribute) + { + this.BasePosition = BasePosition; + this.PagesCount = PagesCount; + this.State = State; + this.Attribute = Attribute; + this.Permission = Permission; + } + + public KMemoryInfo GetInfo() + { + long Size = PagesCount * KMemoryManager.PageSize; + + return new KMemoryInfo( + BasePosition, + Size, + State, + Permission, + Attribute, + IpcRefCount, + DeviceRefCount); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs new file mode 100644 index 00000000..3f79f000 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs @@ -0,0 +1,33 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KMemoryInfo + { + public long Position { get; private set; } + public long Size { get; private set; } + + public MemoryState State { get; private set; } + public MemoryPermission Permission { get; private set; } + public MemoryAttribute Attribute { get; private set; } + + public int IpcRefCount { get; private set; } + public int DeviceRefCount { get; private set; } + + public KMemoryInfo( + long Position, + long Size, + MemoryState State, + MemoryPermission Permission, + MemoryAttribute Attribute, + int IpcRefCount, + int DeviceRefCount) + { + this.Position = Position; + this.Size = Size; + this.State = State; + this.Attribute = Attribute; + this.Permission = Permission; + this.IpcRefCount = IpcRefCount; + this.DeviceRefCount = DeviceRefCount; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs new file mode 100644 index 00000000..76c5a53d --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs @@ -0,0 +1,1082 @@ +using ChocolArm64.Memory; +using Ryujinx.HLE.Memory; +using Ryujinx.HLE.OsHle.Kernel; +using System; +using System.Collections.Generic; + +using static Ryujinx.HLE.OsHle.ErrorCode; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KMemoryManager + { + public const int PageSize = 0x1000; + + private LinkedList<KMemoryBlock> Blocks; + + private AMemory CpuMemory; + + private ArenaAllocator Allocator; + + public long AddrSpaceStart { get; private set; } + public long AddrSpaceEnd { get; private set; } + + public long CodeRegionStart { get; private set; } + public long CodeRegionEnd { get; private set; } + + public long MapRegionStart { get; private set; } + public long MapRegionEnd { get; private set; } + + public long HeapRegionStart { get; private set; } + public long HeapRegionEnd { get; private set; } + + public long NewMapRegionStart { get; private set; } + public long NewMapRegionEnd { get; private set; } + + public long TlsIoRegionStart { get; private set; } + public long TlsIoRegionEnd { get; private set; } + + public long PersonalMmHeapUsage { get; private set; } + + private long CurrentHeapAddr; + + public KMemoryManager(Process Process) + { + CpuMemory = Process.Memory; + Allocator = Process.Ns.Memory.Allocator; + + long CodeRegionSize; + long MapRegionSize; + long HeapRegionSize; + long NewMapRegionSize; + long TlsIoRegionSize; + int AddrSpaceWidth; + + AddressSpaceType AddrType = AddressSpaceType.Addr39Bits; + + if (Process.MetaData != null) + { + AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth; + } + + switch (AddrType) + { + case AddressSpaceType.Addr32Bits: + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + MapRegionSize = 0x40000000; + HeapRegionSize = 0x40000000; + NewMapRegionSize = 0; + TlsIoRegionSize = 0; + AddrSpaceWidth = 32; + break; + + case AddressSpaceType.Addr36Bits: + CodeRegionStart = 0x8000000; + CodeRegionSize = 0x78000000; + MapRegionSize = 0x180000000; + HeapRegionSize = 0x180000000; + NewMapRegionSize = 0; + TlsIoRegionSize = 0; + AddrSpaceWidth = 36; + break; + + case AddressSpaceType.Addr36BitsNoMap: + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + MapRegionSize = 0; + HeapRegionSize = 0x80000000; + NewMapRegionSize = 0; + TlsIoRegionSize = 0; + AddrSpaceWidth = 36; + break; + + case AddressSpaceType.Addr39Bits: + CodeRegionStart = 0; + CodeRegionSize = 0x80000000; + MapRegionSize = 0x1000000000; + HeapRegionSize = 0x180000000; + NewMapRegionSize = 0x80000000; + TlsIoRegionSize = 0x1000000000; + AddrSpaceWidth = 39; + break; + + default: throw new InvalidOperationException(); + } + + AddrSpaceStart = 0; + AddrSpaceEnd = 1L << AddrSpaceWidth; + + CodeRegionEnd = CodeRegionStart + CodeRegionSize; + MapRegionStart = CodeRegionEnd; + MapRegionEnd = CodeRegionEnd + MapRegionSize; + HeapRegionStart = MapRegionEnd; + HeapRegionEnd = MapRegionEnd + HeapRegionSize; + NewMapRegionStart = HeapRegionEnd; + NewMapRegionEnd = HeapRegionEnd + NewMapRegionSize; + TlsIoRegionStart = NewMapRegionEnd; + TlsIoRegionEnd = NewMapRegionEnd + TlsIoRegionSize; + + CurrentHeapAddr = HeapRegionStart; + + if (NewMapRegionSize == 0) + { + NewMapRegionStart = AddrSpaceStart; + NewMapRegionEnd = AddrSpaceEnd; + } + + Blocks = new LinkedList<KMemoryBlock>(); + + long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize; + + InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped); + } + + public void HleMapProcessCode(long Position, long Size) + { + long PagesCount = Size / PageSize; + + if (!Allocator.TryAllocate(Size, out long PA)) + { + throw new InvalidOperationException(); + } + + lock (Blocks) + { + InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute); + + CpuMemory.Map(Position, PA, Size); + } + } + + public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission) + { + long PagesCount = Size / PageSize; + + if (!Allocator.TryAllocate(Size, out long PA)) + { + throw new InvalidOperationException(); + } + + lock (Blocks) + { + InsertBlock(Position, PagesCount, State, Permission); + + CpuMemory.Map(Position, PA, Size); + } + } + + public long HleMapTlsPage() + { + bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd; + + long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart; + + lock (Blocks) + { + while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd)) + { + if (FindBlock(Position).State == MemoryState.Unmapped) + { + InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite); + + if (!Allocator.TryAllocate(PageSize, out long PA)) + { + throw new InvalidOperationException(); + } + + CpuMemory.Map(Position, PA, PageSize); + + return Position; + } + + Position += PageSize; + } + + throw new InvalidOperationException(); + } + } + + public long TrySetHeapSize(long Size, out long Position) + { + Position = 0; + + if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart)) + { + return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + } + + bool Success = false; + + long CurrentHeapSize = GetHeapSize(); + + if ((ulong)CurrentHeapSize <= (ulong)Size) + { + //Expand. + long DiffSize = Size - CurrentHeapSize; + + lock (Blocks) + { + if (Success = IsUnmapped(CurrentHeapAddr, DiffSize)) + { + if (!Allocator.TryAllocate(DiffSize, out long PA)) + { + return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + } + + long PagesCount = DiffSize / PageSize; + + InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); + + CpuMemory.Map(CurrentHeapAddr, PA, DiffSize); + } + } + } + else + { + //Shrink. + long FreeAddr = HeapRegionStart + Size; + long DiffSize = CurrentHeapSize - Size; + + lock (Blocks) + { + Success = CheckRange( + FreeAddr, + DiffSize, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + long PagesCount = DiffSize / PageSize; + + InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped); + + CpuMemory.Unmap(FreeAddr, DiffSize); + + FreePages(FreeAddr, PagesCount); + } + } + } + + CurrentHeapAddr = HeapRegionStart + Size; + + if (Success) + { + Position = HeapRegionStart; + + return 0; + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long GetHeapSize() + { + return CurrentHeapAddr - HeapRegionStart; + } + + public long SetMemoryAttribute( + long Position, + long Size, + MemoryAttribute AttributeMask, + MemoryAttribute AttributeValue) + { + lock (Blocks) + { + if (CheckRange( + Position, + Size, + MemoryState.AttributeChangeAllowed, + MemoryState.AttributeChangeAllowed, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.BorrowedAndIpcMapped, + MemoryAttribute.None, + MemoryAttribute.DeviceMappedAndUncached, + out MemoryState State, + out MemoryPermission Permission, + out MemoryAttribute Attribute)) + { + long PagesCount = Size / PageSize; + + Attribute &= ~AttributeMask; + Attribute |= AttributeMask & AttributeValue; + + InsertBlock(Position, PagesCount, State, Permission, Attribute); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public KMemoryInfo QueryMemory(long Position) + { + if ((ulong)Position >= (ulong)AddrSpaceStart && + (ulong)Position < (ulong)AddrSpaceEnd) + { + lock (Blocks) + { + return FindBlock(Position).GetInfo(); + } + } + else + { + return new KMemoryInfo( + AddrSpaceEnd, + -AddrSpaceEnd, + MemoryState.Reserved, + MemoryPermission.None, + MemoryAttribute.None, + 0, + 0); + } + } + + public long Map(long Src, long Dst, long Size) + { + bool Success; + + lock (Blocks) + { + Success = CheckRange( + Src, + Size, + MemoryState.MapAllowed, + MemoryState.MapAllowed, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState SrcState, + out _, + out _); + + Success &= IsUnmapped(Dst, Size); + + if (Success) + { + long PagesCount = Size / PageSize; + + InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed); + + InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite); + + long PA = CpuMemory.GetPhysicalAddress(Src); + + CpuMemory.Map(Dst, PA, Size); + } + } + + return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long Unmap(long Src, long Dst, long Size) + { + bool Success; + + lock (Blocks) + { + Success = CheckRange( + Src, + Size, + MemoryState.MapAllowed, + MemoryState.MapAllowed, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.Borrowed, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState SrcState, + out _, + out _); + + Success &= CheckRange( + Dst, + Size, + MemoryState.Mask, + MemoryState.MappedMemory, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + long PagesCount = Size / PageSize; + + InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite); + + InsertBlock(Dst, PagesCount, MemoryState.Unmapped); + + CpuMemory.Unmap(Dst, Size); + } + } + + return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position) + { + lock (Blocks) + { + if (IsUnmapped(Position, SharedMemory.Size)) + { + long PagesCount = SharedMemory.Size / PageSize; + + InsertBlock(Position, PagesCount, MemoryState.SharedMemory, Permission); + + CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long UnmapSharedMemory(long Position, long Size) + { + 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, + Size, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out _, + out MemoryAttribute Attribute)) + { + long PagesCount = Size / PageSize; + + Attribute |= MemoryAttribute.Borrowed; + + InsertBlock(Position, PagesCount, State, Permission, Attribute); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long ResetTransferMemory(long Position, long Size) + { + lock (Blocks) + { + if (CheckRange( + Position, + Size, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.Borrowed, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out _, + out _)) + { + long PagesCount = Size / PageSize; + + InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission) + { + lock (Blocks) + { + if (CheckRange( + Position, + Size, + MemoryState.ProcessPermissionChangeAllowed, + MemoryState.ProcessPermissionChangeAllowed, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out _, + out _)) + { + if (State == MemoryState.CodeStatic) + { + State = MemoryState.CodeMutable; + } + else if (State == MemoryState.ModCodeStatic) + { + State = MemoryState.ModCodeMutable; + } + else + { + throw new InvalidOperationException(); + } + + long PagesCount = Size / PageSize; + + InsertBlock(Position, PagesCount, State, Permission); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long MapPhysicalMemory(long Position, long Size) + { + long End = Position + Size; + + lock (Blocks) + { + long MappedSize = 0; + + KMemoryInfo Info; + + LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Position); + + LinkedListNode<KMemoryBlock> Node = BaseNode; + + do + { + Info = Node.Value.GetInfo(); + + if (Info.State != MemoryState.Unmapped) + { + MappedSize += GetSizeInRange(Info, Position, End); + } + + Node = Node.Next; + } + while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + + if (MappedSize == Size) + { + return 0; + } + + long RemainingSize = Size - MappedSize; + + if (!Allocator.TryAllocate(RemainingSize, out long PA)) + { + return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + } + + Node = BaseNode; + + do + { + Info = Node.Value.GetInfo(); + + if (Info.State == MemoryState.Unmapped) + { + long CurrSize = GetSizeInRange(Info, Position, End); + + CpuMemory.Map(Info.Position, PA, CurrSize); + + PA += CurrSize; + } + + Node = Node.Next; + } + while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + + PersonalMmHeapUsage += RemainingSize; + + long PagesCount = Size / PageSize; + + InsertBlock( + Position, + PagesCount, + MemoryState.Unmapped, + MemoryPermission.None, + MemoryAttribute.None, + MemoryState.Heap, + MemoryPermission.ReadAndWrite, + MemoryAttribute.None); + } + + return 0; + } + + public long UnmapPhysicalMemory(long Position, long Size) + { + long End = Position + Size; + + lock (Blocks) + { + long HeapMappedSize = 0; + + long CurrPosition = Position; + + KMemoryInfo Info; + + LinkedListNode<KMemoryBlock> Node = FindBlockNode(CurrPosition); + + do + { + Info = Node.Value.GetInfo(); + + if (Info.State == MemoryState.Heap) + { + if (Info.Attribute != MemoryAttribute.None) + { + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + HeapMappedSize += GetSizeInRange(Info, Position, End); + } + else if (Info.State != MemoryState.Unmapped) + { + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + Node = Node.Next; + } + while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + + if (HeapMappedSize == 0) + { + return 0; + } + + PersonalMmHeapUsage -= HeapMappedSize; + + long PagesCount = Size / PageSize; + + InsertBlock(Position, PagesCount, MemoryState.Unmapped); + + CpuMemory.Unmap(Position, Size); + + FreePages(Position, PagesCount); + + 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; + + long PA = CpuMemory.GetPhysicalAddress(VA); + + Allocator.Free(PA, PageSize); + } + } + + private bool IsUnmapped(long Position, long Size) + { + return CheckRange( + Position, + Size, + MemoryState.Mask, + MemoryState.Unmapped, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + } + + private bool CheckRange( + long Position, + long Size, + MemoryState StateMask, + MemoryState StateExpected, + MemoryPermission PermissionMask, + MemoryPermission PermissionExpected, + MemoryAttribute AttributeMask, + MemoryAttribute AttributeExpected, + MemoryAttribute AttributeIgnoreMask, + out MemoryState OutState, + out MemoryPermission OutPermission, + out MemoryAttribute OutAttribute) + { + KMemoryInfo BlkInfo = FindBlock(Position).GetInfo(); + + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size)) + { + if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected && + (BlkInfo.State & StateMask) == StateExpected && + (BlkInfo.Permission & PermissionMask) == PermissionExpected) + { + OutState = BlkInfo.State; + OutPermission = BlkInfo.Permission; + OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask; + + return true; + } + } + + OutState = MemoryState.Unmapped; + OutPermission = MemoryPermission.None; + OutAttribute = MemoryAttribute.None; + + return false; + } + + private void InsertBlock( + long BasePosition, + long PagesCount, + MemoryState OldState, + MemoryPermission OldPermission, + MemoryAttribute OldAttribute, + MemoryState NewState, + MemoryPermission NewPermission, + MemoryAttribute NewAttribute) + { + //Insert new block on the list only on areas where the state + //of the block matches the state specified on the Old* state + //arguments, otherwise leave it as is. + OldAttribute |= MemoryAttribute.IpcAndDeviceMapped; + + ulong Start = (ulong)BasePosition; + ulong End = (ulong)PagesCount * PageSize + Start; + + LinkedListNode<KMemoryBlock> Node = Blocks.First; + + while (Node != null) + { + LinkedListNode<KMemoryBlock> NewNode = Node; + LinkedListNode<KMemoryBlock> NextNode = Node.Next; + + KMemoryBlock CurrBlock = Node.Value; + + ulong CurrStart = (ulong)CurrBlock.BasePosition; + ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart; + + if (Start < CurrEnd && CurrStart < End) + { + MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped; + + if (CurrBlock.State != OldState || + CurrBlock.Permission != OldPermission || + CurrBlockAttr != OldAttribute) + { + Node = NextNode; + + continue; + } + + if (CurrStart >= Start && CurrEnd <= End) + { + CurrBlock.State = NewState; + CurrBlock.Permission = NewPermission; + CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped; + CurrBlock.Attribute |= NewAttribute; + } + else if (CurrStart >= Start) + { + CurrBlock.BasePosition = (long)End; + + CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize); + + long NewPagesCount = (long)((End - CurrStart) / PageSize); + + NewNode = Blocks.AddBefore(Node, new KMemoryBlock( + (long)CurrStart, + NewPagesCount, + NewState, + NewPermission, + NewAttribute)); + } + else if (CurrEnd <= End) + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + long NewPagesCount = (long)((CurrEnd - Start) / PageSize); + + NewNode = Blocks.AddAfter(Node, new KMemoryBlock( + BasePosition, + NewPagesCount, + NewState, + NewPermission, + NewAttribute)); + } + else + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + long NextPagesCount = (long)((CurrEnd - End) / PageSize); + + NewNode = Blocks.AddAfter(Node, new KMemoryBlock( + BasePosition, + PagesCount, + NewState, + NewPermission, + NewAttribute)); + + Blocks.AddAfter(NewNode, new KMemoryBlock( + (long)End, + NextPagesCount, + CurrBlock.State, + CurrBlock.Permission, + CurrBlock.Attribute)); + + NextNode = null; + } + + MergeEqualStateNeighbours(NewNode); + } + + Node = NextNode; + } + } + + private void InsertBlock( + long BasePosition, + long PagesCount, + MemoryState State, + MemoryPermission Permission = MemoryPermission.None, + MemoryAttribute Attribute = MemoryAttribute.None) + { + //Inserts new block at the list, replacing and spliting + //existing blocks as needed. + KMemoryBlock Block = new KMemoryBlock(BasePosition, PagesCount, State, Permission, Attribute); + + ulong Start = (ulong)BasePosition; + ulong End = (ulong)PagesCount * PageSize + Start; + + LinkedListNode<KMemoryBlock> NewNode = null; + + LinkedListNode<KMemoryBlock> Node = Blocks.First; + + while (Node != null) + { + KMemoryBlock CurrBlock = Node.Value; + + LinkedListNode<KMemoryBlock> NextNode = Node.Next; + + ulong CurrStart = (ulong)CurrBlock.BasePosition; + ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart; + + if (Start < CurrEnd && CurrStart < End) + { + if (Start >= CurrStart && End <= CurrEnd) + { + Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped; + } + + if (Start > CurrStart && End < CurrEnd) + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + long NextPagesCount = (long)((CurrEnd - End) / PageSize); + + NewNode = Blocks.AddAfter(Node, Block); + + Blocks.AddAfter(NewNode, new KMemoryBlock( + (long)End, + NextPagesCount, + CurrBlock.State, + CurrBlock.Permission, + CurrBlock.Attribute)); + + break; + } + else if (Start <= CurrStart && End < CurrEnd) + { + CurrBlock.BasePosition = (long)End; + + CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize); + + if (NewNode == null) + { + NewNode = Blocks.AddBefore(Node, Block); + } + } + else if (Start > CurrStart && End >= CurrEnd) + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + if (NewNode == null) + { + NewNode = Blocks.AddAfter(Node, Block); + } + } + else + { + if (NewNode == null) + { + NewNode = Blocks.AddBefore(Node, Block); + } + + Blocks.Remove(Node); + } + } + + Node = NextNode; + } + + if (NewNode == null) + { + NewNode = Blocks.AddFirst(Block); + } + + MergeEqualStateNeighbours(NewNode); + } + + private void MergeEqualStateNeighbours(LinkedListNode<KMemoryBlock> Node) + { + KMemoryBlock Block = Node.Value; + + ulong Start = (ulong)Block.BasePosition; + ulong End = (ulong)Block.PagesCount * PageSize + Start; + + if (Node.Previous != null) + { + KMemoryBlock Previous = Node.Previous.Value; + + if (BlockStateEquals(Block, Previous)) + { + Blocks.Remove(Node.Previous); + + Block.BasePosition = Previous.BasePosition; + + Start = (ulong)Block.BasePosition; + } + } + + if (Node.Next != null) + { + KMemoryBlock Next = Node.Next.Value; + + if (BlockStateEquals(Block, Next)) + { + Blocks.Remove(Node.Next); + + End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize); + } + } + + Block.PagesCount = (long)((End - Start) / PageSize); + } + + private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS) + { + return LHS.State == RHS.State && + LHS.Permission == RHS.Permission && + LHS.Attribute == RHS.Attribute && + LHS.DeviceRefCount == RHS.DeviceRefCount && + LHS.IpcRefCount == RHS.IpcRefCount; + } + + private KMemoryBlock FindBlock(long Position) + { + return FindBlockNode(Position)?.Value; + } + + private LinkedListNode<KMemoryBlock> FindBlockNode(long Position) + { + ulong Addr = (ulong)Position; + + lock (Blocks) + { + LinkedListNode<KMemoryBlock> Node = Blocks.First; + + while (Node != null) + { + KMemoryBlock Block = Node.Value; + + ulong Start = (ulong)Block.BasePosition; + ulong End = (ulong)Block.PagesCount * PageSize + Start; + + if (Start <= Addr && End - 1 >= Addr) + { + return Node; + } + + Node = Node.Next; + } + } + + return null; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs b/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs new file mode 100644 index 00000000..5c410474 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KSharedMemory + { + public long PA { get; private set; } + public long Size { get; private set; } + + public KSharedMemory(long PA, long Size) + { + this.PA = PA; + this.Size = Size; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs b/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs new file mode 100644 index 00000000..f116f548 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs @@ -0,0 +1,60 @@ +using System; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KTlsPageManager + { + private const int TlsEntrySize = 0x200; + + private long PagePosition; + + private int UsedSlots; + + private bool[] Slots; + + public bool IsEmpty => UsedSlots == 0; + public bool IsFull => UsedSlots == Slots.Length; + + public KTlsPageManager(long PagePosition) + { + this.PagePosition = PagePosition; + + Slots = new bool[KMemoryManager.PageSize / TlsEntrySize]; + } + + public bool TryGetFreeTlsAddr(out long Position) + { + Position = PagePosition; + + for (int Index = 0; Index < Slots.Length; Index++) + { + if (!Slots[Index]) + { + Slots[Index] = true; + + UsedSlots++; + + return true; + } + + Position += TlsEntrySize; + } + + Position = 0; + + return false; + } + + public void FreeTlsSlot(int Slot) + { + if ((uint)Slot > Slots.Length) + { + throw new ArgumentOutOfRangeException(nameof(Slot)); + } + + Slots[Slot] = false; + + UsedSlots--; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs b/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs new file mode 100644 index 00000000..5f9c0409 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KTransferMemory + { + public long Position { get; private set; } + public long Size { get; private set; } + + public KTransferMemory(long Position, long Size) + { + this.Position = Position; + this.Size = Size; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs b/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs new file mode 100644 index 00000000..e234d7f0 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace Ryujinx.HLE.OsHle.Handles +{ + [Flags] + enum MemoryAttribute : byte + { + None = 0, + Mask = 0xff, + + Borrowed = 1 << 0, + IpcMapped = 1 << 1, + DeviceMapped = 1 << 2, + Uncached = 1 << 3, + + IpcAndDeviceMapped = IpcMapped | DeviceMapped, + + BorrowedAndIpcMapped = Borrowed | IpcMapped, + + DeviceMappedAndUncached = DeviceMapped | Uncached + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs b/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs new file mode 100644 index 00000000..416f171e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs @@ -0,0 +1,18 @@ +using System; + +namespace Ryujinx.HLE.OsHle.Handles +{ + [Flags] + enum MemoryPermission : byte + { + None = 0, + Mask = 0xff, + + Read = 1 << 0, + Write = 1 << 1, + Execute = 1 << 2, + + ReadAndWrite = Read | Write, + ReadAndExecute = Read | Execute + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryState.cs b/Ryujinx.HLE/OsHle/Handles/MemoryState.cs new file mode 100644 index 00000000..5437d6a3 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/MemoryState.cs @@ -0,0 +1,49 @@ +using System; + +namespace Ryujinx.HLE.OsHle +{ + [Flags] + enum MemoryState : uint + { + Unmapped = 0x00000000, + Io = 0x00002001, + Normal = 0x00042002, + CodeStatic = 0x00DC7E03, + CodeMutable = 0x03FEBD04, + Heap = 0x037EBD05, + SharedMemory = 0x00402006, + ModCodeStatic = 0x00DD7E08, + ModCodeMutable = 0x03FFBD09, + IpcBuffer0 = 0x005C3C0A, + MappedMemory = 0x005C3C0B, + ThreadLocal = 0x0040200C, + TransferMemoryIsolated = 0x015C3C0D, + TransferMemory = 0x005C380E, + ProcessMemory = 0x0040380F, + Reserved = 0x00000010, + IpcBuffer1 = 0x005C3811, + IpcBuffer3 = 0x004C2812, + KernelStack = 0x00002013, + CodeReadOnly = 0x00402214, + CodeWritable = 0x00402015, + Mask = 0xffffffff, + + PermissionChangeAllowed = 1 << 8, + ForceReadWritableByDebugSyscalls = 1 << 9, + IpcSendAllowedType0 = 1 << 10, + IpcSendAllowedType3 = 1 << 11, + IpcSendAllowedType1 = 1 << 12, + ProcessPermissionChangeAllowed = 1 << 14, + MapAllowed = 1 << 15, + UnmapProcessCodeMemoryAllowed = 1 << 16, + TransferMemoryAllowed = 1 << 17, + QueryPhysicalAddressAllowed = 1 << 18, + MapDeviceAllowed = 1 << 19, + MapDeviceAlignedAllowed = 1 << 20, + IpcBufferAllowed = 1 << 21, + IsPoolAllocated = 1 << 22, + MapProcessAllowed = 1 << 23, + AttributeChangeAllowed = 1 << 24, + CodeMemoryAllowed = 1 << 25 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Homebrew.cs b/Ryujinx.HLE/OsHle/Homebrew.cs index 778e52fe..90c6c4c6 100644 --- a/Ryujinx.HLE/OsHle/Homebrew.cs +++ b/Ryujinx.HLE/OsHle/Homebrew.cs @@ -10,23 +10,23 @@ namespace Ryujinx.HLE.OsHle //http://switchbrew.org/index.php?title=Homebrew_ABI public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath) { - Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW); - - //MainThreadHandle + //MainThreadHandle. WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle); - //NextLoadPath + //NextLoadPath. WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400); - // Argv + //Argv. long ArgvPosition = Position + 0xC00; - WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition); + Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0")); - //AppletType + WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition); + + //AppletType. WriteConfigEntry(Memory, ref Position, 7); - //EndOfList + //EndOfList. WriteConfigEntry(Memory, ref Position, 0); } diff --git a/Ryujinx.HLE/OsHle/Horizon.cs b/Ryujinx.HLE/OsHle/Horizon.cs index b4c7d36e..1fd210dd 100644 --- a/Ryujinx.HLE/OsHle/Horizon.cs +++ b/Ryujinx.HLE/OsHle/Horizon.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.HLE.Logging; +using Ryujinx.HLE.OsHle.Font; using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.SystemState; using System; @@ -12,7 +13,7 @@ namespace Ryujinx.HLE.OsHle public class Horizon : IDisposable { internal const int HidSize = 0x40000; - internal const int FontSize = 0x50; + internal const int FontSize = 0x1100000; private Switch Ns; @@ -22,10 +23,10 @@ namespace Ryujinx.HLE.OsHle public SystemStateMgr SystemState { get; private set; } - internal MemoryAllocator Allocator { get; private set; } + internal KSharedMemory HidSharedMem { get; private set; } + internal KSharedMemory FontSharedMem { get; private set; } - internal HSharedMem HidSharedMem { get; private set; } - internal HSharedMem FontSharedMem { get; private set; } + internal SharedFontManager Font { get; private set; } internal KEvent VsyncEvent { get; private set; } @@ -39,10 +40,16 @@ namespace Ryujinx.HLE.OsHle SystemState = new SystemStateMgr(); - Allocator = new MemoryAllocator(); + if (!Ns.Memory.Allocator.TryAllocate(HidSize, out long HidPA) || + !Ns.Memory.Allocator.TryAllocate(FontSize, out long FontPA)) + { + throw new InvalidOperationException(); + } + + HidSharedMem = new KSharedMemory(HidPA, HidSize); + FontSharedMem = new KSharedMemory(FontPA, FontSize); - HidSharedMem = new HSharedMem(); - FontSharedMem = new HSharedMem(); + Font = new SharedFontManager(Ns, FontSharedMem.PA); VsyncEvent = new KEvent(); } @@ -54,7 +61,25 @@ namespace Ryujinx.HLE.OsHle Ns.VFs.LoadRomFs(RomFsFile); } - Process MainProcess = MakeProcess(); + string NpdmFileName = Path.Combine(ExeFsDir, "main.npdm"); + + Npdm MetaData = null; + + if (File.Exists(NpdmFileName)) + { + Ns.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); + + using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open)) + { + MetaData = new Npdm(Input); + } + } + else + { + Ns.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + } + + Process MainProcess = MakeProcess(MetaData); void LoadNso(string FileName) { @@ -78,21 +103,7 @@ namespace Ryujinx.HLE.OsHle } } - void LoadNpdm(string FileName) - { - string File = Directory.GetFiles(ExeFsDir, FileName)[0]; - - Ns.Log.PrintInfo(LogClass.Loader, "Loading Title Metadata..."); - - using (FileStream Input = new FileStream(File, FileMode.Open)) - { - MainProcess.Metadata = new Npdm(Input); - } - } - - LoadNpdm("*.npdm"); - - if (!MainProcess.Metadata.Is64Bits) + if (!MainProcess.MetaData.Is64Bits) { throw new NotImplementedException("32-bit titles are unsupported!"); } @@ -145,7 +156,7 @@ namespace Ryujinx.HLE.OsHle public void SignalVsync() => VsyncEvent.WaitEvent.Set(); - private Process MakeProcess() + private Process MakeProcess(Npdm MetaData = null) { Process Process; @@ -158,7 +169,7 @@ namespace Ryujinx.HLE.OsHle ProcessId++; } - Process = new Process(Ns, Scheduler, ProcessId); + Process = new Process(Ns, Scheduler, ProcessId, MetaData); Processes.TryAdd(ProcessId, Process); } diff --git a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs index bbae5325..a62fc1bf 100644 --- a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs +++ b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs @@ -2,18 +2,21 @@ namespace Ryujinx.HLE.OsHle.Kernel { static class KernelErr { - public const int InvalidAlignment = 102; - public const int InvalidAddress = 106; - public const int InvalidMemRange = 110; - public const int InvalidPriority = 112; - public const int InvalidCoreId = 113; - public const int InvalidHandle = 114; - public const int InvalidCoreMask = 116; - public const int Timeout = 117; - public const int Canceled = 118; - public const int CountOutOfRange = 119; - public const int InvalidEnumValue = 120; - public const int InvalidThread = 122; - public const int InvalidState = 125; + public const int InvalidSize = 101; + public const int InvalidAddress = 102; + public const int OutOfMemory = 104; + public const int NoAccessPerm = 106; + public const int InvalidPermission = 108; + public const int InvalidMemRange = 110; + public const int InvalidPriority = 112; + public const int InvalidCoreId = 113; + public const int InvalidHandle = 114; + public const int InvalidMaskValue = 116; + public const int Timeout = 117; + public const int Canceled = 118; + public const int CountOutOfRange = 119; + public const int InvalidEnumValue = 120; + public const int InvalidThread = 122; + public const int InvalidState = 125; } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs index 6f7bc42f..a33ffe5e 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs @@ -10,7 +10,7 @@ using System.Threading; namespace Ryujinx.HLE.OsHle.Kernel { - partial class SvcHandler : IDisposable + partial class SvcHandler { private delegate void SvcFunc(AThreadState ThreadState); @@ -22,10 +22,6 @@ namespace Ryujinx.HLE.OsHle.Kernel private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits; - private HashSet<(HSharedMem, long, long)> MappedSharedMems; - - private ulong CurrentHeapSize; - private const uint SelfThreadHandle = 0xffff8000; private const uint SelfProcessHandle = 0xffff8001; @@ -82,8 +78,6 @@ namespace Ryujinx.HLE.OsHle.Kernel this.Memory = Process.Memory; SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>(); - - MappedSharedMems = new HashSet<(HSharedMem, long, long)>(); } static SvcHandler() @@ -126,26 +120,5 @@ namespace Ryujinx.HLE.OsHle.Kernel return Process.HandleTable.GetData<KThread>(Handle); } } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - lock (MappedSharedMems) - { - foreach ((HSharedMem SharedMem, long Position, long Size) in MappedSharedMems) - { - SharedMem.RemoveVirtualPosition(Memory, Position, Size); - } - - MappedSharedMems.Clear(); - } - } - } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs index f10cad7a..68d87293 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Memory; using ChocolArm64.State; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Handles; @@ -11,43 +10,85 @@ namespace Ryujinx.HLE.OsHle.Kernel { private void SvcSetHeapSize(AThreadState ThreadState) { - uint Size = (uint)ThreadState.X1; + long Size = (long)ThreadState.X1; - long Position = MemoryRegions.HeapRegionAddress; + if ((Size & 0x1fffff) != 0 || Size != (uint)Size) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + long Result = Process.MemoryManager.TrySetHeapSize(Size, out long Position); - if (Size > CurrentHeapSize) + ThreadState.X0 = (ulong)Result; + + if (Result == 0) { - Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); + ThreadState.X1 = (ulong)Position; } else { - Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - - CurrentHeapSize = Size; - - ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Position; } private void SvcSetMemoryAttribute(AThreadState ThreadState) { long Position = (long)ThreadState.X0; long Size = (long)ThreadState.X1; - int State0 = (int)ThreadState.X2; - int State1 = (int)ThreadState.X3; - if ((State0 == 0 && State1 == 0) || - (State0 == 8 && State1 == 0)) + if (!PageAligned(Position)) { - Memory.Manager.ClearAttrBit(Position, Size, 3); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; } - else if (State0 == 8 && State1 == 8) + + if (!PageAligned(Size) || Size == 0) { - Memory.Manager.SetAttrBit(Position, Size, 3); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; } - ThreadState.X0 = 0; + MemoryAttribute AttributeMask = (MemoryAttribute)ThreadState.X2; + MemoryAttribute AttributeValue = (MemoryAttribute)ThreadState.X3; + + MemoryAttribute Attributes = AttributeMask | AttributeValue; + + if (Attributes != AttributeMask || + (Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); + + return; + } + + long Result = Process.MemoryManager.SetMemoryAttribute( + Position, + Size, + AttributeMask, + AttributeValue); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + else + { + Memory.StopObservingRegion(Position, Size); + } + + ThreadState.X0 = (ulong)Result; } private void SvcMapMemory(AThreadState ThreadState) @@ -56,33 +97,59 @@ namespace Ryujinx.HLE.OsHle.Kernel long Src = (long)ThreadState.X1; long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Src | Dst)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - if (!IsValidMapPosition(Dst)) + if (!PageAligned(Size) || Size == 0) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); return; } - AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); + if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); - Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); - Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None); + return; + } + + if (!InsideAddrSpace(Src, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); - Memory.Manager.SetAttrBit(Src, Size, 0); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); - ThreadState.X0 = 0; + return; + } + + if (!InsideNewMapRegion(Dst, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + long Result = Process.MemoryManager.Map(Src, Dst, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } private void SvcUnmapMemory(AThreadState ThreadState) @@ -91,33 +158,59 @@ namespace Ryujinx.HLE.OsHle.Kernel long Src = (long)ThreadState.X1; long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Src | Dst)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - if (!IsValidMapPosition(Dst)) + if (!PageAligned(Size) || Size == 0) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } - AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); + if (!InsideAddrSpace(Src, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); - Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); - Memory.Manager.Reprotect(Src, Size, DstInfo.Perm); + return; + } - Memory.Manager.ClearAttrBit(Src, Size, 0); + if (!InsideNewMapRegion(Dst, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); - ThreadState.X0 = 0; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + long Result = Process.MemoryManager.Unmap(Src, Dst, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } private void SvcQueryMemory(AThreadState ThreadState) @@ -125,26 +218,16 @@ namespace Ryujinx.HLE.OsHle.Kernel long InfoPtr = (long)ThreadState.X0; long Position = (long)ThreadState.X2; - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); - - if (MapInfo == null) - { - long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; - - long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1; + KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position); - MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None); - } - - Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); - Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size); - Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type); - Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr); - Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm); - Memory.WriteInt32(InfoPtr + 0x1c, 0); - Memory.WriteInt32(InfoPtr + 0x20, 0); + Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position); + Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size); + Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff); + Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute); + Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission); + Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount); + Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount); Memory.WriteInt32(InfoPtr + 0x24, 0); - //TODO: X1. ThreadState.X0 = 0; ThreadState.X1 = 0; @@ -152,134 +235,344 @@ namespace Ryujinx.HLE.OsHle.Kernel private void SvcMapSharedMemory(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; + int Handle = (int)ThreadState.X0; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Position)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } - HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle); + MemoryPermission Permission = (MemoryPermission)ThreadState.X3; - if (SharedMem != null) + if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite) { - Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); - AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); - SharedMem.AddVirtualPosition(Memory, Src, Size); + return; + } - Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle); + + if (SharedMemory == null) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); - lock (MappedSharedMems) - { - MappedSharedMems.Add((SharedMem, Src, Size)); - } + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } - ThreadState.X0 = 0; + if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (SharedMemory.Size != Size) + { + Ns.Log.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) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - //TODO: Error codes. + ThreadState.X0 = (ulong)Result; } private void SvcUnmapSharedMemory(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; + int Handle = (int)ThreadState.X0; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Position)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle); + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } - if (SharedMem != null) + if ((ulong)(Position + Size) <= (ulong)Position) { - Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); - SharedMem.RemoveVirtualPosition(Memory, Src, Size); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); - lock (MappedSharedMems) - { - MappedSharedMems.Remove((SharedMem, Src, Size)); - } + return; + } - ThreadState.X0 = 0; + KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle); + + if (SharedMemory == null) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; } - //TODO: Error codes. + if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } private void SvcCreateTransferMemory(AThreadState ThreadState) { - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Position)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src); + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + MemoryPermission Permission = (MemoryPermission)ThreadState.X3; + + if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); + + return; + } - Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission); - HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size); + KTransferMemory TransferMemory = new KTransferMemory(Position, Size); - ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem); + int Handle = Process.HandleTable.OpenHandle(TransferMemory); ThreadState.X0 = 0; - ThreadState.X1 = Handle; + ThreadState.X1 = (ulong)Handle; } private void SvcMapPhysicalMemory(AThreadState ThreadState) { long Position = (long)ThreadState.X0; - uint Size = (uint)ThreadState.X1; + long Size = (long)ThreadState.X1; + + if (!PageAligned(Position)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); - ThreadState.X0 = 0; + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } private void SvcUnmapPhysicalMemory(AThreadState ThreadState) { long Position = (long)ThreadState.X0; - uint Size = (uint)ThreadState.X1; + long Size = (long)ThreadState.X1; + + if (!PageAligned(Position)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - Memory.Manager.Unmap(Position, Size); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); - ThreadState.X0 = 0; + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; + } + + private static bool PageAligned(long Position) + { + return (Position & (KMemoryManager.PageSize - 1)) == 0; + } + + private bool InsideAddrSpace(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.AddrSpaceStart && + End < (ulong)Process.MemoryManager.AddrSpaceEnd; } - private static bool IsValidPosition(long Position) + private bool InsideMapRegion(long Position, long Size) { - return Position >= MemoryRegions.AddrSpaceStart && - Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.MapRegionStart && + End < (ulong)Process.MemoryManager.MapRegionEnd; } - private static bool IsValidMapPosition(long Position) + private bool InsideHeapRegion(long Position, long Size) { - return Position >= MemoryRegions.MapRegionAddress && - Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.HeapRegionStart && + End < (ulong)Process.MemoryManager.HeapRegionEnd; + } + + private bool InsideNewMapRegion(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.NewMapRegionStart && + End < (ulong)Process.MemoryManager.NewMapRegionEnd; } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs index 08305522..f833745b 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs @@ -18,8 +18,6 @@ namespace Ryujinx.HLE.OsHle.Kernel private const bool EnableProcessDebugging = false; - private const bool IsVirtualMemoryEnabled = true; //This is always true(?) - private void SvcExitProcess(AThreadState ThreadState) { Ns.Os.ExitProcess(ThreadState.ProcessId); @@ -53,12 +51,11 @@ namespace Ryujinx.HLE.OsHle.Kernel { Session.Dispose(); } - else if (Obj is HTransferMem TMem) + else if (Obj is KTransferMemory TransferMemory) { - TMem.Memory.Manager.Reprotect( - TMem.Position, - TMem.Size, - TMem.Perm); + Process.MemoryManager.ResetTransferMemory( + TransferMemory.Position, + TransferMemory.Size); } ThreadState.X0 = 0; @@ -306,27 +303,29 @@ namespace Ryujinx.HLE.OsHle.Kernel break; case 2: - ThreadState.X1 = MemoryRegions.MapRegionAddress; + ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart; break; case 3: - ThreadState.X1 = MemoryRegions.MapRegionSize; + ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd - + (ulong)Process.MemoryManager.MapRegionStart; break; case 4: - ThreadState.X1 = MemoryRegions.HeapRegionAddress; + ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart; break; case 5: - ThreadState.X1 = MemoryRegions.HeapRegionSize; + ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd - + (ulong)Process.MemoryManager.HeapRegionStart; break; case 6: - ThreadState.X1 = MemoryRegions.TotalMemoryAvailable; + ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalAvailableSize; break; case 7: - ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize; + ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalUsedSize; break; case 8: @@ -338,23 +337,29 @@ namespace Ryujinx.HLE.OsHle.Kernel break; case 12: - ThreadState.X1 = MemoryRegions.AddrSpaceStart; + ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart; break; case 13: - ThreadState.X1 = MemoryRegions.AddrSpaceSize; + ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd - + (ulong)Process.MemoryManager.AddrSpaceStart; break; case 14: - ThreadState.X1 = MemoryRegions.MapRegionAddress; + ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart; break; case 15: - ThreadState.X1 = MemoryRegions.MapRegionSize; + ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd - + (ulong)Process.MemoryManager.NewMapRegionStart; break; case 16: - ThreadState.X1 = IsVirtualMemoryEnabled ? 1 : 0; + ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0); + break; + + case 17: + ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage; break; default: diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs index 8702203e..04524850 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs @@ -204,7 +204,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); return; } @@ -226,7 +226,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); return; } diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs index 9fc42617..164a85ca 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -124,7 +124,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } @@ -214,7 +214,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -223,7 +223,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } diff --git a/Ryujinx.HLE/OsHle/MemoryAllocator.cs b/Ryujinx.HLE/OsHle/MemoryAllocator.cs deleted file mode 100644 index 8696aa4d..00000000 --- a/Ryujinx.HLE/OsHle/MemoryAllocator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Ryujinx.HLE.OsHle -{ - class MemoryAllocator - { - public bool TryAllocate(long Size, out long Address) - { - throw new NotImplementedException(); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/MemoryRegions.cs b/Ryujinx.HLE/OsHle/MemoryRegions.cs deleted file mode 100644 index 198c621c..00000000 --- a/Ryujinx.HLE/OsHle/MemoryRegions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.HLE.OsHle -{ - static class MemoryRegions - { - public const long AddrSpaceStart = 0x08000000; - - public const long MapRegionAddress = 0x10000000; - public const long MapRegionSize = 0x20000000; - - public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; - public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress; - - public const long MainStackSize = 0x100000; - - public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize; - - public const long TlsPagesSize = 0x20000; - - public const long TlsPagesAddress = MainStackAddress - TlsPagesSize; - - public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize; - - public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart; - - public const long AddrSpaceSize = AMemoryMgr.AddrSize - AddrSpaceStart; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Process.cs b/Ryujinx.HLE/OsHle/Process.cs index 6cbc0860..96fe3610 100644 --- a/Ryujinx.HLE/OsHle/Process.cs +++ b/Ryujinx.HLE/OsHle/Process.cs @@ -12,6 +12,7 @@ using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.Kernel; using Ryujinx.HLE.OsHle.Services.Nv; using Ryujinx.HLE.OsHle.SystemState; +using Ryujinx.HLE.OsHle.Utilities; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -22,13 +23,9 @@ namespace Ryujinx.HLE.OsHle { class Process : IDisposable { - private const int TlsSize = 0x200; - - private const int TotalTlsSlots = (int)MemoryRegions.TlsPagesSize / TlsSize; - private const int TickFreq = 19_200_000; - private Switch Ns; + public Switch Ns { get; private set; } public bool NeedsHbAbi { get; private set; } @@ -40,22 +37,24 @@ namespace Ryujinx.HLE.OsHle public AMemory Memory { get; private set; } + public KMemoryManager MemoryManager { get; private set; } + + private List<KTlsPageManager> TlsPages; + public KProcessScheduler Scheduler { get; private set; } public List<KThread> ThreadArbiterList { get; private set; } public object ThreadSyncLock { get; private set; } + public Npdm MetaData { get; private set; } + public KProcessHandleTable HandleTable { get; private set; } public AppletStateMgr AppletState { get; private set; } - public Npdm Metadata { get; set; } - private SvcHandler SvcHandler; - private ConcurrentDictionary<int, AThread> TlsSlots; - private ConcurrentDictionary<long, KThread> Threads; private KThread MainThread; @@ -70,13 +69,18 @@ namespace Ryujinx.HLE.OsHle private bool Disposed; - public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId) + public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId, Npdm MetaData) { this.Ns = Ns; this.Scheduler = Scheduler; + this.MetaData = MetaData; this.ProcessId = ProcessId; - Memory = new AMemory(); + Memory = new AMemory(Ns.Memory.RamPointer); + + MemoryManager = new KMemoryManager(this); + + TlsPages = new List<KTlsPageManager>(); ThreadArbiterList = new List<KThread>(); @@ -88,18 +92,11 @@ namespace Ryujinx.HLE.OsHle SvcHandler = new SvcHandler(Ns, this); - TlsSlots = new ConcurrentDictionary<int, AThread>(); - Threads = new ConcurrentDictionary<long, KThread>(); Executables = new List<Executable>(); - ImageBase = MemoryRegions.AddrSpaceStart; - - MapRWMemRegion( - MemoryRegions.TlsPagesAddress, - MemoryRegions.TlsPagesSize, - MemoryType.ThreadLocal); + ImageBase = MemoryManager.CodeRegionStart; } public void LoadProgram(IExecutable Program) @@ -111,17 +108,17 @@ namespace Ryujinx.HLE.OsHle Ns.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}."); - Executable Executable = new Executable(Program, Memory, ImageBase); + Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase); Executables.Add(Executable); - ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd); + ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize); } public void SetEmptyArgs() { //TODO: This should be part of Run. - ImageBase += AMemoryMgr.PageSize; + ImageBase += KMemoryManager.PageSize; } public bool Run(bool NeedsHbAbi = false) @@ -140,14 +137,19 @@ namespace Ryujinx.HLE.OsHle MakeSymbolTable(); - MapRWMemRegion( - MemoryRegions.MainStackAddress, - MemoryRegions.MainStackSize, - MemoryType.Normal); + long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize; + + long MainStackSize = 1 * 1024 * 1024; - long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize; + long MainStackBottom = MainStackTop - MainStackSize; - int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0); + MemoryManager.HleMapCustom( + MainStackBottom, + MainStackSize, + MemoryState.MappedMemory, + MemoryPermission.ReadAndWrite); + + int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0); if (Handle == -1) { @@ -158,7 +160,15 @@ namespace Ryujinx.HLE.OsHle if (NeedsHbAbi) { - HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); + HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize); + + const long HbAbiDataSize = KMemoryManager.PageSize; + + MemoryManager.HleMapCustom( + HbAbiDataPosition, + HbAbiDataSize, + MemoryState.MappedMemory, + MemoryPermission.ReadAndWrite); string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath); @@ -173,11 +183,6 @@ namespace Ryujinx.HLE.OsHle return true; } - private void MapRWMemRegion(long Position, long Size, MemoryType Type) - { - Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); - } - public void StopAllThreadsAsync() { if (Disposed) @@ -190,9 +195,9 @@ namespace Ryujinx.HLE.OsHle MainThread.Thread.StopExecution(); } - foreach (AThread Thread in TlsSlots.Values) + foreach (KThread Thread in Threads.Values) { - Thread.StopExecution(); + Thread.Thread.StopExecution(); } } @@ -216,9 +221,9 @@ namespace Ryujinx.HLE.OsHle int Handle = HandleTable.OpenHandle(Thread); - int ThreadId = GetFreeTlsSlot(CpuThread); + long Tpidr = GetFreeTls(); - long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize; + int ThreadId = (int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1; CpuThread.ThreadState.ProcessId = ProcessId; CpuThread.ThreadState.ThreadId = ThreadId; @@ -240,6 +245,32 @@ namespace Ryujinx.HLE.OsHle return Handle; } + private long GetFreeTls() + { + long Position; + + lock (TlsPages) + { + for (int Index = 0; Index < TlsPages.Count; Index++) + { + if (TlsPages[Index].TryGetFreeTlsAddr(out Position)) + { + return Position; + } + } + + long PagePosition = MemoryManager.HleMapTlsPage(); + + KTlsPageManager TlsPage = new KTlsPageManager(PagePosition); + + TlsPages.Add(TlsPage); + + TlsPage.TryGetFreeTlsAddr(out Position); + } + + return Position; + } + private void BreakHandler(object sender, AInstExceptionEventArgs e) { throw new GuestBrokeExecutionException(); @@ -346,25 +377,10 @@ namespace Ryujinx.HLE.OsHle return Name; } - private int GetFreeTlsSlot(AThread Thread) - { - for (int Index = 1; Index < TotalTlsSlots; Index++) - { - if (TlsSlots.TryAdd(Index, Thread)) - { - return Index; - } - } - - throw new InvalidOperationException(); - } - private void ThreadFinished(object sender, EventArgs e) { if (sender is AThread Thread) { - TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); - Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread); Scheduler.RemoveThread(KernelThread); @@ -372,7 +388,7 @@ namespace Ryujinx.HLE.OsHle KernelThread.WaitEvent.Set(); } - if (TlsSlots.Count == 0) + if (Threads.Count == 0) { if (ShouldDispose) { @@ -383,11 +399,6 @@ namespace Ryujinx.HLE.OsHle } } - private int GetTlsSlot(long Position) - { - return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize); - } - public KThread GetThread(long Tpidr) { if (!Threads.TryGetValue(Tpidr, out KThread Thread)) @@ -411,7 +422,7 @@ namespace Ryujinx.HLE.OsHle //safe as the thread may try to access those resources. Instead, we set //the flag to have the Process disposed when all threads finishes. //Note: This may not happen if the guest code gets stuck on a infinite loop. - if (TlsSlots.Count > 0) + if (Threads.Count > 0) { ShouldDispose = true; @@ -439,10 +450,6 @@ namespace Ryujinx.HLE.OsHle AppletState.Dispose(); - SvcHandler.Dispose(); - - Memory.Dispose(); - Ns.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); } } diff --git a/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs b/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs index 2ef67cc3..5c32ca83 100644 --- a/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs +++ b/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs @@ -10,9 +10,9 @@ namespace Ryujinx.HLE.OsHle.Services.Hid public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; - private HSharedMem HidSharedMem; + private KSharedMemory HidSharedMem; - public IAppletResource(HSharedMem HidSharedMem) + public IAppletResource(KSharedMemory HidSharedMem) { m_Commands = new Dictionary<int, ServiceProcessRequest>() { diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs new file mode 100644 index 00000000..e718182a --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs @@ -0,0 +1,198 @@ +using ChocolArm64.Memory; +using Ryujinx.HLE.Gpu.Memory; +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS +{ + class NvGpuASCtx + { + public NvGpuVmm Vmm { get; private set; } + + private class Range + { + public ulong Start { get; private set; } + public ulong End { get; private set; } + + public Range(long Position, long Size) + { + Start = (ulong)Position; + End = (ulong)Size + Start; + } + } + + private class MappedMemory : Range + { + public long PhysicalAddress { get; private set; } + public bool VaAllocated { get; private set; } + + public MappedMemory( + long Position, + long Size, + long PhysicalAddress, + bool VaAllocated) : base(Position, Size) + { + this.PhysicalAddress = PhysicalAddress; + this.VaAllocated = VaAllocated; + } + } + + private SortedList<long, Range> Maps; + private SortedList<long, Range> Reservations; + + public NvGpuASCtx(ServiceCtx Context) + { + Vmm = new NvGpuVmm(Context.Memory); + + Maps = new SortedList<long, Range>(); + Reservations = new SortedList<long, Range>(); + } + + public bool ValidateFixedBuffer(long Position, long Size) + { + long MapEnd = Position + Size; + + //Check if size is valid (0 is also not allowed). + if ((ulong)MapEnd <= (ulong)Position) + { + return false; + } + + //Check if address is page aligned. + if ((Position & NvGpuVmm.PageMask) != 0) + { + return false; + } + + //Check if region is reserved. + if (BinarySearch(Reservations, Position) == null) + { + return false; + } + + //Check for overlap with already mapped buffers. + Range Map = BinarySearchLt(Maps, MapEnd); + + if (Map != null && Map.End > (ulong)Position) + { + return false; + } + + return true; + } + + public void AddMap( + long Position, + long Size, + long PhysicalAddress, + bool VaAllocated) + { + Maps.Add(Position, new MappedMemory(Position, Size, PhysicalAddress, VaAllocated)); + } + + public bool RemoveMap(long Position, out long Size) + { + Size = 0; + + if (Maps.Remove(Position, out Range Value)) + { + MappedMemory Map = (MappedMemory)Value; + + if (Map.VaAllocated) + { + Size = (long)(Map.End - Map.Start); + } + + return true; + } + + return false; + } + + public bool TryGetMapPhysicalAddress(long Position, out long PhysicalAddress) + { + Range Map = BinarySearch(Maps, Position); + + if (Map != null) + { + PhysicalAddress = ((MappedMemory)Map).PhysicalAddress; + + return true; + } + + PhysicalAddress = 0; + + return false; + } + + public void AddReservation(long Position, long Size) + { + Reservations.Add(Position, new Range(Position, Size)); + } + + public bool RemoveReservation(long Position) + { + return Reservations.Remove(Position); + } + + private Range BinarySearch(SortedList<long, Range> Lst, long Position) + { + int Left = 0; + int Right = Lst.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + Range Rg = Lst.Values[Middle]; + + if ((ulong)Position >= Rg.Start && (ulong)Position < Rg.End) + { + return Rg; + } + + if ((ulong)Position < Rg.Start) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + } + } + + return null; + } + + private Range BinarySearchLt(SortedList<long, Range> Lst, long Position) + { + Range LtRg = null; + + int Left = 0; + int Right = Lst.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + Range Rg = Lst.Values[Middle]; + + if ((ulong)Position < Rg.Start) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + + LtRg = Rg; + } + } + + return LtRg; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs index fcc478a4..3c67cef0 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs @@ -11,11 +11,13 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS { private const int FlagFixedOffset = 1; - private static ConcurrentDictionary<Process, NvGpuVmm> Vmms; + private const int FlagRemapSubRange = 0x100; + + private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs; static NvGpuASIoctl() { - Vmms = new ConcurrentDictionary<Process, NvGpuVmm>(); + ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>(); } public static int ProcessIoctl(ServiceCtx Context, int Cmd) @@ -29,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS case 0x4106: return MapBufferEx (Context); case 0x4108: return GetVaRegions(Context); case 0x4109: return InitializeEx(Context); - case 0x4114: return Remap (Context); + case 0x4114: return Remap (Context, Cmd); } throw new NotImplementedException(Cmd.ToString("x8")); @@ -52,29 +54,38 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); ulong Size = (ulong)Args.Pages * (ulong)Args.PageSize; - if ((Args.Flags & FlagFixedOffset) != 0) - { - Args.Offset = Vmm.Reserve(Args.Offset, (long)Size, 1); - } - else - { - Args.Offset = Vmm.Reserve((long)Size, 1); - } - int Result = NvResult.Success; - if (Args.Offset < 0) + lock (ASCtx) { - Args.Offset = 0; + //Note: When the fixed offset flag is not set, + //the Offset field holds the alignment size instead. + if ((Args.Flags & FlagFixedOffset) != 0) + { + Args.Offset = ASCtx.Vmm.ReserveFixed(Args.Offset, (long)Size); + } + else + { + Args.Offset = ASCtx.Vmm.Reserve((long)Size, Args.Offset); + } - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to allocate size {Size:x16}!"); + if (Args.Offset < 0) + { + Args.Offset = 0; - Result = NvResult.OutOfMemory; + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!"); + + Result = NvResult.OutOfMemory; + } + else + { + ASCtx.AddReservation(Args.Offset, (long)Size); + } } AMemoryHelper.Write(Context.Memory, OutputPosition, Args); @@ -89,14 +100,29 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); - ulong Size = (ulong)Args.Pages * - (ulong)Args.PageSize; + int Result = NvResult.Success; - Vmm.Free(Args.Offset, (long)Size); + lock (ASCtx) + { + ulong Size = (ulong)Args.Pages * + (ulong)Args.PageSize; - return NvResult.Success; + if (ASCtx.RemoveReservation(Args.Offset)) + { + ASCtx.Vmm.Free(Args.Offset, (long)Size); + } + else + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, + $"Failed to free offset 0x{Args.Offset:x16} size 0x{Size:x16}!"); + + Result = NvResult.InvalidInput; + } + } + + return Result; } private static int UnmapBuffer(ServiceCtx Context) @@ -106,11 +132,21 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS NvGpuASUnmapBuffer Args = AMemoryHelper.Read<NvGpuASUnmapBuffer>(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); - if (!Vmm.Unmap(Args.Offset)) + lock (ASCtx) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); + if (ASCtx.RemoveMap(Args.Offset, out long Size)) + { + if (Size != 0) + { + ASCtx.Vmm.Free(Args.Offset, Size); + } + } + else + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); + } } return NvResult.Success; @@ -118,12 +154,14 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS private static int MapBufferEx(ServiceCtx Context) { + const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16} and size 0x{1:x16}!"; + long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; NvGpuASMapBufferEx Args = AMemoryHelper.Read<NvGpuASMapBufferEx>(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); @@ -134,7 +172,39 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS return NvResult.InvalidInput; } - long PA = Map.Address + Args.BufferOffset; + long PA; + + if ((Args.Flags & FlagRemapSubRange) != 0) + { + lock (ASCtx) + { + if (ASCtx.TryGetMapPhysicalAddress(Args.Offset, out PA)) + { + long VA = Args.Offset + Args.BufferOffset; + + PA += Args.BufferOffset; + + if (ASCtx.Vmm.Map(PA, VA, Args.MappingSize) < 0) + { + string Msg = string.Format(MapErrorMsg, VA, Args.MappingSize); + + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg); + + return NvResult.InvalidInput; + } + + return NvResult.Success; + } + else + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!"); + + return NvResult.InvalidInput; + } + } + } + + PA = Map.Address + Args.BufferOffset; long Size = Args.MappingSize; @@ -145,40 +215,44 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS int Result = NvResult.Success; - //Note: When the fixed offset flag is not set, - //the Offset field holds the alignment size instead. - if ((Args.Flags & FlagFixedOffset) != 0) + lock (ASCtx) { - long MapEnd = Args.Offset + Args.MappingSize; + //Note: When the fixed offset flag is not set, + //the Offset field holds the alignment size instead. + bool VaAllocated = (Args.Flags & FlagFixedOffset) == 0; - if ((ulong)MapEnd <= (ulong)Args.Offset) + if (!VaAllocated) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} and size 0x{Args.MappingSize:x16} results in a overflow!"); - - return NvResult.InvalidInput; + if (ASCtx.ValidateFixedBuffer(Args.Offset, Size)) + { + Args.Offset = ASCtx.Vmm.Map(PA, Args.Offset, Size); + } + else + { + string Msg = string.Format(MapErrorMsg, Args.Offset, Size); + + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg); + + Result = NvResult.InvalidInput; + } } - - if ((Args.Offset & NvGpuVmm.PageMask) != 0) + else { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} is not page aligned!"); - - return NvResult.InvalidInput; + Args.Offset = ASCtx.Vmm.Map(PA, Size); } - Args.Offset = Vmm.Map(PA, Args.Offset, Size); - } - else - { - Args.Offset = Vmm.Map(PA, Size); - if (Args.Offset < 0) { Args.Offset = 0; - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to map size {Args.MappingSize:x16}!"); + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!"); Result = NvResult.InvalidInput; } + else + { + ASCtx.AddMap(Args.Offset, Size, PA, VaAllocated); + } } AMemoryHelper.Write(Context.Memory, OutputPosition, Args); @@ -206,38 +280,50 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS return NvResult.Success; } - private static int Remap(ServiceCtx Context) + private static int Remap(ServiceCtx Context, int Cmd) { + int Count = ((Cmd >> 16) & 0xff) / 0x14; + long InputPosition = Context.Request.GetBufferType0x21().Position; - NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition); + for (int Index = 0; Index < Count; Index++, InputPosition += 0x14) + { + NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuVmm Vmm = GetASCtx(Context).Vmm; - NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); + NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); - return NvResult.InvalidInput; - } + return NvResult.InvalidInput; + } - //FIXME: This is most likely wrong... - Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16, - (long)(uint)Args.Pages << 16); + long Result = Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16, + (long)(uint)Args.Pages << 16); + + if (Result < 0) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, + $"Page 0x{Args.Offset:x16} size 0x{Args.Pages:x16} not allocated!"); + + return NvResult.InvalidInput; + } + } return NvResult.Success; } - public static NvGpuVmm GetVmm(ServiceCtx Context) + public static NvGpuASCtx GetASCtx(ServiceCtx Context) { - return Vmms.GetOrAdd(Context.Process, (Key) => new NvGpuVmm(Context.Memory)); + return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context)); } public static void UnloadProcess(Process Process) { - Vmms.TryRemove(Process, out _); + ASCtxs.TryRemove(Process, out _); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs index 411a579a..3e030643 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition); - NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context); + NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;; for (int Index = 0; Index < Args.NumEntries; Index++) { @@ -162,7 +162,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition); - NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context); + NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;; for (int Index = 0; Index < Args.NumEntries; Index++) { diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs index 7705a1f7..7f203453 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl Context.Ns.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}"); } - + return NvResult.Success; } diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs index 1e13f197..bc66fc71 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap { public int Handle; public int Padding; - public long RefCount; + public long Address; public int Size; public int Flags; } diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs index 21fce700..f349a03e 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap public long DecrementRefCount() { - return Interlocked.Decrement(ref Dupes) + 1; + return Interlocked.Decrement(ref Dupes); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs index 4c6107f8..e5b29825 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs @@ -129,7 +129,8 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap { //When the address is zero, we need to allocate //our own backing memory for the NvMap. - if (!Context.Ns.Os.Allocator.TryAllocate((uint)Size, out Address)) + //TODO: Is this allocation inside the transfer memory? + if (!Context.Ns.Memory.Allocator.TryAllocate((uint)Size, out Address)) { Result = NvResult.OutOfMemory; } @@ -163,23 +164,22 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap return NvResult.InvalidInput; } - long OldRefCount = Map.DecrementRefCount(); - - if (OldRefCount <= 1) + if (Map.DecrementRefCount() <= 0) { DeleteNvMap(Context, Args.Handle); Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); - Args.Flags = 0; + Args.Address = Map.Address; + Args.Flags = 0; } else { - Args.Flags = FlagNotFreedYet; + Args.Address = 0; + Args.Flags = FlagNotFreedYet; } - Args.RefCount = OldRefCount; - Args.Size = Map.Size; + Args.Size = Map.Size; AMemoryHelper.Write(Context.Memory, OutputPosition, Args); diff --git a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs index b8447ac6..4788c5af 100644 --- a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.Font; +using Ryujinx.HLE.OsHle.Font; using Ryujinx.HLE.OsHle.Ipc; using System.Collections.Generic; @@ -27,8 +27,8 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.Ns.Font.Load(FontType); - + //We don't need to do anything here because we do lazy initialization + //on SharedFontManager (the font is loaded when necessary). return 0; } @@ -36,7 +36,9 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.ResponseData.Write(Context.Ns.Font.GetLoadState(FontType)); + //1 (true) indicates that the font is already loaded. + //All fonts are already loaded. + Context.ResponseData.Write(1); return 0; } @@ -45,7 +47,7 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.ResponseData.Write(Context.Ns.Font.GetFontSize(FontType)); + Context.ResponseData.Write(Context.Ns.Os.Font.GetFontSize(FontType)); return 0; } @@ -54,13 +56,15 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.ResponseData.Write(Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); + Context.ResponseData.Write(Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType)); return 0; } public long GetSharedMemoryNativeHandle(ServiceCtx Context) { + Context.Ns.Os.Font.EnsureInitialized(); + int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -68,50 +72,54 @@ namespace Ryujinx.HLE.OsHle.Services.Pl return 0; } - private uint AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, uint BufferPos, out uint LoadState) + public long GetSharedFontInOrderOfPriority(ServiceCtx Context) { - long TypesPosition = Context.Request.ReceiveBuff[0].Position; - long TypesSize = Context.Request.ReceiveBuff[0].Size; + long LanguageCode = Context.RequestData.ReadInt64(); + int LoadedCount = 0; - long OffsetsPosition = Context.Request.ReceiveBuff[1].Position; - long OffsetsSize = Context.Request.ReceiveBuff[1].Size; - - long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position; - long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size; + for (SharedFontType Type = 0; Type < SharedFontType.Count; Type++) + { + int Offset = (int)Type * 4; - LoadState = Context.Ns.Font.GetLoadState(FontType); + if (!AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, Offset)) + { + break; + } - if (BufferPos >= TypesSize || BufferPos >= OffsetsSize || BufferPos >= FontSizeBufferSize) - { - return 0; + LoadedCount++; } - Context.Memory.WriteUInt32(TypesPosition + BufferPos, (uint)FontType); - Context.Memory.WriteUInt32(OffsetsPosition + BufferPos, Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); - Context.Memory.WriteUInt32(FontSizeBufferPosition + BufferPos, Context.Ns.Font.GetFontSize(FontType)); - - BufferPos += 4; + Context.ResponseData.Write(LoadedCount); + Context.ResponseData.Write((int)SharedFontType.Count); - return BufferPos; + return 0; } - public long GetSharedFontInOrderOfPriority(ServiceCtx Context) + private bool AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, int Offset) { - ulong LanguageCode = Context.RequestData.ReadUInt64(); - uint LoadedCount = 0; - uint BufferPos = 0; - uint Loaded = 0; + long TypesPosition = Context.Request.ReceiveBuff[0].Position; + long TypesSize = Context.Request.ReceiveBuff[0].Size; + + long OffsetsPosition = Context.Request.ReceiveBuff[1].Position; + long OffsetsSize = Context.Request.ReceiveBuff[1].Size; + + long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position; + long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size; - for (int Type = 0; Type < Context.Ns.Font.Count; Type++) + if ((uint)Offset + 4 > (uint)TypesSize || + (uint)Offset + 4 > (uint)OffsetsSize || + (uint)Offset + 4 > (uint)FontSizeBufferSize) { - BufferPos = AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, BufferPos, out Loaded); - LoadedCount += Loaded; + return false; } - Context.ResponseData.Write(LoadedCount); - Context.ResponseData.Write(Context.Ns.Font.Count); + Context.Memory.WriteInt32(TypesPosition + Offset, (int)FontType); - return 0; + Context.Memory.WriteInt32(OffsetsPosition + Offset, Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType)); + + Context.Memory.WriteInt32(FontSizeBufferPosition + Offset, Context.Ns.Os.Font.GetFontSize(FontType)); + + return true; } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs index 41f2916f..04b50bb3 100644 --- a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android int Slot = GetFreeSlotBlocking(Width, Height); - return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return MakeReplyParcel(Context, Slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) diff --git a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs index 39cced28..010dbb20 100644 --- a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs +++ b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs @@ -11,5 +11,15 @@ namespace Ryujinx.HLE.OsHle.Utilities { return (Value + (Size - 1)) & ~((long)Size - 1); } + + public static int AlignDown(int Value, int Size) + { + return Value & ~(Size - 1); + } + + public static long AlignDown(long Value, int Size) + { + return Value & ~((long)Size - 1); + } } } |
