ProgramLoader.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. using ARMeilleure.Translation.PTC;
  2. using Ryujinx.Common;
  3. using Ryujinx.Common.Logging;
  4. using Ryujinx.HLE.HOS.Kernel;
  5. using Ryujinx.HLE.HOS.Kernel.Common;
  6. using Ryujinx.HLE.HOS.Kernel.Memory;
  7. using Ryujinx.HLE.HOS.Kernel.Process;
  8. using Ryujinx.HLE.Loaders.Executables;
  9. using Ryujinx.HLE.Loaders.Npdm;
  10. using System;
  11. using System.Linq;
  12. namespace Ryujinx.HLE.HOS
  13. {
  14. static class ProgramLoader
  15. {
  16. private const bool AslrEnabled = true;
  17. private const int ArgsHeaderSize = 8;
  18. private const int ArgsDataSize = 0x9000;
  19. private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize;
  20. public static bool LoadKip(KernelContext context, KipExecutable kip)
  21. {
  22. uint endOffset = kip.DataOffset + (uint)kip.Data.Length;
  23. if (kip.BssSize != 0)
  24. {
  25. endOffset = kip.BssOffset + kip.BssSize;
  26. }
  27. uint codeSize = BitUtils.AlignUp(kip.TextOffset + endOffset, KPageTableBase.PageSize);
  28. int codePagesCount = (int)(codeSize / KPageTableBase.PageSize);
  29. ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL;
  30. ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset;
  31. ProcessCreationFlags flags = 0;
  32. if (AslrEnabled)
  33. {
  34. // TODO: Randomization.
  35. flags |= ProcessCreationFlags.EnableAslr;
  36. }
  37. if (kip.Is64BitAddressSpace)
  38. {
  39. flags |= ProcessCreationFlags.AddressSpace64Bit;
  40. }
  41. if (kip.Is64Bit)
  42. {
  43. flags |= ProcessCreationFlags.Is64Bit;
  44. }
  45. ProcessCreationInfo creationInfo = new ProcessCreationInfo(
  46. kip.Name,
  47. kip.Version,
  48. kip.ProgramId,
  49. codeAddress,
  50. codePagesCount,
  51. flags,
  52. 0,
  53. 0);
  54. MemoryRegion memoryRegion = kip.UsesSecureMemory
  55. ? MemoryRegion.Service
  56. : MemoryRegion.Application;
  57. KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion];
  58. KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList);
  59. if (result != KernelResult.Success)
  60. {
  61. Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
  62. return false;
  63. }
  64. KProcess process = new KProcess(context);
  65. var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
  66. result = process.InitializeKip(
  67. creationInfo,
  68. kip.Capabilities,
  69. pageList,
  70. context.ResourceLimit,
  71. memoryRegion,
  72. processContextFactory);
  73. if (result != KernelResult.Success)
  74. {
  75. Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
  76. return false;
  77. }
  78. result = LoadIntoMemory(process, kip, codeBaseAddress);
  79. if (result != KernelResult.Success)
  80. {
  81. Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
  82. return false;
  83. }
  84. process.DefaultCpuCore = kip.IdealCoreId;
  85. result = process.Start(kip.Priority, (ulong)kip.StackSize);
  86. if (result != KernelResult.Success)
  87. {
  88. Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\".");
  89. return false;
  90. }
  91. context.Processes.TryAdd(process.Pid, process);
  92. return true;
  93. }
  94. public static bool LoadNsos(KernelContext context, out ProcessTamperInfo tamperInfo, Npdm metaData, byte[] arguments = null, params IExecutable[] executables)
  95. {
  96. ulong argsStart = 0;
  97. uint argsSize = 0;
  98. ulong codeStart = metaData.Is64Bit ? 0x8000000UL : 0x200000UL;
  99. uint codeSize = 0;
  100. var buildIds = executables.Select(e => (e switch
  101. {
  102. NsoExecutable nso => BitConverter.ToString(nso.BuildId.Bytes.ToArray()),
  103. NroExecutable nro => BitConverter.ToString(nro.Header.BuildId),
  104. _ => ""
  105. }).Replace("-", "").ToUpper());
  106. ulong[] nsoBase = new ulong[executables.Length];
  107. for (int index = 0; index < executables.Length; index++)
  108. {
  109. IExecutable nso = executables[index];
  110. uint textEnd = nso.TextOffset + (uint)nso.Text.Length;
  111. uint roEnd = nso.RoOffset + (uint)nso.Ro.Length;
  112. uint dataEnd = nso.DataOffset + (uint)nso.Data.Length + nso.BssSize;
  113. uint nsoSize = textEnd;
  114. if (nsoSize < roEnd)
  115. {
  116. nsoSize = roEnd;
  117. }
  118. if (nsoSize < dataEnd)
  119. {
  120. nsoSize = dataEnd;
  121. }
  122. nsoSize = BitUtils.AlignUp(nsoSize, KPageTableBase.PageSize);
  123. nsoBase[index] = codeStart + (ulong)codeSize;
  124. codeSize += nsoSize;
  125. if (arguments != null && argsSize == 0)
  126. {
  127. argsStart = (ulong)codeSize;
  128. argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize);
  129. codeSize += argsSize;
  130. }
  131. }
  132. PtcProfiler.StaticCodeStart = codeStart;
  133. PtcProfiler.StaticCodeSize = (ulong)codeSize;
  134. int codePagesCount = (int)(codeSize / KPageTableBase.PageSize);
  135. int personalMmHeapPagesCount = metaData.PersonalMmHeapSize / KPageTableBase.PageSize;
  136. ProcessCreationInfo creationInfo = new ProcessCreationInfo(
  137. metaData.TitleName,
  138. metaData.Version,
  139. metaData.Aci0.TitleId,
  140. codeStart,
  141. codePagesCount,
  142. (ProcessCreationFlags)metaData.ProcessFlags | ProcessCreationFlags.IsApplication,
  143. 0,
  144. personalMmHeapPagesCount);
  145. KernelResult result;
  146. KResourceLimit resourceLimit = new KResourceLimit(context);
  147. long applicationRgSize = (long)context.MemoryManager.MemoryRegions[(int)MemoryRegion.Application].Size;
  148. result = resourceLimit.SetLimitValue(LimitableResource.Memory, applicationRgSize);
  149. result |= resourceLimit.SetLimitValue(LimitableResource.Thread, 608);
  150. result |= resourceLimit.SetLimitValue(LimitableResource.Event, 700);
  151. result |= resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128);
  152. result |= resourceLimit.SetLimitValue(LimitableResource.Session, 894);
  153. if (result != KernelResult.Success)
  154. {
  155. Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values.");
  156. tamperInfo = null;
  157. return false;
  158. }
  159. KProcess process = new KProcess(context);
  160. MemoryRegion memoryRegion = (MemoryRegion)((metaData.Acid.Flags >> 2) & 0xf);
  161. if (memoryRegion > MemoryRegion.NvServices)
  162. {
  163. Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags.");
  164. tamperInfo = null;
  165. return false;
  166. }
  167. var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
  168. result = process.Initialize(
  169. creationInfo,
  170. metaData.Aci0.KernelAccessControl.Capabilities,
  171. resourceLimit,
  172. memoryRegion,
  173. processContextFactory);
  174. if (result != KernelResult.Success)
  175. {
  176. Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
  177. tamperInfo = null;
  178. return false;
  179. }
  180. for (int index = 0; index < executables.Length; index++)
  181. {
  182. Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBase[index]:x16}...");
  183. result = LoadIntoMemory(process, executables[index], nsoBase[index]);
  184. if (result != KernelResult.Success)
  185. {
  186. Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
  187. tamperInfo = null;
  188. return false;
  189. }
  190. }
  191. process.DefaultCpuCore = metaData.DefaultCpuId;
  192. result = process.Start(metaData.MainThreadPriority, (ulong)metaData.MainThreadStackSize);
  193. if (result != KernelResult.Success)
  194. {
  195. Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\".");
  196. tamperInfo = null;
  197. return false;
  198. }
  199. context.Processes.TryAdd(process.Pid, process);
  200. // Keep the build ids because the tamper machine uses them to know which process to associate a
  201. // tamper to and also keep the starting address of each executable inside a process because some
  202. // memory modifications are relative to this address.
  203. tamperInfo = new ProcessTamperInfo(process, buildIds, nsoBase, process.MemoryManager.HeapRegionStart);
  204. return true;
  205. }
  206. private static KernelResult LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)
  207. {
  208. ulong textStart = baseAddress + (ulong)image.TextOffset;
  209. ulong roStart = baseAddress + (ulong)image.RoOffset;
  210. ulong dataStart = baseAddress + (ulong)image.DataOffset;
  211. ulong bssStart = baseAddress + (ulong)image.BssOffset;
  212. ulong end = dataStart + (ulong)image.Data.Length;
  213. if (image.BssSize != 0)
  214. {
  215. end = bssStart + (ulong)image.BssSize;
  216. }
  217. process.CpuMemory.Write(textStart, image.Text);
  218. process.CpuMemory.Write(roStart, image.Ro);
  219. process.CpuMemory.Write(dataStart, image.Data);
  220. process.CpuMemory.Fill(bssStart, image.BssSize, 0);
  221. KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
  222. {
  223. if (size == 0)
  224. {
  225. return KernelResult.Success;
  226. }
  227. size = BitUtils.AlignUp(size, KPageTableBase.PageSize);
  228. return process.MemoryManager.SetProcessMemoryPermission(address, size, permission);
  229. }
  230. KernelResult result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, KMemoryPermission.ReadAndExecute);
  231. if (result != KernelResult.Success)
  232. {
  233. return result;
  234. }
  235. result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, KMemoryPermission.Read);
  236. if (result != KernelResult.Success)
  237. {
  238. return result;
  239. }
  240. return SetProcessMemoryPermission(dataStart, end - dataStart, KMemoryPermission.ReadAndWrite);
  241. }
  242. }
  243. }