patch.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/arm64/native_clock.h"
  4. #include "common/bit_cast.h"
  5. #include "common/literals.h"
  6. #include "core/arm/nce/arm_nce.h"
  7. #include "core/arm/nce/guest_context.h"
  8. #include "core/arm/nce/instructions.h"
  9. #include "core/arm/nce/patch.h"
  10. #include "core/core.h"
  11. #include "core/core_timing.h"
  12. #include "core/hle/kernel/svc.h"
  13. namespace Core::NCE {
  14. using namespace Common::Literals;
  15. using namespace oaknut::util;
  16. using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
  17. constexpr size_t MaxRelativeBranch = 128_MiB;
  18. Patcher::Patcher() : c(m_patch_instructions) {}
  19. Patcher::~Patcher() = default;
  20. void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
  21. const Kernel::CodeSet::Segment& code) {
  22. // Write save context helper function.
  23. c.l(m_save_context);
  24. WriteSaveContext();
  25. // Write load context helper function.
  26. c.l(m_load_context);
  27. WriteLoadContext();
  28. // Retrieve text segment data.
  29. const auto text = std::span{program_image}.subspan(code.offset, code.size);
  30. const auto text_words =
  31. std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)};
  32. // Loop through instructions, patching as needed.
  33. for (u32 i = 0; i < static_cast<u32>(text_words.size()); i++) {
  34. const u32 inst = text_words[i];
  35. const auto AddRelocations = [&] {
  36. const uintptr_t this_offset = i * sizeof(u32);
  37. const uintptr_t next_offset = this_offset + sizeof(u32);
  38. // Relocate from here to patch.
  39. this->BranchToPatch(this_offset);
  40. // Relocate from patch to next instruction.
  41. return next_offset;
  42. };
  43. // SVC
  44. if (auto svc = SVC{inst}; svc.Verify()) {
  45. WriteSvcTrampoline(AddRelocations(), svc.GetValue());
  46. continue;
  47. }
  48. // MRS Xn, TPIDR_EL0
  49. // MRS Xn, TPIDRRO_EL0
  50. if (auto mrs = MRS{inst};
  51. mrs.Verify() && (mrs.GetSystemReg() == TpidrroEl0 || mrs.GetSystemReg() == TpidrEl0)) {
  52. const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0
  53. : oaknut::SystemReg::TPIDR_EL0;
  54. const auto dest_reg = oaknut::XReg{static_cast<int>(mrs.GetRt())};
  55. WriteMrsHandler(AddRelocations(), dest_reg, src_reg);
  56. continue;
  57. }
  58. // MRS Xn, CNTPCT_EL0
  59. if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntpctEl0) {
  60. WriteCntpctHandler(AddRelocations(), oaknut::XReg{static_cast<int>(mrs.GetRt())});
  61. continue;
  62. }
  63. // MRS Xn, CNTFRQ_EL0
  64. if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
  65. UNREACHABLE();
  66. }
  67. // MSR TPIDR_EL0, Xn
  68. if (auto msr = MSR{inst}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) {
  69. WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())});
  70. continue;
  71. }
  72. }
  73. // Determine patching mode for the final relocation step
  74. const size_t image_size = program_image.size();
  75. this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
  76. }
  77. void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
  78. const Kernel::CodeSet::Segment& code,
  79. Kernel::PhysicalMemory& program_image,
  80. EntryTrampolines* out_trampolines) {
  81. const size_t patch_size = GetSectionSize();
  82. const size_t image_size = program_image.size();
  83. // Retrieve text segment data.
  84. const auto text = std::span{program_image}.subspan(code.offset, code.size);
  85. const auto text_words =
  86. std::span<u32>{reinterpret_cast<u32*>(text.data()), text.size() / sizeof(u32)};
  87. const auto ApplyBranchToPatchRelocation = [&](u32* target, const Relocation& rel) {
  88. oaknut::CodeGenerator rc{target};
  89. if (mode == PatchMode::PreText) {
  90. rc.B(rel.patch_offset - patch_size - rel.module_offset);
  91. } else {
  92. rc.B(image_size - rel.module_offset + rel.patch_offset);
  93. }
  94. };
  95. const auto ApplyBranchToModuleRelocation = [&](u32* target, const Relocation& rel) {
  96. oaknut::CodeGenerator rc{target};
  97. if (mode == PatchMode::PreText) {
  98. rc.B(patch_size - rel.patch_offset + rel.module_offset);
  99. } else {
  100. rc.B(rel.module_offset - image_size - rel.patch_offset);
  101. }
  102. };
  103. const auto RebasePatch = [&](ptrdiff_t patch_offset) {
  104. if (mode == PatchMode::PreText) {
  105. return GetInteger(load_base) + patch_offset;
  106. } else {
  107. return GetInteger(load_base) + image_size + patch_offset;
  108. }
  109. };
  110. const auto RebasePc = [&](uintptr_t module_offset) {
  111. if (mode == PatchMode::PreText) {
  112. return GetInteger(load_base) + patch_size + module_offset;
  113. } else {
  114. return GetInteger(load_base) + module_offset;
  115. }
  116. };
  117. // We are now ready to relocate!
  118. for (const Relocation& rel : m_branch_to_patch_relocations) {
  119. ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
  120. }
  121. for (const Relocation& rel : m_branch_to_module_relocations) {
  122. ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
  123. rel);
  124. }
  125. // Rewrite PC constants and record post trampolines
  126. for (const Relocation& rel : m_write_module_pc_relocations) {
  127. oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
  128. rc.dx(RebasePc(rel.module_offset));
  129. }
  130. for (const Trampoline& rel : m_trampolines) {
  131. out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
  132. }
  133. // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
  134. // Convert to ordered to preserve this assumption
  135. for (u32 i = 0; i < static_cast<u32>(text_words.size()); i++) {
  136. const u32 inst = text_words[i];
  137. if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
  138. text_words[i] = exclusive.AsOrdered();
  139. }
  140. }
  141. // Copy to program image
  142. if (this->mode == PatchMode::PreText) {
  143. std::memcpy(program_image.data(), m_patch_instructions.data(),
  144. m_patch_instructions.size() * sizeof(u32));
  145. } else {
  146. program_image.resize(image_size + patch_size);
  147. std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
  148. m_patch_instructions.size() * sizeof(u32));
  149. }
  150. }
  151. size_t Patcher::GetSectionSize() const noexcept {
  152. return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
  153. }
  154. void Patcher::WriteLoadContext() {
  155. // This function was called, which modifies X30, so use that as a scratch register.
  156. // SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes
  157. // of stack.
  158. c.STR(X30, SP, 8);
  159. c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
  160. c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
  161. // Load system registers.
  162. c.LDR(W0, X30, offsetof(GuestContext, fpsr));
  163. c.MSR(oaknut::SystemReg::FPSR, X0);
  164. c.LDR(W0, X30, offsetof(GuestContext, fpcr));
  165. c.MSR(oaknut::SystemReg::FPCR, X0);
  166. c.LDR(W0, X30, offsetof(GuestContext, nzcv));
  167. c.MSR(oaknut::SystemReg::NZCV, X0);
  168. // Load all vector registers.
  169. static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
  170. for (int i = 0; i <= 30; i += 2) {
  171. c.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
  172. }
  173. // Load all general-purpose registers except X30.
  174. for (int i = 0; i <= 28; i += 2) {
  175. c.LDP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
  176. }
  177. // Reload our return X30 from the stack and return.
  178. // The patch code will reload the guest X30 for us.
  179. c.LDR(X30, SP, 8);
  180. c.RET();
  181. }
  182. void Patcher::WriteSaveContext() {
  183. // This function was called, which modifies X30, so use that as a scratch register.
  184. // SP contains the guest X30, so save our X30 to SP + 8, since we have allocated 16 bytes of
  185. // stack.
  186. c.STR(X30, SP, 8);
  187. c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
  188. c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
  189. // Store all general-purpose registers except X30.
  190. for (int i = 0; i <= 28; i += 2) {
  191. c.STP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
  192. }
  193. // Store all vector registers.
  194. static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
  195. for (int i = 0; i <= 30; i += 2) {
  196. c.STP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
  197. }
  198. // Store guest system registers, X30 and SP, using X0 as a scratch register.
  199. c.STR(X0, SP, PRE_INDEXED, -16);
  200. c.LDR(X0, SP, 16);
  201. c.STR(X0, X30, 8 * 30);
  202. c.ADD(X0, SP, 32);
  203. c.STR(X0, X30, offsetof(GuestContext, sp));
  204. c.MRS(X0, oaknut::SystemReg::FPSR);
  205. c.STR(W0, X30, offsetof(GuestContext, fpsr));
  206. c.MRS(X0, oaknut::SystemReg::FPCR);
  207. c.STR(W0, X30, offsetof(GuestContext, fpcr));
  208. c.MRS(X0, oaknut::SystemReg::NZCV);
  209. c.STR(W0, X30, offsetof(GuestContext, nzcv));
  210. c.LDR(X0, SP, POST_INDEXED, 16);
  211. // Reload our return X30 from the stack, and return.
  212. c.LDR(X30, SP, 8);
  213. c.RET();
  214. }
  215. void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
  216. // We are about to start saving state, so we need to lock the context.
  217. this->LockContext();
  218. // Store guest X30 to the stack. Then, save the context and restore the stack.
  219. // This will save all registers except PC, but we know PC at patch time.
  220. c.STR(X30, SP, PRE_INDEXED, -16);
  221. c.BL(m_save_context);
  222. c.LDR(X30, SP, POST_INDEXED, 16);
  223. // Now that we've saved all registers, we can use any registers as scratch.
  224. // Store PC + 4 to arm interface, since we know the instruction offset from the entry point.
  225. oaknut::Label pc_after_svc;
  226. c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
  227. c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
  228. c.LDR(X2, pc_after_svc);
  229. c.STR(X2, X1, offsetof(GuestContext, pc));
  230. // Store SVC number to execute when we return
  231. c.MOV(X2, svc_id);
  232. c.STR(W2, X1, offsetof(GuestContext, svc_swi));
  233. // We are calling a SVC. Clear esr_el1 and return it.
  234. static_assert(std::is_same_v<std::underlying_type_t<HaltReason>, u64>);
  235. oaknut::Label retry;
  236. c.ADD(X2, X1, offsetof(GuestContext, esr_el1));
  237. c.l(retry);
  238. c.LDAXR(X0, X2);
  239. c.STLXR(W3, XZR, X2);
  240. c.CBNZ(W3, retry);
  241. // Add "calling SVC" flag. Since this is X0, this is now our return value.
  242. c.ORR(X0, X0, static_cast<u64>(HaltReason::SupervisorCall));
  243. // Offset the GuestContext pointer to the HostContext member.
  244. // STP has limited range of [-512, 504] which we can't reach otherwise
  245. // NB: Due to this all offsets below are from the start of HostContext.
  246. c.ADD(X1, X1, offsetof(GuestContext, host_ctx));
  247. // Reload host TPIDR_EL0 and SP.
  248. static_assert(offsetof(HostContext, host_sp) + 8 == offsetof(HostContext, host_tpidr_el0));
  249. c.LDP(X2, X3, X1, offsetof(HostContext, host_sp));
  250. c.MOV(SP, X2);
  251. c.MSR(oaknut::SystemReg::TPIDR_EL0, X3);
  252. // Load callee-saved host registers and return to host.
  253. static constexpr size_t HOST_REGS_OFF = offsetof(HostContext, host_saved_regs);
  254. static constexpr size_t HOST_VREGS_OFF = offsetof(HostContext, host_saved_vregs);
  255. c.LDP(X19, X20, X1, HOST_REGS_OFF);
  256. c.LDP(X21, X22, X1, HOST_REGS_OFF + 2 * sizeof(u64));
  257. c.LDP(X23, X24, X1, HOST_REGS_OFF + 4 * sizeof(u64));
  258. c.LDP(X25, X26, X1, HOST_REGS_OFF + 6 * sizeof(u64));
  259. c.LDP(X27, X28, X1, HOST_REGS_OFF + 8 * sizeof(u64));
  260. c.LDP(X29, X30, X1, HOST_REGS_OFF + 10 * sizeof(u64));
  261. c.LDP(Q8, Q9, X1, HOST_VREGS_OFF);
  262. c.LDP(Q10, Q11, X1, HOST_VREGS_OFF + 2 * sizeof(u128));
  263. c.LDP(Q12, Q13, X1, HOST_VREGS_OFF + 4 * sizeof(u128));
  264. c.LDP(Q14, Q15, X1, HOST_VREGS_OFF + 6 * sizeof(u128));
  265. c.RET();
  266. // Write the post-SVC trampoline address, which will jump back to the guest after restoring its
  267. // state.
  268. m_trampolines.push_back({c.offset(), module_dest});
  269. // Host called this location. Save the return address so we can
  270. // unwind the stack properly when jumping back.
  271. c.MRS(X2, oaknut::SystemReg::TPIDR_EL0);
  272. c.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context));
  273. c.ADD(X0, X2, offsetof(GuestContext, host_ctx));
  274. c.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64));
  275. // Reload all guest registers except X30 and PC.
  276. // The function also expects 16 bytes of stack already allocated.
  277. c.STR(X30, SP, PRE_INDEXED, -16);
  278. c.BL(m_load_context);
  279. c.LDR(X30, SP, POST_INDEXED, 16);
  280. // Use X1 as a scratch register to restore X30.
  281. c.STR(X1, SP, PRE_INDEXED, -16);
  282. c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
  283. c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
  284. c.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30);
  285. c.LDR(X1, SP, POST_INDEXED, 16);
  286. // Unlock the context.
  287. this->UnlockContext();
  288. // Jump back to the instruction after the emulated SVC.
  289. this->BranchToModule(module_dest);
  290. // Store PC after call.
  291. c.l(pc_after_svc);
  292. this->WriteModulePc(module_dest);
  293. }
  294. void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
  295. oaknut::SystemReg src_reg) {
  296. // Retrieve emulated TLS register from GuestContext.
  297. c.MRS(dest_reg, oaknut::SystemReg::TPIDR_EL0);
  298. if (src_reg == oaknut::SystemReg::TPIDRRO_EL0) {
  299. c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0));
  300. } else {
  301. c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0));
  302. }
  303. // Jump back to the instruction after the emulated MRS.
  304. this->BranchToModule(module_dest);
  305. }
  306. void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) {
  307. const auto scratch_reg = src_reg.index() == 0 ? X1 : X0;
  308. c.STR(scratch_reg, SP, PRE_INDEXED, -16);
  309. // Save guest value to NativeExecutionParameters::tpidr_el0.
  310. c.MRS(scratch_reg, oaknut::SystemReg::TPIDR_EL0);
  311. c.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0));
  312. // Restore scratch register.
  313. c.LDR(scratch_reg, SP, POST_INDEXED, 16);
  314. // Jump back to the instruction after the emulated MSR.
  315. this->BranchToModule(module_dest);
  316. }
  317. void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) {
  318. static Common::Arm64::NativeClock clock{};
  319. const auto factor = clock.GetGuestCNTFRQFactor();
  320. const auto raw_factor = Common::BitCast<std::array<u64, 2>>(factor);
  321. const auto use_x2_x3 = dest_reg.index() == 0 || dest_reg.index() == 1;
  322. oaknut::XReg scratch0 = use_x2_x3 ? X2 : X0;
  323. oaknut::XReg scratch1 = use_x2_x3 ? X3 : X1;
  324. oaknut::Label factorlo;
  325. oaknut::Label factorhi;
  326. // Save scratches.
  327. c.STP(scratch0, scratch1, SP, PRE_INDEXED, -16);
  328. // Load counter value.
  329. c.MRS(dest_reg, oaknut::SystemReg::CNTVCT_EL0);
  330. // Load scaling factor.
  331. c.LDR(scratch0, factorlo);
  332. c.LDR(scratch1, factorhi);
  333. // Multiply low bits and get result.
  334. c.UMULH(scratch0, dest_reg, scratch0);
  335. // Multiply high bits and add low bit result.
  336. c.MADD(dest_reg, dest_reg, scratch1, scratch0);
  337. // Reload scratches.
  338. c.LDP(scratch0, scratch1, SP, POST_INDEXED, 16);
  339. // Jump back to the instruction after the emulated MRS.
  340. this->BranchToModule(module_dest);
  341. // Scaling factor constant values.
  342. c.l(factorlo);
  343. c.dx(raw_factor[0]);
  344. c.l(factorhi);
  345. c.dx(raw_factor[1]);
  346. }
  347. void Patcher::LockContext() {
  348. oaknut::Label retry;
  349. // Save scratches.
  350. c.STP(X0, X1, SP, PRE_INDEXED, -16);
  351. // Reload lock pointer.
  352. c.l(retry);
  353. c.CLREX();
  354. c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
  355. c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
  356. static_assert(SpinLockLocked == 0);
  357. // Load-linked with acquire ordering.
  358. c.LDAXR(W1, X0);
  359. // If the value was SpinLockLocked, clear monitor and retry.
  360. c.CBZ(W1, retry);
  361. // Store-conditional SpinLockLocked with relaxed ordering.
  362. c.STXR(W1, WZR, X0);
  363. // If we failed to store, retry.
  364. c.CBNZ(W1, retry);
  365. // We succeeded! Reload scratches.
  366. c.LDP(X0, X1, SP, POST_INDEXED, 16);
  367. }
  368. void Patcher::UnlockContext() {
  369. // Save scratches.
  370. c.STP(X0, X1, SP, PRE_INDEXED, -16);
  371. // Load lock pointer.
  372. c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
  373. c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
  374. // Load SpinLockUnlocked.
  375. c.MOV(W1, SpinLockUnlocked);
  376. // Store value with release ordering.
  377. c.STLR(W1, X0);
  378. // Load scratches.
  379. c.LDP(X0, X1, SP, POST_INDEXED, 16);
  380. }
  381. } // namespace Core::NCE