Compare commits

...

14 Commits

Author SHA1 Message Date
b1bd6a50b5 Less invasive fix for EventFd blocking operations (#3394) 2022-06-12 09:29:12 +02:00
70895bdb04 Allow concurrent BSD EventFd read/write (#3385) 2022-06-11 14:58:30 -03:00
830cbf91bb Ignore ClipControl on draw texture fallback (#3388) 2022-06-11 14:31:17 -03:00
9a9349f0f4 Fix instanced indexed inline draw index count (#3389) 2022-06-10 23:44:49 -03:00
46cc7b55f0 Fix instanced indexed inline draws (#3383) 2022-06-05 21:24:28 -03:00
dd8f97ab9e Remove freed memory range from tree on memory block disposal (#3347)
* Remove freed memory range from tree on memory block disposal

* PR feedback
2022-06-05 15:12:42 -03:00
633c5ec330 Extend uses count from ushort to uint on Operand Data structure (#3374) 2022-06-05 14:15:27 -03:00
a3e7bb8eb4 Copy dependency for multisample and non-multisample textures (#3382)
* Use copy dependency for textures that differs in multisample but are otherwise compatible

* Remove allowMs flag as it's no longer required for correctness, it's just an optimization now

* Dispose intermmediate pool
2022-06-05 14:06:47 -03:00
2073ba2919 Fix a potential GPFIFO submission race (#3378)
The syncpoint maximum value represents the maximum possible syncpt value at a given time, however due to PBs being submitted before max was incremented, for a brief moment of time this is not the case which could lead to invalid behaviour if a game waits on the fence at that specific time.
2022-06-04 21:36:36 +02:00
d03124a992 Fix 3D semaphore counter type 0 handling (#3380)
Counter type 0 actually releases the semaphore payload rather than a constant zero as was previously thought. This is required by Skyrim.
2022-06-02 19:51:36 -03:00
59490d54b5 infra: Switch to win10-x64 RID and fix PR comment for Avalonia and SDL2 artifact rename (#3375)
* infra: Switch to win10-x64 RID and fix PR comment for Avalonia and SDL2 artifact rename

* Address gdkchan's comments
2022-06-01 02:01:16 +02:00
e546e5933f Rewrite SVC handler using source generators rather than IL emit (#3371)
* Implement syscall handlers using a source generator

* Copy FlushProcessDataCache implementation to Syscall since it was only implemented on Syscall32

* Fix wrong argument order in some syscalls

* Delete old Reflection.Emit based syscall handling code

* Improvements to the code generation

* ControlCodeMemory address and size is always 64-bit
2022-05-31 17:12:46 -03:00
0c87bf9ea4 Refactor CPU interface to allow the implementation of other CPU emulators (#3362)
* Refactor CPU interface

* Use IExecutionContext interface on SVC handler, change how CPU interrupts invokes the handlers

* Make CpuEngine take a ITickSource rather than returning one

The previous implementation had the scenario where the CPU engine had to implement the tick source in mind, like for example, when we have a hypervisor and the game can read CNTPCT on the host directly. However given that we need to do conversion due to different frequencies anyway, it's not worth it. It's better to just let the user pass the tick source and redirect any reads to CNTPCT to the user tick source

* XML docs for the public interfaces

* PPTC invalidation due to NativeInterface function name changes

* Fix build of the CPU tests

* PR feedback
2022-05-31 16:29:35 -03:00
9827dc35e1 Allow loading NSPs without a NCA inside (#3364)
* Allow loading NSPs without a NCA inside

* Set isHomebrew as true
2022-05-31 16:16:59 -03:00
106 changed files with 2147 additions and 1946 deletions

View File

@ -39,7 +39,7 @@ jobs:
- os: windows-latest
OS_NAME: Windows x64
DOTNET_RUNTIME_IDENTIFIER: win-x64
DOTNET_RUNTIME_IDENTIFIER: win10-x64
RELEASE_ZIP_OS_NAME: win_x64
fail-fast: false

View File

@ -36,19 +36,24 @@ jobs:
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
let hidden_avalonia_artifacts = `\n\n <details><summary>Experimental GUI (Avalonia)</summary>\n`;
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
for (const art of artifacts) {
if(art.name.includes('Debug')) {
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else if(art.name.includes('headless-sdl2')) {
} else if(art.name.includes('ava-ryujinx')) {
hidden_avalonia_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else if(art.name.includes('sdl2-ryujinx-headless')) {
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else {
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
}
hidden_avalonia_artifacts += `\n</details>`;
hidden_headless_artifacts += `\n</details>`;
hidden_debug_artifacts += `\n</details>`;
body += hidden_avalonia_artifacts;
body += hidden_headless_artifacts;
body += hidden_debug_artifacts;

View File

@ -51,9 +51,9 @@ jobs:
run: "mkdir release_output"
- name: Publish Windows
run: |
dotnet publish -c Release -r win-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
dotnet publish -c Release -r win-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
dotnet publish -c Release -r win-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
- name: Packing Windows builds
run: |
pushd publish_windows

View File

@ -33,13 +33,13 @@ namespace ARMeilleure.Instructions
switch (GetPackedId(op))
{
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)); break;
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;

View File

@ -107,14 +107,14 @@ namespace ARMeilleure.Instructions
return (uint)GetContext().TpidrEl0;
}
public static ulong GetTpidr()
public static ulong GetTpidrroEl0()
{
return (ulong)GetContext().Tpidr;
return (ulong)GetContext().TpidrroEl0;
}
public static uint GetTpidr32()
{
return (uint)GetContext().Tpidr;
return (uint)GetContext().TpidrroEl0;
}
public static ulong GetCntfrqEl0()

View File

@ -14,10 +14,11 @@ namespace ARMeilleure.IntermediateRepresentation
public byte Kind;
public byte Type;
public byte SymbolType;
public byte Padding; // Unused space.
public ushort AssignmentsCount;
public ushort AssignmentsCapacity;
public ushort UsesCount;
public ushort UsesCapacity;
public uint UsesCount;
public uint UsesCapacity;
public Operation* Assignments;
public Operation* Uses;
public ulong Value;
@ -84,11 +85,11 @@ namespace ARMeilleure.IntermediateRepresentation
{
Debug.Assert(Kind != OperandKind.Memory);
return new ReadOnlySpan<Operation>(_data->Uses, _data->UsesCount);
return new ReadOnlySpan<Operation>(_data->Uses, (int)_data->UsesCount);
}
}
public int UsesCount => _data->UsesCount;
public int UsesCount => (int)_data->UsesCount;
public int AssignmentsCount => _data->AssignmentsCount;
public bool Relocatable => Symbol.Type != SymbolType.None;
@ -178,7 +179,7 @@ namespace ARMeilleure.IntermediateRepresentation
{
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
}
if (index != default)
{
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
@ -265,6 +266,13 @@ namespace ARMeilleure.IntermediateRepresentation
data = Allocators.References.Allocate<T>(initialCapacity);
}
private static void New<T>(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged
{
count = 0;
capacity = initialCapacity;
data = Allocators.References.Allocate<T>(initialCapacity);
}
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
{
if (count < capacity)
@ -294,6 +302,40 @@ namespace ARMeilleure.IntermediateRepresentation
}
}
private static void Add<T>(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged
{
if (count < capacity)
{
data[count++] = item;
return;
}
// Could not add item in the fast path, fallback onto the slow path.
ExpandAdd(item, ref data, ref count, ref capacity);
static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity)
{
uint newCount = checked(count + 1);
uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue);
if (newCapacity <= capacity)
{
throw new OverflowException();
}
var oldSpan = new Span<T>(data, (int)count);
capacity = newCapacity;
data = Allocators.References.Allocate<T>(capacity);
oldSpan.CopyTo(new Span<T>(data, (int)count));
data[count] = item;
count = newCount;
}
}
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
{
var span = new Span<T>(data, count);
@ -314,6 +356,26 @@ namespace ARMeilleure.IntermediateRepresentation
}
}
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
{
var span = new Span<T>(data, (int)count);
for (int i = 0; i < span.Length; i++)
{
if (EqualityComparer<T>.Default.Equals(span[i], item))
{
if (i + 1 < count)
{
span.Slice(i + 1).CopyTo(span.Slice(i));
}
count--;
return;
}
}
}
public override int GetHashCode()
{
if (Kind == OperandKind.LocalVariable)

View File

@ -0,0 +1,5 @@
namespace ARMeilleure.State
{
public delegate void ExceptionCallbackNoArgs(ExecutionContext context);
public delegate void ExceptionCallback(ExecutionContext context, ulong address, int id);
}

View File

@ -1,6 +1,5 @@
using ARMeilleure.Memory;
using System;
using System.Diagnostics;
namespace ARMeilleure.State
{
@ -14,34 +13,22 @@ namespace ARMeilleure.State
private bool _interrupted;
private static Stopwatch _tickCounter;
private readonly ICounter _counter;
private static double _hostTickFreq;
public ulong Pc => _nativeContext.GetPc();
public uint CtrEl0 => 0x8444c004;
public uint CtrEl0 => 0x8444c004;
public uint DczidEl0 => 0x00000004;
public ulong CntfrqEl0 { get; set; }
public ulong CntpctEl0
{
get
{
double ticks = _tickCounter.ElapsedTicks * _hostTickFreq;
return (ulong)(ticks * CntfrqEl0);
}
}
public ulong CntfrqEl0 => _counter.Frequency;
public ulong CntpctEl0 => _counter.Counter;
// CNTVCT_EL0 = CNTPCT_EL0 - CNTVOFF_EL2
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
public ulong CntvctEl0 => CntpctEl0;
public static TimeSpan ElapsedTime => _tickCounter.Elapsed;
public static long ElapsedTicks => _tickCounter.ElapsedTicks;
public static double TickFrequency => _hostTickFreq;
public long TpidrEl0 { get; set; }
public long Tpidr { get; set; }
public long TpidrroEl0 { get; set; }
public uint Pstate
{
@ -78,35 +65,38 @@ namespace ARMeilleure.State
private set => _nativeContext.SetRunning(value);
}
public event EventHandler<EventArgs> Interrupt;
public event EventHandler<InstExceptionEventArgs> Break;
public event EventHandler<InstExceptionEventArgs> SupervisorCall;
public event EventHandler<InstUndefinedEventArgs> Undefined;
private readonly ExceptionCallbackNoArgs _interruptCallback;
private readonly ExceptionCallback _breakCallback;
private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback;
static ExecutionContext()
{
_hostTickFreq = 1.0 / Stopwatch.Frequency;
_tickCounter = new Stopwatch();
_tickCounter.Start();
}
public ExecutionContext(IJitMemoryAllocator allocator)
public ExecutionContext(
IJitMemoryAllocator allocator,
ICounter counter,
ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null,
ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null)
{
_nativeContext = new NativeContext(allocator);
_counter = counter;
_interruptCallback = interruptCallback;
_breakCallback = breakCallback;
_supervisorCallback = supervisorCallback;
_undefinedCallback = undefinedCallback;
Running = true;
_nativeContext.SetCounter(MinCountForCheck);
}
public ulong GetX(int index) => _nativeContext.GetX(index);
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
public ulong GetX(int index) => _nativeContext.GetX(index);
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
public V128 GetV(int index) => _nativeContext.GetV(index);
public V128 GetV(int index) => _nativeContext.GetV(index);
public void SetV(int index, V128 value) => _nativeContext.SetV(index, value);
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value);
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
@ -118,7 +108,7 @@ namespace ARMeilleure.State
{
_interrupted = false;
Interrupt?.Invoke(this, EventArgs.Empty);
_interruptCallback?.Invoke(this);
}
_nativeContext.SetCounter(MinCountForCheck);
@ -131,17 +121,17 @@ namespace ARMeilleure.State
internal void OnBreak(ulong address, int imm)
{
Break?.Invoke(this, new InstExceptionEventArgs(address, imm));
_breakCallback?.Invoke(this, address, imm);
}
internal void OnSupervisorCall(ulong address, int imm)
{
SupervisorCall?.Invoke(this, new InstExceptionEventArgs(address, imm));
_supervisorCallback?.Invoke(this, address, imm);
}
internal void OnUndefined(ulong address, int opCode)
{
Undefined?.Invoke(this, new InstUndefinedEventArgs(address, opCode));
_undefinedCallback?.Invoke(this, address, opCode);
}
public void StopRunning()
@ -151,16 +141,6 @@ namespace ARMeilleure.State
_nativeContext.SetCounter(0);
}
public static void SuspendCounter()
{
_tickCounter.Stop();
}
public static void ResumeCounter()
{
_tickCounter.Start();
}
public void Dispose()
{
_nativeContext.Dispose();

View File

@ -0,0 +1,18 @@
namespace ARMeilleure.State
{
/// <summary>
/// CPU Counter interface.
/// </summary>
public interface ICounter
{
/// <summary>
/// Counter frequency in Hertz.
/// </summary>
ulong Frequency { get; }
/// <summary>
/// Current counter value.
/// </summary>
ulong Counter { get; }
}
}

View File

@ -1,16 +0,0 @@
using System;
namespace ARMeilleure.State
{
public class InstExceptionEventArgs : EventArgs
{
public ulong Address { get; }
public int Id { get; }
public InstExceptionEventArgs(ulong address, int id)
{
Address = address;
Id = id;
}
}
}

View File

@ -1,16 +0,0 @@
using System;
namespace ARMeilleure.State
{
public class InstUndefinedEventArgs : EventArgs
{
public ulong Address { get; }
public int OpCode { get; }
public InstUndefinedEventArgs(ulong address, int opCode)
{
Address = address;
OpCode = opCode;
}
}
}

View File

@ -34,6 +34,12 @@ namespace ARMeilleure.State
GetStorage().ExclusiveAddress = ulong.MaxValue;
}
public ulong GetPc()
{
// TODO: More precise tracking of PC value.
return GetStorage().DispatchAddress;
}
public unsafe ulong GetX(int index)
{
if ((uint)index >= RegisterConsts.IntRegsCount)

View File

@ -115,7 +115,7 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.

View File

@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 3267; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 3362; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
@ -15,11 +15,11 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dylib</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win10-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.so</TargetPath>
</ContentWithTargetPath>

View File

@ -523,9 +523,7 @@ namespace Ryujinx.Audio.Renderer.Server
private ulong GetSystemTicks()
{
double ticks = ARMeilleure.State.ExecutionContext.ElapsedTicks * ARMeilleure.State.ExecutionContext.TickFrequency;
return (ulong)(ticks * Constants.TargetTimerFrequency);
return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
}
private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp)

View File

@ -19,6 +19,7 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.Memory;
using System;
using System.Diagnostics;
@ -77,6 +78,11 @@ namespace Ryujinx.Audio.Renderer.Server
/// </summary>
private IHardwareDeviceDriver _deviceDriver;
/// <summary>
/// Tick source used to measure elapsed time.
/// </summary>
public ITickSource TickSource { get; }
/// <summary>
/// The <see cref="AudioProcessor"/> instance associated to this manager.
/// </summary>
@ -90,9 +96,11 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary>
/// Create a new <see cref="AudioRendererManager"/>.
/// </summary>
public AudioRendererManager()
/// <param name="tickSource">Tick source used to measure elapsed time.</param>
public AudioRendererManager(ITickSource tickSource)
{
Processor = new AudioProcessor();
TickSource = tickSource;
_sessionIds = new int[Constants.AudioRendererSessionCountMax];
_sessions = new AudioRenderSystem[Constants.AudioRendererSessionCountMax];
_activeSessionCount = 0;

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.0-dirty</Version>

View File

@ -0,0 +1,64 @@
namespace Ryujinx.Cpu
{
/// <summary>
/// Exception callback without any additional arguments.
/// </summary>
/// <param name="context">Context for the thread where the exception was triggered</param>
public delegate void ExceptionCallbackNoArgs(IExecutionContext context);
/// <summary>
/// Exception callback.
/// </summary>
/// <param name="context">Context for the thread where the exception was triggered</param>
/// <param name="address">Address of the instruction that caused the exception</param>
/// <param name="imm">Immediate value of the instruction that caused the exception, or for undefined instruction, the instruction itself</param>
public delegate void ExceptionCallback(IExecutionContext context, ulong address, int imm);
/// <summary>
/// Stores handlers for the various CPU exceptions.
/// </summary>
public struct ExceptionCallbacks
{
/// <summary>
/// Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/>.
/// </summary>
public readonly ExceptionCallbackNoArgs InterruptCallback;
/// <summary>
/// Handler for CPU software interrupts caused by the Arm BRK instruction.
/// </summary>
public readonly ExceptionCallback BreakCallback;
/// <summary>
/// Handler for CPU software interrupts caused by the Arm SVC instruction.
/// </summary>
public readonly ExceptionCallback SupervisorCallback;
/// <summary>
/// Handler for CPU software interrupts caused by any undefined Arm instruction.
/// </summary>
public readonly ExceptionCallback UndefinedCallback;
/// <summary>
/// Creates a new exception callbacks structure.
/// </summary>
/// <remarks>
/// All handlers are optional, and if null, the CPU will just continue executing as if nothing happened.
/// </remarks>
/// <param name="interruptCallback">Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/></param>
/// <param name="breakCallback">Handler for CPU software interrupts caused by the Arm BRK instruction</param>
/// <param name="supervisorCallback">Handler for CPU software interrupts caused by the Arm SVC instruction</param>
/// <param name="undefinedCallback">Handler for CPU software interrupts caused by any undefined Arm instruction</param>
public ExceptionCallbacks(
ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null,
ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null)
{
InterruptCallback = interruptCallback;
BreakCallback = breakCallback;
SupervisorCallback = supervisorCallback;
UndefinedCallback = undefinedCallback;
}
}
}

View File

@ -0,0 +1,39 @@
namespace Ryujinx.Cpu
{
/// <summary>
/// CPU context interface.
/// </summary>
public interface ICpuContext
{
/// <summary>
/// Creates a new execution context that will store thread CPU register state when executing guest code.
/// </summary>
/// <param name="exceptionCallbacks">Optional functions to be called when the CPU receives an interrupt</param>
/// <returns>Execution context</returns>
IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks);
/// <summary>
/// Starts executing code at a specified entry point address.
/// </summary>
/// <remarks>
/// This function only returns when the execution is stopped, by calling <see cref="IExecutionContext.StopRunning"/>.
/// </remarks>
/// <param name="context">Execution context to be used for this run</param>
/// <param name="address">Entry point address</param>
void Execute(IExecutionContext context, ulong address);
/// <summary>
/// Invalidates the instruction cache for a given memory region.
/// </summary>
/// <remarks>
/// This should be called if code is modified to make the CPU emulator aware of the modifications,
/// otherwise it might run stale code which will lead to errors and crashes.
/// Calling this function is not necessary if the code memory was modified by guest code,
/// as the expectation is that it will do it on its own using the appropriate cache invalidation instructions,
/// except on Arm32 where those instructions can't be used in unprivileged mode.
/// </remarks>
/// <param name="address">Address of the region to be invalidated</param>
/// <param name="size">Size of the region to be invalidated</param>
void InvalidateCacheRegion(ulong address, ulong size);
}
}

18
Ryujinx.Cpu/ICpuEngine.cs Normal file
View File

@ -0,0 +1,18 @@
using ARMeilleure.Memory;
namespace Ryujinx.Cpu
{
/// <summary>
/// CPU execution engine interface.
/// </summary>
public interface ICpuEngine
{
/// <summary>
/// Creates a new CPU context that can be used to run code for multiple threads sharing an address space.
/// </summary>
/// <param name="memoryManager">Memory manager for the address space of the context</param>
/// <param name="for64Bit">Indicates if the context will be used to run 64-bit or 32-bit Arm code</param>
/// <returns>CPU context</returns>
ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit);
}
}

View File

@ -0,0 +1,112 @@
using ARMeilleure.State;
using System;
namespace Ryujinx.Cpu
{
/// <summary>
/// CPU register state interface.
/// </summary>
public interface IExecutionContext : IDisposable
{
/// <summary>
/// Current Program Counter.
/// </summary>
/// <remarks>
/// In some implementations, this value might not be accurate and might not point to the last instruction executed.
/// </remarks>
ulong Pc { get; }
/// <summary>
/// Thread ID Register (EL0).
/// </summary>
long TpidrEl0 { get; set; }
/// <summary>
/// Thread ID Register (read-only) (EL0).
/// </summary>
long TpidrroEl0 { get; set; }
/// <summary>
/// Processor State register.
/// </summary>
uint Pstate { get; set; }
/// <summary>
/// Floating-point Control Register.
/// </summary>
uint Fpcr { get; set; }
/// <summary>
/// Floating-point Status Register.
/// </summary>
uint Fpsr { get; set; }
/// <summary>
/// Indicates whenever the CPU is running 64-bit (AArch64 mode) or 32-bit (AArch32 mode) code.
/// </summary>
bool IsAarch32 { get; set; }
/// <summary>
/// Indicates whenever the CPU is still running code.
/// </summary>
/// <remarks>
/// Even if this is false, the guest code might be still exiting.
/// One must not assume that the code is no longer running from this property alone.
/// </remarks>
bool Running { get; }
/// <summary>
/// Gets the value of a general purpose register.
/// </summary>
/// <remarks>
/// The special <paramref name="index"/> of 31 can be used to access the SP (Stack Pointer) register.
/// </remarks>
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
/// <returns>The register value</returns>
ulong GetX(int index);
/// <summary>
/// Sets the value of a general purpose register.
/// </summary>
/// <remarks>
/// The special <paramref name="index"/> of 31 can be used to access the SP (Stack Pointer) register.
/// </remarks>
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
/// <param name="value">Value to be set</param>
void SetX(int index, ulong value);
/// <summary>
/// Gets the value of a FP/SIMD register.
/// </summary>
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
/// <returns>The register value</returns>
V128 GetV(int index);
/// <summary>
/// Sets the value of a FP/SIMD register.
/// </summary>
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
/// <param name="value">Value to be set</param>
void SetV(int index, V128 value);
/// <summary>
/// Requests the thread to stop running temporarily and call <see cref="ExceptionCallbacks.InterruptCallback"/>.
/// </summary>
/// <remarks>
/// The thread might not pause immediately.
/// One must not assume that guest code is no longer being executed by the thread after calling this function.
/// </remarks>
void RequestInterrupt();
/// <summary>
/// Requests the thread to stop running guest code and return as soon as possible.
/// </summary>
/// <remarks>
/// The thread might not stop immediately.
/// One must not assume that guest code is no longer being executed by the thread after calling this function.
/// After a thread has been stopped, it can't be restarted with the same <see cref="IExecutionContext"/>.
/// If you only need to pause the thread temporarily, use <see cref="RequestInterrupt"/> instead.
/// </remarks>
void StopRunning();
}
}

View File

@ -0,0 +1,31 @@
using ARMeilleure.State;
using System;
namespace Ryujinx.Cpu
{
/// <summary>
/// Tick source interface.
/// </summary>
public interface ITickSource : ICounter
{
/// <summary>
/// Time elapsed since the counter was created.
/// </summary>
TimeSpan ElapsedTime { get; }
/// <summary>
/// Time elapsed since the counter was created, in seconds.
/// </summary>
double ElapsedSeconds { get; }
/// <summary>
/// Stops counting.
/// </summary>
void Suspend();
/// <summary>
/// Resumes counting after a call to <see cref="Suspend"/>.
/// </summary>
void Resume();
}
}

View File

@ -0,0 +1,41 @@
using ARMeilleure.Memory;
using ARMeilleure.Translation;
namespace Ryujinx.Cpu.Jit
{
class JitCpuContext : ICpuContext
{
private readonly ITickSource _tickSource;
private readonly Translator _translator;
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
{
_tickSource = tickSource;
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
memory.UnmapEvent += UnmapHandler;
}
private void UnmapHandler(ulong address, ulong size)
{
_translator.InvalidateJitCacheRegion(address, size);
}
/// <inheritdoc/>
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
{
return new JitExecutionContext(new JitMemoryAllocator(), _tickSource, exceptionCallbacks);
}
/// <inheritdoc/>
public void Execute(IExecutionContext context, ulong address)
{
_translator.Execute(((JitExecutionContext)context).Impl, address);
}
/// <inheritdoc/>
public void InvalidateCacheRegion(ulong address, ulong size)
{
_translator.InvalidateJitCacheRegion(address, size);
}
}
}

View File

@ -0,0 +1,20 @@
using ARMeilleure.Memory;
namespace Ryujinx.Cpu.Jit
{
public class JitEngine : ICpuEngine
{
private readonly ITickSource _tickSource;
public JitEngine(ITickSource tickSource)
{
_tickSource = tickSource;
}
/// <inheritdoc/>
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
{
return new JitCpuContext(_tickSource, memoryManager, for64Bit);
}
}
}

View File

@ -0,0 +1,123 @@
using ARMeilleure.Memory;
using ARMeilleure.State;
namespace Ryujinx.Cpu.Jit
{
class JitExecutionContext : IExecutionContext
{
private readonly ExecutionContext _impl;
internal ExecutionContext Impl => _impl;
/// <inheritdoc/>
public ulong Pc => _impl.Pc;
/// <inheritdoc/>
public long TpidrEl0
{
get => _impl.TpidrEl0;
set => _impl.TpidrEl0 = value;
}
/// <inheritdoc/>
public long TpidrroEl0
{
get => _impl.TpidrroEl0;
set => _impl.TpidrroEl0 = value;
}
/// <inheritdoc/>
public uint Pstate
{
get => _impl.Pstate;
set => _impl.Pstate = value;
}
/// <inheritdoc/>
public uint Fpcr
{
get => (uint)_impl.Fpcr;
set => _impl.Fpcr = (FPCR)value;
}
/// <inheritdoc/>
public uint Fpsr
{
get => (uint)_impl.Fpsr;
set => _impl.Fpsr = (FPSR)value;
}
/// <inheritdoc/>
public bool IsAarch32
{
get => _impl.IsAarch32;
set => _impl.IsAarch32 = value;
}
/// <inheritdoc/>
public bool Running => _impl.Running;
private readonly ExceptionCallbacks _exceptionCallbacks;
public JitExecutionContext(IJitMemoryAllocator allocator, ICounter counter, ExceptionCallbacks exceptionCallbacks)
{
_impl = new ExecutionContext(
allocator,
counter,
InterruptHandler,
BreakHandler,
SupervisorCallHandler,
UndefinedHandler);
_exceptionCallbacks = exceptionCallbacks;
}
/// <inheritdoc/>
public ulong GetX(int index) => _impl.GetX(index);
/// <inheritdoc/>
public void SetX(int index, ulong value) => _impl.SetX(index, value);
/// <inheritdoc/>
public V128 GetV(int index) => _impl.GetV(index);
/// <inheritdoc/>
public void SetV(int index, V128 value) => _impl.SetV(index, value);
private void InterruptHandler(ExecutionContext context)
{
_exceptionCallbacks.InterruptCallback?.Invoke(this);
}
private void BreakHandler(ExecutionContext context, ulong address, int imm)
{
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
}
private void SupervisorCallHandler(ExecutionContext context, ulong address, int imm)
{
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
}
private void UndefinedHandler(ExecutionContext context, ulong address, int opCode)
{
_exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
}
/// <inheritdoc/>
public void RequestInterrupt()
{
_impl.RequestInterrupt();
}
/// <inheritdoc/>
public void StopRunning()
{
_impl.StopRunning();
}
public void Dispose()
{
_impl.Dispose();
}
}
}

View File

@ -1,9 +1,9 @@
using ARMeilleure.Memory;
using Ryujinx.Memory;
namespace Ryujinx.Cpu
namespace Ryujinx.Cpu.Jit
{
class JitMemoryAllocator : IJitMemoryAllocator
public class JitMemoryAllocator : IJitMemoryAllocator
{
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve);

View File

@ -2,9 +2,9 @@
using Ryujinx.Memory;
using System;
namespace Ryujinx.Cpu
namespace Ryujinx.Cpu.Jit
{
class JitMemoryBlock : IJitMemoryBlock
public class JitMemoryBlock : IJitMemoryBlock
{
private readonly MemoryBlock _impl;

View File

@ -10,7 +10,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.Cpu
namespace Ryujinx.Cpu.Jit
{
/// <summary>
/// Represents a CPU memory manager.

View File

@ -8,12 +8,12 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Ryujinx.Cpu
namespace Ryujinx.Cpu.Jit
{
/// <summary>
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
/// </summary>
public class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
public sealed class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
{
public const int PageBits = 12;
public const int PageSize = 1 << PageBits;

45
Ryujinx.Cpu/TickSource.cs Normal file
View File

@ -0,0 +1,45 @@
using System;
using System.Diagnostics;
namespace Ryujinx.Cpu
{
public class TickSource : ITickSource
{
private static Stopwatch _tickCounter;
private static double _hostTickFreq;
/// <inheritdoc/>
public ulong Frequency { get; }
/// <inheritdoc/>
public ulong Counter => (ulong)(ElapsedSeconds * Frequency);
/// <inheritdoc/>
public TimeSpan ElapsedTime => _tickCounter.Elapsed;
/// <inheritdoc/>
public double ElapsedSeconds => _tickCounter.ElapsedTicks * _hostTickFreq;
public TickSource(ulong frequency)
{
Frequency = frequency;
_hostTickFreq = 1.0 / Stopwatch.Frequency;
_tickCounter = new Stopwatch();
_tickCounter.Start();
}
/// <inheritdoc/>
public void Suspend()
{
_tickCounter.Stop();
}
/// <inheritdoc/>
public void Resume()
{
_tickCounter.Start();
}
}
}

View File

@ -13,4 +13,12 @@ namespace Ryujinx.Graphics.GAL
CubemapArray,
TextureBuffer
}
public static class TargetExtensions
{
public static bool IsMultisample(this Target target)
{
return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray;
}
}
}

View File

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private bool _instancedDrawPending;
private bool _instancedIndexed;
private bool _instancedIndexedInline;
private int _instancedFirstIndex;
private int _instancedFirstVertex;
@ -134,13 +135,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
_instancedDrawPending = true;
int ibCount = _drawState.IbStreamer.InlineIndexCount;
_instancedIndexed = _drawState.DrawIndexed;
_instancedIndexedInline = ibCount != 0;
_instancedFirstIndex = firstIndex;
_instancedFirstVertex = (int)_state.State.FirstVertex;
_instancedFirstInstance = (int)_state.State.FirstInstance;
_instancedIndexCount = indexCount;
_instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
var drawState = _state.State.VertexBufferDrawState;
@ -451,8 +455,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
_instancedDrawPending = false;
if (_instancedIndexed)
bool indexedInline = _instancedIndexedInline;
if (_instancedIndexed || indexedInline)
{
if (indexedInline)
{
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
}
_context.Renderer.Pipeline.DrawIndexed(
_instancedIndexCount,
_instanceIndex + 1,

View File

@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
public bool HasInlineIndexData => _inlineIndexCount != 0;
/// <summary>
/// Total numbers of indices that have been pushed.
/// </summary>
public int InlineIndexCount => _inlineIndexCount;
/// <summary>
/// Gets the handle for the host buffer currently holding the inline index buffer data.
/// </summary>

View File

@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
private enum ReportCounterType
{
Zero = 0,
Payload = 0,
InputVertices = 1,
InputPrimitives = 3,
VertexShaderInvocations = 5,
@ -169,8 +169,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
switch (type)
{
case ReportCounterType.Zero:
resultHandler(null, 0);
case ReportCounterType.Payload:
resultHandler(null, (ulong)_state.State.SemaphorePayload);
break;
case ReportCounterType.SamplesPassed:
counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false);

View File

@ -1136,32 +1136,22 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="range">Texture view physical memory ranges</param>
/// <param name="layerSize">Layer size on the given texture</param>
/// <param name="caps">Host GPU capabilities</param>
/// <param name="allowMs">Indicates that multisample textures are allowed to match non-multisample requested textures</param>
/// <param name="firstLayer">Texture view initial layer on this texture</param>
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, bool allowMs, out int firstLayer, out int firstLevel)
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
{
TextureViewCompatibility result = TextureViewCompatibility.Full;
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
if (result != TextureViewCompatibility.Incompatible)
{
bool msTargetCompatible = false;
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
if (allowMs)
bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
{
msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
}
if (!msTargetCompatible)
{
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)
{
result = TextureViewCompatibility.Incompatible;
}
result = TextureViewCompatibility.Incompatible;
}
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)

View File

@ -547,7 +547,6 @@ namespace Ryujinx.Graphics.Gpu.Image
range.Value,
sizeInfo.LayerSize,
_context.Capabilities,
flags.HasFlag(TextureSearchFlags.ForCopy),
out int firstLayer,
out int firstLevel);
@ -662,7 +661,6 @@ namespace Ryujinx.Graphics.Gpu.Image
overlap.Range,
overlap.LayerSize,
_context.Capabilities,
false,
out int firstLayer,
out int firstLevel);

View File

@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Texture;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.Gpu.Image
{
@ -657,6 +656,11 @@ namespace Ryujinx.Graphics.Gpu.Image
case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray:
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
{
return TextureViewCompatibility.CopyOnly;
}
result = rhs.Target == Target.Texture2DMultisample ||
rhs.Target == Target.Texture2DMultisampleArray;
break;

View File

@ -0,0 +1,98 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.OpenGL.Image
{
class IntermmediatePool : IDisposable
{
private readonly Renderer _renderer;
private readonly List<TextureView> _entries;
public IntermmediatePool(Renderer renderer)
{
_renderer = renderer;
_entries = new List<TextureView>();
}
public TextureView GetOrCreateWithAtLeast(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
TextureView entry;
for (int i = 0; i < _entries.Count; i++)
{
entry = _entries[i];
if (entry.Target == target && entry.Format == format)
{
if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
{
width = Math.Max(width, entry.Width);
height = Math.Max(height, entry.Height);
depth = Math.Max(depth, entry.Info.Depth);
levels = Math.Max(levels, entry.Info.Levels);
entry.Dispose();
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries[i] = entry;
}
return entry;
}
}
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries.Add(entry);
return entry;
}
private TextureView CreateNew(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
width,
height,
depth,
levels,
1,
blockWidth,
blockHeight,
bytesPerPixel,
format,
DepthStencilMode.Depth,
target,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha), 1f);
}
public void Dispose()
{
foreach (TextureView entry in _entries)
{
entry.Dispose();
}
_entries.Clear();
}
}
}

View File

@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
private readonly Renderer _renderer;
public IntermmediatePool IntermmediatePool { get; }
private int _srcFramebuffer;
private int _dstFramebuffer;
@ -18,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
public TextureCopy(Renderer renderer)
{
_renderer = renderer;
IntermmediatePool = new IntermmediatePool(renderer);
}
public void Copy(
@ -25,7 +28,30 @@ namespace Ryujinx.Graphics.OpenGL.Image
TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion,
bool linearFilter)
bool linearFilter,
int srcLayer = 0,
int dstLayer = 0,
int srcLevel = 0,
int dstLevel = 0)
{
int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel);
int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer);
Copy(src, dst, srcRegion, dstRegion, linearFilter, srcLayer, dstLayer, srcLevel, dstLevel, layers, levels);
}
public void Copy(
TextureView src,
TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion,
bool linearFilter,
int srcLayer,
int dstLayer,
int srcLevel,
int dstLevel,
int layers,
int levels)
{
TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src;
@ -34,22 +60,29 @@ namespace Ryujinx.Graphics.OpenGL.Image
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
int levels = Math.Min(src.Info.Levels, dst.Info.Levels);
int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers());
if (srcLevel != 0)
{
srcRegion = srcRegion.Reduce(srcLevel);
}
if (dstLevel != 0)
{
dstRegion = dstRegion.Reduce(dstLevel);
}
for (int level = 0; level < levels; level++)
{
for (int layer = 0; layer < layers; layer++)
{
if (layers > 1)
if ((srcLayer | dstLayer) != 0 || layers > 1)
{
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer);
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer);
}
else
{
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level);
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level);
}
ClearBufferMask mask = GetMask(src.Format);
@ -484,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
_copyPboHandle = 0;
}
IntermmediatePool.Dispose();
}
}
}

View File

@ -115,14 +115,77 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
TextureView destinationView = (TextureView)destination;
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
{
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
GetIntermmediateTarget(Target),
Info.BlockWidth,
Info.BlockHeight,
Info.BytesPerPixel,
Format,
Width,
Height,
Info.Depth,
Info.Levels);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
GL.Enable(EnableCap.FramebufferSrgb);
}
else
{
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
}
}
public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
{
TextureView destinationView = (TextureView)destination;
TextureView destinationView = (TextureView)destination;
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
{
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
GetIntermmediateTarget(Target),
Info.BlockWidth,
Info.BlockHeight,
Info.BytesPerPixel,
Format,
Math.Max(1, Width >> srcLevel),
Math.Max(1, Height >> srcLevel),
1,
1);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
GL.Enable(EnableCap.FramebufferSrgb);
}
else
{
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
}
}
private static Target GetIntermmediateTarget(Target srcTarget)
{
return srcTarget switch
{
Target.Texture2D => Target.Texture2DMultisample,
Target.Texture2DArray => Target.Texture2DMultisampleArray,
Target.Texture2DMultisampleArray => Target.Texture2DArray,
_ => Target.Texture2D
};
}
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)

View File

@ -597,6 +597,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.EndTransformFeedback();
}
GL.ClipControl(ClipOrigin.UpperLeft, ClipDepthMode.NegativeOneToOne);
_drawTexture.Draw(
view,
samp,
@ -627,6 +629,8 @@ namespace Ryujinx.Graphics.OpenGL
{
GL.BeginTransformFeedback(_tfTopology);
}
RestoreClipControl();
}
}
}

