ProgramLoader.cs 13 KB

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