summaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
authorGravatar Liam2023-11-17 23:44:53 +0200
committerGravatar t8952023-11-25 00:46:47 -0500
commit9f91ba1f7357c61dd2c7c3b437ea203d467fd400 (patch)
tree3cfe55acf5d2f8a0d75373934b4c22a4e9bb2a09 /src/core/loader
parentdevice_memory: Enable direct mapped addresses for nce (diff)
downloadyuzu-9f91ba1f7357c61dd2c7c3b437ea203d467fd400.tar.gz
yuzu-9f91ba1f7357c61dd2c7c3b437ea203d467fd400.tar.xz
yuzu-9f91ba1f7357c61dd2c7c3b437ea203d467fd400.zip
arm: Implement native code execution backend
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp61
-rw-r--r--src/core/loader/nro.cpp62
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp56
-rw-r--r--src/core/loader/nso.h7
5 files changed, 162 insertions, 26 deletions
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 48c0edaea..e7fc8f438 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -3,6 +3,7 @@
3 3
4#include <cstring> 4#include <cstring>
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/settings.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/file_sys/content_archive.h" 8#include "core/file_sys/content_archive.h"
8#include "core/file_sys/control_metadata.h" 9#include "core/file_sys/control_metadata.h"
@@ -14,6 +15,10 @@
14#include "core/loader/deconstructed_rom_directory.h" 15#include "core/loader/deconstructed_rom_directory.h"
15#include "core/loader/nso.h" 16#include "core/loader/nso.h"
16 17
18#ifdef ARCHITECTURE_arm64
19#include "core/arm/nce/patch.h"
20#endif
21
17namespace Loader { 22namespace Loader {
18 23
19AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, 24AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
@@ -124,21 +129,41 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
124 } 129 }
125 metadata.Print(); 130 metadata.Print();
126 131
127 const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", 132 // Enable NCE only for 64-bit programs.
128 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", 133 Settings::SetNceEnabled(metadata.Is64BitProgram());
129 "subsdk8", "subsdk9", "sdk"}; 134
135 const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
136 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
137 "subsdk8", "subsdk9", "sdk"};
130 138
131 // Use the NSO module loader to figure out the code layout
132 std::size_t code_size{}; 139 std::size_t code_size{};
133 for (const auto& module : static_modules) { 140
141 // Define an nce patch context for each potential module.
142#ifdef ARCHITECTURE_arm64
143 std::array<Core::NCE::Patcher, 13> module_patchers;
144#endif
145
146 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
147#ifdef ARCHITECTURE_arm64
148 if (Settings::IsNceEnabled()) {
149 return &module_patchers[i];
150 }
151#endif
152 return nullptr;
153 };
154
155 // Use the NSO module loader to figure out the code layout
156 for (size_t i = 0; i < static_modules.size(); i++) {
157 const auto& module = static_modules[i];
134 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 158 const FileSys::VirtualFile module_file{dir->GetFile(module)};
135 if (!module_file) { 159 if (!module_file) {
136 continue; 160 continue;
137 } 161 }
138 162
139 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 163 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
140 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( 164 const auto tentative_next_load_addr =
141 process, system, *module_file, code_size, should_pass_arguments, false); 165 AppLoader_NSO::LoadModule(process, system, *module_file, code_size,
166 should_pass_arguments, false, {}, GetPatcher(i));
142 if (!tentative_next_load_addr) { 167 if (!tentative_next_load_addr) {
143 return {ResultStatus::ErrorLoadingNSO, {}}; 168 return {ResultStatus::ErrorLoadingNSO, {}};
144 } 169 }
@@ -146,8 +171,18 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
146 code_size = *tentative_next_load_addr; 171 code_size = *tentative_next_load_addr;
147 } 172 }
148 173
174 // Enable direct memory mapping in case of NCE.
175 const u64 fastmem_base = [&]() -> size_t {
176 if (Settings::IsNceEnabled()) {
177 auto& buffer = system.DeviceMemory().buffer;
178 buffer.EnableDirectMappedAddress();
179 return reinterpret_cast<u64>(buffer.VirtualBasePointer());
180 }
181 return 0;
182 }();
183
149 // Setup the process code layout 184 // Setup the process code layout
150 if (process.LoadFromMetadata(metadata, code_size, 0, is_hbl).IsError()) { 185 if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) {
151 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; 186 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
152 } 187 }
153 188
@@ -157,7 +192,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
157 VAddr next_load_addr{base_address}; 192 VAddr next_load_addr{base_address};
158 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), 193 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
159 system.GetContentProvider()}; 194 system.GetContentProvider()};
160 for (const auto& module : static_modules) { 195 for (size_t i = 0; i < static_modules.size(); i++) {
196 const auto& module = static_modules[i];
161 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 197 const FileSys::VirtualFile module_file{dir->GetFile(module)};
162 if (!module_file) { 198 if (!module_file) {
163 continue; 199 continue;
@@ -165,15 +201,16 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
165 201
166 const VAddr load_addr{next_load_addr}; 202 const VAddr load_addr{next_load_addr};
167 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 203 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
168 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( 204 const auto tentative_next_load_addr =
169 process, system, *module_file, load_addr, should_pass_arguments, true, pm); 205 AppLoader_NSO::LoadModule(process, system, *module_file, load_addr,
206 should_pass_arguments, true, pm, GetPatcher(i));
170 if (!tentative_next_load_addr) { 207 if (!tentative_next_load_addr) {
171 return {ResultStatus::ErrorLoadingNSO, {}}; 208 return {ResultStatus::ErrorLoadingNSO, {}};
172 } 209 }
173 210
174 next_load_addr = *tentative_next_load_addr; 211 next_load_addr = *tentative_next_load_addr;
175 modules.insert_or_assign(load_addr, module); 212 modules.insert_or_assign(load_addr, module);
176 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 213 LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr);
177 } 214 }
178 215
179 // Find the RomFS by searching for a ".romfs" file in this directory 216 // Find the RomFS by searching for a ".romfs" file in this directory
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index dfed296a5..49cf90317 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -22,6 +22,10 @@
22#include "core/loader/nso.h" 22#include "core/loader/nso.h"
23#include "core/memory.h" 23#include "core/memory.h"
24 24
25#ifdef ARCHITECTURE_arm64
26#include "core/arm/nce/patch.h"
27#endif
28
25namespace Loader { 29namespace Loader {
26 30
27struct NroSegmentHeader { 31struct NroSegmentHeader {
@@ -139,7 +143,8 @@ static constexpr u32 PageAlignSize(u32 size) {
139 return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); 143 return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
140} 144}
141 145
142static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { 146static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
147 const std::vector<u8>& data) {
143 if (data.size() < sizeof(NroHeader)) { 148 if (data.size() < sizeof(NroHeader)) {
144 return {}; 149 return {};
145 } 150 }
@@ -195,14 +200,60 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
195 codeset.DataSegment().size += bss_size; 200 codeset.DataSegment().size += bss_size;
196 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 201 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
197 202
203#ifdef ARCHITECTURE_arm64
204 const auto& code = codeset.CodeSegment();
205
206 // NROs are always 64-bit programs.
207 Settings::SetNceEnabled(true);
208
209 // Create NCE patcher
210 Core::NCE::Patcher patch{};
211 size_t image_size = program_image.size();
212
213 if (Settings::IsNceEnabled()) {
214 // Patch SVCs and MRS calls in the guest code
215 patch.PatchText(program_image, code);
216
217 // We only support PostData patching for NROs.
218 ASSERT(patch.Mode() == Core::NCE::PatchMode::PostData);
219
220 // Update patch section.
221 auto& patch_segment = codeset.PatchSegment();
222 patch_segment.addr = image_size;
223 patch_segment.size = static_cast<u32>(patch.SectionSize());
224
225 // Add patch section size to the module size.
226 image_size += patch_segment.size;
227 }
228#endif
229
230 // Enable direct memory mapping in case of NCE.
231 const u64 fastmem_base = [&]() -> size_t {
232 if (Settings::IsNceEnabled()) {
233 auto& buffer = system.DeviceMemory().buffer;
234 buffer.EnableDirectMappedAddress();
235 return reinterpret_cast<u64>(buffer.VirtualBasePointer());
236 }
237 return 0;
238 }();
239
198 // Setup the process code layout 240 // Setup the process code layout
199 if (process 241 if (process
200 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0, 242 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base,
201 false) 243 false)
202 .IsError()) { 244 .IsError()) {
203 return false; 245 return false;
204 } 246 }
205 247
248 // Relocate code patch and copy to the program_image if running under NCE.
249 // This needs to be after LoadFromMetadata so we can use the process entry point.
250#ifdef ARCHITECTURE_arm64
251 if (Settings::IsNceEnabled()) {
252 patch.RelocateAndCopy(process.GetEntryPoint(), code, program_image,
253 &process.GetPostHandlers());
254 }
255#endif
256
206 // Load codeset for current process 257 // Load codeset for current process
207 codeset.memory = std::move(program_image); 258 codeset.memory = std::move(program_image);
208 process.LoadModule(std::move(codeset), process.GetEntryPoint()); 259 process.LoadModule(std::move(codeset), process.GetEntryPoint());
@@ -210,8 +261,9 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
210 return true; 261 return true;
211} 262}
212 263
213bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) { 264bool AppLoader_NRO::LoadNro(Core::System& system, Kernel::KProcess& process,
214 return LoadNroImpl(process, nro_file.ReadAllBytes()); 265 const FileSys::VfsFile& nro_file) {
266 return LoadNroImpl(system, process, nro_file.ReadAllBytes());
215} 267}
216 268
217AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) { 269AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) {
@@ -219,7 +271,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S
219 return {ResultStatus::ErrorAlreadyLoaded, {}}; 271 return {ResultStatus::ErrorAlreadyLoaded, {}};
220 } 272 }
221 273
222 if (!LoadNro(process, *file)) { 274 if (!LoadNro(system, process, *file)) {
223 return {ResultStatus::ErrorLoadingNRO, {}}; 275 return {ResultStatus::ErrorLoadingNRO, {}};
224 } 276 }
225 277
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 8de6eebc6..d2928cba0 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -54,7 +54,7 @@ public:
54 bool IsRomFSUpdatable() const override; 54 bool IsRomFSUpdatable() const override;
55 55
56private: 56private:
57 bool LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file); 57 bool LoadNro(Core::System& system, Kernel::KProcess& process, const FileSys::VfsFile& nro_file);
58 58
59 std::vector<u8> icon_data; 59 std::vector<u8> icon_data;
60 std::unique_ptr<FileSys::NACP> nacp; 60 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 1350da8dc..34b10ef2e 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -20,6 +20,10 @@
20#include "core/loader/nso.h" 20#include "core/loader/nso.h"
21#include "core/memory.h" 21#include "core/memory.h"
22 22
23#ifdef ARCHITECTURE_arm64
24#include "core/arm/nce/patch.h"
25#endif
26
23namespace Loader { 27namespace Loader {
24namespace { 28namespace {
25struct MODHeader { 29struct MODHeader {
@@ -72,7 +76,8 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) {
72std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, 76std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system,
73 const FileSys::VfsFile& nso_file, VAddr load_base, 77 const FileSys::VfsFile& nso_file, VAddr load_base,
74 bool should_pass_arguments, bool load_into_process, 78 bool should_pass_arguments, bool load_into_process,
75 std::optional<FileSys::PatchManager> pm) { 79 std::optional<FileSys::PatchManager> pm,
80 Core::NCE::Patcher* patch) {
76 if (nso_file.GetSize() < sizeof(NSOHeader)) { 81 if (nso_file.GetSize() < sizeof(NSOHeader)) {
77 return std::nullopt; 82 return std::nullopt;
78 } 83 }
@@ -86,6 +91,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
86 return std::nullopt; 91 return std::nullopt;
87 } 92 }
88 93
94 // Allocate some space at the beginning if we are patching in PreText mode.
95 const size_t module_start = [&]() -> size_t {
96#ifdef ARCHITECTURE_arm64
97 if (patch && patch->Mode() == Core::NCE::PatchMode::PreText) {
98 return patch->SectionSize();
99 }
100#endif
101 return 0;
102 }();
103
89 // Build program image 104 // Build program image
90 Kernel::CodeSet codeset; 105 Kernel::CodeSet codeset;
91 Kernel::PhysicalMemory program_image; 106 Kernel::PhysicalMemory program_image;
@@ -95,11 +110,12 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
95 if (nso_header.IsSegmentCompressed(i)) { 110 if (nso_header.IsSegmentCompressed(i)) {
96 data = DecompressSegment(data, nso_header.segments[i]); 111 data = DecompressSegment(data, nso_header.segments[i]);
97 } 112 }
98 program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size())); 113 program_image.resize(module_start + nso_header.segments[i].location +
99 std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(), 114 static_cast<u32>(data.size()));
100 data.size()); 115 std::memcpy(program_image.data() + module_start + nso_header.segments[i].location,
101 codeset.segments[i].addr = nso_header.segments[i].location; 116 data.data(), data.size());
102 codeset.segments[i].offset = nso_header.segments[i].location; 117 codeset.segments[i].addr = module_start + nso_header.segments[i].location;
118 codeset.segments[i].offset = module_start + nso_header.segments[i].location;
103 codeset.segments[i].size = nso_header.segments[i].size; 119 codeset.segments[i].size = nso_header.segments[i].size;
104 } 120 }
105 121
@@ -118,7 +134,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
118 } 134 }
119 135
120 codeset.DataSegment().size += nso_header.segments[2].bss_size; 136 codeset.DataSegment().size += nso_header.segments[2].bss_size;
121 const u32 image_size{ 137 u32 image_size{
122 PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)}; 138 PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)};
123 program_image.resize(image_size); 139 program_image.resize(image_size);
124 140
@@ -139,6 +155,32 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
139 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data()); 155 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data());
140 } 156 }
141 157
158#ifdef ARCHITECTURE_arm64
159 // If we are computing the process code layout and using nce backend, patch.
160 const auto& code = codeset.CodeSegment();
161 if (patch && patch->Mode() == Core::NCE::PatchMode::None) {
162 // Patch SVCs and MRS calls in the guest code
163 patch->PatchText(program_image, code);
164
165 // Add patch section size to the module size.
166 image_size += patch->SectionSize();
167 } else if (patch) {
168 // Relocate code patch and copy to the program_image.
169 patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers());
170
171 // Update patch section.
172 auto& patch_segment = codeset.PatchSegment();
173 patch_segment.addr = patch->Mode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
174 patch_segment.size = static_cast<u32>(patch->SectionSize());
175
176 // Add patch section size to the module size. In PreText mode image_size
177 // already contains the patch segment as part of module_start.
178 if (patch->Mode() == Core::NCE::PatchMode::PostData) {
179 image_size += patch_segment.size;
180 }
181 }
182#endif
183
142 // If we aren't actually loading (i.e. just computing the process code layout), we are done 184 // If we aren't actually loading (i.e. just computing the process code layout), we are done
143 if (!load_into_process) { 185 if (!load_into_process) {
144 return load_base + image_size; 186 return load_base + image_size;
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 0b53b4ecd..29b86ed4c 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -15,6 +15,10 @@ namespace Core {
15class System; 15class System;
16} 16}
17 17
18namespace Core::NCE {
19class Patcher;
20}
21
18namespace Kernel { 22namespace Kernel {
19class KProcess; 23class KProcess;
20} 24}
@@ -88,7 +92,8 @@ public:
88 static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system, 92 static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
89 const FileSys::VfsFile& nso_file, VAddr load_base, 93 const FileSys::VfsFile& nso_file, VAddr load_base,
90 bool should_pass_arguments, bool load_into_process, 94 bool should_pass_arguments, bool load_into_process,
91 std::optional<FileSys::PatchManager> pm = {}); 95 std::optional<FileSys::PatchManager> pm = {},
96 Core::NCE::Patcher* patch = nullptr);
92 97
93 LoadResult Load(Kernel::KProcess& process, Core::System& system) override; 98 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
94 99