ryujinx/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
gdkchan 5001f78b1d Optimize address translation and write tracking on the MMU (#571)
* Implement faster address translation and write tracking on the MMU

* Rename MemoryAlloc to MemoryManagement, and other nits

* Support multi-level page tables

* Fix typo

* Reword comment a bit

* Support scalar vector loads/stores on the memory fast path, and minor fixes

* Add missing cast

* Alignment

* Fix VirtualFree function signature

* Change MemoryProtection enum to uint aswell for consistency
2019-02-24 18:24:35 +11:00

515 lines
17 KiB
C#

using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
partial class SvcHandler
{
public KernelResult SetHeapSize64(ulong size, out ulong position)
{
return SetHeapSize(size, out position);
}
private KernelResult SetHeapSize(ulong size, out ulong position)
{
if ((size & 0xfffffffe001fffff) != 0)
{
position = 0;
return KernelResult.InvalidSize;
}
return _process.MemoryManager.SetHeapSize(size, out position);
}
public KernelResult SetMemoryAttribute64(
ulong position,
ulong size,
MemoryAttribute attributeMask,
MemoryAttribute attributeValue)
{
return SetMemoryAttribute(position, size, attributeMask, attributeValue);
}
private KernelResult SetMemoryAttribute(
ulong position,
ulong size,
MemoryAttribute attributeMask,
MemoryAttribute attributeValue)
{
if (!PageAligned(position))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
MemoryAttribute attributes = attributeMask | attributeValue;
if (attributes != attributeMask ||
(attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached)
{
return KernelResult.InvalidCombination;
}
KernelResult result = _process.MemoryManager.SetMemoryAttribute(
position,
size,
attributeMask,
attributeValue);
return result;
}
public KernelResult MapMemory64(ulong dst, ulong src, ulong size)
{
return MapMemory(dst, src, size);
}
private KernelResult MapMemory(ulong dst, ulong src, ulong size)
{
if (!PageAligned(src | dst))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (src + size <= src || dst + size <= dst)
{
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{
return KernelResult.InvalidMemState;
}
if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) ||
currentProcess.MemoryManager.InsideHeapRegion (dst, size) ||
currentProcess.MemoryManager.InsideAliasRegion (dst, size))
{
return KernelResult.InvalidMemRange;
}
return _process.MemoryManager.Map(dst, src, size);
}
public KernelResult UnmapMemory64(ulong dst, ulong src, ulong size)
{
return UnmapMemory(dst, src, size);
}
private KernelResult UnmapMemory(ulong dst, ulong src, ulong size)
{
if (!PageAligned(src | dst))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (src + size <= src || dst + size <= dst)
{
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{
return KernelResult.InvalidMemState;
}
if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) ||
currentProcess.MemoryManager.InsideHeapRegion (dst, size) ||
currentProcess.MemoryManager.InsideAliasRegion (dst, size))
{
return KernelResult.InvalidMemRange;
}
return _process.MemoryManager.Unmap(dst, src, size);
}
public KernelResult QueryMemory64(ulong infoPtr, ulong x1, ulong position)
{
return QueryMemory(infoPtr, position);
}
private KernelResult QueryMemory(ulong infoPtr, ulong position)
{
KMemoryInfo blkInfo = _process.MemoryManager.QueryMemory(position);
_process.CpuMemory.WriteUInt64((long)infoPtr + 0x00, blkInfo.Address);
_process.CpuMemory.WriteUInt64((long)infoPtr + 0x08, blkInfo.Size);
_process.CpuMemory.WriteInt32 ((long)infoPtr + 0x10, (int)blkInfo.State & 0xff);
_process.CpuMemory.WriteInt32 ((long)infoPtr + 0x14, (int)blkInfo.Attribute);
_process.CpuMemory.WriteInt32 ((long)infoPtr + 0x18, (int)blkInfo.Permission);
_process.CpuMemory.WriteInt32 ((long)infoPtr + 0x1c, blkInfo.IpcRefCount);
_process.CpuMemory.WriteInt32 ((long)infoPtr + 0x20, blkInfo.DeviceRefCount);
_process.CpuMemory.WriteInt32 ((long)infoPtr + 0x24, 0);
return KernelResult.Success;
}
public KernelResult MapSharedMemory64(int handle, ulong address, ulong size, MemoryPermission permission)
{
return MapSharedMemory(handle, address, size, permission);
}
private KernelResult MapSharedMemory(int handle, ulong address, ulong size, MemoryPermission permission)
{
if (!PageAligned(address))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (address + size <= address)
{
return KernelResult.InvalidMemState;
}
if ((permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
{
return KernelResult.InvalidPermission;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
if (sharedMemory == null)
{
return KernelResult.InvalidHandle;
}
if (currentProcess.MemoryManager.IsInvalidRegion (address, size) ||
currentProcess.MemoryManager.InsideHeapRegion (address, size) ||
currentProcess.MemoryManager.InsideAliasRegion(address, size))
{
return KernelResult.InvalidMemRange;
}
return sharedMemory.MapIntoProcess(
currentProcess.MemoryManager,
address,
size,
currentProcess,
permission);
}
public KernelResult UnmapSharedMemory64(int handle, ulong address, ulong size)
{
return UnmapSharedMemory(handle, address, size);
}
private KernelResult UnmapSharedMemory(int handle, ulong address, ulong size)
{
if (!PageAligned(address))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (address + size <= address)
{
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
if (sharedMemory == null)
{
return KernelResult.InvalidHandle;
}
if (currentProcess.MemoryManager.IsInvalidRegion (address, size) ||
currentProcess.MemoryManager.InsideHeapRegion (address, size) ||
currentProcess.MemoryManager.InsideAliasRegion(address, size))
{
return KernelResult.InvalidMemRange;
}
return sharedMemory.UnmapFromProcess(
currentProcess.MemoryManager,
address,
size,
currentProcess);
}
public KernelResult CreateTransferMemory64(
ulong address,
ulong size,
MemoryPermission permission,
out int handle)
{
return CreateTransferMemory(address, size, permission, out handle);
}
private KernelResult CreateTransferMemory(ulong address, ulong size, MemoryPermission permission, out int handle)
{
handle = 0;
if (!PageAligned(address))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (address + size <= address)
{
return KernelResult.InvalidMemState;
}
if (permission > MemoryPermission.ReadAndWrite || permission == MemoryPermission.Write)
{
return KernelResult.InvalidPermission;
}
KernelResult result = _process.MemoryManager.ReserveTransferMemory(address, size, permission);
if (result != KernelResult.Success)
{
return result;
}
KTransferMemory transferMemory = new KTransferMemory(_system, address, size);
return _process.HandleTable.GenerateHandle(transferMemory, out handle);
}
public KernelResult MapPhysicalMemory64(ulong address, ulong size)
{
return MapPhysicalMemory(address, size);
}
private KernelResult MapPhysicalMemory(ulong address, ulong size)
{
if (!PageAligned(address))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (address + size <= address)
{
return KernelResult.InvalidMemRange;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
return KernelResult.InvalidState;
}
if (!currentProcess.MemoryManager.InsideAddrSpace (address, size) ||
currentProcess.MemoryManager.OutsideAliasRegion(address, size))
{
return KernelResult.InvalidMemRange;
}
return _process.MemoryManager.MapPhysicalMemory(address, size);
}
public KernelResult UnmapPhysicalMemory64(ulong address, ulong size)
{
return UnmapPhysicalMemory(address, size);
}
private KernelResult UnmapPhysicalMemory(ulong address, ulong size)
{
if (!PageAligned(address))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (address + size <= address)
{
return KernelResult.InvalidMemRange;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
return KernelResult.InvalidState;
}
if (!currentProcess.MemoryManager.InsideAddrSpace (address, size) ||
currentProcess.MemoryManager.OutsideAliasRegion(address, size))
{
return KernelResult.InvalidMemRange;
}
return _process.MemoryManager.UnmapPhysicalMemory(address, size);
}
public KernelResult MapProcessCodeMemory64(int handle, ulong dst, ulong src, ulong size)
{
return MapProcessCodeMemory(handle, dst, src, size);
}
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
if (targetProcess == null)
{
return KernelResult.InvalidHandle;
}
if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) ||
targetProcess.MemoryManager.OutsideAddrSpace(src, size) ||
targetProcess.MemoryManager.InsideAliasRegion(dst, size) ||
targetProcess.MemoryManager.InsideHeapRegion(dst, size))
{
return KernelResult.InvalidMemRange;
}
if (size + dst <= dst || size + src <= src)
{
return KernelResult.InvalidMemState;
}
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
}
public KernelResult UnmapProcessCodeMemory64(int handle, ulong dst, ulong src, ulong size)
{
return UnmapProcessCodeMemory(handle, dst, src, size);
}
public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
if (targetProcess == null)
{
return KernelResult.InvalidHandle;
}
if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) ||
targetProcess.MemoryManager.OutsideAddrSpace(src, size) ||
targetProcess.MemoryManager.InsideAliasRegion(dst, size) ||
targetProcess.MemoryManager.InsideHeapRegion(dst, size))
{
return KernelResult.InvalidMemRange;
}
if (size + dst <= dst || size + src <= src)
{
return KernelResult.InvalidMemState;
}
return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size);
}
public KernelResult SetProcessMemoryPermission64(int handle, ulong src, ulong size, MemoryPermission permission)
{
return SetProcessMemoryPermission(handle, src, size, permission);
}
public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, MemoryPermission permission)
{
if (!PageAligned(src))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (permission != MemoryPermission.None &&
permission != MemoryPermission.Read &&
permission != MemoryPermission.ReadAndWrite &&
permission != MemoryPermission.ReadAndExecute)
{
return KernelResult.InvalidPermission;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
if (targetProcess == null)
{
return KernelResult.InvalidHandle;
}
if (targetProcess.MemoryManager.OutsideAddrSpace(src, size))
{
return KernelResult.InvalidMemState;
}
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
}
private static bool PageAligned(ulong position)
{
return (position & (KMemoryManager.PageSize - 1)) == 0;
}
}
}