From cf6cd714884c41e9550757e364c2f4f5b04fc7f3 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 1 Dec 2020 20:23:43 -0300 Subject: [PATCH] IPC refactor part 2: Use ReplyAndReceive on HLE services and remove special handling from kernel (#1458) * IPC refactor part 2: Use ReplyAndReceive on HLE services and remove special handling from kernel * Fix for applet transfer memory + some nits * Keep handles if possible to avoid server handle table exhaustion * Fix IPC ZeroFill bug * am: Correctly implement CreateManagedDisplayLayer and implement CreateManagedDisplaySeparableLayer CreateManagedDisplaySeparableLayer is requires since 10.x+ when appletResourceUserId != 0 * Make it exit properly * Make ServiceNotImplementedException show the full message again * Allow yielding execution to avoid starving other threads * Only wait if active * Merge IVirtualMemoryManager and IAddressSpaceManager * Fix Ro loading data from the wrong process Co-authored-by: Thog --- ARMeilleure/State/ExecutionContext.cs | 2 +- .../Dsp/Command/AuxiliaryBufferCommand.cs | 27 +- .../Dsp/Command/CommandList.cs | 6 +- .../Dsp/DataSourceHelper.cs | 4 +- .../Dsp/State/AuxiliaryBufferHeader.cs | 10 +- .../Ryujinx.Audio.Renderer.csproj | 1 + .../Server/AudioRenderSystem.cs | 20 +- .../Server/AudioRendererManager.cs | 12 +- Ryujinx.Cpu/MemoryHelper.cs | 11 +- Ryujinx.Cpu/MemoryManager.cs | 7 +- Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 3 +- .../ServiceNotImplementedException.cs | 20 +- Ryujinx.HLE/HOS/ArmProcessContext.cs | 24 + Ryujinx.HLE/HOS/ArmProcessContextFactory.cs | 14 + Ryujinx.HLE/HOS/Horizon.cs | 20 +- Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 23 +- Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs | 27 + Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs | 6 + Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs | 11 + .../HOS/Kernel/Ipc/KBufferDescriptorTable.cs | 4 +- Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs | 9 - Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs | 9 - Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs | 34 +- Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs | 15 +- Ryujinx.HLE/HOS/Kernel/KernelContext.cs | 2 +- Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 38 ++ Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs | 16 +- Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs | 8 +- .../HOS/Kernel/Memory/KMemoryManager.cs | 327 ++++++----- .../HOS/Kernel/Memory/KSharedMemory.cs | 12 +- .../HOS/Kernel/Memory/KTransferMemory.cs | 7 +- .../HOS/Kernel/Memory/MemoryPermission.cs | 2 +- .../HOS/Kernel/Process/HleProcessDebugger.cs | 10 +- .../HOS/Kernel/Process/IProcessContext.cs | 13 + .../Kernel/Process/IProcessContextFactory.cs | 10 + Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 234 ++++---- .../Kernel/Process/KProcessCapabilities.cs | 27 +- .../HOS/Kernel/Process/ProcessContext.cs | 25 + .../Kernel/Process/ProcessContextFactory.cs | 13 + .../Kernel/Process/ProcessCreationFlags.cs | 38 ++ .../HOS/Kernel/Process/ProcessCreationInfo.cs | 46 +- .../HOS/Kernel/SupervisorCall/Syscall.cs | 287 ++++++--- .../HOS/Kernel/SupervisorCall/Syscall32.cs | 10 +- .../HOS/Kernel/SupervisorCall/Syscall64.cs | 10 +- .../HOS/Kernel/Threading/KScheduler.cs | 2 + Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | 90 +-- Ryujinx.HLE/HOS/ProgramLoader.cs | 30 +- Ryujinx.HLE/HOS/ServiceCtx.cs | 52 +- .../ISystemAppletProxy.cs | 11 +- .../ILibraryAppletAccessor.cs | 37 +- .../SystemAppletProxy/ICommonStateGetter.cs | 28 +- .../SystemAppletProxy/IHomeMenuFunctions.cs | 10 +- .../ILibraryAppletCreator.cs | 4 +- .../SystemAppletProxy/ISelfController.cs | 42 +- .../SystemAppletProxy/IWindowController.cs | 11 +- .../IAllSystemAppletProxiesService.cs | 2 +- .../ApplicationProxy/IApplicationFunctions.cs | 43 +- .../IApplicationProxy.cs | 11 +- .../Am/AppletOE/IApplicationProxyService.cs | 2 +- .../Audio/AudioOutManager/IAudioOut.cs | 22 +- .../Services/Audio/AudioRendererManager.cs | 4 +- .../Audio/AudioRendererManagerServer.cs | 6 +- .../HOS/Services/Audio/IAudioInManager.cs | 3 +- .../HOS/Services/Audio/IAudioOutManager.cs | 4 +- .../Audio/IHardwareOpusDecoderManager.cs | 3 + .../IDeliveryCacheProgressService.cs | 10 +- .../HOS/Services/Fs/IFileSystemProxy.cs | 22 +- .../Services/Hid/HidServer/IAppletResource.cs | 10 +- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 24 +- Ryujinx.HLE/HOS/Services/IpcService.cs | 62 +- .../HOS/Services/Nfc/Nfp/UserManager/IUser.cs | 32 +- .../Services/Nifm/StaticService/IRequest.cs | 21 +- .../HOS/Services/Nim/IShopServiceAccessor.cs | 11 +- .../HOS/Services/Ns/IAddOnContentManager.cs | 17 +- Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 67 ++- .../Services/Nv/NvDrvServices/NvDeviceFile.cs | 7 +- .../NvHostAsGpu/NvHostAsGpuDeviceFile.cs | 3 +- .../NvHostChannel/NvHostChannelDeviceFile.cs | 9 +- .../NvHostChannel/NvHostGpuDeviceFile.cs | 5 +- .../NvHostCtrl/NvHostCtrlDeviceFile.cs | 6 +- .../NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs | 5 +- .../Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs | 24 +- Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs | 37 +- .../HOS/Services/Sdb/Pl/ISharedFontManager.cs | 11 +- Ryujinx.HLE/HOS/Services/ServerBase.cs | 237 ++++++-- Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 28 +- .../HOS/Services/Sockets/Bsd/IClient.cs | 5 +- .../HOS/Services/Sockets/Nsd/IManager.cs | 20 +- .../Services/SurfaceFlinger/BufferQueue.cs | 12 +- .../SurfaceFlinger/BufferQueueCore.cs | 45 +- .../SurfaceFlinger/BufferQueueProducer.cs | 5 + .../SurfaceFlinger/IHOSBinderDriver.cs | 2 +- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 26 +- .../SurfaceFlinger/Types/GraphicBuffer.cs | 12 +- .../Services/Time/IStaticServiceForGlue.cs | 4 +- .../HOS/Services/Time/ITimeServiceManager.cs | 12 +- .../Services/Vi/IApplicationRootService.cs | 3 +- .../HOS/Services/Vi/IManagerRootService.cs | 2 +- .../HOS/Services/Vi/ISystemRootService.cs | 2 +- .../IManagerDisplayService.cs | 12 +- .../ISystemDisplayService.cs | 2 +- .../RootService/IApplicationDisplayService.cs | 21 +- Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs | 6 +- Ryujinx.HLE/Loaders/Npdm/Npdm.cs | 6 +- Ryujinx.HLE/Switch.cs | 1 + Ryujinx.HLE/Utilities/StructReader.cs | 5 +- Ryujinx.HLE/Utilities/StructWriter.cs | 5 +- .../MockVirtualMemoryManager.cs | 58 +- Ryujinx.Memory/AddressSpaceManager.cs | 549 ++++++++++++++++++ Ryujinx.Memory/IVirtualMemoryManager.cs | 28 + .../MemoryNotContiguousException.cs | 4 +- .../Tracking/IVirtualMemoryManager.cs | 10 - .../WritableRegion.cs | 6 +- Ryujinx/Ui/GLRenderer.cs | 60 +- 115 files changed, 2356 insertions(+), 1088 deletions(-) create mode 100644 Ryujinx.HLE/HOS/ArmProcessContext.cs create mode 100644 Ryujinx.HLE/HOS/ArmProcessContextFactory.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KernelStatic.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs create mode 100644 Ryujinx.Memory/AddressSpaceManager.cs create mode 100644 Ryujinx.Memory/IVirtualMemoryManager.cs rename {Ryujinx.Cpu => Ryujinx.Memory}/MemoryNotContiguousException.cs (82%) delete mode 100644 Ryujinx.Memory/Tracking/IVirtualMemoryManager.cs rename {Ryujinx.Cpu => Ryujinx.Memory}/WritableRegion.cs (74%) diff --git a/ARMeilleure/State/ExecutionContext.cs b/ARMeilleure/State/ExecutionContext.cs index 9f89bff45..a964f6baa 100644 --- a/ARMeilleure/State/ExecutionContext.cs +++ b/ARMeilleure/State/ExecutionContext.cs @@ -66,7 +66,7 @@ namespace ARMeilleure.State } } - internal bool Running { get; private set; } + public bool Running { get; private set; } public event EventHandler Interrupt; public event EventHandler Break; diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs b/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs index f63722c12..46aff27e7 100644 --- a/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs +++ b/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs @@ -16,7 +16,7 @@ // using Ryujinx.Audio.Renderer.Common; -using Ryujinx.Cpu; +using Ryujinx.Memory; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -65,7 +65,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command IsEffectEnabled = isEnabled; } - private uint Read(MemoryManager memoryManager, ulong bufferAddress, uint countMax, Span outBuffer, uint count, uint readOffset, uint updateCount) + private uint Read(IVirtualMemoryManager memoryManager, ulong bufferAddress, uint countMax, Span outBuffer, uint count, uint readOffset, uint updateCount) { if (countMax == 0 || bufferAddress == 0) { @@ -104,7 +104,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command return count; } - private uint Write(MemoryManager memoryManager, ulong outBufferAddress, uint countMax, ReadOnlySpan buffer, uint count, uint writeOffset, uint updateCount) + private uint Write(IVirtualMemoryManager memoryManager, ulong outBufferAddress, uint countMax, ReadOnlySpan buffer, uint count, uint writeOffset, uint updateCount) { if (countMax == 0 || outBufferAddress == 0) { @@ -175,8 +175,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } else { - MemoryHelper.FillWithZeros(context.MemoryManager, (long)BufferInfo.SendBufferInfo, Unsafe.SizeOf()); - MemoryHelper.FillWithZeros(context.MemoryManager, (long)BufferInfo.ReturnBufferInfo, Unsafe.SizeOf()); + ZeroFill(context.MemoryManager, BufferInfo.SendBufferInfo, Unsafe.SizeOf()); + ZeroFill(context.MemoryManager, BufferInfo.ReturnBufferInfo, Unsafe.SizeOf()); if (InputBufferIndex != OutputBufferIndex) { @@ -184,5 +184,22 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } } } + + private static void ZeroFill(IVirtualMemoryManager memoryManager, ulong address, int size) + { + ulong endAddress = address + (ulong)size; + + while (address + 7UL < endAddress) + { + memoryManager.Write(address, 0UL); + address += 8; + } + + while (address < endAddress) + { + memoryManager.Write(address, (byte)0); + address++; + } + } } } diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs b/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs index d65f6ceda..4b80c93b2 100644 --- a/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs +++ b/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs @@ -19,7 +19,7 @@ using Ryujinx.Audio.Renderer.Integration; using Ryujinx.Audio.Renderer.Server; using Ryujinx.Common; using Ryujinx.Common.Logging; -using Ryujinx.Cpu; +using Ryujinx.Memory; using System; using System.Collections.Generic; @@ -37,7 +37,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public List Commands { get; } - public MemoryManager MemoryManager { get; } + public IVirtualMemoryManager MemoryManager { get; } public HardwareDevice OutputDevice { get; private set; } @@ -50,7 +50,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { } - public CommandList(MemoryManager memoryManager, Memory mixBuffer, uint sampleCount, uint sampleRate, uint mixBufferCount, uint voiceChannelCountMax) + public CommandList(IVirtualMemoryManager memoryManager, Memory mixBuffer, uint sampleCount, uint sampleRate, uint mixBufferCount, uint voiceChannelCountMax) { SampleCount = sampleCount; SampleRate = sampleRate; diff --git a/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs b/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs index ccc97b924..6e270df45 100644 --- a/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs +++ b/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs @@ -18,7 +18,7 @@ using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Dsp.State; using Ryujinx.Common.Logging; -using Ryujinx.Cpu; +using Ryujinx.Memory; using System; using System.Buffers; using System.Diagnostics; @@ -63,7 +63,7 @@ namespace Ryujinx.Audio.Renderer.Dsp } } - public static void ProcessWaveBuffers(MemoryManager memoryManager, Span outputBuffer, WaveBufferInformation info, uint targetSampleRate, int sampleCount) + public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span outputBuffer, WaveBufferInformation info, uint targetSampleRate, int sampleCount) { const int tempBufferSize = 0x3F00; diff --git a/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs b/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs index eebbac53e..3923a490d 100644 --- a/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs +++ b/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs @@ -15,7 +15,7 @@ // along with this program. If not, see . // -using Ryujinx.Cpu; +using Ryujinx.Memory; using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Dsp.State @@ -33,22 +33,22 @@ namespace Ryujinx.Audio.Renderer.Dsp.State public uint WriteOffset; private uint _reserved; - public static uint GetReadOffset(MemoryManager manager, ulong bufferAddress) + public static uint GetReadOffset(IVirtualMemoryManager manager, ulong bufferAddress) { return manager.Read(bufferAddress + ReadOffsetPosition); } - public static uint GetWriteOffset(MemoryManager manager, ulong bufferAddress) + public static uint GetWriteOffset(IVirtualMemoryManager manager, ulong bufferAddress) { return manager.Read(bufferAddress + WriteOffsetPosition); } - public static void SetReadOffset(MemoryManager manager, ulong bufferAddress, uint value) + public static void SetReadOffset(IVirtualMemoryManager manager, ulong bufferAddress, uint value) { manager.Write(bufferAddress + ReadOffsetPosition, value); } - public static void SetWriteOffset(MemoryManager manager, ulong bufferAddress, uint value) + public static void SetWriteOffset(IVirtualMemoryManager manager, ulong bufferAddress, uint value) { manager.Write(bufferAddress + WriteOffsetPosition, value); } diff --git a/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj b/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj index eac49fb3a..ccdeae3e6 100644 --- a/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj +++ b/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj @@ -8,6 +8,7 @@ + diff --git a/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs index 30f326a6d..a59252847 100644 --- a/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs +++ b/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs @@ -31,7 +31,7 @@ using Ryujinx.Audio.Renderer.Server.Voice; using Ryujinx.Audio.Renderer.Utils; using Ryujinx.Common; using Ryujinx.Common.Logging; -using Ryujinx.Cpu; +using Ryujinx.Memory; using System; using System.Buffers; using System.Diagnostics; @@ -87,7 +87,7 @@ namespace Ryujinx.Audio.Renderer.Server private Memory _performanceBuffer; - public MemoryManager MemoryManager { get; private set; } + public IVirtualMemoryManager MemoryManager { get; private set; } private ulong _elapsedFrameCount; private ulong _renderingStartTick; @@ -96,14 +96,14 @@ namespace Ryujinx.Audio.Renderer.Server public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent) { - _manager = manager; - _terminationEvent = new ManualResetEvent(false); + _manager = manager; + _terminationEvent = new ManualResetEvent(false); _dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); - _voiceContext = new VoiceContext(); - _mixContext = new MixContext(); - _sinkContext = new SinkContext(); - _splitterContext = new SplitterContext(); - _effectContext = new EffectContext(); + _voiceContext = new VoiceContext(); + _mixContext = new MixContext(); + _sinkContext = new SinkContext(); + _splitterContext = new SplitterContext(); + _effectContext = new EffectContext(); _commandProcessingTimeEstimator = null; _systemEvent = systemEvent; @@ -113,7 +113,7 @@ namespace Ryujinx.Audio.Renderer.Server _sessionId = 0; } - public ResultCode Initialize(ref AudioRendererConfiguration parameter, uint processHandle, CpuAddress workBuffer, ulong workBufferSize, int sessionId, ulong appletResourceId, MemoryManager memoryManager) + public ResultCode Initialize(ref AudioRendererConfiguration parameter, uint processHandle, CpuAddress workBuffer, ulong workBufferSize, int sessionId, ulong appletResourceId, IVirtualMemoryManager memoryManager) { if (!BehaviourContext.CheckValidRevision(parameter.Revision)) { diff --git a/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs b/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs index 023dd477c..3df0ab31e 100644 --- a/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs +++ b/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs @@ -19,7 +19,7 @@ using Ryujinx.Audio.Renderer.Dsp; using Ryujinx.Audio.Renderer.Integration; using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Common.Logging; -using Ryujinx.Cpu; +using Ryujinx.Memory; using System; using System.Diagnostics; using System.Threading; @@ -288,7 +288,7 @@ namespace Ryujinx.Audio.Renderer.Server /// The guest work buffer size. /// The process handle of the application. /// A reporting an error or a success. - public ResultCode OpenAudioRenderer(out AudioRenderSystem renderer, MemoryManager memoryManager, ref AudioRendererConfiguration parameter, ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, uint processHandle) + public ResultCode OpenAudioRenderer(out AudioRenderSystem renderer, IVirtualMemoryManager memoryManager, ref AudioRendererConfiguration parameter, ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, uint processHandle) { int sessionId = AcquireSessionId(); @@ -321,6 +321,14 @@ namespace Ryujinx.Audio.Renderer.Server { if (disposing) { + lock (_audioProcessorLock) + { + if (_isRunning) + { + StopLocked(); + } + } + Processor.Dispose(); foreach (HardwareDevice device in OutputDevices) diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs index 5ba5ef38e..8ef4bc667 100644 --- a/Ryujinx.Cpu/MemoryHelper.cs +++ b/Ryujinx.Cpu/MemoryHelper.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.Memory; +using System; using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -7,7 +8,7 @@ namespace Ryujinx.Cpu { public static class MemoryHelper { - public static void FillWithZeros(MemoryManager memory, long position, int size) + public static void FillWithZeros(IVirtualMemoryManager memory, long position, int size) { int size8 = size & ~(8 - 1); @@ -22,7 +23,7 @@ namespace Ryujinx.Cpu } } - public unsafe static T Read(MemoryManager memory, long position) where T : struct + public unsafe static T Read(IVirtualMemoryManager memory, long position) where T : struct { long size = Marshal.SizeOf(); @@ -36,7 +37,7 @@ namespace Ryujinx.Cpu } } - public unsafe static void Write(MemoryManager memory, long position, T value) where T : struct + public unsafe static void Write(IVirtualMemoryManager memory, long position, T value) where T : struct { long size = Marshal.SizeOf(); @@ -50,7 +51,7 @@ namespace Ryujinx.Cpu memory.Write((ulong)position, data); } - public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1) + public static string ReadAsciiString(IVirtualMemoryManager memory, long position, long maxSize = -1) { using (MemoryStream ms = new MemoryStream()) { diff --git a/Ryujinx.Cpu/MemoryManager.cs b/Ryujinx.Cpu/MemoryManager.cs index 3fa08fe75..36ae9b327 100644 --- a/Ryujinx.Cpu/MemoryManager.cs +++ b/Ryujinx.Cpu/MemoryManager.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Cpu /// /// Represents a CPU memory manager. /// - public sealed class MemoryManager : IMemoryManager, IDisposable, IVirtualMemoryManager + public sealed class MemoryManager : IMemoryManager, IVirtualMemoryManager, IDisposable { public const int PageBits = 12; public const int PageSize = 1 << PageBits; @@ -468,6 +468,11 @@ namespace Ryujinx.Cpu /// True if the entire range is mapped, false otherwise public bool IsRangeMapped(ulong va, ulong size) { + if (size == 0UL) + { + return true; + } + ulong endVa = (va + size + PageMask) & ~(ulong)PageMask; va &= ~(ulong)PageMask; diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 517dacef5..91575e205 100644 --- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -1,4 +1,4 @@ -using Ryujinx.Cpu; +using Ryujinx.Memory; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index 3ebf2fd79..de41fb9ab 100644 --- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -1,5 +1,6 @@ using Ryujinx.Cpu; using Ryujinx.Cpu.Tracking; +using Ryujinx.Memory; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -12,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// class PhysicalMemory { - public const int PageSize = Cpu.MemoryManager.PageSize; + public const int PageSize = 0x1000; private readonly Cpu.MemoryManager _cpuMemory; diff --git a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs index 1be7609bb..58c15fdde 100644 --- a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs +++ b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs @@ -14,28 +14,31 @@ namespace Ryujinx.HLE.Exceptions [Serializable] internal class ServiceNotImplementedException : Exception { + public IIpcService Service { get; } public ServiceCtx Context { get; } public IpcMessage Request { get; } - public ServiceNotImplementedException(ServiceCtx context) - : this(context, "The service call is not implemented.") + public ServiceNotImplementedException(IIpcService service, ServiceCtx context) + : this(service, context, "The service call is not implemented.") { } - public ServiceNotImplementedException(ServiceCtx context, string message) + public ServiceNotImplementedException(IIpcService service, ServiceCtx context, string message) : base(message) { + Service = service; Context = context; Request = context.Request; } - public ServiceNotImplementedException(ServiceCtx context, string message, Exception inner) + public ServiceNotImplementedException(IIpcService service, ServiceCtx context, string message, Exception inner) : base(message, inner) { + Service = service; Context = context; Request = context.Request; } - protected ServiceNotImplementedException(SerializationInfo info, StreamingContext context) + protected ServiceNotImplementedException(SerializationInfo info, StreamingContext context) : base(info, context) { } @@ -59,17 +62,16 @@ namespace Ryujinx.HLE.Exceptions if (callingType != null && callingMethod != null) { - var ipcService = Context.Session.Service; - var ipcCommands = ipcService.Commands; + var ipcCommands = Service.Commands; // Find the handler for the method called - var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value as MethodBase == callingMethod); + var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod); var ipcCommandId = ipcHandler.Key; var ipcMethod = ipcHandler.Value; if (ipcMethod != null) { - sb.AppendLine($"Service Command: {ipcService.GetType().FullName}: {ipcCommandId} ({ipcMethod.Name})"); + sb.AppendLine($"Service Command: {Service.GetType().FullName}: {ipcCommandId} ({ipcMethod.Name})"); sb.AppendLine(); } } diff --git a/Ryujinx.HLE/HOS/ArmProcessContext.cs b/Ryujinx.HLE/HOS/ArmProcessContext.cs new file mode 100644 index 000000000..fb7703b79 --- /dev/null +++ b/Ryujinx.HLE/HOS/ArmProcessContext.cs @@ -0,0 +1,24 @@ +using ARMeilleure.State; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Memory; + +namespace Ryujinx.HLE.HOS +{ + class ArmProcessContext : IProcessContext + { + private readonly MemoryManager _memoryManager; + private readonly CpuContext _cpuContext; + + public IVirtualMemoryManager AddressSpace => _memoryManager; + + public ArmProcessContext(MemoryManager memoryManager) + { + _memoryManager = memoryManager; + _cpuContext = new CpuContext(memoryManager); + } + + public void Execute(ExecutionContext context, ulong codeAddress) => _cpuContext.Execute(context, codeAddress); + public void Dispose() => _memoryManager.Dispose(); + } +} diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs new file mode 100644 index 000000000..050d3690e --- /dev/null +++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -0,0 +1,14 @@ +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Memory; + +namespace Ryujinx.HLE.HOS +{ + class ArmProcessContextFactory : IProcessContextFactory + { + public IProcessContext Create(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler) + { + return new ArmProcessContext(new MemoryManager(backingMemory, addressSpaceSize, invalidAccessHandler)); + } + } +} diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index b7e76a721..d4b49f451 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -120,11 +120,11 @@ namespace Ryujinx.HLE.HOS iirsPageList.AddRange(iirsPa, IirsSize / KMemoryManager.PageSize); timePageList.AddRange(timePa, TimeSize / KMemoryManager.PageSize); - HidSharedMem = new KSharedMemory(KernelContext, hidPageList, 0, 0, MemoryPermission.Read); - FontSharedMem = new KSharedMemory(KernelContext, fontPageList, 0, 0, MemoryPermission.Read); - IirsSharedMem = new KSharedMemory(KernelContext, iirsPageList, 0, 0, MemoryPermission.Read); + HidSharedMem = new KSharedMemory(KernelContext, hidPageList, 0, 0, KMemoryPermission.Read); + FontSharedMem = new KSharedMemory(KernelContext, fontPageList, 0, 0, KMemoryPermission.Read); + IirsSharedMem = new KSharedMemory(KernelContext, iirsPageList, 0, 0, KMemoryPermission.Read); - KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timePageList, 0, 0, MemoryPermission.Read); + KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timePageList, 0, 0, KMemoryPermission.Read); TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timePa - DramMemoryMap.DramBase, TimeSize); @@ -134,8 +134,6 @@ namespace Ryujinx.HLE.HOS Font = new SharedFontManager(device, fontPa - DramMemoryMap.DramBase); - IUserInterface.InitializePort(this); - VsyncEvent = new KEvent(KernelContext); DisplayResolutionChangeEvent = new KEvent(KernelContext); @@ -224,6 +222,16 @@ namespace Ryujinx.HLE.HOS AudioRendererManager.Initialize(writableEvents, devices); } + public void InitializeServices() + { + IUserInterface sm = new IUserInterface(KernelContext); + + // Wait until SM server thread is done with initialization, + // only then doing connections to SM is safe. + sm.Server.InitDone.WaitOne(); + sm.Server.InitDone.Dispose(); + } + public void LoadKip(string kipPath) { using IStorage kipFile = new LocalStorage(kipPath, FileAccess.Read); diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 012c3167a..135a0a1fe 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -84,6 +84,11 @@ namespace Ryujinx.HLE.HOS.Ipc long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr); + if (rawDataSize != 0) + { + rawDataSize -= (int)pad0; + } + reader.BaseStream.Seek(pad0, SeekOrigin.Current); int recvListCount = recvListFlags - 2; @@ -107,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Ipc } } - public byte[] GetBytes(long cmdPtr) + public byte[] GetBytes(long cmdPtr, ulong recvListAddr) { using (MemoryStream ms = new MemoryStream()) { @@ -131,7 +136,11 @@ namespace Ryujinx.HLE.HOS.Ipc int dataLength = RawData?.Length ?? 0; - int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length); + dataLength = (dataLength + 3) & ~3; + + int rawLength = dataLength; + + int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8); // Apparently, padding after Raw Data is 16 bytes, however when there is // padding before Raw Data too, we need to subtract the size of this padding. @@ -140,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Ipc dataLength = (dataLength + pad0 + pad1) / 4; - word1 = dataLength & 0x3ff; + word1 = (dataLength & 0x3ff) | (2 << 10); if (HandleDesc != null) { @@ -151,14 +160,22 @@ namespace Ryujinx.HLE.HOS.Ipc writer.Write(word1); writer.Write(handleData); + for (int index = 0; index < PtrBuff.Count; index++) + { + writer.Write(PtrBuff[index].GetWord0()); + writer.Write(PtrBuff[index].GetWord1()); + } + ms.Seek(pad0, SeekOrigin.Current); if (RawData != null) { writer.Write(RawData); + ms.Seek(rawLength - RawData.Length, SeekOrigin.Current); } writer.Write(new byte[pad1]); + writer.Write(recvListAddr); return ms.ToArray(); } diff --git a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs index cdc43f17f..c17f248f8 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs @@ -8,6 +8,13 @@ namespace Ryujinx.HLE.HOS.Ipc public int Index { get; private set; } public long Size { get; private set; } + public IpcPtrBuffDesc(long position, int index, long size) + { + Position = position; + Index = index; + Size = size; + } + public IpcPtrBuffDesc(BinaryReader reader) { long word0 = reader.ReadUInt32(); @@ -22,5 +29,25 @@ namespace Ryujinx.HLE.HOS.Ipc Size = (ushort)(word0 >> 16); } + + public uint GetWord0() + { + uint word0; + + word0 = (uint)((Position & 0x0f00000000) >> 20); + word0 |= (uint)((Position & 0x7000000000) >> 30); + + word0 |= (uint)(Index & 0x03f) << 0; + word0 |= (uint)(Index & 0x1c0) << 3; + + word0 |= (uint)Size << 16; + + return word0; + } + + public uint GetWord1() + { + return (uint)Position; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs index 3fd92f02a..787325504 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs @@ -7,6 +7,12 @@ namespace Ryujinx.HLE.HOS.Ipc public long Position { get; private set; } public long Size { get; private set; } + public IpcRecvListBuffDesc(long position, long size) + { + Position = position; + Size = size; + } + public IpcRecvListBuffDesc(BinaryReader reader) { long value = reader.ReadInt64(); diff --git a/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs b/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs new file mode 100644 index 000000000..098d83d13 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + struct OnScopeExit : IDisposable + { + private readonly Action _action; + public OnScopeExit(Action action) => _action = action; + public void Dispose() => _action(); + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs index 0986adf7a..fbd328459 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs @@ -99,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc copySize, stateMask, stateMask, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, attributeMask, MemoryAttribute.None, desc.ServerAddress); @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc clientEndAddr - clientEndAddrTruncated, stateMask, stateMask, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, attributeMask, MemoryAttribute.None, serverEndAddrTruncated); diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs index 8d6669cf5..c3b7d1dd3 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs @@ -14,10 +14,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public bool IsLight => _parent.IsLight; - // TODO: Remove that, we need it for now to allow HLE - // SM implementation to work with the new IPC system. - public IpcService Service { get; set; } - public KClientPort(KernelContext context, KPort parent, int maxSessions) : base(context) { _maxSessions = maxSessions; @@ -45,11 +41,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc KSession session = new KSession(KernelContext, this); - if (Service != null) - { - session.ClientSession.Service = Service; - } - KernelResult result = _parent.EnqueueIncomingSession(session.ServerSession); if (result != KernelResult.Success) diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs index 2c2d9644d..d535bf82b 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs @@ -16,10 +16,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public KClientPort ParentPort { get; } - // TODO: Remove that, we need it for now to allow HLE - // services implementation to work with the new IPC system. - public IpcService Service { get; set; } - public KClientSession(KernelContext context, KSession parent, KClientPort parentPort) : base(context) { _parent = parent; @@ -84,11 +80,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { _parent.DisconnectClient(); _parent.DecrementReferenceCount(); - - if (Service is IDisposable disposableObj) - { - disposableObj.Dispose(); - } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs index 486698328..e9c6127fa 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs @@ -430,7 +430,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc descriptor.BufferAddress, MemoryState.IsPoolAllocated, MemoryState.IsPoolAllocated, - MemoryPermission.Read, + KMemoryPermission.Read, MemoryAttribute.Uncached, MemoryAttribute.None); @@ -473,9 +473,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc bool notReceiveDesc = isSendDesc || isExchangeDesc; bool isReceiveDesc = !notReceiveDesc; - MemoryPermission permission = index >= clientHeader.SendBuffersCount - ? MemoryPermission.ReadAndWrite - : MemoryPermission.Read; + KMemoryPermission permission = index >= clientHeader.SendBuffersCount + ? KMemoryPermission.ReadAndWrite + : KMemoryPermission.Read; uint sizeHigh4 = (descWord2 >> 24) & 0xf; @@ -559,9 +559,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc if (serverMsg.IsCustom || clientMsg.IsCustom) { - MemoryPermission permission = clientMsg.IsCustom - ? MemoryPermission.None - : MemoryPermission.Read; + KMemoryPermission permission = clientMsg.IsCustom + ? KMemoryPermission.None + : KMemoryPermission.Read; clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess( copyDst, @@ -795,7 +795,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc descriptor.BufferSize, MemoryState.IsPoolAllocated, MemoryState.IsPoolAllocated, - MemoryPermission.Read, + KMemoryPermission.Read, MemoryAttribute.Uncached, MemoryAttribute.None, descriptor.BufferAddress); @@ -849,9 +849,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc if (serverMsg.IsCustom || clientMsg.IsCustom) { - MemoryPermission permission = clientMsg.IsCustom - ? MemoryPermission.None - : MemoryPermission.Read; + KMemoryPermission permission = clientMsg.IsCustom + ? KMemoryPermission.None + : KMemoryPermission.Read; clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess( copyDst, @@ -898,11 +898,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc return new MessageHeader(word0, word1, word2); } - private KernelResult GetCopyObjectHandle( - KThread srcThread, - KProcess dstProcess, - int srcHandle, - out int dstHandle) + private KernelResult GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle) { dstHandle = 0; @@ -933,11 +929,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } - private KernelResult GetMoveObjectHandle( - KProcess srcProcess, - KProcess dstProcess, - int srcHandle, - out int dstHandle) + private KernelResult GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle) { dstHandle = 0; diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs index 4b5886a9d..d614857c9 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.HLE.HOS.Kernel.Ipc { - class KSession : KAutoObject, IDisposable + class KSession : KAutoObject { public KServerSession ServerSession { get; } public KClientSession ClientSession { get; } @@ -37,19 +37,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc } } - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing && ClientSession.Service is IDisposable disposableService) - { - disposableService.Dispose(); - } - } - protected override void Destroy() { if (_hasBeenInitialized) diff --git a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs index a2d8bc47b..d19c43eef 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel Device = device; Memory = memory; - Syscall = new Syscall(device, this); + Syscall = new Syscall(this); SyscallHandler = new SyscallHandler(this); diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs new file mode 100644 index 000000000..c7deadae9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -0,0 +1,38 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; +using System; +using System.Threading.Tasks; + +namespace Ryujinx.HLE.HOS.Kernel +{ + static class KernelStatic + { + [ThreadStatic] + private static KernelContext Context; + + public static void YieldUntilCompletion(Action action) + { + YieldUntilCompletion(Task.Factory.StartNew(action)); + } + + public static void YieldUntilCompletion(Task task) + { + KThread currentThread = Context.Scheduler.GetCurrentThread(); + + Context.CriticalSection.Enter(); + + currentThread.Reschedule(ThreadSchedState.Paused); + + task.ContinueWith((antecedent) => + { + currentThread.Reschedule(ThreadSchedState.Running); + }); + + Context.CriticalSection.Leave(); + } + + internal static void SetKernelContext(KernelContext context) + { + Context = context; + } + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs index 04e14e1b3..b93b68d94 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs @@ -8,9 +8,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public ulong PagesCount { get; private set; } public MemoryState State { get; private set; } - public MemoryPermission Permission { get; private set; } + public KMemoryPermission Permission { get; private set; } public MemoryAttribute Attribute { get; private set; } - public MemoryPermission SourcePermission { get; private set; } + public KMemoryPermission SourcePermission { get; private set; } public int IpcRefCount { get; private set; } public int DeviceRefCount { get; private set; } @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong baseAddress, ulong pagesCount, MemoryState state, - MemoryPermission permission, + KMemoryPermission permission, MemoryAttribute attribute, int ipcRefCount = 0, int deviceRefCount = 0) @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory DeviceRefCount = deviceRefCount; } - public void SetState(MemoryPermission permission, MemoryState state, MemoryAttribute attribute) + public void SetState(KMemoryPermission permission, MemoryState state, MemoryAttribute attribute) { Permission = permission; State = state; @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Attribute |= attribute; } - public void SetIpcMappingPermission(MemoryPermission newPermission) + public void SetIpcMappingPermission(KMemoryPermission newPermission) { int oldIpcRefCount = IpcRefCount++; @@ -54,8 +54,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { SourcePermission = Permission; - Permission &= ~MemoryPermission.ReadAndWrite; - Permission |= MemoryPermission.ReadAndWrite & newPermission; + Permission &= ~KMemoryPermission.ReadAndWrite; + Permission |= KMemoryPermission.ReadAndWrite & newPermission; } Attribute |= MemoryAttribute.IpcMapped; @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { Permission = SourcePermission; - SourcePermission = MemoryPermission.None; + SourcePermission = KMemoryPermission.None; Attribute &= ~MemoryAttribute.IpcMapped; } diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs index 21e9e4940..af070ac23 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public ulong Size { get; } public MemoryState State { get; } - public MemoryPermission Permission { get; } + public KMemoryPermission Permission { get; } public MemoryAttribute Attribute { get; } - public MemoryPermission SourcePermission { get; } + public KMemoryPermission SourcePermission { get; } public int IpcRefCount { get; } public int DeviceRefCount { get; } @@ -17,9 +17,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong address, ulong size, MemoryState state, - MemoryPermission permission, + KMemoryPermission permission, MemoryAttribute attribute, - MemoryPermission sourcePermission, + KMemoryPermission sourcePermission, int ipcRefCount, int deviceRefCount) { diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs index 43e7ad691..63ac8583e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs @@ -1,9 +1,10 @@ using Ryujinx.Common; -using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Memory; using System; using System.Collections.Generic; +using System.Diagnostics; namespace Ryujinx.HLE.HOS.Kernel.Memory { @@ -27,11 +28,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory // needs to be split in 2, plus one block that will be the new one inserted. private const int MaxBlocksNeededForInsertion = 2; - private LinkedList _blocks; + private readonly LinkedList _blocks; - private MemoryManager _cpuMemory; + private readonly IVirtualMemoryManager _cpuMemory; - private KernelContext _context; + private readonly KernelContext _context; public ulong AddrSpaceStart { get; private set; } public ulong AddrSpaceEnd { get; private set; } @@ -73,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private MersenneTwister _randomNumberGenerator; - public KMemoryManager(KernelContext context, MemoryManager cpuMemory) + public KMemoryManager(KernelContext context, IVirtualMemoryManager cpuMemory) { _context = context; _cpuMemory = cpuMemory; @@ -352,7 +353,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory addrSpaceStart, addrSpacePagesCount, MemoryState.Unmapped, - MemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.None)); return KernelResult.Success; @@ -362,13 +363,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong address, KPageList pageList, MemoryState state, - MemoryPermission permission) + KMemoryPermission permission) { ulong pagesCount = pageList.GetPagesCount(); ulong size = pagesCount * PageSize; - if (!ValidateRegionForState(address, size, state)) + if (!CanContain(address, size, state)) { return KernelResult.InvalidMemState; } @@ -437,8 +438,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, stateExpected, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, @@ -467,13 +468,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public KernelResult MapNormalMemory(long address, long size, MemoryPermission permission) + public KernelResult MapNormalMemory(long address, long size, KMemoryPermission permission) { // TODO. return KernelResult.Success; } - public KernelResult MapIoMemory(long address, long size, MemoryPermission permission) + public KernelResult MapIoMemory(long address, long size, KMemoryPermission permission) { // TODO. return KernelResult.Success; @@ -487,7 +488,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong regionStart, ulong regionPagesCount, MemoryState state, - MemoryPermission permission, + KMemoryPermission permission, out ulong address) { address = 0; @@ -496,7 +497,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong regionEndAddr = regionStart + regionSize; - if (!ValidateRegionForState(regionStart, regionSize, state)) + if (!CanContain(regionStart, regionSize, state)) { return KernelResult.InvalidMemState; } @@ -547,11 +548,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong address, ulong pagesCount, MemoryState state, - MemoryPermission permission) + KMemoryPermission permission) { ulong size = pagesCount * PageSize; - if (!ValidateRegionForState(address, size, state)) + if (!CanContain(address, size, state)) { return KernelResult.InvalidMemState; } @@ -596,13 +597,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, MemoryState.Heap, - MemoryPermission.Mask, - MemoryPermission.ReadAndWrite, + KMemoryPermission.Mask, + KMemoryPermission.ReadAndWrite, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, out MemoryState state, - out MemoryPermission permission, + out KMemoryPermission permission, out _); success &= IsUnmapped(dst, size); @@ -618,14 +619,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory AddVaRangeToPageList(pageList, src, pagesCount); - KernelResult result = MmuChangePermission(src, pagesCount, MemoryPermission.None); + KernelResult result = MmuChangePermission(src, pagesCount, KMemoryPermission.None); if (result != KernelResult.Success) { return result; } - result = MapPages(dst, pageList, MemoryPermission.None); + result = MapPages(dst, pageList, KMemoryPermission.None); if (result != KernelResult.Success) { @@ -634,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - InsertBlock(src, pagesCount, state, MemoryPermission.None, MemoryAttribute.Borrowed); + InsertBlock(src, pagesCount, state, KMemoryPermission.None, MemoryAttribute.Borrowed); InsertBlock(dst, pagesCount, MemoryState.ModCodeStatic); return KernelResult.Success; @@ -657,8 +658,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, MemoryState.Heap, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.Borrowed, MemoryAttribute.IpcAndDeviceMapped, @@ -671,8 +672,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory PageSize, MemoryState.UnmapProcessCodeMemoryAllowed, MemoryState.UnmapProcessCodeMemoryAllowed, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, @@ -685,8 +686,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, state, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.None); @@ -707,7 +708,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } InsertBlock(dst, pagesCount, MemoryState.Unmapped); - InsertBlock(src, pagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); + InsertBlock(src, pagesCount, MemoryState.Heap, KMemoryPermission.ReadAndWrite); return KernelResult.Success; } @@ -788,7 +789,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _currentHeapAddr, pagesCount, pageList, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, MemoryOperation.MapVa); if (result != KernelResult.Success) @@ -798,7 +799,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - InsertBlock(_currentHeapAddr, pagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); + InsertBlock(_currentHeapAddr, pagesCount, MemoryState.Heap, KMemoryPermission.ReadAndWrite); } else { @@ -816,8 +817,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory sizeDelta, MemoryState.Mask, MemoryState.Heap, - MemoryPermission.Mask, - MemoryPermission.ReadAndWrite, + KMemoryPermission.Mask, + KMemoryPermission.ReadAndWrite, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, @@ -886,13 +887,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.AttributeChangeAllowed, MemoryState.AttributeChangeAllowed, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.BorrowedAndIpcMapped, MemoryAttribute.None, MemoryAttribute.DeviceMappedAndUncached, out MemoryState state, - out MemoryPermission permission, + out KMemoryPermission permission, out MemoryAttribute attribute)) { if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion)) @@ -932,9 +933,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory AddrSpaceEnd, ~AddrSpaceEnd + 1, MemoryState.Reserved, - MemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.None, - MemoryPermission.None, + KMemoryPermission.None, 0, 0); } @@ -951,8 +952,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.MapAllowed, MemoryState.MapAllowed, - MemoryPermission.Mask, - MemoryPermission.ReadAndWrite, + KMemoryPermission.Mask, + KMemoryPermission.ReadAndWrite, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, @@ -975,18 +976,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory AddVaRangeToPageList(pageList, src, pagesCount); - KernelResult result = MmuChangePermission(src, pagesCount, MemoryPermission.None); + KernelResult result = MmuChangePermission(src, pagesCount, KMemoryPermission.None); if (result != KernelResult.Success) { return result; } - result = MapPages(dst, pageList, MemoryPermission.ReadAndWrite); + result = MapPages(dst, pageList, KMemoryPermission.ReadAndWrite); if (result != KernelResult.Success) { - if (MmuChangePermission(src, pagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success) + if (MmuChangePermission(src, pagesCount, KMemoryPermission.ReadAndWrite) != KernelResult.Success) { throw new InvalidOperationException("Unexpected failure reverting memory permission."); } @@ -994,8 +995,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - InsertBlock(src, pagesCount, srcState, MemoryPermission.None, MemoryAttribute.Borrowed); - InsertBlock(dst, pagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite); + InsertBlock(src, pagesCount, srcState, KMemoryPermission.None, MemoryAttribute.Borrowed); + InsertBlock(dst, pagesCount, MemoryState.Stack, KMemoryPermission.ReadAndWrite); return KernelResult.Success; } @@ -1017,8 +1018,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, stateExpected, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, @@ -1058,8 +1059,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.MapAllowed, MemoryState.MapAllowed, - MemoryPermission.Mask, - MemoryPermission.None, + KMemoryPermission.Mask, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.Borrowed, MemoryAttribute.IpcAndDeviceMapped, @@ -1072,13 +1073,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, MemoryState.Stack, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, out _, - out MemoryPermission dstPermission, + out KMemoryPermission dstPermission, out _); if (success) @@ -1108,7 +1109,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - result = MmuChangePermission(src, pagesCount, MemoryPermission.ReadAndWrite); + result = MmuChangePermission(src, pagesCount, KMemoryPermission.ReadAndWrite); if (result != KernelResult.Success) { @@ -1117,7 +1118,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - InsertBlock(src, pagesCount, srcState, MemoryPermission.ReadAndWrite); + InsertBlock(src, pagesCount, srcState, KMemoryPermission.ReadAndWrite); InsertBlock(dst, pagesCount, MemoryState.Unmapped); return KernelResult.Success; @@ -1129,7 +1130,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public KernelResult SetProcessMemoryPermission(ulong address, ulong size, MemoryPermission permission) + public KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission) { lock (_blocks) { @@ -1138,20 +1139,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.ProcessPermissionChangeAllowed, MemoryState.ProcessPermissionChangeAllowed, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, out MemoryState oldState, - out MemoryPermission oldPermission, + out KMemoryPermission oldPermission, out _)) { MemoryState newState = oldState; // If writing into the code region is allowed, then we need // to change it to mutable. - if ((permission & MemoryPermission.Write) != 0) + if ((permission & KMemoryPermission.Write) != 0) { if (oldState == MemoryState.CodeStatic) { @@ -1176,7 +1177,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong pagesCount = size / PageSize; - MemoryOperation operation = (permission & MemoryPermission.Execute) != 0 + MemoryOperation operation = (permission & KMemoryPermission.Execute) != 0 ? MemoryOperation.ChangePermsAndAttributes : MemoryOperation.ChangePermRw; @@ -1270,10 +1271,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory address, pagesCount, MemoryState.Unmapped, - MemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.None, MemoryState.Heap, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, MemoryAttribute.None); } @@ -1410,7 +1411,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory pagesCount, srcPa, true, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, MemoryOperation.MapPa); dstVa += pagesCount * PageSize; @@ -1428,7 +1429,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong src, MemoryState stateMask, MemoryState stateExpected, - MemoryPermission permission, + KMemoryPermission permission, MemoryAttribute attributeMask, MemoryAttribute attributeExpected) { @@ -1450,7 +1451,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong size, MemoryState stateMask, MemoryState stateExpected, - MemoryPermission permission, + KMemoryPermission permission, MemoryAttribute attributeMask, MemoryAttribute attributeExpected, ulong src) @@ -1474,7 +1475,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong serverAddress, MemoryState stateMask, MemoryState stateExpected, - MemoryPermission permission, + KMemoryPermission permission, MemoryAttribute attributeMask, MemoryAttribute attributeExpected, bool toServer) @@ -1529,7 +1530,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong size, ulong src, KMemoryManager sourceMemMgr, - MemoryPermission permission, + KMemoryPermission permission, MemoryState state, bool copyData, out ulong dst) @@ -1568,7 +1569,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private KernelResult GetPagesForMappingIntoAnotherProcess( ulong address, ulong size, - MemoryPermission permission, + KMemoryPermission permission, MemoryState state, bool copyData, bool aslrDisabled, @@ -1600,9 +1601,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory default: return KernelResult.InvalidCombination; } - MemoryPermission permissionMask = permission == MemoryPermission.ReadAndWrite - ? MemoryPermission.None - : MemoryPermission.Read; + KMemoryPermission permissionMask = permission == KMemoryPermission.ReadAndWrite + ? KMemoryPermission.None + : KMemoryPermission.Read; MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached; @@ -1634,7 +1635,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrVisited)) { - if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0) + if ((info.Permission & KMemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0) { ulong blockAddress = GetAddrInRange(info, addressRounded); ulong blockSize = GetSizeInRange(info, addressRounded, endAddrVisited); @@ -1661,7 +1662,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (addressRounded < endAddrTruncated) { - foreach (KMemoryInfo info in IterateOverRange(addressTruncated, endAddrRounded)) + foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrTruncated)) { // Check if the block state matches what we expect. if ((info.State & stateMask) != stateMask || @@ -1678,7 +1679,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong blockPagesCount = blockSize / PageSize; - if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0) + if ((info.Permission & KMemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0) { result = DoMmuOperation( blockAddress, @@ -1784,7 +1785,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong unusedSizeBefore = address - addressTruncated; - _context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore); + _context.Memory.ZeroFill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore); ulong copySize = addressRounded <= endAddr ? addressRounded - address : size; @@ -1803,7 +1804,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (unusedSizeAfter != 0) { - _context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter); + _context.Memory.ZeroFill(GetDramAddressFromPa(firstPageFillAddress), unusedSizeAfter); } if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success) @@ -1865,7 +1866,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory unusedSizeAfter = PageSize; } - _context.Memory.ZeroFill(lastPageFillAddr, unusedSizeAfter); + _context.Memory.ZeroFill(GetDramAddressFromPa(lastPageFillAddr), unusedSizeAfter); if (pages.AddRange(dstLastPagePa, 1) != KernelResult.Success) { @@ -1897,7 +1898,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private KernelResult MapPagesFromAnotherProcess( ulong size, ulong address, - MemoryPermission permission, + KMemoryPermission permission, MemoryState state, KPageList pageList, out ulong dst) @@ -1975,8 +1976,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, state, - MemoryPermission.Read, - MemoryPermission.Read, + KMemoryPermission.Read, + KMemoryPermission.Read, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, @@ -1996,14 +1997,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize; - KernelResult result = DoMmuOperation( - addressTruncated, - pagesCount, - 0, - false, - MemoryPermission.None, - MemoryOperation.Unmap); - // Free pages we had to create on-demand, if any of the buffer was not page aligned. // Real kernel has page ref counting, so this is done as part of the unmap operation. if (addressTruncated != addressRounded) @@ -2016,6 +2009,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory FreeSinglePage(_memRegion, ConvertVaToPa(endAddrTruncated)); } + KernelResult result = DoMmuOperation( + addressTruncated, + pagesCount, + 0, + false, + KMemoryPermission.None, + MemoryOperation.Unmap); + if (result == KernelResult.Success) { InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped); @@ -2037,7 +2038,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong addressRounded = BitUtils.AlignUp (address, PageSize); ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize); - ulong pagesCount = (endAddrTruncated - addressRounded) / PageSize; + ulong pagesCount = addressRounded < endAddrTruncated ? (endAddrTruncated - addressRounded) / PageSize : 0; + + if (pagesCount == 0) + { + return KernelResult.Success; + } MemoryState stateMask; @@ -2111,23 +2117,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.IpcBufferAllowed, MemoryState.IpcBufferAllowed, - MemoryPermission.Mask, - MemoryPermission.ReadAndWrite, + KMemoryPermission.Mask, + KMemoryPermission.ReadAndWrite, MemoryAttribute.Mask, MemoryAttribute.None, - MemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Borrowed); } - public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, MemoryPermission permission) + public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, KMemoryPermission permission) { return SetAttributesAndChangePermission( address, size, MemoryState.TransferMemoryAllowed, MemoryState.TransferMemoryAllowed, - MemoryPermission.Mask, - MemoryPermission.ReadAndWrite, + KMemoryPermission.Mask, + KMemoryPermission.ReadAndWrite, MemoryAttribute.Mask, MemoryAttribute.None, permission, @@ -2140,11 +2146,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong size, MemoryState stateMask, MemoryState stateExpected, - MemoryPermission permissionMask, - MemoryPermission permissionExpected, + KMemoryPermission permissionMask, + KMemoryPermission permissionExpected, MemoryAttribute attributeMask, MemoryAttribute attributeExpected, - MemoryPermission newPermission, + KMemoryPermission newPermission, MemoryAttribute attributeSetMask, KPageList pageList = null) { @@ -2166,7 +2172,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory attributeExpected, MemoryAttribute.IpcAndDeviceMapped, out MemoryState oldState, - out MemoryPermission oldPermission, + out KMemoryPermission oldPermission, out MemoryAttribute oldAttribute)) { ulong pagesCount = size / PageSize; @@ -2181,7 +2187,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.OutOfResource; } - if (newPermission == MemoryPermission.None) + if (newPermission == KMemoryPermission.None) { newPermission = oldPermission; } @@ -2222,11 +2228,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.IpcBufferAllowed, MemoryState.IpcBufferAllowed, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.Borrowed, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, MemoryAttribute.Borrowed); } @@ -2237,11 +2243,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.TransferMemoryAllowed, MemoryState.TransferMemoryAllowed, - MemoryPermission.None, - MemoryPermission.None, + KMemoryPermission.None, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.Borrowed, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, MemoryAttribute.Borrowed, pageList); } @@ -2251,11 +2257,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong size, MemoryState stateMask, MemoryState stateExpected, - MemoryPermission permissionMask, - MemoryPermission permissionExpected, + KMemoryPermission permissionMask, + KMemoryPermission permissionExpected, MemoryAttribute attributeMask, MemoryAttribute attributeExpected, - MemoryPermission newPermission, + KMemoryPermission newPermission, MemoryAttribute attributeClearMask, KPageList pageList = null) { @@ -2277,7 +2283,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory attributeExpected, MemoryAttribute.IpcAndDeviceMapped, out MemoryState oldState, - out MemoryPermission oldPermission, + out KMemoryPermission oldPermission, out MemoryAttribute oldAttribute)) { ulong pagesCount = size / PageSize; @@ -2299,7 +2305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.OutOfResource; } - if (newPermission == MemoryPermission.None) + if (newPermission == KMemoryPermission.None) { newPermission = oldPermission; } @@ -2385,8 +2391,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory size, MemoryState.Mask, MemoryState.Unmapped, - MemoryPermission.Mask, - MemoryPermission.None, + KMemoryPermission.Mask, + KMemoryPermission.None, MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, @@ -2400,13 +2406,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong size, MemoryState stateMask, MemoryState stateExpected, - MemoryPermission permissionMask, - MemoryPermission permissionExpected, + KMemoryPermission permissionMask, + KMemoryPermission permissionExpected, MemoryAttribute attributeMask, MemoryAttribute attributeExpected, MemoryAttribute attributeIgnoreMask, out MemoryState outState, - out MemoryPermission outPermission, + out KMemoryPermission outPermission, out MemoryAttribute outAttribute) { ulong endAddr = address + size; @@ -2416,7 +2422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory KMemoryInfo info = node.Value.GetInfo(); MemoryState firstState = info.State; - MemoryPermission firstPermission = info.Permission; + KMemoryPermission firstPermission = info.Permission; MemoryAttribute firstAttribute = info.Attribute; do @@ -2432,7 +2438,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory (firstPermission & permissionMask) != permissionExpected) { outState = MemoryState.Unmapped; - outPermission = MemoryPermission.None; + outPermission = KMemoryPermission.None; outAttribute = MemoryAttribute.None; return false; @@ -2452,8 +2458,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong size, MemoryState stateMask, MemoryState stateExpected, - MemoryPermission permissionMask, - MemoryPermission permissionExpected, + KMemoryPermission permissionMask, + KMemoryPermission permissionExpected, MemoryAttribute attributeMask, MemoryAttribute attributeExpected) { @@ -2490,10 +2496,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong baseAddress, ulong pagesCount, MemoryState oldState, - MemoryPermission oldPermission, + KMemoryPermission oldPermission, MemoryAttribute oldAttribute, MemoryState newState, - MemoryPermission newPermission, + KMemoryPermission newPermission, MemoryAttribute newAttribute) { // Insert new block on the list only on areas where the state @@ -2553,13 +2559,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } _blockAllocator.Count += _blocks.Count - oldCount; + + ValidateInternalState(); } private void InsertBlock( ulong baseAddress, ulong pagesCount, MemoryState state, - MemoryPermission permission = MemoryPermission.None, + KMemoryPermission permission = KMemoryPermission.None, MemoryAttribute attribute = MemoryAttribute.None) { // Inserts new block at the list, replacing and splitting @@ -2605,25 +2613,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } _blockAllocator.Count += _blocks.Count - oldCount; + + ValidateInternalState(); } - private static void SetIpcMappingPermissions(KMemoryBlock block, MemoryPermission permission) + private static void SetIpcMappingPermissions(KMemoryBlock block, KMemoryPermission permission) { block.SetIpcMappingPermission(permission); } - private static void RestoreIpcMappingPermissions(KMemoryBlock block, MemoryPermission permission) + private static void RestoreIpcMappingPermissions(KMemoryBlock block, KMemoryPermission permission) { block.RestoreIpcMappingPermission(); } - private delegate void BlockMutator(KMemoryBlock block, MemoryPermission newPerm); + private delegate void BlockMutator(KMemoryBlock block, KMemoryPermission newPerm); private void InsertBlock( ulong baseAddress, ulong pagesCount, BlockMutator blockMutate, - MemoryPermission permission = MemoryPermission.None) + KMemoryPermission permission = KMemoryPermission.None) { // Inserts new block at the list, replacing and splitting // existing blocks as needed, then calling the callback @@ -2671,6 +2681,31 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } _blockAllocator.Count += _blocks.Count - oldCount; + + ValidateInternalState(); + } + + [Conditional("DEBUG")] + private void ValidateInternalState() + { + ulong expectedAddress = 0; + + LinkedListNode node = _blocks.First; + + while (node != null) + { + LinkedListNode newNode = node; + + KMemoryBlock currBlock = node.Value; + + Debug.Assert(currBlock.BaseAddress == expectedAddress); + + expectedAddress = currBlock.BaseAddress + currBlock.PagesCount * PageSize; + + node = newNode.Next; + } + + Debug.Assert(expectedAddress == AddrSpaceEnd); } private LinkedListNode MergeEqualStateNeighbors(LinkedListNode node) @@ -2876,13 +2911,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return null; } - private bool ValidateRegionForState(ulong address, ulong size, MemoryState state) + public bool CanContain(ulong address, ulong size, MemoryState state) { ulong endAddr = address + size; - ulong regionBaseAddr = GetBaseAddrForState(state); - - ulong regionEndAddr = regionBaseAddr + GetSizeForState(state); + ulong regionBaseAddr = GetBaseAddress(state); + ulong regionEndAddr = regionBaseAddr + GetSize(state); bool InsideRegion() { @@ -2891,17 +2925,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory endAddr - 1 <= regionEndAddr - 1; } - bool OutsideHeapRegion() - { - return endAddr <= HeapRegionStart || - address >= HeapRegionEnd; - } - - bool OutsideMapRegion() - { - return endAddr <= AliasRegionStart || - address >= AliasRegionEnd; - } + bool OutsideHeapRegion() => endAddr <= HeapRegionStart || address >= HeapRegionEnd; + bool OutsideAliasRegion() => endAddr <= AliasRegionStart || address >= AliasRegionEnd; switch (state) { @@ -2919,10 +2944,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory case MemoryState.ProcessMemory: case MemoryState.CodeReadOnly: case MemoryState.CodeWritable: - return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion(); + return InsideRegion() && OutsideHeapRegion() && OutsideAliasRegion(); case MemoryState.Heap: - return InsideRegion() && OutsideMapRegion(); + return InsideRegion() && OutsideAliasRegion(); case MemoryState.IpcBuffer0: case MemoryState.IpcBuffer1: @@ -2936,7 +2961,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory throw new ArgumentException($"Invalid state value \"{state}\"."); } - private ulong GetBaseAddrForState(MemoryState state) + private ulong GetBaseAddress(MemoryState state) { switch (state) { @@ -2975,7 +3000,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory throw new ArgumentException($"Invalid state value \"{state}\"."); } - private ulong GetSizeForState(MemoryState state) + private ulong GetSize(MemoryState state) { switch (state) { @@ -3050,7 +3075,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - private KernelResult MapPages(ulong address, KPageList pageList, MemoryPermission permission) + private KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission) { ulong currAddr = address; @@ -3090,11 +3115,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory pagesCount, 0, false, - MemoryPermission.None, + KMemoryPermission.None, MemoryOperation.Unmap); } - private KernelResult MmuChangePermission(ulong address, ulong pagesCount, MemoryPermission permission) + private KernelResult MmuChangePermission(ulong address, ulong pagesCount, KMemoryPermission permission) { return DoMmuOperation( address, @@ -3110,7 +3135,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong pagesCount, ulong srcPa, bool map, - MemoryPermission permission, + KMemoryPermission permission, MemoryOperation operation) { if (map != (operation == MemoryOperation.MapPa)) @@ -3171,7 +3196,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong address, ulong pagesCount, KPageList pageList, - MemoryPermission permission, + KMemoryPermission permission, MemoryOperation operation) { if (operation != MemoryOperation.MapVa) diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs index 65134f0d5..ca0e3421a 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs @@ -10,15 +10,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private readonly long _ownerPid; - private readonly MemoryPermission _ownerPermission; - private readonly MemoryPermission _userPermission; + private readonly KMemoryPermission _ownerPermission; + private readonly KMemoryPermission _userPermission; public KSharedMemory( KernelContext context, KPageList pageList, long ownerPid, - MemoryPermission ownerPermission, - MemoryPermission userPermission) : base(context) + KMemoryPermission ownerPermission, + KMemoryPermission userPermission) : base(context) { _pageList = pageList; _ownerPid = ownerPid; @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong address, ulong size, KProcess process, - MemoryPermission permission) + KMemoryPermission permission) { ulong pagesCountRounded = BitUtils.DivRoundUp(size, KMemoryManager.PageSize); @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidSize; } - MemoryPermission expectedPermission = process.Pid == _ownerPid + KMemoryPermission expectedPermission = process.Pid == _ownerPid ? _ownerPermission : _userPermission; diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs index d3e6208e3..96349452a 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs @@ -8,12 +8,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { private KProcess _creator; + // TODO: Remove when we no longer need to read it from the owner directly. + public KProcess Creator => _creator; + private readonly KPageList _pageList; public ulong Address { get; private set; } public ulong Size => _pageList.GetPagesCount() * KMemoryManager.PageSize; - public MemoryPermission Permission { get; private set; } + public KMemoryPermission Permission { get; private set; } private bool _hasBeenInitialized; private bool _isMapped; @@ -23,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _pageList = new KPageList(); } - public KernelResult Initialize(ulong address, ulong size, MemoryPermission permission) + public KernelResult Initialize(ulong address, ulong size, KMemoryPermission permission) { KProcess creator = KernelContext.Scheduler.GetCurrentProcess(); diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs b/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs index 0ad90abd7..8bfd8d944 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.HLE.HOS.Kernel.Memory { [Flags] - enum MemoryPermission : byte + enum KMemoryPermission : byte { None = 0, Mask = 0xff, diff --git a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs index 376badfb6..5ad33154d 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs @@ -1,7 +1,7 @@ -using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Diagnostics.Demangler; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.Loaders.Elf; +using Ryujinx.Memory; using System.Collections.Generic; using System.Linq; using System.Text; @@ -224,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - if (info.State == MemoryState.CodeStatic && info.Permission == MemoryPermission.ReadAndExecute) + if (info.State == MemoryState.CodeStatic && info.Permission == KMemoryPermission.ReadAndExecute) { LoadMod0Symbols(_owner.CpuMemory, info.Address); } @@ -235,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - private void LoadMod0Symbols(MemoryManager memory, ulong textOffset) + private void LoadMod0Symbols(IVirtualMemoryManager memory, ulong textOffset) { ulong mod0Offset = textOffset + memory.Read(textOffset + 4); @@ -319,7 +319,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - private ElfSymbol GetSymbol64(MemoryManager memory, ulong address, ulong strTblAddr) + private ElfSymbol GetSymbol64(IVirtualMemoryManager memory, ulong address, ulong strTblAddr) { ElfSymbol64 sym = memory.Read(address); @@ -335,7 +335,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size); } - private ElfSymbol GetSymbol32(MemoryManager memory, ulong address, ulong strTblAddr) + private ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr) { ElfSymbol32 sym = memory.Read(address); diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs new file mode 100644 index 000000000..758ac6b10 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs @@ -0,0 +1,13 @@ +using ARMeilleure.State; +using Ryujinx.Memory; +using System; + +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + interface IProcessContext : IDisposable + { + IVirtualMemoryManager AddressSpace { get; } + + void Execute(ExecutionContext context, ulong codeAddress); + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs new file mode 100644 index 000000000..c438b570e --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs @@ -0,0 +1,10 @@ +using Ryujinx.Cpu; +using Ryujinx.Memory; + +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + interface IProcessContextFactory + { + IProcessContext Create(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler); + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index d02e25a3c..8e914f194 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -6,6 +6,7 @@ using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -15,13 +16,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { class KProcess : KSynchronizationObject { - public const int KernelVersionMajor = 10; - public const int KernelVersionMinor = 4; + public const int KernelVersionMajor = 10; + public const int KernelVersionMinor = 4; public const int KernelVersionRevision = 0; public const int KernelVersionPacked = - (KernelVersionMajor << 19) | - (KernelVersionMinor << 15) | + (KernelVersionMajor << 19) | + (KernelVersionMinor << 15) | (KernelVersionRevision << 0); public KMemoryManager MemoryManager { get; private set; } @@ -47,27 +48,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public long[] RandomEntropy { get; private set; } private bool _signaled; - private bool _useSystemMemBlocks; public string Name { get; private set; } private int _threadCount; - public int MmuFlags { get; private set; } + public ProcessCreationFlags Flags { get; private set; } private MemoryRegion _memRegion; public KProcessCapabilities Capabilities { get; private set; } public ulong TitleId { get; private set; } - public long Pid { get; private set; } + public long Pid { get; private set; } - private long _creationTimestamp; + private long _creationTimestamp; private ulong _entrypoint; + private ThreadStart _customThreadStart; private ulong _imageSize; private ulong _mainThreadStackSize; private ulong _memoryUsageCapacity; - private int _version; + private int _version; public KHandleTable HandleTable { get; private set; } @@ -77,14 +78,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public bool IsPaused { get; private set; } - public MemoryManager CpuMemory { get; private set; } - public CpuContext CpuContext { get; private set; } + private IProcessContextFactory _contextFactory; + public IProcessContext Context { get; private set; } + public IVirtualMemoryManager CpuMemory => Context.AddressSpace; public HleProcessDebugger Debugger { get; private set; } public KProcess(KernelContext context) : base(context) { - _processLock = new object(); + _processLock = new object(); _threadingLock = new object(); AddressArbiter = new KAddressArbiter(context); @@ -96,6 +98,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process RandomEntropy = new long[KScheduler.CpuCoresCount]; + // TODO: Remove once we no longer need to initialize it externally. + HandleTable = new KHandleTable(context); + _threads = new LinkedList(); Debugger = new HleProcessDebugger(this); @@ -103,25 +108,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public KernelResult InitializeKip( ProcessCreationInfo creationInfo, - int[] caps, - KPageList pageList, - KResourceLimit resourceLimit, - MemoryRegion memRegion) + ReadOnlySpan capabilities, + KPageList pageList, + KResourceLimit resourceLimit, + MemoryRegion memRegion, + IProcessContextFactory contextFactory) { ResourceLimit = resourceLimit; - _memRegion = memRegion; + _memRegion = memRegion; + _contextFactory = contextFactory ?? new ProcessContextFactory(); - AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7); + AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift); - InitializeMemoryManager(addrSpaceType, memRegion); + InitializeMemoryManager(creationInfo.Flags); - bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0; + bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr); ulong codeAddress = creationInfo.CodeAddress; ulong codeSize = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize; - KMemoryBlockAllocator memoryBlockAllocator = (MmuFlags & 0x40) != 0 + KMemoryBlockAllocator memoryBlockAllocator = creationInfo.Flags.HasFlag(ProcessCreationFlags.IsApplication) ? KernelContext.LargeMemoryBlockAllocator : KernelContext.SmallMemoryBlockAllocator; @@ -139,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - if (!ValidateCodeAddressAndSize(codeAddress, codeSize)) + if (!MemoryManager.CanContain(codeAddress, codeSize, MemoryState.CodeStatic)) { return KernelResult.InvalidMemRange; } @@ -148,14 +155,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process codeAddress, pageList, MemoryState.CodeStatic, - MemoryPermission.None); + KMemoryPermission.None); if (result != KernelResult.Success) { return result; } - result = Capabilities.InitializeForKernel(caps, MemoryManager); + result = Capabilities.InitializeForKernel(capabilities, MemoryManager); if (result != KernelResult.Success) { @@ -176,14 +183,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public KernelResult Initialize( ProcessCreationInfo creationInfo, - int[] caps, - KResourceLimit resourceLimit, - MemoryRegion memRegion) + ReadOnlySpan capabilities, + KResourceLimit resourceLimit, + MemoryRegion memRegion, + IProcessContextFactory contextFactory, + ThreadStart customThreadStart = null) { ResourceLimit = resourceLimit; - _memRegion = memRegion; + _memRegion = memRegion; + _contextFactory = contextFactory ?? new ProcessContextFactory(); - ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.PersonalMmHeapPagesCount, memRegion); + ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.SystemResourcePagesCount, memRegion); ulong codePagesCount = (ulong)creationInfo.CodePagesCount; @@ -205,7 +215,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - PersonalMmHeapPagesCount = (ulong)creationInfo.PersonalMmHeapPagesCount; + PersonalMmHeapPagesCount = (ulong)creationInfo.SystemResourcePagesCount; KMemoryBlockAllocator memoryBlockAllocator; @@ -215,16 +225,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } else { - memoryBlockAllocator = (MmuFlags & 0x40) != 0 + memoryBlockAllocator = creationInfo.Flags.HasFlag(ProcessCreationFlags.IsApplication) ? KernelContext.LargeMemoryBlockAllocator : KernelContext.SmallMemoryBlockAllocator; } - AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7); + AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift); - InitializeMemoryManager(addrSpaceType, memRegion); + InitializeMemoryManager(creationInfo.Flags); - bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0; + bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr); ulong codeAddress = creationInfo.CodeAddress; @@ -246,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - if (!ValidateCodeAddressAndSize(codeAddress, codeSize)) + if (!MemoryManager.CanContain(codeAddress, codeSize, MemoryState.CodeStatic)) { CleanUpForError(); @@ -257,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process codeAddress, codePagesCount, MemoryState.CodeStatic, - MemoryPermission.None); + KMemoryPermission.None); if (result != KernelResult.Success) { @@ -266,7 +276,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - result = Capabilities.InitializeForUser(caps, MemoryManager); + result = Capabilities.InitializeForUser(capabilities, MemoryManager); if (result != KernelResult.Success) { @@ -289,57 +299,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process CleanUpForError(); } + _customThreadStart = customThreadStart; + return result; } - private bool ValidateCodeAddressAndSize(ulong address, ulong size) - { - ulong codeRegionStart; - ulong codeRegionSize; - - switch (MemoryManager.AddrSpaceWidth) - { - case 32: - codeRegionStart = 0x200000; - codeRegionSize = 0x3fe00000; - break; - - case 36: - codeRegionStart = 0x8000000; - codeRegionSize = 0x78000000; - break; - - case 39: - codeRegionStart = 0x8000000; - codeRegionSize = 0x7ff8000000; - break; - - default: throw new InvalidOperationException("Invalid address space width on memory manager."); - } - - ulong endAddr = address + size; - - ulong codeRegionEnd = codeRegionStart + codeRegionSize; - - if (endAddr <= address || - endAddr - 1 > codeRegionEnd - 1) - { - return false; - } - - if (MemoryManager.InsideHeapRegion (address, size) || - MemoryManager.InsideAliasRegion(address, size)) - { - return false; - } - - return true; - } - private KernelResult ParseProcessInfo(ProcessCreationInfo creationInfo) { // Ensure that the current kernel version is equal or above to the minimum required. - uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19; + uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19; uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf; if (KernelContext.EnableVersionChecks) @@ -377,31 +345,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _creationTimestamp = PerformanceCounter.ElapsedMilliseconds; - MmuFlags = creationInfo.MmuFlags; - _version = creationInfo.Version; - TitleId = creationInfo.TitleId; + Flags = creationInfo.Flags; + _version = creationInfo.Version; + TitleId = creationInfo.TitleId; _entrypoint = creationInfo.CodeAddress; - _imageSize = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize; + _imageSize = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize; - _useSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0; - - switch ((AddressSpaceType)((MmuFlags >> 1) & 7)) + switch (Flags & ProcessCreationFlags.AddressSpaceMask) { - case AddressSpaceType.Addr32Bits: - case AddressSpaceType.Addr36Bits: - case AddressSpaceType.Addr39Bits: + case ProcessCreationFlags.AddressSpace32Bit: + case ProcessCreationFlags.AddressSpace64BitDeprecated: + case ProcessCreationFlags.AddressSpace64Bit: _memoryUsageCapacity = MemoryManager.HeapRegionEnd - MemoryManager.HeapRegionStart; break; - case AddressSpaceType.Addr32BitsNoMap: + case ProcessCreationFlags.AddressSpace32BitWithoutAlias: _memoryUsageCapacity = MemoryManager.HeapRegionEnd - MemoryManager.HeapRegionStart + MemoryManager.AliasRegionEnd - MemoryManager.AliasRegionStart; break; - default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}."); + default: throw new InvalidOperationException($"Invalid MMU flags value 0x{Flags:x2}."); } GenerateRandomEntropy(); @@ -469,7 +435,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } ulong regionStart = MemoryManager.TlsIoRegionStart; - ulong regionSize = MemoryManager.TlsIoRegionEnd - regionStart; + ulong regionSize = MemoryManager.TlsIoRegionEnd - regionStart; ulong regionPagesCount = regionSize / KMemoryManager.PageSize; @@ -481,7 +447,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process regionStart, regionPagesCount, MemoryState.ThreadLocal, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, out ulong tlsPageVa); if (result != KernelResult.Success) @@ -506,7 +472,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process KernelResult result = KernelResult.Success; - KTlsPageInfo pageInfo = null; + KTlsPageInfo pageInfo; if (_fullTlsPages.TryGetValue(tlsPageAddr, out pageInfo)) { @@ -594,8 +560,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process // Check if the needed size for the code and the stack will fit on the // memory usage capacity of this Process. Also check for possible overflow // on the above addition. - if (neededSize > _memoryUsageCapacity || - neededSize < stackSizeRounded) + if (neededSize > _memoryUsageCapacity || neededSize < stackSizeRounded) { threadResourceLimit?.Release(LimitableResource.Thread, 1); @@ -646,7 +611,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process ulong stackPagesCount = stackSizeRounded / KMemoryManager.PageSize; ulong regionStart = MemoryManager.StackRegionStart; - ulong regionSize = MemoryManager.StackRegionEnd - regionStart; + ulong regionSize = MemoryManager.StackRegionEnd - regionStart; ulong regionPagesCount = regionSize / KMemoryManager.PageSize; @@ -658,7 +623,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process regionStart, regionPagesCount, MemoryState.Stack, - MemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, out ulong stackBottom); if (result != KernelResult.Success) @@ -703,7 +668,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process stackTop, mainThreadPriority, DefaultCpuCore, - this); + this, + ThreadType.User, + _customThreadStart); if (result != KernelResult.Success) { @@ -730,20 +697,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process SetState(newState); - // TODO: We can't call KThread.Start from a non-guest thread. - // We will need to make some changes to allow the creation of - // dummy threads that will be used to initialize the current - // thread on KCoreContext so that GetCurrentThread doesn't fail. - /* Result = MainThread.Start(); + result = mainThread.Start(); - if (Result != KernelResult.Success) + if (result != KernelResult.Success) { - SetState(OldState); + SetState(oldState); CleanUpForError(); - } */ - - mainThread.Reschedule(ThreadSchedState.Running); + } if (result == KernelResult.Success) { @@ -760,7 +721,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { if (State != newState) { - State = newState; + State = newState; _signaled = true; Signal(); @@ -769,23 +730,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public KernelResult InitializeThread( KThread thread, - ulong entrypoint, - ulong argsPtr, - ulong stackTop, - int priority, - int cpuCore) + ulong entrypoint, + ulong argsPtr, + ulong stackTop, + int priority, + int cpuCore) { lock (_processLock) { - return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this); + return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null); } } public void SubscribeThreadEventHandlers(ARMeilleure.State.ExecutionContext context) { - context.Interrupt += InterruptHandler; + context.Interrupt += InterruptHandler; context.SupervisorCall += KernelContext.SyscallHandler.SvcCall; - context.Undefined += UndefinedInstructionHandler; + context.Undefined += UndefinedInstructionHandler; } private void InterruptHandler(object sender, EventArgs e) @@ -910,8 +871,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { if (State >= ProcessState.Started) { - if (State == ProcessState.Started || - State == ProcessState.Crashed || + if (State == ProcessState.Started || + State == ProcessState.Crashed || State == ProcessState.Attached || State == ProcessState.DebugSuspended) { @@ -1072,23 +1033,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - private void InitializeMemoryManager(AddressSpaceType addrSpaceType, MemoryRegion memRegion) + private void InitializeMemoryManager(ProcessCreationFlags flags) { - int addrSpaceBits = addrSpaceType switch + int addrSpaceBits = (flags & ProcessCreationFlags.AddressSpaceMask) switch { - AddressSpaceType.Addr32Bits => 32, - AddressSpaceType.Addr36Bits => 36, - AddressSpaceType.Addr32BitsNoMap => 32, - AddressSpaceType.Addr39Bits => 39, - _ => throw new ArgumentException(nameof(addrSpaceType)) + ProcessCreationFlags.AddressSpace32Bit => 32, + ProcessCreationFlags.AddressSpace64BitDeprecated => 36, + ProcessCreationFlags.AddressSpace32BitWithoutAlias => 32, + ProcessCreationFlags.AddressSpace64Bit => 39, + _ => 39 }; - CpuMemory = new MemoryManager(KernelContext.Memory, 1UL << addrSpaceBits, InvalidAccessHandler); - CpuContext = new CpuContext(CpuMemory); + Context = _contextFactory.Create(KernelContext.Memory, 1UL << addrSpaceBits, InvalidAccessHandler); // TODO: This should eventually be removed. // The GPU shouldn't depend on the CPU memory manager at all. - KernelContext.Device.Gpu.SetVmm(CpuMemory); + if (flags.HasFlag(ProcessCreationFlags.IsApplication)) + { + KernelContext.Device.Gpu.SetVmm((MemoryManager)CpuMemory); + } MemoryManager = new KMemoryManager(KernelContext, CpuMemory); } @@ -1109,9 +1072,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process throw new UndefinedInstructionException(e.Address, e.OpCode); } - protected override void Destroy() - { - CpuMemory.Dispose(); - } + protected override void Destroy() => Context.Dispose(); } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index 2396aea83..e1cdb30f7 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; +using System; namespace Ryujinx.HLE.HOS.Kernel.Process { @@ -24,29 +25,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Process IrqAccessMask = new byte[0x80]; } - public KernelResult InitializeForKernel(int[] caps, KMemoryManager memoryManager) + public KernelResult InitializeForKernel(ReadOnlySpan capabilities, KMemoryManager memoryManager) { AllowedCpuCoresMask = 0xf; AllowedThreadPriosMask = -1; DebuggingFlags &= ~3; KernelReleaseVersion = KProcess.KernelVersionPacked; - return Parse(caps, memoryManager); + return Parse(capabilities, memoryManager); } - public KernelResult InitializeForUser(int[] caps, KMemoryManager memoryManager) + public KernelResult InitializeForUser(ReadOnlySpan capabilities, KMemoryManager memoryManager) { - return Parse(caps, memoryManager); + return Parse(capabilities, memoryManager); } - private KernelResult Parse(int[] caps, KMemoryManager memoryManager) + private KernelResult Parse(ReadOnlySpan capabilities, KMemoryManager memoryManager) { int mask0 = 0; int mask1 = 0; - for (int index = 0; index < caps.Length; index++) + for (int index = 0; index < capabilities.Length; index++) { - int cap = caps[index]; + int cap = capabilities[index]; if (((cap + 1) & ~cap) != 0x40) { @@ -59,14 +60,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } else { - if ((uint)index + 1 >= caps.Length) + if ((uint)index + 1 >= capabilities.Length) { return KernelResult.InvalidCombination; } int prevCap = cap; - cap = caps[++index]; + cap = capabilities[++index]; if (((cap + 1) & ~cap) != 0x40) { @@ -91,9 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return KernelResult.InvalidAddress; } - MemoryPermission perm = (prevCap >> 31) != 0 - ? MemoryPermission.Read - : MemoryPermission.ReadAndWrite; + KMemoryPermission perm = (prevCap >> 31) != 0 + ? KMemoryPermission.Read + : KMemoryPermission.ReadAndWrite; KernelResult result; @@ -216,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { long address = ((long)(uint)cap << 4) & 0xffffff000; - memoryManager.MapIoMemory(address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite); + memoryManager.MapIoMemory(address, KMemoryManager.PageSize, KMemoryPermission.ReadAndWrite); break; } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs new file mode 100644 index 000000000..54997cb7c --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs @@ -0,0 +1,25 @@ +using ARMeilleure.State; +using Ryujinx.Memory; +using System; + +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + class ProcessContext : IProcessContext + { + public IVirtualMemoryManager AddressSpace { get; } + + public ProcessContext(IVirtualMemoryManager asManager) + { + AddressSpace = asManager; + } + + public void Execute(ExecutionContext context, ulong codeAddress) + { + throw new NotSupportedException(); + } + + public void Dispose() + { + } + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs new file mode 100644 index 000000000..03db62fa9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs @@ -0,0 +1,13 @@ +using Ryujinx.Cpu; +using Ryujinx.Memory; + +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + class ProcessContextFactory : IProcessContextFactory + { + public IProcessContext Create(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler) + { + return new ProcessContext(new AddressSpaceManager(backingMemory, addressSpaceSize)); + } + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs new file mode 100644 index 000000000..a34481e55 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs @@ -0,0 +1,38 @@ +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + enum ProcessCreationFlags + { + Is64Bit = 1 << 0, + + AddressSpaceShift = 1, + AddressSpace32Bit = 0 << AddressSpaceShift, + AddressSpace64BitDeprecated = 1 << AddressSpaceShift, + AddressSpace32BitWithoutAlias = 2 << AddressSpaceShift, + AddressSpace64Bit = 3 << AddressSpaceShift, + AddressSpaceMask = 7 << AddressSpaceShift, + + EnableDebug = 1 << 4, + EnableAslr = 1 << 5, + IsApplication = 1 << 6, + DeprecatedUseSecureMemory = 1 << 7, + + PoolPartitionShift = 7, + PoolPartitionApplication = 0 << PoolPartitionShift, + PoolPartitionApplet = 1 << PoolPartitionShift, + PoolPartitionSystem = 2 << PoolPartitionShift, + PoolPartitionSystemNonSecure = 3 << PoolPartitionShift, + PoolPartitionMask = 0xf << PoolPartitionShift, + + OptimizeMemoryAllocation = 1 << 11, + + All = + Is64Bit | + AddressSpaceMask | + EnableDebug | + EnableAslr | + IsApplication | + DeprecatedUseSecureMemory | + PoolPartitionMask | + OptimizeMemoryAllocation + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs index a58208653..26c23b3bb 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs @@ -2,36 +2,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { struct ProcessCreationInfo { - public string Name { get; private set; } + public string Name { get; } - public int Version { get; private set; } - public ulong TitleId { get; private set; } + public int Version { get; } + public ulong TitleId { get; } - public ulong CodeAddress { get; private set; } - public int CodePagesCount { get; private set; } + public ulong CodeAddress { get; } + public int CodePagesCount { get; } - public int MmuFlags { get; private set; } - public int ResourceLimitHandle { get; private set; } - public int PersonalMmHeapPagesCount { get; private set; } + public ProcessCreationFlags Flags { get; } + public int ResourceLimitHandle { get; } + public int SystemResourcePagesCount { get; } public ProcessCreationInfo( string name, - int category, - ulong titleId, - ulong codeAddress, - int codePagesCount, - int mmuFlags, - int resourceLimitHandle, - int personalMmHeapPagesCount) + int version, + ulong titleId, + ulong codeAddress, + int codePagesCount, + ProcessCreationFlags flags, + int resourceLimitHandle, + int systemResourcePagesCount) { - Name = name; - Version = category; - TitleId = titleId; - CodeAddress = codeAddress; - CodePagesCount = codePagesCount; - MmuFlags = mmuFlags; - ResourceLimitHandle = resourceLimitHandle; - PersonalMmHeapPagesCount = personalMmHeapPagesCount; + Name = name; + Version = version; + TitleId = titleId; + CodeAddress = codeAddress; + CodePagesCount = codePagesCount; + Flags = flags; + ResourceLimitHandle = resourceLimitHandle; + SystemResourcePagesCount = systemResourcePagesCount; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index c1e7026be..5e32ca580 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -7,21 +7,167 @@ using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Memory; using System; +using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { class Syscall { - private readonly Switch _device; private readonly KernelContext _context; - public Syscall(Switch device, KernelContext context) + public Syscall(KernelContext context) { - _device = device; _context = context; } + // Process + + public KernelResult GetProcessId(int handle, out long pid) + { + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); + + KProcess process = currentProcess.HandleTable.GetKProcess(handle); + + if (process == null) + { + KThread thread = currentProcess.HandleTable.GetKThread(handle); + + if (thread != null) + { + process = thread.Owner; + } + + // TODO: KDebugEvent. + } + + pid = process?.Pid ?? 0; + + return process != null + ? KernelResult.Success + : KernelResult.InvalidHandle; + } + + public KernelResult CreateProcess( + ProcessCreationInfo info, + ReadOnlySpan capabilities, + out int handle, + IProcessContextFactory contextFactory, + ThreadStart customThreadStart = null) + { + handle = 0; + + if ((info.Flags & ~ProcessCreationFlags.All) != 0) + { + return KernelResult.InvalidEnumValue; + } + + // TODO: Address space check. + + if ((info.Flags & ProcessCreationFlags.PoolPartitionMask) > ProcessCreationFlags.PoolPartitionSystemNonSecure) + { + return KernelResult.InvalidEnumValue; + } + + if ((info.CodeAddress & 0x1fffff) != 0) + { + return KernelResult.InvalidAddress; + } + + if (info.CodePagesCount < 0 || info.SystemResourcePagesCount < 0) + { + return KernelResult.InvalidSize; + } + + if (info.Flags.HasFlag(ProcessCreationFlags.OptimizeMemoryAllocation) && + !info.Flags.HasFlag(ProcessCreationFlags.IsApplication)) + { + return KernelResult.InvalidThread; + } + + KHandleTable handleTable = _context.Scheduler.GetCurrentProcess().HandleTable; + + KProcess process = new KProcess(_context); + + using var _ = new OnScopeExit(process.DecrementReferenceCount); + + KResourceLimit resourceLimit; + + if (info.ResourceLimitHandle != 0) + { + resourceLimit = handleTable.GetObject(info.ResourceLimitHandle); + + if (resourceLimit == null) + { + return KernelResult.InvalidHandle; + } + } + else + { + resourceLimit = _context.ResourceLimit; + } + + MemoryRegion memRegion = (info.Flags & ProcessCreationFlags.PoolPartitionMask) switch + { + ProcessCreationFlags.PoolPartitionApplication => MemoryRegion.Application, + ProcessCreationFlags.PoolPartitionApplet => MemoryRegion.Applet, + ProcessCreationFlags.PoolPartitionSystem => MemoryRegion.Service, + ProcessCreationFlags.PoolPartitionSystemNonSecure => MemoryRegion.NvServices, + _ => MemoryRegion.NvServices + }; + + KernelResult result = process.Initialize( + info, + capabilities, + resourceLimit, + memRegion, + contextFactory, + customThreadStart); + + if (result != KernelResult.Success) + { + return result; + } + + _context.Processes.TryAdd(process.Pid, process); + + return handleTable.GenerateHandle(process, out handle); + } + + public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize) + { + KProcess process = _context.Scheduler.GetCurrentProcess().HandleTable.GetObject(handle); + + if (process == null) + { + return KernelResult.InvalidHandle; + } + + if ((uint)cpuCore >= KScheduler.CpuCoresCount || !process.IsCpuCoreAllowed(cpuCore)) + { + return KernelResult.InvalidCpuCore; + } + + if ((uint)priority >= KScheduler.PrioritiesCount || !process.IsPriorityAllowed(priority)) + { + return KernelResult.InvalidPriority; + } + + process.DefaultCpuCore = cpuCore; + + KernelResult result = process.Start(priority, mainThreadStackSize); + + if (result != KernelResult.Success) + { + return result; + } + + process.IncrementReferenceCount(); + + return KernelResult.Success; + } + // IPC public KernelResult ConnectToNamedPort(ulong namePtr, out int handle) @@ -33,6 +179,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } + return ConnectToNamedPort(name, out handle); + } + + public KernelResult ConnectToNamedPort(string name, out int handle) + { + handle = 0; + if (name.Length > 11) { return KernelResult.MaximumExceeded; @@ -70,61 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } - public KernelResult SendSyncRequestHLE(int handle) - { - KProcess process = _context.Scheduler.GetCurrentProcess(); - - KClientSession clientSession = process.HandleTable.GetObject(handle); - - if (clientSession == null || clientSession.Service == null) - { - return SendSyncRequest(handle); - } - - return SendSyncRequestWithUserBufferHLE((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle); - } - - public KernelResult SendSyncRequestWithUserBufferHLE(ulong messagePtr, ulong messageSize, int handle) - { - KProcess process = _context.Scheduler.GetCurrentProcess(); - - byte[] messageData = new byte[messageSize]; - - process.CpuMemory.Read(messagePtr, messageData); - - KClientSession clientSession = process.HandleTable.GetObject(handle); - - if (clientSession == null || clientSession.Service == null) - { - return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle); - } - - if (clientSession != null) - { - _context.CriticalSection.Enter(); - - KThread currentThread = _context.Scheduler.GetCurrentThread(); - - currentThread.SignaledObj = null; - currentThread.ObjSyncResult = KernelResult.Success; - - currentThread.Reschedule(ThreadSchedState.Paused); - - clientSession.Service.Server.PushMessage(_device, currentThread, clientSession, messagePtr, messageSize); - - _context.CriticalSection.Leave(); - - return currentThread.ObjSyncResult; - } - else - { - Logger.Warning?.Print(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!"); - - return KernelResult.InvalidHandle; - } - } - - private KernelResult SendSyncRequest(int handle) + public KernelResult SendSyncRequest(int handle) { KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); @@ -407,9 +506,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } - KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount]; + return ReplyAndReceive(handles, replyTargetHandle, timeout, out handleIndex); + } - for (int index = 0; index < handlesCount; index++) + public KernelResult ReplyAndReceive(ReadOnlySpan handles, int replyTargetHandle, long timeout, out int handleIndex) + { + handleIndex = 0; + + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); + + KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length]; + + for (int index = 0; index < handles.Length; index++) { KSynchronizationObject obj = currentProcess.HandleTable.GetObject(handles[index]); @@ -601,7 +709,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } - if (maxSessions < 0 || name.Length > 11) + if (name.Length > 11) + { + return KernelResult.MaximumExceeded; + } + + return ManageNamedPort(name, maxSessions, out handle); + } + + public KernelResult ManageNamedPort(string name, int maxSessions, out int handle) + { + handle = 0; + + if (maxSessions < 0) { return KernelResult.MaximumExceeded; } @@ -826,7 +946,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.Success; } - public KernelResult MapSharedMemory(int handle, ulong address, ulong size, MemoryPermission permission) + public KernelResult MapSharedMemory(int handle, ulong address, ulong size, KMemoryPermission permission) { if (!PageAligned(address)) { @@ -843,7 +963,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - if ((permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite) + if ((permission | KMemoryPermission.Write) != KMemoryPermission.ReadAndWrite) { return KernelResult.InvalidPermission; } @@ -912,7 +1032,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall currentProcess); } - public KernelResult CreateTransferMemory(ulong address, ulong size, MemoryPermission permission, out int handle) + public KernelResult CreateTransferMemory(ulong address, ulong size, KMemoryPermission permission, out int handle) { handle = 0; @@ -931,7 +1051,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - if (permission > MemoryPermission.ReadAndWrite || permission == MemoryPermission.Write) + if (permission > KMemoryPermission.ReadAndWrite || permission == KMemoryPermission.Write) { return KernelResult.InvalidPermission; } @@ -1119,7 +1239,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size); } - public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, MemoryPermission permission) + public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission) { if (!PageAligned(src)) { @@ -1131,10 +1251,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidSize; } - if (permission != MemoryPermission.None && - permission != MemoryPermission.Read && - permission != MemoryPermission.ReadAndWrite && - permission != MemoryPermission.ReadAndExecute) + if (permission != KMemoryPermission.None && + permission != KMemoryPermission.Read && + permission != KMemoryPermission.ReadAndWrite && + permission != KMemoryPermission.ReadAndExecute) { return KernelResult.InvalidPermission; } @@ -1282,31 +1402,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _context.Scheduler.GetCurrentThread().Context.CntpctEl0; } - public KernelResult GetProcessId(int handle, out long pid) - { - KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); - - KProcess process = currentProcess.HandleTable.GetKProcess(handle); - - if (process == null) - { - KThread thread = currentProcess.HandleTable.GetKThread(handle); - - if (thread != null) - { - process = thread.Owner; - } - - // TODO: KDebugEvent. - } - - pid = process?.Pid ?? 0; - - return process != null - ? KernelResult.Success - : KernelResult.InvalidHandle; - } - public void Break(ulong reason) { KThread currentThread = _context.Scheduler.GetCurrentThread(); @@ -1997,7 +2092,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidThread; } - MemoryManager memory = currentProcess.CpuMemory; + IVirtualMemoryManager memory = currentProcess.CpuMemory; memory.Write(address + 0x0, thread.Context.GetX(0)); memory.Write(address + 0x8, thread.Context.GetX(1)); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs index 224af6d84..b57175a36 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs @@ -22,12 +22,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public KernelResult SendSyncRequest32([R(0)] int handle) { - return _syscall.SendSyncRequestHLE(handle); + return _syscall.SendSyncRequest(handle); } public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint messageSize, [R(2)] int handle) { - return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle); + return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle); } public KernelResult CreateSession32( @@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } - public KernelResult MapSharedMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] MemoryPermission permission) + 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); } @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public KernelResult CreateTransferMemory32( [R(1)] uint address, [R(2)] uint size, - [R(3)] MemoryPermission permission, + [R(3)] KMemoryPermission permission, [R(1)] out int handle) { return _syscall.CreateTransferMemory(address, size, permission, out handle); @@ -169,7 +169,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall [R(2)] uint srcLow, [R(3)] uint srcHigh, [R(4)] uint sizeHigh, - [R(5)] MemoryPermission permission) + [R(5)] KMemoryPermission permission) { ulong src = srcLow | ((ulong)srcHigh << 32); ulong size = sizeLow | ((ulong)sizeHigh << 32); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs index 47f78a25a..07ca4aae7 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs @@ -22,12 +22,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public KernelResult SendSyncRequest64([R(0)] int handle) { - return _syscall.SendSyncRequestHLE(handle); + return _syscall.SendSyncRequest(handle); } public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong messageSize, [R(2)] int handle) { - return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle); + return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle); } public KernelResult SendAsyncRequestWithUserBuffer64( @@ -133,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _syscall.QueryMemory(infoPtr, position, out pageInfo); } - public KernelResult MapSharedMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] MemoryPermission permission) + 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); } @@ -146,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public KernelResult CreateTransferMemory64( [R(1)] ulong address, [R(2)] ulong size, - [R(3)] MemoryPermission permission, + [R(3)] KMemoryPermission permission, [R(1)] out int handle) { return _syscall.CreateTransferMemory(address, size, permission, out handle); @@ -172,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _syscall.UnmapProcessCodeMemory(handle, dst, src, size); } - public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] MemoryPermission permission) + 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); } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 849955139..c6da361d0 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -229,6 +229,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KProcess dummyProcess = new KProcess(_context); + dummyProcess.HandleTable.Initialize(1024); + KThread dummyThread = new KThread(_context); dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 27ff38832..f523cb9cf 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -31,6 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public ulong CondVarAddress { get; set; } private ulong _entrypoint; + private ThreadStart _customThreadStart; public ulong MutexAddress { get; set; } @@ -48,12 +49,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public LinkedListNode[] SiblingsPerCore { get; private set; } - public LinkedList Withholder { get; set; } + public LinkedList Withholder { get; set; } public LinkedListNode WithholderNode { get; set; } public LinkedListNode ProcessListNode { get; set; } - private LinkedList _mutexWaiters; + private LinkedList _mutexWaiters; private LinkedListNode _mutexWaiterNode; public KThread MutexOwner { get; private set; } @@ -65,24 +66,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public KernelResult ObjSyncResult { get; set; } public int DynamicPriority { get; set; } - public int CurrentCore { get; set; } - public int BasePriority { get; set; } - public int PreferredCore { get; set; } + public int CurrentCore { get; set; } + public int BasePriority { get; set; } + public int PreferredCore { get; set; } private long _affinityMaskOverride; - private int _preferredCoreOverride; + private int _preferredCoreOverride; #pragma warning disable CS0649 - private int _affinityOverrideCount; + private int _affinityOverrideCount; #pragma warning restore CS0649 public ThreadSchedState SchedFlags { get; private set; } private int _shallBeTerminated; - public bool ShallBeTerminated { get => _shallBeTerminated != 0; set => _shallBeTerminated = value ? 1 : 0; } + public bool ShallBeTerminated + { + get => _shallBeTerminated != 0; + set => _shallBeTerminated = value ? 1 : 0; + } public bool SyncCancelled { get; set; } - public bool WaitingSync { get; set; } + public bool WaitingSync { get; set; } private bool _hasExited; private bool _hasBeenInitialized; @@ -98,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public KThread(KernelContext context) : base(context) { - _scheduler = KernelContext.Scheduler; + _scheduler = KernelContext.Scheduler; _schedulingData = KernelContext.Scheduler.SchedulingData; WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects]; @@ -110,14 +115,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } public KernelResult Initialize( - ulong entrypoint, - ulong argsPtr, - ulong stackTop, - int priority, - int defaultCpuCore, - KProcess owner, - ThreadType type = ThreadType.User, - ThreadStart customHostThreadStart = null) + ulong entrypoint, + ulong argsPtr, + ulong stackTop, + int priority, + int defaultCpuCore, + KProcess owner, + ThreadType type, + ThreadStart customThreadStart = null) { if ((uint)type > 3) { @@ -135,11 +140,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading CurrentCore = PreferredCore; DynamicPriority = priority; - BasePriority = priority; + BasePriority = priority; ObjSyncResult = KernelResult.ThreadNotStarted; _entrypoint = entrypoint; + _customThreadStart = customThreadStart; if (type == ThreadType.User) { @@ -162,18 +168,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading owner.IncrementReferenceCount(); owner.IncrementThreadCount(); - is64Bits = (owner.MmuFlags & 1) != 0; + is64Bits = owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit); } else { is64Bits = true; } - HostThread = new Thread(customHostThreadStart ?? (() => ThreadStart(entrypoint))); + HostThread = new Thread(ThreadStart); Context = CpuContext.CreateExecutionContext(); - bool isAarch32 = (Owner.MmuFlags & 1) == 0; + bool isAarch32 = !Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit); Context.IsAarch32 = isAarch32; @@ -189,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } Context.CntfrqEl0 = 19200000; - Context.Tpidr = (long)_tlsAddress; + Context.Tpidr = (long)_tlsAddress; owner.SubscribeThreadEventHandlers(Context); @@ -249,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KThread currentThread = KernelContext.Scheduler.GetCurrentThread(); - while (SchedFlags != ThreadSchedState.TerminationPending && + while (SchedFlags != ThreadSchedState.TerminationPending && currentThread.SchedFlags != ThreadSchedState.TerminationPending && !currentThread.ShallBeTerminated) { @@ -351,7 +357,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Context.RequestInterrupt(); } - SignaledObj = null; + SignaledObj = null; ObjSyncResult = KernelResult.ThreadTerminating; ReleaseAndResume(); @@ -524,7 +530,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // If the candidate was scheduled after the current thread, then it's not worth it, // unless the priority is higher than the current one. if (nextThreadOnCurrentQueue.LastScheduledTime >= thread.LastScheduledTime || - nextThreadOnCurrentQueue.DynamicPriority < thread.DynamicPriority) + nextThreadOnCurrentQueue.DynamicPriority < thread.DynamicPriority) { yield return thread; } @@ -701,7 +707,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } else { - SignaledObj = null; + SignaledObj = null; ObjSyncResult = KernelResult.Cancelled; SetNewSchedFlags(ThreadSchedState.Running); @@ -734,14 +740,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (useOverride) { _preferredCoreOverride = newCore; - _affinityMaskOverride = newAffinityMask; + _affinityMaskOverride = newAffinityMask; } else { long oldAffinityMask = AffinityMask; PreferredCore = newCore; - AffinityMask = newAffinityMask; + AffinityMask = newAffinityMask; if (oldAffinityMask != newAffinityMask) { @@ -783,7 +789,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private void CombineForcePauseFlags() { - ThreadSchedState oldFlags = SchedFlags; + ThreadSchedState oldFlags = SchedFlags; ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask; SchedFlags = lowNibble | _forcePauseFlags; @@ -1143,19 +1149,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - private void ThreadStart(ulong entrypoint) + private void ThreadStart() { - Owner.CpuContext.Execute(Context, entrypoint); + KernelStatic.SetKernelContext(KernelContext); - ThreadExit(); + if (_customThreadStart != null) + { + _customThreadStart(); + } + else + { + Owner.Context.Execute(Context, _entrypoint); + } - Context.Dispose(); - } - - private void ThreadExit() - { KernelContext.Scheduler.ExitThread(this); KernelContext.Scheduler.RemoveThread(this); + + Context.Dispose(); } public bool IsCurrentHostThread() @@ -1203,9 +1213,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // Wake up all threads that may be waiting for a mutex being held by this thread. foreach (KThread thread in _mutexWaiters) { - thread.MutexOwner = null; + thread.MutexOwner = null; thread._preferredCoreOverride = 0; - thread.ObjSyncResult = KernelResult.InvalidState; + thread.ObjSyncResult = KernelResult.InvalidState; thread.ReleaseAndResume(); } diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 58e370417..637c3efc2 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -36,23 +36,23 @@ namespace Ryujinx.HLE.HOS ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset; - int mmuFlags = 0; + ProcessCreationFlags flags = 0; if (AslrEnabled) { // TODO: Randomization. - mmuFlags |= 0x20; + flags |= ProcessCreationFlags.EnableAslr; } if (kip.Is64BitAddressSpace) { - mmuFlags |= (int)AddressSpaceType.Addr39Bits << 1; + flags |= ProcessCreationFlags.AddressSpace64Bit; } if (kip.Is64Bit) { - mmuFlags |= 1; + flags |= ProcessCreationFlags.Is64Bit; } ProcessCreationInfo creationInfo = new ProcessCreationInfo( @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS kip.ProgramId, codeAddress, codePagesCount, - mmuFlags, + flags, 0, 0); @@ -82,12 +82,15 @@ namespace Ryujinx.HLE.HOS KProcess process = new KProcess(context); + var processContextFactory = new ArmProcessContextFactory(); + result = process.InitializeKip( creationInfo, kip.Capabilities, pageList, context.ResourceLimit, - memoryRegion); + memoryRegion, + processContextFactory); if (result != KernelResult.Success) { @@ -183,7 +186,7 @@ namespace Ryujinx.HLE.HOS metaData.Aci0.TitleId, codeStart, codePagesCount, - metaData.MmuFlags, + (ProcessCreationFlags)metaData.ProcessFlags | ProcessCreationFlags.IsApplication, 0, personalMmHeapPagesCount); @@ -217,11 +220,14 @@ namespace Ryujinx.HLE.HOS return false; } + var processContextFactory = new ArmProcessContextFactory(); + result = process.Initialize( creationInfo, metaData.Aci0.KernelAccessControl.Capabilities, resourceLimit, - memoryRegion); + memoryRegion, + processContextFactory); if (result != KernelResult.Success) { @@ -280,7 +286,7 @@ namespace Ryujinx.HLE.HOS MemoryHelper.FillWithZeros(process.CpuMemory, (long)bssStart, image.BssSize); - KernelResult SetProcessMemoryPermission(ulong address, ulong size, MemoryPermission permission) + KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission) { if (size == 0) { @@ -292,21 +298,21 @@ namespace Ryujinx.HLE.HOS return process.MemoryManager.SetProcessMemoryPermission(address, size, permission); } - KernelResult result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, MemoryPermission.ReadAndExecute); + KernelResult result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, KMemoryPermission.ReadAndExecute); if (result != KernelResult.Success) { return result; } - result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, MemoryPermission.Read); + result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, KMemoryPermission.Read); if (result != KernelResult.Success) { return result; } - return SetProcessMemoryPermission(dataStart, end - dataStart, MemoryPermission.ReadAndWrite); + return SetProcessMemoryPermission(dataStart, end - dataStart, KMemoryPermission.ReadAndWrite); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs index 533c1d677..36d4163eb 100644 --- a/Ryujinx.HLE/HOS/ServiceCtx.cs +++ b/Ryujinx.HLE/HOS/ServiceCtx.cs @@ -1,43 +1,39 @@ -using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Memory; using System.IO; namespace Ryujinx.HLE.HOS { class ServiceCtx { - public Switch Device { get; } - public KProcess Process { get; } - public MemoryManager Memory { get; } - public KThread Thread { get; } - public KClientSession Session { get; } - public IpcMessage Request { get; } - public IpcMessage Response { get; } - public BinaryReader RequestData { get; } - public BinaryWriter ResponseData { get; } + public Switch Device { get; } + public KProcess Process { get; } + public IVirtualMemoryManager Memory { get; } + public KThread Thread { get; } + public IpcMessage Request { get; } + public IpcMessage Response { get; } + public BinaryReader RequestData { get; } + public BinaryWriter ResponseData { get; } public ServiceCtx( - Switch device, - KProcess process, - MemoryManager memory, - KThread thread, - KClientSession session, - IpcMessage request, - IpcMessage response, - BinaryReader requestData, - BinaryWriter responseData) + Switch device, + KProcess process, + IVirtualMemoryManager memory, + KThread thread, + IpcMessage request, + IpcMessage response, + BinaryReader requestData, + BinaryWriter responseData) { - Device = device; - Process = process; - Memory = memory; - Thread = thread; - Session = session; - Request = request; - Response = response; - RequestData = requestData; + Device = device; + Process = process; + Memory = memory; + Thread = thread; + Request = request; + Response = response; + RequestData = requestData; ResponseData = responseData; } } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs index f131aff85..555396370 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs @@ -4,7 +4,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService { class ISystemAppletProxy : IpcService { - public ISystemAppletProxy() { } + private readonly long _pid; + + public ISystemAppletProxy(long pid) + { + _pid = pid; + } [Command(0)] // GetCommonStateGetter() -> object @@ -19,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // GetSelfController() -> object public ResultCode GetSelfController(ServiceCtx context) { - MakeObject(context, new ISelfController(context.Device.System)); + MakeObject(context, new ISelfController(context.Device.System, _pid)); return ResultCode.Success; } @@ -28,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // GetWindowController() -> object public ResultCode GetWindowController(ServiceCtx context) { - MakeObject(context, new IWindowController()); + MakeObject(context, new IWindowController(_pid)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 16ffe9ee6..7d9d06e55 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -18,6 +18,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib private KEvent _normalOutDataEvent; private KEvent _interactiveOutDataEvent; + private int _stateChangedEventHandle; + private int _normalOutDataEventHandle; + private int _interactiveOutDataEventHandle; + public ILibraryAppletAccessor(AppletId appletId, Horizon system) { _stateChangedEvent = new KEvent(system.KernelContext); @@ -32,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib _applet.AppletStateChanged += OnAppletStateChanged; _normalSession.DataAvailable += OnNormalOutData; _interactiveSession.DataAvailable += OnInteractiveOutData; - + Logger.Info?.Print(LogClass.ServiceAm, $"Applet '{appletId}' created."); } @@ -55,12 +59,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // GetAppletStateChangedEvent() -> handle public ResultCode GetAppletStateChangedEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_stateChangedEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out _stateChangedEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangedEventHandle); return ResultCode.Success; } @@ -69,8 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // Start() public ResultCode Start(ServiceCtx context) { - return (ResultCode)_applet.Start(_normalSession.GetConsumer(), - _interactiveSession.GetConsumer()); + return (ResultCode)_applet.Start(_normalSession.GetConsumer(), _interactiveSession.GetConsumer()); } [Command(30)] @@ -138,12 +144,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // GetPopOutDataEvent() -> handle public ResultCode GetPopOutDataEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_normalOutDataEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_normalOutDataEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_normalOutDataEvent.ReadableEvent, out _normalOutDataEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_normalOutDataEventHandle); return ResultCode.Success; } @@ -152,12 +162,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // GetPopInteractiveOutDataEvent() -> handle public ResultCode GetPopInteractiveOutDataEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_interactiveOutDataEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_interactiveOutDataEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_interactiveOutDataEvent.ReadableEvent, out _interactiveOutDataEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_interactiveOutDataEventHandle); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index 62319d476..e3b73c294 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -12,7 +12,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private Apm.SystemManagerServer _apmSystemManagerServer; private Lbl.LblControllerServer _lblControllerServer; - private bool _vrModeEnabled = false; + private bool _vrModeEnabled; + private int _messageEventHandle; + private int _displayResolutionChangedEventHandle; public ICommonStateGetter(ServiceCtx context) { @@ -25,14 +27,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // GetEventHandle() -> handle public ResultCode GetEventHandle(ServiceCtx context) { - KEvent Event = context.Device.System.AppletState.MessageEvent; + KEvent messageEvent = context.Device.System.AppletState.MessageEvent; - if (context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int handle) != KernelResult.Success) + if (_messageEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(messageEvent.ReadableEvent, out _messageEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_messageEventHandle); return ResultCode.Success; } @@ -147,7 +152,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _lblControllerServer.DisableVrMode(); } - // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used. + // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used. } [Command(60)] // 3.0.0+ @@ -164,12 +169,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // GetDefaultDisplayResolutionChangeEvent() -> handle public ResultCode GetDefaultDisplayResolutionChangeEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(context.Device.System.DisplayResolutionChangeEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_displayResolutionChangedEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(context.Device.System.DisplayResolutionChangeEvent.ReadableEvent, out _displayResolutionChangedEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_displayResolutionChangedEventHandle); Logger.Stub?.PrintStub(LogClass.ServiceAm); @@ -189,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _apmSystemManagerServer.SetCpuBoostMode((Apm.CpuBoostMode)cpuBoostMode); - // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used. + // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used. return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs index 5fafa1547..580574a89 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs @@ -9,6 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys class IHomeMenuFunctions : IpcService { private KEvent _channelEvent; + private int _channelEventHandle; public IHomeMenuFunctions(Horizon system) { @@ -29,12 +30,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // GetPopFromGeneralChannelEvent() -> handle public ResultCode GetPopFromGeneralChannelEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_channelEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_channelEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_channelEvent.ReadableEvent, out _channelEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_channelEventHandle); Logger.Stub?.PrintStub(LogClass.ServiceAm); diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs index decf54708..5b91e235e 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs @@ -49,7 +49,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys } var data = new byte[transferMem.Size]; - context.Memory.Read(transferMem.Address, data); + transferMem.Creator.CpuMemory.Read(transferMem.Address, data); + + context.Device.System.KernelContext.Syscall.CloseHandle(handle); MakeObject(context, new IStorage(data)); diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 8b1275ac2..b2cc71602 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -8,10 +8,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { class ISelfController : IpcService { + private readonly long _pid; + private KEvent _libraryAppletLaunchableEvent; + private int _libraryAppletLaunchableEventHandle; private KEvent _accumulatedSuspendedTickChangedEvent; - private int _accumulatedSuspendedTickChangedEventHandle = 0; + private int _accumulatedSuspendedTickChangedEventHandle; private object _fatalSectionLock = new object(); private int _fatalSectionCount; @@ -32,9 +35,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private uint _screenShotImageOrientation = 0; private uint _idleTimeDetectionExtension = 0; - public ISelfController(Horizon system) + public ISelfController(Horizon system, long pid) { _libraryAppletLaunchableEvent = new KEvent(system.KernelContext); + _pid = pid; } [Command(0)] @@ -103,12 +107,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { _libraryAppletLaunchableEvent.ReadableEvent.Signal(); - if (context.Process.HandleTable.GenerateHandle(_libraryAppletLaunchableEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_libraryAppletLaunchableEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_libraryAppletLaunchableEvent.ReadableEvent, out _libraryAppletLaunchableEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_libraryAppletLaunchableEventHandle); Logger.Stub?.PrintStub(LogClass.ServiceAm); @@ -206,6 +213,31 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [Command(40)] + // CreateManagedDisplayLayer() -> u64 + public ResultCode CreateManagedDisplayLayer(ServiceCtx context) + { + context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long layerId); + + context.ResponseData.Write(layerId); + + return ResultCode.Success; + } + + [Command(44)] // 10.0.0+ + // CreateManagedDisplaySeparableLayer() -> (u64, u64) + public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context) + { + // NOTE: first create the recoding layer and then the display one because right now Surface Flinger only use the last id. + context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long recordingLayerId); + context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long displayLayerId); + + context.ResponseData.Write(displayLayerId); + context.ResponseData.Write(recordingLayerId); + + return ResultCode.Success; + } + [Command(50)] // SetHandlesRequestToDisplay(b8) public ResultCode SetHandlesRequestToDisplay(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs index 5c5c9b885..8c127b988 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs @@ -4,7 +4,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { class IWindowController : IpcService { - public IWindowController() { } + private readonly long _pid; + + public IWindowController(long pid) + { + _pid = pid; + } [Command(1)] // GetAppletResourceUserId() -> nn::applet::AppletResourceUserId @@ -12,7 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { Logger.Stub?.PrintStub(LogClass.ServiceAm); - context.ResponseData.Write(0L); + long appletResourceUserId = context.Device.System.AppletState.AppletResourceUserIds.Add(_pid); + + context.ResponseData.Write(appletResourceUserId); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index d29a8da44..d518dde44 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE // OpenSystemAppletProxy(u64, pid, handle) -> object public ResultCode OpenSystemAppletProxy(ServiceCtx context) { - MakeObject(context, new ISystemAppletProxy()); + MakeObject(context, new ISystemAppletProxy(context.Request.HandleDesc.PId)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index b9dfee30c..6a5ad4c18 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -28,6 +28,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati private KEvent _friendInvitationStorageChannelEvent; private KEvent _notificationStorageChannelEvent; + private int _gpuErrorDetectedSystemEventHandle; + private int _friendInvitationStorageChannelEventHandle; + private int _notificationStorageChannelEventHandle; + public IApplicationFunctions(Horizon system) { _gpuErrorDetectedSystemEvent = new KEvent(system.KernelContext); @@ -122,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } - // If desired language is not supported by application, use first supported language from TitleLanguage. + // If desired language is not supported by application, use first supported language from TitleLanguage. // TODO: In the future, a GUI could enable user-specified search priority if (((1 << (int)context.Device.System.State.DesiredTitleLanguage) & supportedLanguages) == 0) { @@ -293,12 +297,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati resultCode = InitializeApplicationCopyrightFrameBufferImpl(transferMemoryAddress, transferMemorySize, width, height); } - /* - if (transferMemoryHandle) + if (transferMemoryHandle != 0) { - svcCloseHandle(transferMemoryHandle); + context.Device.System.KernelContext.Syscall.CloseHandle(transferMemoryHandle); } - */ return resultCode; } @@ -455,15 +457,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // GetGpuErrorDetectedSystemEvent() -> handle public ResultCode GetGpuErrorDetectedSystemEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_gpuErrorDetectedSystemEvent.ReadableEvent, out int gpuErrorDetectedSystemEventHandle) != KernelResult.Success) + if (_gpuErrorDetectedSystemEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_gpuErrorDetectedSystemEvent.ReadableEvent, out _gpuErrorDetectedSystemEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(gpuErrorDetectedSystemEventHandle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gpuErrorDetectedSystemEventHandle); - // NOTE: This is used by "sdk" NSO during applet-application initialization. - // A seperate thread is setup where event-waiting is handled. + // NOTE: This is used by "sdk" NSO during applet-application initialization. + // A seperate thread is setup where event-waiting is handled. // When the Event is signaled, official sw will assert. return ResultCode.Success; @@ -473,12 +478,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // GetFriendInvitationStorageChannelEvent() -> handle public ResultCode GetFriendInvitationStorageChannelEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_friendInvitationStorageChannelEvent.ReadableEvent, out int friendInvitationStorageChannelEventHandle) != KernelResult.Success) + if (_friendInvitationStorageChannelEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_friendInvitationStorageChannelEvent.ReadableEvent, out _friendInvitationStorageChannelEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(friendInvitationStorageChannelEventHandle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_friendInvitationStorageChannelEventHandle); return ResultCode.Success; } @@ -501,12 +509,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // GetNotificationStorageChannelEvent() -> handle public ResultCode GetNotificationStorageChannelEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_notificationStorageChannelEvent.ReadableEvent, out int notificationStorageChannelEventHandle) != KernelResult.Success) + if (_notificationStorageChannelEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_notificationStorageChannelEvent.ReadableEvent, out _notificationStorageChannelEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(notificationStorageChannelEventHandle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_notificationStorageChannelEventHandle); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs index 700886fd7..28a52573c 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs @@ -5,7 +5,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService { class IApplicationProxy : IpcService { - public IApplicationProxy() { } + private readonly long _pid; + + public IApplicationProxy(long pid) + { + _pid = pid; + } [Command(0)] // GetCommonStateGetter() -> object @@ -20,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService // GetSelfController() -> object public ResultCode GetSelfController(ServiceCtx context) { - MakeObject(context, new ISelfController(context.Device.System)); + MakeObject(context, new ISelfController(context.Device.System, _pid)); return ResultCode.Success; } @@ -29,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService // GetWindowController() -> object public ResultCode GetWindowController(ServiceCtx context) { - MakeObject(context, new IWindowController()); + MakeObject(context, new IWindowController(_pid)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs index 0aee24fe9..8487d3199 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am // OpenApplicationProxy(u64, pid, handle) -> object public ResultCode OpenApplicationProxy(ServiceCtx context) { - MakeObject(context, new IApplicationProxy()); + MakeObject(context, new IApplicationProxy(context.Request.HandleDesc.PId)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs index af9cccb2c..86e5566aa 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs @@ -10,15 +10,18 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager { class IAudioOut : IpcService, IDisposable { - private IAalOutput _audioOut; - private KEvent _releaseEvent; - private int _track; + private readonly IAalOutput _audioOut; + private readonly KEvent _releaseEvent; + private int _releaseEventHandle; + private readonly int _track; + private readonly int _clientHandle; - public IAudioOut(IAalOutput audioOut, KEvent releaseEvent, int track) + public IAudioOut(IAalOutput audioOut, KEvent releaseEvent, int track, int clientHandle) { _audioOut = audioOut; _releaseEvent = releaseEvent; _track = track; + _clientHandle = clientHandle; } [Command(0)] @@ -59,12 +62,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager // RegisterBufferEvent() -> handle public ResultCode RegisterBufferEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_releaseEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out _releaseEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_releaseEventHandle); return ResultCode.Success; } @@ -108,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager // NOTE: Assume PCM16 all the time, change if new format are found. short[] buffer = new short[data.SampleBufferSize / sizeof(short)]; - context.Memory.Read((ulong)data.SampleBufferPtr, MemoryMarshal.Cast(buffer)); + context.Process.HandleTable.GetKProcess(_clientHandle).CpuMemory.Read((ulong)data.SampleBufferPtr, MemoryMarshal.Cast(buffer)); _audioOut.AppendBuffer(_track, tag, buffer); diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs index e12a99199..5aff34757 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs @@ -33,7 +33,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio public ResultCode OpenAudioRenderer(ServiceCtx context, out IAudioRenderer obj, ref AudioRendererConfiguration parameter, ulong workBufferSize, ulong appletResourceUserId, KTransferMemory workBufferTransferMemory, uint processHandle) { - ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, context.Memory, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle); + var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory; + + ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, memoryManager, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle); if (result == ResultCode.Success) { diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs index effd8f151..a789dfed0 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs @@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio private IAudioRendererManager _impl; - public AudioRendererManagerServer(ServiceCtx context) : this(new AudioRendererManager(context.Device.System.AudioRendererManager, context.Device.System.AudioDeviceSessionRegistry)) { } + public AudioRendererManagerServer(ServiceCtx context) : this(context, new AudioRendererManager(context.Device.System.AudioRendererManager, context.Device.System.AudioDeviceSessionRegistry)) { } - public AudioRendererManagerServer(IAudioRendererManager impl) : base(new ServerBase("AudioRendererServer")) + public AudioRendererManagerServer(ServiceCtx context, IAudioRendererManager impl) : base(new ServerBase(context.Device.System.KernelContext, "AudioRendererServer")) { _impl = impl; } @@ -40,6 +40,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio MakeObject(context, new AudioRendererServer(renderer)); } + context.Device.System.KernelContext.Syscall.CloseHandle((int)processHandle); + return result; } diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs index 3081cd499..b3f7f5e0c 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs @@ -1,4 +1,5 @@ using Ryujinx.Cpu; +using Ryujinx.Memory; using System; using System.Text; @@ -57,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio return ResultCode.Success; } - private uint ListAudioInsImpl(MemoryManager memory, long bufferPosition, long bufferSize, bool filtered = false) + private uint ListAudioInsImpl(IVirtualMemoryManager memory, long bufferPosition, long bufferSize, bool filtered = false) { uint count = 0; diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs index 646acef4d..91ec287c3 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio private const int DefaultSampleRate = 48000; private const int DefaultChannelsCount = 2; - public IAudioOutManager(ServiceCtx context) : base(new ServerBase("AudioOutServer")) { } + public IAudioOutManager(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "AudioOutServer")) { } [Command(0)] // ListAudioOuts() -> (u32 count, buffer) @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio int track = audioOut.OpenTrack(sampleRate, channels, callback); - MakeObject(context, new IAudioOut(audioOut, releaseEvent, track)); + MakeObject(context, new IAudioOut(audioOut, releaseEvent, track, context.Request.HandleDesc.ToCopy[0])); context.ResponseData.Write(sampleRate); context.ResponseData.Write(channels); diff --git a/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs index ed40cdadb..c8cc281e2 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs @@ -16,6 +16,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio MakeObject(context, new IHardwareOpusDecoder(sampleRate, channelsCount)); + // Close transfer memory immediately as we don't use it. + context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); + return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs index d56a6a43f..54b1a58ac 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs @@ -12,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator class IDeliveryCacheProgressService : IpcService { private KEvent _event; + private int _eventHandle; public IDeliveryCacheProgressService(ServiceCtx context) { @@ -22,12 +23,15 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator // GetEvent() -> handle public ResultCode GetEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.Success) + if (_eventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle); Logger.Stub?.PrintStub(LogClass.ServiceBcat); diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 44f406a07..e29a040f1 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs } [Command(8)] - // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer, 0x19, 0x301> path) + // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer, 0x19, 0x301> path) // -> object contentFs public ResultCode OpenFileSystemWithId(ServiceCtx context) { @@ -138,12 +138,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // Workaround that by setting the application ID and owner ID if they're not already set if (attribute.ProgramId == ProgramId.InvalidId) { - attribute.ProgramId = new ProgramId(context.Process.TitleId); - } - - if (creationInfo.OwnerId == 0) - { - creationInfo.OwnerId = 0; + attribute.ProgramId = new ProgramId(context.Device.Application.TitleId); } Logger.Info?.Print(LogClass.ServiceFs, $"Creating save with title ID {attribute.ProgramId.Value:x16}"); @@ -215,12 +210,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // Workaround that by setting the application ID and owner ID if they're not already set if (attribute.ProgramId == ProgramId.InvalidId) { - attribute.ProgramId = new ProgramId(context.Process.TitleId); - } - - if (creationInfo.OwnerId == 0) - { - creationInfo.OwnerId = 0; + attribute.ProgramId = new ProgramId(context.Device.Application.TitleId); } Result result = _baseFileSystemProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref creationInfo, ref metaCreateInfo, ref hashSalt); @@ -239,7 +229,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // Workaround that by setting the application ID if it's not already set if (attribute.ProgramId == ProgramId.InvalidId) { - attribute.ProgramId = new ProgramId(context.Process.TitleId); + attribute.ProgramId = new ProgramId(context.Device.Application.TitleId); } Result result = _baseFileSystemProxy.OpenSaveDataFileSystem(out LibHac.Fs.Fsa.IFileSystem fileSystem, spaceId, ref attribute); @@ -280,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // Workaround that by setting the application ID if it's not already set if (attribute.ProgramId == ProgramId.InvalidId) { - attribute.ProgramId = new ProgramId(context.Process.TitleId); + attribute.ProgramId = new ProgramId(context.Device.Application.TitleId); } Result result = _baseFileSystemProxy.OpenReadOnlySaveDataFileSystem(out LibHac.Fs.Fsa.IFileSystem fileSystem, spaceId, ref attribute); @@ -328,7 +318,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs filter.SetSaveDataType(SaveDataType.Cache); filter.SetProgramId(new ProgramId(context.Process.TitleId)); - // FS would query the User and SdCache space IDs to find where the existing cache is (if any). + // FS would query the User and SdCache space IDs to find where the existing cache is (if any). // We always have the SD card inserted, so we can always use SdCache for now. Result result = _baseFileSystemProxy.OpenSaveDataInfoReaderBySaveDataSpaceId( out ReferenceCountedDisposable infoReader, SaveDataSpaceId.SdCache); diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs index 2c3a6500a..dfce2d73e 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs @@ -8,6 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer class IAppletResource : IpcService { private KSharedMemory _hidSharedMem; + private int _hidSharedMemHandle; public IAppletResource(KSharedMemory hidSharedMem) { @@ -18,12 +19,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer // GetSharedMemoryHandle() -> handle public ResultCode GetSharedMemoryHandle(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_hidSharedMem, out int handle) != KernelResult.Success) + if (_hidSharedMemHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_hidSharedMem, out _hidSharedMemHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_hidSharedMemHandle); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index d3bf93195..dfcf2b250 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid private HidAccelerometerParameters _accelerometerParams; private HidVibrationValue _vibrationValue; - public IHidServer(ServiceCtx context) : base(new ServerBase("HidServer")) + public IHidServer(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "HidServer")) { _xpadIdEvent = new KEvent(context.Device.System.KernelContext); _palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext); @@ -559,9 +559,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid ControllerType type = (ControllerType)context.RequestData.ReadInt32(); long appletResourceUserId = context.RequestData.ReadInt64(); - Logger.Stub?.PrintStub(LogClass.ServiceHid, new { - appletResourceUserId, - type + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { + appletResourceUserId, + type }); context.Device.Hid.Npads.SupportedStyleSets = type; @@ -577,9 +577,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid context.ResponseData.Write((int)context.Device.Hid.Npads.SupportedStyleSets); - Logger.Stub?.PrintStub(LogClass.ServiceHid, new { + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, - context.Device.Hid.Npads.SupportedStyleSets + context.Device.Hid.Npads.SupportedStyleSets }); return ResultCode.Success; @@ -704,9 +704,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid long appletResourceUserId = context.RequestData.ReadInt64(); context.Device.Hid.Npads.JoyHold = (NpadJoyHoldType)context.RequestData.ReadInt64(); - Logger.Stub?.PrintStub(LogClass.ServiceHid, new { - appletResourceUserId, - context.Device.Hid.Npads.JoyHold + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { + appletResourceUserId, + context.Device.Hid.Npads.JoyHold }); return ResultCode.Success; @@ -720,9 +720,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid context.ResponseData.Write((long)context.Device.Hid.Npads.JoyHold); - Logger.Stub?.PrintStub(LogClass.ServiceHid, new { - appletResourceUserId, - context.Device.Hid.Npads.JoyHold + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { + appletResourceUserId, + context.Device.Hid.Npads.JoyHold }); return ResultCode.Success; diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index 9d40d80fb..f98581072 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -1,8 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.HLE.HOS.Kernel.Ipc; using System; using System.Collections.Generic; using System.IO; @@ -17,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Services public ServerBase Server { get; private set; } + private IpcService _parent; private IdDictionary _domainObjects; private int _selfId; private bool _isDomain; @@ -32,6 +31,7 @@ namespace Ryujinx.HLE.HOS.Services Server = server; + _parent = this; _domainObjects = new IdDictionary(); _selfId = -1; } @@ -62,8 +62,8 @@ namespace Ryujinx.HLE.HOS.Services int domainWord0 = context.RequestData.ReadInt32(); int domainObjId = context.RequestData.ReadInt32(); - int domainCmd = (domainWord0 >> 0) & 0xff; - int inputObjCount = (domainWord0 >> 8) & 0xff; + int domainCmd = (domainWord0 >> 0) & 0xff; + int inputObjCount = (domainWord0 >> 8) & 0xff; int dataPayloadSize = (domainWord0 >> 16) & 0xffff; context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin); @@ -96,8 +96,8 @@ namespace Ryujinx.HLE.HOS.Services } } - long sfciMagic = context.RequestData.ReadInt64(); - int commandId = (int)context.RequestData.ReadInt64(); + long sfciMagic = context.RequestData.ReadInt64(); + int commandId = (int)context.RequestData.ReadInt64(); bool serviceExists = service.Commands.TryGetValue(commandId, out MethodInfo processRequest); @@ -145,56 +145,37 @@ namespace Ryujinx.HLE.HOS.Services { string dbgMessage = $"{service.GetType().FullName}: {commandId}"; - throw new ServiceNotImplementedException(context, dbgMessage); + throw new ServiceNotImplementedException(service, context, dbgMessage); } } - protected static void MakeObject(ServiceCtx context, IpcService obj) + protected void MakeObject(ServiceCtx context, IpcService obj) { - IpcService service = context.Session.Service; + obj.TrySetServer(_parent.Server); - obj.TrySetServer(service.Server); - - if (service._isDomain) + if (_parent._isDomain) { - context.Response.ObjectIds.Add(service.Add(obj)); + obj._parent = _parent; + + context.Response.ObjectIds.Add(_parent.Add(obj)); } else { - KSession session = new KSession(context.Device.System.KernelContext); + context.Device.System.KernelContext.Syscall.CreateSession(false, 0, out int serverSessionHandle, out int clientSessionHandle); - session.ClientSession.Service = obj; + obj.Server.AddSessionObj(serverSessionHandle, obj); - if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - session.ServerSession.DecrementReferenceCount(); - session.ClientSession.DecrementReferenceCount(); - - context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeMove(clientSessionHandle); } } - protected static T GetObject(ServiceCtx context, int index) where T : IpcService + protected T GetObject(ServiceCtx context, int index) where T : IpcService { - IpcService service = context.Session.Service; - - if (!service._isDomain) - { - int handle = context.Request.HandleDesc.ToMove[index]; - - KClientSession session = context.Process.HandleTable.GetObject(handle); - - return session?.Service is T ? (T)session.Service : null; - } - int objId = context.Request.ObjectIds[index]; - IIpcService obj = service.GetObject(objId); + IIpcService obj = _parent.GetObject(objId); - return obj is T ? (T)obj : null; + return obj is T t ? t : null; } public bool TrySetServer(ServerBase newServer) @@ -230,5 +211,10 @@ namespace Ryujinx.HLE.HOS.Services { return _domainObjects.GetData(id); } + + public void SetParent(IpcService parent) + { + _parent = parent._parent; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs index bc55d1a18..2cd35b9ed 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs @@ -103,98 +103,98 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp // StartDetection(bytes<8, 4>) public ResultCode StartDetection(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(4)] // StopDetection(bytes<8, 4>) public ResultCode StopDetection(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(5)] // Mount(bytes<8, 4>, u32, u32) public ResultCode Mount(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(6)] // Unmount(bytes<8, 4>) public ResultCode Unmount(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(7)] // OpenApplicationArea(bytes<8, 4>, u32) public ResultCode OpenApplicationArea(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(8)] // GetApplicationArea(bytes<8, 4>) -> (u32, buffer) public ResultCode GetApplicationArea(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(9)] // SetApplicationArea(bytes<8, 4>, buffer) public ResultCode SetApplicationArea(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(10)] // Flush(bytes<8, 4>) public ResultCode Flush(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(11)] // Restore(bytes<8, 4>) public ResultCode Restore(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(12)] // CreateApplicationArea(bytes<8, 4>, u32, buffer) public ResultCode CreateApplicationArea(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(13)] // GetTagInfo(bytes<8, 4>) -> buffer, 0x1a> public ResultCode GetTagInfo(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(14)] // GetRegisterInfo(bytes<8, 4>) -> buffer, 0x1a> public ResultCode GetRegisterInfo(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(15)] // GetCommonInfo(bytes<8, 4>) -> buffer, 0x1a> public ResultCode GetCommonInfo(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(16)] // GetModelInfo(bytes<8, 4>) -> buffer, 0x1a> public ResultCode GetModelInfo(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(17)] @@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp // GetApplicationAreaSize(bytes<8, 4>) -> u32 public ResultCode GetApplicationAreaSize(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(23)] // 3.0.0+ @@ -334,7 +334,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp // RecreateApplicationArea(bytes<8, 4>, u32, buffer) public ResultCode RecreateApplicationArea(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs index 2f6be8bf6..51f9487f5 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs @@ -11,6 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService private KEvent _event0; private KEvent _event1; + private int _event0Handle; + private int _event1Handle; + private uint _version; public IRequest(Horizon system, uint version) @@ -50,17 +53,23 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // GetSystemEventReadableHandles() -> (handle, handle) public ResultCode GetSystemEventReadableHandles(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_event0.ReadableEvent, out int handle0) != KernelResult.Success) + if (_event0Handle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_event0.ReadableEvent, out _event0Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - if (context.Process.HandleTable.GenerateHandle(_event1.ReadableEvent, out int handle1) != KernelResult.Success) + if (_event1Handle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_event1.ReadableEvent, out _event1Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle0, handle1); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_event0Handle, _event1Handle); return ResultCode.Success; } @@ -107,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Unknown180; } - // Returns appletId, libraryAppletMode, outSize and a buffer. + // Returns appletId, libraryAppletMode, outSize and a buffer. // Returned applet ids- (0x19, 0xf, 0xe) // libraryAppletMode seems to be 0 for all applets supported. diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs index 8cfafb1ad..e6fca499c 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs @@ -11,6 +11,8 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServ { private readonly KEvent _event; + private int _eventHandle; + public IShopServiceAccessor(Horizon system) { _event = new KEvent(system.KernelContext); @@ -22,12 +24,15 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServ { MakeObject(context, new IShopServiceAsync()); - if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.Success) + if (_eventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle); Logger.Stub?.PrintStub(LogClass.ServiceNim); diff --git a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs index e9f27cb56..debcd57e3 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs @@ -9,7 +9,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns [Service("aoc:u")] class IAddOnContentManager : IpcService { - KEvent _addOnContentListChangedEvent; + private readonly KEvent _addOnContentListChangedEvent; + + private int _addOnContentListChangedEventHandle; public IAddOnContentManager(ServiceCtx context) { @@ -22,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns { long pid = context.Process.Pid; - // Official code checks ApplicationControlProperty.RuntimeAddOnContentInstall + // Official code checks ApplicationControlProperty.RuntimeAddOnContentInstall // if true calls ns:am ListAvailableAddOnContent again to get updated count byte runtimeAddOnContentInstall = context.Device.Application.ControlData.Value.RuntimeAddOnContentInstall; @@ -135,12 +137,15 @@ namespace Ryujinx.HLE.HOS.Services.Ns { // Official code seems to make an internal call to ns:am Cmd 84 GetDynamicCommitEvent() - if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_addOnContentListChangedEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out _addOnContentListChangedEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_addOnContentListChangedEventHandle); Logger.Stub?.PrintStub(LogClass.ServiceNs); @@ -148,7 +153,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns } - [Command(9)] // [10.0.0+] + [Command(9)] // [10.0.0+] // GetAddOnContentLostErrorCode() -> u64 public ResultCode GetAddOnContentLostErrorCode(ServiceCtx context) { diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 0812683b7..9f1ace36b 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -4,7 +4,6 @@ using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Memory; -using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel; @@ -12,6 +11,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using Ryujinx.HLE.HOS.Services.Nv.Types; +using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Reflection; @@ -27,47 +27,45 @@ namespace Ryujinx.HLE.HOS.Services.Nv private static Dictionary _deviceFileRegistry = new Dictionary() { - { "/dev/nvmap", typeof(NvMapDeviceFile) }, - { "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) }, - { "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) }, - { "/dev/nvhost-as-gpu", typeof(NvHostAsGpuDeviceFile) }, - { "/dev/nvhost-gpu", typeof(NvHostGpuDeviceFile) }, - //{ "/dev/nvhost-msenc", typeof(NvHostChannelDeviceFile) }, - { "/dev/nvhost-nvdec", typeof(NvHostChannelDeviceFile) }, - //{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) }, - { "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) }, - //{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) }, + { "/dev/nvmap", typeof(NvMapDeviceFile) }, + { "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) }, + { "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) }, + { "/dev/nvhost-as-gpu", typeof(NvHostAsGpuDeviceFile) }, + { "/dev/nvhost-gpu", typeof(NvHostGpuDeviceFile) }, + //{ "/dev/nvhost-msenc", typeof(NvHostChannelDeviceFile) }, + { "/dev/nvhost-nvdec", typeof(NvHostChannelDeviceFile) }, + //{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) }, + { "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) }, + //{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) }, }; private static IdDictionary _deviceFileIdRegistry = new IdDictionary(); - private KProcess _owner; + private IVirtualMemoryManager _clientMemory; + private long _owner; private bool _transferMemInitialized = false; - public INvDrvServices(ServiceCtx context) : base(new ServerBase("NvservicesServer")) + public INvDrvServices(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "NvservicesServer")) { - _owner = null; + _owner = 0; } private int Open(ServiceCtx context, string path) { - if (context.Process == _owner) + if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass)) { - if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass)) - { - ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx) }); + ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx), typeof(IVirtualMemoryManager), typeof(long) }); - NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke(new object[] { context }); + NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke(new object[] { context, _clientMemory, _owner }); - deviceFile.Path = path; + deviceFile.Path = path; - return _deviceFileIdRegistry.Add(deviceFile); - } - else - { - Logger.Warning?.Print(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!"); - } + return _deviceFileIdRegistry.Add(deviceFile); + } + else + { + Logger.Warning?.Print(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!"); } return -1; @@ -150,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return NvResult.NotImplemented; } - if (deviceFile.Owner.Pid != _owner.Pid) + if (deviceFile.Owner != _owner) { return NvResult.AccessDenied; } @@ -160,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv private NvResult EnsureInitialized() { - if (_owner == null) + if (_owner == 0) { Logger.Warning?.Print(LogClass.ServiceNv, "INvDrvServices is not initialized!"); @@ -229,8 +227,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { long pathPtr = context.Request.SendBuff[0].Position; + long pathSize = context.Request.SendBuff[0].Size; - string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr); + string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, pathSize); fd = Open(context, path); @@ -322,7 +321,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv // TODO: When transfer memory will be implemented, this could be removed. _transferMemInitialized = true; - _owner = context.Process; + int clientHandle = context.Request.HandleDesc.ToCopy[0]; + + _clientMemory = context.Process.HandleTable.GetKProcess(clientHandle).CpuMemory; + + context.Device.System.KernelContext.Syscall.GetProcessId(clientHandle, out _owner); context.ResponseData.Write((uint)NvResult.Success); @@ -425,7 +428,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv // ForceSetClientPid(u64) -> u32 error_code public ResultCode ForceSetClientPid(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(8)] @@ -452,7 +455,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv // InitializeDevtools(u32, handle) -> u32 error_code; public ResultCode InitializeDevtools(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(11)] // 3.0.0+ diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs index af4734ea9..34ff4ccb4 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Memory; -using Ryujinx.HLE.HOS.Kernel.Process; using System; using System.Diagnostics; using System.Reflection; @@ -12,14 +11,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices abstract class NvDeviceFile { public readonly ServiceCtx Context; - public readonly KProcess Owner; + public readonly long Owner; public string Path; - public NvDeviceFile(ServiceCtx context) + public NvDeviceFile(ServiceCtx context, long owner) { Context = context; - Owner = context.Process; + Owner = owner; } public virtual NvInternalResult QueryEvent(out int eventHandle, uint eventId) diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index 0000f4959..6c49fd5ca 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; +using Ryujinx.Memory; using System; using System.Collections.Concurrent; @@ -12,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu { private static ConcurrentDictionary _addressSpaceContextRegistry = new ConcurrentDictionary(); - public NvHostAsGpuDeviceFile(ServiceCtx context) : base(context) { } + public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) { } public override NvInternalResult Ioctl(NvIoctl command, Span arguments) { diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs index 863b01d1d..d675ffc70 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs @@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using Ryujinx.HLE.HOS.Services.Nv.Types; +using Ryujinx.Memory; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -19,9 +20,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private uint _submitTimeout; private uint _timeslice; - private Switch _device; + private readonly Switch _device; - private Cpu.MemoryManager _memory; + private readonly IVirtualMemoryManager _memory; public enum ResourcePolicy { @@ -37,10 +38,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private NvFence _channelSyncpoint; - public NvHostChannelDeviceFile(ServiceCtx context) : base(context) + public NvHostChannelDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) { _device = context.Device; - _memory = context.Memory; + _memory = memory; _timeout = 3000; _submitTimeout = 0; _timeslice = 0; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs index 523891404..f27c065ed 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types; +using Ryujinx.Memory; using System; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel @@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private KEvent _smExceptionBptPauseReportEvent; private KEvent _errorNotifierEvent; - public NvHostGpuDeviceFile(ServiceCtx context) : base(context) + public NvHostGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, memory, owner) { _smExceptionBptIntReportEvent = new KEvent(context.Device.System.KernelContext); _smExceptionBptPauseReportEvent = new KEvent(context.Device.System.KernelContext); @@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel if (targetEvent != null) { - if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) + if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs index a53845960..f0e5634e6 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs @@ -5,7 +5,7 @@ using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types; using Ryujinx.HLE.HOS.Services.Nv.Types; using Ryujinx.HLE.HOS.Services.Settings; - +using Ryujinx.Memory; using System; using System.Text; using System.Threading; @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl private Switch _device; private NvHostEvent[] _events; - public NvHostCtrlDeviceFile(ServiceCtx context) : base(context) + public NvHostCtrlDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) { if (NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object productionModeSetting)) { @@ -126,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl if (targetEvent != null) { - if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) + if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs index e0acf0dfd..840d95b93 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs @@ -2,6 +2,7 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types; +using Ryujinx.Memory; using System; using System.Diagnostics; @@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu private KEvent _errorEvent; private KEvent _unknownEvent; - public NvHostCtrlGpuDeviceFile(ServiceCtx context) : base(context) + public NvHostCtrlGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) { _errorEvent = new KEvent(context.Device.System.KernelContext); _unknownEvent = new KEvent(context.Device.System.KernelContext); @@ -98,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu if (targetEvent != null) { - if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) + if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs index 3cc47c422..a549c81ca 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs @@ -1,7 +1,7 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu.Memory; -using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.Memory; using System; using System.Collections.Concurrent; @@ -11,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { private const int FlagNotFreedYet = 1; - private static ConcurrentDictionary _maps = new ConcurrentDictionary(); + private static ConcurrentDictionary _maps = new ConcurrentDictionary(); - public NvMapDeviceFile(ServiceCtx context) : base(context) + public NvMapDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) { IdDictionary dict = _maps.GetOrAdd(Owner, (key) => new IdDictionary()); @@ -244,9 +244,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap return dict.Add(map); } - private static bool DeleteMapWithHandle(KProcess process, int handle) + private static bool DeleteMapWithHandle(long pid, int handle) { - if (_maps.TryGetValue(process, out IdDictionary dict)) + if (_maps.TryGetValue(pid, out IdDictionary dict)) { return dict.Delete(handle) != null; } @@ -254,14 +254,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap return false; } - public static void IncrementMapRefCount(KProcess process, int handle, bool allowHandleZero = false) + public static void IncrementMapRefCount(long pid, int handle, bool allowHandleZero = false) { - GetMapFromHandle(process, handle, allowHandleZero)?.IncrementRefCount(); + GetMapFromHandle(pid, handle, allowHandleZero)?.IncrementRefCount(); } - public static bool DecrementMapRefCount(KProcess process, int handle) + public static bool DecrementMapRefCount(long pid, int handle) { - NvMapHandle map = GetMapFromHandle(process, handle, false); + NvMapHandle map = GetMapFromHandle(pid, handle, false); if (map == null) { @@ -270,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap if (map.DecrementRefCount() <= 0) { - DeleteMapWithHandle(process, handle); + DeleteMapWithHandle(pid, handle); Logger.Info?.Print(LogClass.ServiceNv, $"Deleted map {handle}!"); @@ -282,9 +282,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap } } - public static NvMapHandle GetMapFromHandle(KProcess process, int handle, bool allowHandleZero = false) + public static NvMapHandle GetMapFromHandle(long pid, int handle, bool allowHandleZero = false) { - if ((allowHandleZero || handle != 0) && _maps.TryGetValue(process, out IdDictionary dict)) + if ((allowHandleZero || handle != 0) && _maps.TryGetValue(pid, out IdDictionary dict)) { return dict.GetData(handle); } diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index 46374acc4..c78634faf 100644 --- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.InvalidAddress; } - StructReader reader = new StructReader(context.Memory, nrrAddress); + StructReader reader = new StructReader(_owner.CpuMemory, nrrAddress); NrrHeader header = reader.Read(); if (header.Magic != NrrMagic) @@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro { byte[] temp = new byte[0x20]; - context.Memory.Read((ulong)(nrrAddress + header.HashOffset + (i * 0x20)), temp); + _owner.CpuMemory.Read((ulong)(nrrAddress + header.HashOffset + (i * 0x20)), temp); hashes.Add(temp); } @@ -131,8 +131,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.InvalidAddress; } - uint magic = context.Memory.Read(nroAddress + 0x10); - uint nroFileSize = context.Memory.Read(nroAddress + 0x18); + uint magic = _owner.CpuMemory.Read(nroAddress + 0x10); + uint nroFileSize = _owner.CpuMemory.Read(nroAddress + 0x18); if (magic != NroMagic || nroSize != nroFileSize) { @@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro byte[] nroData = new byte[nroSize]; - context.Memory.Read(nroAddress, nroData); + _owner.CpuMemory.Read(nroAddress, nroData); byte[] nroHash = null; @@ -176,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro // Check if everything is contiguous. if (nro.RoOffset != nro.TextOffset + nro.Text.Length || nro.DataOffset != nro.RoOffset + nro.Ro.Length || - nroFileSize != nro.DataOffset + nro.Data.Length) + nroFileSize != nro.DataOffset + nro.Data.Length) { return ResultCode.InvalidNro; } @@ -337,21 +337,21 @@ namespace Ryujinx.HLE.HOS.Services.Ro KernelResult result; - result = process.MemoryManager.SetProcessMemoryPermission(textStart, roStart - textStart, MemoryPermission.ReadAndExecute); + result = process.MemoryManager.SetProcessMemoryPermission(textStart, roStart - textStart, KMemoryPermission.ReadAndExecute); if (result != KernelResult.Success) { return result; } - result = process.MemoryManager.SetProcessMemoryPermission(roStart, dataStart - roStart, MemoryPermission.Read); + result = process.MemoryManager.SetProcessMemoryPermission(roStart, dataStart - roStart, KMemoryPermission.Read); if (result != KernelResult.Success) { return result; } - return process.MemoryManager.SetProcessMemoryPermission(dataStart, bssEnd - dataStart, MemoryPermission.ReadAndWrite); + return process.MemoryManager.SetProcessMemoryPermission(dataStart, bssEnd - dataStart, KMemoryPermission.ReadAndWrite); } private ResultCode RemoveNrrInfo(long nrrAddress) @@ -420,9 +420,9 @@ namespace Ryujinx.HLE.HOS.Services.Ro return (ResultCode)result; } - private ResultCode IsInitialized(KProcess process) + private ResultCode IsInitialized(long pid) { - if (_owner != null && _owner.Pid == process.Pid) + if (_owner != null && _owner.Pid == pid) { return ResultCode.Success; } @@ -434,7 +434,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro // LoadNro(u64, u64, u64, u64, u64, pid) -> u64 public ResultCode LoadNro(ServiceCtx context) { - ResultCode result = IsInitialized(context.Process); + ResultCode result = IsInitialized(_owner.Pid); // Zero context.RequestData.ReadUInt64(); @@ -454,11 +454,11 @@ namespace Ryujinx.HLE.HOS.Services.Ro if (result == ResultCode.Success) { - result = MapNro(context.Process, info, out nroMappedAddress); + result = MapNro(_owner, info, out nroMappedAddress); if (result == ResultCode.Success) { - result = (ResultCode)SetNroMemoryPermissions(context.Process, info.Executable, nroMappedAddress); + result = (ResultCode)SetNroMemoryPermissions(_owner, info.Executable, nroMappedAddress); if (result == ResultCode.Success) { @@ -479,7 +479,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro // UnloadNro(u64, u64, pid) public ResultCode UnloadNro(ServiceCtx context) { - ResultCode result = IsInitialized(context.Process); + ResultCode result = IsInitialized(_owner.Pid); // Zero context.RequestData.ReadUInt64(); @@ -503,7 +503,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro // LoadNrr(u64, u64, u64, pid) public ResultCode LoadNrr(ServiceCtx context) { - ResultCode result = IsInitialized(context.Process); + ResultCode result = IsInitialized(_owner.Pid); // pid placeholder, zero context.RequestData.ReadUInt64(); @@ -536,7 +536,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro // UnloadNrr(u64, u64, pid) public ResultCode UnloadNrr(ServiceCtx context) { - ResultCode result = IsInitialized(context.Process); + ResultCode result = IsInitialized(_owner.Pid); // pid placeholder, zero context.RequestData.ReadUInt64(); @@ -565,7 +565,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.InvalidSession; } - _owner = context.Process; + _owner = context.Process.HandleTable.GetKProcess(context.Request.HandleDesc.ToCopy[0]); + context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs index 001c38e2d..5be54f009 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs @@ -9,6 +9,8 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl [Service("pl:s")] // 9.0.0+ class ISharedFontManager : IpcService { + private int _fontSharedMemHandle; + public ISharedFontManager(ServiceCtx context) { } [Command(0)] @@ -63,12 +65,15 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl { context.Device.System.Font.EnsureInitialized(context.Device.System.ContentManager); - if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out int handle) != KernelResult.Success) + if (_fontSharedMemHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out _fontSharedMemHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_fontSharedMemHandle); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 211e0e6be..f70d930fa 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -1,62 +1,193 @@ -using Ryujinx.Common; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using System; +using System.Buffers.Binary; +using System.Collections.Generic; using System.IO; +using System.Threading; namespace Ryujinx.HLE.HOS.Services { class ServerBase { - private struct IpcRequest - { - public Switch Device { get; } - public KProcess Process => Thread?.Owner; - public KThread Thread { get; } - public KClientSession Session { get; } - public ulong MessagePtr { get; } - public ulong MessageSize { get; } + // Must be the maximum value used by services (highest one know is the one used by nvservices = 0x8000). + // Having a size that is too low will cause failures as data copy will fail if the receiving buffer is + // not large enough. + private const int PointerBufferSize = 0x8000; - public IpcRequest(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize) + private readonly static int[] DefaultCapabilities = new int[] + { + 0x030363F7, + 0x1FFFFFCF, + 0x207FFFEF, + 0x47E0060F, + 0x0048BFFF, + 0x01007FFF + }; + + private readonly KernelContext _context; + private readonly KProcess _selfProcess; + + private readonly List _sessionHandles = new List(); + private readonly List _portHandles = new List(); + private readonly Dictionary _sessions = new Dictionary(); + private readonly Dictionary _ports = new Dictionary(); + + public ManualResetEvent InitDone { get; } + public IpcService SmObject { get; set; } + public string Name { get; } + + public ServerBase(KernelContext context, string name) + { + InitDone = new ManualResetEvent(false); + Name = name; + _context = context; + + const ProcessCreationFlags flags = + ProcessCreationFlags.EnableAslr | + ProcessCreationFlags.AddressSpace64Bit | + ProcessCreationFlags.Is64Bit | + ProcessCreationFlags.PoolPartitionSystem; + + ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); + + context.Syscall.CreateProcess(creationInfo, DefaultCapabilities, out int handle, null, ServerLoop); + + _selfProcess = context.Scheduler.GetCurrentProcess().HandleTable.GetKProcess(handle); + + context.Syscall.StartProcess(handle, 44, 3, 0x1000); + } + + private void AddPort(int serverPortHandle, IpcService obj) + { + _portHandles.Add(serverPortHandle); + _ports.Add(serverPortHandle, obj); + } + + public void AddSessionObj(KServerSession serverSession, IpcService obj) + { + _selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle); + AddSessionObj(serverSessionHandle, obj); + } + + public void AddSessionObj(int serverSessionHandle, IpcService obj) + { + _sessionHandles.Add(serverSessionHandle); + _sessions.Add(serverSessionHandle, obj); + } + + private void ServerLoop() + { + if (SmObject != null) { - Device = device; - Thread = thread; - Session = session; - MessagePtr = messagePtr; - MessageSize = messageSize; + _context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle); + + AddPort(serverPortHandle, SmObject); + + InitDone.Set(); + } + else + { + InitDone.Dispose(); } - public void SignalDone(KernelResult result) + KThread thread = _context.Scheduler.GetCurrentThread(); + ulong messagePtr = thread.TlsAddress; + _context.Syscall.SetHeapSize(0x200000, out ulong heapAddr); + + _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0); + _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10); + _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48)); + + int replyTargetHandle = 0; + + while (true) { - Thread.ObjSyncResult = result; - Thread.Reschedule(ThreadSchedState.Running); + int[] handles = _portHandles.ToArray(); + + for (int i = 0; i < handles.Length; i++) + { + if (_context.Syscall.AcceptSession(handles[i], out int serverSessionHandle) == KernelResult.Success) + { + AddSessionObj(serverSessionHandle, _ports[handles[i]]); + } + } + + handles = _sessionHandles.ToArray(); + + var rc = _context.Syscall.ReplyAndReceive(handles, replyTargetHandle, 1000000L, out int signaledIndex); + + thread.HandlePostSyscall(); + + if (!thread.Context.Running) + { + break; + } + + replyTargetHandle = 0; + + if (rc == KernelResult.Success && signaledIndex != -1) + { + int signaledHandle = handles[signaledIndex]; + + if (Process(signaledHandle, heapAddr)) + { + replyTargetHandle = signaledHandle; + } + } + else + { + _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0); + _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10); + _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48)); + } } } - private readonly AsyncWorkQueue _ipcProcessor; - - public ServerBase(string name) + private bool Process(int serverSessionHandle, ulong recvListAddr) { - _ipcProcessor = new AsyncWorkQueue(Process, name); - } + KProcess process = _context.Scheduler.GetCurrentProcess(); + KThread thread = _context.Scheduler.GetCurrentThread(); + ulong messagePtr = thread.TlsAddress; + ulong messageSize = 0x100; - public void PushMessage(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize) - { - _ipcProcessor.Add(new IpcRequest(device, thread, session, messagePtr, messageSize)); - } + byte[] reqData = new byte[messageSize]; - private void Process(IpcRequest message) - { - byte[] reqData = new byte[message.MessageSize]; + process.CpuMemory.Read(messagePtr, reqData); - message.Process.CpuMemory.Read(message.MessagePtr, reqData); - - IpcMessage request = new IpcMessage(reqData, (long)message.MessagePtr); + IpcMessage request = new IpcMessage(reqData, (long)messagePtr); IpcMessage response = new IpcMessage(); + ulong tempAddr = recvListAddr; + int sizesOffset = request.RawData.Length - ((request.RecvListBuff.Count * 2 + 3) & ~3); + + bool noReceive = true; + + for (int i = 0; i < request.ReceiveBuff.Count; i++) + { + noReceive &= (request.ReceiveBuff[i].Position == 0); + } + + if (noReceive) + { + for (int i = 0; i < request.RecvListBuff.Count; i++) + { + int size = BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2)); + + response.PtrBuff.Add(new IpcPtrBuffDesc((long)tempAddr, i, size)); + + request.RecvListBuff[i] = new IpcRecvListBuffDesc((long)tempAddr, size); + + tempAddr += (ulong)size; + } + } + + bool shouldReply = true; + using (MemoryStream raw = new MemoryStream(request.RawData)) { BinaryReader reqReader = new BinaryReader(raw); @@ -71,17 +202,16 @@ namespace Ryujinx.HLE.HOS.Services BinaryWriter resWriter = new BinaryWriter(resMs); ServiceCtx context = new ServiceCtx( - message.Device, - message.Process, - message.Process.CpuMemory, - message.Thread, - message.Session, + _context.Device, + process, + process.CpuMemory, + thread, request, response, reqReader, resWriter); - message.Session.Service.CallMethod(context); + _sessions[serverSessionHandle].CallMethod(context); response.RawData = resMs.ToArray(); } @@ -95,11 +225,11 @@ namespace Ryujinx.HLE.HOS.Services switch (cmdId) { case 0: - request = FillResponse(response, 0, message.Session.Service.ConvertToDomain()); + request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain()); break; case 3: - request = FillResponse(response, 0, 0x1000); + request = FillResponse(response, 0, PointerBufferSize); break; // TODO: Whats the difference between IpcDuplicateSession/Ex? @@ -107,12 +237,11 @@ namespace Ryujinx.HLE.HOS.Services case 4: int unknown = reqReader.ReadInt32(); - if (message.Process.HandleTable.GenerateHandle(message.Session, out int handle) != KernelResult.Success) - { - throw new InvalidOperationException("Out of handles!"); - } + _context.Syscall.CreateSession(false, 0, out int dupServerSessionHandle, out int dupClientSessionHandle); - response.HandleDesc = IpcHandleDesc.MakeMove(handle); + AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]); + + response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle); request = FillResponse(response, 0); @@ -123,18 +252,24 @@ namespace Ryujinx.HLE.HOS.Services } else if (request.Type == IpcMessageType.CloseSession) { - message.SignalDone(KernelResult.PortRemoteClosed); - return; + _context.Syscall.CloseHandle(serverSessionHandle); + _sessionHandles.Remove(serverSessionHandle); + IpcService service = _sessions[serverSessionHandle]; + if (service is IDisposable disposableObj) + { + disposableObj.Dispose(); + } + _sessions.Remove(serverSessionHandle); + shouldReply = false; } else { throw new NotImplementedException(request.Type.ToString()); } - message.Process.CpuMemory.Write(message.MessagePtr, response.GetBytes((long)message.MessagePtr)); + process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48))); + return shouldReply; } - - message.SignalDone(KernelResult.Success); } private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values) diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index d8f31faf5..69bb37080 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Ipc; using System; @@ -11,18 +12,17 @@ using System.Reflection; namespace Ryujinx.HLE.HOS.Services.Sm { - [Service("sm:")] class IUserInterface : IpcService { private Dictionary _services; - private ConcurrentDictionary _registeredServices; + private readonly ConcurrentDictionary _registeredServices; private readonly ServerBase _commonServer; private bool _isInitialized; - public IUserInterface(ServiceCtx context = null) : base(new ServerBase("SmServer")) + public IUserInterface(KernelContext context) { _registeredServices = new ConcurrentDictionary(); @@ -31,18 +31,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm .Select(service => (((ServiceAttribute)service).Name, type))) .ToDictionary(service => service.Name, service => service.type); - _commonServer = new ServerBase("CommonServer"); - } + TrySetServer(new ServerBase(context, "SmServer") { SmObject = this }); - public static void InitializePort(Horizon system) - { - KPort port = new KPort(system.KernelContext, 256, false, 0); - - port.ClientPort.SetName("sm:"); - - IUserInterface smService = new IUserInterface(); - - port.ClientPort.Service = smService; + _commonServer = new ServerBase(context, "CommonServer"); } [Command(0)] @@ -92,16 +83,13 @@ namespace Ryujinx.HLE.HOS.Services.Sm : (IpcService)Activator.CreateInstance(type, context); service.TrySetServer(_commonServer); - - session.ClientSession.Service = service; + service.Server.AddSessionObj(session.ServerSession, service); } else { if (ServiceConfiguration.IgnoreMissingServices) { Logger.Warning?.Print(LogClass.Service, $"Missing service {name} ignored"); - - session.ClientSession.Service = new DummyService(name); } else { @@ -142,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm int maxSessions = context.RequestData.ReadInt32(); - if (name == string.Empty) + if (string.IsNullOrEmpty(name)) { return ResultCode.InvalidName; } @@ -185,7 +173,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm int maxSessions = context.RequestData.ReadInt32(); - if (name == string.Empty) + if (string.IsNullOrEmpty(name)) { return ResultCode.InvalidName; } diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs index 4ed621288..f7dd7e172 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd private List _sockets = new List(); - public IClient(ServiceCtx context, bool isPrivileged) : base(new ServerBase("BsdServer")) + public IClient(ServiceCtx context, bool isPrivileged) : base(new ServerBase(context.Device.System.KernelContext, "BsdServer")) { _isPrivileged = isPrivileged; } @@ -247,6 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd Logger.Stub?.PrintStub(LogClass.ServiceBsd); + // Close transfer memory immediately as we don't use it. + context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); + return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs index 02a508b01..90f22dfb4 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs @@ -118,14 +118,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd // ImportSettings(u32, buffer) -> buffer public ResultCode ImportSettings(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(15)] // Unknown(bytes<1>) public ResultCode Unknown(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(20)] @@ -164,49 +164,49 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd // GetNasServiceSetting(buffer, 0x15>) -> buffer, 0x16> public ResultCode GetNasServiceSetting(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(31)] // GetNasServiceSettingEx(buffer, 0x15>) -> (u32, buffer, 0x16>) public ResultCode GetNasServiceSettingEx(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(40)] // GetNasRequestFqdn() -> buffer, 0x16> public ResultCode GetNasRequestFqdn(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(41)] // GetNasRequestFqdnEx() -> (u32, buffer, 0x16>) public ResultCode GetNasRequestFqdnEx(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(42)] // GetNasApiFqdn() -> buffer, 0x16> public ResultCode GetNasApiFqdn(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(43)] // GetNasApiFqdnEx() -> (u32, buffer, 0x16>) public ResultCode GetNasApiFqdnEx(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(50)] // GetCurrentSetting() -> buffer, 0x16> public ResultCode GetCurrentSetting(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(60)] @@ -262,7 +262,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd // IsChangeEnvironmentIdentifierDisabled() -> bytes<1> public ResultCode IsChangeEnvironmentIdentifierDisabled(ServiceCtx context) { - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs index 3dd21fde9..2a9a60644 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs @@ -1,15 +1,15 @@ -using Ryujinx.HLE.HOS.Kernel.Process; - -namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger +namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { - class BufferQueue + static class BufferQueue { - public static void CreateBufferQueue(Switch device, KProcess process, out BufferQueueProducer producer, out BufferQueueConsumer consumer) + public static BufferQueueCore CreateBufferQueue(Switch device, long pid, out BufferQueueProducer producer, out BufferQueueConsumer consumer) { - BufferQueueCore core = new BufferQueueCore(device, process); + BufferQueueCore core = new BufferQueueCore(device, pid); producer = new BufferQueueProducer(core); consumer = new BufferQueueConsumer(core); + + return core; } } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs index 7f6f6c310..389a980bb 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs @@ -1,5 +1,5 @@ using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; using System; @@ -40,11 +40,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private KEvent _waitBufferFreeEvent; private KEvent _frameAvailableEvent; - public KProcess Owner { get; } + public long Owner { get; } + + public bool Active { get; private set; } public const int BufferHistoryArraySize = 8; - public BufferQueueCore(Switch device, KProcess process) + public BufferQueueCore(Switch device, long pid) { Slots = new BufferSlotArray(); IsAbandoned = false; @@ -70,7 +72,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger _waitBufferFreeEvent = new KEvent(device.System.KernelContext); _frameAvailableEvent = new KEvent(device.System.KernelContext); - Owner = process; + Owner = pid; + + Active = true; BufferHistory = new BufferInfo[BufferHistoryArraySize]; EnableExternalEvent = true; @@ -162,6 +166,16 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } } + public void PrepareForExit() + { + lock (Lock) + { + Active = false; + + Monitor.PulseAll(Lock); + } + } + // TODO: Find an accurate way to handle a regular condvar here as this will wake up unwanted threads in some edge cases. public void SignalDequeueEvent() { @@ -170,7 +184,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void WaitDequeueEvent() { - Monitor.Wait(Lock); + Monitor.Exit(Lock); + + KernelStatic.YieldUntilCompletion(WaitForLock); + + Monitor.Enter(Lock); } public void SignalIsAllocatingEvent() @@ -180,7 +198,22 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void WaitIsAllocatingEvent() { - Monitor.Wait(Lock); + Monitor.Exit(Lock); + + KernelStatic.YieldUntilCompletion(WaitForLock); + + Monitor.Enter(Lock); + } + + private void WaitForLock() + { + lock (Lock) + { + if (Active) + { + Monitor.Wait(Lock); + } + } } public void FreeBufferLocked(int slot) diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs index 6ef49538e..03df04adb 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs @@ -816,6 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } Core.WaitDequeueEvent(); + + if (!Core.Active) + { + break; + } } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index 8ee943df8..b3c813819 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { abstract class IHOSBinderDriver : IpcService { - public IHOSBinderDriver() {} + public IHOSBinderDriver() { } [Command(0)] // TransactParcel(s32, u32, u32, buffer) -> buffer diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 4713e50b6..4927b40ec 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -1,10 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; -using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; -using Ryujinx.HLE.HOS.Services.Nv.Types; using System; using System.Collections.Generic; using System.Diagnostics; @@ -40,7 +37,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public int ProducerBinderId; public IGraphicBufferProducer Producer; public BufferItemConsumer Consumer; - public KProcess Owner; + public BufferQueueCore Core; + public long Owner; } private class TextureCallbackInformation @@ -84,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } } - public IGraphicBufferProducer OpenLayer(KProcess process, long layerId) + public IGraphicBufferProducer OpenLayer(long pid, long layerId) { bool needCreate; @@ -95,13 +93,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (needCreate) { - CreateLayerFromId(process, layerId); + CreateLayerFromId(pid, layerId); } return GetProducerByLayerId(layerId); } - public IGraphicBufferProducer CreateLayer(KProcess process, out long layerId) + public IGraphicBufferProducer CreateLayer(long pid, out long layerId) { layerId = 1; @@ -116,25 +114,26 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } } - CreateLayerFromId(process, layerId); + CreateLayerFromId(pid, layerId); return GetProducerByLayerId(layerId); } - private void CreateLayerFromId(KProcess process, long layerId) + private void CreateLayerFromId(long pid, long layerId) { lock (Lock) { Logger.Info?.Print(LogClass.SurfaceFlinger, $"Creating layer {layerId}"); - BufferQueue.CreateBufferQueue(_device, process, out BufferQueueProducer producer, out BufferQueueConsumer consumer); + BufferQueueCore core = BufferQueue.CreateBufferQueue(_device, pid, out BufferQueueProducer producer, out BufferQueueConsumer consumer); _layers.Add(layerId, new Layer { ProducerBinderId = HOSBinderDriverServer.RegisterBinderObject(producer), Producer = producer, Consumer = new BufferItemConsumer(_device, consumer, 0, -1, false, this), - Owner = process + Core = core, + Owner = pid }); LastId = layerId; @@ -345,6 +344,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void Dispose() { _isRunning = false; + + foreach (Layer layer in _layers.Values) + { + layer.Core.PrepareForExit(); + } } public void OnFrameAvailable(ref BufferItem item) diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs index 8d63d9ccc..d86ff21e8 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs @@ -42,23 +42,23 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Buffer = parcel.ReadUnmanagedType(); } - public void IncrementNvMapHandleRefCount(KProcess process) + public void IncrementNvMapHandleRefCount(long pid) { - NvMapDeviceFile.IncrementMapRefCount(process, Buffer.NvMapId); + NvMapDeviceFile.IncrementMapRefCount(pid, Buffer.NvMapId); for (int i = 0; i < Buffer.Surfaces.Length; i++) { - NvMapDeviceFile.IncrementMapRefCount(process, Buffer.Surfaces[i].NvMapHandle); + NvMapDeviceFile.IncrementMapRefCount(pid, Buffer.Surfaces[i].NvMapHandle); } } - public void DecrementNvMapHandleRefCount(KProcess process) + public void DecrementNvMapHandleRefCount(long pid) { - NvMapDeviceFile.DecrementMapRefCount(process, Buffer.NvMapId); + NvMapDeviceFile.DecrementMapRefCount(pid, Buffer.NvMapId); for (int i = 0; i < Buffer.Surfaces.Length; i++) { - NvMapDeviceFile.DecrementMapRefCount(process, Buffer.Surfaces[i].NvMapHandle); + NvMapDeviceFile.DecrementMapRefCount(pid, Buffer.Surfaces[i].NvMapHandle); } } diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs index 743b9e13b..ecee7206f 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs @@ -15,10 +15,12 @@ namespace Ryujinx.HLE.HOS.Services.Time private IStaticServiceForPsc _inner; private TimePermissions _permissions; - public IStaticServiceForGlue(ServiceCtx context, TimePermissions permissions) : base(new ServerBase("TimeServer")) + public IStaticServiceForGlue(ServiceCtx context, TimePermissions permissions) : base(new ServerBase(context.Device.System.KernelContext, "TimeServer")) { _permissions = permissions; _inner = new IStaticServiceForPsc(context, permissions); + _inner.TrySetServer(Server); + _inner.SetParent(this); } [Command(0)] diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs index 64b213816..aeb0e8a8c 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs @@ -149,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public ResultCode Unknown50(ServiceCtx context) { // TODO: figure out the usage of this event - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(51)] @@ -157,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public ResultCode Unknown51(ServiceCtx context) { // TODO: figure out the usage of this event - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(52)] @@ -165,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public ResultCode Unknown52(ServiceCtx context) { // TODO: figure out the usage of this event - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(60)] @@ -201,7 +201,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public ResultCode GetAlarmRegistrationEvent(ServiceCtx context) { // TODO - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(201)] @@ -209,7 +209,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public ResultCode UpdateSteadyAlarms(ServiceCtx context) { // TODO - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } [Command(202)] @@ -217,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public ResultCode TryGetNextSteadyClockAlarmSnapshot(ServiceCtx context) { // TODO - throw new ServiceNotImplementedException(context); + throw new ServiceNotImplementedException(this, context); } } } diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs index 22ffe6561..ee99da775 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs @@ -5,8 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi [Service("vi:u")] class IApplicationRootService : IpcService { - // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase - public IApplicationRootService(ServiceCtx context) : base(new ServerBase("ViServerU")) { } + public IApplicationRootService(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "ViServerU")) { } [Command(0)] // GetDisplayService(u32) -> object diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs index a90690ea5..6157a16f8 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi class IManagerRootService : IpcService { // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase - public IManagerRootService(ServiceCtx context) : base(new ServerBase("ViServerM")) { } + public IManagerRootService(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "ViServerM")) { } [Command(2)] // GetDisplayService(u32) -> object diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs index 65b216130..9847a2da2 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi class ISystemRootService : IpcService { // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase - public ISystemRootService(ServiceCtx context) : base(new ServerBase("ViServerS")) { } + public ISystemRootService(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "ViServerS")) { } [Command(1)] // GetDisplayService(u32) -> object diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs index 012a81ff8..6b874722d 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs @@ -1,11 +1,10 @@ using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Services.SurfaceFlinger; namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService { class IManagerDisplayService : IpcService { - private static IApplicationDisplayService _applicationDisplayService; + private IApplicationDisplayService _applicationDisplayService; public IManagerDisplayService(IApplicationDisplayService applicationDisplayService) { @@ -16,10 +15,13 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService // CreateManagedLayer(u32, u64, nn::applet::AppletResourceUserId) -> u64 public ResultCode CreateManagedLayer(ServiceCtx context) { - long layerFlags = context.RequestData.ReadInt64(); - long displayId = context.RequestData.ReadInt64(); + long layerFlags = context.RequestData.ReadInt64(); + long displayId = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); - context.Device.System.SurfaceFlinger.CreateLayer(context.Process, out long layerId); + long pid = context.Device.System.AppletState.AppletResourceUserIds.GetData((int)appletResourceUserId); + + context.Device.System.SurfaceFlinger.CreateLayer(pid, out long layerId); context.ResponseData.Write(layerId); diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs index 194bda2da..36f220c62 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService { class ISystemDisplayService : IpcService { - private static IApplicationDisplayService _applicationDisplayService; + private IApplicationDisplayService _applicationDisplayService; public ISystemDisplayService(IApplicationDisplayService applicationDisplayService) { diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index bd00fc02b..a4b9f358a 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -11,7 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService { class IApplicationDisplayService : IpcService { - private IdDictionary _displays; + private readonly IdDictionary _displays; + + private int _vsyncEventHandle; public IApplicationDisplayService() { @@ -121,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService long userId = context.RequestData.ReadInt64(); long parcelPtr = context.Request.ReceiveBuff[0].Position; - IBinder producer = context.Device.System.SurfaceFlinger.OpenLayer(context.Process, layerId); + IBinder producer = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId); Parcel parcel = new Parcel(0x28, 0x4); @@ -159,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // TODO: support multi display. Display disp = _displays.GetData((int)displayId); - IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(context.Process, out long layerId); + IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(0, out long layerId); Parcel parcel = new Parcel(0x28, 0x4); @@ -268,8 +270,8 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService const ulong defaultAlignment = 0x1000; const ulong defaultSize = 0x20000; - // NOTE: The official service setup a A8B8G8R8 texture with a linear layout and then query its size. - // As we don't need this texture on the emulator, we can just simplify this logic and directly + // NOTE: The official service setup a A8B8G8R8 texture with a linear layout and then query its size. + // As we don't need this texture on the emulator, we can just simplify this logic and directly // do a linear layout size calculation. (stride * height * bytePerPixel) int pitch = BitUtils.AlignUp(BitUtils.DivRoundUp(width * 32, 8), 64); int memorySize = pitch * BitUtils.AlignUp(height, 64); @@ -289,12 +291,15 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService { string name = GetDisplayName(context); - if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out int handle) != KernelResult.Success) + if (_vsyncEventHandle == 0) { - throw new InvalidOperationException("Out of handles!"); + if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_vsyncEventHandle); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs index 14348a12f..b9229b330 100644 --- a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs @@ -10,13 +10,17 @@ namespace Ryujinx.HLE.HOS.SystemState public FocusState FocusState { get; private set; } - public KEvent MessageEvent { get; private set; } + public KEvent MessageEvent { get; } + + public IdDictionary AppletResourceUserIds { get; } public AppletStateMgr(Horizon system) { _messages = new ConcurrentQueue(); MessageEvent = new KEvent(system.KernelContext); + + AppletResourceUserIds = new IdDictionary(); } public void SetFocus(bool isFocused) diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs index 7f1e2935e..29a2b0fc0 100644 --- a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs +++ b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.Loaders.Npdm { private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24; - public byte MmuFlags { get; private set; } + public byte ProcessFlags { get; private set; } public bool Is64Bit { get; private set; } public byte MainThreadPriority { get; private set; } public byte DefaultCpuId { get; private set; } @@ -35,9 +35,9 @@ namespace Ryujinx.HLE.Loaders.Npdm reader.ReadInt64(); - MmuFlags = reader.ReadByte(); + ProcessFlags = reader.ReadByte(); - Is64Bit = (MmuFlags & 1) != 0; + Is64Bit = (ProcessFlags & 1) != 0; reader.ReadByte(); diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 36ffc5632..2c58898c4 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -93,6 +93,7 @@ namespace Ryujinx.HLE FileSystem = fileSystem; System = new Horizon(this, contentManager); + System.InitializeServices(); Statistics = new PerformanceStatistics(); diff --git a/Ryujinx.HLE/Utilities/StructReader.cs b/Ryujinx.HLE/Utilities/StructReader.cs index 0c45a8ee5..3bde1c77b 100644 --- a/Ryujinx.HLE/Utilities/StructReader.cs +++ b/Ryujinx.HLE/Utilities/StructReader.cs @@ -1,15 +1,16 @@ using Ryujinx.Cpu; +using Ryujinx.Memory; using System.Runtime.InteropServices; namespace Ryujinx.HLE.Utilities { class StructReader { - private MemoryManager _memory; + private IVirtualMemoryManager _memory; public long Position { get; private set; } - public StructReader(MemoryManager memory, long position) + public StructReader(IVirtualMemoryManager memory, long position) { _memory = memory; Position = position; diff --git a/Ryujinx.HLE/Utilities/StructWriter.cs b/Ryujinx.HLE/Utilities/StructWriter.cs index 46a07a60c..6b1e0838d 100644 --- a/Ryujinx.HLE/Utilities/StructWriter.cs +++ b/Ryujinx.HLE/Utilities/StructWriter.cs @@ -1,15 +1,16 @@ using Ryujinx.Cpu; +using Ryujinx.Memory; using System.Runtime.InteropServices; namespace Ryujinx.HLE.Utilities { class StructWriter { - private MemoryManager _memory; + private IVirtualMemoryManager _memory; public long Position { get; private set; } - public StructWriter(MemoryManager memory, long position) + public StructWriter(IVirtualMemoryManager memory, long position) { _memory = memory; Position = position; diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 037bedc8c..c673bc803 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -1,4 +1,4 @@ -using Ryujinx.Memory.Tracking; +using System; namespace Ryujinx.Memory.Tests { @@ -10,19 +10,73 @@ namespace Ryujinx.Memory.Tests { } + public void Map(ulong va, ulong pa, ulong size) + { + throw new NotImplementedException(); + } + + public void Unmap(ulong va, ulong size) + { + throw new NotImplementedException(); + } + + public T Read(ulong va) where T : unmanaged + { + throw new NotImplementedException(); + } + + public void Read(ulong va, Span data) + { + throw new NotImplementedException(); + } + + public void Write(ulong va, T value) where T : unmanaged + { + throw new NotImplementedException(); + } + + public void Write(ulong va, ReadOnlySpan data) + { + throw new NotImplementedException(); + } + + public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) + { + throw new NotImplementedException(); + } + + public WritableRegion GetWritableRegion(ulong va, int size) + { + throw new NotImplementedException(); + } + + public ref T GetRef(ulong va) where T : unmanaged + { + throw new NotImplementedException(); + } + public (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size) { return NoMappings ? new (ulong address, ulong size)[0] : new (ulong address, ulong size)[] { (va, size) }; } + public bool IsMapped(ulong va) + { + return true; + } + public bool IsRangeMapped(ulong va, ulong size) { return true; } + public ulong GetPhysicalAddress(ulong va) + { + throw new NotImplementedException(); + } + public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection) { - } } } diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs new file mode 100644 index 000000000..c6d6cab56 --- /dev/null +++ b/Ryujinx.Memory/AddressSpaceManager.cs @@ -0,0 +1,549 @@ +using Ryujinx.Common; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Memory +{ + /// + /// Represents a address space manager. + /// Supports virtual memory region mapping, address translation and read/write access to mapped regions. + /// + public sealed class AddressSpaceManager : IVirtualMemoryManager + { + public const int PageBits = 12; + public const int PageSize = 1 << PageBits; + public const int PageMask = PageSize - 1; + + private const int PtLevelBits = 9; // 9 * 4 + 12 = 48 (max address space size) + private const int PtLevelSize = 1 << PtLevelBits; + private const int PtLevelMask = PtLevelSize - 1; + + private const ulong Unmapped = ulong.MaxValue; + + /// + /// Address space width in bits. + /// + public int AddressSpaceBits { get; } + + private readonly ulong _addressSpaceSize; + + private readonly MemoryBlock _backingMemory; + + private readonly ulong[][][][] _pageTable; + + /// + /// Creates a new instance of the memory manager. + /// + /// Physical backing memory where virtual memory will be mapped to + /// Size of the address space + public AddressSpaceManager(MemoryBlock backingMemory, ulong addressSpaceSize) + { + ulong asSize = PageSize; + int asBits = PageBits; + + while (asSize < addressSpaceSize) + { + asSize <<= 1; + asBits++; + } + + AddressSpaceBits = asBits; + _addressSpaceSize = asSize; + _backingMemory = backingMemory; + _pageTable = new ulong[PtLevelSize][][][]; + } + + /// + /// Maps a virtual memory range into a physical memory range. + /// + /// + /// Addresses and size must be page aligned. + /// + /// Virtual memory address + /// Physical memory address + /// Size to be mapped + public void Map(ulong va, ulong pa, ulong size) + { + while (size != 0) + { + PtMap(va, pa); + + va += PageSize; + pa += PageSize; + size -= PageSize; + } + } + + /// + /// Unmaps a previously mapped range of virtual memory. + /// + /// Virtual address of the range to be unmapped + /// Size of the range to be unmapped + public void Unmap(ulong va, ulong size) + { + while (size != 0) + { + PtUnmap(va); + + va += PageSize; + size -= PageSize; + } + } + + /// + /// Reads data from mapped memory. + /// + /// Type of the data being read + /// Virtual address of the data in memory + /// The data + /// Throw for unhandled invalid or unmapped memory accesses + public T Read(ulong va) where T : unmanaged + { + return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0]; + } + + /// + /// Reads data from mapped memory. + /// + /// Virtual address of the data in memory + /// Span to store the data being read into + /// Throw for unhandled invalid or unmapped memory accesses + public void Read(ulong va, Span data) + { + ReadImpl(va, data); + } + + /// + /// Writes data to mapped memory. + /// + /// Type of the data being written + /// Virtual address to write the data into + /// Data to be written + /// Throw for unhandled invalid or unmapped memory accesses + public void Write(ulong va, T value) where T : unmanaged + { + Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1))); + } + + /// + /// Writes data to mapped memory. + /// + /// Virtual address to write the data into + /// Data to be written + /// Throw for unhandled invalid or unmapped memory accesses + public void Write(ulong va, ReadOnlySpan data) + { + if (data.Length == 0) + { + return; + } + + if (IsContiguousAndMapped(va, data.Length)) + { + data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); + } + else + { + int offset = 0, size; + + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressInternal(va); + + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + + data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); + + data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); + } + } + } + + /// + /// Gets a read-only span of data from mapped memory. + /// + /// + /// This may perform a allocation if the data is not contiguous in memory. + /// For this reason, the span is read-only, you can't modify the data. + /// + /// Virtual address of the data + /// Size of the data + /// True if read tracking is triggered on the span + /// A read-only span of the data + /// Throw for unhandled invalid or unmapped memory accesses + public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) + { + if (size == 0) + { + return ReadOnlySpan.Empty; + } + + if (IsContiguousAndMapped(va, size)) + { + return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); + } + else + { + Span data = new byte[size]; + + ReadImpl(va, data); + + return data; + } + } + + /// + /// Gets a region of memory that can be written to. + /// + /// + /// If the requested region is not contiguous in physical memory, + /// this will perform an allocation, and flush the data (writing it + /// back to the backing memory) on disposal. + /// + /// Virtual address of the data + /// Size of the data + /// A writable region of memory containing the data + /// Throw for unhandled invalid or unmapped memory accesses + public WritableRegion GetWritableRegion(ulong va, int size) + { + if (size == 0) + { + return new WritableRegion(null, va, Memory.Empty); + } + + if (IsContiguousAndMapped(va, size)) + { + return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); + } + else + { + Memory memory = new byte[size]; + + GetSpan(va, size).CopyTo(memory.Span); + + return new WritableRegion(this, va, memory); + } + } + + /// + /// Gets a reference for the given type at the specified virtual memory address. + /// + /// + /// The data must be located at a contiguous memory region. + /// + /// Type of the data to get the reference + /// Virtual address of the data + /// A reference to the data in memory + /// Throw if the specified memory region is not contiguous in physical memory + public ref T GetRef(ulong va) where T : unmanaged + { + if (!IsContiguous(va, Unsafe.SizeOf())) + { + ThrowMemoryNotContiguous(); + } + + return ref _backingMemory.GetRef(GetPhysicalAddressInternal(va)); + } + + private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsContiguous(ulong va, int size) + { + if (!ValidateAddress(va)) + { + return false; + } + + ulong endVa = (va + (ulong)size + PageMask) & ~(ulong)PageMask; + + va &= ~(ulong)PageMask; + + int pages = (int)((endVa - va) / PageSize); + + for (int page = 0; page < pages - 1; page++) + { + if (!ValidateAddress(va + PageSize)) + { + return false; + } + + if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize)) + { + return false; + } + + va += PageSize; + } + + return true; + } + + /// + /// Gets the physical regions that make up the given virtual address region. + /// If any part of the virtual region is unmapped, null is returned. + /// + /// Virtual address of the range + /// Size of the range + /// Array of physical regions + public (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size) + { + throw new NotImplementedException(); + } + + private void ReadImpl(ulong va, Span data) + { + if (data.Length == 0) + { + return; + } + + int offset = 0, size; + + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressInternal(va); + + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); + + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); + } + } + + /// + /// Checks if the page at a given virtual address is mapped. + /// + /// Virtual address to check + /// True if the address is mapped, false otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsMapped(ulong va) + { + if (!ValidateAddress(va)) + { + return false; + } + + return PtRead(va) != Unmapped; + } + + /// + /// Checks if a memory range is mapped. + /// + /// Virtual address of the range + /// Size of the range in bytes + /// True if the entire range is mapped, false otherwise + public bool IsRangeMapped(ulong va, ulong size) + { + if (size == 0UL) + { + return true; + } + + ulong endVa = (va + size + PageMask) & ~(ulong)PageMask; + + va &= ~(ulong)PageMask; + + while (va < endVa) + { + if (!IsMapped(va)) + { + return false; + } + + va += PageSize; + } + + return true; + } + + private bool ValidateAddress(ulong va) + { + return va < _addressSpaceSize; + } + + /// + /// Performs address translation of the address inside a mapped memory range. + /// + /// + /// If the address is invalid or unmapped, -1 will be returned. + /// + /// Virtual address to be translated + /// The physical address + public ulong GetPhysicalAddress(ulong va) + { + // We return -1L if the virtual address is invalid or unmapped. + if (!ValidateAddress(va) || !IsMapped(va)) + { + return ulong.MaxValue; + } + + return GetPhysicalAddressInternal(va); + } + + private ulong GetPhysicalAddressInternal(ulong va) + { + return PtRead(va) + (va & PageMask); + } + + /// + /// Reprotect a region of virtual memory for tracking. Sets software protection bits. + /// + /// Virtual address base + /// Size of the region to protect + /// Memory protection to set + public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection) + { + throw new NotImplementedException(); + } + + private ulong PtRead(ulong va) + { + int l3 = (int)(va >> PageBits) & PtLevelMask; + int l2 = (int)(va >> (PageBits + PtLevelBits)) & PtLevelMask; + int l1 = (int)(va >> (PageBits + PtLevelBits * 2)) & PtLevelMask; + int l0 = (int)(va >> (PageBits + PtLevelBits * 3)) & PtLevelMask; + + if (_pageTable[l0] == null) + { + return Unmapped; + } + + if (_pageTable[l0][l1] == null) + { + return Unmapped; + } + + if (_pageTable[l0][l1][l2] == null) + { + return Unmapped; + } + + return _pageTable[l0][l1][l2][l3]; + } + + private void PtMap(ulong va, ulong value) + { + int l3 = (int)(va >> PageBits) & PtLevelMask; + int l2 = (int)(va >> (PageBits + PtLevelBits)) & PtLevelMask; + int l1 = (int)(va >> (PageBits + PtLevelBits * 2)) & PtLevelMask; + int l0 = (int)(va >> (PageBits + PtLevelBits * 3)) & PtLevelMask; + + if (_pageTable[l0] == null) + { + _pageTable[l0] = new ulong[PtLevelSize][][]; + } + + if (_pageTable[l0][l1] == null) + { + _pageTable[l0][l1] = new ulong[PtLevelSize][]; + } + + if (_pageTable[l0][l1][l2] == null) + { + _pageTable[l0][l1][l2] = new ulong[PtLevelSize]; + + for (int i = 0; i < _pageTable[l0][l1][l2].Length; i++) + { + _pageTable[l0][l1][l2][i] = Unmapped; + } + } + + _pageTable[l0][l1][l2][l3] = value; + } + + private void PtUnmap(ulong va) + { + int l3 = (int)(va >> PageBits) & PtLevelMask; + int l2 = (int)(va >> (PageBits + PtLevelBits)) & PtLevelMask; + int l1 = (int)(va >> (PageBits + PtLevelBits * 2)) & PtLevelMask; + int l0 = (int)(va >> (PageBits + PtLevelBits * 3)) & PtLevelMask; + + if (_pageTable[l0] == null) + { + return; + } + + if (_pageTable[l0][l1] == null) + { + return; + } + + if (_pageTable[l0][l1][l2] == null) + { + return; + } + + _pageTable[l0][l1][l2][l3] = Unmapped; + + bool empty = true; + + for (int i = 0; i < _pageTable[l0][l1][l2].Length; i++) + { + empty &= (_pageTable[l0][l1][l2][i] == Unmapped); + } + + if (empty) + { + _pageTable[l0][l1][l2] = null; + + RemoveIfAllNull(l0, l1); + } + } + + private void RemoveIfAllNull(int l0, int l1) + { + bool empty = true; + + for (int i = 0; i < _pageTable[l0][l1].Length; i++) + { + empty &= (_pageTable[l0][l1][i] == null); + } + + if (empty) + { + _pageTable[l0][l1] = null; + + RemoveIfAllNull(l0); + } + } + + private void RemoveIfAllNull(int l0) + { + bool empty = true; + + for (int i = 0; i < _pageTable[l0].Length; i++) + { + empty &= (_pageTable[l0][i] == null); + } + + if (empty) + { + _pageTable[l0] = null; + } + } + } +} diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs new file mode 100644 index 000000000..8c2296e22 --- /dev/null +++ b/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -0,0 +1,28 @@ +using System; + +namespace Ryujinx.Memory +{ + public interface IVirtualMemoryManager + { + void Map(ulong va, ulong pa, ulong size); + void Unmap(ulong va, ulong size); + + T Read(ulong va) where T : unmanaged; + void Read(ulong va, Span data); + + void Write(ulong va, T value) where T : unmanaged; + void Write(ulong va, ReadOnlySpan data); + + ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false); + WritableRegion GetWritableRegion(ulong va, int size); + ref T GetRef(ulong va) where T : unmanaged; + + (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size); + + bool IsMapped(ulong va); + bool IsRangeMapped(ulong va, ulong size); + ulong GetPhysicalAddress(ulong va); + + void TrackingReprotect(ulong va, ulong size, MemoryPermission protection); + } +} diff --git a/Ryujinx.Cpu/MemoryNotContiguousException.cs b/Ryujinx.Memory/MemoryNotContiguousException.cs similarity index 82% rename from Ryujinx.Cpu/MemoryNotContiguousException.cs rename to Ryujinx.Memory/MemoryNotContiguousException.cs index d7c827cc9..3106955b8 100644 --- a/Ryujinx.Cpu/MemoryNotContiguousException.cs +++ b/Ryujinx.Memory/MemoryNotContiguousException.cs @@ -1,8 +1,8 @@ using System; -namespace Ryujinx.Cpu +namespace Ryujinx.Memory { - class MemoryNotContiguousException : Exception + public class MemoryNotContiguousException : Exception { public MemoryNotContiguousException() : base("The specified memory region is not contiguous.") { diff --git a/Ryujinx.Memory/Tracking/IVirtualMemoryManager.cs b/Ryujinx.Memory/Tracking/IVirtualMemoryManager.cs deleted file mode 100644 index e6d8e8c98..000000000 --- a/Ryujinx.Memory/Tracking/IVirtualMemoryManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Memory.Tracking -{ - public interface IVirtualMemoryManager - { - (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size); - - bool IsRangeMapped(ulong va, ulong size); - void TrackingReprotect(ulong va, ulong size, MemoryPermission protection); - } -} diff --git a/Ryujinx.Cpu/WritableRegion.cs b/Ryujinx.Memory/WritableRegion.cs similarity index 74% rename from Ryujinx.Cpu/WritableRegion.cs rename to Ryujinx.Memory/WritableRegion.cs index 5ea0a2d83..e8ea310fb 100644 --- a/Ryujinx.Cpu/WritableRegion.cs +++ b/Ryujinx.Memory/WritableRegion.cs @@ -1,17 +1,17 @@ using System; -namespace Ryujinx.Cpu +namespace Ryujinx.Memory { public sealed class WritableRegion : IDisposable { - private readonly MemoryManager _mm; + private readonly IVirtualMemoryManager _mm; private readonly ulong _va; private bool NeedsWriteback => _mm != null; public Memory Memory { get; } - internal WritableRegion(MemoryManager mm, ulong va, Memory memory) + public WritableRegion(IVirtualMemoryManager mm, ulong va, Memory memory) { _mm = mm; _va = va; diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs index 10525a4be..c8887544c 100644 --- a/Ryujinx/Ui/GLRenderer.cs +++ b/Ryujinx/Ui/GLRenderer.cs @@ -33,9 +33,9 @@ namespace Ryujinx.Ui public static event EventHandler StatusUpdatedEvent; - public bool IsActive { get; set; } - public bool IsStopped { get; set; } - public bool IsFocused { get; set; } + private bool _isActive; + private bool _isStopped; + private bool _isFocused; private double _mouseX; private double _mouseY; @@ -48,9 +48,9 @@ namespace Ryujinx.Ui private long _ticks = 0; - private System.Diagnostics.Stopwatch _chrono; + private readonly System.Diagnostics.Stopwatch _chrono; - private Switch _device; + private readonly Switch _device; private Renderer _renderer; @@ -60,11 +60,13 @@ namespace Ryujinx.Ui private GraphicsDebugLevel _glLogLevel; + private readonly ManualResetEvent _exitEvent; + public GlRenderer(Switch device, GraphicsDebugLevel glLogLevel) : base (GetGraphicsMode(), 3, 3, - glLogLevel == GraphicsDebugLevel.None - ? GraphicsContextFlags.ForwardCompatible + glLogLevel == GraphicsDebugLevel.None + ? GraphicsContextFlags.ForwardCompatible : GraphicsContextFlags.ForwardCompatible | GraphicsContextFlags.Debug) { WaitEvent = new ManualResetEvent(false); @@ -92,6 +94,8 @@ namespace Ryujinx.Ui _dsuClient = new Client(); _glLogLevel = glLogLevel; + + _exitEvent = new ManualResetEvent(false); } private static GraphicsMode GetGraphicsMode() @@ -107,12 +111,12 @@ namespace Ryujinx.Ui private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args) { - IsFocused = false; + _isFocused = false; } private void Parent_FocusInEvent(object o, Gtk.FocusInEventArgs args) { - IsFocused = true; + _isFocused = true; } private void GLRenderer_Destroyed(object sender, EventArgs e) @@ -123,7 +127,7 @@ namespace Ryujinx.Ui protected void Renderer_Shown(object sender, EventArgs e) { - IsFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused); + _isFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused); } public void HandleScreenState(KeyboardState keyboard) @@ -204,7 +208,7 @@ namespace Ryujinx.Ui _chrono.Restart(); - IsActive = true; + _isActive = true; Gtk.Window parent = this.Toplevel as Gtk.Window; @@ -316,13 +320,17 @@ namespace Ryujinx.Ui public void Exit() { _dsuClient?.Dispose(); - if (IsStopped) + + if (_isStopped) { return; } - IsStopped = true; - IsActive = false; + _isStopped = true; + _isActive = false; + + _exitEvent.WaitOne(); + _exitEvent.Dispose(); } public void Initialize() @@ -353,9 +361,9 @@ namespace Ryujinx.Ui _device.Gpu.InitializeShaderCache(); Translator.IsReadyForTranslation.Set(); - while (IsActive) + while (_isActive) { - if (IsStopped) + if (_isStopped) { return; } @@ -401,28 +409,30 @@ namespace Ryujinx.Ui public void MainLoop() { - while (IsActive) + while (_isActive) { UpdateFrame(); // Polling becomes expensive if it's not slept Thread.Sleep(1); } + + _exitEvent.Set(); } private bool UpdateFrame() { - if (!IsActive) + if (!_isActive) { return true; } - if (IsStopped) + if (_isStopped) { return false; } - if (IsFocused) + if (_isFocused) { Gtk.Application.Invoke(delegate { @@ -444,7 +454,7 @@ namespace Ryujinx.Ui List motionInputs = new List(NpadDevices.MaxControllers); MotionDevice motionDevice = new MotionDevice(_dsuClient); - + foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value) { ControllerKeys currentButton = 0; @@ -464,7 +474,7 @@ namespace Ryujinx.Ui if (inputConfig is KeyboardConfig keyboardConfig) { - if (IsFocused) + if (_isFocused) { // Keyboard Input KeyboardController keyboardController = new KeyboardController(keyboardConfig); @@ -571,11 +581,11 @@ namespace Ryujinx.Ui motionInputs.Add(sixAxisInput); } } - + _device.Hid.Npads.Update(gamepadInputs); _device.Hid.Npads.UpdateSixAxis(motionInputs); - if(IsFocused) + if(_isFocused) { // Hotkeys HotkeyButtons currentHotkeyButtons = KeyboardController.GetHotkeyButtons(OpenTK.Input.Keyboard.GetState()); @@ -594,7 +604,7 @@ namespace Ryujinx.Ui // Get screen touch position from left mouse click // OpenTK always captures mouse events, even if out of focus, so check if window is focused. - if (IsFocused && _mousePressed) + if (_isFocused && _mousePressed) { int screenWidth = AllocatedWidth; int screenHeight = AllocatedHeight;