View File

@ -280,13 +280,6 @@ namespace Ryujinx.HLE.HOS
return;
}
if (mainNca == null)
{
Logger.Error?.Print(LogClass.Loader, "Unable to load NSP: Could not find Main NCA");
return;
}
if (mainNca != null)
{
_device.Configuration.ContentManager.ClearAocData();
@ -298,7 +291,7 @@ namespace Ryujinx.HLE.HOS
}
// This is not a normal NSP, it's actually a ExeFS as a NSP
LoadExeFs(nsp);
LoadExeFs(nsp, null, isHomebrew: true);
}
public void LoadNca(string ncaFile)
@ -593,7 +586,7 @@ namespace Ryujinx.HLE.HOS
}
}
private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null)
private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null, bool isHomebrew = false)
{
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
{
@ -661,7 +654,7 @@ namespace Ryujinx.HLE.HOS
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
// We allow it for nx-hbloader because it can be used to launch homebrew.
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL;
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew;
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit);

View File

@ -1,5 +1,4 @@
using ARMeilleure.Memory;
using ARMeilleure.State;
using Ryujinx.Cpu;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel.Process;
@ -11,12 +10,12 @@ namespace Ryujinx.HLE.HOS
{
private readonly ulong _pid;
private readonly GpuContext _gpuContext;
private readonly CpuContext _cpuContext;
private readonly ICpuContext _cpuContext;
private T _memoryManager;
public IVirtualMemoryManager AddressSpace => _memoryManager;
public ArmProcessContext(ulong pid, GpuContext gpuContext, T memoryManager, bool for64Bit)
public ArmProcessContext(ulong pid, ICpuEngine cpuEngine, GpuContext gpuContext, T memoryManager, bool for64Bit)
{
if (memoryManager is IRefCounted rc)
{
@ -27,11 +26,16 @@ namespace Ryujinx.HLE.HOS
_pid = pid;
_gpuContext = gpuContext;
_cpuContext = new CpuContext(memoryManager, for64Bit);
_cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit);
_memoryManager = memoryManager;
}
public void Execute(ExecutionContext context, ulong codeAddress)
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
{
return _cpuContext.CreateExecutionContext(exceptionCallbacks);
}
public void Execute(IExecutionContext context, ulong codeAddress)
{
_cpuContext.Execute(context, codeAddress);
}

View File

@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Cpu;
using Ryujinx.Cpu.Jit;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Process;
@ -10,10 +11,12 @@ namespace Ryujinx.HLE.HOS
{
class ArmProcessContextFactory : IProcessContextFactory
{
private readonly ICpuEngine _cpuEngine;
private readonly GpuContext _gpu;
public ArmProcessContextFactory(GpuContext gpu)
public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu)
{
_cpuEngine = cpuEngine;
_gpu = gpu;
}
@ -29,12 +32,14 @@ namespace Ryujinx.HLE.HOS
switch (mode)
{
case MemoryManagerMode.SoftwarePageTable:
return new ArmProcessContext<MemoryManager>(pid, _gpu, new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler), for64Bit);
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
return new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
case MemoryManagerMode.HostMapped:
case MemoryManagerMode.HostMappedUnsafe:
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _gpu, new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler);
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
default:
throw new ArgumentOutOfRangeException();

View File

@ -10,6 +10,8 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Output;
using Ryujinx.Audio.Renderer.Device;
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.Cpu;
using Ryujinx.Cpu.Jit;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Memory;
@ -57,6 +59,9 @@ namespace Ryujinx.HLE.HOS
internal Switch Device { get; private set; }
internal ITickSource TickSource { get; }
internal ICpuEngine CpuEngine { get; }
internal SurfaceFlinger SurfaceFlinger { get; private set; }
internal AudioManager AudioManager { get; private set; }
internal AudioOutputManager AudioOutputManager { get; private set; }
@ -121,7 +126,11 @@ namespace Ryujinx.HLE.HOS
public Horizon(Switch device)
{
TickSource = new TickSource(KernelConstants.CounterFrequency);
CpuEngine = new JitEngine(TickSource);
KernelContext = new KernelContext(
TickSource,
device,
device.Memory,
device.Configuration.MemoryConfiguration.ToKernelMemorySize(),
@ -215,40 +224,40 @@ namespace Ryujinx.HLE.HOS
internalOffset = new TimeSpanType(-internalOffset.NanoSeconds);
// First init the standard steady clock
TimeServiceManager.Instance.SetupStandardSteadyClock(null, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
TimeServiceManager.Instance.SetupStandardLocalSystemClock(null, new SystemClockContext(), systemTime.ToSeconds());
TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds());
if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
{
TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
// The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(null, out SystemClockContext localSytemClockContext);
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
}
TimeServiceManager.Instance.SetupStandardUserSystemClock(null, false, SteadyClockTimePoint.GetRandom());
TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom());
// FIXME: TimeZone should be init here but it's actually done in ContentManager
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
DatabaseImpl.Instance.InitializeDatabase(LibHacHorizonManager.SdbClient);
DatabaseImpl.Instance.InitializeDatabase(TickSource, LibHacHorizonManager.SdbClient);
HostSyncpoint = new NvHostSyncpt(device);
SurfaceFlinger = new SurfaceFlinger(device);
InitializeAudioRenderer();
InitializeAudioRenderer(TickSource);
InitializeServices();
}
private void InitializeAudioRenderer()
private void InitializeAudioRenderer(ITickSource tickSource)
{
AudioManager = new AudioManager();
AudioOutputManager = new AudioOutputManager();
AudioInputManager = new AudioInputManager();
AudioRendererManager = new AudioRendererManager();
AudioRendererManager = new AudioRendererManager(tickSource);
AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
@ -492,12 +501,12 @@ namespace Ryujinx.HLE.HOS
if (pause && !IsPaused)
{
Device.AudioDeviceDriver.GetPauseEvent().Reset();
ARMeilleure.State.ExecutionContext.SuspendCounter();
TickSource.Suspend();
}
else if (!pause && IsPaused)
{
Device.AudioDeviceDriver.GetPauseEvent().Set();
ARMeilleure.State.ExecutionContext.ResumeCounter();
TickSource.Resume();
}
}
IsPaused = pause;

View File

@ -12,5 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
public const ulong UserSlabHeapItemSize = KPageTableBase.PageSize;
public const ulong UserSlabHeapSize = 0x3de000;
public const ulong CounterFrequency = 19200000;
}
}

