Horizon.cs
1 using LibHac.Common; 2 using LibHac.Common.Keys; 3 using LibHac.Fs; 4 using LibHac.Fs.Shim; 5 using LibHac.FsSystem; 6 using LibHac.Tools.FsSystem; 7 using Ryujinx.Cpu; 8 using Ryujinx.HLE.FileSystem; 9 using Ryujinx.HLE.HOS.Kernel; 10 using Ryujinx.HLE.HOS.Kernel.Memory; 11 using Ryujinx.HLE.HOS.Kernel.Process; 12 using Ryujinx.HLE.HOS.Kernel.Threading; 13 using Ryujinx.HLE.HOS.Services; 14 using Ryujinx.HLE.HOS.Services.Account.Acc; 15 using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; 16 using Ryujinx.HLE.HOS.Services.Apm; 17 using Ryujinx.HLE.HOS.Services.Caps; 18 using Ryujinx.HLE.HOS.Services.Mii; 19 using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; 20 using Ryujinx.HLE.HOS.Services.Nv; 21 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; 22 using Ryujinx.HLE.HOS.Services.Pcv.Bpc; 23 using Ryujinx.HLE.HOS.Services.Sdb.Pl; 24 using Ryujinx.HLE.HOS.Services.Settings; 25 using Ryujinx.HLE.HOS.Services.Sm; 26 using Ryujinx.HLE.HOS.Services.SurfaceFlinger; 27 using Ryujinx.HLE.HOS.Services.Time.Clock; 28 using Ryujinx.HLE.HOS.SystemState; 29 using Ryujinx.HLE.Loaders.Executables; 30 using Ryujinx.HLE.Loaders.Processes; 31 using Ryujinx.Horizon; 32 using System; 33 using System.Collections.Generic; 34 using System.IO; 35 using System.Linq; 36 using System.Threading; 37 38 namespace Ryujinx.HLE.HOS 39 { 40 using TimeServiceManager = Services.Time.TimeManager; 41 42 public class Horizon : IDisposable 43 { 44 internal const int HidSize = 0x40000; 45 internal const int FontSize = 0x1100000; 46 internal const int IirsSize = 0x8000; 47 internal const int TimeSize = 0x1000; 48 internal const int AppletCaptureBufferSize = 0x384000; 49 50 internal KernelContext KernelContext { get; } 51 52 internal Switch Device { get; private set; } 53 54 internal ITickSource TickSource { get; } 55 56 internal SurfaceFlinger SurfaceFlinger { get; private set; } 57 58 public SystemStateMgr State { get; private set; } 59 60 internal PerformanceState PerformanceState { get; private set; } 61 62 internal AppletStateMgr AppletState { get; private set; } 63 64 internal List<NfpDevice> NfpDevices { get; private set; } 65 66 internal SmRegistry SmRegistry { get; private set; } 67 68 internal ServerBase SmServer { get; private set; } 69 internal ServerBase BsdServer { get; private set; } 70 internal ServerBase FsServer { get; private set; } 71 internal ServerBase HidServer { get; private set; } 72 internal ServerBase NvDrvServer { get; private set; } 73 internal ServerBase TimeServer { get; private set; } 74 internal ServerBase ViServer { get; private set; } 75 internal ServerBase ViServerM { get; private set; } 76 internal ServerBase ViServerS { get; private set; } 77 internal ServerBase LdnServer { get; private set; } 78 79 internal KSharedMemory HidSharedMem { get; private set; } 80 internal KSharedMemory FontSharedMem { get; private set; } 81 internal KSharedMemory IirsSharedMem { get; private set; } 82 83 internal KTransferMemory AppletCaptureBufferTransfer { get; private set; } 84 85 internal SharedFontManager SharedFontManager { get; private set; } 86 internal AccountManager AccountManager { get; private set; } 87 internal ContentManager ContentManager { get; private set; } 88 internal CaptureManager CaptureManager { get; private set; } 89 90 internal KEvent VsyncEvent { get; private set; } 91 92 internal KEvent DisplayResolutionChangeEvent { get; private set; } 93 94 public KeySet KeySet => Device.FileSystem.KeySet; 95 96 private bool _isDisposed; 97 98 public bool EnablePtc { get; set; } 99 100 public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; } 101 102 public int GlobalAccessLogMode { get; set; } 103 104 internal SharedMemoryStorage HidStorage { get; private set; } 105 106 internal NvHostSyncpt HostSyncpoint { get; private set; } 107 108 internal LibHacHorizonManager LibHacHorizonManager { get; private set; } 109 110 internal ServiceTable ServiceTable { get; private set; } 111 112 public bool IsPaused { get; private set; } 113 114 public Horizon(Switch device) 115 { 116 TickSource = new TickSource(KernelConstants.CounterFrequency); 117 118 KernelContext = new KernelContext( 119 TickSource, 120 device, 121 device.Memory, 122 device.Configuration.MemoryConfiguration.ToKernelMemorySize(), 123 device.Configuration.MemoryConfiguration.ToKernelMemoryArrange()); 124 125 Device = device; 126 127 State = new SystemStateMgr(); 128 129 PerformanceState = new PerformanceState(); 130 131 NfpDevices = new List<NfpDevice>(); 132 133 // Note: This is not really correct, but with HLE of services, the only memory 134 // region used that is used is Application, so we can use the other ones for anything. 135 KMemoryRegionManager region = KernelContext.MemoryManager.MemoryRegions[(int)MemoryRegion.NvServices]; 136 137 ulong hidPa = region.Address; 138 ulong fontPa = region.Address + HidSize; 139 ulong iirsPa = region.Address + HidSize + FontSize; 140 ulong timePa = region.Address + HidSize + FontSize + IirsSize; 141 ulong appletCaptureBufferPa = region.Address + HidSize + FontSize + IirsSize + TimeSize; 142 143 KPageList hidPageList = new(); 144 KPageList fontPageList = new(); 145 KPageList iirsPageList = new(); 146 KPageList timePageList = new(); 147 KPageList appletCaptureBufferPageList = new(); 148 149 hidPageList.AddRange(hidPa, HidSize / KPageTableBase.PageSize); 150 fontPageList.AddRange(fontPa, FontSize / KPageTableBase.PageSize); 151 iirsPageList.AddRange(iirsPa, IirsSize / KPageTableBase.PageSize); 152 timePageList.AddRange(timePa, TimeSize / KPageTableBase.PageSize); 153 appletCaptureBufferPageList.AddRange(appletCaptureBufferPa, AppletCaptureBufferSize / KPageTableBase.PageSize); 154 155 var hidStorage = new SharedMemoryStorage(KernelContext, hidPageList); 156 var fontStorage = new SharedMemoryStorage(KernelContext, fontPageList); 157 var iirsStorage = new SharedMemoryStorage(KernelContext, iirsPageList); 158 var timeStorage = new SharedMemoryStorage(KernelContext, timePageList); 159 var appletCaptureBufferStorage = new SharedMemoryStorage(KernelContext, appletCaptureBufferPageList); 160 161 HidStorage = hidStorage; 162 163 HidSharedMem = new KSharedMemory(KernelContext, hidStorage, 0, 0, KMemoryPermission.Read); 164 FontSharedMem = new KSharedMemory(KernelContext, fontStorage, 0, 0, KMemoryPermission.Read); 165 IirsSharedMem = new KSharedMemory(KernelContext, iirsStorage, 0, 0, KMemoryPermission.Read); 166 167 KSharedMemory timeSharedMemory = new(KernelContext, timeStorage, 0, 0, KMemoryPermission.Read); 168 169 TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timeStorage, TimeSize); 170 171 AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage); 172 173 AppletState = new AppletStateMgr(this); 174 175 AppletState.SetFocus(true); 176 177 VsyncEvent = new KEvent(KernelContext); 178 179 DisplayResolutionChangeEvent = new KEvent(KernelContext); 180 181 SharedFontManager = new SharedFontManager(device, fontStorage); 182 AccountManager = device.Configuration.AccountManager; 183 ContentManager = device.Configuration.ContentManager; 184 CaptureManager = new CaptureManager(device); 185 186 LibHacHorizonManager = device.Configuration.LibHacHorizonManager; 187 188 // We hardcode a clock source id to avoid it changing between each start. 189 // TODO: use set:sys (and get external clock source id from settings) 190 // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. 191 UInt128 clockSourceId = new(0x36a0328702ce8bc1, 0x1608eaba02333284); 192 IRtcManager.GetExternalRtcValue(out ulong rtcValue); 193 194 // We assume the rtc is system time. 195 TimeSpanType systemTime = TimeSpanType.FromSeconds((long)rtcValue); 196 197 // Configure and setup internal offset 198 TimeSpanType internalOffset = TimeSpanType.FromSeconds(device.Configuration.SystemTimeOffset); 199 200 TimeSpanType systemTimeOffset = new(systemTime.NanoSeconds + internalOffset.NanoSeconds); 201 202 if (systemTime.IsDaylightSavingTime() && !systemTimeOffset.IsDaylightSavingTime()) 203 { 204 internalOffset = internalOffset.AddSeconds(3600L); 205 } 206 else if (!systemTime.IsDaylightSavingTime() && systemTimeOffset.IsDaylightSavingTime()) 207 { 208 internalOffset = internalOffset.AddSeconds(-3600L); 209 } 210 211 systemTime = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds); 212 213 // First init the standard steady clock 214 TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, TimeSpanType.Zero, TimeSpanType.Zero, TimeSpanType.Zero, false); 215 TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds()); 216 TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext); 217 218 if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes)) 219 { 220 TimeSpanType standardNetworkClockSufficientAccuracy = new((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000); 221 222 // The network system clock needs a valid system clock, as such we setup this system clock using the local system clock. 223 TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy); 224 } 225 226 TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, true, localSytemClockContext.SteadyTimePoint); 227 228 // FIXME: TimeZone should be init here but it's actually done in ContentManager 229 230 TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock(); 231 232 DatabaseImpl.Instance.InitializeDatabase(TickSource, LibHacHorizonManager.SdbClient); 233 234 HostSyncpoint = new NvHostSyncpt(device); 235 236 SurfaceFlinger = new SurfaceFlinger(device); 237 } 238 239 public void InitializeServices() 240 { 241 SmRegistry = new SmRegistry(); 242 SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry)); 243 244 // Wait until SM server thread is done with initialization, 245 // only then doing connections to SM is safe. 246 SmServer.InitDone.WaitOne(); 247 248 BsdServer = new ServerBase(KernelContext, "BsdServer"); 249 FsServer = new ServerBase(KernelContext, "FsServer"); 250 HidServer = new ServerBase(KernelContext, "HidServer"); 251 NvDrvServer = new ServerBase(KernelContext, "NvservicesServer"); 252 TimeServer = new ServerBase(KernelContext, "TimeServer"); 253 ViServer = new ServerBase(KernelContext, "ViServerU"); 254 ViServerM = new ServerBase(KernelContext, "ViServerM"); 255 ViServerS = new ServerBase(KernelContext, "ViServerS"); 256 LdnServer = new ServerBase(KernelContext, "LdnServer"); 257 258 StartNewServices(); 259 } 260 261 private void StartNewServices() 262 { 263 HorizonFsClient fsClient = new(this); 264 265 ServiceTable = new ServiceTable(); 266 var services = ServiceTable.GetServices(new HorizonOptions 267 (Device.Configuration.IgnoreMissingServices, 268 LibHacHorizonManager.BcatClient, 269 fsClient, 270 AccountManager, 271 Device.AudioDeviceDriver, 272 TickSource)); 273 274 foreach (var service in services) 275 { 276 const ProcessCreationFlags Flags = 277 ProcessCreationFlags.EnableAslr | 278 ProcessCreationFlags.AddressSpace64Bit | 279 ProcessCreationFlags.Is64Bit | 280 ProcessCreationFlags.PoolPartitionSystem; 281 282 ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); 283 284 uint[] defaultCapabilities = { 285 0x030363F7, 286 0x1FFFFFCF, 287 0x207FFFEF, 288 0x47E0060F, 289 0x0048BFFF, 290 0x01007FFF, 291 }; 292 293 // TODO: 294 // - Pass enough information (capabilities, process creation info, etc) on ServiceEntry for proper initialization. 295 // - Have the ThreadStart function take the syscall, address space and thread context parameters instead of passing them here. 296 KernelStatic.StartInitialProcess(KernelContext, creationInfo, defaultCapabilities, 44, () => 297 { 298 service.Start(KernelContext.Syscall, KernelStatic.GetCurrentProcess().CpuMemory, KernelStatic.GetCurrentThread().ThreadContext); 299 }); 300 } 301 } 302 303 public bool LoadKip(string kipPath) 304 { 305 using var kipFile = new SharedRef<IStorage>(new LocalStorage(kipPath, FileAccess.Read)); 306 307 return ProcessLoaderHelper.LoadKip(KernelContext, new KipExecutable(in kipFile)); 308 } 309 310 public void ChangeDockedModeState(bool newState) 311 { 312 if (newState != State.DockedMode) 313 { 314 State.DockedMode = newState; 315 PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; 316 317 AppletState.Messages.Enqueue(AppletMessage.OperationModeChanged); 318 AppletState.Messages.Enqueue(AppletMessage.PerformanceModeChanged); 319 AppletState.MessageEvent.ReadableEvent.Signal(); 320 321 SignalDisplayResolutionChange(); 322 323 Device.Configuration.RefreshInputConfig?.Invoke(); 324 } 325 } 326 327 public void ReturnFocus() 328 { 329 AppletState.SetFocus(true); 330 } 331 332 public void SimulateWakeUpMessage() 333 { 334 AppletState.Messages.Enqueue(AppletMessage.Resume); 335 AppletState.MessageEvent.ReadableEvent.Signal(); 336 } 337 338 public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid) 339 { 340 if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag) 341 { 342 NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound; 343 NfpDevices[nfpDeviceId].AmiiboId = amiiboId; 344 NfpDevices[nfpDeviceId].UseRandomUuid = useRandomUuid; 345 } 346 } 347 348 public bool SearchingForAmiibo(out int nfpDeviceId) 349 { 350 nfpDeviceId = default; 351 352 for (int i = 0; i < NfpDevices.Count; i++) 353 { 354 if (NfpDevices[i].State == NfpDeviceState.SearchingForTag) 355 { 356 nfpDeviceId = i; 357 358 return true; 359 } 360 } 361 362 return false; 363 } 364 365 public void SignalDisplayResolutionChange() 366 { 367 DisplayResolutionChangeEvent.ReadableEvent.Signal(); 368 } 369 370 public void SignalVsync() 371 { 372 VsyncEvent.ReadableEvent.Signal(); 373 } 374 375 public void Dispose() 376 { 377 GC.SuppressFinalize(this); 378 Dispose(true); 379 } 380 381 protected virtual void Dispose(bool disposing) 382 { 383 if (!_isDisposed && disposing) 384 { 385 _isDisposed = true; 386 387 // "Soft" stops AudioRenderer and AudioManager to avoid some sound between resume and stop. 388 if (IsPaused) 389 { 390 TogglePauseEmulation(false); 391 } 392 393 KProcess terminationProcess = new(KernelContext); 394 KThread terminationThread = new(KernelContext); 395 396 terminationThread.Initialize(0, 0, 0, 3, 0, terminationProcess, ThreadType.Kernel, () => 397 { 398 // Force all threads to exit. 399 lock (KernelContext.Processes) 400 { 401 // Terminate application. 402 foreach (KProcess process in KernelContext.Processes.Values.Where(x => x.IsApplication)) 403 { 404 process.Terminate(); 405 process.DecrementReferenceCount(); 406 } 407 408 // The application existed, now surface flinger can exit too. 409 SurfaceFlinger.Dispose(); 410 411 // Terminate HLE services (must be done after the application is already terminated, 412 // otherwise the application will receive errors due to service termination). 413 foreach (KProcess process in KernelContext.Processes.Values.Where(x => !x.IsApplication)) 414 { 415 process.Terminate(); 416 process.DecrementReferenceCount(); 417 } 418 419 KernelContext.Processes.Clear(); 420 } 421 422 // Exit ourself now! 423 KernelStatic.GetCurrentThread().Exit(); 424 }); 425 426 terminationThread.Start(); 427 428 // Wait until the thread is actually started. 429 while (terminationThread.HostThread.ThreadState == ThreadState.Unstarted) 430 { 431 Thread.Sleep(10); 432 } 433 434 // Wait until the termination thread is done terminating all the other threads. 435 terminationThread.HostThread.Join(); 436 437 // Destroy nvservices channels as KThread could be waiting on some user events. 438 // 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. 439 INvDrvServices.Destroy(); 440 441 if (LibHacHorizonManager.ApplicationClient != null) 442 { 443 LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure(); 444 } 445 446 KernelContext.Dispose(); 447 } 448 } 449 450 public void TogglePauseEmulation(bool pause) 451 { 452 lock (KernelContext.Processes) 453 { 454 foreach (KProcess process in KernelContext.Processes.Values) 455 { 456 if (process.IsApplication) 457 { 458 // Only game process should be paused. 459 process.SetActivity(pause); 460 } 461 } 462 463 if (pause && !IsPaused) 464 { 465 Device.AudioDeviceDriver.GetPauseEvent().Reset(); 466 TickSource.Suspend(); 467 } 468 else if (!pause && IsPaused) 469 { 470 Device.AudioDeviceDriver.GetPauseEvent().Set(); 471 TickSource.Resume(); 472 } 473 } 474 IsPaused = pause; 475 } 476 } 477 }