summaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
authorGravatar liamwhite2023-11-30 09:20:55 -0500
committerGravatar GitHub2023-11-30 09:20:55 -0500
commit57a391e71db13ade7a3d96f59d53781eff18d2ac (patch)
tree0b4223de40a2d77598ac9095b1374353c2e9da7c /src/core/loader
parentMerge pull request #12223 from liamwhite/fruit-company (diff)
parentcore: Rename patcher file (diff)
downloadyuzu-57a391e71db13ade7a3d96f59d53781eff18d2ac.tar.gz
yuzu-57a391e71db13ade7a3d96f59d53781eff18d2ac.tar.xz
yuzu-57a391e71db13ade7a3d96f59d53781eff18d2ac.zip
Merge pull request #12074 from GPUCode/yuwu-on-the-metal
Implement Native Code Execution (NCE)
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp63
-rw-r--r--src/core/loader/kip.cpp3
-rw-r--r--src/core/loader/nro.cpp63
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp67
-rw-r--r--src/core/loader/nso.h7
6 files changed, 174 insertions, 31 deletions
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 5c36b71e5..60ee78e89 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 HAS_NCE
19#include "core/arm/nce/patcher.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,43 @@ 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 programs with 39-bit address space.
128 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", 133 const bool is_39bit =
129 "subsdk8", "subsdk9", "sdk"}; 134 metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit;
135 Settings::SetNceEnabled(is_39bit);
136
137 const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
138 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
139 "subsdk8", "subsdk9", "sdk"};
130 140
131 // Use the NSO module loader to figure out the code layout
132 std::size_t code_size{}; 141 std::size_t code_size{};
133 for (const auto& module : static_modules) { 142
143 // Define an nce patch context for each potential module.
144#ifdef HAS_NCE
145 std::array<Core::NCE::Patcher, 13> module_patchers;
146#endif
147
148 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
149#ifdef HAS_NCE
150 if (Settings::IsNceEnabled()) {
151 return &module_patchers[i];
152 }
153#endif
154 return nullptr;
155 };
156
157 // Use the NSO module loader to figure out the code layout
158 for (size_t i = 0; i < static_modules.size(); i++) {
159 const auto& module = static_modules[i];
134 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 160 const FileSys::VirtualFile module_file{dir->GetFile(module)};
135 if (!module_file) { 161 if (!module_file) {
136 continue; 162 continue;
137 } 163 }
138 164
139 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 165 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
140 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( 166 const auto tentative_next_load_addr =
141 process, system, *module_file, code_size, should_pass_arguments, false); 167 AppLoader_NSO::LoadModule(process, system, *module_file, code_size,
168 should_pass_arguments, false, {}, GetPatcher(i));
142 if (!tentative_next_load_addr) { 169 if (!tentative_next_load_addr) {
143 return {ResultStatus::ErrorLoadingNSO, {}}; 170 return {ResultStatus::ErrorLoadingNSO, {}};
144 } 171 }
@@ -146,8 +173,18 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
146 code_size = *tentative_next_load_addr; 173 code_size = *tentative_next_load_addr;
147 } 174 }
148 175
176 // Enable direct memory mapping in case of NCE.
177 const u64 fastmem_base = [&]() -> size_t {
178 if (Settings::IsNceEnabled()) {
179 auto& buffer = system.DeviceMemory().buffer;
180 buffer.EnableDirectMappedAddress();
181 return reinterpret_cast<u64>(buffer.VirtualBasePointer());
182 }
183 return 0;
184 }();
185
149 // Setup the process code layout 186 // Setup the process code layout
150 if (process.LoadFromMetadata(metadata, code_size, is_hbl).IsError()) { 187 if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) {
151 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; 188 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
152 } 189 }
153 190
@@ -157,7 +194,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
157 VAddr next_load_addr{base_address}; 194 VAddr next_load_addr{base_address};
158 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), 195 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
159 system.GetContentProvider()}; 196 system.GetContentProvider()};
160 for (const auto& module : static_modules) { 197 for (size_t i = 0; i < static_modules.size(); i++) {
198 const auto& module = static_modules[i];
161 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 199 const FileSys::VirtualFile module_file{dir->GetFile(module)};
162 if (!module_file) { 200 if (!module_file) {
163 continue; 201 continue;
@@ -165,15 +203,16 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
165 203
166 const VAddr load_addr{next_load_addr}; 204 const VAddr load_addr{next_load_addr};
167 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 205 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
168 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( 206 const auto tentative_next_load_addr =
169 process, system, *module_file, load_addr, should_pass_arguments, true, pm); 207 AppLoader_NSO::LoadModule(process, system, *module_file, load_addr,
208 should_pass_arguments, true, pm, GetPatcher(i));
170 if (!tentative_next_load_addr) { 209 if (!tentative_next_load_addr) {
171 return {ResultStatus::ErrorLoadingNSO, {}}; 210 return {ResultStatus::ErrorLoadingNSO, {}};
172 } 211 }
173 212
174 next_load_addr = *tentative_next_load_addr; 213 next_load_addr = *tentative_next_load_addr;
175 modules.insert_or_assign(load_addr, module); 214 modules.insert_or_assign(load_addr, module);
176 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 215 LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr);
177 } 216 }
178 217
179 // Find the RomFS by searching for a ".romfs" file in this directory 218 // Find the RomFS by searching for a ".romfs" file in this directory
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index bf56a08b4..cd6982921 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -91,7 +91,8 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
91 91
92 // Setup the process code layout 92 // Setup the process code layout
93 if (process 93 if (process
94 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) 94 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0,
95 false)
95 .IsError()) { 96 .IsError()) {
96 return {ResultStatus::ErrorNotInitialized, {}}; 97 return {ResultStatus::ErrorNotInitialized, {}};
97 } 98 }
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 69f1a54ed..e74697cda 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 HAS_NCE
26#include "core/arm/nce/patcher.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 }
@@ -194,14 +199,61 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
194 199
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);
202 size_t image_size = program_image.size();
203
204#ifdef HAS_NCE
205 const auto& code = codeset.CodeSegment();
206
207 // NROs always have a 39-bit address space.
208 Settings::SetNceEnabled(true);
209
210 // Create NCE patcher
211 Core::NCE::Patcher patch{};
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.GetPatchMode() == 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.GetSectionSize());
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 }();
197 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(), false) 242 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base,
243 false)
201 .IsError()) { 244 .IsError()) {
202 return false; 245 return false;
203 } 246 }
204 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 HAS_NCE
251 if (Settings::IsNceEnabled()) {
252 patch.RelocateAndCopy(process.GetEntryPoint(), code, program_image,
253 &process.GetPostHandlers());
254 }
255#endif
256
205 // Load codeset for current process 257 // Load codeset for current process
206 codeset.memory = std::move(program_image); 258 codeset.memory = std::move(program_image);
207 process.LoadModule(std::move(codeset), process.GetEntryPoint()); 259 process.LoadModule(std::move(codeset), process.GetEntryPoint());
@@ -209,8 +261,9 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
209 return true; 261 return true;
210} 262}
211 263
212bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) { 264bool AppLoader_NRO::LoadNro(Core::System& system, Kernel::KProcess& process,
213 return LoadNroImpl(process, nro_file.ReadAllBytes()); 265 const FileSys::VfsFile& nro_file) {
266 return LoadNroImpl(system, process, nro_file.ReadAllBytes());
214} 267}
215 268
216AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) { 269AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) {
@@ -218,7 +271,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S
218 return {ResultStatus::ErrorAlreadyLoaded, {}}; 271 return {ResultStatus::ErrorAlreadyLoaded, {}};
219 } 272 }
220 273
221 if (!LoadNro(process, *file)) { 274 if (!LoadNro(system, process, *file)) {
222 return {ResultStatus::ErrorLoadingNRO, {}}; 275 return {ResultStatus::ErrorLoadingNRO, {}};
223 } 276 }
224 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..b053a0d14 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 HAS_NCE
24#include "core/arm/nce/patcher.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 HAS_NCE
97 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
98 return patch->GetSectionSize();
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
@@ -129,15 +145,44 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
129 // Apply patches if necessary 145 // Apply patches if necessary
130 const auto name = nso_file.GetName(); 146 const auto name = nso_file.GetName();
131 if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { 147 if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) {
132 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); 148 std::span<u8> patchable_section(program_image.data() + module_start,
149 program_image.size() - module_start);
150 std::vector<u8> pi_header(sizeof(NSOHeader) + patchable_section.size());
133 std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); 151 std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
134 std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(), 152 std::memcpy(pi_header.data() + sizeof(NSOHeader), patchable_section.data(),
135 program_image.size()); 153 patchable_section.size());
136 154
137 pi_header = pm->PatchNSO(pi_header, name); 155 pi_header = pm->PatchNSO(pi_header, name);
138 156
139 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data()); 157 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), patchable_section.data());
158 }
159
160#ifdef HAS_NCE
161 // If we are computing the process code layout and using nce backend, patch.
162 const auto& code = codeset.CodeSegment();
163 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) {
164 // Patch SVCs and MRS calls in the guest code
165 patch->PatchText(program_image, code);
166
167 // Add patch section size to the module size.
168 image_size += static_cast<u32>(patch->GetSectionSize());
169 } else if (patch) {
170 // Relocate code patch and copy to the program_image.
171 patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers());
172
173 // Update patch section.
174 auto& patch_segment = codeset.PatchSegment();
175 patch_segment.addr =
176 patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
177 patch_segment.size = static_cast<u32>(patch->GetSectionSize());
178
179 // Add patch section size to the module size. In PreText mode image_size
180 // already contains the patch segment as part of module_start.
181 if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) {
182 image_size += patch_segment.size;
183 }
140 } 184 }
185#endif
141 186
142 // If we aren't actually loading (i.e. just computing the process code layout), we are done 187 // If we aren't actually loading (i.e. just computing the process code layout), we are done
143 if (!load_into_process) { 188 if (!load_into_process) {
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