View File

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
@ -23,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public Switch Device { get; }
public MemoryBlock Memory { get; }
public ITickSource TickSource { get; }
public Syscall Syscall { get; }
public SyscallHandler SyscallHandler { get; }
@ -52,11 +54,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private ulong _threadUid;
public KernelContext(
ITickSource tickSource,
Switch device,
MemoryBlock memory,
MemorySize memorySize,
MemoryArrange memoryArrange)
{
TickSource = tickSource;
Device = device;
Memory = memory;

View File

@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
string GetReg(int x)
{
var v = x == 32 ? (ulong)thread.LastPc : context.GetX(x);
var v = x == 32 ? context.Pc : context.GetX(x);
if (!AnalyzePointer(out PointerInfo info, v, thread))
{
return $"0x{v:x16}";

View File

@ -1,4 +1,4 @@
using ARMeilleure.State;
using Ryujinx.Cpu;
using Ryujinx.Memory;
using System;
@ -8,7 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
IVirtualMemoryManager AddressSpace { get; }
void Execute(ExecutionContext context, ulong codeAddress);
IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks);
void Execute(IExecutionContext context, ulong codeAddress);
void InvalidateCacheRegion(ulong address, ulong size);
}
}

View File

@ -1,4 +1,3 @@
using ARMeilleure.State;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
@ -736,22 +735,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
ulong argsPtr,
ulong stackTop,
int priority,
int cpuCore)
int cpuCore,
ThreadStart customThreadStart = null)
{
lock (_processLock)
{
return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null);
return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, customThreadStart);
}
}
public void SubscribeThreadEventHandlers(ARMeilleure.State.ExecutionContext context)
public IExecutionContext CreateExecutionContext()
{
context.Interrupt += InterruptHandler;
context.SupervisorCall += KernelContext.SyscallHandler.SvcCall;
context.Undefined += UndefinedInstructionHandler;
return Context?.CreateExecutionContext(new ExceptionCallbacks(
InterruptHandler,
null,
KernelContext.SyscallHandler.SvcCall,
UndefinedInstructionHandler));
}
private void InterruptHandler(object sender, EventArgs e)
private void InterruptHandler(IExecutionContext context)
{
KThread currentThread = KernelStatic.GetCurrentThread();
@ -1093,12 +1095,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return false;
}
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
private void UndefinedInstructionHandler(IExecutionContext context, ulong address, int opCode)
{
KernelStatic.GetCurrentThread().PrintGuestStackTrace();
KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
throw new UndefinedInstructionException(e.Address, e.OpCode);
throw new UndefinedInstructionException(address, opCode);
}
protected override void Destroy() => Context.Dispose();

View File

@ -1,4 +1,4 @@
using ARMeilleure.State;
using Ryujinx.Cpu;
using Ryujinx.Memory;
using System;
@ -13,7 +13,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
AddressSpace = asManager;
}
public void Execute(ExecutionContext context, ulong codeAddress)
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
{
return new ProcessExecutionContext();
}
public void Execute(IExecutionContext context, ulong codeAddress)
{
throw new NotSupportedException();
}

View File

@ -0,0 +1,44 @@
using ARMeilleure.State;
using Ryujinx.Cpu;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class ProcessExecutionContext : IExecutionContext
{
public ulong Pc => 0UL;
public ulong CntfrqEl0 { get => 0; set { } }
public ulong CntpctEl0 => 0UL;
public long TpidrEl0 { get => 0; set { } }
public long TpidrroEl0 { get => 0; set { } }
public uint Pstate { get => 0; set { } }
public uint Fpcr { get => 0; set { } }
public uint Fpsr { get => 0; set { } }
public bool IsAarch32 { get => false; set { } }
public bool Running { get; private set; } = true;
public ulong GetX(int index) => 0UL;
public void SetX(int index, ulong value) { }
public V128 GetV(int index) => default;
public void SetV(int index, V128 value) { }
public void RequestInterrupt()
{
}
public void StopRunning()
{
Running = false;
}
public void Dispose()
{
}
}
}

View File

@ -1,9 +0,0 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
class InvalidSvcException : Exception
{
public InvalidSvcException(string message) : base(message) { }
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
class PointerSizedAttribute : Attribute
{
}
}

View File

