KProcess.cs 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  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. }
  727. private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread)
  728. {
  729. lock (_threadingLock)
  730. {
  731. KernelContext.CriticalSection.Enter();
  732. if (currentThread != null && PinnedThreads[currentThread.CurrentCore] == currentThread)
  733. {
  734. UnpinThread(currentThread);
  735. }
  736. foreach (KThread thread in _threads)
  737. {
  738. if ((thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
  739. {
  740. thread.PrepareForTermination();
  741. }
  742. }
  743. KernelContext.CriticalSection.Leave();
  744. }
  745. while (true)
  746. {
  747. KThread blockedThread = null;
  748. lock (_threadingLock)
  749. {
  750. foreach (KThread thread in _threads)
  751. {
  752. if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
  753. {
  754. thread.IncrementReferenceCount();
  755. blockedThread = thread;
  756. break;
  757. }
  758. }
  759. }
  760. if (blockedThread == null)
  761. {
  762. break;
  763. }
  764. blockedThread.Terminate();
  765. blockedThread.DecrementReferenceCount();
  766. }
  767. }
  768. private void SignalExitToDebugTerminated()
  769. {
  770. // TODO: Debug events.
  771. }
  772. private void SignalExitToDebugExited()
  773. {
  774. // TODO: Debug events.
  775. }
  776. private void SignalExit()
  777. {
  778. if (ResourceLimit != null)
  779. {
  780. ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage());
  781. }
  782. KernelContext.CriticalSection.Enter();
  783. SetState(ProcessState.Exited);
  784. KernelContext.CriticalSection.Leave();
  785. }
  786. public KernelResult ClearIfNotExited()
  787. {
  788. KernelResult result;
  789. KernelContext.CriticalSection.Enter();
  790. lock (_processLock)
  791. {
  792. if (State != ProcessState.Exited && _signaled)
  793. {
  794. _signaled = false;
  795. result = KernelResult.Success;
  796. }
  797. else
  798. {
  799. result = KernelResult.InvalidState;
  800. }
  801. }
  802. KernelContext.CriticalSection.Leave();
  803. return result;
  804. }
  805. private void InitializeMemoryManager(ProcessCreationFlags flags)
  806. {
  807. int addrSpaceBits = (flags & ProcessCreationFlags.AddressSpaceMask) switch
  808. {
  809. ProcessCreationFlags.AddressSpace32Bit => 32,
  810. ProcessCreationFlags.AddressSpace64BitDeprecated => 36,
  811. ProcessCreationFlags.AddressSpace32BitWithoutAlias => 32,
  812. ProcessCreationFlags.AddressSpace64Bit => 39,
  813. _ => 39
  814. };
  815. bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit);
  816. Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
  817. MemoryManager = new KPageTable(KernelContext, CpuMemory);
  818. }
  819. private bool InvalidAccessHandler(ulong va)
  820. {
  821. KernelStatic.GetCurrentThread()?.PrintGuestStackTrace();
  822. KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
  823. Logger.Error?.Print(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}.");
  824. return false;
  825. }
  826. private void UndefinedInstructionHandler(IExecutionContext context, ulong address, int opCode)
  827. {
  828. KernelStatic.GetCurrentThread().PrintGuestStackTrace();
  829. KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
  830. throw new UndefinedInstructionException(address, opCode);
  831. }
  832. protected override void Destroy() => Context.Dispose();
  833. public KernelResult SetActivity(bool pause)
  834. {
  835. KernelContext.CriticalSection.Enter();
  836. if (State != ProcessState.Exiting && State != ProcessState.Exited)
  837. {
  838. if (pause)
  839. {
  840. if (IsPaused)
  841. {
  842. KernelContext.CriticalSection.Leave();
  843. return KernelResult.InvalidState;
  844. }
  845. lock (_threadingLock)
  846. {
  847. foreach (KThread thread in _threads)
  848. {
  849. thread.Suspend(ThreadSchedState.ProcessPauseFlag);
  850. }
  851. }
  852. IsPaused = true;
  853. }
  854. else
  855. {
  856. if (!IsPaused)
  857. {
  858. KernelContext.CriticalSection.Leave();
  859. return KernelResult.InvalidState;
  860. }
  861. lock (_threadingLock)
  862. {
  863. foreach (KThread thread in _threads)
  864. {
  865. thread.Resume(ThreadSchedState.ProcessPauseFlag);
  866. }
  867. }
  868. IsPaused = false;
  869. }
  870. KernelContext.CriticalSection.Leave();
  871. return KernelResult.Success;
  872. }
  873. KernelContext.CriticalSection.Leave();
  874. return KernelResult.InvalidState;
  875. }
  876. public void PinThread(KThread thread)
  877. {
  878. if (!thread.TerminationRequested)
  879. {
  880. PinnedThreads[thread.CurrentCore] = thread;
  881. thread.Pin();
  882. KernelContext.ThreadReselectionRequested = true;
  883. }
  884. }
  885. public void UnpinThread(KThread thread)
  886. {
  887. if (!thread.TerminationRequested)
  888. {
  889. thread.Unpin();
  890. PinnedThreads[thread.CurrentCore] = null;
  891. KernelContext.ThreadReselectionRequested = true;
  892. }
  893. }
  894. public bool IsExceptionUserThread(KThread thread)
  895. {
  896. // TODO
  897. return false;
  898. }
  899. }
  900. }