Implement partial support for device address space SVCs, CreateInterruptEvent, initial work to support devices

This commit is contained in:
gdkchan 2019-01-03 22:58:18 -03:00
parent d215478b9e
commit 72d972d70c
23 changed files with 772 additions and 106 deletions

View file

@ -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;
}
}
}

View file

@ -0,0 +1,14 @@
using System;
namespace ChocolArm64.Events
{
public class MemoryAccessEventArgs : EventArgs
{
public long VirtualAddress { get; }
public MemoryAccessEventArgs(long va)
{
VirtualAddress = va;
}
}
}

View file

@ -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);
}
}

View file

@ -45,29 +45,33 @@ namespace ChocolArm64.Memory
private Dictionary<int, ArmMonitor> _monitors;
private ConcurrentDictionary<long, IntPtr> _observedPages;
private ConcurrentDictionary<long, Pte> _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<MemoryAccessEventArgs> InvalidAccess;
public event EventHandler<MemoryAccessEventArgs> ObservedAccess;
public MemoryManager(IntPtr ram)
public MemoryManager(IBus bus = null)
{
_monitors = new Dictionary<int, ArmMonitor>();
_observedPages = new ConcurrentDictionary<long, IntPtr>();
_observedPages = new ConcurrentDictionary<long, Pte>();
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);
}
}

View file

@ -31,6 +31,7 @@ namespace Ryujinx.Common.Logging
ServiceNs,
ServiceNv,
ServicePctl,
ServicePcv,
ServicePl,
ServicePrepo,
ServicePsm,

View file

@ -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;

View file

@ -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)

View file

@ -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;
}
}
}

View file

@ -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<KMemoryBlock> 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;
}

View file

@ -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()

View file

@ -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;
}

View file

@ -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,

View file

@ -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);

View file

@ -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) },

View file

@ -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);
}
}

View file

@ -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()
{

View file

@ -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<int, ServiceProcessRequest> _commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
public IPcvService(bool needInitialize = true)
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
{ 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;
}
}
}

View file

@ -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();

View file

@ -25,6 +25,7 @@
<ItemGroup>
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
<ProjectReference Include="..\Ryujinx.LLE\Luea.csproj" />
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />

View file

@ -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}.");
}
}
}

View file

@ -0,0 +1,7 @@
namespace Luea.Devices
{
public class DummyDevice : Device
{
}
}

View file

@ -6,4 +6,8 @@
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
</ItemGroup>
</Project>

View file

@ -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)