From 72d972d70c80a06ba91270606fd7d73dc6959964 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 3 Jan 2019 22:58:18 -0300 Subject: [PATCH] Implement partial support for device address space SVCs, CreateInterruptEvent, initial work to support devices --- ChocolArm64/Events/InvalidAccessEventArgs.cs | 14 - ChocolArm64/Events/MemoryAccessEventArgs.cs | 14 + ChocolArm64/Memory/IBus.cs | 15 + ChocolArm64/Memory/MemoryManager.cs | 349 +++++++++++++++--- Ryujinx.Common/Logging/LogClass.cs | 1 + Ryujinx.Graphics/Memory/NvGpuVmmCache.cs | 4 +- Ryujinx.HLE/DeviceMemory.cs | 18 + .../HOS/Kernel/Interrupt/KInterruptEvent.cs | 19 + .../HOS/Kernel/Memory/KMemoryManager.cs | 226 ++++++++++-- Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 7 +- .../Kernel/Process/KProcessCapabilities.cs | 12 +- .../HOS/Kernel/SupervisorCall/SvcMemory.cs | 26 +- .../HOS/Kernel/SupervisorCall/SvcSystem.cs | 32 ++ .../HOS/Kernel/SupervisorCall/SvcTable.cs | 4 +- Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs | 2 +- .../HOS/Kernel/Threading/KReadableEvent.cs | 7 +- Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs | 52 +++ Ryujinx.HLE/HOS/Services/ServiceFactory.cs | 4 + Ryujinx.HLE/Ryujinx.HLE.csproj | 1 + Ryujinx.LLE/Devices/Device.cs | 56 +++ Ryujinx.LLE/Devices/DummyDevice.cs | 7 + Ryujinx.LLE/Luea.csproj | 4 + Ryujinx.Tests/Cpu/CpuTest.cs | 4 +- 23 files changed, 772 insertions(+), 106 deletions(-) delete mode 100644 ChocolArm64/Events/InvalidAccessEventArgs.cs create mode 100644 ChocolArm64/Events/MemoryAccessEventArgs.cs create mode 100644 ChocolArm64/Memory/IBus.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Interrupt/KInterruptEvent.cs create mode 100644 Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs create mode 100644 Ryujinx.LLE/Devices/Device.cs create mode 100644 Ryujinx.LLE/Devices/DummyDevice.cs diff --git a/ChocolArm64/Events/InvalidAccessEventArgs.cs b/ChocolArm64/Events/InvalidAccessEventArgs.cs deleted file mode 100644 index 9c349755f0..0000000000 --- a/ChocolArm64/Events/InvalidAccessEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class MemoryAccessEventArgs : EventArgs - { - public long Position { get; private set; } - - public MemoryAccessEventArgs(long position) - { - Position = position; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Events/MemoryAccessEventArgs.cs b/ChocolArm64/Events/MemoryAccessEventArgs.cs new file mode 100644 index 0000000000..d81b5dc067 --- /dev/null +++ b/ChocolArm64/Events/MemoryAccessEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ChocolArm64.Events +{ + public class MemoryAccessEventArgs : EventArgs + { + public long VirtualAddress { get; } + + public MemoryAccessEventArgs(long va) + { + VirtualAddress = va; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/IBus.cs b/ChocolArm64/Memory/IBus.cs new file mode 100644 index 0000000000..507aff3b7b --- /dev/null +++ b/ChocolArm64/Memory/IBus.cs @@ -0,0 +1,15 @@ +namespace ChocolArm64.Memory +{ + public interface IBus + { + byte ReadByte(ulong address); + ushort ReadUInt16(ulong address); + uint ReadUInt32(ulong address); + ulong ReadUInt64(ulong address); + + void WriteByte(ulong address, byte value); + void WriteUInt16(ulong address, ushort value); + void WriteUInt32(ulong address, uint value); + void WriteUInt64(ulong address, ulong value); + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryManager.cs b/ChocolArm64/Memory/MemoryManager.cs index 1f21256807..b5c0af35dc 100644 --- a/ChocolArm64/Memory/MemoryManager.cs +++ b/ChocolArm64/Memory/MemoryManager.cs @@ -45,29 +45,33 @@ namespace ChocolArm64.Memory private Dictionary _monitors; - private ConcurrentDictionary _observedPages; + private ConcurrentDictionary _observedPages; - public IntPtr Ram { get; private set; } + private IBus _bus; - private byte* _ramPtr; + private struct Pte + { + public byte* ptr; - private byte*** _pageTable; + public long pa; + + public bool IsUnmapped => ptr == null && pa == 0; + } + + private Pte** _pageTable; public event EventHandler InvalidAccess; - public event EventHandler ObservedAccess; - public MemoryManager(IntPtr ram) + public MemoryManager(IBus bus = null) { _monitors = new Dictionary(); - _observedPages = new ConcurrentDictionary(); + _observedPages = new ConcurrentDictionary(); - Ram = ram; + _bus = bus; - _ramPtr = (byte*)ram; - - _pageTable = (byte***)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size); + _pageTable = (Pte**)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size); for (int l0 = 0; l0 < PtLvl0Size; l0++) { @@ -197,14 +201,48 @@ namespace ChocolArm64.Memory public byte ReadByte(long position) { - return *((byte*)Translate(position)); + Pte pte = GetPtEntry(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + return *((byte*)(pte.ptr + pageOffset)); + } + else if (pte.pa != 0) + { + return _bus?.ReadByte((ulong)(pte.pa + pageOffset)) ?? 0; + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + + return 0; + } } public ushort ReadUInt16(long position) { if ((position & 1) == 0) { - return *((ushort*)Translate(position)); + Pte pte = GetPtEntry(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + return *((ushort*)(pte.ptr + pageOffset)); + } + else if (pte.pa != 0) + { + return _bus?.ReadUInt16((ulong)(pte.pa + pageOffset)) ?? 0; + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + + return 0; + } } else { @@ -217,7 +255,24 @@ namespace ChocolArm64.Memory { if ((position & 3) == 0) { - return *((uint*)Translate(position)); + Pte pte = GetPtEntry(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + return *((uint*)(pte.ptr + pageOffset)); + } + else if (pte.pa != 0) + { + return _bus?.ReadUInt32((ulong)(pte.pa + pageOffset)) ?? 0; + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + + return 0; + } } else { @@ -230,7 +285,24 @@ namespace ChocolArm64.Memory { if ((position & 7) == 0) { - return *((ulong*)Translate(position)); + Pte pte = GetPtEntry(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + return *((ulong*)(pte.ptr + pageOffset)); + } + else if (pte.pa != 0) + { + return _bus?.ReadUInt64((ulong)(pte.pa + pageOffset)) ?? 0; + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + + return 0; + } } else { @@ -419,14 +491,44 @@ namespace ChocolArm64.Memory public void WriteByte(long position, byte value) { - *((byte*)TranslateWrite(position)) = value; + Pte pte = GetPtEntryForWrite(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + *((byte*)(pte.ptr + pageOffset)) = value; + } + else if (pte.pa != 0) + { + _bus?.WriteByte((ulong)(pte.pa + pageOffset), value); + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + } } public void WriteUInt16(long position, ushort value) { if ((position & 1) == 0) { - *((ushort*)TranslateWrite(position)) = value; + Pte pte = GetPtEntryForWrite(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + *((ushort*)(pte.ptr + pageOffset)) = value; + } + else if (pte.pa != 0) + { + _bus?.WriteUInt16((ulong)(pte.pa + pageOffset), value); + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + } } else { @@ -439,7 +541,22 @@ namespace ChocolArm64.Memory { if ((position & 3) == 0) { - *((uint*)TranslateWrite(position)) = value; + Pte pte = GetPtEntryForWrite(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + *((uint*)(pte.ptr + pageOffset)) = value; + } + else if (pte.pa != 0) + { + _bus?.WriteUInt32((ulong)(pte.pa + pageOffset), value); + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + } } else { @@ -452,7 +569,22 @@ namespace ChocolArm64.Memory { if ((position & 7) == 0) { - *((ulong*)TranslateWrite(position)) = value; + Pte pte = GetPtEntryForWrite(position); + + long pageOffset = position & PageMask; + + if (pte.ptr != null) + { + *((ulong*)(pte.ptr + pageOffset)) = value; + } + else if (pte.pa != 0) + { + _bus?.WriteUInt64((ulong)(pte.pa + pageOffset), value); + } + else + { + InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + } } else { @@ -607,14 +739,14 @@ namespace ChocolArm64.Memory } } - public void Map(long va, long pa, long size) + public void Map(long va, long pa, IntPtr ptr, long size) { - SetPtEntries(va, _ramPtr + pa, size); + SetPtEntries(va, pa, (byte*)ptr, size); } public void Unmap(long position, long size) { - SetPtEntries(position, null, size); + SetPtEntries(position, 0, null, size); StopObservingRegion(position, size); } @@ -634,14 +766,64 @@ namespace ChocolArm64.Memory return false; } - return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PageBits); + return _pageTable[l0][l1].ptr != null || _observedPages.ContainsKey(position >> PageBits); + } + + public IEnumerable<(long, long)> IteratePages(long address) + { + long l0 = (address >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (address >> PtLvl1Bit) & PtLvl1Mask; + + long currPa = 0; + long size = 0; + + for (; l0 < PtLvl0Size; l0++, l1 = 0) + { + if (IsL0Null((int)l0)) + { + if (currPa != 0) + { + yield return (currPa, size); + + currPa = 0; + size = 0; + } + + size += PtLvl1Size * PageSize; + + continue; + } + + for (; l1 < PtLvl1Size; l1++) + { + Pte pte = GetPtEntry((l0 << PtLvl0Bit) | (l1 << PtLvl1Bit)); + + if ((currPa != 0 || pte.pa != 0) && pte.pa != currPa + size) + { + yield return (currPa, size); + + currPa = pte.pa; + size = 0; + } + + size += PageSize; + } + } + + if (size != 0) + { + yield return (currPa, size); + } + } + + private bool IsL0Null(int l0) + { + return _pageTable[l0] == null; } public long GetPhysicalAddress(long virtualAddress) { - byte* ptr = Translate(virtualAddress); - - return (long)(ptr - _ramPtr); + return GetPtEntry(virtualAddress).pa + (virtualAddress & PageMask); } internal byte* Translate(long position) @@ -651,7 +833,7 @@ namespace ChocolArm64.Memory long old = position; - byte** lvl1 = _pageTable[l0]; + Pte* lvl1 = _pageTable[l0]; if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) { @@ -665,7 +847,7 @@ namespace ChocolArm64.Memory position &= PageMask; - byte* ptr = lvl1[l1]; + byte* ptr = lvl1[l1].ptr; if (ptr == null) { @@ -682,9 +864,9 @@ Unmapped: { long key = position >> PageBits; - if (_observedPages.TryGetValue(key, out IntPtr ptr)) + if (_observedPages.TryGetValue(key, out Pte pte)) { - return (byte*)ptr + (position & PageMask); + return pte.ptr + (position & PageMask); } InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); @@ -699,7 +881,7 @@ Unmapped: long old = position; - byte** lvl1 = _pageTable[l0]; + Pte* lvl1 = _pageTable[l0]; if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) { @@ -713,7 +895,7 @@ Unmapped: position &= PageMask; - byte* ptr = lvl1[l1]; + byte* ptr = lvl1[l1].ptr; if (ptr == null) { @@ -728,17 +910,17 @@ Unmapped: private byte* HandleNullPteWrite(long position) { - long key = position >> PageBits; - MemoryAccessEventArgs e = new MemoryAccessEventArgs(position); - if (_observedPages.TryGetValue(key, out IntPtr ptr)) + long key = position >> PageBits; + + if (_observedPages.TryGetValue(key, out Pte pte)) { - SetPtEntry(position, (byte*)ptr); + SetPtEntry(position, pte.pa, pte.ptr); ObservedAccess?.Invoke(this, e); - return (byte*)ptr + (position & PageMask); + return (byte*)pte.ptr + (position & PageMask); } InvalidAccess?.Invoke(this, e); @@ -746,15 +928,87 @@ Unmapped: throw new VmmPageFaultException(position); } - private void SetPtEntries(long va, byte* ptr, long size) + private Pte GetPtEntry(long position) + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + long old = position; + + Pte* lvl1 = _pageTable[l0]; + + if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) + { + return default(Pte); + } + + if (lvl1 == null) + { + return default(Pte); + } + + position &= PageMask; + + Pte pte = lvl1[l1]; + + if (pte.IsUnmapped && !_observedPages.TryGetValue(old >> PageBits, out pte)) + { + return default(Pte); + } + + return pte; + } + + private Pte GetPtEntryForWrite(long position) + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + long old = position; + + Pte* lvl1 = _pageTable[l0]; + + if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) + { + return default(Pte); + } + + if (lvl1 == null) + { + return default(Pte); + } + + position &= PageMask; + + Pte pte = lvl1[l1]; + + if (pte.IsUnmapped) + { + if (!_observedPages.TryGetValue(old >> PageBits, out pte)) + { + return default(Pte); + } + else + { + SetPtEntry(position, pte.pa, pte.ptr); + + ObservedAccess?.Invoke(this, new MemoryAccessEventArgs(position)); + } + } + + return pte; + } + + private void SetPtEntries(long va, long pa, byte* ptr, long size) { long endPosition = (va + size + PageMask) & ~PageMask; while ((ulong)va < (ulong)endPosition) { - SetPtEntry(va, ptr); + SetPtEntry(va, pa, ptr); va += PageSize; + pa += PageSize; if (ptr != null) { @@ -763,7 +1017,7 @@ Unmapped: } } - private void SetPtEntry(long position, byte* ptr) + private void SetPtEntry(long position, long pa, byte* ptr) { if (!IsValidPosition(position)) { @@ -775,11 +1029,11 @@ Unmapped: if (_pageTable[l0] == null) { - byte** lvl1 = (byte**)Marshal.AllocHGlobal(PtLvl1Size * IntPtr.Size); + Pte* lvl1 = (Pte*)Marshal.AllocHGlobal(PtLvl1Size * sizeof(Pte)); for (int zl1 = 0; zl1 < PtLvl1Size; zl1++) { - lvl1[zl1] = null; + lvl1[zl1] = default(Pte); } Thread.MemoryBarrier(); @@ -787,7 +1041,8 @@ Unmapped: _pageTable[l0] = lvl1; } - _pageTable[l0][l1] = ptr; + _pageTable[l0][l1].ptr = ptr; + _pageTable[l0][l1].pa = pa; } public void StartObservingRegion(long position, long size) @@ -798,9 +1053,9 @@ Unmapped: while ((ulong)position < (ulong)endPosition) { - _observedPages[position >> PageBits] = (IntPtr)Translate(position); + _observedPages[position >> PageBits] = GetPtEntry(position); - SetPtEntry(position, null); + SetPtEntry(position, 0, null); position += PageSize; } @@ -814,9 +1069,9 @@ Unmapped: { lock (_observedPages) { - if (_observedPages.TryRemove(position >> PageBits, out IntPtr ptr)) + if (_observedPages.TryRemove(position >> PageBits, out Pte pte)) { - SetPtEntry(position, (byte*)ptr); + SetPtEntry(position, pte.pa, pte.ptr); } } diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 8739fbc677..8055f6dd04 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Common.Logging ServiceNs, ServiceNv, ServicePctl, + ServicePcv, ServicePl, ServicePrepo, ServicePsm, diff --git a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs index 2f50463ded..51899f1d28 100644 --- a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs +++ b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs @@ -26,14 +26,14 @@ namespace Ryujinx.Graphics.Memory private void MemoryAccessHandler(object sender, MemoryAccessEventArgs e) { - long pa = _memory.GetPhysicalAddress(e.Position); + long pa = _memory.GetPhysicalAddress(e.VirtualAddress) - 0x80000000; CachedPages[pa >> PageBits]?.Clear(); } public bool IsRegionModified(long position, long size, NvGpuBufferType bufferType) { - long pa = _memory.GetPhysicalAddress(position); + long pa = _memory.GetPhysicalAddress(position) - 0x80000000; long addr = pa; diff --git a/Ryujinx.HLE/DeviceMemory.cs b/Ryujinx.HLE/DeviceMemory.cs index 310942b872..0313f20667 100644 --- a/Ryujinx.HLE/DeviceMemory.cs +++ b/Ryujinx.HLE/DeviceMemory.cs @@ -113,6 +113,24 @@ namespace Ryujinx.HLE } } + public IntPtr GetRamPointer(ulong address, ulong size) + { + if (address + size < address) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + if (address + size > RamSize) + { + throw new ArgumentOutOfRangeException(nameof(address)); + } + + unsafe + { + return new IntPtr(_ramPtr + address); + } + } + public void Set(ulong address, byte value, ulong size) { if (address + size < address) diff --git a/Ryujinx.HLE/HOS/Kernel/Interrupt/KInterruptEvent.cs b/Ryujinx.HLE/HOS/Kernel/Interrupt/KInterruptEvent.cs new file mode 100644 index 0000000000..bab7b788b6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Interrupt/KInterruptEvent.cs @@ -0,0 +1,19 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Kernel.Interrupt +{ + class KInterruptEvent + { + public KReadableEvent Event { get; } + + public int IrqId { get; private set; } + public bool Enabled { get; private set; } + + public KInterruptEvent(Horizon system) + { + Event = new KReadableEvent(system); + + IrqId = -1; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs index 7a40139ccc..0c55f36ecb 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs @@ -464,15 +464,72 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public KernelResult MapNormalMemory(long address, long size, MemoryPermission permission) + public KernelResult MapPhysical(ulong address, ulong size, MemoryPermission permission) { //TODO. return KernelResult.Success; } - public KernelResult MapIoMemory(long address, long size, MemoryPermission permission) + public KernelResult MapIo(ulong pa, ulong size, MemoryPermission permission) { - //TODO. + ulong endAddr = pa + size; + + if (pa >> 31 <= 4 && (endAddr >> 31) != 0) + { + return KernelResult.InvalidAddress; + } + + if (!ValidateIoPhysicalAddress(pa, size)) + { + return KernelResult.InvalidAddress; + } + + ulong neededPagesCount = size / PageSize; + + ulong regionPagesCount = (TlsIoRegionEnd - TlsIoRegionStart) / PageSize; + + if (regionPagesCount < neededPagesCount) + { + return KernelResult.OutOfMemory; + } + + lock (_blocks) + { + ulong va = 0; + + for (int unit = MappingUnitSizes.Length - 1; unit >= 0 && va == 0; unit--) + { + int alignemnt = MappingUnitSizes[unit]; + + va = AllocateVa(TlsIoRegionStart, regionPagesCount, neededPagesCount, alignemnt); + } + + if (va == 0) + { + return KernelResult.OutOfVaSpace; + } + + if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion)) + { + return KernelResult.OutOfResource; + } + + KernelResult result = DoMmuOperation( + va, + neededPagesCount, + pa, + true, + permission, + MemoryOperation.MapPa); + + if (result != KernelResult.Success) + { + return result; + } + + InsertBlock(va, neededPagesCount, MemoryState.Io, permission); + } + return KernelResult.Success; } @@ -940,6 +997,64 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } + public KernelResult QueryIoMapping(ulong pa, ulong size, out ulong va) + { + va = 0; + + KernelResult result = KernelResult.NotFound; + + ulong paEnd = pa + size; + + ulong tlsIoRegionSize = TlsIoRegionEnd - TlsIoRegionStart; + + lock (_blocks) + { + ulong offset = 0; + + foreach ((long mapPa, long mapSize) in _cpuMemory.IteratePages((long)TlsIoRegionStart)) + { + ulong mapPaEnd = (ulong)mapPa + (ulong)mapSize; + + if ((ulong)mapPa <= pa && paEnd <= mapPaEnd) + { + ulong paSubOffs = pa - (ulong)mapPa; + + ulong ioAddr = TlsIoRegionStart + offset + paSubOffs; + + if (CheckRange( + ioAddr, + size, + MemoryState.Mask, + MemoryState.Io, + MemoryPermission.Read, + MemoryPermission.Read, + MemoryAttribute.None, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _)) + { + va = ioAddr; + + result = KernelResult.Success; + } + + break; + } + + offset += (ulong)mapSize; + + if (offset >= tlsIoRegionSize) + { + break; + } + } + } + + return result; + } + public KernelResult Map(ulong dst, ulong src, ulong size) { bool success; @@ -1958,6 +2073,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { mappedVa = 0; + if (AliasRegionEnd - AliasRegionStart <= size) + { + return KernelResult.OutOfVaSpace; + } + lock (_blocks) { if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion)) @@ -2718,16 +2838,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong regionEndAddr = regionStart + regionPagesCount * PageSize; - LinkedListNode node = FindBlockNode(regionStart); - - KMemoryInfo info = node.Value.GetInfo(); - - while (regionEndAddr >= info.Address) + foreach (KMemoryInfo info in IterateOverRange(regionStart, regionEndAddr)) { if (info.State == MemoryState.Unmapped) { - ulong currBaseAddr = info.Address + reservedSize; - ulong currEndAddr = info.Address + info.Size - 1; + ulong currBaseAddr = GetAddrInRange(info, regionStart) + reservedSize; ulong address = BitUtils.AlignDown(currBaseAddr, alignment) + reservedStart; @@ -2738,22 +2853,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong allocationEndAddr = address + totalNeededSize - 1; - if (allocationEndAddr <= regionEndAddr && - allocationEndAddr <= currEndAddr && + ulong currEndAddr = info.Address + info.Size - 1; + + if (allocationEndAddr <= regionEndAddr - 1 && + allocationEndAddr <= currEndAddr && address < allocationEndAddr) { return address; } } - - node = node.Next; - - if (node == null) - { - break; - } - - info = node.Value.GetInfo(); } return 0; @@ -2788,6 +2896,60 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return null; } + private static bool ValidateIoPhysicalAddress(ulong pa, ulong size) + { + //Size would cause an overflow (or the address is invalid). + if (pa + size < pa) + { + return false; + } + + ulong endAddr = pa + size - 1; + + //ARM registers, interrupt controller, etc region. + if (0x50040000 <= endAddr && pa <= 0x5005ffff) + { + return false; + } + + //Check exception vectors region. + if (0x6000f000 <= endAddr && pa <= 0x6000ffff) + { + return false; + } + + //Check IPATCH region. + if (0x6001dc00 <= endAddr && pa <= 0x6001dfff) + { + return false; + } + + //MC region. + if (0x70019000 <= endAddr && pa <= 0x70019fff) + { + return false; + } + + //RTC and PMC region. + if (0x7000e000 <= endAddr && pa <= 0x7000efff) + { + return false; + } + + //MC0 and MC1 region. + if (0x7001c000 <= endAddr && pa <= 0x7001dfff) + { + return false; + } + + return true; + } + + private static bool InsideRange(ulong start, ulong end, ulong rgStart, ulong rgEnd) + { + return start <= rgEnd && rgStart <= end; + } + private bool ValidateRegionForState(ulong address, ulong size, MemoryState state) { ulong endAddr = address + size; @@ -3038,7 +3200,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong size = pagesCount * PageSize; - _cpuMemory.Map((long)dstVa, (long)(srcPa - DramMemoryMap.DramBase), (long)size); + MapMemoryOrIo(dstVa, srcPa, size); result = KernelResult.Success; @@ -3105,7 +3267,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong size = pageNode.PagesCount * PageSize; - _cpuMemory.Map((long)address, (long)(pageNode.Address - DramMemoryMap.DramBase), (long)size); + MapMemoryOrIo(address, pageNode.Address, size); address += size; } @@ -3113,14 +3275,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.Success; } + private void MapMemoryOrIo(ulong va, ulong pa, ulong size) + { + if (pa >= DramMemoryMap.DramBase) + { + IntPtr ptr = _system.Device.Memory.GetRamPointer(pa - DramMemoryMap.DramBase, size); + + _cpuMemory.Map((long)va, (long)pa, ptr, (long)size); + } + else + { + _cpuMemory.Map((long)va, (long)pa, IntPtr.Zero, (long)size); + } + } + public ulong GetDramAddressFromVa(ulong va) { - return (ulong)_cpuMemory.GetPhysicalAddress((long)va); + return (ulong)_cpuMemory.GetPhysicalAddress((long)va) - DramMemoryMap.DramBase; } public bool ConvertVaToPa(ulong va, out ulong pa) { - pa = DramMemoryMap.DramBase + (ulong)_cpuMemory.GetPhysicalAddress((long)va); + pa = (ulong)_cpuMemory.GetPhysicalAddress((long)va); return true; } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 855f3a1896..a4150b4748 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -1,6 +1,7 @@ using ChocolArm64; using ChocolArm64.Events; using ChocolArm64.Memory; +using Luea.Devices; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Common; @@ -91,7 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _processLock = new object(); _threadingLock = new object(); - CpuMemory = new MemoryManager(system.Device.Memory.RamPointer); + DummyDevice device = new DummyDevice(); + + CpuMemory = new MemoryManager(device); CpuMemory.InvalidAccess += InvalidAccessHandler; @@ -1010,6 +1013,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private void InvalidAccessHandler(object sender, MemoryAccessEventArgs e) { PrintCurrentThreadStackTrace(); + + throw new Exception($"Attempted to access unmapped address 0x{e.VirtualAddress:X16}."); } public void PrintCurrentThreadStackTrace() diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index 964762bb6f..e2c27f9417 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -83,8 +83,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return KernelResult.InvalidSize; } - long address = ((long)(uint)prevCap << 5) & 0xffffff000; - long size = ((long)(uint)cap << 5) & 0xfffff000; + ulong address = ((ulong)(uint)prevCap << 5) & 0xffffff000; + ulong size = ((ulong)(uint)cap << 5) & 0xfffff000; if (((ulong)(address + size - 1) >> 36) != 0) { @@ -99,11 +99,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if ((cap >> 31) != 0) { - result = memoryManager.MapNormalMemory(address, size, perm); + result = memoryManager.MapPhysical(address, size, perm); } else { - result = memoryManager.MapIoMemory(address, size, perm); + result = memoryManager.MapIo(address, size, perm); } if (result != KernelResult.Success) @@ -214,9 +214,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process case 0x80: { - long address = ((long)(uint)cap << 4) & 0xffffff000; + ulong address = ((ulong)(uint)cap << 4) & 0xffffff000; - memoryManager.MapIoMemory(address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite); + memoryManager.MapIo(address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite); break; } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs index b9ac682b69..010a490c1f 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs @@ -386,6 +386,30 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _process.MemoryManager.UnmapPhysicalMemory(address, size); } + public KernelResult QueryIoMapping64(ulong pa, ulong size, out ulong va) + { + return QueryIoMapping(pa, size, out va); + } + + private KernelResult QueryIoMapping(ulong pa, ulong size, out ulong va) + { + va = 0; + + if (size == 0) + { + return KernelResult.InvalidSize; + } + + if (pa + size <= pa) + { + return KernelResult.NotFound; + } + + KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); + + return currentProcess.MemoryManager.QueryIoMapping(pa, size, out va); + } + public KernelResult CreateDeviceAddressSpace64(ulong address, ulong size, out int handle) { return CreateDeviceAddressSpace(address, size, out handle); @@ -431,7 +455,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return deviceAs.Attach(deviceName); } - public KernelResult MapDeviceAddressSpace64( + public KernelResult MapDeviceAddressSpaceByForce64( int dasHandle, int processHandle, ulong mapAddress, diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs index be136ff0a5..b691139fa3 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs @@ -3,6 +3,7 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Interrupt; using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; @@ -465,6 +466,37 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } + public KernelResult CreateInterruptEvent64(int irqId, uint flags, out int handle) + { + return CreateInterruptEvent(irqId, flags, out handle); + } + + private KernelResult CreateInterruptEvent(int irqId, uint flags, out int handle) + { + handle = 0; + + if (flags > 1) + { + return KernelResult.InvalidEnumValue; + } + + if ((uint)irqId > 0x3ff) + { + return KernelResult.NotFound; + } + + KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); + + if ((currentProcess.Capabilities.IrqAccessMask[irqId / 8] & (1 << (irqId & 7))) == 0) + { + return KernelResult.NotFound; + } + + KInterruptEvent interruptEvent = new KInterruptEvent(_system); + + return currentProcess.HandleTable.GenerateHandle(interruptEvent.Event, out handle); + } + public KernelResult GetProcessList64(ulong address, int maxCount, out int count) { return GetProcessList(address, maxCount, out count); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs index 1e3d8db7b3..df30553086 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs @@ -67,9 +67,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { 0x41, nameof(SvcHandler.AcceptSession64) }, { 0x43, nameof(SvcHandler.ReplyAndReceive64) }, { 0x45, nameof(SvcHandler.CreateEvent64) }, + { 0x53, nameof(SvcHandler.CreateInterruptEvent64) }, + { 0x55, nameof(SvcHandler.QueryIoMapping64) }, { 0x56, nameof(SvcHandler.CreateDeviceAddressSpace64) }, { 0x57, nameof(SvcHandler.AttachDeviceAddressSpace64) }, - { 0x5b, nameof(SvcHandler.MapDeviceAddressSpace64) }, + { 0x59, nameof(SvcHandler.MapDeviceAddressSpaceByForce64) }, { 0x65, nameof(SvcHandler.GetProcessList64) }, { 0x6f, nameof(SvcHandler.GetSystemInfo64) }, { 0x70, nameof(SvcHandler.CreatePort64) }, diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs index dd9822918c..8bdb5e52ba 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public KEvent(Horizon system) { - ReadableEvent = new KReadableEvent(system, this); + ReadableEvent = new KReadableEvent(system); WritableEvent = new KWritableEvent(system, this); } } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs index 9821de35b1..fa13dbace2 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs @@ -4,14 +4,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { class KReadableEvent : KSynchronizationObject { - private KEvent _parent; - private bool _signaled; - public KReadableEvent(Horizon system, KEvent parent) : base(system) - { - _parent = parent; - } + public KReadableEvent(Horizon system) : base(system) { } public override void Signal() { diff --git a/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs b/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs new file mode 100644 index 0000000000..db465bbbc8 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs @@ -0,0 +1,52 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Pcv +{ + class IPcvService : IpcService + { + private Dictionary _commands; + + public override IReadOnlyDictionary Commands => _commands; + + public IPcvService(bool needInitialize = true) + { + _commands = new Dictionary + { + { 0, SetPowerEnabled }, + { 1, SetClockEnabled }, + { 2, SetClockRate }, + { 7, SetReset } + }; + } + + private long SetPowerEnabled(ServiceCtx context) + { + Logger.PrintStub(LogClass.ServicePcv, "Stubbed."); + + return 0; + } + + private long SetClockEnabled(ServiceCtx context) + { + Logger.PrintStub(LogClass.ServicePcv, "Stubbed."); + + return 0; + } + + private long SetClockRate(ServiceCtx context) + { + Logger.PrintStub(LogClass.ServicePcv, "Stubbed."); + + return 0; + } + + private long SetReset(ServiceCtx context) + { + Logger.PrintStub(LogClass.ServicePcv, "Stubbed."); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index 3853d82e6f..871597eb53 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -16,6 +16,7 @@ using Ryujinx.HLE.HOS.Services.Nfp; using Ryujinx.HLE.HOS.Services.Ns; using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.Services.Pctl; +using Ryujinx.HLE.HOS.Services.Pcv; using Ryujinx.HLE.HOS.Services.Pl; using Ryujinx.HLE.HOS.Services.Prepo; using Ryujinx.HLE.HOS.Services.Psm; @@ -158,6 +159,9 @@ namespace Ryujinx.HLE.HOS.Services case "pctl": return new IParentalControlServiceFactory(); + case "pcv": + return new IPcvService(); + case "pl:u": return new ISharedFontManager(); diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index 6285825a5c..1097576f8e 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -25,6 +25,7 @@ + diff --git a/Ryujinx.LLE/Devices/Device.cs b/Ryujinx.LLE/Devices/Device.cs new file mode 100644 index 0000000000..22289a07a6 --- /dev/null +++ b/Ryujinx.LLE/Devices/Device.cs @@ -0,0 +1,56 @@ +using ChocolArm64.Memory; +using System; + +namespace Luea.Devices +{ + public class Device : IBus + { + public byte ReadByte(ulong address) + { + Console.WriteLine($"ReadByte to unimplemented device at 0x{address:X16}."); + + return 0; + } + + public ushort ReadUInt16(ulong address) + { + Console.WriteLine($"ReadUInt16 to unimplemented device at 0x{address:X16}."); + + return 0; + } + + public uint ReadUInt32(ulong address) + { + Console.WriteLine($"ReadUInt32 to unimplemented device at 0x{address:X16}."); + + return 0; + } + + public ulong ReadUInt64(ulong address) + { + Console.WriteLine($"ReadUInt64 to unimplemented device at 0x{address:X16}."); + + return 0; + } + + public void WriteByte(ulong address, byte value) + { + Console.WriteLine($"WriteByte to unimplemented device at 0x{address:X16} = 0x{value:X2}."); + } + + public void WriteUInt16(ulong address, ushort value) + { + Console.WriteLine($"WriteUInt16 to unimplemented device at 0x{address:X16} = 0x{value:X4}."); + } + + public void WriteUInt32(ulong address, uint value) + { + Console.WriteLine($"WriteUInt32 to unimplemented device at 0x{address:X16} = 0x{value:X8}."); + } + + public void WriteUInt64(ulong address, ulong value) + { + Console.WriteLine($"WriteUInt64 to unimplemented device at 0x{address:X16} = 0x{value:X16}."); + } + } +} diff --git a/Ryujinx.LLE/Devices/DummyDevice.cs b/Ryujinx.LLE/Devices/DummyDevice.cs new file mode 100644 index 0000000000..2f7d104209 --- /dev/null +++ b/Ryujinx.LLE/Devices/DummyDevice.cs @@ -0,0 +1,7 @@ +namespace Luea.Devices +{ + public class DummyDevice : Device + { + + } +} diff --git a/Ryujinx.LLE/Luea.csproj b/Ryujinx.LLE/Luea.csproj index 5c57156812..45b1bb530f 100644 --- a/Ryujinx.LLE/Luea.csproj +++ b/Ryujinx.LLE/Luea.csproj @@ -6,4 +6,8 @@ Exe + + + + diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index b970e05540..291f169a9d 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -50,8 +50,8 @@ namespace Ryujinx.Tests.Cpu Translator translator = new Translator(); _ramPointer = Marshal.AllocHGlobal(new IntPtr(_size)); - _memory = new MemoryManager(_ramPointer); - _memory.Map(Position, 0, _size); + _memory = new MemoryManager(); + _memory.Map(Position, Position, _ramPointer, _size); _thread = new CpuThread(translator, _memory, _entryPoint); if (_unicornAvailable)