diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/SvcMemory.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SvcMemory.cs | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs new file mode 100644 index 00000000..b9bca74a --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs @@ -0,0 +1,577 @@ +using ChocolArm64.State; +using Ryujinx.HLE.Logging; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.HOS.Kernel +{ + partial class SvcHandler + { + private void SvcSetHeapSize(AThreadState ThreadState) + { + long Size = (long)ThreadState.X1; + + if ((Size & 0x1fffff) != 0 || Size != (uint)Size) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + long Result = Process.MemoryManager.TrySetHeapSize(Size, out long Position); + + ThreadState.X0 = (ulong)Result; + + if (Result == 0) + { + ThreadState.X1 = (ulong)Position; + } + else + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + } + + private void SvcSetMemoryAttribute(AThreadState ThreadState) + { + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; + + if (!PageAligned(Position)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + MemoryAttribute AttributeMask = (MemoryAttribute)ThreadState.X2; + MemoryAttribute AttributeValue = (MemoryAttribute)ThreadState.X3; + + MemoryAttribute Attributes = AttributeMask | AttributeValue; + + if (Attributes != AttributeMask || + (Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); + + return; + } + + long Result = Process.MemoryManager.SetMemoryAttribute( + Position, + Size, + AttributeMask, + AttributeValue); + + if (Result != 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + else + { + Memory.StopObservingRegion(Position, Size); + } + + ThreadState.X0 = (ulong)Result; + } + + private void SvcMapMemory(AThreadState ThreadState) + { + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!PageAligned(Src | Dst)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Src, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideNewMapRegion(Dst, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + long Result = Process.MemoryManager.Map(Src, Dst, Size); + + if (Result != 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; + } + + private void SvcUnmapMemory(AThreadState ThreadState) + { + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!PageAligned(Src | Dst)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Src, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideNewMapRegion(Dst, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + long Result = Process.MemoryManager.Unmap(Src, Dst, Size); + + if (Result != 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; + } + + private void SvcQueryMemory(AThreadState ThreadState) + { + long InfoPtr = (long)ThreadState.X0; + long Position = (long)ThreadState.X2; + + KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position); + + Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position); + Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size); + Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff); + Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute); + Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission); + Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount); + Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount); + Memory.WriteInt32(InfoPtr + 0x24, 0); + + ThreadState.X0 = 0; + ThreadState.X1 = 0; + } + + private void SvcMapSharedMemory(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!PageAligned(Position)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + MemoryPermission Permission = (MemoryPermission)ThreadState.X3; + + if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); + + return; + } + + KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle); + + if (SharedMemory == null) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (SharedMemory.Size != Size) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position); + + if (Result != 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; + } + + private void SvcUnmapSharedMemory(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!PageAligned(Position)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle); + + if (SharedMemory == null) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size); + + if (Result != 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; + } + + private void SvcCreateTransferMemory(AThreadState ThreadState) + { + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!PageAligned(Position)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + MemoryPermission Permission = (MemoryPermission)ThreadState.X3; + + if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); + + return; + } + + Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission); + + KTransferMemory TransferMemory = new KTransferMemory(Position, Size); + + int Handle = Process.HandleTable.OpenHandle(TransferMemory); + + ThreadState.X0 = 0; + ThreadState.X1 = (ulong)Handle; + } + + private void SvcMapPhysicalMemory(AThreadState ThreadState) + { + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; + + if (!PageAligned(Position)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Position, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size); + + if (Result != 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; + } + + private void SvcUnmapPhysicalMemory(AThreadState ThreadState) + { + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; + + if (!PageAligned(Position)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Position, Size)) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size); + + if (Result != 0) + { + Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; + } + + private static bool PageAligned(long Position) + { + return (Position & (KMemoryManager.PageSize - 1)) == 0; + } + + private bool InsideAddrSpace(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.AddrSpaceStart && + End < (ulong)Process.MemoryManager.AddrSpaceEnd; + } + + private bool InsideMapRegion(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.MapRegionStart && + End < (ulong)Process.MemoryManager.MapRegionEnd; + } + + private bool InsideHeapRegion(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.HeapRegionStart && + End < (ulong)Process.MemoryManager.HeapRegionEnd; + } + + private bool InsideNewMapRegion(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)Process.MemoryManager.NewMapRegionStart && + End < (ulong)Process.MemoryManager.NewMapRegionEnd; + } + } +}
\ No newline at end of file |