@ -1,15 +0,0 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class RAttribute : Attribute
{
public readonly int Index;
public RAttribute(int index)
{
Index = index;
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
class SvcAttribute : Attribute
{
public int Id { get; }
public SvcAttribute(int id)
{
Id = id;
}
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
class SvcImplAttribute : Attribute
{
}
}

View File

@ -12,6 +12,7 @@ using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
[SvcImpl]
class Syscall
{
private readonly KernelContext _context;
@ -23,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
// Process
[Svc(0x24)]
public KernelResult GetProcessId(out ulong pid, int handle)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@ -167,9 +169,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x5f)]
public KernelResult FlushProcessDataCache(int processHandle, ulong address, ulong size)
{
// FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0.
// As we don't support (and don't actually need) to flush the cache, this is stubbed.
return KernelResult.Success;
}
// IPC
public KernelResult ConnectToNamedPort(out int handle, ulong namePtr)
[Svc(0x1f)]
public KernelResult ConnectToNamedPort(out int handle, [PointerSized] ulong namePtr)
{
handle = 0;
@ -222,6 +233,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x21)]
public KernelResult SendSyncRequest(int handle)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@ -236,7 +248,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return session.SendSyncRequest();
}
public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle)
[Svc(0x22)]
public KernelResult SendSyncRequestWithUserBuffer(
[PointerSized] ulong messagePtr,
[PointerSized] ulong messageSize,
int handle)
{
if (!PageAligned(messagePtr))
{
@ -283,7 +299,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
public KernelResult SendAsyncRequestWithUserBuffer(out int doneEventHandle, ulong messagePtr, ulong messageSize, int handle)
[Svc(0x23)]
public KernelResult SendAsyncRequestWithUserBuffer(
out int doneEventHandle,
[PointerSized] ulong messagePtr,
[PointerSized] ulong messageSize,
int handle)
{
doneEventHandle = 0;
@ -353,11 +374,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x40)]
public KernelResult CreateSession(
out int serverSessionHandle,
out int clientSessionHandle,
bool isLight,
ulong namePtr)
[PointerSized] ulong namePtr)
{
serverSessionHandle = 0;
clientSessionHandle = 0;
@ -419,6 +441,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x41)]
public KernelResult AcceptSession(out int sessionHandle, int portHandle)
{
sessionHandle = 0;
@ -470,9 +493,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x43)]
public KernelResult ReplyAndReceive(
out int handleIndex,
ulong handlesPtr,
[PointerSized] ulong handlesPtr,
int handlesCount,
int replyTargetHandle,
long timeout)
@ -575,11 +599,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x44)]
public KernelResult ReplyAndReceiveWithUserBuffer(
out int handleIndex,
ulong handlesPtr,
ulong messagePtr,
ulong messageSize,
[PointerSized] ulong messagePtr,
[PointerSized] ulong messageSize,
[PointerSized] ulong handlesPtr,
int handlesCount,
int replyTargetHandle,
long timeout)
@ -679,12 +704,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x70)]
public KernelResult CreatePort(
out int serverPortHandle,
out int clientPortHandle,
int maxSessions,
bool isLight,
ulong namePtr)
[PointerSized] ulong namePtr)
{
serverPortHandle = clientPortHandle = 0;
@ -714,7 +740,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
public KernelResult ManageNamedPort(out int handle, ulong namePtr, int maxSessions)
[Svc(0x71)]
public KernelResult ManageNamedPort(out int handle, [PointerSized] ulong namePtr, int maxSessions)
{
handle = 0;
@ -766,6 +793,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x72)]
public KernelResult ConnectToPort(out int clientSessionHandle, int clientPortHandle)
{
clientSessionHandle = 0;
@ -819,7 +847,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
// Memory
public KernelResult SetHeapSize(out ulong address, ulong size)
[Svc(1)]
public KernelResult SetHeapSize([PointerSized] out ulong address, [PointerSized] ulong size)
{
if ((size & 0xfffffffe001fffff) != 0)
{
@ -833,7 +862,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.SetHeapSize(size, out address);
}
public KernelResult SetMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
[Svc(2)]
public KernelResult SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
{
if (!PageAligned(address))
{
@ -865,9 +895,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission);
}
[Svc(3)]
public KernelResult SetMemoryAttribute(
ulong address,
ulong size,
[PointerSized] ulong address,
[PointerSized] ulong size,
MemoryAttribute attributeMask,
MemoryAttribute attributeValue)
{
@ -905,7 +936,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
public KernelResult MapMemory(ulong dst, ulong src, ulong size)
[Svc(4)]
public KernelResult MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
{
if (!PageAligned(src | dst))
{
@ -941,7 +973,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.Map(dst, src, size);
}
public KernelResult UnmapMemory(ulong dst, ulong src, ulong size)
[Svc(5)]
public KernelResult UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
{
if (!PageAligned(src | dst))
{
@ -977,7 +1010,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.Unmap(dst, src, size);
}
public KernelResult QueryMemory(ulong infoPtr, out ulong pageInfo, ulong address)
[Svc(6)]
public KernelResult QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address)
{
KernelResult result = QueryMemory(out MemoryInfo info, out pageInfo, address);
@ -1011,7 +1045,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
public KernelResult MapSharedMemory(int handle, ulong address, ulong size, KMemoryPermission permission)
[Svc(0x13)]
public KernelResult MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
{
if (!PageAligned(address))
{
@ -1057,7 +1092,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
permission);
}
public KernelResult UnmapSharedMemory(int handle, ulong address, ulong size)
[Svc(0x14)]
public KernelResult UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
{
@ -1097,7 +1133,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
currentProcess);
}
public KernelResult CreateTransferMemory(out int handle, ulong address, ulong size, KMemoryPermission permission)
[Svc(0x15)]
public KernelResult CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
{
handle = 0;
@ -1160,7 +1197,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
public KernelResult MapTransferMemory(int handle, ulong address, ulong size, KMemoryPermission permission)
[Svc(0x51)]
public KernelResult MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
{
if (!PageAligned(address))
{
@ -1206,7 +1244,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
permission);
}
public KernelResult UnmapTransferMemory(int handle, ulong address, ulong size)
[Svc(0x52)]
public KernelResult UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
{
@ -1246,7 +1285,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
currentProcess);
}
public KernelResult MapPhysicalMemory(ulong address, ulong size)
[Svc(0x2c)]
public KernelResult MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
{
@ -1281,7 +1321,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.MapPhysicalMemory(address, size);
}
public KernelResult UnmapPhysicalMemory(ulong address, ulong size)
[Svc(0x2d)]
public KernelResult UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
{
@ -1316,7 +1357,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.UnmapPhysicalMemory(address, size);
}
public KernelResult CreateCodeMemory(ulong address, ulong size, out int handle)
[Svc(0x4b)]
public KernelResult CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size)
{
handle = 0;
@ -1356,7 +1398,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.HandleTable.GenerateHandle(codeMemory, out handle);
}
public KernelResult ControlCodeMemory(int handle, CodeMemoryOperation op, ulong address, ulong size, KMemoryPermission permission)
[Svc(0x4c)]
public KernelResult ControlCodeMemory(
int handle,
CodeMemoryOperation op,
ulong address,
ulong size,
KMemoryPermission permission)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@ -1428,7 +1476,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission)
[Svc(0x73)]
public KernelResult SetProcessMemoryPermission(
int handle,
[PointerSized] ulong src,
[PointerSized] ulong size,
KMemoryPermission permission)
{
if (!PageAligned(src))
{
@ -1465,7 +1518,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
}
public KernelResult MapProcessMemory(ulong dst, int handle, ulong src, ulong size)
[Svc(0x74)]
public KernelResult MapProcessMemory(
[PointerSized] ulong dst,
int handle,
ulong src,
[PointerSized] ulong size)
{
if (!PageAligned(src) || !PageAligned(dst))
{
@ -1517,7 +1575,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite);
}
public KernelResult UnmapProcessMemory(ulong dst, int handle, ulong src, ulong size)
[Svc(0x75)]
public KernelResult UnmapProcessMemory(
[PointerSized] ulong dst,
int handle,
ulong src,
[PointerSized] ulong size)
{
if (!PageAligned(src) || !PageAligned(dst))
{
@ -1558,6 +1621,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x77)]
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
@ -1595,6 +1659,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
}
[Svc(0x78)]
public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
@ -1639,6 +1704,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
// System
[Svc(0x7b)]
public KernelResult TerminateProcess(int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -1668,11 +1734,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(7)]
public void ExitProcess()
{
KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
}
[Svc(0x11)]
public KernelResult SignalEvent(int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -1695,6 +1763,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x12)]
public KernelResult ClearEvent(int handle)
{
KernelResult result;
@ -1717,6 +1786,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x16)]
public KernelResult CloseHandle(int handle)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@ -1724,6 +1794,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle;
}
[Svc(0x17)]
public KernelResult ResetSignal(int handle)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@ -1753,11 +1824,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x1e)]
public ulong GetSystemTick()
{
return KernelStatic.GetCurrentThread().Context.CntpctEl0;
return _context.TickSource.Counter;
}
[Svc(0x26)]
public void Break(ulong reason)
{
KThread currentThread = KernelStatic.GetCurrentThread();
@ -1784,7 +1857,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
public void OutputDebugString(ulong strPtr, ulong size)
[Svc(0x27)]
public void OutputDebugString([PointerSized] ulong strPtr, [PointerSized] ulong size)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -1793,6 +1867,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
Logger.Warning?.Print(LogClass.KernelSvc, str);
}
[Svc(0x29)]
public KernelResult GetInfo(out ulong value, InfoType id, int handle, long subId)
{
value = 0;
@ -2038,6 +2113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x45)]
public KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
{
KEvent Event = new KEvent(_context);
@ -2063,7 +2139,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
public KernelResult GetProcessList(out int count, ulong address, int maxCount)
[Svc(0x65)]
public KernelResult GetProcessList(out int count, [PointerSized] ulong address, int maxCount)
{
count = 0;
@ -2112,6 +2189,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x6f)]
public KernelResult GetSystemInfo(out long value, uint id, int handle, long subId)
{
value = 0;
@ -2168,6 +2246,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x30)]
public KernelResult GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource)
{
limitValue = 0;
@ -2189,6 +2268,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x31)]
public KernelResult GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource)
{
limitValue = 0;
@ -2210,6 +2290,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x37)]
public KernelResult GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource)
{
peak = 0;
@ -2231,6 +2312,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0x7d)]
public KernelResult CreateResourceLimit(out int handle)
{
KResourceLimit limit = new KResourceLimit(_context);
@ -2240,6 +2322,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.HandleTable.GenerateHandle(limit, out handle);
}
[Svc(0x7e)]
public KernelResult SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue)
{
if (resource >= LimitableResource.Count)
@ -2259,13 +2342,26 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
// Thread
[Svc(8)]
public KernelResult CreateThread(
out int handle,
[PointerSized] ulong entrypoint,
[PointerSized] ulong argsPtr,
[PointerSized] ulong stackTop,
int priority,
int cpuCore)
{
return CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore, null);
}
public KernelResult CreateThread(
out int handle,
ulong entrypoint,
ulong argsPtr,
ulong stackTop,
int priority,
int cpuCore)
int cpuCore,
ThreadStart customThreadStart)
{
handle = 0;
@ -2302,7 +2398,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
argsPtr,
stackTop,
priority,
cpuCore);
cpuCore,
customThreadStart);
if (result == KernelResult.Success)
{
@ -2320,6 +2417,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(9)]
public KernelResult StartThread(int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -2347,6 +2445,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
[Svc(0xa)]
public void ExitThread()
{
KThread currentThread = KernelStatic.GetCurrentThread();
@ -2354,6 +2453,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
currentThread.Exit();
}
[Svc(0xb)]
public void SleepThread(long timeout)
{
if (timeout < 1)
@ -2371,6 +2471,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
[Svc(0xc)]
public KernelResult GetThreadPriority(out int priority, int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -2391,6 +2492,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
[Svc(0xd)]
public KernelResult SetThreadPriority(int handle, int priority)
{
// TODO: NPDM check.
@ -2409,6 +2511,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
[Svc(0xe)]
public KernelResult GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -2431,6 +2534,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
[Svc(0xf)]
public KernelResult SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@ -2479,11 +2583,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
}
[Svc(0x10)]
public int GetCurrentProcessorNumber()
{
return KernelStatic.GetCurrentThread().CurrentCore;
}
[Svc(0x25)]
public KernelResult GetThreadId(out ulong threadUid, int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -2504,6 +2610,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
[Svc(0x32)]
public KernelResult SetThreadActivity(int handle, bool pause)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -2528,7 +2635,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return thread.SetActivity(pause);
}
public KernelResult GetThreadContext3(ulong address, int handle)
[Svc(0x33)]
public KernelResult GetThreadContext3([PointerSized] ulong address, int handle)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KThread currentThread = KernelStatic.GetCurrentThread();
@ -2564,7 +2672,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
// Thread synchronization
public KernelResult WaitSynchronization(out int handleIndex, ulong handlesPtr, int handlesCount, long timeout)
[Svc(0x18)]
public KernelResult WaitSynchronization(out int handleIndex, [PointerSized] ulong handlesPtr, int handlesCount, long timeout)
{
handleIndex = 0;
@ -2653,6 +2762,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
[Svc(0x19)]
public KernelResult CancelSynchronization(int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@ -2669,7 +2779,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
[Svc(0x1a)]
public KernelResult ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle)
{
if (IsPointingInsideKernel(mutexAddress))
{
@ -2686,7 +2797,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
}
public KernelResult ArbitrateUnlock(ulong mutexAddress)
[Svc(0x1b)]
public KernelResult ArbitrateUnlock([PointerSized] ulong mutexAddress)
{
if (IsPointingInsideKernel(mutexAddress))
{
@ -2703,9 +2815,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
}
[Svc(0x1c)]
public KernelResult WaitProcessWideKeyAtomic(
ulong mutexAddress,
ulong condVarAddress,
[PointerSized] ulong mutexAddress,
[PointerSized] ulong condVarAddress,
int handle,
long timeout)
{
@ -2733,7 +2846,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
timeout);
}
public KernelResult SignalProcessWideKey(ulong address, int count)
[Svc(0x1d)]
public KernelResult SignalProcessWideKey([PointerSized] ulong address, int count)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@ -2742,7 +2856,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
public KernelResult WaitForAddress(ulong address, ArbitrationType type, int value, long timeout)
[Svc(0x34)]
public KernelResult WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout)
{
if (IsPointingInsideKernel(address))
{
@ -2773,7 +2888,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
};
}
public KernelResult SignalToAddress(ulong address, SignalType type, int value, int count)
[Svc(0x35)]
public KernelResult SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count)
{
if (IsPointingInsideKernel(address))
{
@ -2799,6 +2915,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
};
}
[Svc(0x36)]
public KernelResult SynchronizePreemptionState()
{
KernelStatic.GetCurrentThread().SynchronizePreemptionState();
@ -2806,12 +2923,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
private bool IsPointingInsideKernel(ulong address)
private static bool IsPointingInsideKernel(ulong address)
{
return (address + 0x1000000000) < 0xffffff000;
}
private bool IsAddressNotWordAligned(ulong address)
private static bool IsAddressNotWordAligned(ulong address)
{
return (address & 3) != 0;
}

View File

@ -1,534 +0,0 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
class Syscall32
{
private readonly Syscall _syscall;
public Syscall32(Syscall syscall)
{
_syscall = syscall;
}
// IPC
public KernelResult ConnectToNamedPort32([R(1)] uint namePtr, [R(1)] out int handle)
{
return _syscall.ConnectToNamedPort(out handle, namePtr);
}
public KernelResult SendSyncRequest32([R(0)] int handle)
{
return _syscall.SendSyncRequest(handle);
}
public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint messageSize, [R(2)] int handle)
{
return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
}
public KernelResult CreateSession32(
[R(2)] bool isLight,
[R(3)] uint namePtr,
[R(1)] out int serverSessionHandle,
[R(2)] out int clientSessionHandle)
{
return _syscall.CreateSession(out serverSessionHandle, out clientSessionHandle, isLight, namePtr);
}
public KernelResult AcceptSession32([R(1)] int portHandle, [R(1)] out int sessionHandle)
{
return _syscall.AcceptSession(out sessionHandle, portHandle);
}
public KernelResult ReplyAndReceive32(
[R(0)] uint timeoutLow,
[R(1)] uint handlesPtr,
[R(2)] int handlesCount,
[R(3)] int replyTargetHandle,
[R(4)] uint timeoutHigh,
[R(1)] out int handleIndex)
{
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
return _syscall.ReplyAndReceive(out handleIndex, handlesPtr, handlesCount, replyTargetHandle, timeout);
}
public KernelResult CreatePort32(
[R(0)] uint namePtr,
[R(2)] int maxSessions,
[R(3)] bool isLight,
[R(1)] out int serverPortHandle,
[R(2)] out int clientPortHandle)
{
return _syscall.CreatePort(out serverPortHandle, out clientPortHandle, maxSessions, isLight, namePtr);
}
public KernelResult ManageNamedPort32([R(1)] uint namePtr, [R(2)] int maxSessions, [R(1)] out int handle)
{
return _syscall.ManageNamedPort(out handle, namePtr, maxSessions);
}
public KernelResult ConnectToPort32([R(1)] int clientPortHandle, [R(1)] out int clientSessionHandle)
{
return _syscall.ConnectToPort(out clientSessionHandle, clientPortHandle);
}
// Memory
public KernelResult SetHeapSize32([R(1)] uint size, [R(1)] out uint address)
{
KernelResult result = _syscall.SetHeapSize(out ulong address64, size);
address = (uint)address64;
return result;
}
public KernelResult SetMemoryPermission32(
[R(0)] uint address,
[R(1)] uint size,
[R(2)] KMemoryPermission permission)
{
return _syscall.SetMemoryPermission(address, size, permission);
}
public KernelResult SetMemoryAttribute32(
[R(0)] uint address,
[R(1)] uint size,
[R(2)] MemoryAttribute attributeMask,
[R(3)] MemoryAttribute attributeValue)
{
return _syscall.SetMemoryAttribute(address, size, attributeMask, attributeValue);
}
public KernelResult MapMemory32([R(0)] uint dst, [R(1)] uint src, [R(2)] uint size)
{
return _syscall.MapMemory(dst, src, size);
}
public KernelResult UnmapMemory32([R(0)] uint dst, [R(1)] uint src, [R(2)] uint size)
{
return _syscall.UnmapMemory(dst, src, size);
}
public KernelResult QueryMemory32([R(0)] uint infoPtr, [R(1)] uint r1, [R(2)] uint address, [R(1)] out uint pageInfo)
{
KernelResult result = _syscall.QueryMemory(infoPtr, out ulong pageInfo64, address);
pageInfo = (uint)pageInfo64;
return result;
}
public KernelResult MapSharedMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] KMemoryPermission permission)
{
return _syscall.MapSharedMemory(handle, address, size, permission);
}
public KernelResult UnmapSharedMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size)
{
return _syscall.UnmapSharedMemory(handle, address, size);
}
public KernelResult CreateTransferMemory32(
[R(1)] uint address,
[R(2)] uint size,
[R(3)] KMemoryPermission permission,
[R(1)] out int handle)
{
return _syscall.CreateTransferMemory(out handle, address, size, permission);
}
public KernelResult CreateCodeMemory32([R(1)] uint address, [R(2)] uint size, [R(1)] out int handle)
{
return _syscall.CreateCodeMemory(address, size, out handle);
}
public KernelResult ControlCodeMemory32(
[R(0)] int handle,
[R(1)] CodeMemoryOperation op,
[R(2)] uint addressLow,
[R(3)] uint addressHigh,
[R(4)] uint sizeLow,
[R(5)] uint sizeHigh,
[R(6)] KMemoryPermission permission)
{
ulong address = addressLow | ((ulong)addressHigh << 32);
ulong size = sizeLow | ((ulong)sizeHigh << 32);
return _syscall.ControlCodeMemory(handle, op, address, size, permission);
}
public KernelResult MapTransferMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] KMemoryPermission permission)
{
return _syscall.MapTransferMemory(handle, address, size, permission);
}
public KernelResult UnmapTransferMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size)
{
return _syscall.UnmapTransferMemory(handle, address, size);
}
public KernelResult MapPhysicalMemory32([R(0)] uint address, [R(1)] uint size)
{
return _syscall.MapPhysicalMemory(address, size);
}
public KernelResult UnmapPhysicalMemory32([R(0)] uint address, [R(1)] uint size)
{
return _syscall.UnmapPhysicalMemory(address, size);
}
public KernelResult SetProcessMemoryPermission32(
[R(0)] int handle,
[R(1)] uint sizeLow,
[R(2)] uint srcLow,
[R(3)] uint srcHigh,
[R(4)] uint sizeHigh,
[R(5)] KMemoryPermission permission)
{
ulong src = srcLow | ((ulong)srcHigh << 32);
ulong size = sizeLow | ((ulong)sizeHigh << 32);
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
}
public KernelResult MapProcessMemory32([R(0)] uint dst, [R(1)] int handle, [R(2)] uint srcLow, [R(3)] uint srcHigh, [R(4)] uint size)
{
ulong src = srcLow | ((ulong)srcHigh << 32);
return _syscall.MapProcessMemory(dst, handle, src, size);
}
public KernelResult UnmapProcessMemory32([R(0)] uint dst, [R(1)] int handle, [R(2)] uint srcLow, [R(3)] uint srcHigh, [R(4)] uint size)
{
ulong src = srcLow | ((ulong)srcHigh << 32);
return _syscall.UnmapProcessMemory(dst, handle, src, size);
}
public KernelResult MapProcessCodeMemory32([R(0)] int handle, [R(1)] uint srcLow, [R(2)] uint dstLow, [R(3)] uint dstHigh, [R(4)] uint srcHigh, [R(5)] uint sizeLow, [R(6)] uint sizeHigh)
{
ulong src = srcLow | ((ulong)srcHigh << 32);
ulong dst = dstLow | ((ulong)dstHigh << 32);
ulong size = sizeLow | ((ulong)sizeHigh << 32);
return _syscall.MapProcessCodeMemory(handle, dst, src, size);
}
public KernelResult UnmapProcessCodeMemory32([R(0)] int handle, [R(1)] uint srcLow, [R(2)] uint dstLow, [R(3)] uint dstHigh, [R(4)] uint srcHigh, [R(5)] uint sizeLow, [R(6)] uint sizeHigh)
{
ulong src = srcLow | ((ulong)srcHigh << 32);
ulong dst = dstLow | ((ulong)dstHigh << 32);
ulong size = sizeLow | ((ulong)sizeHigh << 32);
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
}
// System
public void ExitProcess32()
{
_syscall.ExitProcess();
}
public KernelResult TerminateProcess32([R(0)] int handle)
{
return _syscall.TerminateProcess(handle);
}
public KernelResult SignalEvent32([R(0)] int handle)
{
return _syscall.SignalEvent(handle);
}
public KernelResult ClearEvent32([R(0)] int handle)
{
return _syscall.ClearEvent(handle);
}
public KernelResult CloseHandle32([R(0)] int handle)
{
return _syscall.CloseHandle(handle);
}
public KernelResult ResetSignal32([R(0)] int handle)
{
return _syscall.ResetSignal(handle);
}
public void GetSystemTick32([R(0)] out uint resultLow, [R(1)] out uint resultHigh)
{
ulong result = _syscall.GetSystemTick();
resultLow = (uint)(result & uint.MaxValue);
resultHigh = (uint)(result >> 32);
}
public KernelResult GetProcessId32([R(1)] int handle, [R(1)] out uint pidLow, [R(2)] out uint pidHigh)
{
KernelResult result = _syscall.GetProcessId(out ulong pid, handle);
pidLow = (uint)(pid & uint.MaxValue);
pidHigh = (uint)(pid >> 32);
return result;
}
public void Break32([R(0)] uint reason, [R(1)] uint r1, [R(2)] uint info)
{
_syscall.Break(reason);
}
public void OutputDebugString32([R(0)] uint strPtr, [R(1)] uint size)
{
_syscall.OutputDebugString(strPtr, size);
}
public KernelResult GetInfo32(
[R(0)] uint subIdLow,
[R(1)] InfoType id,
[R(2)] int handle,
[R(3)] uint subIdHigh,
[R(1)] out uint valueLow,
[R(2)] out uint valueHigh)
{
long subId = (long)(subIdLow | ((ulong)subIdHigh << 32));
KernelResult result = _syscall.GetInfo(out ulong value, id, handle, subId);
valueHigh = (uint)(value >> 32);
valueLow = (uint)(value & uint.MaxValue);
return result;
}
public KernelResult CreateEvent32([R(1)] out int wEventHandle, [R(2)] out int rEventHandle)
{
return _syscall.CreateEvent(out wEventHandle, out rEventHandle);
}
public KernelResult GetProcessList32([R(1)] ulong address, [R(2)] int maxCount, [R(1)] out int count)
{
return _syscall.GetProcessList(out count, address, maxCount);
}
public KernelResult GetSystemInfo32([R(1)] uint subIdLow, [R(2)] uint id, [R(3)] int handle, [R(3)] uint subIdHigh, [R(1)] out int valueLow, [R(2)] out int valueHigh)
{
long subId = (long)(subIdLow | ((ulong)subIdHigh << 32));
KernelResult result = _syscall.GetSystemInfo(out long value, id, handle, subId);
valueHigh = (int)(value >> 32);
valueLow = (int)(value & uint.MaxValue);
return result;
}
public KernelResult GetResourceLimitLimitValue32([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out int limitValueLow, [R(2)] out int limitValueHigh)
{
KernelResult result = _syscall.GetResourceLimitLimitValue(out long limitValue, handle, resource);
limitValueHigh = (int)(limitValue >> 32);
limitValueLow = (int)(limitValue & uint.MaxValue);
return result;
}
public KernelResult GetResourceLimitCurrentValue32([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out int limitValueLow, [R(2)] out int limitValueHigh)
{
KernelResult result = _syscall.GetResourceLimitCurrentValue(out long limitValue, handle, resource);
limitValueHigh = (int)(limitValue >> 32);
limitValueLow = (int)(limitValue & uint.MaxValue);
return result;
}
public KernelResult GetResourceLimitPeakValue32([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out int peakLow, [R(2)] out int peakHigh)
{
KernelResult result = _syscall.GetResourceLimitPeakValue(out long peak, handle, resource);
peakHigh = (int)(peak >> 32);
peakLow = (int)(peak & uint.MaxValue);
return result;
}
public KernelResult CreateResourceLimit32([R(1)] out int handle)
{
return _syscall.CreateResourceLimit(out handle);
}
public KernelResult SetResourceLimitLimitValue32([R(0)] int handle, [R(1)] LimitableResource resource, [R(2)] uint limitValueLow, [R(3)] uint limitValueHigh)
{
long limitValue = (long)(limitValueLow | ((ulong)limitValueHigh << 32));
return _syscall.SetResourceLimitLimitValue(handle, resource, limitValue);
}
public KernelResult FlushProcessDataCache32(
[R(0)] uint processHandle,
[R(2)] uint addressLow,
[R(3)] uint addressHigh,
[R(1)] uint sizeLow,
[R(4)] uint sizeHigh)
{
// FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0.
// As we don't support (and don't actually need) to flush the cache, this is stubbed.
return KernelResult.Success;
}
// Thread
public KernelResult CreateThread32(
[R(1)] uint entrypoint,
[R(2)] uint argsPtr,
[R(3)] uint stackTop,
[R(0)] int priority,
[R(4)] int cpuCore,
[R(1)] out int handle)
{
return _syscall.CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore);
}
public KernelResult StartThread32([R(0)] int handle)
{
return _syscall.StartThread(handle);
}
public void ExitThread32()
{
_syscall.ExitThread();
}
public void SleepThread32([R(0)] uint timeoutLow, [R(1)] uint timeoutHigh)
{
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
_syscall.SleepThread(timeout);
}
public KernelResult GetThreadPriority32([R(1)] int handle, [R(1)] out int priority)
{
return _syscall.GetThreadPriority(out priority, handle);
}
public KernelResult SetThreadPriority32([R(0)] int handle, [R(1)] int priority)
{
return _syscall.SetThreadPriority(handle, priority);
}
public KernelResult GetThreadCoreMask32([R(2)] int handle, [R(1)] out int preferredCore, [R(2)] out uint affinityMaskLow, [R(3)] out uint affinityMaskHigh)
{
KernelResult result = _syscall.GetThreadCoreMask(out preferredCore, out ulong affinityMask, handle);
affinityMaskLow = (uint)(affinityMask & uint.MaxValue);
affinityMaskHigh = (uint)(affinityMask >> 32);
return result;
}
public KernelResult SetThreadCoreMask32([R(0)] int handle, [R(1)] int preferredCore, [R(2)] uint affinityMaskLow, [R(3)] uint affinityMaskHigh)
{
ulong affinityMask = affinityMaskLow | ((ulong)affinityMaskHigh << 32);
return _syscall.SetThreadCoreMask(handle, preferredCore, affinityMask);
}
public int GetCurrentProcessorNumber32()
{
return _syscall.GetCurrentProcessorNumber();
}
public KernelResult GetThreadId32([R(1)] int handle, [R(1)] out uint threadUidLow, [R(2)] out uint threadUidHigh)
{
ulong threadUid;
KernelResult result = _syscall.GetThreadId(out threadUid, handle);
threadUidLow = (uint)(threadUid >> 32);
threadUidHigh = (uint)(threadUid & uint.MaxValue);
return result;
}
public KernelResult SetThreadActivity32([R(0)] int handle, [R(1)] bool pause)
{
return _syscall.SetThreadActivity(handle, pause);
}
public KernelResult GetThreadContext332([R(0)] uint address, [R(1)] int handle)
{
return _syscall.GetThreadContext3(address, handle);
}
// Thread synchronization
public KernelResult WaitSynchronization32(
[R(0)] uint timeoutLow,
[R(1)] uint handlesPtr,
[R(2)] int handlesCount,
[R(3)] uint timeoutHigh,
[R(1)] out int handleIndex)
{
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
return _syscall.WaitSynchronization(out handleIndex, handlesPtr, handlesCount, timeout);
}
public KernelResult CancelSynchronization32([R(0)] int handle)
{
return _syscall.CancelSynchronization(handle);
}
public KernelResult ArbitrateLock32([R(0)] int ownerHandle, [R(1)] uint mutexAddress, [R(2)] int requesterHandle)
{
return _syscall.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
}
public KernelResult ArbitrateUnlock32([R(0)] uint mutexAddress)
{
return _syscall.ArbitrateUnlock(mutexAddress);
}
public KernelResult WaitProcessWideKeyAtomic32(
[R(0)] uint mutexAddress,
[R(1)] uint condVarAddress,
[R(2)] int handle,
[R(3)] uint timeoutLow,
[R(4)] uint timeoutHigh)
{
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
return _syscall.WaitProcessWideKeyAtomic(mutexAddress, condVarAddress, handle, timeout);
}
public KernelResult SignalProcessWideKey32([R(0)] uint address, [R(1)] int count)
{
return _syscall.SignalProcessWideKey(address, count);
}
public KernelResult WaitForAddress32([R(0)] uint address, [R(1)] ArbitrationType type, [R(2)] int value, [R(3)] uint timeoutLow, [R(4)] uint timeoutHigh)
{
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
return _syscall.WaitForAddress(address, type, value, timeout);
}
public KernelResult SignalToAddress32([R(0)] uint address, [R(1)] SignalType type, [R(2)] int value, [R(3)] int count)
{
return _syscall.SignalToAddress(address, type, value, count);
}
public KernelResult SynchronizePreemptionState32()
{
return _syscall.SynchronizePreemptionState();
}
}
}

View File

