mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2024-11-15 09:35:27 +00:00
Refactor SVC handler (#540)
* Refactor SVC handler * Get rid of KernelErr * Split kernel code files into multiple folders
This commit is contained in:
parent
2534a7f10c
commit
0039bb6394
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@ using LibHac;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.HLE.HOS.Font;
|
using Ryujinx.HLE.HOS.Font;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -7,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
{
|
{
|
||||||
static class IpcHandler
|
static class IpcHandler
|
||||||
{
|
{
|
||||||
public static long IpcCall(
|
public static KernelResult IpcCall(
|
||||||
Switch device,
|
Switch device,
|
||||||
KProcess process,
|
KProcess process,
|
||||||
MemoryManager memory,
|
MemoryManager memory,
|
||||||
|
@ -100,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
memory.WriteBytes(cmdPtr, response.GetBytes(cmdPtr));
|
memory.WriteBytes(cmdPtr, response.GetBytes(cmdPtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
|
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
interface IKFutureSchedulerObject
|
interface IKFutureSchedulerObject
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KAutoObject
|
class KAutoObject
|
||||||
{
|
{
|
|
@ -1,7 +1,8 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KResourceLimit
|
class KResourceLimit
|
||||||
{
|
{
|
|
@ -1,6 +1,7 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KSynchronizationObject : KAutoObject
|
class KSynchronizationObject : KAutoObject
|
||||||
{
|
{
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KTimeManager : IDisposable
|
class KTimeManager : IDisposable
|
||||||
{
|
{
|
|
@ -1,6 +1,7 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
static class KernelInit
|
static class KernelInit
|
||||||
{
|
{
|
|
@ -1,9 +1,10 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
enum KernelResult
|
enum KernelResult
|
||||||
{
|
{
|
||||||
Success = 0,
|
Success = 0,
|
||||||
InvalidCapability = 0x1c01,
|
InvalidCapability = 0x1c01,
|
||||||
|
ThreadNotStarted = 0x7201,
|
||||||
ThreadTerminating = 0x7601,
|
ThreadTerminating = 0x7601,
|
||||||
InvalidSize = 0xca01,
|
InvalidSize = 0xca01,
|
||||||
InvalidAddress = 0xcc01,
|
InvalidAddress = 0xcc01,
|
72
Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
Normal file
72
Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
|
{
|
||||||
|
static class KernelTransfer
|
||||||
|
{
|
||||||
|
public static bool UserToKernelInt32(Horizon system, ulong address, out int value)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||||
|
currentProcess.CpuMemory.IsMapped((long)address + 3))
|
||||||
|
{
|
||||||
|
value = currentProcess.CpuMemory.ReadInt32((long)address);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool UserToKernelString(Horizon system, ulong address, int size, out string value)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||||
|
currentProcess.CpuMemory.IsMapped((long)address + size - 1))
|
||||||
|
{
|
||||||
|
value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, (long)address, size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = null;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool KernelToUserInt32(Horizon system, ulong address, int value)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||||
|
currentProcess.CpuMemory.IsMapped((long)address + 3))
|
||||||
|
{
|
||||||
|
currentProcess.CpuMemory.WriteInt32ToSharedAddr((long)address, value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool KernelToUserInt64(Horizon system, ulong address, long value)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||||
|
currentProcess.CpuMemory.IsMapped((long)address + 7))
|
||||||
|
{
|
||||||
|
currentProcess.CpuMemory.WriteInt64((long)address, value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
enum LimitableResource : byte
|
enum LimitableResource : byte
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class MersenneTwister
|
class MersenneTwister
|
||||||
{
|
{
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KClientPort : KSynchronizationObject
|
class KClientPort : KSynchronizationObject
|
||||||
{
|
{
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KPort : KAutoObject
|
class KPort : KAutoObject
|
||||||
{
|
{
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KServerPort : KSynchronizationObject
|
class KServerPort : KSynchronizationObject
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using Ryujinx.HLE.HOS.Services;
|
using Ryujinx.HLE.HOS.Services;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KSession : IDisposable
|
class KSession : IDisposable
|
||||||
{
|
{
|
|
@ -1,24 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
|
||||||
static class KernelErr
|
|
||||||
{
|
|
||||||
public const int ThreadTerminating = 59;
|
|
||||||
public const int InvalidSize = 101;
|
|
||||||
public const int InvalidAddress = 102;
|
|
||||||
public const int OutOfMemory = 104;
|
|
||||||
public const int HandleTableFull = 105;
|
|
||||||
public const int NoAccessPerm = 106;
|
|
||||||
public const int InvalidPermission = 108;
|
|
||||||
public const int InvalidMemRange = 110;
|
|
||||||
public const int InvalidPriority = 112;
|
|
||||||
public const int InvalidCoreId = 113;
|
|
||||||
public const int InvalidHandle = 114;
|
|
||||||
public const int InvalidMaskValue = 116;
|
|
||||||
public const int Timeout = 117;
|
|
||||||
public const int Cancelled = 118;
|
|
||||||
public const int CountOutOfRange = 119;
|
|
||||||
public const int InvalidEnumValue = 120;
|
|
||||||
public const int InvalidThread = 122;
|
|
||||||
public const int InvalidState = 125;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
|
||||||
static class KernelTransfer
|
|
||||||
{
|
|
||||||
public static bool UserToKernelInt32(Horizon system, long address, out int value)
|
|
||||||
{
|
|
||||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (currentProcess.CpuMemory.IsMapped(address) &&
|
|
||||||
currentProcess.CpuMemory.IsMapped(address + 3))
|
|
||||||
{
|
|
||||||
value = currentProcess.CpuMemory.ReadInt32(address);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool UserToKernelString(Horizon system, long address, int size, out string value)
|
|
||||||
{
|
|
||||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (currentProcess.CpuMemory.IsMapped(address) &&
|
|
||||||
currentProcess.CpuMemory.IsMapped(address + size - 1))
|
|
||||||
{
|
|
||||||
value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, address, size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = null;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool KernelToUserInt32(Horizon system, long address, int value)
|
|
||||||
{
|
|
||||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (currentProcess.CpuMemory.IsMapped(address) &&
|
|
||||||
currentProcess.CpuMemory.IsMapped(address + 3))
|
|
||||||
{
|
|
||||||
currentProcess.CpuMemory.WriteInt32ToSharedAddr(address, value);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool KernelToUserInt64(Horizon system, long address, long value)
|
|
||||||
{
|
|
||||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (currentProcess.CpuMemory.IsMapped(address) &&
|
|
||||||
currentProcess.CpuMemory.IsMapped(address + 7))
|
|
||||||
{
|
|
||||||
currentProcess.CpuMemory.WriteInt64(address, value);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
enum AddressSpaceType
|
enum AddressSpaceType
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
static class DramMemoryMap
|
static class DramMemoryMap
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryArrange
|
class KMemoryArrange
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
struct KMemoryArrangeRegion
|
struct KMemoryArrangeRegion
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryBlock
|
class KMemoryBlock
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryBlockAllocator
|
class KMemoryBlockAllocator
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryInfo
|
class KMemoryInfo
|
||||||
{
|
{
|
|
@ -1,9 +1,11 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryManager
|
class KMemoryManager
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryRegionBlock
|
class KMemoryRegionBlock
|
||||||
{
|
{
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryRegionManager
|
class KMemoryRegionManager
|
||||||
{
|
{
|
|
@ -1,7 +1,8 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KPageList : IEnumerable<KPageNode>
|
class KPageList : IEnumerable<KPageNode>
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
struct KPageNode
|
struct KPageNode
|
||||||
{
|
{
|
|
@ -1,6 +1,8 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KSharedMemory
|
class KSharedMemory
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KSlabHeap
|
class KSlabHeap
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KTransferMemory
|
class KTransferMemory
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
enum MemoryAttribute : byte
|
enum MemoryAttribute : byte
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
enum MemoryOperation
|
enum MemoryOperation
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
enum MemoryPermission : byte
|
enum MemoryPermission : byte
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
enum MemoryRegion
|
enum MemoryRegion
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
enum MemoryState : uint
|
enum MemoryState : uint
|
|
@ -2,13 +2,14 @@ using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
|
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.Loaders.Elf;
|
using Ryujinx.HLE.Loaders.Elf;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class HleProcessDebugger
|
class HleProcessDebugger
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KContextIdManager
|
class KContextIdManager
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KHandleEntry
|
class KHandleEntry
|
||||||
{
|
{
|
|
@ -1,6 +1,8 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KHandleTable
|
class KHandleTable
|
||||||
{
|
{
|
|
@ -3,12 +3,16 @@ using ChocolArm64.Events;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
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.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KProcess : KSynchronizationObject
|
class KProcess : KSynchronizationObject
|
||||||
{
|
{
|
|
@ -1,6 +1,9 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KProcessCapabilities
|
class KProcessCapabilities
|
||||||
{
|
{
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KTlsPageInfo
|
class KTlsPageInfo
|
||||||
{
|
{
|
|
@ -1,6 +1,7 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KTlsPageManager
|
class KTlsPageManager
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
struct ProcessCreationInfo
|
struct ProcessCreationInfo
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
enum ProcessState : byte
|
enum ProcessState : byte
|
||||||
{
|
{
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
public class InvalidSvcException : Exception
|
||||||
|
{
|
||||||
|
public InvalidSvcException(string message) : base(message) { }
|
||||||
|
}
|
||||||
|
}
|
61
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs
Normal file
61
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using ChocolArm64.Events;
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using ChocolArm64.State;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
partial class SvcHandler
|
||||||
|
{
|
||||||
|
private Switch _device;
|
||||||
|
private KProcess _process;
|
||||||
|
private Horizon _system;
|
||||||
|
private MemoryManager _memory;
|
||||||
|
|
||||||
|
private struct HleIpcMessage
|
||||||
|
{
|
||||||
|
public KThread Thread { get; private set; }
|
||||||
|
public KSession Session { get; private set; }
|
||||||
|
public IpcMessage Message { get; private set; }
|
||||||
|
public long MessagePtr { get; private set; }
|
||||||
|
|
||||||
|
public HleIpcMessage(
|
||||||
|
KThread thread,
|
||||||
|
KSession session,
|
||||||
|
IpcMessage message,
|
||||||
|
long messagePtr)
|
||||||
|
{
|
||||||
|
Thread = thread;
|
||||||
|
Session = session;
|
||||||
|
Message = message;
|
||||||
|
MessagePtr = messagePtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SvcHandler(Switch device, KProcess process)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_process = process;
|
||||||
|
_system = device.System;
|
||||||
|
_memory = process.CpuMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SvcCall(object sender, InstExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
Action<SvcHandler, CpuThreadState> svcFunc = SvcTable.GetSvcFunc(e.Id);
|
||||||
|
|
||||||
|
if (svcFunc == null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CpuThreadState threadState = (CpuThreadState)sender;
|
||||||
|
|
||||||
|
svcFunc(this, threadState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
394
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
Normal file
394
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
_memory.StopObservingRegion((long)position, (long)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
_memory.WriteUInt64((long)infoPtr + 0x00, blkInfo.Address);
|
||||||
|
_memory.WriteUInt64((long)infoPtr + 0x08, blkInfo.Size);
|
||||||
|
_memory.WriteInt32 ((long)infoPtr + 0x10, (int)blkInfo.State & 0xff);
|
||||||
|
_memory.WriteInt32 ((long)infoPtr + 0x14, (int)blkInfo.Attribute);
|
||||||
|
_memory.WriteInt32 ((long)infoPtr + 0x18, (int)blkInfo.Permission);
|
||||||
|
_memory.WriteInt32 ((long)infoPtr + 0x1c, blkInfo.IpcRefCount);
|
||||||
|
_memory.WriteInt32 ((long)infoPtr + 0x20, blkInfo.DeviceRefCount);
|
||||||
|
_memory.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(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 MapPhysicalMemory(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool PageAligned(ulong position)
|
||||||
|
{
|
||||||
|
return (position & (KMemoryManager.PageSize - 1)) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,33 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services;
|
using Ryujinx.HLE.HOS.Services;
|
||||||
using System;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private void SvcExitProcess(CpuThreadState threadState)
|
public void ExitProcess64()
|
||||||
|
{
|
||||||
|
ExitProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExitProcess()
|
||||||
{
|
{
|
||||||
_system.Scheduler.GetCurrentProcess().Terminate();
|
_system.Scheduler.GetCurrentProcess().Terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignalEvent64(CpuThreadState threadState)
|
public KernelResult SignalEvent64(int handle)
|
||||||
{
|
{
|
||||||
threadState.X0 = (ulong)SignalEvent((int)threadState.X0);
|
return SignalEvent(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult SignalEvent(int handle)
|
private KernelResult SignalEvent(int handle)
|
||||||
|
@ -41,17 +47,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
result = KernelResult.InvalidHandle;
|
result = KernelResult.InvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearEvent64(CpuThreadState threadState)
|
public KernelResult ClearEvent64(int handle)
|
||||||
{
|
{
|
||||||
threadState.X0 = (ulong)ClearEvent((int)threadState.X0);
|
return ClearEvent(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult ClearEvent(int handle)
|
private KernelResult ClearEvent(int handle)
|
||||||
|
@ -71,29 +72,23 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
result = writableEvent.Clear();
|
result = writableEvent.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcCloseHandle(CpuThreadState threadState)
|
public KernelResult CloseHandle64(int handle)
|
||||||
{
|
{
|
||||||
int handle = (int)threadState.X0;
|
return CloseHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult CloseHandle(int handle)
|
||||||
|
{
|
||||||
object obj = _process.HandleTable.GetObject<object>(handle);
|
object obj = _process.HandleTable.GetObject<object>(handle);
|
||||||
|
|
||||||
_process.HandleTable.CloseHandle(handle);
|
_process.HandleTable.CloseHandle(handle);
|
||||||
|
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{handle:x8}!");
|
return KernelResult.InvalidHandle;
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj is KSession session)
|
if (obj is KSession session)
|
||||||
|
@ -107,12 +102,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
transferMemory.Size);
|
transferMemory.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
threadState.X0 = 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetSignal64(CpuThreadState threadState)
|
public KernelResult ResetSignal64(int handle)
|
||||||
{
|
{
|
||||||
threadState.X0 = (ulong)ResetSignal((int)threadState.X0);
|
return ResetSignal(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult ResetSignal(int handle)
|
private KernelResult ResetSignal(int handle)
|
||||||
|
@ -141,60 +136,43 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == KernelResult.InvalidState)
|
|
||||||
{
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
|
|
||||||
}
|
|
||||||
else if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetSystemTick(CpuThreadState threadState)
|
public ulong GetSystemTick64()
|
||||||
{
|
{
|
||||||
threadState.X0 = threadState.CntpctEl0;
|
return _system.Scheduler.GetCurrentThread().Context.ThreadState.CntpctEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcConnectToNamedPort(CpuThreadState threadState)
|
public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
|
||||||
{
|
{
|
||||||
long stackPtr = (long)threadState.X0;
|
return ConnectToNamedPort(namePtr, out handle);
|
||||||
long namePtr = (long)threadState.X1;
|
}
|
||||||
|
|
||||||
string name = MemoryHelper.ReadAsciiString(_memory, namePtr, 8);
|
private KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
|
||||||
|
{
|
||||||
|
string name = MemoryHelper.ReadAsciiString(_memory, (long)namePtr, 8);
|
||||||
|
|
||||||
//TODO: Validate that app has perms to access the service, and that the service
|
//TODO: Validate that app has perms to access the service, and that the service
|
||||||
//actually exists, return error codes otherwise.
|
//actually exists, return error codes otherwise.
|
||||||
KSession session = new KSession(ServiceFactory.MakeService(_system, name), name);
|
KSession session = new KSession(ServiceFactory.MakeService(_system, name), name);
|
||||||
|
|
||||||
if (_process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
|
return _process.HandleTable.GenerateHandle(session, out handle);
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Out of handles!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
threadState.X0 = 0;
|
public KernelResult SendSyncRequest64(int handle)
|
||||||
threadState.X1 = (uint)handle;
|
{
|
||||||
|
return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSendSyncRequest(CpuThreadState threadState)
|
public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle)
|
||||||
{
|
{
|
||||||
SendSyncRequest(threadState, threadState.Tpidr, 0x100, (int)threadState.X0);
|
return SendSyncRequest(messagePtr, size, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSendSyncRequestWithUserBuffer(CpuThreadState threadState)
|
private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
|
||||||
{
|
{
|
||||||
SendSyncRequest(
|
byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size);
|
||||||
threadState,
|
|
||||||
(long)threadState.X0,
|
|
||||||
(long)threadState.X1,
|
|
||||||
(int)threadState.X2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendSyncRequest(CpuThreadState threadState, long messagePtr, long size, int handle)
|
|
||||||
{
|
|
||||||
byte[] messageData = _memory.ReadBytes(messagePtr, size);
|
|
||||||
|
|
||||||
KSession session = _process.HandleTable.GetObject<KSession>(handle);
|
KSession session = _process.HandleTable.GetObject<KSession>(handle);
|
||||||
|
|
||||||
|
@ -205,29 +183,29 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
currentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = 0;
|
currentThread.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
IpcMessage message = new IpcMessage(messageData, messagePtr);
|
IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
|
ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
|
||||||
currentThread,
|
currentThread,
|
||||||
session,
|
session,
|
||||||
message,
|
message,
|
||||||
messagePtr));
|
(long)messagePtr));
|
||||||
|
|
||||||
_system.ThreadCounter.AddCount();
|
_system.ThreadCounter.AddCount();
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
threadState.X0 = (ulong)currentThread.ObjSyncResult;
|
return currentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
return KernelResult.InvalidHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +213,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
HleIpcMessage ipcMessage = (HleIpcMessage)state;
|
HleIpcMessage ipcMessage = (HleIpcMessage)state;
|
||||||
|
|
||||||
ipcMessage.Thread.ObjSyncResult = (int)IpcHandler.IpcCall(
|
ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
|
||||||
_device,
|
_device,
|
||||||
_process,
|
_process,
|
||||||
_memory,
|
_memory,
|
||||||
|
@ -248,14 +226,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetProcessId64(CpuThreadState threadState)
|
public KernelResult GetProcessId64(int handle, out long pid)
|
||||||
{
|
{
|
||||||
int handle = (int)threadState.X1;
|
return GetProcessId(handle, out pid);
|
||||||
|
|
||||||
KernelResult result = GetProcessId(handle, out long pid);
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
threadState.X1 = (ulong)pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult GetProcessId(int handle, out long pid)
|
private KernelResult GetProcessId(int handle, out long pid)
|
||||||
|
@ -283,15 +256,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
: KernelResult.InvalidHandle;
|
: KernelResult.InvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcBreak(CpuThreadState threadState)
|
public void Break64(ulong reason, ulong x1, ulong info)
|
||||||
{
|
{
|
||||||
long reason = (long)threadState.X0;
|
Break(reason);
|
||||||
long unknown = (long)threadState.X1;
|
}
|
||||||
long info = (long)threadState.X2;
|
|
||||||
|
|
||||||
|
private void Break(ulong reason)
|
||||||
|
{
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
if ((reason & (1 << 31)) == 0)
|
if ((reason & (1UL << 31)) == 0)
|
||||||
{
|
{
|
||||||
currentThread.PrintGuestStackTrace();
|
currentThread.PrintGuestStackTrace();
|
||||||
|
|
||||||
|
@ -305,29 +279,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcOutputDebugString(CpuThreadState threadState)
|
public void OutputDebugString64(ulong strPtr, ulong size)
|
||||||
{
|
{
|
||||||
long position = (long)threadState.X0;
|
OutputDebugString(strPtr, size);
|
||||||
long size = (long)threadState.X1;
|
|
||||||
|
|
||||||
string str = MemoryHelper.ReadAsciiString(_memory, position, size);
|
|
||||||
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, str);
|
|
||||||
|
|
||||||
threadState.X0 = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetInfo64(CpuThreadState threadState)
|
private void OutputDebugString(ulong strPtr, ulong size)
|
||||||
{
|
{
|
||||||
long stackPtr = (long)threadState.X0;
|
string str = MemoryHelper.ReadAsciiString(_memory, (long)strPtr, (long)size);
|
||||||
uint id = (uint)threadState.X1;
|
|
||||||
int handle = (int)threadState.X2;
|
|
||||||
long subId = (long)threadState.X3;
|
|
||||||
|
|
||||||
KernelResult result = GetInfo(id, handle, subId, out long value);
|
Logger.PrintWarning(LogClass.KernelSvc, str);
|
||||||
|
}
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
public KernelResult GetInfo64(uint id, int handle, long subId, out long value)
|
||||||
threadState.X1 = (ulong)value;
|
{
|
||||||
|
return GetInfo(id, handle, subId, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult GetInfo(uint id, int handle, long subId, out long value)
|
private KernelResult GetInfo(uint id, int handle, long subId, out long value)
|
||||||
|
@ -556,13 +522,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateEvent64(CpuThreadState state)
|
public KernelResult CreateEvent64(out int wEventHandle, out int rEventHandle)
|
||||||
{
|
{
|
||||||
KernelResult result = CreateEvent(out int wEventHandle, out int rEventHandle);
|
return CreateEvent(out wEventHandle, out rEventHandle);
|
||||||
|
|
||||||
state.X0 = (ulong)result;
|
|
||||||
state.X1 = (ulong)wEventHandle;
|
|
||||||
state.X2 = (ulong)rEventHandle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
|
private KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
|
||||||
|
@ -588,15 +550,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetProcessList64(CpuThreadState state)
|
public KernelResult GetProcessList64(ulong address, int maxCount, out int count)
|
||||||
{
|
{
|
||||||
ulong address = state.X1;
|
return GetProcessList(address, maxCount, out count);
|
||||||
int maxOut = (int)state.X2;
|
|
||||||
|
|
||||||
KernelResult result = GetProcessList(address, maxOut, out int count);
|
|
||||||
|
|
||||||
state.X0 = (ulong)result;
|
|
||||||
state.X1 = (ulong)count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult GetProcessList(ulong address, int maxCount, out int count)
|
private KernelResult GetProcessList(ulong address, int maxCount, out int count)
|
||||||
|
@ -633,7 +589,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
if (copyCount < maxCount)
|
if (copyCount < maxCount)
|
||||||
{
|
{
|
||||||
if (!KernelTransfer.KernelToUserInt64(_system, (long)address + copyCount * 8, process.Pid))
|
if (!KernelTransfer.KernelToUserInt64(_system, address + (ulong)copyCount * 8, process.Pid))
|
||||||
{
|
{
|
||||||
return KernelResult.UserCopyFailed;
|
return KernelResult.UserCopyFailed;
|
||||||
}
|
}
|
||||||
|
@ -648,16 +604,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetSystemInfo64(CpuThreadState state)
|
public KernelResult GetSystemInfo64(uint id, int handle, long subId, out long value)
|
||||||
{
|
{
|
||||||
uint id = (uint)state.X1;
|
return GetSystemInfo(id, handle, subId, out value);
|
||||||
int handle = (int)state.X2;
|
|
||||||
long subId = (long)state.X3;
|
|
||||||
|
|
||||||
KernelResult result = GetSystemInfo(id, handle, subId, out long value);
|
|
||||||
|
|
||||||
state.X0 = (ulong)result;
|
|
||||||
state.X1 = (ulong)value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult GetSystemInfo(uint id, int handle, long subId, out long value)
|
private KernelResult GetSystemInfo(uint id, int handle, long subId, out long value)
|
||||||
|
@ -716,28 +665,20 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreatePort64(CpuThreadState state)
|
public KernelResult CreatePort64(
|
||||||
{
|
int maxSessions,
|
||||||
int maxSessions = (int)state.X2;
|
bool isLight,
|
||||||
bool isLight = (state.X3 & 1) != 0;
|
ulong namePtr,
|
||||||
long nameAddress = (long)state.X4;
|
|
||||||
|
|
||||||
KernelResult result = CreatePort(
|
|
||||||
maxSessions,
|
|
||||||
isLight,
|
|
||||||
nameAddress,
|
|
||||||
out int serverPortHandle,
|
out int serverPortHandle,
|
||||||
out int clientPortHandle);
|
out int clientPortHandle)
|
||||||
|
{
|
||||||
state.X0 = (ulong)result;
|
return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
|
||||||
state.X1 = (ulong)serverPortHandle;
|
|
||||||
state.X2 = (ulong)clientPortHandle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult CreatePort(
|
private KernelResult CreatePort(
|
||||||
int maxSessions,
|
int maxSessions,
|
||||||
bool isLight,
|
bool isLight,
|
||||||
long nameAddress,
|
ulong namePtr,
|
||||||
out int serverPortHandle,
|
out int serverPortHandle,
|
||||||
out int clientPortHandle)
|
out int clientPortHandle)
|
||||||
{
|
{
|
||||||
|
@ -750,7 +691,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
KPort port = new KPort(_system);
|
KPort port = new KPort(_system);
|
||||||
|
|
||||||
port.Initialize(maxSessions, isLight, nameAddress);
|
port.Initialize(maxSessions, isLight, (long)namePtr);
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
@ -771,22 +712,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ManageNamedPort64(CpuThreadState state)
|
public KernelResult ManageNamedPort64(ulong namePtr, int maxSessions, out int handle)
|
||||||
{
|
{
|
||||||
long nameAddress = (long)state.X1;
|
return ManageNamedPort(namePtr, maxSessions, out handle);
|
||||||
int maxSessions = (int)state.X2;
|
|
||||||
|
|
||||||
KernelResult result = ManageNamedPort(nameAddress, maxSessions, out int handle);
|
|
||||||
|
|
||||||
state.X0 = (ulong)result;
|
|
||||||
state.X1 = (ulong)handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult ManageNamedPort(long nameAddress, int maxSessions, out int handle)
|
private KernelResult ManageNamedPort(ulong namePtr, int maxSessions, out int handle)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelString(_system, nameAddress, 12, out string name))
|
if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
|
||||||
{
|
{
|
||||||
return KernelResult.UserCopyFailed;
|
return KernelResult.UserCopyFailed;
|
||||||
}
|
}
|
340
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs
Normal file
340
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
using ChocolArm64.State;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
static class SvcTable
|
||||||
|
{
|
||||||
|
private const int SvcFuncMaxArguments = 8;
|
||||||
|
|
||||||
|
private static Dictionary<int, string> _svcFuncs64;
|
||||||
|
|
||||||
|
private static Action<SvcHandler, CpuThreadState>[] _svcTable64;
|
||||||
|
|
||||||
|
static SvcTable()
|
||||||
|
{
|
||||||
|
_svcFuncs64 = new Dictionary<int, string>
|
||||||
|
{
|
||||||
|
{ 0x01, nameof(SvcHandler.SetHeapSize64) },
|
||||||
|
{ 0x03, nameof(SvcHandler.SetMemoryAttribute64) },
|
||||||
|
{ 0x04, nameof(SvcHandler.MapMemory64) },
|
||||||
|
{ 0x05, nameof(SvcHandler.UnmapMemory64) },
|
||||||
|
{ 0x06, nameof(SvcHandler.QueryMemory64) },
|
||||||
|
{ 0x07, nameof(SvcHandler.ExitProcess64) },
|
||||||
|
{ 0x08, nameof(SvcHandler.CreateThread64) },
|
||||||
|
{ 0x09, nameof(SvcHandler.StartThread64) },
|
||||||
|
{ 0x0a, nameof(SvcHandler.ExitThread64) },
|
||||||
|
{ 0x0b, nameof(SvcHandler.SleepThread64) },
|
||||||
|
{ 0x0c, nameof(SvcHandler.GetThreadPriority64) },
|
||||||
|
{ 0x0d, nameof(SvcHandler.SetThreadPriority64) },
|
||||||
|
{ 0x0e, nameof(SvcHandler.GetThreadCoreMask64) },
|
||||||
|
{ 0x0f, nameof(SvcHandler.SetThreadCoreMask64) },
|
||||||
|
{ 0x10, nameof(SvcHandler.GetCurrentProcessorNumber64) },
|
||||||
|
{ 0x11, nameof(SvcHandler.SignalEvent64) },
|
||||||
|
{ 0x12, nameof(SvcHandler.ClearEvent64) },
|
||||||
|
{ 0x13, nameof(SvcHandler.MapSharedMemory64) },
|
||||||
|
{ 0x14, nameof(SvcHandler.UnmapSharedMemory64) },
|
||||||
|
{ 0x15, nameof(SvcHandler.CreateTransferMemory64) },
|
||||||
|
{ 0x16, nameof(SvcHandler.CloseHandle64) },
|
||||||
|
{ 0x17, nameof(SvcHandler.ResetSignal64) },
|
||||||
|
{ 0x18, nameof(SvcHandler.WaitSynchronization64) },
|
||||||
|
{ 0x19, nameof(SvcHandler.CancelSynchronization64) },
|
||||||
|
{ 0x1a, nameof(SvcHandler.ArbitrateLock64) },
|
||||||
|
{ 0x1b, nameof(SvcHandler.ArbitrateUnlock64) },
|
||||||
|
{ 0x1c, nameof(SvcHandler.WaitProcessWideKeyAtomic64) },
|
||||||
|
{ 0x1d, nameof(SvcHandler.SignalProcessWideKey64) },
|
||||||
|
{ 0x1e, nameof(SvcHandler.GetSystemTick64) },
|
||||||
|
{ 0x1f, nameof(SvcHandler.ConnectToNamedPort64) },
|
||||||
|
{ 0x21, nameof(SvcHandler.SendSyncRequest64) },
|
||||||
|
{ 0x22, nameof(SvcHandler.SendSyncRequestWithUserBuffer64) },
|
||||||
|
{ 0x24, nameof(SvcHandler.GetProcessId64) },
|
||||||
|
{ 0x25, nameof(SvcHandler.GetThreadId64) },
|
||||||
|
{ 0x26, nameof(SvcHandler.Break64) },
|
||||||
|
{ 0x27, nameof(SvcHandler.OutputDebugString64) },
|
||||||
|
{ 0x29, nameof(SvcHandler.GetInfo64) },
|
||||||
|
{ 0x2c, nameof(SvcHandler.MapPhysicalMemory64) },
|
||||||
|
{ 0x2d, nameof(SvcHandler.UnmapPhysicalMemory64) },
|
||||||
|
{ 0x32, nameof(SvcHandler.SetThreadActivity64) },
|
||||||
|
{ 0x33, nameof(SvcHandler.GetThreadContext364) },
|
||||||
|
{ 0x34, nameof(SvcHandler.WaitForAddress64) },
|
||||||
|
{ 0x35, nameof(SvcHandler.SignalToAddress64) },
|
||||||
|
{ 0x45, nameof(SvcHandler.CreateEvent64) },
|
||||||
|
{ 0x65, nameof(SvcHandler.GetProcessList64) },
|
||||||
|
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
|
||||||
|
{ 0x70, nameof(SvcHandler.CreatePort64) },
|
||||||
|
{ 0x71, nameof(SvcHandler.ManageNamedPort64) }
|
||||||
|
};
|
||||||
|
|
||||||
|
_svcTable64 = new Action<SvcHandler, CpuThreadState>[0x80];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Action<SvcHandler, CpuThreadState> GetSvcFunc(int svcId)
|
||||||
|
{
|
||||||
|
if (_svcTable64[svcId] != null)
|
||||||
|
{
|
||||||
|
return _svcTable64[svcId];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_svcFuncs64.TryGetValue(svcId, out string svcName))
|
||||||
|
{
|
||||||
|
return _svcTable64[svcId] = GenerateMethod(svcName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<SvcHandler, CpuThreadState> GenerateMethod(string svcName)
|
||||||
|
{
|
||||||
|
Type[] argTypes = new Type[] { typeof(SvcHandler), typeof(CpuThreadState) };
|
||||||
|
|
||||||
|
DynamicMethod method = new DynamicMethod(svcName, null, argTypes);
|
||||||
|
|
||||||
|
MethodInfo methodInfo = typeof(SvcHandler).GetMethod(svcName);
|
||||||
|
|
||||||
|
ParameterInfo[] methodArgs = methodInfo.GetParameters();
|
||||||
|
|
||||||
|
if (methodArgs.Length > SvcFuncMaxArguments)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is 8.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ILGenerator generator = method.GetILGenerator();
|
||||||
|
|
||||||
|
void ConvertToArgType(Type sourceType)
|
||||||
|
{
|
||||||
|
CheckIfTypeIsSupported(sourceType, svcName);
|
||||||
|
|
||||||
|
switch (Type.GetTypeCode(sourceType))
|
||||||
|
{
|
||||||
|
case TypeCode.UInt32: generator.Emit(OpCodes.Conv_U4); break;
|
||||||
|
case TypeCode.Int32: generator.Emit(OpCodes.Conv_I4); break;
|
||||||
|
case TypeCode.UInt16: generator.Emit(OpCodes.Conv_U2); break;
|
||||||
|
case TypeCode.Int16: generator.Emit(OpCodes.Conv_I2); break;
|
||||||
|
case TypeCode.Byte: generator.Emit(OpCodes.Conv_U1); break;
|
||||||
|
case TypeCode.SByte: generator.Emit(OpCodes.Conv_I1); break;
|
||||||
|
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
generator.Emit(OpCodes.Conv_I4);
|
||||||
|
generator.Emit(OpCodes.Ldc_I4_1);
|
||||||
|
generator.Emit(OpCodes.And);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertToFieldType(Type sourceType)
|
||||||
|
{
|
||||||
|
CheckIfTypeIsSupported(sourceType, svcName);
|
||||||
|
|
||||||
|
switch (Type.GetTypeCode(sourceType))
|
||||||
|
{
|
||||||
|
case TypeCode.UInt32:
|
||||||
|
case TypeCode.Int32:
|
||||||
|
case TypeCode.UInt16:
|
||||||
|
case TypeCode.Int16:
|
||||||
|
case TypeCode.Byte:
|
||||||
|
case TypeCode.SByte:
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
generator.Emit(OpCodes.Conv_U8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//For functions returning output values, the first registers
|
||||||
|
//are used to hold pointers where the value will be stored,
|
||||||
|
//so they can't be used to pass argument and we must
|
||||||
|
//skip them.
|
||||||
|
int byRefArgsCount = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < methodArgs.Length; index++)
|
||||||
|
{
|
||||||
|
if (methodArgs[index].ParameterType.IsByRef)
|
||||||
|
{
|
||||||
|
byRefArgsCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Print all the arguments for debugging purposes.
|
||||||
|
int inputArgsCount = methodArgs.Length - byRefArgsCount;
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ldc_I4_S, inputArgsCount);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Newarr, typeof(object));
|
||||||
|
|
||||||
|
string argsFormat = svcName;
|
||||||
|
|
||||||
|
for (int index = 0; index < inputArgsCount; index++)
|
||||||
|
{
|
||||||
|
argsFormat += $" {methodArgs[index].Name}: 0x{{{index}:X8}},";
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Dup);
|
||||||
|
generator.Emit(OpCodes.Ldc_I4_S, index);
|
||||||
|
generator.Emit(OpCodes.Conv_I);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ldarg_1);
|
||||||
|
generator.Emit(OpCodes.Ldfld, GetStateFieldX(byRefArgsCount + index));
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Box, typeof(ulong));
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Stelem_Ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
argsFormat = argsFormat.Substring(0, argsFormat.Length - 1);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ldstr, argsFormat);
|
||||||
|
|
||||||
|
BindingFlags staticNonPublic = BindingFlags.NonPublic | BindingFlags.Static;
|
||||||
|
|
||||||
|
MethodInfo printArgsMethod = typeof(SvcTable).GetMethod(nameof(PrintArguments), staticNonPublic);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Call, printArgsMethod);
|
||||||
|
|
||||||
|
//Call the SVC function handler.
|
||||||
|
generator.Emit(OpCodes.Ldarg_0);
|
||||||
|
|
||||||
|
List<LocalBuilder> locals = new List<LocalBuilder>();
|
||||||
|
|
||||||
|
for (int index = 0; index < methodArgs.Length; index++)
|
||||||
|
{
|
||||||
|
Type argType = methodArgs[index].ParameterType;
|
||||||
|
|
||||||
|
if (argType.IsByRef)
|
||||||
|
{
|
||||||
|
argType = argType.GetElementType();
|
||||||
|
|
||||||
|
LocalBuilder local = generator.DeclareLocal(argType);
|
||||||
|
|
||||||
|
locals.Add(local);
|
||||||
|
|
||||||
|
if (!methodArgs[index].IsOut)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Method \"{svcName}\" has a invalid ref type \"{argType.Name}\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ldloca_S, (byte)local.LocalIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
generator.Emit(OpCodes.Ldarg_1);
|
||||||
|
generator.Emit(OpCodes.Ldfld, GetStateFieldX(byRefArgsCount + index));
|
||||||
|
|
||||||
|
ConvertToArgType(argType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Call, methodInfo);
|
||||||
|
|
||||||
|
int outRegIndex = 0;
|
||||||
|
|
||||||
|
Type retType = methodInfo.ReturnType;
|
||||||
|
|
||||||
|
//Print result code.
|
||||||
|
if (retType == typeof(KernelResult))
|
||||||
|
{
|
||||||
|
MethodInfo printResultMethod = typeof(SvcTable).GetMethod(nameof(PrintResult), staticNonPublic);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Dup);
|
||||||
|
generator.Emit(OpCodes.Ldstr, svcName);
|
||||||
|
generator.Emit(OpCodes.Call, printResultMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save return value into register X0 (when the method has a return value).
|
||||||
|
if (retType != typeof(void))
|
||||||
|
{
|
||||||
|
CheckIfTypeIsSupported(retType, svcName);
|
||||||
|
|
||||||
|
LocalBuilder tempLocal = generator.DeclareLocal(retType);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Stloc, tempLocal);
|
||||||
|
generator.Emit(OpCodes.Ldarg_1);
|
||||||
|
generator.Emit(OpCodes.Ldloc, tempLocal);
|
||||||
|
|
||||||
|
ConvertToFieldType(retType);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < locals.Count; index++)
|
||||||
|
{
|
||||||
|
generator.Emit(OpCodes.Ldarg_1);
|
||||||
|
generator.Emit(OpCodes.Ldloc, locals[index]);
|
||||||
|
|
||||||
|
ConvertToFieldType(locals[index].LocalType);
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Zero out the remaining unused registers.
|
||||||
|
while (outRegIndex < SvcFuncMaxArguments)
|
||||||
|
{
|
||||||
|
generator.Emit(OpCodes.Ldarg_1);
|
||||||
|
generator.Emit(OpCodes.Ldc_I8, 0L);
|
||||||
|
generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ret);
|
||||||
|
|
||||||
|
return (Action<SvcHandler, CpuThreadState>)method.CreateDelegate(typeof(Action<SvcHandler, CpuThreadState>));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FieldInfo GetStateFieldX(int index)
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X0));
|
||||||
|
case 1: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X1));
|
||||||
|
case 2: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X2));
|
||||||
|
case 3: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X3));
|
||||||
|
case 4: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X4));
|
||||||
|
case 5: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X5));
|
||||||
|
case 6: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X6));
|
||||||
|
case 7: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X7));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckIfTypeIsSupported(Type type, string svcName)
|
||||||
|
{
|
||||||
|
switch (Type.GetTypeCode(type))
|
||||||
|
{
|
||||||
|
case TypeCode.UInt64:
|
||||||
|
case TypeCode.Int64:
|
||||||
|
case TypeCode.UInt32:
|
||||||
|
case TypeCode.Int32:
|
||||||
|
case TypeCode.UInt16:
|
||||||
|
case TypeCode.Int16:
|
||||||
|
case TypeCode.Byte:
|
||||||
|
case TypeCode.SByte:
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidSvcException($"Method \"{svcName}\" has a invalid ref type \"{type.Name}\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintResult(KernelResult result, string svcName)
|
||||||
|
{
|
||||||
|
if (result != KernelResult.Success &&
|
||||||
|
result != KernelResult.TimedOut &&
|
||||||
|
result != KernelResult.Cancelled &&
|
||||||
|
result != KernelResult.InvalidState)
|
||||||
|
{
|
||||||
|
Logger.PrintWarning(LogClass.KernelSvc, $"{svcName} returned error {result}.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.PrintDebug(LogClass.KernelSvc, $"{svcName} returned result {result}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintArguments(object[] argValues, string format)
|
||||||
|
{
|
||||||
|
Logger.PrintDebug(LogClass.KernelSvc, string.Format(format, argValues));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
420
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs
Normal file
420
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs
Normal file
|
@ -0,0 +1,420 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
partial class SvcHandler
|
||||||
|
{
|
||||||
|
public KernelResult CreateThread64(
|
||||||
|
ulong entrypoint,
|
||||||
|
ulong argsPtr,
|
||||||
|
ulong stackTop,
|
||||||
|
int priority,
|
||||||
|
int cpuCore,
|
||||||
|
out int handle)
|
||||||
|
{
|
||||||
|
return CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult CreateThread(
|
||||||
|
ulong entrypoint,
|
||||||
|
ulong argsPtr,
|
||||||
|
ulong stackTop,
|
||||||
|
int priority,
|
||||||
|
int cpuCore,
|
||||||
|
out int handle)
|
||||||
|
{
|
||||||
|
handle = 0;
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (cpuCore == -2)
|
||||||
|
{
|
||||||
|
cpuCore = currentProcess.DefaultCpuCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint)cpuCore >= KScheduler.CpuCoresCount || !currentProcess.IsCpuCoreAllowed(cpuCore))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCpuCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint)priority >= KScheduler.PrioritiesCount || !currentProcess.IsPriorityAllowed(priority))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
long timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
|
||||||
|
|
||||||
|
if (currentProcess.ResourceLimit != null &&
|
||||||
|
!currentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, timeout))
|
||||||
|
{
|
||||||
|
return KernelResult.ResLimitExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
KThread thread = new KThread(_system);
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.InitializeThread(
|
||||||
|
thread,
|
||||||
|
entrypoint,
|
||||||
|
argsPtr,
|
||||||
|
stackTop,
|
||||||
|
priority,
|
||||||
|
cpuCore);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _process.HandleTable.GenerateHandle(thread, out handle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
thread.Terminate();
|
||||||
|
|
||||||
|
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult StartThread64(int handle)
|
||||||
|
{
|
||||||
|
return StartThread(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult StartThread(int handle)
|
||||||
|
{
|
||||||
|
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
|
||||||
|
|
||||||
|
if (thread != null)
|
||||||
|
{
|
||||||
|
return thread.Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitThread64()
|
||||||
|
{
|
||||||
|
ExitThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExitThread()
|
||||||
|
{
|
||||||
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
_system.Scheduler.ExitThread(currentThread);
|
||||||
|
|
||||||
|
currentThread.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SleepThread64(long timeout)
|
||||||
|
{
|
||||||
|
SleepThread(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SleepThread(long timeout)
|
||||||
|
{
|
||||||
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
if (timeout < 1)
|
||||||
|
{
|
||||||
|
switch (timeout)
|
||||||
|
{
|
||||||
|
case 0: currentThread.Yield(); break;
|
||||||
|
case -1: currentThread.YieldWithLoadBalancing(); break;
|
||||||
|
case -2: currentThread.YieldAndWaitForLoadBalancing(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentThread.Sleep(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult GetThreadPriority64(int handle, out int priority)
|
||||||
|
{
|
||||||
|
return GetThreadPriority(handle, out priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult GetThreadPriority(int handle, out int priority)
|
||||||
|
{
|
||||||
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
|
if (thread != null)
|
||||||
|
{
|
||||||
|
priority = thread.DynamicPriority;
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
priority = 0;
|
||||||
|
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SetThreadPriority64(int handle, int priority)
|
||||||
|
{
|
||||||
|
return SetThreadPriority(handle, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SetThreadPriority(int handle, int priority)
|
||||||
|
{
|
||||||
|
//TODO: NPDM check.
|
||||||
|
|
||||||
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
|
if (thread == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.SetPriority(priority);
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult GetThreadCoreMask64(int handle, out int preferredCore, out long affinityMask)
|
||||||
|
{
|
||||||
|
return GetThreadCoreMask(handle, out preferredCore, out affinityMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult GetThreadCoreMask(int handle, out int preferredCore, out long affinityMask)
|
||||||
|
{
|
||||||
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
|
if (thread != null)
|
||||||
|
{
|
||||||
|
preferredCore = thread.PreferredCore;
|
||||||
|
affinityMask = thread.AffinityMask;
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
preferredCore = 0;
|
||||||
|
affinityMask = 0;
|
||||||
|
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SetThreadCoreMask64(int handle, int preferredCore, long affinityMask)
|
||||||
|
{
|
||||||
|
return SetThreadCoreMask(handle, preferredCore, affinityMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SetThreadCoreMask(int handle, int preferredCore, long affinityMask)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (preferredCore == -2)
|
||||||
|
{
|
||||||
|
preferredCore = currentProcess.DefaultCpuCore;
|
||||||
|
|
||||||
|
affinityMask = 1 << preferredCore;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((currentProcess.Capabilities.AllowedCpuCoresMask | affinityMask) !=
|
||||||
|
currentProcess.Capabilities.AllowedCpuCoresMask)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCpuCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (affinityMask == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint)preferredCore > 3)
|
||||||
|
{
|
||||||
|
if ((preferredCore | 2) != -1)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCpuCore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((affinityMask & (1 << preferredCore)) == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
|
if (thread == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCurrentProcessorNumber64()
|
||||||
|
{
|
||||||
|
return _system.Scheduler.GetCurrentThread().CurrentCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult GetThreadId64(int handle, out long threadUid)
|
||||||
|
{
|
||||||
|
return GetThreadId(handle, out threadUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult GetThreadId(int handle, out long threadUid)
|
||||||
|
{
|
||||||
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
|
if (thread != null)
|
||||||
|
{
|
||||||
|
threadUid = thread.ThreadUid;
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
threadUid = 0;
|
||||||
|
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SetThreadActivity64(int handle, bool pause)
|
||||||
|
{
|
||||||
|
return SetThreadActivity(handle, pause);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SetThreadActivity(int handle, bool pause)
|
||||||
|
{
|
||||||
|
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
|
||||||
|
|
||||||
|
if (thread == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread.Owner != _system.Scheduler.GetCurrentProcess())
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread == _system.Scheduler.GetCurrentThread())
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
return thread.SetActivity(pause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult GetThreadContext364(ulong address, int handle)
|
||||||
|
{
|
||||||
|
return GetThreadContext3(address, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult GetThreadContext3(ulong address, int handle)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
|
||||||
|
|
||||||
|
if (thread == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread.Owner != currentProcess)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentThread == thread)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
_memory.WriteUInt64((long)address + 0x0, thread.Context.ThreadState.X0);
|
||||||
|
_memory.WriteUInt64((long)address + 0x8, thread.Context.ThreadState.X1);
|
||||||
|
_memory.WriteUInt64((long)address + 0x10, thread.Context.ThreadState.X2);
|
||||||
|
_memory.WriteUInt64((long)address + 0x18, thread.Context.ThreadState.X3);
|
||||||
|
_memory.WriteUInt64((long)address + 0x20, thread.Context.ThreadState.X4);
|
||||||
|
_memory.WriteUInt64((long)address + 0x28, thread.Context.ThreadState.X5);
|
||||||
|
_memory.WriteUInt64((long)address + 0x30, thread.Context.ThreadState.X6);
|
||||||
|
_memory.WriteUInt64((long)address + 0x38, thread.Context.ThreadState.X7);
|
||||||
|
_memory.WriteUInt64((long)address + 0x40, thread.Context.ThreadState.X8);
|
||||||
|
_memory.WriteUInt64((long)address + 0x48, thread.Context.ThreadState.X9);
|
||||||
|
_memory.WriteUInt64((long)address + 0x50, thread.Context.ThreadState.X10);
|
||||||
|
_memory.WriteUInt64((long)address + 0x58, thread.Context.ThreadState.X11);
|
||||||
|
_memory.WriteUInt64((long)address + 0x60, thread.Context.ThreadState.X12);
|
||||||
|
_memory.WriteUInt64((long)address + 0x68, thread.Context.ThreadState.X13);
|
||||||
|
_memory.WriteUInt64((long)address + 0x70, thread.Context.ThreadState.X14);
|
||||||
|
_memory.WriteUInt64((long)address + 0x78, thread.Context.ThreadState.X15);
|
||||||
|
_memory.WriteUInt64((long)address + 0x80, thread.Context.ThreadState.X16);
|
||||||
|
_memory.WriteUInt64((long)address + 0x88, thread.Context.ThreadState.X17);
|
||||||
|
_memory.WriteUInt64((long)address + 0x90, thread.Context.ThreadState.X18);
|
||||||
|
_memory.WriteUInt64((long)address + 0x98, thread.Context.ThreadState.X19);
|
||||||
|
_memory.WriteUInt64((long)address + 0xa0, thread.Context.ThreadState.X20);
|
||||||
|
_memory.WriteUInt64((long)address + 0xa8, thread.Context.ThreadState.X21);
|
||||||
|
_memory.WriteUInt64((long)address + 0xb0, thread.Context.ThreadState.X22);
|
||||||
|
_memory.WriteUInt64((long)address + 0xb8, thread.Context.ThreadState.X23);
|
||||||
|
_memory.WriteUInt64((long)address + 0xc0, thread.Context.ThreadState.X24);
|
||||||
|
_memory.WriteUInt64((long)address + 0xc8, thread.Context.ThreadState.X25);
|
||||||
|
_memory.WriteUInt64((long)address + 0xd0, thread.Context.ThreadState.X26);
|
||||||
|
_memory.WriteUInt64((long)address + 0xd8, thread.Context.ThreadState.X27);
|
||||||
|
_memory.WriteUInt64((long)address + 0xe0, thread.Context.ThreadState.X28);
|
||||||
|
_memory.WriteUInt64((long)address + 0xe8, thread.Context.ThreadState.X29);
|
||||||
|
_memory.WriteUInt64((long)address + 0xf0, thread.Context.ThreadState.X30);
|
||||||
|
_memory.WriteUInt64((long)address + 0xf8, thread.Context.ThreadState.X31);
|
||||||
|
|
||||||
|
_memory.WriteInt64((long)address + 0x100, thread.LastPc);
|
||||||
|
|
||||||
|
_memory.WriteUInt64((long)address + 0x108, (ulong)thread.Context.ThreadState.Psr);
|
||||||
|
|
||||||
|
_memory.WriteVector128((long)address + 0x110, thread.Context.ThreadState.V0);
|
||||||
|
_memory.WriteVector128((long)address + 0x120, thread.Context.ThreadState.V1);
|
||||||
|
_memory.WriteVector128((long)address + 0x130, thread.Context.ThreadState.V2);
|
||||||
|
_memory.WriteVector128((long)address + 0x140, thread.Context.ThreadState.V3);
|
||||||
|
_memory.WriteVector128((long)address + 0x150, thread.Context.ThreadState.V4);
|
||||||
|
_memory.WriteVector128((long)address + 0x160, thread.Context.ThreadState.V5);
|
||||||
|
_memory.WriteVector128((long)address + 0x170, thread.Context.ThreadState.V6);
|
||||||
|
_memory.WriteVector128((long)address + 0x180, thread.Context.ThreadState.V7);
|
||||||
|
_memory.WriteVector128((long)address + 0x190, thread.Context.ThreadState.V8);
|
||||||
|
_memory.WriteVector128((long)address + 0x1a0, thread.Context.ThreadState.V9);
|
||||||
|
_memory.WriteVector128((long)address + 0x1b0, thread.Context.ThreadState.V10);
|
||||||
|
_memory.WriteVector128((long)address + 0x1c0, thread.Context.ThreadState.V11);
|
||||||
|
_memory.WriteVector128((long)address + 0x1d0, thread.Context.ThreadState.V12);
|
||||||
|
_memory.WriteVector128((long)address + 0x1e0, thread.Context.ThreadState.V13);
|
||||||
|
_memory.WriteVector128((long)address + 0x1f0, thread.Context.ThreadState.V14);
|
||||||
|
_memory.WriteVector128((long)address + 0x200, thread.Context.ThreadState.V15);
|
||||||
|
_memory.WriteVector128((long)address + 0x210, thread.Context.ThreadState.V16);
|
||||||
|
_memory.WriteVector128((long)address + 0x220, thread.Context.ThreadState.V17);
|
||||||
|
_memory.WriteVector128((long)address + 0x230, thread.Context.ThreadState.V18);
|
||||||
|
_memory.WriteVector128((long)address + 0x240, thread.Context.ThreadState.V19);
|
||||||
|
_memory.WriteVector128((long)address + 0x250, thread.Context.ThreadState.V20);
|
||||||
|
_memory.WriteVector128((long)address + 0x260, thread.Context.ThreadState.V21);
|
||||||
|
_memory.WriteVector128((long)address + 0x270, thread.Context.ThreadState.V22);
|
||||||
|
_memory.WriteVector128((long)address + 0x280, thread.Context.ThreadState.V23);
|
||||||
|
_memory.WriteVector128((long)address + 0x290, thread.Context.ThreadState.V24);
|
||||||
|
_memory.WriteVector128((long)address + 0x2a0, thread.Context.ThreadState.V25);
|
||||||
|
_memory.WriteVector128((long)address + 0x2b0, thread.Context.ThreadState.V26);
|
||||||
|
_memory.WriteVector128((long)address + 0x2c0, thread.Context.ThreadState.V27);
|
||||||
|
_memory.WriteVector128((long)address + 0x2d0, thread.Context.ThreadState.V28);
|
||||||
|
_memory.WriteVector128((long)address + 0x2e0, thread.Context.ThreadState.V29);
|
||||||
|
_memory.WriteVector128((long)address + 0x2f0, thread.Context.ThreadState.V30);
|
||||||
|
_memory.WriteVector128((long)address + 0x300, thread.Context.ThreadState.V31);
|
||||||
|
|
||||||
|
_memory.WriteInt32((long)address + 0x310, thread.Context.ThreadState.Fpcr);
|
||||||
|
_memory.WriteInt32((long)address + 0x314, thread.Context.ThreadState.Fpsr);
|
||||||
|
_memory.WriteInt64((long)address + 0x318, thread.Context.ThreadState.Tpidr);
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
250
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs
Normal file
250
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
partial class SvcHandler
|
||||||
|
{
|
||||||
|
public KernelResult WaitSynchronization64(ulong handlesPtr, int handlesCount, long timeout, out int handleIndex)
|
||||||
|
{
|
||||||
|
return WaitSynchronization(handlesPtr, handlesCount, timeout, out handleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult WaitSynchronization(ulong handlesPtr, int handlesCount, long timeout, out int handleIndex)
|
||||||
|
{
|
||||||
|
handleIndex = 0;
|
||||||
|
|
||||||
|
if ((uint)handlesCount > 0x40)
|
||||||
|
{
|
||||||
|
return KernelResult.MaximumExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<KSynchronizationObject> syncObjs = new List<KSynchronizationObject>();
|
||||||
|
|
||||||
|
for (int index = 0; index < handlesCount; index++)
|
||||||
|
{
|
||||||
|
int handle = _memory.ReadInt32((long)handlesPtr + index * 4);
|
||||||
|
|
||||||
|
KSynchronizationObject syncObj = _process.HandleTable.GetObject<KSynchronizationObject>(handle);
|
||||||
|
|
||||||
|
if (syncObj == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncObjs.Add(syncObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _system.Synchronization.WaitFor(syncObjs.ToArray(), timeout, out handleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult CancelSynchronization64(int handle)
|
||||||
|
{
|
||||||
|
return CancelSynchronization(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult CancelSynchronization(int handle)
|
||||||
|
{
|
||||||
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
|
if (thread == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.CancelSynchronization();
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ArbitrateLock64(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
||||||
|
{
|
||||||
|
return ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
||||||
|
{
|
||||||
|
if (IsPointingInsideKernel(mutexAddress))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAddressNotWordAligned(mutexAddress))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ArbitrateUnlock64(ulong mutexAddress)
|
||||||
|
{
|
||||||
|
return ArbitrateUnlock(mutexAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult ArbitrateUnlock(ulong mutexAddress)
|
||||||
|
{
|
||||||
|
if (IsPointingInsideKernel(mutexAddress))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAddressNotWordAligned(mutexAddress))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult WaitProcessWideKeyAtomic64(
|
||||||
|
ulong mutexAddress,
|
||||||
|
ulong condVarAddress,
|
||||||
|
int handle,
|
||||||
|
long timeout)
|
||||||
|
{
|
||||||
|
return WaitProcessWideKeyAtomic(mutexAddress, condVarAddress, handle, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult WaitProcessWideKeyAtomic(
|
||||||
|
ulong mutexAddress,
|
||||||
|
ulong condVarAddress,
|
||||||
|
int handle,
|
||||||
|
long timeout)
|
||||||
|
{
|
||||||
|
if (IsPointingInsideKernel(mutexAddress))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAddressNotWordAligned(mutexAddress))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
return currentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
|
||||||
|
mutexAddress,
|
||||||
|
condVarAddress,
|
||||||
|
handle,
|
||||||
|
timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SignalProcessWideKey64(ulong address, int count)
|
||||||
|
{
|
||||||
|
return SignalProcessWideKey(address, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SignalProcessWideKey(ulong address, int count)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
currentProcess.AddressArbiter.SignalProcessWideKey(address, count);
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult WaitForAddress64(ulong address, ArbitrationType type, int value, long timeout)
|
||||||
|
{
|
||||||
|
return WaitForAddress(address, type, value, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult WaitForAddress(ulong address, ArbitrationType type, int value, long timeout)
|
||||||
|
{
|
||||||
|
if (IsPointingInsideKernel(address))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAddressNotWordAligned(address))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ArbitrationType.WaitIfLessThan:
|
||||||
|
result = currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, false, timeout);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArbitrationType.DecrementAndWaitIfLessThan:
|
||||||
|
result = currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, true, timeout);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArbitrationType.WaitIfEqual:
|
||||||
|
result = currentProcess.AddressArbiter.WaitForAddressIfEqual(address, value, timeout);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = KernelResult.InvalidEnumValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SignalToAddress64(ulong address, SignalType type, int value, int count)
|
||||||
|
{
|
||||||
|
return SignalToAddress(address, type, value, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SignalToAddress(ulong address, SignalType type, int value, int count)
|
||||||
|
{
|
||||||
|
if (IsPointingInsideKernel(address))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAddressNotWordAligned(address))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SignalType.Signal:
|
||||||
|
result = currentProcess.AddressArbiter.Signal(address, count);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SignalType.SignalAndIncrementIfEqual:
|
||||||
|
result = currentProcess.AddressArbiter.SignalAndIncrementIfEqual(address, value, count);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SignalType.SignalAndModifyIfEqual:
|
||||||
|
result = currentProcess.AddressArbiter.SignalAndModifyIfEqual(address, value, count);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = KernelResult.InvalidEnumValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsPointingInsideKernel(ulong address)
|
||||||
|
{
|
||||||
|
return (address + 0x1000000000) < 0xffffff000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsAddressNotWordAligned(ulong address)
|
||||||
|
{
|
||||||
|
return (address & 3) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,122 +0,0 @@
|
||||||
using ChocolArm64.Events;
|
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using ChocolArm64.State;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
|
||||||
partial class SvcHandler
|
|
||||||
{
|
|
||||||
private delegate void SvcFunc(CpuThreadState threadState);
|
|
||||||
|
|
||||||
private Dictionary<int, SvcFunc> _svcFuncs;
|
|
||||||
|
|
||||||
private Switch _device;
|
|
||||||
private KProcess _process;
|
|
||||||
private Horizon _system;
|
|
||||||
private MemoryManager _memory;
|
|
||||||
|
|
||||||
private struct HleIpcMessage
|
|
||||||
{
|
|
||||||
public KThread Thread { get; private set; }
|
|
||||||
public KSession Session { get; private set; }
|
|
||||||
public IpcMessage Message { get; private set; }
|
|
||||||
public long MessagePtr { get; private set; }
|
|
||||||
|
|
||||||
public HleIpcMessage(
|
|
||||||
KThread thread,
|
|
||||||
KSession session,
|
|
||||||
IpcMessage message,
|
|
||||||
long messagePtr)
|
|
||||||
{
|
|
||||||
Thread = thread;
|
|
||||||
Session = session;
|
|
||||||
Message = message;
|
|
||||||
MessagePtr = messagePtr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SvcHandler(Switch device, KProcess process)
|
|
||||||
{
|
|
||||||
_svcFuncs = new Dictionary<int, SvcFunc>
|
|
||||||
{
|
|
||||||
{ 0x01, SvcSetHeapSize },
|
|
||||||
{ 0x03, SvcSetMemoryAttribute },
|
|
||||||
{ 0x04, SvcMapMemory },
|
|
||||||
{ 0x05, SvcUnmapMemory },
|
|
||||||
{ 0x06, SvcQueryMemory },
|
|
||||||
{ 0x07, SvcExitProcess },
|
|
||||||
{ 0x08, CreateThread64 },
|
|
||||||
{ 0x09, SvcStartThread },
|
|
||||||
{ 0x0a, SvcExitThread },
|
|
||||||
{ 0x0b, SvcSleepThread },
|
|
||||||
{ 0x0c, SvcGetThreadPriority },
|
|
||||||
{ 0x0d, SvcSetThreadPriority },
|
|
||||||
{ 0x0e, SvcGetThreadCoreMask },
|
|
||||||
{ 0x0f, SetThreadCoreMask64 },
|
|
||||||
{ 0x10, SvcGetCurrentProcessorNumber },
|
|
||||||
{ 0x11, SignalEvent64 },
|
|
||||||
{ 0x12, ClearEvent64 },
|
|
||||||
{ 0x13, SvcMapSharedMemory },
|
|
||||||
{ 0x14, SvcUnmapSharedMemory },
|
|
||||||
{ 0x15, SvcCreateTransferMemory },
|
|
||||||
{ 0x16, SvcCloseHandle },
|
|
||||||
{ 0x17, ResetSignal64 },
|
|
||||||
{ 0x18, SvcWaitSynchronization },
|
|
||||||
{ 0x19, SvcCancelSynchronization },
|
|
||||||
{ 0x1a, SvcArbitrateLock },
|
|
||||||
{ 0x1b, SvcArbitrateUnlock },
|
|
||||||
{ 0x1c, SvcWaitProcessWideKeyAtomic },
|
|
||||||
{ 0x1d, SvcSignalProcessWideKey },
|
|
||||||
{ 0x1e, SvcGetSystemTick },
|
|
||||||
{ 0x1f, SvcConnectToNamedPort },
|
|
||||||
{ 0x21, SvcSendSyncRequest },
|
|
||||||
{ 0x22, SvcSendSyncRequestWithUserBuffer },
|
|
||||||
{ 0x24, GetProcessId64 },
|
|
||||||
{ 0x25, SvcGetThreadId },
|
|
||||||
{ 0x26, SvcBreak },
|
|
||||||
{ 0x27, SvcOutputDebugString },
|
|
||||||
{ 0x29, GetInfo64 },
|
|
||||||
{ 0x2c, SvcMapPhysicalMemory },
|
|
||||||
{ 0x2d, SvcUnmapPhysicalMemory },
|
|
||||||
{ 0x32, SvcSetThreadActivity },
|
|
||||||
{ 0x33, SvcGetThreadContext3 },
|
|
||||||
{ 0x34, SvcWaitForAddress },
|
|
||||||
{ 0x35, SvcSignalToAddress },
|
|
||||||
{ 0x45, CreateEvent64 },
|
|
||||||
{ 0x65, GetProcessList64 },
|
|
||||||
{ 0x6f, GetSystemInfo64 },
|
|
||||||
{ 0x70, CreatePort64 },
|
|
||||||
{ 0x71, ManageNamedPort64 }
|
|
||||||
};
|
|
||||||
|
|
||||||
_device = device;
|
|
||||||
_process = process;
|
|
||||||
_system = device.System;
|
|
||||||
_memory = process.CpuMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SvcCall(object sender, InstExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
CpuThreadState threadState = (CpuThreadState)sender;
|
|
||||||
|
|
||||||
if (_svcFuncs.TryGetValue(e.Id, out SvcFunc func))
|
|
||||||
{
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, $"{func.Method.Name} called.");
|
|
||||||
|
|
||||||
func(threadState);
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, $"{func.Method.Name} ended.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Process.PrintStackTrace(ThreadState);
|
|
||||||
|
|
||||||
throw new NotImplementedException($"0x{e.Id:x4}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,581 +0,0 @@
|
||||||
using ChocolArm64.State;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
|
||||||
partial class SvcHandler
|
|
||||||
{
|
|
||||||
private void SvcSetHeapSize(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong size = threadState.X1;
|
|
||||||
|
|
||||||
if ((size & 0xfffffffe001fffff) != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{size:x16} is not aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = _process.MemoryManager.SetHeapSize(size, out ulong position);
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
|
||||||
{
|
|
||||||
threadState.X1 = position;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{result}\".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcSetMemoryAttribute(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong position = threadState.X0;
|
|
||||||
ulong size = threadState.X1;
|
|
||||||
|
|
||||||
if (!PageAligned(position))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{position:x16} is not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.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)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = _process.MemoryManager.SetMemoryAttribute(
|
|
||||||
position,
|
|
||||||
size,
|
|
||||||
attributeMask,
|
|
||||||
attributeValue);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{result}\".");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_memory.StopObservingRegion((long)position, (long)size);
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcMapMemory(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong dst = threadState.X0;
|
|
||||||
ulong src = threadState.X1;
|
|
||||||
ulong size = threadState.X2;
|
|
||||||
|
|
||||||
if (!PageAligned(src | dst))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{size:x16} is not page aligned or is zero!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src + size <= src || dst + size <= dst)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{src:x16} out of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideHeapRegion (dst, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideAliasRegion (dst, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{dst:x16} out of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = _process.MemoryManager.Map(dst, src, size);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcUnmapMemory(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong dst = threadState.X0;
|
|
||||||
ulong src = threadState.X1;
|
|
||||||
ulong size = threadState.X2;
|
|
||||||
|
|
||||||
if (!PageAligned(src | dst))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{size:x16} is not page aligned or is zero!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src + size <= src || dst + size <= dst)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{src:x16} out of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideHeapRegion (dst, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideAliasRegion (dst, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{dst:x16} out of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = _process.MemoryManager.Unmap(dst, src, size);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcQueryMemory(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long infoPtr = (long)threadState.X0;
|
|
||||||
ulong position = threadState.X2;
|
|
||||||
|
|
||||||
KMemoryInfo blkInfo = _process.MemoryManager.QueryMemory(position);
|
|
||||||
|
|
||||||
_memory.WriteUInt64(infoPtr + 0x00, blkInfo.Address);
|
|
||||||
_memory.WriteUInt64(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(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X0;
|
|
||||||
ulong address = threadState.X1;
|
|
||||||
ulong size = threadState.X2;
|
|
||||||
|
|
||||||
if (!PageAligned(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{address:x16} is not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{size:x16} is not page aligned or is zero!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address + size <= address)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{address:x16} / size 0x{size:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryPermission permission = (MemoryPermission)threadState.X3;
|
|
||||||
|
|
||||||
if ((permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {permission}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
|
|
||||||
|
|
||||||
if (sharedMemory == null)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentProcess.MemoryManager.IsInvalidRegion (address, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideHeapRegion (address, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideAliasRegion(address, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{address:x16} out of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = sharedMemory.MapIntoProcess(
|
|
||||||
currentProcess.MemoryManager,
|
|
||||||
address,
|
|
||||||
size,
|
|
||||||
currentProcess,
|
|
||||||
permission);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{result}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcUnmapSharedMemory(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X0;
|
|
||||||
ulong address = threadState.X1;
|
|
||||||
ulong size = threadState.X2;
|
|
||||||
|
|
||||||
if (!PageAligned(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{address:x16} is not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{size:x16} is not page aligned or is zero!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address + size <= address)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{address:x16} / size 0x{size:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
|
|
||||||
|
|
||||||
if (sharedMemory == null)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentProcess.MemoryManager.IsInvalidRegion (address, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideHeapRegion (address, size) ||
|
|
||||||
currentProcess.MemoryManager.InsideAliasRegion(address, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{address:x16} out of range!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = sharedMemory.UnmapFromProcess(
|
|
||||||
currentProcess.MemoryManager,
|
|
||||||
address,
|
|
||||||
size,
|
|
||||||
currentProcess);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{result}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcCreateTransferMemory(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong address = threadState.X1;
|
|
||||||
ulong size = threadState.X2;
|
|
||||||
|
|
||||||
if (!PageAligned(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{address:x16} is not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{size:x16} is not page aligned or is zero!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address + size <= address)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{address: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)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {permission}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_process.MemoryManager.ReserveTransferMemory(address, size, permission);
|
|
||||||
|
|
||||||
KTransferMemory transferMemory = new KTransferMemory(address, size);
|
|
||||||
|
|
||||||
KernelResult result = _process.HandleTable.GenerateHandle(transferMemory, out int handle);
|
|
||||||
|
|
||||||
threadState.X0 = (uint)result;
|
|
||||||
threadState.X1 = (ulong)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcMapPhysicalMemory(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong address = threadState.X0;
|
|
||||||
ulong size = threadState.X1;
|
|
||||||
|
|
||||||
if (!PageAligned(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{address:x16} is not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{size:x16} is not page aligned or is zero!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address + size <= address)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{address:x16} / size 0x{size:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentProcess.MemoryManager.InsideAddrSpace (address, size) ||
|
|
||||||
currentProcess.MemoryManager.OutsideAliasRegion(address, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {address:x16}.");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = _process.MemoryManager.MapPhysicalMemory(address, size);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcUnmapPhysicalMemory(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong address = threadState.X0;
|
|
||||||
ulong size = threadState.X1;
|
|
||||||
|
|
||||||
if (!PageAligned(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{address:x16} is not page aligned!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageAligned(size) || size == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{size:x16} is not page aligned or is zero!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address + size <= address)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{address:x16} / size 0x{size:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentProcess.MemoryManager.InsideAddrSpace (address, size) ||
|
|
||||||
currentProcess.MemoryManager.OutsideAliasRegion(address, size))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {address:x16}.");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelResult result = _process.MemoryManager.UnmapPhysicalMemory(address, size);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool PageAligned(ulong position)
|
|
||||||
{
|
|
||||||
return (position & (KMemoryManager.PageSize - 1)) == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,464 +0,0 @@
|
||||||
using ChocolArm64.State;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
|
||||||
partial class SvcHandler
|
|
||||||
{
|
|
||||||
private void CreateThread64(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
ulong entrypoint = threadState.X1;
|
|
||||||
ulong argsPtr = threadState.X2;
|
|
||||||
ulong stackTop = threadState.X3;
|
|
||||||
int priority = (int)threadState.X4;
|
|
||||||
int cpuCore = (int)threadState.X5;
|
|
||||||
|
|
||||||
KernelResult result = CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out int handle);
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
threadState.X1 = (ulong)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private KernelResult CreateThread(
|
|
||||||
ulong entrypoint,
|
|
||||||
ulong argsPtr,
|
|
||||||
ulong stackTop,
|
|
||||||
int priority,
|
|
||||||
int cpuCore,
|
|
||||||
out int handle)
|
|
||||||
{
|
|
||||||
handle = 0;
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (cpuCore == -2)
|
|
||||||
{
|
|
||||||
cpuCore = currentProcess.DefaultCpuCore;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)cpuCore >= KScheduler.CpuCoresCount || !currentProcess.IsCpuCoreAllowed(cpuCore))
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidCpuCore;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)priority >= KScheduler.PrioritiesCount || !currentProcess.IsPriorityAllowed(priority))
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidPriority;
|
|
||||||
}
|
|
||||||
|
|
||||||
long timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
|
|
||||||
|
|
||||||
if (currentProcess.ResourceLimit != null &&
|
|
||||||
!currentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, timeout))
|
|
||||||
{
|
|
||||||
return KernelResult.ResLimitExceeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
KThread thread = new KThread(_system);
|
|
||||||
|
|
||||||
KernelResult result = currentProcess.InitializeThread(
|
|
||||||
thread,
|
|
||||||
entrypoint,
|
|
||||||
argsPtr,
|
|
||||||
stackTop,
|
|
||||||
priority,
|
|
||||||
cpuCore);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = _process.HandleTable.GenerateHandle(thread, out handle);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
thread.Terminate();
|
|
||||||
|
|
||||||
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcStartThread(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X0;
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
|
|
||||||
|
|
||||||
if (thread != null)
|
|
||||||
{
|
|
||||||
KernelResult result = thread.Start();
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{result}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcExitThread(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
|
||||||
|
|
||||||
_system.Scheduler.ExitThread(currentThread);
|
|
||||||
|
|
||||||
currentThread.Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcSleepThread(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long timeout = (long)threadState.X0;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, "Timeout = 0x" + timeout.ToString("x16"));
|
|
||||||
|
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
|
||||||
|
|
||||||
if (timeout < 1)
|
|
||||||
{
|
|
||||||
switch (timeout)
|
|
||||||
{
|
|
||||||
case 0: currentThread.Yield(); break;
|
|
||||||
case -1: currentThread.YieldWithLoadBalancing(); break;
|
|
||||||
case -2: currentThread.YieldAndWaitForLoadBalancing(); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentThread.Sleep(timeout);
|
|
||||||
|
|
||||||
threadState.X0 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcGetThreadPriority(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X1;
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetKThread(handle);
|
|
||||||
|
|
||||||
if (thread != null)
|
|
||||||
{
|
|
||||||
threadState.X0 = 0;
|
|
||||||
threadState.X1 = (ulong)thread.DynamicPriority;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcSetThreadPriority(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X0;
|
|
||||||
int priority = (int)threadState.X1;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"Handle = 0x" + handle .ToString("x8") + ", " +
|
|
||||||
"Priority = 0x" + priority.ToString("x8"));
|
|
||||||
|
|
||||||
//TODO: NPDM check.
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetKThread(handle);
|
|
||||||
|
|
||||||
if (thread == null)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread.SetPriority(priority);
|
|
||||||
|
|
||||||
threadState.X0 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcGetThreadCoreMask(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X2;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + handle.ToString("x8"));
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetKThread(handle);
|
|
||||||
|
|
||||||
if (thread != null)
|
|
||||||
{
|
|
||||||
threadState.X0 = 0;
|
|
||||||
threadState.X1 = (ulong)thread.PreferredCore;
|
|
||||||
threadState.X2 = (ulong)thread.AffinityMask;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetThreadCoreMask64(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X0;
|
|
||||||
int preferredCore = (int)threadState.X1;
|
|
||||||
long affinityMask = (long)threadState.X2;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"Handle = 0x" + handle .ToString("x8") + ", " +
|
|
||||||
"PreferredCore = 0x" + preferredCore.ToString("x8") + ", " +
|
|
||||||
"AffinityMask = 0x" + affinityMask .ToString("x16"));
|
|
||||||
|
|
||||||
KernelResult result = SetThreadCoreMask(handle, preferredCore, affinityMask);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{result}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private KernelResult SetThreadCoreMask(int handle, int preferredCore, long affinityMask)
|
|
||||||
{
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (preferredCore == -2)
|
|
||||||
{
|
|
||||||
preferredCore = currentProcess.DefaultCpuCore;
|
|
||||||
|
|
||||||
affinityMask = 1 << preferredCore;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((currentProcess.Capabilities.AllowedCpuCoresMask | affinityMask) !=
|
|
||||||
currentProcess.Capabilities.AllowedCpuCoresMask)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidCpuCore;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (affinityMask == 0)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidCombination;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)preferredCore > 3)
|
|
||||||
{
|
|
||||||
if ((preferredCore | 2) != -1)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidCpuCore;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((affinityMask & (1 << preferredCore)) == 0)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidCombination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetKThread(handle);
|
|
||||||
|
|
||||||
if (thread == null)
|
|
||||||
{
|
|
||||||
return KernelResult.InvalidHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcGetCurrentProcessorNumber(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
threadState.X0 = (ulong)_system.Scheduler.GetCurrentThread().CurrentCore;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcGetThreadId(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X1;
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetKThread(handle);
|
|
||||||
|
|
||||||
if (thread != null)
|
|
||||||
{
|
|
||||||
threadState.X0 = 0;
|
|
||||||
threadState.X1 = (ulong)thread.ThreadUid;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcSetThreadActivity(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int handle = (int)threadState.X0;
|
|
||||||
bool pause = (int)threadState.X1 == 1;
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
|
|
||||||
|
|
||||||
if (thread == null)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread.Owner != _system.Scheduler.GetCurrentProcess())
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread == _system.Scheduler.GetCurrentThread())
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)KernelResult.InvalidThread;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long result = thread.SetActivity(pause);
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcGetThreadContext3(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long position = (long)threadState.X0;
|
|
||||||
int handle = (int)threadState.X1;
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
|
|
||||||
|
|
||||||
if (thread == null)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread.Owner != currentProcess)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentThread == thread)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_memory.WriteUInt64(position + 0x0, thread.Context.ThreadState.X0);
|
|
||||||
_memory.WriteUInt64(position + 0x8, thread.Context.ThreadState.X1);
|
|
||||||
_memory.WriteUInt64(position + 0x10, thread.Context.ThreadState.X2);
|
|
||||||
_memory.WriteUInt64(position + 0x18, thread.Context.ThreadState.X3);
|
|
||||||
_memory.WriteUInt64(position + 0x20, thread.Context.ThreadState.X4);
|
|
||||||
_memory.WriteUInt64(position + 0x28, thread.Context.ThreadState.X5);
|
|
||||||
_memory.WriteUInt64(position + 0x30, thread.Context.ThreadState.X6);
|
|
||||||
_memory.WriteUInt64(position + 0x38, thread.Context.ThreadState.X7);
|
|
||||||
_memory.WriteUInt64(position + 0x40, thread.Context.ThreadState.X8);
|
|
||||||
_memory.WriteUInt64(position + 0x48, thread.Context.ThreadState.X9);
|
|
||||||
_memory.WriteUInt64(position + 0x50, thread.Context.ThreadState.X10);
|
|
||||||
_memory.WriteUInt64(position + 0x58, thread.Context.ThreadState.X11);
|
|
||||||
_memory.WriteUInt64(position + 0x60, thread.Context.ThreadState.X12);
|
|
||||||
_memory.WriteUInt64(position + 0x68, thread.Context.ThreadState.X13);
|
|
||||||
_memory.WriteUInt64(position + 0x70, thread.Context.ThreadState.X14);
|
|
||||||
_memory.WriteUInt64(position + 0x78, thread.Context.ThreadState.X15);
|
|
||||||
_memory.WriteUInt64(position + 0x80, thread.Context.ThreadState.X16);
|
|
||||||
_memory.WriteUInt64(position + 0x88, thread.Context.ThreadState.X17);
|
|
||||||
_memory.WriteUInt64(position + 0x90, thread.Context.ThreadState.X18);
|
|
||||||
_memory.WriteUInt64(position + 0x98, thread.Context.ThreadState.X19);
|
|
||||||
_memory.WriteUInt64(position + 0xa0, thread.Context.ThreadState.X20);
|
|
||||||
_memory.WriteUInt64(position + 0xa8, thread.Context.ThreadState.X21);
|
|
||||||
_memory.WriteUInt64(position + 0xb0, thread.Context.ThreadState.X22);
|
|
||||||
_memory.WriteUInt64(position + 0xb8, thread.Context.ThreadState.X23);
|
|
||||||
_memory.WriteUInt64(position + 0xc0, thread.Context.ThreadState.X24);
|
|
||||||
_memory.WriteUInt64(position + 0xc8, thread.Context.ThreadState.X25);
|
|
||||||
_memory.WriteUInt64(position + 0xd0, thread.Context.ThreadState.X26);
|
|
||||||
_memory.WriteUInt64(position + 0xd8, thread.Context.ThreadState.X27);
|
|
||||||
_memory.WriteUInt64(position + 0xe0, thread.Context.ThreadState.X28);
|
|
||||||
_memory.WriteUInt64(position + 0xe8, thread.Context.ThreadState.X29);
|
|
||||||
_memory.WriteUInt64(position + 0xf0, thread.Context.ThreadState.X30);
|
|
||||||
_memory.WriteUInt64(position + 0xf8, thread.Context.ThreadState.X31);
|
|
||||||
|
|
||||||
_memory.WriteInt64(position + 0x100, thread.LastPc);
|
|
||||||
|
|
||||||
_memory.WriteUInt64(position + 0x108, (ulong)thread.Context.ThreadState.Psr);
|
|
||||||
|
|
||||||
_memory.WriteVector128(position + 0x110, thread.Context.ThreadState.V0);
|
|
||||||
_memory.WriteVector128(position + 0x120, thread.Context.ThreadState.V1);
|
|
||||||
_memory.WriteVector128(position + 0x130, thread.Context.ThreadState.V2);
|
|
||||||
_memory.WriteVector128(position + 0x140, thread.Context.ThreadState.V3);
|
|
||||||
_memory.WriteVector128(position + 0x150, thread.Context.ThreadState.V4);
|
|
||||||
_memory.WriteVector128(position + 0x160, thread.Context.ThreadState.V5);
|
|
||||||
_memory.WriteVector128(position + 0x170, thread.Context.ThreadState.V6);
|
|
||||||
_memory.WriteVector128(position + 0x180, thread.Context.ThreadState.V7);
|
|
||||||
_memory.WriteVector128(position + 0x190, thread.Context.ThreadState.V8);
|
|
||||||
_memory.WriteVector128(position + 0x1a0, thread.Context.ThreadState.V9);
|
|
||||||
_memory.WriteVector128(position + 0x1b0, thread.Context.ThreadState.V10);
|
|
||||||
_memory.WriteVector128(position + 0x1c0, thread.Context.ThreadState.V11);
|
|
||||||
_memory.WriteVector128(position + 0x1d0, thread.Context.ThreadState.V12);
|
|
||||||
_memory.WriteVector128(position + 0x1e0, thread.Context.ThreadState.V13);
|
|
||||||
_memory.WriteVector128(position + 0x1f0, thread.Context.ThreadState.V14);
|
|
||||||
_memory.WriteVector128(position + 0x200, thread.Context.ThreadState.V15);
|
|
||||||
_memory.WriteVector128(position + 0x210, thread.Context.ThreadState.V16);
|
|
||||||
_memory.WriteVector128(position + 0x220, thread.Context.ThreadState.V17);
|
|
||||||
_memory.WriteVector128(position + 0x230, thread.Context.ThreadState.V18);
|
|
||||||
_memory.WriteVector128(position + 0x240, thread.Context.ThreadState.V19);
|
|
||||||
_memory.WriteVector128(position + 0x250, thread.Context.ThreadState.V20);
|
|
||||||
_memory.WriteVector128(position + 0x260, thread.Context.ThreadState.V21);
|
|
||||||
_memory.WriteVector128(position + 0x270, thread.Context.ThreadState.V22);
|
|
||||||
_memory.WriteVector128(position + 0x280, thread.Context.ThreadState.V23);
|
|
||||||
_memory.WriteVector128(position + 0x290, thread.Context.ThreadState.V24);
|
|
||||||
_memory.WriteVector128(position + 0x2a0, thread.Context.ThreadState.V25);
|
|
||||||
_memory.WriteVector128(position + 0x2b0, thread.Context.ThreadState.V26);
|
|
||||||
_memory.WriteVector128(position + 0x2c0, thread.Context.ThreadState.V27);
|
|
||||||
_memory.WriteVector128(position + 0x2d0, thread.Context.ThreadState.V28);
|
|
||||||
_memory.WriteVector128(position + 0x2e0, thread.Context.ThreadState.V29);
|
|
||||||
_memory.WriteVector128(position + 0x2f0, thread.Context.ThreadState.V30);
|
|
||||||
_memory.WriteVector128(position + 0x300, thread.Context.ThreadState.V31);
|
|
||||||
|
|
||||||
_memory.WriteInt32(position + 0x310, thread.Context.ThreadState.Fpcr);
|
|
||||||
_memory.WriteInt32(position + 0x314, thread.Context.ThreadState.Fpsr);
|
|
||||||
_memory.WriteInt64(position + 0x318, thread.Context.ThreadState.Tpidr);
|
|
||||||
|
|
||||||
threadState.X0 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,373 +0,0 @@
|
||||||
using ChocolArm64.State;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
|
||||||
partial class SvcHandler
|
|
||||||
{
|
|
||||||
private void SvcWaitSynchronization(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long handlesPtr = (long)threadState.X1;
|
|
||||||
int handlesCount = (int)threadState.X2;
|
|
||||||
long timeout = (long)threadState.X3;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"HandlesPtr = 0x" + handlesPtr .ToString("x16") + ", " +
|
|
||||||
"HandlesCount = 0x" + handlesCount.ToString("x8") + ", " +
|
|
||||||
"Timeout = 0x" + timeout .ToString("x16"));
|
|
||||||
|
|
||||||
if ((uint)handlesCount > 0x40)
|
|
||||||
{
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<KSynchronizationObject> syncObjs = new List<KSynchronizationObject>();
|
|
||||||
|
|
||||||
for (int index = 0; index < handlesCount; index++)
|
|
||||||
{
|
|
||||||
int handle = _memory.ReadInt32(handlesPtr + index * 4);
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{handle:x8}");
|
|
||||||
|
|
||||||
KSynchronizationObject syncObj = _process.HandleTable.GetObject<KSynchronizationObject>(handle);
|
|
||||||
|
|
||||||
if (syncObj == null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
syncObjs.Add(syncObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hndIndex = (int)threadState.X1;
|
|
||||||
|
|
||||||
ulong high = threadState.X1 & (0xffffffffUL << 32);
|
|
||||||
|
|
||||||
long result = _system.Synchronization.WaitFor(syncObjs.ToArray(), timeout, ref hndIndex);
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
if (result == MakeError(ErrorModule.Kernel, KernelErr.Timeout) ||
|
|
||||||
result == MakeError(ErrorModule.Kernel, KernelErr.Cancelled))
|
|
||||||
{
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
threadState.X1 = (uint)hndIndex | high;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcCancelSynchronization(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int threadHandle = (int)threadState.X0;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + threadHandle.ToString("x8"));
|
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetKThread(threadHandle);
|
|
||||||
|
|
||||||
if (thread == null)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{threadHandle:x8}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread.CancelSynchronization();
|
|
||||||
|
|
||||||
threadState.X0 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcArbitrateLock(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
int ownerHandle = (int)threadState.X0;
|
|
||||||
long mutexAddress = (long)threadState.X1;
|
|
||||||
int requesterHandle = (int)threadState.X2;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"OwnerHandle = 0x" + ownerHandle .ToString("x8") + ", " +
|
|
||||||
"MutexAddress = 0x" + mutexAddress .ToString("x16") + ", " +
|
|
||||||
"RequesterHandle = 0x" + requesterHandle.ToString("x8"));
|
|
||||||
|
|
||||||
if (IsPointingInsideKernel(mutexAddress))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{mutexAddress:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsAddressNotWordAligned(mutexAddress))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{mutexAddress:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
long result = currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcArbitrateUnlock(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long mutexAddress = (long)threadState.X0;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + mutexAddress.ToString("x16"));
|
|
||||||
|
|
||||||
if (IsPointingInsideKernel(mutexAddress))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{mutexAddress:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsAddressNotWordAligned(mutexAddress))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{mutexAddress:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
long result = currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcWaitProcessWideKeyAtomic(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long mutexAddress = (long)threadState.X0;
|
|
||||||
long condVarAddress = (long)threadState.X1;
|
|
||||||
int threadHandle = (int)threadState.X2;
|
|
||||||
long timeout = (long)threadState.X3;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"MutexAddress = 0x" + mutexAddress .ToString("x16") + ", " +
|
|
||||||
"CondVarAddress = 0x" + condVarAddress.ToString("x16") + ", " +
|
|
||||||
"ThreadHandle = 0x" + threadHandle .ToString("x8") + ", " +
|
|
||||||
"Timeout = 0x" + timeout .ToString("x16"));
|
|
||||||
|
|
||||||
if (IsPointingInsideKernel(mutexAddress))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{mutexAddress:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsAddressNotWordAligned(mutexAddress))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{mutexAddress:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
long result = currentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
|
|
||||||
mutexAddress,
|
|
||||||
condVarAddress,
|
|
||||||
threadHandle,
|
|
||||||
timeout);
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
if (result == MakeError(ErrorModule.Kernel, KernelErr.Timeout))
|
|
||||||
{
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcSignalProcessWideKey(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long address = (long)threadState.X0;
|
|
||||||
int count = (int)threadState.X1;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"Address = 0x" + address.ToString("x16") + ", " +
|
|
||||||
"Count = 0x" + count .ToString("x8"));
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
currentProcess.AddressArbiter.SignalProcessWideKey(address, count);
|
|
||||||
|
|
||||||
threadState.X0 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcWaitForAddress(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long address = (long)threadState.X0;
|
|
||||||
ArbitrationType type = (ArbitrationType)threadState.X1;
|
|
||||||
int value = (int)threadState.X2;
|
|
||||||
long timeout = (long)threadState.X3;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"Address = 0x" + address.ToString("x16") + ", " +
|
|
||||||
"Type = " + type .ToString() + ", " +
|
|
||||||
"Value = 0x" + value .ToString("x8") + ", " +
|
|
||||||
"Timeout = 0x" + timeout.ToString("x16"));
|
|
||||||
|
|
||||||
if (IsPointingInsideKernel(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{address:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsAddressNotWordAligned(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{address:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
long result;
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case ArbitrationType.WaitIfLessThan:
|
|
||||||
result = currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, false, timeout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ArbitrationType.DecrementAndWaitIfLessThan:
|
|
||||||
result = currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, true, timeout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ArbitrationType.WaitIfEqual:
|
|
||||||
result = currentProcess.AddressArbiter.WaitForAddressIfEqual(address, value, timeout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SvcSignalToAddress(CpuThreadState threadState)
|
|
||||||
{
|
|
||||||
long address = (long)threadState.X0;
|
|
||||||
SignalType type = (SignalType)threadState.X1;
|
|
||||||
int value = (int)threadState.X2;
|
|
||||||
int count = (int)threadState.X3;
|
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
|
||||||
"Address = 0x" + address.ToString("x16") + ", " +
|
|
||||||
"Type = " + type .ToString() + ", " +
|
|
||||||
"Value = 0x" + value .ToString("x8") + ", " +
|
|
||||||
"Count = 0x" + count .ToString("x8"));
|
|
||||||
|
|
||||||
if (IsPointingInsideKernel(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{address:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsAddressNotWordAligned(address))
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{address:x16}!");
|
|
||||||
|
|
||||||
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
long result;
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case SignalType.Signal:
|
|
||||||
result = currentProcess.AddressArbiter.Signal(address, count);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SignalType.SignalAndIncrementIfEqual:
|
|
||||||
result = currentProcess.AddressArbiter.SignalAndIncrementIfEqual(address, value, count);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SignalType.SignalAndModifyIfEqual:
|
|
||||||
result = currentProcess.AddressArbiter.SignalAndModifyIfEqual(address, value, count);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
threadState.X0 = (ulong)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsPointingInsideKernel(long address)
|
|
||||||
{
|
|
||||||
return ((ulong)address + 0x1000000000) < 0xffffff000;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsAddressNotWordAligned(long address)
|
|
||||||
{
|
|
||||||
return (address & 3) != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
enum ArbitrationType
|
enum ArbitrationType
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class HleCoreManager
|
class HleCoreManager
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
partial class KScheduler
|
partial class KScheduler
|
||||||
{
|
{
|
|
@ -1,9 +1,9 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
{
|
||||||
class KAddressArbiter
|
class KAddressArbiter
|
||||||
{
|
{
|
||||||
|
@ -22,14 +22,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ArbiterThreads = new List<KThread>();
|
ArbiterThreads = new List<KThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ArbitrateLock(int ownerHandle, long mutexAddress, int requesterHandle)
|
public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
||||||
{
|
{
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
currentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = 0;
|
currentThread.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return KernelResult.InvalidMemState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mutexValue != (ownerHandle | HasListenersMask))
|
if (mutexValue != (ownerHandle | HasListenersMask))
|
||||||
|
@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
return KernelResult.InvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.MutexAddress = mutexAddress;
|
currentThread.MutexAddress = mutexAddress;
|
||||||
|
@ -73,21 +73,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return (uint)currentThread.ObjSyncResult;
|
return (KernelResult)currentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ArbitrateUnlock(long mutexAddress)
|
public KernelResult ArbitrateUnlock(ulong mutexAddress)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
(long result, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress);
|
(KernelResult result, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress);
|
||||||
|
|
||||||
if (result != 0 && newOwnerThread != null)
|
if (result != KernelResult.Success && newOwnerThread != null)
|
||||||
{
|
{
|
||||||
newOwnerThread.SignaledObj = null;
|
newOwnerThread.SignaledObj = null;
|
||||||
newOwnerThread.ObjSyncResult = (int)result;
|
newOwnerThread.ObjSyncResult = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
@ -95,9 +95,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitProcessWideKeyAtomic(
|
public KernelResult WaitProcessWideKeyAtomic(
|
||||||
long mutexAddress,
|
ulong mutexAddress,
|
||||||
long condVarAddress,
|
ulong condVarAddress,
|
||||||
int threadHandle,
|
int threadHandle,
|
||||||
long timeout)
|
long timeout)
|
||||||
{
|
{
|
||||||
|
@ -106,19 +106,19 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
currentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
currentThread.ObjSyncResult = KernelResult.TimedOut;
|
||||||
|
|
||||||
if (currentThread.ShallBeTerminated ||
|
if (currentThread.ShallBeTerminated ||
|
||||||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return KernelResult.ThreadTerminating;
|
||||||
}
|
}
|
||||||
|
|
||||||
(long result, _) = MutexUnlock(currentThread, mutexAddress);
|
(KernelResult result, _) = MutexUnlock(currentThread, mutexAddress);
|
||||||
|
|
||||||
if (result != 0)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
|
@ -159,10 +159,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return (uint)currentThread.ObjSyncResult;
|
return (KernelResult)currentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (long, KThread) MutexUnlock(KThread currentThread, long mutexAddress)
|
private (KernelResult, KThread) MutexUnlock(KThread currentThread, ulong mutexAddress)
|
||||||
{
|
{
|
||||||
KThread newOwnerThread = currentThread.RelinquishMutex(mutexAddress, out int count);
|
KThread newOwnerThread = currentThread.RelinquishMutex(mutexAddress, out int count);
|
||||||
|
|
||||||
|
@ -178,22 +178,22 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
|
|
||||||
newOwnerThread.SignaledObj = null;
|
newOwnerThread.SignaledObj = null;
|
||||||
newOwnerThread.ObjSyncResult = 0;
|
newOwnerThread.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
newOwnerThread.ReleaseAndResume();
|
newOwnerThread.ReleaseAndResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
long result = 0;
|
KernelResult result = KernelResult.Success;
|
||||||
|
|
||||||
if (!KernelTransfer.KernelToUserInt32(_system, mutexAddress, mutexValue))
|
if (!KernelTransfer.KernelToUserInt32(_system, mutexAddress, mutexValue))
|
||||||
{
|
{
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
result = KernelResult.InvalidMemState;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (result, newOwnerThread);
|
return (result, newOwnerThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SignalProcessWideKey(long address, int count)
|
public void SignalProcessWideKey(ulong address, int count)
|
||||||
{
|
{
|
||||||
Queue<KThread> signaledThreads = new Queue<KThread>();
|
Queue<KThread> signaledThreads = new Queue<KThread>();
|
||||||
|
|
||||||
|
@ -224,11 +224,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private KThread TryAcquireMutex(KThread requester)
|
private KThread TryAcquireMutex(KThread requester)
|
||||||
{
|
{
|
||||||
long address = requester.MutexAddress;
|
ulong address = requester.MutexAddress;
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int mutexValue))
|
if (!KernelTransfer.UserToKernelInt32(_system, address, out int mutexValue))
|
||||||
{
|
{
|
||||||
|
@ -236,24 +236,24 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
currentProcess.CpuMemory.ClearExclusive(0);
|
currentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
|
||||||
requester.SignaledObj = null;
|
requester.SignaledObj = null;
|
||||||
requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
requester.ObjSyncResult = KernelResult.InvalidMemState;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (currentProcess.CpuMemory.TestExclusive(0, (long)address))
|
||||||
{
|
{
|
||||||
if (mutexValue != 0)
|
if (mutexValue != 0)
|
||||||
{
|
{
|
||||||
//Update value to indicate there is a mutex waiter now.
|
//Update value to indicate there is a mutex waiter now.
|
||||||
currentProcess.CpuMemory.WriteInt32(address, mutexValue | HasListenersMask);
|
currentProcess.CpuMemory.WriteInt32((long)address, mutexValue | HasListenersMask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//No thread owning the mutex, assign to requesting thread.
|
//No thread owning the mutex, assign to requesting thread.
|
||||||
currentProcess.CpuMemory.WriteInt32(address, requester.ThreadHandleForUserMutex);
|
currentProcess.CpuMemory.WriteInt32((long)address, requester.ThreadHandleForUserMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
@ -261,16 +261,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
mutexValue = currentProcess.CpuMemory.ReadInt32(address);
|
mutexValue = currentProcess.CpuMemory.ReadInt32((long)address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mutexValue == 0)
|
if (mutexValue == 0)
|
||||||
{
|
{
|
||||||
//We now own the mutex.
|
//We now own the mutex.
|
||||||
requester.SignaledObj = null;
|
requester.SignaledObj = null;
|
||||||
requester.ObjSyncResult = 0;
|
requester.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
requester.ReleaseAndResume();
|
requester.ReleaseAndResume();
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
//Invalid mutex owner.
|
//Invalid mutex owner.
|
||||||
requester.SignaledObj = null;
|
requester.SignaledObj = null;
|
||||||
requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
requester.ObjSyncResult = KernelResult.InvalidHandle;
|
||||||
|
|
||||||
requester.ReleaseAndResume();
|
requester.ReleaseAndResume();
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return mutexOwner;
|
return mutexOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitForAddressIfEqual(long address, int value, long timeout)
|
public KernelResult WaitForAddressIfEqual(ulong address, int value, long timeout)
|
||||||
{
|
{
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
@ -309,17 +309,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return KernelResult.ThreadTerminating;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
currentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
currentThread.ObjSyncResult = KernelResult.TimedOut;
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return KernelResult.InvalidMemState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentValue == value)
|
if (currentValue == value)
|
||||||
|
@ -328,7 +328,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
return KernelResult.TimedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.MutexAddress = address;
|
currentThread.MutexAddress = address;
|
||||||
|
@ -361,15 +361,19 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return currentThread.ObjSyncResult;
|
return (KernelResult)currentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitForAddressIfLessThan(long address, int value, bool shouldDecrement, long timeout)
|
public KernelResult WaitForAddressIfLessThan(
|
||||||
|
ulong address,
|
||||||
|
int value,
|
||||||
|
bool shouldDecrement,
|
||||||
|
long timeout)
|
||||||
{
|
{
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
@ -380,40 +384,40 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return KernelResult.ThreadTerminating;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
currentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
currentThread.ObjSyncResult = KernelResult.TimedOut;
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return KernelResult.InvalidMemState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldDecrement)
|
if (shouldDecrement)
|
||||||
{
|
{
|
||||||
while (currentValue < value)
|
while (currentValue < value)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (currentProcess.CpuMemory.TestExclusive(0, (long)address))
|
||||||
{
|
{
|
||||||
currentProcess.CpuMemory.WriteInt32(address, currentValue - 1);
|
currentProcess.CpuMemory.WriteInt32((long)address, currentValue - 1);
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
currentValue = currentProcess.CpuMemory.ReadInt32(address);
|
currentValue = currentProcess.CpuMemory.ReadInt32((long)address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +429,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
return KernelResult.TimedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.MutexAddress = address;
|
currentThread.MutexAddress = address;
|
||||||
|
@ -458,12 +462,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return currentThread.ObjSyncResult;
|
return (KernelResult)currentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InsertSortedByPriority(List<KThread> threads, KThread thread)
|
private void InsertSortedByPriority(List<KThread> threads, KThread thread)
|
||||||
|
@ -490,7 +494,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Signal(long address, int count)
|
public KernelResult Signal(ulong address, int count)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
|
@ -498,38 +502,38 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long SignalAndIncrementIfEqual(long address, int value, int count)
|
public KernelResult SignalAndIncrementIfEqual(ulong address, int value, int count)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return KernelResult.InvalidMemState;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (currentValue == value)
|
while (currentValue == value)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (currentProcess.CpuMemory.TestExclusive(0, (long)address))
|
||||||
{
|
{
|
||||||
currentProcess.CpuMemory.WriteInt32(address, currentValue + 1);
|
currentProcess.CpuMemory.WriteInt32((long)address, currentValue + 1);
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
currentValue = currentProcess.CpuMemory.ReadInt32(address);
|
currentValue = currentProcess.CpuMemory.ReadInt32((long)address);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusive(0);
|
currentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
@ -538,17 +542,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeArbiterThreads(address, count);
|
WakeArbiterThreads(address, count);
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long SignalAndModifyIfEqual(long address, int value, int count)
|
public KernelResult SignalAndModifyIfEqual(ulong address, int value, int count)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
|
@ -578,29 +582,29 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return KernelResult.InvalidMemState;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (currentValue == value)
|
while (currentValue == value)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (currentProcess.CpuMemory.TestExclusive(0, (long)address))
|
||||||
{
|
{
|
||||||
currentProcess.CpuMemory.WriteInt32(address, currentValue + offset);
|
currentProcess.CpuMemory.WriteInt32((long)address, currentValue + offset);
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
currentProcess.CpuMemory.SetExclusive(0, (long)address);
|
||||||
|
|
||||||
currentValue = currentProcess.CpuMemory.ReadInt32(address);
|
currentValue = currentProcess.CpuMemory.ReadInt32((long)address);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusive(0);
|
currentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
@ -609,17 +613,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeArbiterThreads(address, count);
|
WakeArbiterThreads(address, count);
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WakeArbiterThreads(long address, int count)
|
private void WakeArbiterThreads(ulong address, int count)
|
||||||
{
|
{
|
||||||
Queue<KThread> signaledThreads = new Queue<KThread>();
|
Queue<KThread> signaledThreads = new Queue<KThread>();
|
||||||
|
|
||||||
|
@ -637,7 +641,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
while (signaledThreads.TryDequeue(out KThread thread))
|
while (signaledThreads.TryDequeue(out KThread thread))
|
||||||
{
|
{
|
||||||
thread.SignaledObj = null;
|
thread.SignaledObj = null;
|
||||||
thread.ObjSyncResult = 0;
|
thread.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
thread.ReleaseAndResume();
|
thread.ReleaseAndResume();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
static class KConditionVariable
|
static class KConditionVariable
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KCoreContext
|
class KCoreContext
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using ChocolArm64;
|
using ChocolArm64;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KCriticalSection
|
class KCriticalSection
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KEvent
|
class KEvent
|
||||||
{
|
{
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KReadableEvent : KSynchronizationObject
|
class KReadableEvent : KSynchronizationObject
|
||||||
{
|
{
|
|
@ -1,8 +1,9 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
partial class KScheduler : IDisposable
|
partial class KScheduler : IDisposable
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KSchedulingData
|
class KSchedulingData
|
||||||
{
|
{
|
|
@ -1,8 +1,7 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
{
|
||||||
class KSynchronization
|
class KSynchronization
|
||||||
{
|
{
|
||||||
|
@ -13,9 +12,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
_system = system;
|
_system = system;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitFor(KSynchronizationObject[] syncObjs, long timeout, ref int hndIndex)
|
public KernelResult WaitFor(KSynchronizationObject[] syncObjs, long timeout, out int handleIndex)
|
||||||
{
|
{
|
||||||
long result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
handleIndex = 0;
|
||||||
|
|
||||||
|
KernelResult result = KernelResult.TimedOut;
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
hndIndex = index;
|
handleIndex = index;
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
|
@ -46,13 +47,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
if (currentThread.ShallBeTerminated ||
|
if (currentThread.ShallBeTerminated ||
|
||||||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
result = KernelResult.ThreadTerminating;
|
||||||
}
|
}
|
||||||
else if (currentThread.SyncCancelled)
|
else if (currentThread.SyncCancelled)
|
||||||
{
|
{
|
||||||
currentThread.SyncCancelled = false;
|
currentThread.SyncCancelled = false;
|
||||||
|
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.Cancelled);
|
result = KernelResult.Cancelled;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -65,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
currentThread.WaitingSync = true;
|
currentThread.WaitingSync = true;
|
||||||
currentThread.SignaledObj = null;
|
currentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = (int)result;
|
currentThread.ObjSyncResult = result;
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
|
@ -85,9 +86,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
result = (uint)currentThread.ObjSyncResult;
|
result = currentThread.ObjSyncResult;
|
||||||
|
|
||||||
hndIndex = -1;
|
handleIndex = -1;
|
||||||
|
|
||||||
for (int index = 0; index < syncObjs.Length; index++)
|
for (int index = 0; index < syncObjs.Length; index++)
|
||||||
{
|
{
|
||||||
|
@ -95,7 +96,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (syncObjs[index] == currentThread.SignaledObj)
|
if (syncObjs[index] == currentThread.SignaledObj)
|
||||||
{
|
{
|
||||||
hndIndex = index;
|
handleIndex = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
||||||
{
|
{
|
||||||
thread.SignaledObj = syncObj;
|
thread.SignaledObj = syncObj;
|
||||||
thread.ObjSyncResult = 0;
|
thread.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
thread.Reschedule(ThreadSchedState.Running);
|
thread.Reschedule(ThreadSchedState.Running);
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
using ChocolArm64;
|
using ChocolArm64;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
|
||||||
{
|
{
|
||||||
class KThread : KSynchronizationObject, IKFutureSchedulerObject
|
class KThread : KSynchronizationObject, IKFutureSchedulerObject
|
||||||
{
|
{
|
||||||
|
@ -20,11 +20,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public KSynchronizationObject SignaledObj { get; set; }
|
public KSynchronizationObject SignaledObj { get; set; }
|
||||||
|
|
||||||
public long CondVarAddress { get; set; }
|
public ulong CondVarAddress { get; set; }
|
||||||
|
|
||||||
private ulong _entrypoint;
|
private ulong _entrypoint;
|
||||||
|
|
||||||
public long MutexAddress { get; set; }
|
public ulong MutexAddress { get; set; }
|
||||||
|
|
||||||
public KProcess Owner { get; private set; }
|
public KProcess Owner { get; private set; }
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private ThreadSchedState _forcePauseFlags;
|
private ThreadSchedState _forcePauseFlags;
|
||||||
|
|
||||||
public int ObjSyncResult { get; set; }
|
public KernelResult ObjSyncResult { get; set; }
|
||||||
|
|
||||||
public int DynamicPriority { get; set; }
|
public int DynamicPriority { get; set; }
|
||||||
public int CurrentCore { get; set; }
|
public int CurrentCore { get; set; }
|
||||||
|
@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
DynamicPriority = priority;
|
DynamicPriority = priority;
|
||||||
BasePriority = priority;
|
BasePriority = priority;
|
||||||
|
|
||||||
ObjSyncResult = 0x7201;
|
ObjSyncResult = KernelResult.ThreadNotStarted;
|
||||||
|
|
||||||
_entrypoint = entrypoint;
|
_entrypoint = entrypoint;
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Sleep(long timeout)
|
public KernelResult Sleep(long timeout)
|
||||||
{
|
{
|
||||||
System.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return KernelResult.ThreadTerminating;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.Paused);
|
SetNewSchedFlags(ThreadSchedState.Paused);
|
||||||
|
@ -468,9 +468,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long SetActivity(bool pause)
|
public KernelResult SetActivity(bool pause)
|
||||||
{
|
{
|
||||||
long result = 0;
|
KernelResult result = KernelResult.Success;
|
||||||
|
|
||||||
System.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
@ -480,7 +480,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
@ -498,7 +498,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
result = KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -521,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
result = KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -553,7 +553,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SignaledObj = null;
|
SignaledObj = null;
|
||||||
ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Cancelled);
|
ObjSyncResult = KernelResult.Cancelled;
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.Running);
|
SetNewSchedFlags(ThreadSchedState.Running);
|
||||||
|
|
||||||
|
@ -716,7 +716,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
UpdatePriorityInheritance();
|
UpdatePriorityInheritance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KThread RelinquishMutex(long mutexAddress, out int count)
|
public KThread RelinquishMutex(ulong mutexAddress, out int count)
|
||||||
{
|
{
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
|
@ -1017,7 +1017,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
thread.MutexOwner = null;
|
thread.MutexOwner = null;
|
||||||
thread._preferredCoreOverride = 0;
|
thread._preferredCoreOverride = 0;
|
||||||
thread.ObjSyncResult = 0xfa01;
|
thread.ObjSyncResult = KernelResult.InvalidState;
|
||||||
|
|
||||||
thread.ReleaseAndResume();
|
thread.ReleaseAndResume();
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KWritableEvent
|
class KWritableEvent
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
enum SignalType
|
enum SignalType
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
enum ThreadSchedState : ushort
|
enum ThreadSchedState : ushort
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
enum ThreadType
|
enum ThreadType
|
||||||
{
|
{
|
|
@ -1,7 +1,9 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
namespace Ryujinx.HLE.HOS
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Audio;
|
using Ryujinx.Audio;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ using Ryujinx.Audio;
|
||||||
using Ryujinx.Audio.Adpcm;
|
using Ryujinx.Audio.Adpcm;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
@ -2,7 +2,7 @@ using ChocolArm64.Memory;
|
||||||
using Ryujinx.Audio;
|
using Ryujinx.Audio;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Aud.AudioOut;
|
using Ryujinx.HLE.HOS.Services.Aud.AudioOut;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.Input;
|
using Ryujinx.HLE.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.Input;
|
using Ryujinx.HLE.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
|
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu;
|
using Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvHostChannel;
|
using Ryujinx.HLE.HOS.Services.Nv.NvHostChannel;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
|
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
|
||||||
using System;
|
using System;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.HLE.HOS.Font;
|
using Ryujinx.HLE.HOS.Font;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Psm
|
namespace Ryujinx.HLE.HOS.Services.Psm
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue