KProcess.cs 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  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. {
  546. lock (_processLock)
  547. {
  548. return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null);
  549. }
  550. }
  551. public IExecutionContext CreateExecutionContext()
  552. {
  553. return Context?.CreateExecutionContext(new ExceptionCallbacks(
  554. InterruptHandler,
  555. null,
  556. KernelContext.SyscallHandler.SvcCall,
  557. UndefinedInstructionHandler));
  558. }
  559. private void InterruptHandler(IExecutionContext context)
  560. {
  561. KThread currentThread = KernelStatic.GetCurrentThread();
  562. if (currentThread.Context.Running &&
  563. currentThread.Owner != null &&
  564. currentThread.GetUserDisableCount() != 0 &&
  565. currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
  566. {
  567. KernelContext.CriticalSection.Enter();
  568. currentThread.Owner.PinThread(currentThread);
  569. currentThread.SetUserInterruptFlag();
  570. KernelContext.CriticalSection.Leave();
  571. }
  572. if (currentThread.IsSchedulable)
  573. {
  574. KernelContext.Schedulers[currentThread.CurrentCore].Schedule();
  575. }
  576. currentThread.HandlePostSyscall();
  577. }
  578. public void IncrementThreadCount()
  579. {
  580. Interlocked.Increment(ref _threadCount);
  581. }
  582. public void DecrementThreadCountAndTerminateIfZero()
  583. {
  584. if (Interlocked.Decrement(ref _threadCount) == 0)
  585. {
  586. Terminate();
  587. }
  588. }
  589. public void DecrementToZeroWhileTerminatingCurrent()
  590. {
  591. while (Interlocked.Decrement(ref _threadCount) != 0)
  592. {
  593. Destroy();
  594. TerminateCurrentProcess();
  595. }
  596. // Nintendo panic here because if it reaches this point, the current thread should be already dead.
  597. // As we handle the death of the thread in the post SVC handler and inside the CPU emulator, we don't panic here.
  598. }
  599. public ulong GetMemoryCapacity()
  600. {
  601. ulong totalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory);
  602. totalCapacity += MemoryManager.GetTotalHeapSize();
  603. totalCapacity += GetPersonalMmHeapSize();
  604. totalCapacity += _imageSize + _mainThreadStackSize;
  605. if (totalCapacity <= _memoryUsageCapacity)
  606. {
  607. return totalCapacity;
  608. }
  609. return _memoryUsageCapacity;
  610. }
  611. public ulong GetMemoryUsage()
  612. {
  613. return _imageSize + _mainThreadStackSize + MemoryManager.GetTotalHeapSize() + GetPersonalMmHeapSize();
  614. }
  615. public ulong GetMemoryCapacityWithoutPersonalMmHeap()
  616. {
  617. return GetMemoryCapacity() - GetPersonalMmHeapSize();
  618. }
  619. public ulong GetMemoryUsageWithoutPersonalMmHeap()
  620. {
  621. return GetMemoryUsage() - GetPersonalMmHeapSize();
  622. }
  623. private ulong GetPersonalMmHeapSize()
  624. {
  625. return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, _memRegion);
  626. }
  627. private static ulong GetPersonalMmHeapSize(ulong personalMmHeapPagesCount, MemoryRegion memRegion)
  628. {
  629. if (memRegion == MemoryRegion.Applet)
  630. {
  631. return 0;
  632. }
  633. return personalMmHeapPagesCount * KPageTableBase.PageSize;
  634. }
  635. public void AddCpuTime(long ticks)
  636. {
  637. Interlocked.Add(ref _totalTimeRunning, ticks);
  638. }
  639. public void AddThread(KThread thread)
  640. {
  641. lock (_threadingLock)
  642. {
  643. thread.ProcessListNode = _threads.AddLast(thread);
  644. }
  645. }
  646. public void RemoveThread(KThread thread)
  647. {
  648. lock (_threadingLock)
  649. {
  650. _threads.Remove(thread.ProcessListNode);
  651. }
  652. }
  653. public bool IsCpuCoreAllowed(int core)
  654. {
  655. return (Capabilities.AllowedCpuCoresMask & (1UL << core)) != 0;
  656. }
  657. public bool IsPriorityAllowed(int priority)
  658. {
  659. return (Capabilities.AllowedThreadPriosMask & (1UL << priority)) != 0;
  660. }
  661. public override bool IsSignaled()
  662. {
  663. return _signaled;
  664. }
  665. public KernelResult Terminate()
  666. {
  667. KernelResult result;
  668. bool shallTerminate = false;
  669. KernelContext.CriticalSection.Enter();
  670. lock (_processLock)
  671. {
  672. if (State >= ProcessState.Started)
  673. {
  674. if (State == ProcessState.Started ||
  675. State == ProcessState.Crashed ||
  676. State == ProcessState.Attached ||
  677. State == ProcessState.DebugSuspended)
  678. {
  679. SetState(ProcessState.Exiting);
  680. shallTerminate = true;
  681. }
  682. result = KernelResult.Success;
  683. }
  684. else
  685. {
  686. result = KernelResult.InvalidState;
  687. }
  688. }
  689. KernelContext.CriticalSection.Leave();
  690. if (shallTerminate)
  691. {
  692. UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
  693. HandleTable.Destroy();
  694. SignalExitToDebugTerminated();
  695. SignalExit();
  696. }
  697. return result;
  698. }
  699. public void TerminateCurrentProcess()
  700. {
  701. bool shallTerminate = false;
  702. KernelContext.CriticalSection.Enter();
  703. lock (_processLock)
  704. {
  705. if (State >= ProcessState.Started)
  706. {
  707. if (State == ProcessState.Started ||
  708. State == ProcessState.Attached ||
  709. State == ProcessState.DebugSuspended)
  710. {
  711. SetState(ProcessState.Exiting);
  712. shallTerminate = true;
  713. }
  714. }
  715. }
  716. KernelContext.CriticalSection.Leave();
  717. if (shallTerminate)
  718. {
  719. UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
  720. HandleTable.Destroy();
  721. // NOTE: this is supposed to be called in receiving of the mailbox.
  722. SignalExitToDebugExited();
  723. SignalExit();
  724. }
  725. }
  726. private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread)
  727. {
  728. lock (_threadingLock)
  729. {
  730. KernelContext.CriticalSection.Enter();
  731. if (currentThread != null && PinnedThreads[currentThread.CurrentCore] == currentThread)
  732. {
  733. UnpinThread(currentThread);
  734. }
  735. foreach (KThread thread in _threads)
  736. {
  737. if ((thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
  738. {
  739. thread.PrepareForTermination();
  740. }
  741. }
  742. KernelContext.CriticalSection.Leave();
  743. }
  744. while (true)
  745. {
  746. KThread blockedThread = null;
  747. lock (_threadingLock)
  748. {
  749. foreach (KThread thread in _threads)
  750. {
  751. if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
  752. {
  753. thread.IncrementReferenceCount();
  754. blockedThread = thread;
  755. break;
  756. }
  757. }
  758. }
  759. if (blockedThread == null)
  760. {
  761. break;
  762. }
  763. blockedThread.Terminate();
  764. blockedThread.DecrementReferenceCount();
  765. }
  766. }
  767. private void SignalExitToDebugTerminated()
  768. {
  769. // TODO: Debug events.
  770. }
  771. private void SignalExitToDebugExited()
  772. {
  773. // TODO: Debug events.
  774. }
  775. private void SignalExit()
  776. {
  777. if (ResourceLimit != null)
  778. {
  779. ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage());
  780. }
  781. KernelContext.CriticalSection.Enter();
  782. SetState(ProcessState.Exited);
  783. KernelContext.CriticalSection.Leave();
  784. }
  785. public KernelResult ClearIfNotExited()
  786. {
  787. KernelResult result;
  788. KernelContext.CriticalSection.Enter();
  789. lock (_processLock)
  790. {
  791. if (State != ProcessState.Exited && _signaled)
  792. {
  793. _signaled = false;
  794. result = KernelResult.Success;
  795. }
  796. else
  797. {
  798. result = KernelResult.InvalidState;
  799. }
  800. }
  801. KernelContext.CriticalSection.Leave();
  802. return result;
  803. }
  804. private void InitializeMemoryManager(ProcessCreationFlags flags)
  805. {
  806. int addrSpaceBits = (flags & ProcessCreationFlags.AddressSpaceMask) switch
  807. {
  808. ProcessCreationFlags.AddressSpace32Bit => 32,
  809. ProcessCreationFlags.AddressSpace64BitDeprecated => 36,
  810. ProcessCreationFlags.AddressSpace32BitWithoutAlias => 32,
  811. ProcessCreationFlags.AddressSpace64Bit => 39,
  812. _ => 39
  813. };
  814. bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit);
  815. Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
  816. MemoryManager = new KPageTable(KernelContext, CpuMemory);
  817. }
  818. private bool InvalidAccessHandler(ulong va)
  819. {
  820. KernelStatic.GetCurrentThread()?.PrintGuestStackTrace();
  821. KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
  822. Logger.Error?.Print(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}.");
  823. return false;
  824. }
  825. private void UndefinedInstructionHandler(IExecutionContext context, ulong address, int opCode)
  826. {
  827. KernelStatic.GetCurrentThread().PrintGuestStackTrace();
  828. KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
  829. throw new UndefinedInstructionException(address, opCode);
  830. }
  831. protected override void Destroy() => Context.Dispose();
  832. public KernelResult SetActivity(bool pause)
  833. {
  834. KernelContext.CriticalSection.Enter();
  835. if (State != ProcessState.Exiting && State != ProcessState.Exited)
  836. {
  837. if (pause)
  838. {
  839. if (IsPaused)
  840. {
  841. KernelContext.CriticalSection.Leave();
  842. return KernelResult.InvalidState;
  843. }
  844. lock (_threadingLock)
  845. {
  846. foreach (KThread thread in _threads)
  847. {
  848. thread.Suspend(ThreadSchedState.ProcessPauseFlag);
  849. }
  850. }
  851. IsPaused = true;
  852. }
  853. else
  854. {
  855. if (!IsPaused)
  856. {
  857. KernelContext.CriticalSection.Leave();
  858. return KernelResult.InvalidState;
  859. }
  860. lock (_threadingLock)
  861. {
  862. foreach (KThread thread in _threads)
  863. {
  864. thread.Resume(ThreadSchedState.ProcessPauseFlag);
  865. }
  866. }
  867. IsPaused = false;
  868. }
  869. KernelContext.CriticalSection.Leave();
  870. return KernelResult.Success;
  871. }
  872. KernelContext.CriticalSection.Leave();
  873. return KernelResult.InvalidState;
  874. }
  875. public void PinThread(KThread thread)
  876. {
  877. if (!thread.TerminationRequested)
  878. {
  879. PinnedThreads[thread.CurrentCore] = thread;
  880. thread.Pin();
  881. KernelContext.ThreadReselectionRequested = true;
  882. }
  883. }
  884. public void UnpinThread(KThread thread)
  885. {
  886. if (!thread.TerminationRequested)
  887. {
  888. thread.Unpin();
  889. PinnedThreads[thread.CurrentCore] = null;
  890. KernelContext.ThreadReselectionRequested = true;
  891. }
  892. }
  893. public bool IsExceptionUserThread(KThread thread)
  894. {
  895. // TODO
  896. return false;
  897. }
  898. }
  899. }