@ -1,434 +0,0 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
class Syscall64
{
private readonly Syscall _syscall;
public Syscall64(Syscall syscall)
{
_syscall = syscall;
}
// IPC
public KernelResult ConnectToNamedPort64([R(1)] ulong namePtr, [R(1)] out int handle)
{
return _syscall.ConnectToNamedPort(out handle, namePtr);
}
public KernelResult SendSyncRequest64([R(0)] int handle)
{
return _syscall.SendSyncRequest(handle);
}
public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong messageSize, [R(2)] int handle)
{
return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
}
public KernelResult SendAsyncRequestWithUserBuffer64(
[R(1)] ulong messagePtr,
[R(2)] ulong messageSize,
[R(3)] int handle,
[R(1)] out int doneEventHandle)
{
return _syscall.SendAsyncRequestWithUserBuffer(out doneEventHandle, messagePtr, messageSize, handle);
}
public KernelResult CreateSession64(
[R(2)] bool isLight,
[R(3)] ulong namePtr,
[R(1)] out int serverSessionHandle,
[R(2)] out int clientSessionHandle)
{
return _syscall.CreateSession(out serverSessionHandle, out clientSessionHandle, isLight, namePtr);
}
public KernelResult AcceptSession64([R(1)] int portHandle, [R(1)] out int sessionHandle)
{
return _syscall.AcceptSession(out sessionHandle, portHandle);
}
public KernelResult ReplyAndReceive64(
[R(1)] ulong handlesPtr,
[R(2)] int handlesCount,
[R(3)] int replyTargetHandle,
[R(4)] long timeout,
[R(1)] out int handleIndex)
{
return _syscall.ReplyAndReceive(out handleIndex, handlesPtr, handlesCount, replyTargetHandle, timeout);
}
public KernelResult ReplyAndReceiveWithUserBuffer64(
[R(1)] ulong messagePtr,
[R(2)] ulong messageSize,
[R(3)] ulong handlesPtr,
[R(4)] int handlesCount,
[R(5)] int replyTargetHandle,
[R(6)] long timeout,
[R(1)] out int handleIndex)
{
return _syscall.ReplyAndReceiveWithUserBuffer(
out handleIndex,
handlesPtr,
messagePtr,
messageSize,
handlesCount,
replyTargetHandle,
timeout);
}
public KernelResult CreatePort64(
[R(2)] int maxSessions,
[R(3)] bool isLight,
[R(4)] ulong namePtr,
[R(1)] out int serverPortHandle,
[R(2)] out int clientPortHandle)
{
return _syscall.CreatePort(out serverPortHandle, out clientPortHandle, maxSessions, isLight, namePtr);
}
public KernelResult ManageNamedPort64([R(1)] ulong namePtr, [R(2)] int maxSessions, [R(1)] out int handle)
{
return _syscall.ManageNamedPort(out handle, namePtr, maxSessions);
}
public KernelResult ConnectToPort64([R(1)] int clientPortHandle, [R(1)] out int clientSessionHandle)
{
return _syscall.ConnectToPort(out clientSessionHandle, clientPortHandle);
}
// Memory
public KernelResult SetHeapSize64([R(1)] ulong size, [R(1)] out ulong address)
{
return _syscall.SetHeapSize(out address, size);
}
public KernelResult SetMemoryPermission64(
[R(0)] ulong address,
[R(1)] ulong size,
[R(2)] KMemoryPermission permission)
{
return _syscall.SetMemoryPermission(address, size, permission);
}
public KernelResult SetMemoryAttribute64(
[R(0)] ulong address,
[R(1)] ulong size,
[R(2)] MemoryAttribute attributeMask,
[R(3)] MemoryAttribute attributeValue)
{
return _syscall.SetMemoryAttribute(address, size, attributeMask, attributeValue);
}
public KernelResult MapMemory64([R(0)] ulong dst, [R(1)] ulong src, [R(2)] ulong size)
{
return _syscall.MapMemory(dst, src, size);
}
public KernelResult UnmapMemory64([R(0)] ulong dst, [R(1)] ulong src, [R(2)] ulong size)
{
return _syscall.UnmapMemory(dst, src, size);
}
public KernelResult QueryMemory64([R(0)] ulong infoPtr, [R(2)] ulong address, [R(1)] out ulong pageInfo)
{
return _syscall.QueryMemory(infoPtr, out pageInfo, address);
}
public KernelResult MapSharedMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
{
return _syscall.MapSharedMemory(handle, address, size, permission);
}
public KernelResult UnmapSharedMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size)
{
return _syscall.UnmapSharedMemory(handle, address, size);
}
public KernelResult CreateTransferMemory64(
[R(1)] ulong address,
[R(2)] ulong size,
[R(3)] KMemoryPermission permission,
[R(1)] out int handle)
{
return _syscall.CreateTransferMemory(out handle, address, size, permission);
}
public KernelResult CreateCodeMemory64([R(1)] ulong address, [R(2)] ulong size, [R(1)] out int handle)
{
return _syscall.CreateCodeMemory(address, size, out handle);
}
public KernelResult ControlCodeMemory64([R(0)] int handle, [R(1)] CodeMemoryOperation op, [R(2)] ulong address, [R(3)] ulong size, [R(4)] KMemoryPermission permission)
{
return _syscall.ControlCodeMemory(handle, op, address, size, permission);
}
public KernelResult MapTransferMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
{
return _syscall.MapTransferMemory(handle, address, size, permission);
}
public KernelResult UnmapTransferMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size)
{
return _syscall.UnmapTransferMemory(handle, address, size);
}
public KernelResult MapPhysicalMemory64([R(0)] ulong address, [R(1)] ulong size)
{
return _syscall.MapPhysicalMemory(address, size);
}
public KernelResult UnmapPhysicalMemory64([R(0)] ulong address, [R(1)] ulong size)
{
return _syscall.UnmapPhysicalMemory(address, size);
}
public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
{
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
}
public KernelResult MapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size)
{
return _syscall.MapProcessMemory(dst, handle, src, size);
}
public KernelResult UnmapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size)
{
return _syscall.UnmapProcessMemory(dst, handle, src, size);
}
public KernelResult MapProcessCodeMemory64([R(0)] int handle, [R(1)] ulong dst, [R(2)] ulong src, [R(3)] ulong size)
{
return _syscall.MapProcessCodeMemory(handle, dst, src, size);
}
public KernelResult UnmapProcessCodeMemory64([R(0)] int handle, [R(1)] ulong dst, [R(2)] ulong src, [R(3)] ulong size)
{
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
}
// System
public void ExitProcess64()
{
_syscall.ExitProcess();
}
public KernelResult TerminateProcess64([R(0)] int handle)
{
return _syscall.TerminateProcess(handle);
}
public KernelResult SignalEvent64([R(0)] int handle)
{
return _syscall.SignalEvent(handle);
}
public KernelResult ClearEvent64([R(0)] int handle)
{
return _syscall.ClearEvent(handle);
}
public KernelResult CloseHandle64([R(0)] int handle)
{
return _syscall.CloseHandle(handle);
}
public KernelResult ResetSignal64([R(0)] int handle)
{
return _syscall.ResetSignal(handle);
}
public ulong GetSystemTick64()
{
return _syscall.GetSystemTick();
}
public KernelResult GetProcessId64([R(1)] int handle, [R(1)] out ulong pid)
{
return _syscall.GetProcessId(out pid, handle);
}
public void Break64([R(0)] ulong reason, [R(1)] ulong x1, [R(2)] ulong info)
{
_syscall.Break(reason);
}
public void OutputDebugString64([R(0)] ulong strPtr, [R(1)] ulong size)
{
_syscall.OutputDebugString(strPtr, size);
}
public KernelResult GetInfo64([R(1)] InfoType id, [R(2)] int handle, [R(3)] long subId, [R(1)] out ulong value)
{
return _syscall.GetInfo(out value, id, handle, subId);
}
public KernelResult CreateEvent64([R(1)] out int wEventHandle, [R(2)] out int rEventHandle)
{
return _syscall.CreateEvent(out wEventHandle, out rEventHandle);
}
public KernelResult GetProcessList64([R(1)] ulong address, [R(2)] int maxCount, [R(1)] out int count)
{
return _syscall.GetProcessList(out count, address, maxCount);
}
public KernelResult GetSystemInfo64([R(1)] uint id, [R(2)] int handle, [R(3)] long subId, [R(1)] out long value)
{
return _syscall.GetSystemInfo(out value, id, handle, subId);
}
public KernelResult GetResourceLimitLimitValue64([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out long limitValue)
{
return _syscall.GetResourceLimitLimitValue(out limitValue, handle, resource);
}
public KernelResult GetResourceLimitCurrentValue64([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out long limitValue)
{
return _syscall.GetResourceLimitCurrentValue(out limitValue, handle, resource);
}
public KernelResult GetResourceLimitPeakValue64([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out long peak)
{
return _syscall.GetResourceLimitPeakValue(out peak, handle, resource);
}
public KernelResult CreateResourceLimit64([R(1)] out int handle)
{
return _syscall.CreateResourceLimit(out handle);
}
public KernelResult SetResourceLimitLimitValue64([R(0)] int handle, [R(1)] LimitableResource resource, [R(2)] long limitValue)
{
return _syscall.SetResourceLimitLimitValue(handle, resource, limitValue);
}
// Thread
public KernelResult CreateThread64(
[R(1)] ulong entrypoint,
[R(2)] ulong argsPtr,
[R(3)] ulong stackTop,
[R(4)] int priority,
[R(5)] int cpuCore,
[R(1)] out int handle)
{
return _syscall.CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore);
}
public KernelResult StartThread64([R(0)] int handle)
{
return _syscall.StartThread(handle);
}
public void ExitThread64()
{
_syscall.ExitThread();
}
public void SleepThread64([R(0)] long timeout)
{
_syscall.SleepThread(timeout);
}
public KernelResult GetThreadPriority64([R(1)] int handle, [R(1)] out int priority)
{
return _syscall.GetThreadPriority(out priority, handle);
}
public KernelResult SetThreadPriority64([R(0)] int handle, [R(1)] int priority)
{
return _syscall.SetThreadPriority(handle, priority);
}
public KernelResult GetThreadCoreMask64([R(2)] int handle, [R(1)] out int preferredCore, [R(2)] out ulong affinityMask)
{
return _syscall.GetThreadCoreMask(out preferredCore, out affinityMask, handle);
}
public KernelResult SetThreadCoreMask64([R(0)] int handle, [R(1)] int preferredCore, [R(2)] ulong affinityMask)
{
return _syscall.SetThreadCoreMask(handle, preferredCore, affinityMask);
}
public int GetCurrentProcessorNumber64()
{
return _syscall.GetCurrentProcessorNumber();
}
public KernelResult GetThreadId64([R(1)] int handle, [R(1)] out ulong threadUid)
{
return _syscall.GetThreadId(out threadUid, handle);
}
public KernelResult SetThreadActivity64([R(0)] int handle, [R(1)] bool pause)
{
return _syscall.SetThreadActivity(handle, pause);
}
public KernelResult GetThreadContext364([R(0)] ulong address, [R(1)] int handle)
{
return _syscall.GetThreadContext3(address, handle);
}
// Thread synchronization
public KernelResult WaitSynchronization64([R(1)] ulong handlesPtr, [R(2)] int handlesCount, [R(3)] long timeout, [R(1)] out int handleIndex)
{
return _syscall.WaitSynchronization(out handleIndex, handlesPtr, handlesCount, timeout);
}
public KernelResult CancelSynchronization64([R(0)] int handle)
{
return _syscall.CancelSynchronization(handle);
}
public KernelResult ArbitrateLock64([R(0)] int ownerHandle, [R(1)] ulong mutexAddress, [R(2)] int requesterHandle)
{
return _syscall.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
}
public KernelResult ArbitrateUnlock64([R(0)] ulong mutexAddress)
{
return _syscall.ArbitrateUnlock(mutexAddress);
}
public KernelResult WaitProcessWideKeyAtomic64(
[R(0)] ulong mutexAddress,
[R(1)] ulong condVarAddress,
[R(2)] int handle,
[R(3)] long timeout)
{
return _syscall.WaitProcessWideKeyAtomic(mutexAddress, condVarAddress, handle, timeout);
}
public KernelResult SignalProcessWideKey64([R(0)] ulong address, [R(1)] int count)
{
return _syscall.SignalProcessWideKey(address, count);
}
public KernelResult WaitForAddress64([R(0)] ulong address, [R(1)] ArbitrationType type, [R(2)] int value, [R(3)] long timeout)
{
return _syscall.WaitForAddress(address, type, value, timeout);
}
public KernelResult SignalToAddress64([R(0)] ulong address, [R(1)] SignalType type, [R(2)] int value, [R(3)] int count)
{
return _syscall.SignalToAddress(address, type, value, count);
}
public KernelResult SynchronizePreemptionState64()
{
return _syscall.SynchronizePreemptionState();
}
}
}

View File

@ -1,23 +1,18 @@
using ARMeilleure.State;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
partial class SyscallHandler
{
private readonly KernelContext _context;
private readonly Syscall32 _syscall32;
private readonly Syscall64 _syscall64;
public SyscallHandler(KernelContext context)
{
_context = context;
_syscall32 = new Syscall32(context.Syscall);
_syscall64 = new Syscall64(context.Syscall);
}
public void SvcCall(object sender, InstExceptionEventArgs e)
public void SvcCall(IExecutionContext context, ulong address, int id)
{
KThread currentThread = KernelStatic.GetCurrentThread();
@ -34,29 +29,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
_context.CriticalSection.Leave();
}
ExecutionContext context = (ExecutionContext)sender;
if (context.IsAarch32)
{
var svcFunc = SyscallTable.SvcTable32[e.Id];
if (svcFunc == null)
{
throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
}
svcFunc(_syscall32, context);
SyscallDispatch.Dispatch32(_context.Syscall, context, id);
}
else
{
var svcFunc = SyscallTable.SvcTable64[e.Id];
if (svcFunc == null)
{
throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
}
svcFunc(_syscall64, context);
SyscallDispatch.Dispatch64(_context.Syscall, context, id);
}
currentThread.HandlePostSyscall();

View File

@ -1,494 +0,0 @@
using ARMeilleure.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 SyscallTable
{
private const int SvcFuncMaxArguments64 = 8;
private const int SvcFuncMaxArguments32 = 4;
private const int SvcMax = 0x80;
public static Action<Syscall32, ExecutionContext>[] SvcTable32 { get; }
public static Action<Syscall64, ExecutionContext>[] SvcTable64 { get; }
static SyscallTable()
{
SvcTable32 = new Action<Syscall32, ExecutionContext>[SvcMax];
SvcTable64 = new Action<Syscall64, ExecutionContext>[SvcMax];
Dictionary<int, string> svcFuncs64 = new Dictionary<int, string>
{
{ 0x01, nameof(Syscall64.SetHeapSize64) },
{ 0x02, nameof(Syscall64.SetMemoryPermission64) },
{ 0x03, nameof(Syscall64.SetMemoryAttribute64) },
{ 0x04, nameof(Syscall64.MapMemory64) },
{ 0x05, nameof(Syscall64.UnmapMemory64) },
{ 0x06, nameof(Syscall64.QueryMemory64) },
{ 0x07, nameof(Syscall64.ExitProcess64) },
{ 0x08, nameof(Syscall64.CreateThread64) },
{ 0x09, nameof(Syscall64.StartThread64) },
{ 0x0a, nameof(Syscall64.ExitThread64) },
{ 0x0b, nameof(Syscall64.SleepThread64) },
{ 0x0c, nameof(Syscall64.GetThreadPriority64) },
{ 0x0d, nameof(Syscall64.SetThreadPriority64) },
{ 0x0e, nameof(Syscall64.GetThreadCoreMask64) },
{ 0x0f, nameof(Syscall64.SetThreadCoreMask64) },
{ 0x10, nameof(Syscall64.GetCurrentProcessorNumber64) },
{ 0x11, nameof(Syscall64.SignalEvent64) },
{ 0x12, nameof(Syscall64.ClearEvent64) },
{ 0x13, nameof(Syscall64.MapSharedMemory64) },
{ 0x14, nameof(Syscall64.UnmapSharedMemory64) },
{ 0x15, nameof(Syscall64.CreateTransferMemory64) },
{ 0x16, nameof(Syscall64.CloseHandle64) },
{ 0x17, nameof(Syscall64.ResetSignal64) },
{ 0x18, nameof(Syscall64.WaitSynchronization64) },
{ 0x19, nameof(Syscall64.CancelSynchronization64) },
{ 0x1a, nameof(Syscall64.ArbitrateLock64) },
{ 0x1b, nameof(Syscall64.ArbitrateUnlock64) },
{ 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64) },
{ 0x1d, nameof(Syscall64.SignalProcessWideKey64) },
{ 0x1e, nameof(Syscall64.GetSystemTick64) },
{ 0x1f, nameof(Syscall64.ConnectToNamedPort64) },
{ 0x21, nameof(Syscall64.SendSyncRequest64) },
{ 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
{ 0x23, nameof(Syscall64.SendAsyncRequestWithUserBuffer64) },
{ 0x24, nameof(Syscall64.GetProcessId64) },
{ 0x25, nameof(Syscall64.GetThreadId64) },
{ 0x26, nameof(Syscall64.Break64) },
{ 0x27, nameof(Syscall64.OutputDebugString64) },
{ 0x29, nameof(Syscall64.GetInfo64) },
{ 0x2c, nameof(Syscall64.MapPhysicalMemory64) },
{ 0x2d, nameof(Syscall64.UnmapPhysicalMemory64) },
{ 0x30, nameof(Syscall64.GetResourceLimitLimitValue64) },
{ 0x31, nameof(Syscall64.GetResourceLimitCurrentValue64) },
{ 0x32, nameof(Syscall64.SetThreadActivity64) },
{ 0x33, nameof(Syscall64.GetThreadContext364) },
{ 0x34, nameof(Syscall64.WaitForAddress64) },
{ 0x35, nameof(Syscall64.SignalToAddress64) },
{ 0x36, nameof(Syscall64.SynchronizePreemptionState64) },
{ 0x37, nameof(Syscall64.GetResourceLimitPeakValue64) },
{ 0x40, nameof(Syscall64.CreateSession64) },
{ 0x41, nameof(Syscall64.AcceptSession64) },
{ 0x43, nameof(Syscall64.ReplyAndReceive64) },
{ 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) },
{ 0x45, nameof(Syscall64.CreateEvent64) },
{ 0x4b, nameof(Syscall64.CreateCodeMemory64) },
{ 0x4c, nameof(Syscall64.ControlCodeMemory64) },
{ 0x51, nameof(Syscall64.MapTransferMemory64) },
{ 0x52, nameof(Syscall64.UnmapTransferMemory64) },
{ 0x65, nameof(Syscall64.GetProcessList64) },
{ 0x6f, nameof(Syscall64.GetSystemInfo64) },
{ 0x70, nameof(Syscall64.CreatePort64) },
{ 0x71, nameof(Syscall64.ManageNamedPort64) },
{ 0x72, nameof(Syscall64.ConnectToPort64) },
{ 0x73, nameof(Syscall64.SetProcessMemoryPermission64) },
{ 0x74, nameof(Syscall64.MapProcessMemory64) },
{ 0x75, nameof(Syscall64.UnmapProcessMemory64) },
{ 0x77, nameof(Syscall64.MapProcessCodeMemory64) },
{ 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) },
{ 0x7B, nameof(Syscall64.TerminateProcess64) },
{ 0x7D, nameof(Syscall64.CreateResourceLimit64) },
{ 0x7E, nameof(Syscall64.SetResourceLimitLimitValue64) }
};
foreach (KeyValuePair<int, string> value in svcFuncs64)
{
SvcTable64[value.Key] = GenerateMethod<Syscall64>(value.Value, SvcFuncMaxArguments64);
}
Dictionary<int, string> svcFuncs32 = new Dictionary<int, string>
{
{ 0x01, nameof(Syscall32.SetHeapSize32) },
{ 0x02, nameof(Syscall32.SetMemoryPermission32) },
{ 0x03, nameof(Syscall32.SetMemoryAttribute32) },
{ 0x04, nameof(Syscall32.MapMemory32) },
{ 0x05, nameof(Syscall32.UnmapMemory32) },
{ 0x06, nameof(Syscall32.QueryMemory32) },
{ 0x07, nameof(Syscall32.ExitProcess32) },
{ 0x08, nameof(Syscall32.CreateThread32) },
{ 0x09, nameof(Syscall32.StartThread32) },
{ 0x0a, nameof(Syscall32.ExitThread32) },
{ 0x0b, nameof(Syscall32.SleepThread32) },
{ 0x0c, nameof(Syscall32.GetThreadPriority32) },
{ 0x0d, nameof(Syscall32.SetThreadPriority32) },
{ 0x0e, nameof(Syscall32.GetThreadCoreMask32) },
{ 0x0f, nameof(Syscall32.SetThreadCoreMask32) },
{ 0x10, nameof(Syscall32.GetCurrentProcessorNumber32) },
{ 0x11, nameof(Syscall32.SignalEvent32) },
{ 0x12, nameof(Syscall32.ClearEvent32) },
{ 0x13, nameof(Syscall32.MapSharedMemory32) },
{ 0x14, nameof(Syscall32.UnmapSharedMemory32) },
{ 0x15, nameof(Syscall32.CreateTransferMemory32) },
{ 0x16, nameof(Syscall32.CloseHandle32) },
{ 0x17, nameof(Syscall32.ResetSignal32) },
{ 0x18, nameof(Syscall32.WaitSynchronization32) },
{ 0x19, nameof(Syscall32.CancelSynchronization32) },
{ 0x1a, nameof(Syscall32.ArbitrateLock32) },
{ 0x1b, nameof(Syscall32.ArbitrateUnlock32) },
{ 0x1c, nameof(Syscall32.WaitProcessWideKeyAtomic32) },
{ 0x1d, nameof(Syscall32.SignalProcessWideKey32) },
{ 0x1e, nameof(Syscall32.GetSystemTick32) },
{ 0x1f, nameof(Syscall32.ConnectToNamedPort32) },
{ 0x21, nameof(Syscall32.SendSyncRequest32) },
{ 0x22, nameof(Syscall32.SendSyncRequestWithUserBuffer32) },
{ 0x24, nameof(Syscall32.GetProcessId32) },
{ 0x25, nameof(Syscall32.GetThreadId32) },
{ 0x26, nameof(Syscall32.Break32) },
{ 0x27, nameof(Syscall32.OutputDebugString32) },
{ 0x29, nameof(Syscall32.GetInfo32) },
{ 0x2c, nameof(Syscall32.MapPhysicalMemory32) },
{ 0x2d, nameof(Syscall32.UnmapPhysicalMemory32) },
{ 0x30, nameof(Syscall32.GetResourceLimitLimitValue32) },
{ 0x31, nameof(Syscall32.GetResourceLimitCurrentValue32) },
{ 0x32, nameof(Syscall32.SetThreadActivity32) },
{ 0x33, nameof(Syscall32.GetThreadContext332) },
{ 0x34, nameof(Syscall32.WaitForAddress32) },
{ 0x35, nameof(Syscall32.SignalToAddress32) },
{ 0x36, nameof(Syscall32.SynchronizePreemptionState32) },
{ 0x37, nameof(Syscall32.GetResourceLimitPeakValue32) },
{ 0x40, nameof(Syscall32.CreateSession32) },
{ 0x41, nameof(Syscall32.AcceptSession32) },
{ 0x43, nameof(Syscall32.ReplyAndReceive32) },
{ 0x45, nameof(Syscall32.CreateEvent32) },
{ 0x4b, nameof(Syscall32.CreateCodeMemory32) },
{ 0x4c, nameof(Syscall32.ControlCodeMemory32) },
{ 0x51, nameof(Syscall32.MapTransferMemory32) },
{ 0x52, nameof(Syscall32.UnmapTransferMemory32) },
{ 0x5F, nameof(Syscall32.FlushProcessDataCache32) },
{ 0x65, nameof(Syscall32.GetProcessList32) },
{ 0x6f, nameof(Syscall32.GetSystemInfo32) },
{ 0x70, nameof(Syscall32.CreatePort32) },
{ 0x71, nameof(Syscall32.ManageNamedPort32) },
{ 0x72, nameof(Syscall32.ConnectToPort32) },
{ 0x73, nameof(Syscall32.SetProcessMemoryPermission32) },
{ 0x74, nameof(Syscall32.MapProcessMemory32) },
{ 0x75, nameof(Syscall32.UnmapProcessMemory32) },
{ 0x77, nameof(Syscall32.MapProcessCodeMemory32) },
{ 0x78, nameof(Syscall32.UnmapProcessCodeMemory32) },
{ 0x7B, nameof(Syscall32.TerminateProcess32) },
{ 0x7D, nameof(Syscall32.CreateResourceLimit32) },
{ 0x7E, nameof(Syscall32.SetResourceLimitLimitValue32) }
};
foreach (KeyValuePair<int, string> value in svcFuncs32)
{
SvcTable32[value.Key] = GenerateMethod<Syscall32>(value.Value, SvcFuncMaxArguments32);
}
}
private static Action<T, ExecutionContext> GenerateMethod<T>(string svcName, int registerCleanCount)
{
Type[] argTypes = new Type[] { typeof(T), typeof(ExecutionContext) };
DynamicMethod method = new DynamicMethod(svcName, null, argTypes);
MethodInfo methodInfo = typeof(T).GetMethod(svcName);
ParameterInfo[] methodArgs = methodInfo.GetParameters();
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;
}
}
RAttribute GetRegisterAttribute(ParameterInfo parameterInfo)
{
RAttribute argumentAttribute = (RAttribute)parameterInfo.GetCustomAttribute(typeof(RAttribute));
if (argumentAttribute == null)
{
throw new InvalidOperationException($"Method \"{svcName}\" is missing a {typeof(RAttribute).Name} attribute on parameter \"{parameterInfo.Name}\"");
}
return argumentAttribute;
}
// 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++;
}
}
BindingFlags staticNonPublic = BindingFlags.NonPublic | BindingFlags.Static;
// Print all the arguments for debugging purposes.
int inputArgsCount = methodArgs.Length - byRefArgsCount;
if (inputArgsCount != 0)
{
generator.Emit(OpCodes.Ldc_I4, inputArgsCount);
generator.Emit(OpCodes.Newarr, typeof(object));
string argsFormat = svcName;
for (int index = 0; index < methodArgs.Length; index++)
{
Type argType = methodArgs[index].ParameterType;
// Ignore out argument for printing
if (argType.IsByRef)
{
continue;
}
RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]);
argsFormat += $" {methodArgs[index].Name}: 0x{{{index}:X8}},";
generator.Emit(OpCodes.Dup);
generator.Emit(OpCodes.Ldc_I4, index);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index);
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
generator.Emit(OpCodes.Call, info);
generator.Emit(OpCodes.Box, typeof(ulong));
generator.Emit(OpCodes.Stelem_Ref);
}
argsFormat = argsFormat.Substring(0, argsFormat.Length - 1);
generator.Emit(OpCodes.Ldstr, argsFormat);
}
else
{
generator.Emit(OpCodes.Ldnull);
generator.Emit(OpCodes.Ldstr, svcName);
}
MethodInfo printArgsMethod = typeof(SyscallTable).GetMethod(nameof(PrintArguments), staticNonPublic);
generator.Emit(OpCodes.Call, printArgsMethod);
// Call the SVC function handler.
generator.Emit(OpCodes.Ldarg_0);
List<(LocalBuilder, RAttribute)> locals = new List<(LocalBuilder, RAttribute)>();
for (int index = 0; index < methodArgs.Length; index++)
{
Type argType = methodArgs[index].ParameterType;
RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]);
if (argType.IsByRef)
{
argType = argType.GetElementType();
LocalBuilder local = generator.DeclareLocal(argType);
locals.Add((local, registerAttribute));
if (!methodArgs[index].IsOut)
{
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index);
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
generator.Emit(OpCodes.Call, info);
ConvertToArgType(argType);
generator.Emit(OpCodes.Stloc, local);
}
generator.Emit(OpCodes.Ldloca, local);
}
else
{
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index);
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
generator.Emit(OpCodes.Call, info);
ConvertToArgType(argType);
}
}
generator.Emit(OpCodes.Call, methodInfo);
Type retType = methodInfo.ReturnType;
// Print result code.
if (retType == typeof(KernelResult))
{
MethodInfo printResultMethod = typeof(SyscallTable).GetMethod(nameof(PrintResult), staticNonPublic);
generator.Emit(OpCodes.Dup);
generator.Emit(OpCodes.Ldstr, svcName);
generator.Emit(OpCodes.Call, printResultMethod);
}
uint registerInUse = 0;
// 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.Ldc_I4, 0);
generator.Emit(OpCodes.Ldloc, tempLocal);
ConvertToFieldType(retType);
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX));
generator.Emit(OpCodes.Call, info);
registerInUse |= 1u << 0;
}
for (int index = 0; index < locals.Count; index++)
{
(LocalBuilder local, RAttribute attribute) = locals[index];
if ((registerInUse & (1u << attribute.Index)) != 0)
{
throw new InvalidSvcException($"Method \"{svcName}\" has conflicting output values at register index \"{attribute.Index}\".");
}
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldc_I4, attribute.Index);
generator.Emit(OpCodes.Ldloc, local);
ConvertToFieldType(local.LocalType);
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX));
generator.Emit(OpCodes.Call, info);
registerInUse |= 1u << attribute.Index;
}
// Zero out the remaining unused registers.
for (int i = 0; i < registerCleanCount; i++)
{
if ((registerInUse & (1u << i)) != 0)
{
continue;
}
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Ldc_I8, 0L);
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX));
generator.Emit(OpCodes.Call, info);
}
generator.Emit(OpCodes.Ret);
return method.CreateDelegate<Action<T, ExecutionContext>>();
}
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 PrintArguments(object[] argValues, string formatOrSvcName)
{
if (argValues != null)
{
Logger.Trace?.Print(LogClass.KernelSvc, string.Format(formatOrSvcName, argValues));
}
else
{
Logger.Trace?.Print(LogClass.KernelSvc, formatOrSvcName);
}
}
private static void PrintResult(KernelResult result, string svcName)
{
if (result != KernelResult.Success &&
result != KernelResult.TimedOut &&
result != KernelResult.Cancelled &&
result != KernelResult.PortRemoteClosed &&
result != KernelResult.InvalidState)
{
Logger.Warning?.Print(LogClass.KernelSvc, $"{svcName} returned error {result}.");
}
else
{
Logger.Trace?.Print(LogClass.KernelSvc, $"{svcName} returned result {result}.");
}
}
}
}

View File

@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public Thread HostThread { get; private set; }
public ARMeilleure.State.ExecutionContext Context { get; private set; }
public IExecutionContext Context { get; private set; }
public KThreadContext ThreadContext { get; private set; }
@ -115,9 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public bool WaitingInArbitration { get; set; }
public long LastPc { get; set; }
private object ActivityOperationLock = new object();
private object _activityOperationLock;
public KThread(KernelContext context) : base(context)
{
@ -128,6 +126,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_mutexWaiters = new LinkedList<KThread>();
_pinnedWaiters = new LinkedList<KThread>();
_activityOperationLock = new object();
}
public KernelResult Initialize(
@ -192,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
HostThread = new Thread(ThreadStart);
Context = CpuContext.CreateExecutionContext();
Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext();
Context.IsAarch32 = !is64Bits;
@ -208,8 +208,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Context.SetX(13, (uint)stackTop);
}
Context.CntfrqEl0 = 19200000;
Context.Tpidr = (long)_tlsAddress;
Context.TpidrroEl0 = (long)_tlsAddress;
ThreadUid = KernelContext.NewThreadUid();
@ -221,7 +220,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (owner != null)
{
owner.SubscribeThreadEventHandlers(Context);
owner.AddThread(this);
if (owner.IsPaused)
@ -538,7 +536,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult SetActivity(bool pause)
{
lock (ActivityOperationLock)
lock (_activityOperationLock)
{
KernelResult result = KernelResult.Success;
@ -634,7 +632,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
context = default;
lock (ActivityOperationLock)
lock (_activityOperationLock)
{
KernelContext.CriticalSection.Enter();
@ -656,7 +654,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return KernelResult.Success;
}
private static uint GetPsr(ARMeilleure.State.ExecutionContext context)
private static uint GetPsr(IExecutionContext context)
{
return context.Pstate & 0xFF0FFE20;
}
@ -683,9 +681,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
context.Fp = Context.GetX(29);
context.Lr = Context.GetX(30);
context.Sp = Context.GetX(31);
context.Pc = (ulong)LastPc;
context.Pc = Context.Pc;
context.Pstate = GetPsr(Context);
context.Tpidr = (ulong)Context.Tpidr;
context.Tpidr = (ulong)Context.TpidrroEl0;
}
else
{
@ -699,9 +697,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
context.FpuRegisters[i] = Context.GetV(i);
}
context.Pc = (uint)LastPc;
context.Pc = (uint)Context.Pc;
context.Pstate = GetPsr(Context);
context.Tpidr = (uint)Context.Tpidr;
context.Tpidr = (uint)Context.TpidrroEl0;
}
context.Fpcr = (uint)Context.Fpcr;
@ -743,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult SetCoreAndAffinityMask(int newCore, ulong newAffinityMask)
{
lock (ActivityOperationLock)
lock (_activityOperationLock)
{
KernelContext.CriticalSection.Enter();

View File

@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS
KProcess process = new KProcess(context);
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu);
result = process.InitializeKip(
creationInfo,
@ -264,7 +264,7 @@ namespace Ryujinx.HLE.HOS
return false;
}
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu);
result = process.Initialize(
creationInfo,

View File

@ -1,4 +1,5 @@
using LibHac;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Mii.Types;
using System;
@ -27,7 +28,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii
public DatabaseImpl()
{
_utilityImpl = new UtilityImpl();
_miiDatabase = new MiiDatabaseManager();
}
@ -148,12 +148,13 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return GetDefault(flag, ref count, elements);
}
public ResultCode InitializeDatabase(HorizonClient horizonClient)
public ResultCode InitializeDatabase(ITickSource tickSource, HorizonClient horizonClient)
{
_utilityImpl = new UtilityImpl(tickSource);
_miiDatabase.InitializeDatabase(horizonClient);
_miiDatabase.LoadFromFile(out _isBroken);
// Nintendo ignore any error code from before
// Nintendo ignores any error code from before.
return ResultCode.Success;
}

View File

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Mii.Types;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Mii.Types;
using Ryujinx.HLE.HOS.Services.Time;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using System;
@ -12,12 +13,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii
private uint _z;
private uint _w;
public UtilityImpl()
public UtilityImpl(ITickSource tickSource)
{
_x = 123456789;
_y = 362436069;
TimeSpanType time = TimeManager.Instance.TickBasedSteadyClock.GetCurrentRawTimePoint(null);
TimeSpanType time = TimeManager.Instance.TickBasedSteadyClock.GetCurrentRawTimePoint(tickSource);
_w = (uint)(time.NanoSeconds & uint.MaxValue);
_z = (uint)((time.NanoSeconds >> 32) & uint.MaxValue);

View File

@ -688,7 +688,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
{
RegisterInfo registerInfo = VirtualAmiibo.GetRegisterInfo(context.Device.System.NfpDevices[i].AmiiboId, context.Device.System.AccountManager.LastOpenedUser.Name);
RegisterInfo registerInfo = VirtualAmiibo.GetRegisterInfo(
context.Device.System.TickSource,
context.Device.System.NfpDevices[i].AmiiboId,
context.Device.System.AccountManager.LastOpenedUser.Name);
context.Memory.Write(outputPosition, registerInfo);
@ -911,7 +914,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
throw new ArgumentOutOfRangeException();
}
context.ResponseData.Write((uint)context.Device.System.NfpDevices[i].State);
return ResultCode.Success;

View File

@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Memory;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Mii.Types;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
@ -63,11 +64,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
};
}
public static RegisterInfo GetRegisterInfo(string amiiboId, string nickname)
public static RegisterInfo GetRegisterInfo(ITickSource tickSource, string amiiboId, string nickname)
{
VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId);
UtilityImpl utilityImpl = new UtilityImpl();
UtilityImpl utilityImpl = new UtilityImpl(tickSource);
CharInfo charInfo = new CharInfo();
charInfo.SetFromStoreData(StoreData.BuildDefault(utilityImpl, 0));

View File

@ -429,8 +429,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
Channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
}
Channel.PushEntries(entries);
header.Fence.Id = _channelSyncpoint.Id;
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) || header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
@ -449,6 +447,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
header.Fence.Value = _device.System.HostSyncpoint.ReadSyncpointMaxValue(header.Fence.Id);
}
Channel.PushEntries(entries);
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
{
Channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Common;
@ -38,15 +39,15 @@ namespace Ryujinx.HLE.HOS.Services
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
public ManualResetEvent InitDone { get; }
public Func<IpcService> SmObjectFactory { get; }
public string Name { get; }
public Func<IpcService> SmObjectFactory { get; }
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
{
InitDone = new ManualResetEvent(false);
_context = context;
Name = name;
SmObjectFactory = smObjectFactory;
_context = context;
const ProcessCreationFlags flags =
ProcessCreationFlags.EnableAslr |
@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main);
}
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
@ -80,6 +81,11 @@ namespace Ryujinx.HLE.HOS.Services
_sessions.Add(serverSessionHandle, obj);
}
private void Main()
{
ServerLoop();
}
private void ServerLoop()
{
_selfProcess = KernelStatic.GetCurrentProcess();

View File

@ -8,7 +8,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
private ulong _value;
private readonly EventFdFlags _flags;
private AutoResetEvent _event;
private object _lock = new object();
@ -19,9 +18,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
public EventFileDescriptor(ulong value, EventFdFlags flags)
{
// FIXME: We should support blocking operations.
// Right now they can't be supported because it would cause the
// service to lock up as we only have one thread processing requests.
flags |= EventFdFlags.NonBlocking;
_value = value;
_flags = flags;
_event = new AutoResetEvent(false);
WriteEvent = new ManualResetEvent(true);
ReadEvent = new ManualResetEvent(true);
@ -31,7 +34,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
public void Dispose()
{
_event.Dispose();
WriteEvent.Dispose();
ReadEvent.Dispose();
}
@ -57,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
while (_value == 0)
{
_event.WaitOne();
Monitor.Wait(_lock);
}
}
else
@ -106,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
if (Blocking)
{
_event.WaitOne();
Monitor.Wait(_lock);
}
else
{
@ -119,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
writeSize = sizeof(ulong);
_value += count;
_event.Set();
Monitor.Pulse(_lock);
WriteEvent.Set();

View File

@ -6,7 +6,7 @@
{
BufferQueueCore core = new BufferQueueCore(device, pid);
producer = new BufferQueueProducer(core);
producer = new BufferQueueProducer(core, device.System.TickSource);
consumer = new BufferQueueConsumer(core);
return core;

View File

@ -1,5 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
using System;
@ -241,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
BufferSlot slot = Slots[item.Slot];
// TODO: Check this. On Android, this checks the "handle". I assume NvMapHandle is the handle, but it might not be.
// TODO: Check this. On Android, this checks the "handle". I assume NvMapHandle is the handle, but it might not be.
return !slot.GraphicBuffer.IsNull && slot.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle == item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle;
}

View File

@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Settings;
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
@ -12,6 +13,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
public BufferQueueCore Core { get; }
private readonly ITickSource _tickSource;
private uint _stickyTransform;
private uint _nextCallbackTicket;
@ -20,9 +23,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private readonly object _callbackLock = new object();
public BufferQueueProducer(BufferQueueCore core)
public BufferQueueProducer(BufferQueueCore core, ITickSource tickSource)
{
Core = core;
_tickSource = tickSource;
_stickyTransform = 0;
_callbackTicket = 0;
@ -179,8 +183,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
GraphicBuffer graphicBuffer = Core.Slots[slot].GraphicBuffer.Object;
if (Core.Slots[slot].GraphicBuffer.IsNull
|| graphicBuffer.Width != width
|| graphicBuffer.Height != height
|| graphicBuffer.Width != width
|| graphicBuffer.Height != height
|| graphicBuffer.Format != format
|| (graphicBuffer.Usage & usage) != usage)
{
@ -193,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
}
else
{
Logger.Error?.Print(LogClass.SurfaceFlinger,
Logger.Error?.Print(LogClass.SurfaceFlinger,
$"Preallocated buffer mismatch - slot {slot}\n" +
$"available: Width = {graphicBuffer.Width} Height = {graphicBuffer.Height} Format = {graphicBuffer.Format} Usage = {graphicBuffer.Usage:x} " +
$"requested: Width = {width} Height = {height} Format = {format} Usage = {usage:x}");
@ -388,7 +392,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
Core.Slots[slot].BufferState = BufferState.Queued;
Core.FrameCounter++;
Core.Slots[slot].FrameNumber = Core.FrameCounter;
Core.Slots[slot].QueueTime = TimeSpanType.FromTimeSpan(ARMeilleure.State.ExecutionContext.ElapsedTime);
Core.Slots[slot].QueueTime = TimeSpanType.FromTimeSpan(_tickSource.ElapsedTime);
Core.Slots[slot].PresentationTime = TimeSpanType.Zero;
item.AcquireCalled = Core.Slots[slot].AcquireCalled;

View File

@ -1,4 +1,4 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Cpu;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
@ -11,14 +11,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
_standardNetworkClockSufficientAccuracy = new TimeSpanType(0);
}
public bool IsStandardNetworkSystemClockAccuracySufficient(KThread thread)
public bool IsStandardNetworkSystemClockAccuracySufficient(ITickSource tickSource)
{
SteadyClockCore steadyClockCore = GetSteadyClockCore();
SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(thread);
SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(tickSource);
bool isStandardNetworkClockSufficientAccuracy = false;
ResultCode result = GetClockContext(thread, out SystemClockContext context);
ResultCode result = GetClockContext(tickSource, out SystemClockContext context);
if (result == ResultCode.Success && context.SteadyTimePoint.GetSpanBetween(currentTimePoint, out long outSpan) == ResultCode.Success)
{

View File

@ -1,4 +1,4 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Cpu;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
@ -17,11 +17,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
_cachedRawTimePoint = TimeSpanType.Zero;
}
public override SteadyClockTimePoint GetTimePoint(KThread thread)
public override SteadyClockTimePoint GetTimePoint(ITickSource tickSource)
{
SteadyClockTimePoint result = new SteadyClockTimePoint
{
TimePoint = GetCurrentRawTimePoint(thread).ToSeconds(),
TimePoint = GetCurrentRawTimePoint(tickSource).ToSeconds(),
ClockSourceId = GetClockSourceId()
};
@ -48,19 +48,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
_internalOffset = internalOffset;
}
public override TimeSpanType GetCurrentRawTimePoint(KThread thread)
public override TimeSpanType GetCurrentRawTimePoint(ITickSource tickSource)
{
TimeSpanType ticksTimeSpan;
// As this may be called before the guest code, we support passing a null thread to make this api usable.
if (thread == null)
{
ticksTimeSpan = TimeSpanType.FromSeconds(0);
}
else
{
ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
}
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
TimeSpanType rawTimePoint = new TimeSpanType(_setupValue.NanoSeconds + ticksTimeSpan.NanoSeconds);

View File

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
@ -26,15 +27,15 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
throw new NotImplementedException();
}
public override ResultCode GetClockContext(KThread thread, out SystemClockContext context)
public override ResultCode GetClockContext(ITickSource tickSource, out SystemClockContext context)
{
ResultCode result = ApplyAutomaticCorrection(thread, false);
ResultCode result = ApplyAutomaticCorrection(tickSource, false);
context = new SystemClockContext();
if (result == ResultCode.Success)
{
return _localSystemClockCore.GetClockContext(thread, out context);
return _localSystemClockCore.GetClockContext(tickSource, out context);
}
return result;
@ -45,13 +46,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return ResultCode.NotImplemented;
}
private ResultCode ApplyAutomaticCorrection(KThread thread, bool autoCorrectionEnabled)
private ResultCode ApplyAutomaticCorrection(ITickSource tickSource, bool autoCorrectionEnabled)
{
ResultCode result = ResultCode.Success;
if (_autoCorrectionEnabled != autoCorrectionEnabled && _networkSystemClockCore.IsClockSetup(thread))
if (_autoCorrectionEnabled != autoCorrectionEnabled && _networkSystemClockCore.IsClockSetup(tickSource))
{
result = _networkSystemClockCore.GetClockContext(thread, out SystemClockContext context);
result = _networkSystemClockCore.GetClockContext(tickSource, out SystemClockContext context);
if (result == ResultCode.Success)
{
@ -67,9 +68,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
_autoCorrectionEvent = new KEvent(system.KernelContext);
}
public ResultCode SetAutomaticCorrectionEnabled(KThread thread, bool autoCorrectionEnabled)
public ResultCode SetAutomaticCorrectionEnabled(ITickSource tickSource, bool autoCorrectionEnabled)
{
ResultCode result = ApplyAutomaticCorrection(thread, autoCorrectionEnabled);
ResultCode result = ApplyAutomaticCorrection(tickSource, autoCorrectionEnabled);
if (result == ResultCode.Success)
{

View File

@ -1,4 +1,4 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Cpu;
using Ryujinx.HLE.Utilities;
using System;
@ -63,21 +63,21 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public virtual void SetInternalOffset(TimeSpanType internalOffset) {}
public virtual SteadyClockTimePoint GetTimePoint(KThread thread)
public virtual SteadyClockTimePoint GetTimePoint(ITickSource tickSource)
{
throw new NotImplementedException();
}
public virtual TimeSpanType GetCurrentRawTimePoint(KThread thread)
public virtual TimeSpanType GetCurrentRawTimePoint(ITickSource tickSource)
{
SteadyClockTimePoint timePoint = GetTimePoint(thread);
SteadyClockTimePoint timePoint = GetTimePoint(tickSource);
return TimeSpanType.FromSeconds(timePoint.TimePoint);
}
public SteadyClockTimePoint GetCurrentTimePoint(KThread thread)
public SteadyClockTimePoint GetCurrentTimePoint(ITickSource tickSource)
{
SteadyClockTimePoint result = GetTimePoint(thread);
SteadyClockTimePoint result = GetTimePoint(tickSource);
result.TimePoint += GetTestOffset().ToSeconds();
result.TimePoint += GetInternalOffset().ToSeconds();

View File

@ -1,4 +1,4 @@
using System;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
@ -25,13 +25,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return _steadyClockCore;
}
public ResultCode GetCurrentTime(KThread thread, out long posixTime)
public ResultCode GetCurrentTime(ITickSource tickSource, out long posixTime)
{
posixTime = 0;
SteadyClockTimePoint currentTimePoint = _steadyClockCore.GetCurrentTimePoint(thread);
SteadyClockTimePoint currentTimePoint = _steadyClockCore.GetCurrentTimePoint(tickSource);
ResultCode result = GetClockContext(thread, out SystemClockContext clockContext);
ResultCode result = GetClockContext(tickSource, out SystemClockContext clockContext);
if (result == ResultCode.Success)
{
@ -48,9 +48,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return result;
}
public ResultCode SetCurrentTime(KThread thread, long posixTime)
public ResultCode SetCurrentTime(ITickSource tickSource, long posixTime)
{
SteadyClockTimePoint currentTimePoint = _steadyClockCore.GetCurrentTimePoint(thread);
SteadyClockTimePoint currentTimePoint = _steadyClockCore.GetCurrentTimePoint(tickSource);
SystemClockContext clockContext = new SystemClockContext()
{
@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return result;
}
public virtual ResultCode GetClockContext(KThread thread, out SystemClockContext context)
public virtual ResultCode GetClockContext(ITickSource tickSource, out SystemClockContext context)
{
context = _context;
@ -127,13 +127,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
_isInitialized = true;
}
public bool IsClockSetup(KThread thread)
public bool IsClockSetup(ITickSource tickSource)
{
ResultCode result = GetClockContext(thread, out SystemClockContext context);
ResultCode result = GetClockContext(tickSource, out SystemClockContext context);
if (result == ResultCode.Success)
{
SteadyClockTimePoint steadyClockTimePoint = _steadyClockCore.GetCurrentTimePoint(thread);
SteadyClockTimePoint steadyClockTimePoint = _steadyClockCore.GetCurrentTimePoint(tickSource);
return steadyClockTimePoint.ClockSourceId == context.SteadyTimePoint.ClockSourceId;
}

View File

@ -1,4 +1,4 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Cpu;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
public TickBasedSteadyClockCore() {}
public override SteadyClockTimePoint GetTimePoint(KThread thread)
public override SteadyClockTimePoint GetTimePoint(ITickSource tickSource)
{
SteadyClockTimePoint result = new SteadyClockTimePoint
{
@ -14,17 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
ClockSourceId = GetClockSourceId()
};
TimeSpanType ticksTimeSpan;
// As this may be called before the guest code, we support passing a null thread to make this api usable.
if (thread == null)
{
ticksTimeSpan = TimeSpanType.FromSeconds(0);
}
else
{
ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
}
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
result.TimePoint = ticksTimeSpan.ToSeconds();

View File

@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.StaticService;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
@ -163,13 +162,15 @@ namespace Ryujinx.HLE.HOS.Services.Time
bool autoCorrectionEnabled = context.RequestData.ReadBoolean();
ResultCode result = userClock.SetAutomaticCorrectionEnabled(context.Thread, autoCorrectionEnabled);
ITickSource tickSource = context.Device.System.TickSource;
ResultCode result = userClock.SetAutomaticCorrectionEnabled(tickSource, autoCorrectionEnabled);
if (result == ResultCode.Success)
{
_timeManager.SharedMemory.SetAutomaticCorrectionEnabled(autoCorrectionEnabled);
SteadyClockTimePoint currentTimePoint = userClock.GetSteadyClockCore().GetCurrentTimePoint(context.Thread);
SteadyClockTimePoint currentTimePoint = userClock.GetSteadyClockCore().GetCurrentTimePoint(tickSource);
userClock.SetAutomaticCorrectionUpdatedTime(currentTimePoint);
userClock.SignalAutomaticCorrectionEvent();
@ -190,7 +191,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
// IsStandardNetworkSystemClockAccuracySufficient() -> bool
public ResultCode IsStandardNetworkSystemClockAccuracySufficient(ServiceCtx context)
{
context.ResponseData.Write(_timeManager.StandardNetworkSystemClock.IsStandardNetworkSystemClockAccuracySufficient(context.Thread));
ITickSource tickSource = context.Device.System.TickSource;
context.ResponseData.Write(_timeManager.StandardNetworkSystemClock.IsStandardNetworkSystemClockAccuracySufficient(tickSource));
return ResultCode.Success;
}
@ -222,14 +225,16 @@ namespace Ryujinx.HLE.HOS.Services.Time
return ResultCode.UninitializedClock;
}
ITickSource tickSource = context.Device.System.TickSource;
SystemClockContext otherContext = context.RequestData.ReadStruct<SystemClockContext>();
SteadyClockTimePoint currentTimePoint = steadyClock.GetCurrentTimePoint(context.Thread);
SteadyClockTimePoint currentTimePoint = steadyClock.GetCurrentTimePoint(tickSource);
ResultCode result = ResultCode.TimeMismatch;
if (currentTimePoint.ClockSourceId == otherContext.SteadyTimePoint.ClockSourceId)
{
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(context.Thread.Context.CntpctEl0, context.Thread.Context.CntfrqEl0);
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
long baseTimePoint = otherContext.Offset + currentTimePoint.TimePoint - ticksTimeSpan.ToSeconds();
context.ResponseData.Write(baseTimePoint);
@ -248,15 +253,17 @@ namespace Ryujinx.HLE.HOS.Services.Time
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ClockSnapshot>());
ResultCode result = _timeManager.StandardUserSystemClock.GetClockContext(context.Thread, out SystemClockContext userContext);
ITickSource tickSource = context.Device.System.TickSource;
ResultCode result = _timeManager.StandardUserSystemClock.GetClockContext(tickSource, out SystemClockContext userContext);
if (result == ResultCode.Success)
{
result = _timeManager.StandardNetworkSystemClock.GetClockContext(context.Thread, out SystemClockContext networkContext);
result = _timeManager.StandardNetworkSystemClock.GetClockContext(tickSource, out SystemClockContext networkContext);
if (result == ResultCode.Success)
{
result = GetClockSnapshotFromSystemClockContextInternal(context.Thread, userContext, networkContext, type, out ClockSnapshot clockSnapshot);
result = GetClockSnapshotFromSystemClockContextInternal(tickSource, userContext, networkContext, type, out ClockSnapshot clockSnapshot);
if (result == ResultCode.Success)
{
@ -281,7 +288,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
SystemClockContext userContext = context.RequestData.ReadStruct<SystemClockContext>();
SystemClockContext networkContext = context.RequestData.ReadStruct<SystemClockContext>();
ResultCode result = GetClockSnapshotFromSystemClockContextInternal(context.Thread, userContext, networkContext, type, out ClockSnapshot clockSnapshot);
ITickSource tickSource = context.Device.System.TickSource;
ResultCode result = GetClockSnapshotFromSystemClockContextInternal(tickSource, userContext, networkContext, type, out ClockSnapshot clockSnapshot);
if (result == ResultCode.Success)
{
@ -344,12 +353,12 @@ namespace Ryujinx.HLE.HOS.Services.Time
return resultCode;
}
private ResultCode GetClockSnapshotFromSystemClockContextInternal(KThread thread, SystemClockContext userContext, SystemClockContext networkContext, byte type, out ClockSnapshot clockSnapshot)
private ResultCode GetClockSnapshotFromSystemClockContextInternal(ITickSource tickSource, SystemClockContext userContext, SystemClockContext networkContext, byte type, out ClockSnapshot clockSnapshot)
{
clockSnapshot = new ClockSnapshot();
SteadyClockCore steadyClockCore = _timeManager.StandardSteadyClock;
SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(thread);
SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(tickSource);
clockSnapshot.IsAutomaticCorrectionEnabled = _timeManager.StandardUserSystemClock.IsAutomaticCorrectionEnabled();
clockSnapshot.UserContext = userContext;

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
@ -6,7 +7,6 @@ using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.Utilities;
using System;
using System.IO;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Time
{
@ -68,7 +68,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>();
bool isRtcResetDetected = context.RequestData.ReadBoolean();
_timeManager.SetupStandardSteadyClock(context.Thread, clockSourceId, setupValue, internalOffset, testOffset, isRtcResetDetected);
ITickSource tickSource = context.Device.System.TickSource;
_timeManager.SetupStandardSteadyClock(tickSource, clockSourceId, setupValue, internalOffset, testOffset, isRtcResetDetected);
return ResultCode.Success;
}
@ -80,7 +82,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>();
long posixTime = context.RequestData.ReadInt64();
_timeManager.SetupStandardLocalSystemClock(context.Thread, clockContext, posixTime);
ITickSource tickSource = context.Device.System.TickSource;
_timeManager.SetupStandardLocalSystemClock(tickSource, clockContext, posixTime);
return ResultCode.Success;
}
@ -107,7 +111,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
SteadyClockTimePoint steadyClockTimePoint = context.RequestData.ReadStruct<SteadyClockTimePoint>();
_timeManager.SetupStandardUserSystemClock(context.Thread, isAutomaticCorrectionEnabled, steadyClockTimePoint);
ITickSource tickSource = context.Device.System.TickSource;
_timeManager.SetupStandardUserSystemClock(tickSource, isAutomaticCorrectionEnabled, steadyClockTimePoint);
return ResultCode.Success;
}
@ -191,7 +197,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
{
TimeSpanType rtcOffset = context.RequestData.ReadStruct<TimeSpanType>();
_timeManager.SetStandardSteadyClockRtcOffset(context.Thread, rtcOffset);
ITickSource tickSource = context.Device.System.TickSource;
_timeManager.SetStandardSteadyClockRtcOffset(tickSource, rtcOffset);
return ResultCode.Success;
}

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Time.Clock;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
@ -25,7 +26,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
return ResultCode.UninitializedClock;
}
SteadyClockTimePoint currentTimePoint = _steadyClock.GetCurrentTimePoint(context.Thread);
ITickSource tickSource = context.Device.System.TickSource;
SteadyClockTimePoint currentTimePoint = _steadyClock.GetCurrentTimePoint(tickSource);
context.ResponseData.WriteStruct(currentTimePoint);

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
@ -31,7 +32,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
return ResultCode.UninitializedClock;
}
ResultCode result = _clockCore.GetCurrentTime(context.Thread, out long posixTime);
ITickSource tickSource = context.Device.System.TickSource;
ResultCode result = _clockCore.GetCurrentTime(tickSource, out long posixTime);
if (result == ResultCode.Success)
{
@ -57,7 +60,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
long posixTime = context.RequestData.ReadInt64();
return _clockCore.SetCurrentTime(context.Thread, posixTime);
ITickSource tickSource = context.Device.System.TickSource;
return _clockCore.SetCurrentTime(tickSource, posixTime);
}
[CommandHipc(2)]
@ -69,7 +74,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
return ResultCode.UninitializedClock;
}
ResultCode result = _clockCore.GetClockContext(context.Thread, out SystemClockContext clockContext);
ITickSource tickSource = context.Device.System.TickSource;
ResultCode result = _clockCore.GetClockContext(tickSource, out SystemClockContext clockContext);
if (result == ResultCode.Success)
{

View File

@ -7,7 +7,6 @@ using Ryujinx.HLE.Utilities;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
{
@ -44,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
{
return ResultCode.PermissionDenied;
}
return ResultCode.NotImplemented;
}

View File

@ -1,11 +1,10 @@
using System;
using System.IO;
using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.HLE.Utilities;
using System.IO;
namespace Ryujinx.HLE.HOS.Services.Time
{
@ -68,14 +67,13 @@ namespace Ryujinx.HLE.HOS.Services.Time
TimeZone.Initialize(this, device);
}
public void SetupStandardSteadyClock(KThread thread, UInt128 clockSourceId, TimeSpanType setupValue, TimeSpanType internalOffset, TimeSpanType testOffset, bool isRtcResetDetected)
public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType setupValue, TimeSpanType internalOffset, TimeSpanType testOffset, bool isRtcResetDetected)
{
SetupInternalStandardSteadyClock(clockSourceId, setupValue, internalOffset, testOffset, isRtcResetDetected);
TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(thread);
TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(tickSource);
SharedMemory.SetupStandardSteadyClock(thread, clockSourceId, currentTimePoint);
SharedMemory.SetupStandardSteadyClock(tickSource, clockSourceId, currentTimePoint);
// TODO: propagate IPC late binding of "time:s" and "time:p"
}
@ -97,18 +95,18 @@ namespace Ryujinx.HLE.HOS.Services.Time
// TODO: propagate IPC late binding of "time:s" and "time:p"
}
public void SetupStandardLocalSystemClock(KThread thread, SystemClockContext clockContext, long posixTime)
public void SetupStandardLocalSystemClock(ITickSource tickSource, SystemClockContext clockContext, long posixTime)
{
StandardLocalSystemClock.SetUpdateCallbackInstance(LocalClockContextWriter);
SteadyClockTimePoint currentTimePoint = StandardLocalSystemClock.GetSteadyClockCore().GetCurrentTimePoint(thread);
SteadyClockTimePoint currentTimePoint = StandardLocalSystemClock.GetSteadyClockCore().GetCurrentTimePoint(tickSource);
if (currentTimePoint.ClockSourceId == clockContext.SteadyTimePoint.ClockSourceId)
{
StandardLocalSystemClock.SetSystemClockContext(clockContext);
}
else
{
if (StandardLocalSystemClock.SetCurrentTime(thread, posixTime) != ResultCode.Success)
if (StandardLocalSystemClock.SetCurrentTime(tickSource, posixTime) != ResultCode.Success)
{
throw new InternalServiceException("Cannot set current local time");
}
@ -157,9 +155,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
// TODO: propagate IPC late binding of "time:s" and "time:p"
}
public void SetupStandardUserSystemClock(KThread thread, bool isAutomaticCorrectionEnabled, SteadyClockTimePoint steadyClockTimePoint)
public void SetupStandardUserSystemClock(ITickSource tickSource, bool isAutomaticCorrectionEnabled, SteadyClockTimePoint steadyClockTimePoint)
{
if (StandardUserSystemClock.SetAutomaticCorrectionEnabled(thread, isAutomaticCorrectionEnabled) != ResultCode.Success)
if (StandardUserSystemClock.SetAutomaticCorrectionEnabled(tickSource, isAutomaticCorrectionEnabled) != ResultCode.Success)
{
throw new InternalServiceException("Cannot set automatic user time correction state");
}
@ -172,13 +170,13 @@ namespace Ryujinx.HLE.HOS.Services.Time
// TODO: propagate IPC late binding of "time:s" and "time:p"
}
public void SetStandardSteadyClockRtcOffset(KThread thread, TimeSpanType rtcOffset)
public void SetStandardSteadyClockRtcOffset(ITickSource tickSource, TimeSpanType rtcOffset)
{
StandardSteadyClock.SetSetupValue(rtcOffset);
TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(thread);
TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(tickSource);
SharedMemory.SetSteadyClockRawTimePoint(thread, currentTimePoint);
SharedMemory.SetSteadyClockRawTimePoint(tickSource, currentTimePoint);
}
}
}

View File

@ -1,12 +1,11 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.Types;
using Ryujinx.HLE.Utilities;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Time
{
@ -38,19 +37,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
return _sharedMemory;
}
public void SetupStandardSteadyClock(KThread thread, UInt128 clockSourceId, TimeSpanType currentTimePoint)
public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint)
{
TimeSpanType ticksTimeSpan;
// As this may be called before the guest code, we support passing a null thread to make this api usable.
if (thread == null)
{
ticksTimeSpan = TimeSpanType.FromSeconds(0);
}
else
{
ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
}
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
SteadyClockContext context = new SteadyClockContext
{
@ -67,10 +56,10 @@ namespace Ryujinx.HLE.HOS.Services.Time
WriteObjectToSharedMemory(AutomaticCorrectionEnabledOffset, 0, Convert.ToByte(isAutomaticCorrectionEnabled));
}
public void SetSteadyClockRawTimePoint(KThread thread, TimeSpanType currentTimePoint)
public void SetSteadyClockRawTimePoint(ITickSource tickSource, TimeSpanType currentTimePoint)
{
SteadyClockContext context = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4);
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
context.InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds);

View File

@ -7,6 +7,7 @@ using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Time.Clock;
@ -63,7 +64,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
InitializeInstance(device.FileSystem, device.System.ContentManager, device.System.FsIntegrityCheckLevel);
SteadyClockTimePoint timeZoneUpdatedTimePoint = timeManager.StandardSteadyClock.GetCurrentTimePoint(null);
ITickSource tickSource = device.System.TickSource;
SteadyClockTimePoint timeZoneUpdatedTimePoint = timeManager.StandardSteadyClock.GetCurrentTimePoint(tickSource);
string deviceLocationName = SanityCheckDeviceLocationName(device.Configuration.TimeZone);

View File

@ -11,6 +11,9 @@
<ProjectReference Include="..\Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Vic\Ryujinx.Graphics.Vic.csproj" />
<ProjectReference Include="..\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.0-dirty</Version>

View File

@ -0,0 +1,58 @@
using System.Text;
namespace Ryujinx.Horizon.Generators
{
class CodeGenerator
{
private const string Indent = " ";
private readonly StringBuilder _sb;
private string _currentIndent;
public CodeGenerator()
{
_sb = new StringBuilder();
}
public void EnterScope(string header = null)
{
if (header != null)
{
AppendLine(header);
}
AppendLine("{");
IncreaseIndentation();
}
public void LeaveScope()
{
DecreaseIndentation();
AppendLine("}");
}
public void IncreaseIndentation()
{
_currentIndent += Indent;
}
public void DecreaseIndentation()
{
_currentIndent = _currentIndent.Substring(0, _currentIndent.Length - Indent.Length);
}
public void AppendLine()
{
_sb.AppendLine();
}
public void AppendLine(string text)
{
_sb.AppendLine(_currentIndent + text);
}
public override string ToString()
{
return _sb.ToString();
}
}
}

View File

@ -0,0 +1,524 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Ryujinx.Horizon.Generators.Kernel
{
[Generator]
class SyscallGenerator : ISourceGenerator
{
private const string ClassNamespace = "Ryujinx.HLE.HOS.Kernel.SupervisorCall";
private const string ClassName = "SyscallDispatch";
private const string A32Suffix = "32";
private const string A64Suffix = "64";
private const string ResultVariableName = "result";
private const string ArgVariablePrefix = "arg";
private const string ResultCheckHelperName = "LogResultAsTrace";
private const string TypeSystemBoolean = "System.Boolean";
private const string TypeSystemInt32 = "System.Int32";
private const string TypeSystemInt64 = "System.Int64";
private const string TypeSystemUInt32 = "System.UInt32";
private const string TypeSystemUInt64 = "System.UInt64";
private const string NamespaceKernel = "Ryujinx.HLE.HOS.Kernel";
private const string TypeSvcAttribute = NamespaceKernel + ".SupervisorCall.SvcAttribute";
private const string TypePointerSizedAttribute = NamespaceKernel + ".SupervisorCall.PointerSizedAttribute";
private const string TypeKernelResultName = "KernelResult";
private const string TypeKernelResult = NamespaceKernel + ".Common." + TypeKernelResultName;
private const string TypeExecutionContext = "IExecutionContext";
private static readonly string[] _expectedResults = new string[]
{
$"{TypeKernelResultName}.Success",
$"{TypeKernelResultName}.TimedOut",
$"{TypeKernelResultName}.Cancelled",
$"{TypeKernelResultName}.PortRemoteClosed",
$"{TypeKernelResultName}.InvalidState"
};
private struct OutParameter
{
public readonly string Identifier;
public readonly bool NeedsSplit;
public OutParameter(string identifier, bool needsSplit = false)
{
Identifier = identifier;
NeedsSplit = needsSplit;
}
}
private struct RegisterAllocatorA32
{
private uint _useSet;
private int _linearIndex;
public int AllocateSingle()
{
return Allocate();
}
public (int, int) AllocatePair()
{
_linearIndex += _linearIndex & 1;
return (Allocate(), Allocate());
}
private int Allocate()
{
int regIndex;
if (_linearIndex < 4)
{
regIndex = _linearIndex++;
}
else
{
regIndex = -1;
for (int i = 0; i < 32; i++)
{
if ((_useSet & (1 << i)) == 0)
{
regIndex = i;
break;
}
}
Debug.Assert(regIndex != -1);
}
_useSet |= 1u << regIndex;
return regIndex;
}
public void AdvanceLinearIndex()
{
_linearIndex++;
}
}
private struct SyscallIdAndName : IComparable<SyscallIdAndName>
{
public readonly int Id;
public readonly string Name;
public SyscallIdAndName(int id, string name)
{
Id = id;
Name = name;
}
public int CompareTo(SyscallIdAndName other)
{
return Id.CompareTo(other.Id);
}
}
public void Execute(GeneratorExecutionContext context)
{
SyscallSyntaxReceiver syntaxReceiver = (SyscallSyntaxReceiver)context.SyntaxReceiver;
CodeGenerator generator = new CodeGenerator();
generator.AppendLine("using Ryujinx.Common.Logging;");
generator.AppendLine("using Ryujinx.Cpu;");
generator.AppendLine($"using {NamespaceKernel}.Common;");
generator.AppendLine($"using {NamespaceKernel}.Memory;");
generator.AppendLine($"using {NamespaceKernel}.Process;");
generator.AppendLine($"using {NamespaceKernel}.Threading;");
generator.AppendLine("using System;");
generator.AppendLine();
generator.EnterScope($"namespace {ClassNamespace}");
generator.EnterScope($"static class {ClassName}");
GenerateResultCheckHelper(generator);
generator.AppendLine();
List<SyscallIdAndName> syscalls = new List<SyscallIdAndName>();
foreach (var method in syntaxReceiver.SvcImplementations)
{
GenerateMethod32(generator, context.Compilation, method);
GenerateMethod64(generator, context.Compilation, method);
foreach (var attributeList in method.AttributeLists)
{
foreach (var attribute in attributeList.Attributes)
{
if (GetCanonicalTypeName(context.Compilation, attribute) != TypeSvcAttribute)
{
continue;
}
foreach (var attributeArg in attribute.ArgumentList.Arguments)
{
if (attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression)
{
LiteralExpressionSyntax numericLiteral = (LiteralExpressionSyntax)attributeArg.Expression;
syscalls.Add(new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text));
}
}
}
}
}
syscalls.Sort();
GenerateDispatch(generator, syscalls, A32Suffix);
generator.AppendLine();
GenerateDispatch(generator, syscalls, A64Suffix);
generator.LeaveScope();
generator.LeaveScope();
context.AddSource($"{ClassName}.g.cs", generator.ToString());
}
private static void GenerateResultCheckHelper(CodeGenerator generator)
{
generator.EnterScope($"private static bool {ResultCheckHelperName}({TypeKernelResultName} {ResultVariableName})");
string[] expectedChecks = new string[_expectedResults.Length];
for (int i = 0; i < expectedChecks.Length; i++)
{
expectedChecks[i] = $"{ResultVariableName} == {_expectedResults[i]}";
}
string checks = string.Join(" || ", expectedChecks);
generator.AppendLine($"return {checks};");
generator.LeaveScope();
}
private static void GenerateMethod32(CodeGenerator generator, Compilation compilation, MethodDeclarationSyntax method)
{
generator.EnterScope($"private static void {method.Identifier.Text}{A32Suffix}(Syscall syscall, {TypeExecutionContext} context)");
string[] args = new string[method.ParameterList.Parameters.Count];
int index = 0;
RegisterAllocatorA32 regAlloc = new RegisterAllocatorA32();
List<OutParameter> outParameters = new List<OutParameter>();
List<string> logInArgs = new List<string>();
List<string> logOutArgs = new List<string>();
foreach (var methodParameter in method.ParameterList.Parameters)
{
string name = methodParameter.Identifier.Text;
string argName = GetPrefixedArgName(name);
string typeName = methodParameter.Type.ToString();
string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type);
if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword))
{
bool needsSplit = Is64BitInteger(canonicalTypeName) && !IsPointerSized(compilation, methodParameter);
outParameters.Add(new OutParameter(argName, needsSplit));
logOutArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
argName = $"out {typeName} {argName}";
regAlloc.AdvanceLinearIndex();
}
else
{
if (Is64BitInteger(canonicalTypeName))
{
if (IsPointerSized(compilation, methodParameter))
{
int registerIndex = regAlloc.AllocateSingle();
generator.AppendLine($"var {argName} = (uint)context.GetX({registerIndex});");
}
else
{
(int registerIndex, int registerIndex2) = regAlloc.AllocatePair();
string valueLow = $"(ulong)(uint)context.GetX({registerIndex})";
string valueHigh = $"(ulong)(uint)context.GetX({registerIndex2})";
string value = $"{valueLow} | ({valueHigh} << 32)";
generator.AppendLine($"var {argName} = ({typeName})({value});");
}
}
else
{
int registerIndex = regAlloc.AllocateSingle();
string value = GenerateCastFromUInt64($"context.GetX({registerIndex})", canonicalTypeName, typeName);
generator.AppendLine($"var {argName} = {value};");
}
logInArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
}
args[index++] = argName;
}
GenerateLogPrintBeforeCall(generator, method.Identifier.Text, logInArgs);
string returnTypeName = method.ReturnType.ToString();
string argsList = string.Join(", ", args);
int returnRegisterIndex = 0;
string result = null;
string canonicalReturnTypeName = null;
if (returnTypeName != "void")
{
generator.AppendLine($"var {ResultVariableName} = syscall.{method.Identifier.Text}({argsList});");
generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){ResultVariableName});");
canonicalReturnTypeName = GetCanonicalTypeName(compilation, method.ReturnType);
if (Is64BitInteger(canonicalReturnTypeName))
{
generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint)({ResultVariableName} >> 32));");
}
result = GetFormattedLogValue(ResultVariableName, canonicalReturnTypeName);
}
else
{
generator.AppendLine($"syscall.{method.Identifier.Text}({argsList});");
}
foreach (OutParameter outParameter in outParameters)
{
generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){outParameter.Identifier});");
if (outParameter.NeedsSplit)
{
generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint)({outParameter.Identifier} >> 32));");
}
}
while (returnRegisterIndex < 4)
{
generator.AppendLine($"context.SetX({returnRegisterIndex++}, 0);");
}
GenerateLogPrintAfterCall(generator, method.Identifier.Text, logOutArgs, result, canonicalReturnTypeName);
generator.LeaveScope();
generator.AppendLine();
}
private static void GenerateMethod64(CodeGenerator generator, Compilation compilation, MethodDeclarationSyntax method)
{
generator.EnterScope($"private static void {method.Identifier.Text}{A64Suffix}(Syscall syscall, {TypeExecutionContext} context)");
string[] args = new string[method.ParameterList.Parameters.Count];
int registerIndex = 0;
int index = 0;
List<OutParameter> outParameters = new List<OutParameter>();
List<string> logInArgs = new List<string>();
List<string> logOutArgs = new List<string>();
foreach (var methodParameter in method.ParameterList.Parameters)
{
string name = methodParameter.Identifier.Text;
string argName = GetPrefixedArgName(name);
string typeName = methodParameter.Type.ToString();
string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type);
if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword))
{
outParameters.Add(new OutParameter(argName));
logOutArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
argName = $"out {typeName} {argName}";
registerIndex++;
}
else
{
string value = GenerateCastFromUInt64($"context.GetX({registerIndex++})", canonicalTypeName, typeName);
generator.AppendLine($"var {argName} = {value};");
logInArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
}
args[index++] = argName;
}
GenerateLogPrintBeforeCall(generator, method.Identifier.Text, logInArgs);
string argsList = string.Join(", ", args);
int returnRegisterIndex = 0;
string result = null;
string canonicalReturnTypeName = null;
if (method.ReturnType.ToString() != "void")
{
generator.AppendLine($"var {ResultVariableName} = syscall.{method.Identifier.Text}({argsList});");
generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){ResultVariableName});");
canonicalReturnTypeName = GetCanonicalTypeName(compilation, method.ReturnType);
result = GetFormattedLogValue(ResultVariableName, canonicalReturnTypeName);
}
else
{
generator.AppendLine($"syscall.{method.Identifier.Text}({argsList});");
}
foreach (OutParameter outParameter in outParameters)
{
generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){outParameter.Identifier});");
}
while (returnRegisterIndex < 8)
{
generator.AppendLine($"context.SetX({returnRegisterIndex++}, 0);");
}
GenerateLogPrintAfterCall(generator, method.Identifier.Text, logOutArgs, result, canonicalReturnTypeName);
generator.LeaveScope();
generator.AppendLine();
}
private static string GetFormattedLogValue(string value, string canonicalTypeName)
{
if (Is32BitInteger(canonicalTypeName))
{
return $"0x{{{value}:X8}}";
}
else if (Is64BitInteger(canonicalTypeName))
{
return $"0x{{{value}:X16}}";
}
return $"{{{value}}}";
}
private static string GetPrefixedArgName(string name)
{
return ArgVariablePrefix + name[0].ToString().ToUpperInvariant() + name.Substring(1);
}
private static string GetCanonicalTypeName(Compilation compilation, SyntaxNode syntaxNode)
{
TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode);
if (typeInfo.Type.ContainingNamespace == null)
{
return typeInfo.Type.Name;
}
return $"{typeInfo.Type.ContainingNamespace.ToDisplayString()}.{typeInfo.Type.Name}";
}
private static void GenerateLogPrintBeforeCall(CodeGenerator generator, string methodName, List<string> argList)
{
string log = $"{methodName}({string.Join(", ", argList)})";
GenerateLogPrint(generator, "Trace", "KernelSvc", log);
}
private static void GenerateLogPrintAfterCall(
CodeGenerator generator,
string methodName,
List<string> argList,
string result,
string canonicalResultTypeName)
{
string log = $"{methodName}({string.Join(", ", argList)})";
if (result != null)
{
log += $" = {result}";
}
if (canonicalResultTypeName == TypeKernelResult)
{
generator.EnterScope($"if ({ResultCheckHelperName}({ResultVariableName}))");
GenerateLogPrint(generator, "Trace", "KernelSvc", log);
generator.LeaveScope();
generator.EnterScope("else");
GenerateLogPrint(generator, "Warning", "KernelSvc", log);
generator.LeaveScope();
}
else
{
GenerateLogPrint(generator, "Trace", "KernelSvc", log);
}
}
private static void GenerateLogPrint(CodeGenerator generator, string logLevel, string logClass, string log)
{
generator.AppendLine($"Logger.{logLevel}?.PrintMsg(LogClass.{logClass}, $\"{log}\");");
}
private static void GenerateDispatch(CodeGenerator generator, List<SyscallIdAndName> syscalls, string suffix)
{
generator.EnterScope($"public static void Dispatch{suffix}(Syscall syscall, {TypeExecutionContext} context, int id)");
generator.EnterScope("switch (id)");
foreach (var syscall in syscalls)
{
generator.AppendLine($"case {syscall.Id}:");
generator.IncreaseIndentation();
generator.AppendLine($"{syscall.Name}{suffix}(syscall, context);");
generator.AppendLine("break;");
generator.DecreaseIndentation();
}
generator.AppendLine($"default:");
generator.IncreaseIndentation();
generator.AppendLine("throw new NotImplementedException($\"SVC 0x{id:X4} is not implemented.\");");
generator.DecreaseIndentation();
generator.LeaveScope();
generator.LeaveScope();
}
private static bool Is32BitInteger(string canonicalTypeName)
{
return canonicalTypeName == TypeSystemInt32 || canonicalTypeName == TypeSystemUInt32;
}
private static bool Is64BitInteger(string canonicalTypeName)
{
return canonicalTypeName == TypeSystemInt64 || canonicalTypeName == TypeSystemUInt64;
}
private static string GenerateCastFromUInt64(string value, string canonicalTargetTypeName, string targetTypeName)
{
if (canonicalTargetTypeName == TypeSystemBoolean)
{
return $"({value} & 1) != 0";
}
return $"({targetTypeName}){value}";
}
private static bool IsPointerSized(Compilation compilation, ParameterSyntax parameterSyntax)
{
foreach (var attributeList in parameterSyntax.AttributeLists)
{
foreach (var attribute in attributeList.Attributes)
{
if (GetCanonicalTypeName(compilation, attribute) == TypePointerSizedAttribute)
{
return true;
}
}
}
return false;
}
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new SyscallSyntaxReceiver());
}
}
}

