| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- using LibHac;
- using LibHac.Bcat;
- using LibHac.Fs;
- using LibHac.FsSystem;
- using Ryujinx.Common;
- using Ryujinx.Configuration;
- using Ryujinx.HLE.FileSystem.Content;
- using Ryujinx.HLE.HOS.Font;
- 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.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
- using Ryujinx.HLE.HOS.Services.Arp;
- using Ryujinx.HLE.HOS.Services.Mii;
- 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.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 Ryujinx.HLE.Utilities;
- using System;
- using System.IO;
- 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 KernelContext KernelContext { get; }
- internal Switch Device { get; private set; }
- internal SurfaceFlinger SurfaceFlinger { get; private set; }
- public SystemStateMgr State { get; private set; }
- internal AppletStateMgr AppletState { get; private set; }
- internal KSharedMemory HidSharedMem { get; private set; }
- internal KSharedMemory FontSharedMem { get; private set; }
- internal KSharedMemory IirsSharedMem { get; private set; }
- internal SharedFontManager Font { get; private set; }
- internal ContentManager ContentManager { get; private set; }
- internal KEvent VsyncEvent { get; private set; }
- internal KEvent DisplayResolutionChangeEvent { get; private set; }
- public Keyset KeySet => Device.FileSystem.KeySet;
- #pragma warning disable CS0649
- private bool _hasStarted;
- #pragma warning restore CS0649
- private bool _isDisposed;
- public bool EnablePtc { get; set; }
- public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
- public int GlobalAccessLogMode { get; set; }
- internal ulong HidBaseAddress { get; private set; }
- internal NvHostSyncpt HostSyncpoint { get; private set; }
- internal LibHac.Horizon LibHacHorizonServer { get; private set; }
- internal HorizonClient LibHacHorizonClient { get; private set; }
- public Horizon(Switch device, ContentManager contentManager)
- {
- KernelContext = new KernelContext(device, device.Memory);
- Device = device;
- State = new SystemStateMgr();
- // 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.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;
- HidBaseAddress = hidPa - DramMemoryMap.DramBase;
- KPageList hidPageList = new KPageList();
- KPageList fontPageList = new KPageList();
- KPageList iirsPageList = new KPageList();
- KPageList timePageList = new KPageList();
- hidPageList .AddRange(hidPa, HidSize / KMemoryManager.PageSize);
- fontPageList.AddRange(fontPa, FontSize / KMemoryManager.PageSize);
- 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);
- KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timePageList, 0, 0, MemoryPermission.Read);
- TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timePa - DramMemoryMap.DramBase, TimeSize);
- AppletState = new AppletStateMgr(this);
- AppletState.SetFocus(true);
- Font = new SharedFontManager(device, fontPa - DramMemoryMap.DramBase);
- IUserInterface.InitializePort(this);
- VsyncEvent = new KEvent(KernelContext);
- DisplayResolutionChangeEvent = new KEvent(KernelContext);
- ContentManager = contentManager;
- // 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 = new UInt128(Guid.NewGuid().ToByteArray());
- 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(ConfigurationState.Instance.System.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(null, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
- TimeServiceManager.Instance.SetupStandardLocalSystemClock(null, new SystemClockContext(), systemTime.ToSeconds());
- if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
- {
- TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
- // The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
- TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(null, out SystemClockContext localSytemClockContext);
- TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
- }
- TimeServiceManager.Instance.SetupStandardUserSystemClock(null, false, SteadyClockTimePoint.GetRandom());
- // FIXME: TimeZone shoud be init here but it's actually done in ContentManager
- TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
- DatabaseImpl.Instance.InitializeDatabase(device);
- HostSyncpoint = new NvHostSyncpt(device);
- SurfaceFlinger = new SurfaceFlinger(device);
- ConfigurationState.Instance.System.EnableDockedMode.Event += OnDockedModeChange;
- InitLibHacHorizon();
- }
- public void LoadKip(string kipFile)
- {
- using IStorage fs = new LocalStorage(kipFile, FileAccess.Read);
- ProgramLoader.LoadKip(KernelContext, new KipExecutable(fs));
- }
- private void InitLibHacHorizon()
- {
- LibHac.Horizon horizon = new LibHac.Horizon(null, Device.FileSystem.FsServer);
- horizon.CreateHorizonClient(out HorizonClient ryujinxClient).ThrowIfFailure();
- horizon.CreateHorizonClient(out HorizonClient bcatClient).ThrowIfFailure();
- ryujinxClient.Sm.RegisterService(new LibHacIReader(this), "arp:r").ThrowIfFailure();
- new BcatServer(bcatClient);
- LibHacHorizonServer = horizon;
- LibHacHorizonClient = ryujinxClient;
- }
- private void OnDockedModeChange(object sender, ReactiveEventArgs<bool> e)
- {
- if (e.NewValue != State.DockedMode)
- {
- State.DockedMode = e.NewValue;
- AppletState.EnqueueMessage(MessageInfo.OperationModeChanged);
- AppletState.EnqueueMessage(MessageInfo.PerformanceModeChanged);
- SignalDisplayResolutionChange();
- }
- }
- public void SignalDisplayResolutionChange()
- {
- DisplayResolutionChangeEvent.ReadableEvent.Signal();
- }
- public void SignalVsync()
- {
- VsyncEvent.ReadableEvent.Signal();
- }
- public void EnableMultiCoreScheduling()
- {
- if (!_hasStarted)
- {
- KernelContext.Scheduler.MultiCoreScheduling = true;
- }
- }
- public void DisableMultiCoreScheduling()
- {
- if (!_hasStarted)
- {
- KernelContext.Scheduler.MultiCoreScheduling = false;
- }
- }
- public void Dispose()
- {
- Dispose(true);
- }
- protected virtual void Dispose(bool disposing)
- {
- if (!_isDisposed && disposing)
- {
- ConfigurationState.Instance.System.EnableDockedMode.Event -= OnDockedModeChange;
- _isDisposed = true;
- SurfaceFlinger.Dispose();
- 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)
- {
- foreach (KProcess process in KernelContext.Processes.Values)
- {
- process.Terminate();
- }
- }
- // Exit ourself now!
- KernelContext.Scheduler.ExitThread(terminationThread);
- KernelContext.Scheduler.GetCurrentThread().Exit();
- KernelContext.Scheduler.RemoveThread(terminationThread);
- });
- terminationThread.Start();
- // 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();
- // This is needed as the IPC Dummy KThread is also counted in the ThreadCounter.
- KernelContext.ThreadCounter.Signal();
- // It's only safe to release resources once all threads
- // have exited.
- KernelContext.ThreadCounter.Signal();
- KernelContext.ThreadCounter.Wait();
- KernelContext.Dispose();
- Device.Unload();
- }
- }
- }
- }
|