ProgramLoader.cs 13 KB

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