| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- using LibHac.Common;
- using LibHac.Common.Keys;
- using LibHac.Fs;
- using LibHac.Fs.Shim;
- using LibHac.FsSystem;
- using LibHac.Tools.FsSystem;
- using Ryujinx.Audio;
- using Ryujinx.Audio.Input;
- using Ryujinx.Audio.Integration;
- using Ryujinx.Audio.Output;
- using Ryujinx.Audio.Renderer.Device;
- using Ryujinx.Audio.Renderer.Server;
- using Ryujinx.Common.Utilities;
- using Ryujinx.Cpu;
- using Ryujinx.Cpu.Jit;
- using Ryujinx.HLE.FileSystem;
- using Ryujinx.HLE.HOS.Kernel;
- using Ryujinx.HLE.HOS.Kernel.Memory;
- using Ryujinx.HLE.HOS.Kernel.Process;
- using Ryujinx.HLE.HOS.Kernel.Threading;
- using Ryujinx.HLE.HOS.Services;
- using Ryujinx.HLE.HOS.Services.Account.Acc;
- using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
- using Ryujinx.HLE.HOS.Services.Apm;
- using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer;
- using Ryujinx.HLE.HOS.Services.Caps;
- using Ryujinx.HLE.HOS.Services.Mii;
- using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
- using Ryujinx.HLE.HOS.Services.Nv;
- using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
- using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
- using Ryujinx.HLE.HOS.Services.Sdb.Pl;
- using Ryujinx.HLE.HOS.Services.Settings;
- using Ryujinx.HLE.HOS.Services.Sm;
- using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
- using Ryujinx.HLE.HOS.Services.Time.Clock;
- using Ryujinx.HLE.HOS.SystemState;
- using Ryujinx.HLE.Loaders.Executables;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using TimeSpanType = Ryujinx.HLE.HOS.Services.Time.Clock.TimeSpanType;
- namespace Ryujinx.HLE.HOS
- {
- using TimeServiceManager = Services.Time.TimeManager;
- public class Horizon : IDisposable
- {
- internal const int HidSize = 0x40000;
- internal const int FontSize = 0x1100000;
- internal const int IirsSize = 0x8000;
- internal const int TimeSize = 0x1000;
- internal const int AppletCaptureBufferSize = 0x384000;
- internal KernelContext KernelContext { get; }
- internal Switch Device { get; private set; }
- internal ITickSource TickSource { get; }
- internal ICpuEngine CpuEngine { get; }
- internal SurfaceFlinger SurfaceFlinger { get; private set; }
- internal AudioManager AudioManager { get; private set; }
- internal AudioOutputManager AudioOutputManager { get; private set; }
- internal AudioInputManager AudioInputManager { get; private set; }
- internal AudioRendererManager AudioRendererManager { get; private set; }
- internal VirtualDeviceSessionRegistry AudioDeviceSessionRegistry { get; private set; }
- public SystemStateMgr State { get; private set; }
- internal PerformanceState PerformanceState { get; private set; }
- internal AppletStateMgr AppletState { get; private set; }
- internal List<NfpDevice> NfpDevices { get; private set; }
- internal SmRegistry SmRegistry { get; private set; }
- internal ServerBase SmServer { get; private set; }
- internal ServerBase BsdServer { get; private set; }
- internal ServerBase AudRenServer { get; private set; }
- internal ServerBase AudOutServer { get; private set; }
- internal ServerBase FsServer { get; private set; }
- internal ServerBase HidServer { get; private set; }
- internal ServerBase NvDrvServer { get; private set; }
- internal ServerBase TimeServer { get; private set; }
- internal ServerBase ViServer { get; private set; }
- internal ServerBase ViServerM { get; private set; }
- internal ServerBase ViServerS { get; private set; }
- internal KSharedMemory HidSharedMem { get; private set; }
- internal KSharedMemory FontSharedMem { get; private set; }
- internal KSharedMemory IirsSharedMem { get; private set; }
- internal KTransferMemory AppletCaptureBufferTransfer { get; private set; }
- internal SharedFontManager SharedFontManager { get; private set; }
- internal AccountManager AccountManager { get; private set; }
- internal ContentManager ContentManager { get; private set; }
- internal CaptureManager CaptureManager { get; private set; }
- internal KEvent VsyncEvent { get; private set; }
- internal KEvent DisplayResolutionChangeEvent { get; private set; }
- public KeySet KeySet => Device.FileSystem.KeySet;
- private bool _isDisposed;
- public bool EnablePtc { get; set; }
- public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
- public int GlobalAccessLogMode { get; set; }
- internal SharedMemoryStorage HidStorage { get; private set; }
- internal NvHostSyncpt HostSyncpoint { get; private set; }
- internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
- public bool IsPaused { get; private set; }
- public Horizon(Switch device)
- {
- TickSource = new TickSource(KernelConstants.CounterFrequency);
- CpuEngine = new JitEngine(TickSource);
- KernelContext = new KernelContext(
- TickSource,
- device,
- device.Memory,
- device.Configuration.MemoryConfiguration.ToKernelMemorySize(),
- device.Configuration.MemoryConfiguration.ToKernelMemoryArrange());
- Device = device;
- State = new SystemStateMgr();
- PerformanceState = new PerformanceState();
- NfpDevices = new List<NfpDevice>();
- // Note: This is not really correct, but with HLE of services, the only memory
- // region used that is used is Application, so we can use the other ones for anything.
- KMemoryRegionManager region = KernelContext.MemoryManager.MemoryRegions[(int)MemoryRegion.NvServices];
- ulong hidPa = region.Address;
- ulong fontPa = region.Address + HidSize;
- ulong iirsPa = region.Address + HidSize + FontSize;
- ulong timePa = region.Address + HidSize + FontSize + IirsSize;
- ulong appletCaptureBufferPa = region.Address + HidSize + FontSize + IirsSize + TimeSize;
- KPageList hidPageList = new KPageList();
- KPageList fontPageList = new KPageList();
- KPageList iirsPageList = new KPageList();
- KPageList timePageList = new KPageList();
- KPageList appletCaptureBufferPageList = new KPageList();
- hidPageList.AddRange(hidPa, HidSize / KPageTableBase.PageSize);
- fontPageList.AddRange(fontPa, FontSize / KPageTableBase.PageSize);
- iirsPageList.AddRange(iirsPa, IirsSize / KPageTableBase.PageSize);
- timePageList.AddRange(timePa, TimeSize / KPageTableBase.PageSize);
- appletCaptureBufferPageList.AddRange(appletCaptureBufferPa, AppletCaptureBufferSize / KPageTableBase.PageSize);
- var hidStorage = new SharedMemoryStorage(KernelContext, hidPageList);
- var fontStorage = new SharedMemoryStorage(KernelContext, fontPageList);
- var iirsStorage = new SharedMemoryStorage(KernelContext, iirsPageList);
- var timeStorage = new SharedMemoryStorage(KernelContext, timePageList);
- var appletCaptureBufferStorage = new SharedMemoryStorage(KernelContext, appletCaptureBufferPageList);
- HidStorage = hidStorage;
- HidSharedMem = new KSharedMemory(KernelContext, hidStorage, 0, 0, KMemoryPermission.Read);
- FontSharedMem = new KSharedMemory(KernelContext, fontStorage, 0, 0, KMemoryPermission.Read);
- IirsSharedMem = new KSharedMemory(KernelContext, iirsStorage, 0, 0, KMemoryPermission.Read);
- KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timeStorage, 0, 0, KMemoryPermission.Read);
- TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timeStorage, TimeSize);
- AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage);
- AppletState = new AppletStateMgr(this);
- AppletState.SetFocus(true);
- VsyncEvent = new KEvent(KernelContext);
- DisplayResolutionChangeEvent = new KEvent(KernelContext);
- SharedFontManager = new SharedFontManager(device, fontStorage);
- AccountManager = device.Configuration.AccountManager;
- ContentManager = device.Configuration.ContentManager;
- CaptureManager = new CaptureManager(device);
- LibHacHorizonManager = device.Configuration.LibHacHorizonManager;
- // TODO: use set:sys (and get external clock source id from settings)
- // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate.
- UInt128 clockSourceId = UInt128Utils.CreateRandom();
- IRtcManager.GetExternalRtcValue(out ulong rtcValue);
- // We assume the rtc is system time.
- TimeSpanType systemTime = TimeSpanType.FromSeconds((long)rtcValue);
- // Configure and setup internal offset
- TimeSpanType internalOffset = TimeSpanType.FromSeconds(device.Configuration.SystemTimeOffset);
- TimeSpanType systemTimeOffset = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds);
- if (systemTime.IsDaylightSavingTime() && !systemTimeOffset.IsDaylightSavingTime())
- {
- internalOffset = internalOffset.AddSeconds(3600L);
- }
- else if (!systemTime.IsDaylightSavingTime() && systemTimeOffset.IsDaylightSavingTime())
- {
- internalOffset = internalOffset.AddSeconds(-3600L);
- }
- internalOffset = new TimeSpanType(-internalOffset.NanoSeconds);
- // First init the standard steady clock
- TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
- TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds());
- if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
- {
- TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
- // The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
- TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
- TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
- }
- TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom());
- // FIXME: TimeZone should be init here but it's actually done in ContentManager
- TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
- DatabaseImpl.Instance.InitializeDatabase(TickSource, LibHacHorizonManager.SdbClient);
- HostSyncpoint = new NvHostSyncpt(device);
- SurfaceFlinger = new SurfaceFlinger(device);
- InitializeAudioRenderer(TickSource);
- InitializeServices();
- }
- private void InitializeAudioRenderer(ITickSource tickSource)
- {
- AudioManager = new AudioManager();
- AudioOutputManager = new AudioOutputManager();
- AudioInputManager = new AudioInputManager();
- AudioRendererManager = new AudioRendererManager(tickSource);
- AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
- AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
- IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];
- for (int i = 0; i < audioOutputRegisterBufferEvents.Length; i++)
- {
- KEvent registerBufferEvent = new KEvent(KernelContext);
- audioOutputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
- }
- AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents);
- AudioOutputManager.SetVolume(Device.Configuration.AudioVolume);
- IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax];
- for (int i = 0; i < audioInputRegisterBufferEvents.Length; i++)
- {
- KEvent registerBufferEvent = new KEvent(KernelContext);
- audioInputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
- }
- AudioInputManager.Initialize(Device.AudioDeviceDriver, audioInputRegisterBufferEvents);
- IWritableEvent[] systemEvents = new IWritableEvent[Constants.AudioRendererSessionCountMax];
- for (int i = 0; i < systemEvents.Length; i++)
- {
- KEvent systemEvent = new KEvent(KernelContext);
- systemEvents[i] = new AudioKernelEvent(systemEvent);
- }
- AudioManager.Initialize(Device.AudioDeviceDriver.GetUpdateRequiredEvent(), AudioOutputManager.Update, AudioInputManager.Update);
- AudioRendererManager.Initialize(systemEvents, Device.AudioDeviceDriver);
- AudioManager.Start();
- }
- private void InitializeServices()
- {
- SmRegistry = new SmRegistry();
- SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry));
- // Wait until SM server thread is done with initialization,
- // only then doing connections to SM is safe.
- SmServer.InitDone.WaitOne();
- BsdServer = new ServerBase(KernelContext, "BsdServer");
- AudRenServer = new ServerBase(KernelContext, "AudioRendererServer");
- AudOutServer = new ServerBase(KernelContext, "AudioOutServer");
- FsServer = new ServerBase(KernelContext, "FsServer");
- HidServer = new ServerBase(KernelContext, "HidServer");
- NvDrvServer = new ServerBase(KernelContext, "NvservicesServer");
- TimeServer = new ServerBase(KernelContext, "TimeServer");
- ViServer = new ServerBase(KernelContext, "ViServerU");
- ViServerM = new ServerBase(KernelContext, "ViServerM");
- ViServerS = new ServerBase(KernelContext, "ViServerS");
- }
- public void LoadKip(string kipPath)
- {
- using var kipFile = new SharedRef<IStorage>(new LocalStorage(kipPath, FileAccess.Read));
- ProgramLoader.LoadKip(KernelContext, new KipExecutable(in kipFile));
- }
- public void ChangeDockedModeState(bool newState)
- {
- if (newState != State.DockedMode)
- {
- State.DockedMode = newState;
- PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
- AppletState.Messages.Enqueue(AppletMessage.OperationModeChanged);
- AppletState.Messages.Enqueue(AppletMessage.PerformanceModeChanged);
- AppletState.MessageEvent.ReadableEvent.Signal();
- SignalDisplayResolutionChange();
- Device.Configuration.RefreshInputConfig?.Invoke();
- }
- }
- public void SetVolume(float volume)
- {
- AudioOutputManager.SetVolume(volume);
- AudioRendererManager.SetVolume(volume);
- }
- public float GetVolume()
- {
- return AudioOutputManager.GetVolume() == 0 ? AudioRendererManager.GetVolume() : AudioOutputManager.GetVolume();
- }
- public void ReturnFocus()
- {
- AppletState.SetFocus(true);
- }
- public void SimulateWakeUpMessage()
- {
- AppletState.Messages.Enqueue(AppletMessage.Resume);
- AppletState.MessageEvent.ReadableEvent.Signal();
- }
- public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
- {
- if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag)
- {
- NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
- NfpDevices[nfpDeviceId].AmiiboId = amiiboId;
- NfpDevices[nfpDeviceId].UseRandomUuid = useRandomUuid;
- }
- }
- public bool SearchingForAmiibo(out int nfpDeviceId)
- {
- nfpDeviceId = default;
- for (int i = 0; i < NfpDevices.Count; i++)
- {
- if (NfpDevices[i].State == NfpDeviceState.SearchingForTag)
- {
- nfpDeviceId = i;
- return true;
- }
- }
- return false;
- }
- public void SignalDisplayResolutionChange()
- {
- DisplayResolutionChangeEvent.ReadableEvent.Signal();
- }
- public void SignalVsync()
- {
- VsyncEvent.ReadableEvent.Signal();
- }
- public void Dispose()
- {
- Dispose(true);
- }
- protected virtual void Dispose(bool disposing)
- {
- if (!_isDisposed && disposing)
- {
- _isDisposed = true;
- // "Soft" stops AudioRenderer and AudioManager to avoid some sound between resume and stop.
- if (IsPaused)
- {
- AudioManager.StopUpdates();
- TogglePauseEmulation(false);
- AudioRendererManager.StopSendingCommands();
- }
- KProcess terminationProcess = new KProcess(KernelContext);
- KThread terminationThread = new KThread(KernelContext);
- terminationThread.Initialize(0, 0, 0, 3, 0, terminationProcess, ThreadType.Kernel, () =>
- {
- // Force all threads to exit.
- lock (KernelContext.Processes)
- {
- // Terminate application.
- foreach (KProcess process in KernelContext.Processes.Values.Where(x => x.IsApplication))
- {
- process.Terminate();
- process.DecrementReferenceCount();
- }
- // The application existed, now surface flinger can exit too.
- SurfaceFlinger.Dispose();
- // Terminate HLE services (must be done after the application is already terminated,
- // otherwise the application will receive errors due to service termination).
- foreach (KProcess process in KernelContext.Processes.Values.Where(x => !x.IsApplication))
- {
- process.Terminate();
- process.DecrementReferenceCount();
- }
- KernelContext.Processes.Clear();
- }
- // Exit ourself now!
- KernelStatic.GetCurrentThread().Exit();
- });
- terminationThread.Start();
- // Wait until the thread is actually started.
- while (terminationThread.HostThread.ThreadState == ThreadState.Unstarted)
- {
- Thread.Sleep(10);
- }
- // Wait until the termination thread is done terminating all the other threads.
- terminationThread.HostThread.Join();
- // Destroy nvservices channels as KThread could be waiting on some user events.
- // This is safe as KThread that are likely to call ioctls are going to be terminated by the post handler hook on the SVC facade.
- INvDrvServices.Destroy();
- AudioManager.Dispose();
- AudioOutputManager.Dispose();
- AudioInputManager.Dispose();
- AudioRendererManager.Dispose();
- LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure();
- KernelContext.Dispose();
- }
- }
- public void TogglePauseEmulation(bool pause)
- {
- lock (KernelContext.Processes)
- {
- foreach (KProcess process in KernelContext.Processes.Values)
- {
- if (process.IsApplication)
- {
- // Only game process should be paused.
- process.SetActivity(pause);
- }
- }
- if (pause && !IsPaused)
- {
- Device.AudioDeviceDriver.GetPauseEvent().Reset();
- TickSource.Suspend();
- }
- else if (!pause && IsPaused)
- {
- Device.AudioDeviceDriver.GetPauseEvent().Set();
- TickSource.Resume();
- }
- }
- IsPaused = pause;
- }
- }
- }
|