Implement partial support for device address space SVCs, CreateInterruptEvent, initial work to support devices
This commit is contained in:
parent
d215478b9e
commit
72d972d70c
23 changed files with 772 additions and 106 deletions
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
14
ChocolArm64/Events/MemoryAccessEventArgs.cs
Normal file
14
ChocolArm64/Events/MemoryAccessEventArgs.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ChocolArm64.Events
|
||||||
|
{
|
||||||
|
public class MemoryAccessEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public long VirtualAddress { get; }
|
||||||
|
|
||||||
|
public MemoryAccessEventArgs(long va)
|
||||||
|
{
|
||||||
|
VirtualAddress = va;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
ChocolArm64/Memory/IBus.cs
Normal file
15
ChocolArm64/Memory/IBus.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,29 +45,33 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
private Dictionary<int, ArmMonitor> _monitors;
|
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> InvalidAccess;
|
||||||
|
|
||||||
public event EventHandler<MemoryAccessEventArgs> ObservedAccess;
|
public event EventHandler<MemoryAccessEventArgs> ObservedAccess;
|
||||||
|
|
||||||
public MemoryManager(IntPtr ram)
|
public MemoryManager(IBus bus = null)
|
||||||
{
|
{
|
||||||
_monitors = new Dictionary<int, ArmMonitor>();
|
_monitors = new Dictionary<int, ArmMonitor>();
|
||||||
|
|
||||||
_observedPages = new ConcurrentDictionary<long, IntPtr>();
|
_observedPages = new ConcurrentDictionary<long, Pte>();
|
||||||
|
|
||||||
Ram = ram;
|
_bus = bus;
|
||||||
|
|
||||||
_ramPtr = (byte*)ram;
|
_pageTable = (Pte**)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size);
|
||||||
|
|
||||||
_pageTable = (byte***)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size);
|
|
||||||
|
|
||||||
for (int l0 = 0; l0 < PtLvl0Size; l0++)
|
for (int l0 = 0; l0 < PtLvl0Size; l0++)
|
||||||
{
|
{
|
||||||
|
@ -197,14 +201,48 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public byte ReadByte(long position)
|
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)
|
public ushort ReadUInt16(long position)
|
||||||
{
|
{
|
||||||
if ((position & 1) == 0)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -217,7 +255,24 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
if ((position & 3) == 0)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -230,7 +285,24 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
if ((position & 7) == 0)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -419,14 +491,44 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public void WriteByte(long position, byte value)
|
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)
|
public void WriteUInt16(long position, ushort value)
|
||||||
{
|
{
|
||||||
if ((position & 1) == 0)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -439,7 +541,22 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
if ((position & 3) == 0)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -452,7 +569,22 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
if ((position & 7) == 0)
|
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
|
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)
|
public void Unmap(long position, long size)
|
||||||
{
|
{
|
||||||
SetPtEntries(position, null, size);
|
SetPtEntries(position, 0, null, size);
|
||||||
|
|
||||||
StopObservingRegion(position, size);
|
StopObservingRegion(position, size);
|
||||||
}
|
}
|
||||||
|
@ -634,14 +766,64 @@ namespace ChocolArm64.Memory
|
||||||
return false;
|
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)
|
public long GetPhysicalAddress(long virtualAddress)
|
||||||
{
|
{
|
||||||
byte* ptr = Translate(virtualAddress);
|
return GetPtEntry(virtualAddress).pa + (virtualAddress & PageMask);
|
||||||
|
|
||||||
return (long)(ptr - _ramPtr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal byte* Translate(long position)
|
internal byte* Translate(long position)
|
||||||
|
@ -651,7 +833,7 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
long old = position;
|
long old = position;
|
||||||
|
|
||||||
byte** lvl1 = _pageTable[l0];
|
Pte* lvl1 = _pageTable[l0];
|
||||||
|
|
||||||
if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
|
if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
|
||||||
{
|
{
|
||||||
|
@ -665,7 +847,7 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
position &= PageMask;
|
position &= PageMask;
|
||||||
|
|
||||||
byte* ptr = lvl1[l1];
|
byte* ptr = lvl1[l1].ptr;
|
||||||
|
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
|
@ -682,9 +864,9 @@ Unmapped:
|
||||||
{
|
{
|
||||||
long key = position >> PageBits;
|
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));
|
InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position));
|
||||||
|
@ -699,7 +881,7 @@ Unmapped:
|
||||||
|
|
||||||
long old = position;
|
long old = position;
|
||||||
|
|
||||||
byte** lvl1 = _pageTable[l0];
|
Pte* lvl1 = _pageTable[l0];
|
||||||
|
|
||||||
if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
|
if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
|
||||||
{
|
{
|
||||||
|
@ -713,7 +895,7 @@ Unmapped:
|
||||||
|
|
||||||
position &= PageMask;
|
position &= PageMask;
|
||||||
|
|
||||||
byte* ptr = lvl1[l1];
|
byte* ptr = lvl1[l1].ptr;
|
||||||
|
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
|
@ -728,17 +910,17 @@ Unmapped:
|
||||||
|
|
||||||
private byte* HandleNullPteWrite(long position)
|
private byte* HandleNullPteWrite(long position)
|
||||||
{
|
{
|
||||||
long key = position >> PageBits;
|
|
||||||
|
|
||||||
MemoryAccessEventArgs e = new MemoryAccessEventArgs(position);
|
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);
|
ObservedAccess?.Invoke(this, e);
|
||||||
|
|
||||||
return (byte*)ptr + (position & PageMask);
|
return (byte*)pte.ptr + (position & PageMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
InvalidAccess?.Invoke(this, e);
|
InvalidAccess?.Invoke(this, e);
|
||||||
|
@ -746,15 +928,87 @@ Unmapped:
|
||||||
throw new VmmPageFaultException(position);
|
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;
|
long endPosition = (va + size + PageMask) & ~PageMask;
|
||||||
|
|
||||||
while ((ulong)va < (ulong)endPosition)
|
while ((ulong)va < (ulong)endPosition)
|
||||||
{
|
{
|
||||||
SetPtEntry(va, ptr);
|
SetPtEntry(va, pa, ptr);
|
||||||
|
|
||||||
va += PageSize;
|
va += PageSize;
|
||||||
|
pa += PageSize;
|
||||||
|
|
||||||
if (ptr != null)
|
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))
|
if (!IsValidPosition(position))
|
||||||
{
|
{
|
||||||
|
@ -775,11 +1029,11 @@ Unmapped:
|
||||||
|
|
||||||
if (_pageTable[l0] == null)
|
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++)
|
for (int zl1 = 0; zl1 < PtLvl1Size; zl1++)
|
||||||
{
|
{
|
||||||
lvl1[zl1] = null;
|
lvl1[zl1] = default(Pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.MemoryBarrier();
|
Thread.MemoryBarrier();
|
||||||
|
@ -787,7 +1041,8 @@ Unmapped:
|
||||||
_pageTable[l0] = lvl1;
|
_pageTable[l0] = lvl1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_pageTable[l0][l1] = ptr;
|
_pageTable[l0][l1].ptr = ptr;
|
||||||
|
_pageTable[l0][l1].pa = pa;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartObservingRegion(long position, long size)
|
public void StartObservingRegion(long position, long size)
|
||||||
|
@ -798,9 +1053,9 @@ Unmapped:
|
||||||
|
|
||||||
while ((ulong)position < (ulong)endPosition)
|
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;
|
position += PageSize;
|
||||||
}
|
}
|
||||||
|
@ -814,9 +1069,9 @@ Unmapped:
|
||||||
{
|
{
|
||||||
lock (_observedPages)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace Ryujinx.Common.Logging
|
||||||
ServiceNs,
|
ServiceNs,
|
||||||
ServiceNv,
|
ServiceNv,
|
||||||
ServicePctl,
|
ServicePctl,
|
||||||
|
ServicePcv,
|
||||||
ServicePl,
|
ServicePl,
|
||||||
ServicePrepo,
|
ServicePrepo,
|
||||||
ServicePsm,
|
ServicePsm,
|
||||||
|
|
|
@ -26,14 +26,14 @@ namespace Ryujinx.Graphics.Memory
|
||||||
|
|
||||||
private void MemoryAccessHandler(object sender, MemoryAccessEventArgs e)
|
private void MemoryAccessHandler(object sender, MemoryAccessEventArgs e)
|
||||||
{
|
{
|
||||||
long pa = _memory.GetPhysicalAddress(e.Position);
|
long pa = _memory.GetPhysicalAddress(e.VirtualAddress) - 0x80000000;
|
||||||
|
|
||||||
CachedPages[pa >> PageBits]?.Clear();
|
CachedPages[pa >> PageBits]?.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRegionModified(long position, long size, NvGpuBufferType bufferType)
|
public bool IsRegionModified(long position, long size, NvGpuBufferType bufferType)
|
||||||
{
|
{
|
||||||
long pa = _memory.GetPhysicalAddress(position);
|
long pa = _memory.GetPhysicalAddress(position) - 0x80000000;
|
||||||
|
|
||||||
long addr = pa;
|
long addr = pa;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
public void Set(ulong address, byte value, ulong size)
|
||||||
{
|
{
|
||||||
if (address + size < address)
|
if (address + size < address)
|
||||||
|
|
19
Ryujinx.HLE/HOS/Kernel/Interrupt/KInterruptEvent.cs
Normal file
19
Ryujinx.HLE/HOS/Kernel/Interrupt/KInterruptEvent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
//TODO.
|
||||||
return KernelResult.Success;
|
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;
|
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)
|
public KernelResult Map(ulong dst, ulong src, ulong size)
|
||||||
{
|
{
|
||||||
bool success;
|
bool success;
|
||||||
|
@ -1958,6 +2073,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
mappedVa = 0;
|
mappedVa = 0;
|
||||||
|
|
||||||
|
if (AliasRegionEnd - AliasRegionStart <= size)
|
||||||
|
{
|
||||||
|
return KernelResult.OutOfVaSpace;
|
||||||
|
}
|
||||||
|
|
||||||
lock (_blocks)
|
lock (_blocks)
|
||||||
{
|
{
|
||||||
if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
|
if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
|
||||||
|
@ -2718,16 +2838,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
|
ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
|
||||||
|
|
||||||
LinkedListNode<KMemoryBlock> node = FindBlockNode(regionStart);
|
foreach (KMemoryInfo info in IterateOverRange(regionStart, regionEndAddr))
|
||||||
|
|
||||||
KMemoryInfo info = node.Value.GetInfo();
|
|
||||||
|
|
||||||
while (regionEndAddr >= info.Address)
|
|
||||||
{
|
{
|
||||||
if (info.State == MemoryState.Unmapped)
|
if (info.State == MemoryState.Unmapped)
|
||||||
{
|
{
|
||||||
ulong currBaseAddr = info.Address + reservedSize;
|
ulong currBaseAddr = GetAddrInRange(info, regionStart) + reservedSize;
|
||||||
ulong currEndAddr = info.Address + info.Size - 1;
|
|
||||||
|
|
||||||
ulong address = BitUtils.AlignDown(currBaseAddr, alignment) + reservedStart;
|
ulong address = BitUtils.AlignDown(currBaseAddr, alignment) + reservedStart;
|
||||||
|
|
||||||
|
@ -2738,22 +2853,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
ulong allocationEndAddr = address + totalNeededSize - 1;
|
ulong allocationEndAddr = address + totalNeededSize - 1;
|
||||||
|
|
||||||
if (allocationEndAddr <= regionEndAddr &&
|
ulong currEndAddr = info.Address + info.Size - 1;
|
||||||
|
|
||||||
|
if (allocationEndAddr <= regionEndAddr - 1 &&
|
||||||
allocationEndAddr <= currEndAddr &&
|
allocationEndAddr <= currEndAddr &&
|
||||||
address < allocationEndAddr)
|
address < allocationEndAddr)
|
||||||
{
|
{
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.Next;
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
info = node.Value.GetInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2788,6 +2896,60 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
return null;
|
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)
|
private bool ValidateRegionForState(ulong address, ulong size, MemoryState state)
|
||||||
{
|
{
|
||||||
ulong endAddr = address + size;
|
ulong endAddr = address + size;
|
||||||
|
@ -3038,7 +3200,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
ulong size = pagesCount * PageSize;
|
ulong size = pagesCount * PageSize;
|
||||||
|
|
||||||
_cpuMemory.Map((long)dstVa, (long)(srcPa - DramMemoryMap.DramBase), (long)size);
|
MapMemoryOrIo(dstVa, srcPa, size);
|
||||||
|
|
||||||
result = KernelResult.Success;
|
result = KernelResult.Success;
|
||||||
|
|
||||||
|
@ -3105,7 +3267,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
ulong size = pageNode.PagesCount * PageSize;
|
ulong size = pageNode.PagesCount * PageSize;
|
||||||
|
|
||||||
_cpuMemory.Map((long)address, (long)(pageNode.Address - DramMemoryMap.DramBase), (long)size);
|
MapMemoryOrIo(address, pageNode.Address, size);
|
||||||
|
|
||||||
address += size;
|
address += size;
|
||||||
}
|
}
|
||||||
|
@ -3113,14 +3275,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
return KernelResult.Success;
|
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)
|
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)
|
public bool ConvertVaToPa(ulong va, out ulong pa)
|
||||||
{
|
{
|
||||||
pa = DramMemoryMap.DramBase + (ulong)_cpuMemory.GetPhysicalAddress((long)va);
|
pa = (ulong)_cpuMemory.GetPhysicalAddress((long)va);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64;
|
using ChocolArm64;
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
|
using Luea.Devices;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
@ -91,7 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
_processLock = new object();
|
_processLock = new object();
|
||||||
_threadingLock = new object();
|
_threadingLock = new object();
|
||||||
|
|
||||||
CpuMemory = new MemoryManager(system.Device.Memory.RamPointer);
|
DummyDevice device = new DummyDevice();
|
||||||
|
|
||||||
|
CpuMemory = new MemoryManager(device);
|
||||||
|
|
||||||
CpuMemory.InvalidAccess += InvalidAccessHandler;
|
CpuMemory.InvalidAccess += InvalidAccessHandler;
|
||||||
|
|
||||||
|
@ -1010,6 +1013,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
private void InvalidAccessHandler(object sender, MemoryAccessEventArgs e)
|
private void InvalidAccessHandler(object sender, MemoryAccessEventArgs e)
|
||||||
{
|
{
|
||||||
PrintCurrentThreadStackTrace();
|
PrintCurrentThreadStackTrace();
|
||||||
|
|
||||||
|
throw new Exception($"Attempted to access unmapped address 0x{e.VirtualAddress:X16}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintCurrentThreadStackTrace()
|
public void PrintCurrentThreadStackTrace()
|
||||||
|
|
|
@ -83,8 +83,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
return KernelResult.InvalidSize;
|
return KernelResult.InvalidSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
long address = ((long)(uint)prevCap << 5) & 0xffffff000;
|
ulong address = ((ulong)(uint)prevCap << 5) & 0xffffff000;
|
||||||
long size = ((long)(uint)cap << 5) & 0xfffff000;
|
ulong size = ((ulong)(uint)cap << 5) & 0xfffff000;
|
||||||
|
|
||||||
if (((ulong)(address + size - 1) >> 36) != 0)
|
if (((ulong)(address + size - 1) >> 36) != 0)
|
||||||
{
|
{
|
||||||
|
@ -99,11 +99,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
if ((cap >> 31) != 0)
|
if ((cap >> 31) != 0)
|
||||||
{
|
{
|
||||||
result = memoryManager.MapNormalMemory(address, size, perm);
|
result = memoryManager.MapPhysical(address, size, perm);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = memoryManager.MapIoMemory(address, size, perm);
|
result = memoryManager.MapIo(address, size, perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
|
@ -214,9 +214,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
case 0x80:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,6 +386,30 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
return _process.MemoryManager.UnmapPhysicalMemory(address, size);
|
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)
|
public KernelResult CreateDeviceAddressSpace64(ulong address, ulong size, out int handle)
|
||||||
{
|
{
|
||||||
return CreateDeviceAddressSpace(address, size, out handle);
|
return CreateDeviceAddressSpace(address, size, out handle);
|
||||||
|
@ -431,7 +455,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
return deviceAs.Attach(deviceName);
|
return deviceAs.Attach(deviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult MapDeviceAddressSpace64(
|
public KernelResult MapDeviceAddressSpaceByForce64(
|
||||||
int dasHandle,
|
int dasHandle,
|
||||||
int processHandle,
|
int processHandle,
|
||||||
ulong mapAddress,
|
ulong mapAddress,
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Interrupt;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
@ -465,6 +466,37 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
return result;
|
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)
|
public KernelResult GetProcessList64(ulong address, int maxCount, out int count)
|
||||||
{
|
{
|
||||||
return GetProcessList(address, maxCount, out count);
|
return GetProcessList(address, maxCount, out count);
|
||||||
|
|
|
@ -67,9 +67,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
{ 0x41, nameof(SvcHandler.AcceptSession64) },
|
{ 0x41, nameof(SvcHandler.AcceptSession64) },
|
||||||
{ 0x43, nameof(SvcHandler.ReplyAndReceive64) },
|
{ 0x43, nameof(SvcHandler.ReplyAndReceive64) },
|
||||||
{ 0x45, nameof(SvcHandler.CreateEvent64) },
|
{ 0x45, nameof(SvcHandler.CreateEvent64) },
|
||||||
|
{ 0x53, nameof(SvcHandler.CreateInterruptEvent64) },
|
||||||
|
{ 0x55, nameof(SvcHandler.QueryIoMapping64) },
|
||||||
{ 0x56, nameof(SvcHandler.CreateDeviceAddressSpace64) },
|
{ 0x56, nameof(SvcHandler.CreateDeviceAddressSpace64) },
|
||||||
{ 0x57, nameof(SvcHandler.AttachDeviceAddressSpace64) },
|
{ 0x57, nameof(SvcHandler.AttachDeviceAddressSpace64) },
|
||||||
{ 0x5b, nameof(SvcHandler.MapDeviceAddressSpace64) },
|
{ 0x59, nameof(SvcHandler.MapDeviceAddressSpaceByForce64) },
|
||||||
{ 0x65, nameof(SvcHandler.GetProcessList64) },
|
{ 0x65, nameof(SvcHandler.GetProcessList64) },
|
||||||
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
|
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
|
||||||
{ 0x70, nameof(SvcHandler.CreatePort64) },
|
{ 0x70, nameof(SvcHandler.CreatePort64) },
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
public KEvent(Horizon system)
|
public KEvent(Horizon system)
|
||||||
{
|
{
|
||||||
ReadableEvent = new KReadableEvent(system, this);
|
ReadableEvent = new KReadableEvent(system);
|
||||||
WritableEvent = new KWritableEvent(system, this);
|
WritableEvent = new KWritableEvent(system, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KReadableEvent : KSynchronizationObject
|
class KReadableEvent : KSynchronizationObject
|
||||||
{
|
{
|
||||||
private KEvent _parent;
|
|
||||||
|
|
||||||
private bool _signaled;
|
private bool _signaled;
|
||||||
|
|
||||||
public KReadableEvent(Horizon system, KEvent parent) : base(system)
|
public KReadableEvent(Horizon system) : base(system) { }
|
||||||
{
|
|
||||||
_parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Signal()
|
public override void Signal()
|
||||||
{
|
{
|
||||||
|
|
52
Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs
Normal file
52
Ryujinx.HLE/HOS/Services/Pcv/IPcvService.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ using Ryujinx.HLE.HOS.Services.Nfp;
|
||||||
using Ryujinx.HLE.HOS.Services.Ns;
|
using Ryujinx.HLE.HOS.Services.Ns;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv;
|
using Ryujinx.HLE.HOS.Services.Nv;
|
||||||
using Ryujinx.HLE.HOS.Services.Pctl;
|
using Ryujinx.HLE.HOS.Services.Pctl;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Pcv;
|
||||||
using Ryujinx.HLE.HOS.Services.Pl;
|
using Ryujinx.HLE.HOS.Services.Pl;
|
||||||
using Ryujinx.HLE.HOS.Services.Prepo;
|
using Ryujinx.HLE.HOS.Services.Prepo;
|
||||||
using Ryujinx.HLE.HOS.Services.Psm;
|
using Ryujinx.HLE.HOS.Services.Psm;
|
||||||
|
@ -158,6 +159,9 @@ namespace Ryujinx.HLE.HOS.Services
|
||||||
case "pctl":
|
case "pctl":
|
||||||
return new IParentalControlServiceFactory();
|
return new IParentalControlServiceFactory();
|
||||||
|
|
||||||
|
case "pcv":
|
||||||
|
return new IPcvService();
|
||||||
|
|
||||||
case "pl:u":
|
case "pl:u":
|
||||||
return new ISharedFontManager();
|
return new ISharedFontManager();
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.LLE\Luea.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
||||||
|
|
56
Ryujinx.LLE/Devices/Device.cs
Normal file
56
Ryujinx.LLE/Devices/Device.cs
Normal 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}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
Ryujinx.LLE/Devices/DummyDevice.cs
Normal file
7
Ryujinx.LLE/Devices/DummyDevice.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Luea.Devices
|
||||||
|
{
|
||||||
|
public class DummyDevice : Device
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,4 +6,8 @@
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -50,8 +50,8 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
Translator translator = new Translator();
|
Translator translator = new Translator();
|
||||||
_ramPointer = Marshal.AllocHGlobal(new IntPtr(_size));
|
_ramPointer = Marshal.AllocHGlobal(new IntPtr(_size));
|
||||||
_memory = new MemoryManager(_ramPointer);
|
_memory = new MemoryManager();
|
||||||
_memory.Map(Position, 0, _size);
|
_memory.Map(Position, Position, _ramPointer, _size);
|
||||||
_thread = new CpuThread(translator, _memory, _entryPoint);
|
_thread = new CpuThread(translator, _memory, _entryPoint);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
if (_unicornAvailable)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue