KProcess.cs 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Cpu;
  4. using Ryujinx.HLE.Exceptions;
  5. using Ryujinx.HLE.HOS.Kernel.Common;
  6. using Ryujinx.HLE.HOS.Kernel.Memory;
  7. using Ryujinx.HLE.HOS.Kernel.Threading;
  8. using Ryujinx.Memory;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using System.Threading;
  13. namespace Ryujinx.HLE.HOS.Kernel.Process
  14. {
  15. class KProcess : KSynchronizationObject
  16. {
  17. public const int KernelVersionMajor = 10;
  18. public const int KernelVersionMinor = 4;
  19. public const int KernelVersionRevision = 0;
  20. public const int KernelVersionPacked =
  21. (KernelVersionMajor << 19) |
  22. (KernelVersionMinor << 15) |
  23. (KernelVersionRevision << 0);
  24. public KPageTableBase MemoryManager { get; private set; }
  25. private SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages;
  26. private SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages;
  27. public int DefaultCpuCore { get; set; }
  28. public bool Debug { get; private set; }
  29. public KResourceLimit ResourceLimit { get; private set; }
  30. public ulong PersonalMmHeapPagesCount { get; private set; }
  31. public ProcessState State { get; private set; }
  32. private object _processLock;
  33. private object _threadingLock;
  34. public KAddressArbiter AddressArbiter { get; private set; }
  35. public ulong[] RandomEntropy { get; private set; }
  36. public KThread[] PinnedThreads { get; private set; }
  37. private bool _signaled;
  38. public string Name { get; private set; }
  39. private int _threadCount;
  40. public ProcessCreationFlags Flags { get; private set; }
  41. private MemoryRegion _memRegion;
  42. public KProcessCapabilities Capabilities { get; private set; }
  43. public bool AllowCodeMemoryForJit { get; private set; }
  44. public ulong TitleId { get; private set; }
  45. public bool IsApplication { get; private set; }
  46. public ulong Pid { get; private set; }
  47. private long _creationTimestamp;
  48. private ulong _entrypoint;
  49. private ThreadStart _customThreadStart;
  50. private ulong _imageSize;
  51. private ulong _mainThreadStackSize;
  52. private ulong _memoryUsageCapacity;
  53. private int _version;
  54. public KHandleTable HandleTable { get; private set; }
  55. public ulong UserExceptionContextAddress { get; private set; }
  56. private LinkedList<KThread> _threads;
  57. public bool IsPaused { get; private set; }
  58. private long _totalTimeRunning;
  59. public long TotalTimeRunning => _totalTimeRunning;
  60. private IProcessContextFactory _contextFactory;
  61. public IProcessContext Context { get; private set; }
  62. public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
  63. public HleProcessDebugger Debugger { get; private set; }
  64. public KProcess(KernelContext context, bool allowCodeMemoryForJit = false) : base(context)
  65. {
  66. _processLock = new object();
  67. _threadingLock = new object();
  68. AddressArbiter = new KAddressArbiter(context);
  69. _fullTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
  70. _freeTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
  71. Capabilities = new KProcessCapabilities();
  72. AllowCodeMemoryForJit = allowCodeMemoryForJit;
  73. RandomEntropy = new ulong[KScheduler.CpuCoresCount];
  74. PinnedThreads = new KThread[KScheduler.CpuCoresCount];
  75. // TODO: Remove once we no longer need to initialize it externally.
  76. HandleTable = new KHandleTable(context);
  77. _threads = new LinkedList<KThread>();
  78. Debugger = new HleProcessDebugger(this);
  79. }
  80. public KernelResult InitializeKip(
  81. ProcessCreationInfo creationInfo,
  82. ReadOnlySpan<int> capabilities,
  83. KPageList pageList,
  84. KResourceLimit resourceLimit,
  85. MemoryRegion memRegion,
  86. IProcessContextFactory contextFactory,
  87. ThreadStart customThreadStart = null)
  88. {
  89. ResourceLimit = resourceLimit;
  90. _memRegion = memRegion;
  91. _contextFactory = contextFactory ?? new ProcessContextFactory();
  92. _customThreadStart = customThreadStart;
  93. AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
  94. Pid = KernelContext.NewKipId();
  95. if (Pid == 0 || Pid >= KernelConstants.InitialProcessId)
  96. {
  97. throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
  98. }
  99. InitializeMemoryManager(creationInfo.Flags);
  100. bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
  101. ulong codeAddress = creationInfo.CodeAddress;
  102. ulong codeSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
  103. KMemoryBlockSlabManager slabManager = creationInfo.Flags.HasFlag(ProcessCreationFlags.IsApplication)
  104. ? KernelContext.LargeMemoryBlockSlabManager
  105. : KernelContext.SmallMemoryBlockSlabManager;
  106. KernelResult result = MemoryManager.InitializeForProcess(
  107. addrSpaceType,
  108. aslrEnabled,
  109. !aslrEnabled,
  110. memRegion,
  111. codeAddress,
  112. codeSize,
  113. slabManager);
  114. if (result != KernelResult.Success)
  115. {
  116. return result;
  117. }
  118. if (!MemoryManager.CanContain(codeAddress, codeSize, MemoryState.CodeStatic))
  119. {
  120. return KernelResult.InvalidMemRange;
  121. }
  122. result = MemoryManager.MapPages(codeAddress, pageList, MemoryState.CodeStatic, KMemoryPermission.None);
  123. if (result != KernelResult.Success)
  124. {
  125. return result;
  126. }
  127. result = Capabilities.InitializeForKernel(capabilities, MemoryManager);
  128. if (result != KernelResult.Success)
  129. {
  130. return result;
  131. }
  132. return ParseProcessInfo(creationInfo);
  133. }
  134. public KernelResult Initialize(
  135. ProcessCreationInfo creationInfo,
  136. ReadOnlySpan<int> capabilities,
  137. KResourceLimit resourceLimit,
  138. MemoryRegion memRegion,
  139. IProcessContextFactory contextFactory,
  140. ThreadStart customThreadStart = null)
  141. {
  142. ResourceLimit = resourceLimit;
  143. _memRegion = memRegion;
  144. _contextFactory = contextFactory ?? new ProcessContextFactory();
  145. _customThreadStart = customThreadStart;
  146. IsApplication = creationInfo.Flags.HasFlag(ProcessCreationFlags.IsApplication);
  147. ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.SystemResourcePagesCount, memRegion);
  148. ulong codePagesCount = (ulong)creationInfo.CodePagesCount;
  149. ulong neededSizeForProcess = personalMmHeapSize + codePagesCount * KPageTableBase.PageSize;
  150. if (neededSizeForProcess != 0 && resourceLimit != null)
  151. {
  152. if (!resourceLimit.Reserve(LimitableResource.Memory, neededSizeForProcess))
  153. {
  154. return KernelResult.ResLimitExceeded;
  155. }
  156. }
  157. void CleanUpForError()
  158. {
  159. if (neededSizeForProcess != 0 && resourceLimit != null)
  160. {
  161. resourceLimit.Release(LimitableResource.Memory, neededSizeForProcess);
  162. }
  163. }
  164. PersonalMmHeapPagesCount = (ulong)creationInfo.SystemResourcePagesCount;
  165. KMemoryBlockSlabManager slabManager;
  166. if (PersonalMmHeapPagesCount != 0)
  167. {
  168. slabManager = new KMemoryBlockSlabManager(PersonalMmHeapPagesCount * KPageTableBase.PageSize);
  169. }
  170. else
  171. {
  172. slabManager = creationInfo.Flags.HasFlag(ProcessCreationFlags.IsApplication)
  173. ? KernelContext.LargeMemoryBlockSlabManager
  174. : KernelContext.SmallMemoryBlockSlabManager;
  175. }
  176. AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
  177. Pid = KernelContext.NewProcessId();
  178. if (Pid == ulong.MaxValue || Pid < KernelConstants.InitialProcessId)
  179. {
  180. throw new InvalidOperationException($"Invalid Process Id {Pid}.");
  181. }
  182. InitializeMemoryManager(creationInfo.Flags);
  183. bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
  184. ulong codeAddress = creationInfo.CodeAddress;
  185. ulong codeSize = codePagesCount * KPageTableBase.PageSize;
  186. KernelResult result = MemoryManager.InitializeForProcess(
  187. addrSpaceType,
  188. aslrEnabled,
  189. !aslrEnabled,
  190. memRegion,
  191. codeAddress,
  192. codeSize,
  193. slabManager);
  194. if (result != KernelResult.Success)
  195. {
  196. CleanUpForError();
  197. return result;
  198. }
  199. if (!MemoryManager.CanContain(codeAddress, codeSize, MemoryState.CodeStatic))
  200. {
  201. CleanUpForError();
  202. return KernelResult.InvalidMemRange;
  203. }
  204. result = MemoryManager.MapPages(
  205. codeAddress,
  206. codePagesCount,
  207. MemoryState.CodeStatic,
  208. KMemoryPermission.None);
  209. if (result != KernelResult.Success)
  210. {
  211. CleanUpForError();
  212. return result;
  213. }
  214. result = Capabilities.InitializeForUser(capabilities, MemoryManager);
  215. if (result != KernelResult.Success)
  216. {
  217. CleanUpForError();
  218. return result;
  219. }
  220. result = ParseProcessInfo(creationInfo);
  221. if (result != KernelResult.Success)
  222. {
  223. CleanUpForError();
  224. }
  225. return result;
  226. }
  227. private KernelResult ParseProcessInfo(ProcessCreationInfo creationInfo)
  228. {
  229. // Ensure that the current kernel version is equal or above to the minimum required.
  230. uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
  231. uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
  232. if (KernelContext.EnableVersionChecks)
  233. {
  234. if (requiredKernelVersionMajor > KernelVersionMajor)
  235. {
  236. return KernelResult.InvalidCombination;
  237. }
  238. if (requiredKernelVersionMajor != KernelVersionMajor && requiredKernelVersionMajor < 3)
  239. {
  240. return KernelResult.InvalidCombination;
  241. }
  242. if (requiredKernelVersionMinor > KernelVersionMinor)
  243. {
  244. return KernelResult.InvalidCombination;
  245. }
  246. }
  247. KernelResult result = AllocateThreadLocalStorage(out ulong userExceptionContextAddress);
  248. if (result != KernelResult.Success)
  249. {
  250. return result;
  251. }
  252. UserExceptionContextAddress = userExceptionContextAddress;
  253. MemoryHelper.FillWithZeros(CpuMemory, userExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
  254. Name = creationInfo.Name;
  255. State = ProcessState.Created;
  256. _creationTimestamp = PerformanceCounter.ElapsedMilliseconds;
  257. Flags = creationInfo.Flags;
  258. _version = creationInfo.Version;
  259. TitleId = creationInfo.TitleId;
  260. _entrypoint = creationInfo.CodeAddress;
  261. _imageSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
  262. switch (Flags & ProcessCreationFlags.AddressSpaceMask)
  263. {
  264. case ProcessCreationFlags.AddressSpace32Bit:
  265. case ProcessCreationFlags.AddressSpace64BitDeprecated:
  266. case ProcessCreationFlags.AddressSpace64Bit:
  267. _memoryUsageCapacity = MemoryManager.HeapRegionEnd -
  268. MemoryManager.HeapRegionStart;
  269. break;
  270. case ProcessCreationFlags.AddressSpace32BitWithoutAlias:
  271. _memoryUsageCapacity = MemoryManager.HeapRegionEnd -
  272. MemoryManager.HeapRegionStart +
  273. MemoryManager.AliasRegionEnd -
  274. MemoryManager.AliasRegionStart;
  275. break;
  276. default: throw new InvalidOperationException($"Invalid MMU flags value 0x{Flags:x2}.");
  277. }
  278. GenerateRandomEntropy();
  279. return KernelResult.Success;
  280. }
  281. public KernelResult AllocateThreadLocalStorage(out ulong address)
  282. {
  283. KernelContext.CriticalSection.Enter();
  284. KernelResult result;
  285. if (_freeTlsPages.Count > 0)
  286. {
  287. // If we have free TLS pages available, just use the first one.
  288. KTlsPageInfo pageInfo = _freeTlsPages.Values.First();
  289. if (!pageInfo.TryGetFreePage(out address))
  290. {
  291. throw new InvalidOperationException("Unexpected failure getting free TLS page!");
  292. }
  293. if (pageInfo.IsFull())
  294. {
  295. _freeTlsPages.Remove(pageInfo.PageVirtualAddress);
  296. _fullTlsPages.Add(pageInfo.PageVirtualAddress, pageInfo);
  297. }
  298. result = KernelResult.Success;
  299. }
  300. else
  301. {
  302. // Otherwise, we need to create a new one.
  303. result = AllocateTlsPage(out KTlsPageInfo pageInfo);
  304. if (result == KernelResult.Success)
  305. {
  306. if (!pageInfo.TryGetFreePage(out address))
  307. {
  308. throw new InvalidOperationException("Unexpected failure getting free TLS page!");
  309. }
  310. _freeTlsPages.Add(pageInfo.PageVirtualAddress, pageInfo);
  311. }
  312. else
  313. {
  314. address = 0;
  315. }
  316. }
  317. KernelContext.CriticalSection.Leave();
  318. return result;
  319. }
  320. private KernelResult AllocateTlsPage(out KTlsPageInfo pageInfo)
  321. {
  322. pageInfo = default;
  323. if (!KernelContext.UserSlabHeapPages.TryGetItem(out ulong tlsPagePa))
  324. {
  325. return KernelResult.OutOfMemory;
  326. }
  327. ulong regionStart = MemoryManager.TlsIoRegionStart;
  328. ulong regionSize = MemoryManager.TlsIoRegionEnd - regionStart;
  329. ulong regionPagesCount = regionSize / KPageTableBase.PageSize;
  330. KernelResult result = MemoryManager.MapPages(
  331. 1,
  332. KPageTableBase.PageSize,
  333. tlsPagePa,
  334. true,
  335. regionStart,
  336. regionPagesCount,
  337. MemoryState.ThreadLocal,
  338. KMemoryPermission.ReadAndWrite,
  339. out ulong tlsPageVa);
  340. if (result != KernelResult.Success)
  341. {
  342. KernelContext.UserSlabHeapPages.Free(tlsPagePa);
  343. }
  344. else
  345. {
  346. pageInfo = new KTlsPageInfo(tlsPageVa, tlsPagePa);
  347. MemoryHelper.FillWithZeros(CpuMemory, tlsPageVa, KPageTableBase.PageSize);
  348. }
  349. return result;
  350. }
  351. public KernelResult FreeThreadLocalStorage(ulong tlsSlotAddr)
  352. {
  353. ulong tlsPageAddr = BitUtils.AlignDown(tlsSlotAddr, KPageTableBase.PageSize);
  354. KernelContext.CriticalSection.Enter();
  355. KernelResult result = KernelResult.Success;
  356. KTlsPageInfo pageInfo;
  357. if (_fullTlsPages.TryGetValue(tlsPageAddr, out pageInfo))
  358. {
  359. // TLS page was full, free slot and move to free pages tree.
  360. _fullTlsPages.Remove(tlsPageAddr);
  361. _freeTlsPages.Add(tlsPageAddr, pageInfo);
  362. }
  363. else if (!_freeTlsPages.TryGetValue(tlsPageAddr, out pageInfo))
  364. {
  365. result = KernelResult.InvalidAddress;
  366. }
  367. if (pageInfo != null)
  368. {
  369. pageInfo.FreeTlsSlot(tlsSlotAddr);
  370. if (pageInfo.IsEmpty())
  371. {
  372. // TLS page is now empty, we should ensure it is removed
  373. // from all trees, and free the memory it was using.
  374. _freeTlsPages.Remove(tlsPageAddr);
  375. KernelContext.CriticalSection.Leave();
  376. FreeTlsPage(pageInfo);
  377. return KernelResult.Success;
  378. }
  379. }
  380. KernelContext.CriticalSection.Leave();
  381. return result;
  382. }
  383. private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
  384. {
  385. KernelResult result = MemoryManager.UnmapForKernel(pageInfo.PageVirtualAddress, 1, MemoryState.ThreadLocal);
  386. if (result == KernelResult.Success)
  387. {
  388. KernelContext.UserSlabHeapPages.Free(pageInfo.PagePhysicalAddress);
  389. }
  390. return result;
  391. }
  392. private void GenerateRandomEntropy()
  393. {
  394. // TODO.
  395. }
  396. public KernelResult Start(int mainThreadPriority, ulong stackSize)
  397. {
  398. lock (_processLock)
  399. {
  400. if (State > ProcessState.CreatedAttached)
  401. {
  402. return KernelResult.InvalidState;
  403. }
  404. if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1))
  405. {
  406. return KernelResult.ResLimitExceeded;
  407. }
  408. KResourceLimit threadResourceLimit = ResourceLimit;
  409. KResourceLimit memoryResourceLimit = null;
  410. if (_mainThreadStackSize != 0)
  411. {
  412. throw new InvalidOperationException("Trying to start a process with a invalid state!");
  413. }
  414. ulong stackSizeRounded = BitUtils.AlignUp(stackSize, KPageTableBase.PageSize);
  415. ulong neededSize = stackSizeRounded + _imageSize;
  416. // Check if the needed size for the code and the stack will fit on the
  417. // memory usage capacity of this Process. Also check for possible overflow
  418. // on the above addition.
  419. if (neededSize > _memoryUsageCapacity || neededSize < stackSizeRounded)
  420. {
  421. threadResourceLimit?.Release(LimitableResource.Thread, 1);
  422. return KernelResult.OutOfMemory;
  423. }
  424. if (stackSizeRounded != 0 && ResourceLimit != null)
  425. {
  426. memoryResourceLimit = ResourceLimit;
  427. if (!memoryResourceLimit.Reserve(LimitableResource.Memory, stackSizeRounded))
  428. {
  429. threadResourceLimit?.Release(LimitableResource.Thread, 1);
  430. return KernelResult.ResLimitExceeded;
  431. }
  432. }
  433. KernelResult result;
  434. KThread mainThread = null;
  435. ulong stackTop = 0;
  436. void CleanUpForError()
  437. {
  438. HandleTable.Destroy();
  439. mainThread?.DecrementReferenceCount();
  440. if (_mainThreadStackSize != 0)
  441. {
  442. ulong stackBottom = stackTop - _mainThreadStackSize;
  443. ulong stackPagesCount = _mainThreadStackSize / KPageTableBase.PageSize;
  444. MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack);
  445. _mainThreadStackSize = 0;
  446. }
  447. memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded);
  448. threadResourceLimit?.Release(LimitableResource.Thread, 1);
  449. }
  450. if (stackSizeRounded != 0)
  451. {
  452. ulong stackPagesCount = stackSizeRounded / KPageTableBase.PageSize;
  453. ulong regionStart = MemoryManager.StackRegionStart;
  454. ulong regionSize = MemoryManager.StackRegionEnd - regionStart;
  455. ulong regionPagesCount = regionSize / KPageTableBase.PageSize;
  456. result = MemoryManager.MapPages(
  457. stackPagesCount,
  458. KPageTableBase.PageSize,
  459. 0,
  460. false,
  461. regionStart,
  462. regionPagesCount,
  463. MemoryState.Stack,
  464. KMemoryPermission.ReadAndWrite,
  465. out ulong stackBottom);
  466. if (result != KernelResult.Success)
  467. {
  468. CleanUpForError();
  469. return result;
  470. }
  471. _mainThreadStackSize += stackSizeRounded;
  472. stackTop = stackBottom + stackSizeRounded;
  473. }
  474. ulong heapCapacity = _memoryUsageCapacity - _mainThreadStackSize - _imageSize;
  475. result = MemoryManager.SetHeapCapacity(heapCapacity);
  476. if (result != KernelResult.Success)
  477. {
  478. CleanUpForError();
  479. return result;
  480. }
  481. HandleTable = new KHandleTable(KernelContext);
  482. result = HandleTable.Initialize(Capabilities.HandleTableSize);
  483. if (result != KernelResult.Success)
  484. {
  485. CleanUpForError();
  486. return result;
  487. }
  488. mainThread = new KThread(KernelContext);
  489. result = mainThread.Initialize(
  490. _entrypoint,
  491. 0,
  492. stackTop,
  493. mainThreadPriority,
  494. DefaultCpuCore,
  495. this,
  496. ThreadType.User,
  497. _customThreadStart);
  498. if (result != KernelResult.Success)
  499. {
  500. CleanUpForError();
  501. return result;
  502. }
  503. result = HandleTable.GenerateHandle(mainThread, out int mainThreadHandle);
  504. if (result != KernelResult.Success)
  505. {
  506. CleanUpForError();
  507. return result;
  508. }
  509. mainThread.SetEntryArguments(0, mainThreadHandle);
  510. ProcessState oldState = State;
  511. ProcessState newState = State != ProcessState.Created
  512. ? ProcessState.Attached
  513. : ProcessState.Started;
  514. SetState(newState);
  515. result = mainThread.Start();
  516. if (result != KernelResult.Success)
  517. {
  518. SetState(oldState);
  519. CleanUpForError();
  520. }
  521. if (result == KernelResult.Success)
  522. {
  523. mainThread.IncrementReferenceCount();
  524. }
  525. mainThread.DecrementReferenceCount();
  526. return result;
  527. }
  528. }
  529. private void SetState(ProcessState newState)
  530. {
  531. if (State != newState)
  532. {
  533. State = newState;
  534. _signaled = true;
  535. Signal();
  536. }
  537. }
  538. public KernelResult InitializeThread(
  539. KThread thread,
  540. ulong entrypoint,
  541. ulong argsPtr,
  542. ulong stackTop,
  543. int priority,
  544. int cpuCore,
  545. ThreadStart customThreadStart = null)
  546. {
  547. lock (_processLock)
  548. {
  549. return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, customThreadStart);
  550. }
  551. }
  552. public IExecutionContext CreateExecutionContext()
  553. {
  554. return Context?.CreateExecutionContext(new ExceptionCallbacks(
  555. InterruptHandler,
  556. null,
  557. KernelContext.SyscallHandler.SvcCall,
  558. UndefinedInstructionHandler));
  559. }
  560. private void InterruptHandler(IExecutionContext context)
  561. {
  562. KThread currentThread = KernelStatic.GetCurrentThread();
  563. if (currentThread.Context.Running &&
  564. currentThread.Owner != null &&
  565. currentThread.GetUserDisableCount() != 0 &&
  566. currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
  567. {
  568. KernelContext.CriticalSection.Enter();
  569. currentThread.Owner.PinThread(currentThread);
  570. currentThread.SetUserInterruptFlag();
  571. KernelContext.CriticalSection.Leave();
  572. }
  573. if (currentThread.IsSchedulable)
  574. {
  575. KernelContext.Schedulers[currentThread.CurrentCore].Schedule();
  576. }
  577. currentThread.HandlePostSyscall();
  578. }
  579. public void IncrementThreadCount()
  580. {
  581. Interlocked.Increment(ref _threadCount);
  582. }
  583. public void DecrementThreadCountAndTerminateIfZero()
  584. {
  585. if (Interlocked.Decrement(ref _threadCount) == 0)
  586. {
  587. Terminate();
  588. }
  589. }
  590. public void DecrementToZeroWhileTerminatingCurrent()
  591. {
  592. while (Interlocked.Decrement(ref _threadCount) != 0)
  593. {
  594. Destroy();
  595. TerminateCurrentProcess();
  596. }
  597. // Nintendo panic here because if it reaches this point, the current thread should be already dead.
  598. // As we handle the death of the thread in the post SVC handler and inside the CPU emulator, we don't panic here.
  599. }
  600. public ulong GetMemoryCapacity()
  601. {
  602. ulong totalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory);
  603. totalCapacity += MemoryManager.GetTotalHeapSize();
  604. totalCapacity += GetPersonalMmHeapSize();
  605. totalCapacity += _imageSize + _mainThreadStackSize;
  606. if (totalCapacity <= _memoryUsageCapacity)
  607. {
  608. return totalCapacity;
  609. }
  610. return _memoryUsageCapacity;
  611. }
  612. public ulong GetMemoryUsage()
  613. {
  614. return _imageSize + _mainThreadStackSize + MemoryManager.GetTotalHeapSize() + GetPersonalMmHeapSize();
  615. }
  616. public ulong GetMemoryCapacityWithoutPersonalMmHeap()
  617. {
  618. return GetMemoryCapacity() - GetPersonalMmHeapSize();
  619. }
  620. public ulong GetMemoryUsageWithoutPersonalMmHeap()
  621. {
  622. return GetMemoryUsage() - GetPersonalMmHeapSize();
  623. }
  624. private ulong GetPersonalMmHeapSize()
  625. {
  626. return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, _memRegion);
  627. }
  628. private static ulong GetPersonalMmHeapSize(ulong personalMmHeapPagesCount, MemoryRegion memRegion)
  629. {
  630. if (memRegion == MemoryRegion.Applet)
  631. {
  632. return 0;
  633. }
  634. return personalMmHeapPagesCount * KPageTableBase.PageSize;
  635. }
  636. public void AddCpuTime(long ticks)
  637. {
  638. Interlocked.Add(ref _totalTimeRunning, ticks);
  639. }
  640. public void AddThread(KThread thread)
  641. {
  642. lock (_threadingLock)
  643. {
  644. thread.ProcessListNode = _threads.AddLast(thread);
  645. }
  646. }
  647. public void RemoveThread(KThread thread)
  648. {
  649. lock (_threadingLock)
  650. {
  651. _threads.Remove(thread.ProcessListNode);
  652. }
  653. }
  654. public bool IsCpuCoreAllowed(int core)
  655. {
  656. return (Capabilities.AllowedCpuCoresMask & (1UL << core)) != 0;
  657. }
  658. public bool IsPriorityAllowed(int priority)
  659. {
  660. return (Capabilities.AllowedThreadPriosMask & (1UL << priority)) != 0;
  661. }
  662. public override bool IsSignaled()
  663. {
  664. return _signaled;
  665. }
  666. public KernelResult Terminate()
  667. {
  668. KernelResult result;
  669. bool shallTerminate = false;
  670. KernelContext.CriticalSection.Enter();
  671. lock (_processLock)
  672. {
  673. if (State >= ProcessState.Started)
  674. {
  675. if (State == ProcessState.Started ||
  676. State == ProcessState.Crashed ||
  677. State == ProcessState.Attached ||
  678. State == ProcessState.DebugSuspended)
  679. {
  680. SetState(ProcessState.Exiting);
  681. shallTerminate = true;
  682. }
  683. result = KernelResult.Success;
  684. }
  685. else
  686. {
  687. result = KernelResult.InvalidState;
  688. }
  689. }
  690. KernelContext.CriticalSection.Leave();
  691. if (shallTerminate)
  692. {
  693. UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
  694. HandleTable.Destroy();
  695. SignalExitToDebugTerminated();
  696. SignalExit();
  697. }
  698. return result;
  699. }
  700. public void TerminateCurrentProcess()
  701. {
  702. bool shallTerminate = false;
  703. KernelContext.CriticalSection.Enter();
  704. lock (_processLock)
  705. {
  706. if (State >= ProcessState.Started)
  707. {
  708. if (State == ProcessState.Started ||
  709. State == ProcessState.Attached ||
  710. State == ProcessState.DebugSuspended)
  711. {
  712. SetState(ProcessState.Exiting);
  713. shallTerminate = true;
  714. }
  715. }
  716. }
  717. KernelContext.CriticalSection.Leave();
  718. if (shallTerminate)
  719. {
  720. UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
  721. HandleTable.Destroy();
  722. // NOTE: this is supposed to be called in receiving of the mailbox.
  723. SignalExitToDebugExited();
  724. SignalExit();
  725. }
  726. KernelStatic.GetCurrentThread().Exit();
  727. }
  728. private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread)
  729. {
  730. lock (_threadingLock)
  731. {
  732. KernelContext.CriticalSection.Enter();
  733. if (currentThread != null && PinnedThreads[currentThread.CurrentCore] == currentThread)
  734. {
  735. UnpinThread(currentThread);
  736. }
  737. foreach (KThread thread in _threads)
  738. {
  739. if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
  740. {
  741. thread.PrepareForTermination();
  742. }
  743. }
  744. KernelContext.CriticalSection.Leave();
  745. }
  746. while (true)
  747. {
  748. KThread blockedThread = null;
  749. lock (_threadingLock)
  750. {
  751. foreach (KThread thread in _threads)
  752. {
  753. if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
  754. {
  755. thread.IncrementReferenceCount();
  756. blockedThread = thread;
  757. break;
  758. }
  759. }
  760. }
  761. if (blockedThread == null)
  762. {
  763. break;
  764. }
  765. blockedThread.Terminate();
  766. blockedThread.DecrementReferenceCount();
  767. }
  768. }
  769. private void SignalExitToDebugTerminated()
  770. {
  771. // TODO: Debug events.
  772. }
  773. private void SignalExitToDebugExited()
  774. {
  775. // TODO: Debug events.
  776. }
  777. private void SignalExit()
  778. {
  779. if (ResourceLimit != null)
  780. {
  781. ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage());
  782. }
  783. KernelContext.CriticalSection.Enter();
  784. SetState(ProcessState.Exited);
  785. KernelContext.CriticalSection.Leave();
  786. }
  787. public KernelResult ClearIfNotExited()
  788. {
  789. KernelResult result;
  790. KernelContext.CriticalSection.Enter();
  791. lock (_processLock)
  792. {
  793. if (State != ProcessState.Exited && _signaled)
  794. {
  795. _signaled = false;
  796. result = KernelResult.Success;
  797. }
  798. else
  799. {
  800. result = KernelResult.InvalidState;
  801. }
  802. }
  803. KernelContext.CriticalSection.Leave();
  804. return result;
  805. }
  806. private void InitializeMemoryManager(ProcessCreationFlags flags)
  807. {
  808. int addrSpaceBits = (flags & ProcessCreationFlags.AddressSpaceMask) switch
  809. {
  810. ProcessCreationFlags.AddressSpace32Bit => 32,
  811. ProcessCreationFlags.AddressSpace64BitDeprecated => 36,
  812. ProcessCreationFlags.AddressSpace32BitWithoutAlias => 32,
  813. ProcessCreationFlags.AddressSpace64Bit => 39,
  814. _ => 39
  815. };
  816. bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit);
  817. Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
  818. MemoryManager = new KPageTable(KernelContext, CpuMemory);
  819. }
  820. private bool InvalidAccessHandler(ulong va)
  821. {
  822. KernelStatic.GetCurrentThread()?.PrintGuestStackTrace();
  823. KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
  824. Logger.Error?.Print(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}.");
  825. return false;
  826. }
  827. private void UndefinedInstructionHandler(IExecutionContext context, ulong address, int opCode)
  828. {
  829. KernelStatic.GetCurrentThread().PrintGuestStackTrace();
  830. KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
  831. throw new UndefinedInstructionException(address, opCode);
  832. }
  833. protected override void Destroy() => Context.Dispose();
  834. public KernelResult SetActivity(bool pause)
  835. {
  836. KernelContext.CriticalSection.Enter();
  837. if (State != ProcessState.Exiting && State != ProcessState.Exited)
  838. {
  839. if (pause)
  840. {
  841. if (IsPaused)
  842. {
  843. KernelContext.CriticalSection.Leave();
  844. return KernelResult.InvalidState;
  845. }
  846. lock (_threadingLock)
  847. {
  848. foreach (KThread thread in _threads)
  849. {
  850. thread.Suspend(ThreadSchedState.ProcessPauseFlag);
  851. }
  852. }
  853. IsPaused = true;
  854. }
  855. else
  856. {
  857. if (!IsPaused)
  858. {
  859. KernelContext.CriticalSection.Leave();
  860. return KernelResult.InvalidState;
  861. }
  862. lock (_threadingLock)
  863. {
  864. foreach (KThread thread in _threads)
  865. {
  866. thread.Resume(ThreadSchedState.ProcessPauseFlag);
  867. }
  868. }
  869. IsPaused = false;
  870. }
  871. KernelContext.CriticalSection.Leave();
  872. return KernelResult.Success;
  873. }
  874. KernelContext.CriticalSection.Leave();
  875. return KernelResult.InvalidState;
  876. }
  877. public void PinThread(KThread thread)
  878. {
  879. if (!thread.TerminationRequested)
  880. {
  881. PinnedThreads[thread.CurrentCore] = thread;
  882. thread.Pin();
  883. KernelContext.ThreadReselectionRequested = true;
  884. }
  885. }
  886. public void UnpinThread(KThread thread)
  887. {
  888. if (!thread.TerminationRequested)
  889. {
  890. thread.Unpin();
  891. PinnedThreads[thread.CurrentCore] = null;
  892. KernelContext.ThreadReselectionRequested = true;
  893. }
  894. }
  895. public bool IsExceptionUserThread(KThread thread)
  896. {
  897. // TODO
  898. return false;
  899. }
  900. }
  901. }