View File

@ -0,0 +1,54 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Horizon.Generators.Kernel
{
class SyscallSyntaxReceiver : ISyntaxReceiver
{
public List<MethodDeclarationSyntax> SvcImplementations { get; }
public SyscallSyntaxReceiver()
{
SvcImplementations = new List<MethodDeclarationSyntax>();
}
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Count != 0)
{
foreach (var attributeList in classDeclaration.AttributeLists)
{
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "SvcImpl"))
{
foreach (var memberDeclaration in classDeclaration.Members)
{
if (memberDeclaration is MethodDeclarationSyntax methodDeclaration)
{
VisitMethod(methodDeclaration);
}
}
break;
}
}
}
}
private void VisitMethod(MethodDeclarationSyntax methodDeclaration)
{
if (methodDeclaration.AttributeLists.Count != 0)
{
foreach (var attributeList in methodDeclaration.AttributeLists)
{
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "Svc"))
{
SvcImplementations.Add(methodDeclaration);
break;
}
}
}
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" />
</ItemGroup>
</Project>

View File

@ -19,6 +19,8 @@ namespace Ryujinx.Memory
private ConcurrentDictionary<MemoryBlock, byte> _viewStorages;
private int _viewCount;
internal bool ForceWindows4KBView => _forceWindows4KBView;
/// <summary>
/// Pointer to the memory block data.
/// </summary>
@ -145,7 +147,7 @@ namespace Ryujinx.Memory
srcBlock.IncrementViewCount();
}
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, _forceWindows4KBView);
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, this);
}
/// <summary>
@ -156,7 +158,7 @@ namespace Ryujinx.Memory
/// <param name="size">Size of the range to be unmapped</param>
public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size)
{
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, _forceWindows4KBView);
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, this);
}
/// <summary>

View File

@ -68,17 +68,17 @@ namespace Ryujinx.Memory
}
}
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, bool force4KBMap)
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, MemoryBlock owner)
{
if (OperatingSystem.IsWindows())
{
if (force4KBMap)
if (owner.ForceWindows4KBView)
{
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size);
}
else
{
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size);
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner);
}
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@ -91,17 +91,17 @@ namespace Ryujinx.Memory
}
}
public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, bool force4KBMap)
public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, MemoryBlock owner)
{
if (OperatingSystem.IsWindows())
{
if (force4KBMap)
if (owner.ForceWindows4KBView)
{
MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size);
}
else
{
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size);
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner);
}
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())

View File

@ -68,9 +68,9 @@ namespace Ryujinx.Memory
return WindowsApi.VirtualFree(location, size, AllocationType.Decommit);
}
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner)
{
_placeholders.MapView(sharedMemory, srcOffset, location, size);
_placeholders.MapView(sharedMemory, srcOffset, location, size, owner);
}
public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
@ -106,9 +106,9 @@ namespace Ryujinx.Memory
}
}
public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size)
public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
{
_placeholders.UnmapView(sharedMemory, location, size);
_placeholders.UnmapView(sharedMemory, location, size, owner);
}
public static void UnmapView4KB(IntPtr location, IntPtr size)
@ -154,7 +154,7 @@ namespace Ryujinx.Memory
}
else
{
_placeholders.UnmapView(IntPtr.Zero, address, size);
_placeholders.UnreserveRange((ulong)address, (ulong)size);
}
return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release);

View File

@ -44,6 +44,50 @@ namespace Ryujinx.Memory.WindowsShared
}
}
/// <summary>
/// Unreserves a range of memory that has been previously reserved with <see cref="ReserveRange"/>.
/// </summary>
/// <param name="address">Start address of the region to unreserve</param>
/// <param name="size">Size in bytes of the region to unreserve</param>
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unreserving the memory</exception>
public void UnreserveRange(ulong address, ulong size)
{
ulong endAddress = address + size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int count;
lock (_mappings)
{
count = _mappings.Get(address, endAddress, ref overlaps);
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
if (IsMapped(overlap.Value))
{
if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
{
throw new WindowsApiException("UnmapViewOfFile2");
}
}
_mappings.Remove(overlap);
}
}
if (count > 1)
{
CheckFreeResult(WindowsApi.VirtualFree(
(IntPtr)address,
(IntPtr)size,
AllocationType.Release | AllocationType.CoalescePlaceholders));
}
RemoveProtection(address, size);
}
/// <summary>
/// Maps a shared memory view on a previously reserved memory region.
/// </summary>
@ -51,13 +95,14 @@ namespace Ryujinx.Memory.WindowsShared
/// <param name="srcOffset">Offset in the shared memory to map</param>
/// <param name="location">Address to map the view into</param>
/// <param name="size">Size of the view in bytes</param>
public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
/// <param name="owner">Memory block that owns the mapping</param>
public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner)
{
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
try
{
UnmapViewInternal(sharedMemory, location, size);
UnmapViewInternal(sharedMemory, location, size, owner);
MapViewInternal(sharedMemory, srcOffset, location, size);
}
finally
@ -173,13 +218,14 @@ namespace Ryujinx.Memory.WindowsShared
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
/// <param name="location">Address to unmap</param>
/// <param name="size">Size of the region to unmap in bytes</param>
public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size)
/// <param name="owner">Memory block that owns the mapping</param>
public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
{
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
try
{
UnmapViewInternal(sharedMemory, location, size);
UnmapViewInternal(sharedMemory, location, size, owner);
}
finally
{
@ -197,8 +243,9 @@ namespace Ryujinx.Memory.WindowsShared
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
/// <param name="location">Address to unmap</param>
/// <param name="size">Size of the region to unmap in bytes</param>
/// <param name="owner">Memory block that owns the mapping</param>
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception>
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size)
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
@ -272,7 +319,7 @@ namespace Ryujinx.Memory.WindowsShared
}
}
CoalesceForUnmap(startAddress, unmapSize);
CoalesceForUnmap(startAddress, unmapSize, owner);
RemoveProtection(startAddress, unmapSize);
}
@ -281,15 +328,21 @@ namespace Ryujinx.Memory.WindowsShared
/// </summary>
/// <param name="address">Address of the region that was unmapped</param>
/// <param name="size">Size of the region that was unmapped in bytes</param>
private void CoalesceForUnmap(ulong address, ulong size)
/// <param name="owner">Memory block that owns the mapping</param>
private void CoalesceForUnmap(ulong address, ulong size, MemoryBlock owner)
{
ulong endAddress = address + size;
ulong blockAddress = (ulong)owner.Pointer;
ulong blockEnd = blockAddress + owner.Size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int unmappedCount = 0;
lock (_mappings)
{
int count = _mappings.Get(address - MinimumPageSize, endAddress + MinimumPageSize, ref overlaps);
int count = _mappings.Get(
Math.Max(address - MinimumPageSize, blockAddress),
Math.Min(endAddress + MinimumPageSize, blockEnd), ref overlaps);
if (count < 2)
{
// Nothing to coalesce if we only have 1 or no overlaps.

View File

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>

Some files were not shown because too many files have changed in this diff Show More