summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------externals/mbedtls0
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/crypto/key_manager.cpp826
-rw-r--r--src/core/crypto/key_manager.h104
-rw-r--r--src/core/crypto/partition_data_manager.cpp593
-rw-r--r--src/core/crypto/partition_data_manager.h109
-rw-r--r--src/core/file_sys/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/vfs.h10
-rw-r--r--src/core/file_sys/vfs_types.h21
-rw-r--r--src/core/hle/kernel/errors.h2
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp24
-rw-r--r--src/core/hle/kernel/process.h49
-rw-r--r--src/core/hle/kernel/svc.cpp130
-rw-r--r--src/core/hle/kernel/svc_wrap.h64
-rw-r--r--src/core/hle/kernel/thread.cpp5
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp18
-rw-r--r--src/core/hle/service/audio/hwopus.cpp37
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp16
-rw-r--r--src/core/hle/service/filesystem/filesystem.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp72
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/elf.cpp32
-rw-r--r--src/core/loader/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp29
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp42
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/video_core/engines/fermi_2d.cpp14
-rw-r--r--src/video_core/engines/fermi_2d.h14
-rw-r--r--src/video_core/engines/maxwell_3d.h24
-rw-r--r--src/video_core/engines/maxwell_dma.cpp10
-rw-r--r--src/video_core/engines/maxwell_dma.h4
-rw-r--r--src/video_core/engines/shader_bytecode.h38
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp90
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h29
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp90
-rw-r--r--src/video_core/textures/decoders.cpp209
-rw-r--r--src/video_core/textures/decoders.h21
-rw-r--r--src/video_core/textures/texture.h18
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp5
-rw-r--r--src/yuzu/main.cpp118
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
53 files changed, 2536 insertions, 434 deletions
diff --git a/externals/mbedtls b/externals/mbedtls
Subproject d409b75a4cf75a5b358b352c75826ddbca44db5 Subproject a280e602f3a4ae001d3a83cbc3e6e04c99c2227
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 194e5f3f7..78986deb5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,6 +18,8 @@ add_library(core STATIC
18 crypto/encryption_layer.h 18 crypto/encryption_layer.h
19 crypto/key_manager.cpp 19 crypto/key_manager.cpp
20 crypto/key_manager.h 20 crypto/key_manager.h
21 crypto/partition_data_manager.cpp
22 crypto/partition_data_manager.h
21 crypto/ctr_encryption_layer.cpp 23 crypto/ctr_encryption_layer.cpp
22 crypto/ctr_encryption_layer.h 24 crypto/ctr_encryption_layer.h
23 crypto/xts_encryption_layer.cpp 25 crypto/xts_encryption_layer.cpp
@@ -70,6 +72,7 @@ add_library(core STATIC
70 file_sys/vfs_real.cpp 72 file_sys/vfs_real.cpp
71 file_sys/vfs_real.h 73 file_sys/vfs_real.h
72 file_sys/vfs_static.h 74 file_sys/vfs_static.h
75 file_sys/vfs_types.h
73 file_sys/vfs_vector.cpp 76 file_sys/vfs_vector.cpp
74 file_sys/vfs_vector.h 77 file_sys/vfs_vector.h
75 file_sys/xts_archive.cpp 78 file_sys/xts_archive.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e2fb9e038..32baa40dc 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -148,7 +148,7 @@ struct System::Impl {
148 telemetry_session = std::make_unique<Core::TelemetrySession>(); 148 telemetry_session = std::make_unique<Core::TelemetrySession>();
149 service_manager = std::make_shared<Service::SM::ServiceManager>(); 149 service_manager = std::make_shared<Service::SM::ServiceManager>();
150 150
151 Service::Init(service_manager, virtual_filesystem); 151 Service::Init(service_manager, *virtual_filesystem);
152 GDBStub::Init(); 152 GDBStub::Init();
153 153
154 renderer = VideoCore::CreateRenderer(emu_window); 154 renderer = VideoCore::CreateRenderer(emu_window);
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index bf3a70944..fd0786068 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -4,23 +4,56 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bitset>
8#include <cctype>
7#include <fstream> 9#include <fstream>
8#include <locale> 10#include <locale>
11#include <map>
9#include <sstream> 12#include <sstream>
10#include <string_view> 13#include <string_view>
11#include <tuple> 14#include <tuple>
12#include <vector> 15#include <vector>
16#include <mbedtls/bignum.h>
17#include <mbedtls/cipher.h>
18#include <mbedtls/cmac.h>
19#include <mbedtls/sha256.h>
20#include "common/common_funcs.h"
13#include "common/common_paths.h" 21#include "common/common_paths.h"
14#include "common/file_util.h" 22#include "common/file_util.h"
15#include "common/hex_util.h" 23#include "common/hex_util.h"
16#include "common/logging/log.h" 24#include "common/logging/log.h"
17#include "core/crypto/aes_util.h" 25#include "core/crypto/aes_util.h"
18#include "core/crypto/key_manager.h" 26#include "core/crypto/key_manager.h"
27#include "core/crypto/partition_data_manager.h"
28#include "core/file_sys/content_archive.h"
29#include "core/file_sys/nca_metadata.h"
30#include "core/file_sys/partition_filesystem.h"
31#include "core/file_sys/registered_cache.h"
32#include "core/hle/service/filesystem/filesystem.h"
19#include "core/loader/loader.h" 33#include "core/loader/loader.h"
20#include "core/settings.h" 34#include "core/settings.h"
21 35
22namespace Core::Crypto { 36namespace Core::Crypto {
23 37
38constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
39
40using namespace Common;
41
42const std::array<SHA256Hash, 2> eticket_source_hashes{
43 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
44 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
45};
46
47const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
48 {{S128KeyType::Master, 0}, "master_key_"},
49 {{S128KeyType::Package1, 0}, "package1_key_"},
50 {{S128KeyType::Package2, 0}, "package2_key_"},
51 {{S128KeyType::Titlekek, 0}, "titlekek_"},
52 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
53 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
54 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
55};
56
24Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { 57Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
25 Key128 out{}; 58 Key128 out{};
26 59
@@ -37,57 +70,136 @@ Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, K
37 return out; 70 return out;
38} 71}
39 72
73Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source) {
74 AESCipher<Key128> sbk_cipher(sbk, Mode::ECB);
75 AESCipher<Key128> tsec_cipher(tsec, Mode::ECB);
76 tsec_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
77 sbk_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
78 return source;
79}
80
81Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source) {
82 Key128 master_root;
83 std::memcpy(master_root.data(), keyblob.data(), sizeof(Key128));
84
85 AESCipher<Key128> master_cipher(master_root, Mode::ECB);
86
87 Key128 master{};
88 master_cipher.Transcode(master_source.data(), master_source.size(), master.data(), Op::Decrypt);
89 return master;
90}
91
92std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
93 const Key128& key) {
94 std::array<u8, 0x90> keyblob;
95 AESCipher<Key128> cipher(key, Mode::CTR);
96 cipher.SetIV(std::vector<u8>(encrypted_keyblob.data() + 0x10, encrypted_keyblob.data() + 0x20));
97 cipher.Transcode(encrypted_keyblob.data() + 0x20, keyblob.size(), keyblob.data(), Op::Decrypt);
98 return keyblob;
99}
100
101void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
102 const auto kek_generation_source =
103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
104 const auto key_generation_source =
105 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
106
107 if (HasKey(S128KeyType::Master, crypto_revision)) {
108 for (auto kak_type :
109 {KeyAreaKeyType::Application, KeyAreaKeyType::Ocean, KeyAreaKeyType::System}) {
110 if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
111 static_cast<u64>(kak_type))) {
112 const auto source =
113 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
114 static_cast<u64>(kak_type));
115 const auto kek =
116 GenerateKeyEncryptionKey(source, GetKey(S128KeyType::Master, crypto_revision),
117 kek_generation_source, key_generation_source);
118 SetKey(S128KeyType::KeyArea, kek, crypto_revision, static_cast<u64>(kak_type));
119 }
120 }
121
122 AESCipher<Key128> master_cipher(GetKey(S128KeyType::Master, crypto_revision), Mode::ECB);
123 for (auto key_type : {SourceKeyType::Titlekek, SourceKeyType::Package2}) {
124 if (HasKey(S128KeyType::Source, static_cast<u64>(key_type))) {
125 Key128 key{};
126 master_cipher.Transcode(
127 GetKey(S128KeyType::Source, static_cast<u64>(key_type)).data(), key.size(),
128 key.data(), Op::Decrypt);
129 SetKey(key_type == SourceKeyType::Titlekek ? S128KeyType::Titlekek
130 : S128KeyType::Package2,
131 key, crypto_revision);
132 }
133 }
134 }
135}
136
137Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
138 AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
139 Key128 mac_key{};
140 mac_cipher.Transcode(mac_source.data(), mac_key.size(), mac_key.data(), Op::Decrypt);
141 return mac_key;
142}
143
40boost::optional<Key128> DeriveSDSeed() { 144boost::optional<Key128> DeriveSDSeed() {
41 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 145 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
42 "/system/save/8000000000000043", 146 "/system/save/8000000000000043",
43 "rb+"); 147 "rb+");
44 if (!save_43.IsOpen()) 148 if (!save_43.IsOpen())
45 return boost::none; 149 return boost::none;
150
46 const FileUtil::IOFile sd_private( 151 const FileUtil::IOFile sd_private(
47 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
48 if (!sd_private.IsOpen()) 153 if (!sd_private.IsOpen())
49 return boost::none; 154 return boost::none;
50 155
51 sd_private.Seek(0, SEEK_SET);
52 std::array<u8, 0x10> private_seed{}; 156 std::array<u8, 0x10> private_seed{};
53 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10) 157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
54 return boost::none; 158 return boost::none;
159 }
55 160
56 std::array<u8, 0x10> buffer{}; 161 std::array<u8, 0x10> buffer{};
57 std::size_t offset = 0; 162 std::size_t offset = 0;
58 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 163 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
59 save_43.Seek(offset, SEEK_SET); 164 if (!save_43.Seek(offset, SEEK_SET)) {
165 return boost::none;
166 }
167
60 save_43.ReadBytes(buffer.data(), buffer.size()); 168 save_43.ReadBytes(buffer.data(), buffer.size());
61 if (buffer == private_seed) 169 if (buffer == private_seed) {
62 break; 170 break;
171 }
63 } 172 }
64 173
65 if (offset + 0x10 >= save_43.GetSize()) 174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
66 return boost::none; 175 return boost::none;
176 }
67 177
68 Key128 seed{}; 178 Key128 seed{};
69 save_43.Seek(offset + 0x10, SEEK_SET); 179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
70 save_43.ReadBytes(seed.data(), seed.size()); 180 return boost::none;
181 }
71 return seed; 182 return seed;
72} 183}
73 184
74Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) { 185Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
75 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK))) 186 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)))
76 return Loader::ResultStatus::ErrorMissingSDKEKSource; 187 return Loader::ResultStatus::ErrorMissingSDKEKSource;
77 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration))) 188 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)))
78 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 189 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
79 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 190 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
80 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 191 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
81 192
82 const auto sd_kek_source = 193 const auto sd_kek_source =
83 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)); 194 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
84 const auto aes_kek_gen = 195 const auto aes_kek_gen =
85 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)); 196 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
86 const auto aes_key_gen = 197 const auto aes_key_gen =
87 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)); 198 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
88 const auto master_00 = keys.GetKey(S128KeyType::Master); 199 const auto master_00 = keys.GetKey(S128KeyType::Master);
89 const auto sd_kek = 200 const auto sd_kek =
90 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); 201 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
202 keys.SetKey(S128KeyType::SDKek, sd_kek);
91 203
92 if (!keys.HasKey(S128KeyType::SDSeed)) 204 if (!keys.HasKey(S128KeyType::SDSeed))
93 return Loader::ResultStatus::ErrorMissingSDSeed; 205 return Loader::ResultStatus::ErrorMissingSDSeed;
@@ -118,9 +230,147 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
118 return source; ///< Return unaltered source to satisfy output requirement. 230 return source; ///< Return unaltered source to satisfy output requirement.
119 }); 231 });
120 232
233 keys.SetKey(S256KeyType::SDKey, sd_keys[0], static_cast<u64>(SDKeyType::Save));
234 keys.SetKey(S256KeyType::SDKey, sd_keys[1], static_cast<u64>(SDKeyType::NCA));
235
121 return Loader::ResultStatus::Success; 236 return Loader::ResultStatus::Success;
122} 237}
123 238
239std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
240 if (!ticket_save.IsOpen())
241 return {};
242
243 std::vector<u8> buffer(ticket_save.GetSize());
244 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
245 return {};
246 }
247
248 std::vector<TicketRaw> out;
249 u32 magic{};
250 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
251 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
252 buffer[offset + 3] == 0x0) {
253 out.emplace_back();
254 auto& next = out.back();
255 std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
256 offset += next.size();
257 }
258 }
259
260 return out;
261}
262
263template <size_t size>
264static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
265 const std::array<u8, size>& rhs) {
266 std::array<u8, size> out{};
267 std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(), std::bit_xor<>());
268 return out;
269}
270
271template <size_t target_size, size_t in_size>
272static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
273 // Avoids truncation overflow within the loop below.
274 static_assert(target_size <= 0xFF);
275
276 std::array<u8, in_size + 4> seed_exp{};
277 std::memcpy(seed_exp.data(), seed.data(), in_size);
278
279 std::vector<u8> out;
280 size_t i = 0;
281 while (out.size() < target_size) {
282 out.resize(out.size() + 0x20);
283 seed_exp[in_size + 3] = static_cast<u8>(i);
284 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
285 ++i;
286 }
287
288 std::array<u8, target_size> target;
289 std::memcpy(target.data(), out.data(), target_size);
290 return target;
291}
292
293template <size_t size>
294static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
295 u64 offset = 0;
296 for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
297 if (data[i] == 0x1) {
298 offset = i + 1;
299 break;
300 } else if (data[i] != 0x0) {
301 return boost::none;
302 }
303 }
304
305 return offset;
306}
307
308boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
309 const RSAKeyPair<2048>& key) {
310 u32 cert_authority;
311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
312 if (cert_authority == 0)
313 return boost::none;
314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
315 LOG_INFO(Crypto,
316 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
317 cert_authority);
318 }
319
320 Key128 rights_id;
321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
322
323 if (rights_id == Key128{})
324 return boost::none;
325
326 Key128 key_temp{};
327
328 if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
329 std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
330 return std::make_pair(rights_id, key_temp);
331 }
332
333 mbedtls_mpi D; // RSA Private Exponent
334 mbedtls_mpi N; // RSA Modulus
335 mbedtls_mpi S; // Input
336 mbedtls_mpi M; // Output
337
338 mbedtls_mpi_init(&D);
339 mbedtls_mpi_init(&N);
340 mbedtls_mpi_init(&S);
341 mbedtls_mpi_init(&M);
342
343 mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
344 mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
345 mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
346
347 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
348
349 std::array<u8, 0x100> rsa_step;
350 mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
351
352 u8 m_0 = rsa_step[0];
353 std::array<u8, 0x20> m_1;
354 std::memcpy(m_1.data(), rsa_step.data() + 0x01, m_1.size());
355 std::array<u8, 0xDF> m_2;
356 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
357
358 if (m_0 != 0)
359 return boost::none;
360
361 m_1 = m_1 ^ MGF1<0x20>(m_2);
362 m_2 = m_2 ^ MGF1<0xDF>(m_1);
363
364 const auto offset = FindTicketOffset(m_2);
365 if (offset == boost::none)
366 return boost::none;
367 ASSERT(offset.get() > 0);
368
369 std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size());
370
371 return std::make_pair(rights_id, key_temp);
372}
373
124KeyManager::KeyManager() { 374KeyManager::KeyManager() {
125 // Initialize keys 375 // Initialize keys
126 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 376 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -137,6 +387,15 @@ KeyManager::KeyManager() {
137 387
138 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true); 388 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
139 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true); 389 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
390 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
391 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
392}
393
394static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
395 if (base.size() < begin + length)
396 return false;
397 return std::all_of(base.begin() + begin, base.begin() + begin + length,
398 [](u8 c) { return std::isdigit(c); });
140} 399}
141 400
142void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 401void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -158,6 +417,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
158 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); 417 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
159 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); 418 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
160 419
420 if (out[0].compare(0, 1, "#") == 0)
421 continue;
422
161 if (is_title_keys) { 423 if (is_title_keys) {
162 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 424 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
163 u128 rights_id{}; 425 u128 rights_id{};
@@ -174,6 +436,50 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
174 const auto index = s256_file_id.at(out[0]); 436 const auto index = s256_file_id.at(out[0]);
175 Key256 key = Common::HexStringToArray<32>(out[1]); 437 Key256 key = Common::HexStringToArray<32>(out[1]);
176 s256_keys[{index.type, index.field1, index.field2}] = key; 438 s256_keys[{index.type, index.field1, index.field2}] = key;
439 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
440 out[0].compare(0, 9, "keyblob_k") != 0) {
441 if (!ValidCryptoRevisionString(out[0], 8, 2))
442 continue;
443
444 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
445 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
446 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
447 if (!ValidCryptoRevisionString(out[0], 18, 2))
448 continue;
449
450 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
451 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
452 } else {
453 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
454 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
455 continue;
456 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
457 const auto index =
458 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
459 const auto sub = kv.first.second;
460 if (sub == 0) {
461 s128_keys[{kv.first.first, index, 0}] =
462 Common::HexStringToArray<16>(out[1]);
463 } else {
464 s128_keys[{kv.first.first, kv.first.second, index}] =
465 Common::HexStringToArray<16>(out[1]);
466 }
467
468 break;
469 }
470 }
471
472 static constexpr std::array<const char*, 3> kak_names = {
473 "key_area_key_application_", "key_area_key_ocean_", "key_area_key_system_"};
474 for (size_t j = 0; j < kak_names.size(); ++j) {
475 const auto& match = kak_names[j];
476 if (out[0].compare(0, std::strlen(match), match) == 0) {
477 const auto index =
478 std::stoul(out[0].substr(std::strlen(match), 2), nullptr, 16);
479 s128_keys[{S128KeyType::KeyArea, index, j}] =
480 Common::HexStringToArray<16>(out[1]);
481 }
482 }
177 } 483 }
178 } 484 }
179 } 485 }
@@ -187,6 +493,28 @@ void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string&
187 LoadFromFile(dir2 + DIR_SEP + filename, title); 493 LoadFromFile(dir2 + DIR_SEP + filename, title);
188} 494}
189 495
496bool KeyManager::BaseDeriveNecessary() const {
497 const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
498 return !HasKey(key_type, index1, index2);
499 };
500
501 if (check_key_existence(S256KeyType::Header))
502 return true;
503
504 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
505 if (check_key_existence(S128KeyType::Master, i) ||
506 check_key_existence(S128KeyType::KeyArea, i,
507 static_cast<u64>(KeyAreaKeyType::Application)) ||
508 check_key_existence(S128KeyType::KeyArea, i, static_cast<u64>(KeyAreaKeyType::Ocean)) ||
509 check_key_existence(S128KeyType::KeyArea, i,
510 static_cast<u64>(KeyAreaKeyType::System)) ||
511 check_key_existence(S128KeyType::Titlekek, i))
512 return true;
513 }
514
515 return false;
516}
517
190bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const { 518bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
191 return s128_keys.find({id, field1, field2}) != s128_keys.end(); 519 return s128_keys.find({id, field1, field2}) != s128_keys.end();
192} 520}
@@ -207,13 +535,30 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
207 return s256_keys.at({id, field1, field2}); 535 return s256_keys.at({id, field1, field2});
208} 536}
209 537
210template <std::size_t Size> 538Key256 KeyManager::GetBISKey(u8 partition_id) const {
211void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, 539 Key256 out{};
540
541 for (const auto& bis_type : {BISKeyType::Crypto, BISKeyType::Tweak}) {
542 if (HasKey(S128KeyType::BIS, partition_id, static_cast<u64>(bis_type))) {
543 std::memcpy(
544 out.data() + sizeof(Key128) * static_cast<u64>(bis_type),
545 s128_keys.at({S128KeyType::BIS, partition_id, static_cast<u64>(bis_type)}).data(),
546 sizeof(Key128));
547 }
548 }
549
550 return out;
551}
552
553template <size_t Size>
554void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
212 const std::array<u8, Size>& key) { 555 const std::array<u8, Size>& key) {
213 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 556 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
214 std::string filename = "title.keys_autogenerated"; 557 std::string filename = "title.keys_autogenerated";
215 if (!title_key) 558 if (category == KeyCategory::Standard)
216 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 559 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
560 else if (category == KeyCategory::Console)
561 filename = "console.keys_autogenerated";
217 const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename); 562 const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
218 FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename); 563 FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
219 std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app); 564 std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
@@ -227,7 +572,7 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
227 } 572 }
228 573
229 file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key)); 574 file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
230 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key); 575 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
231} 576}
232 577
233void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { 578void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
@@ -237,8 +582,15 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
237 Key128 rights_id; 582 Key128 rights_id;
238 std::memcpy(rights_id.data(), &field2, sizeof(u64)); 583 std::memcpy(rights_id.data(), &field2, sizeof(u64));
239 std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64)); 584 std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
240 WriteKeyToFile(true, Common::HexArrayToString(rights_id), key); 585 WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
241 } 586 }
587
588 auto category = KeyCategory::Standard;
589 if (id == S128KeyType::Keyblob || id == S128KeyType::KeyblobMAC || id == S128KeyType::TSEC ||
590 id == S128KeyType::SecureBoot || id == S128KeyType::SDSeed || id == S128KeyType::BIS) {
591 category = KeyCategory::Console;
592 }
593
242 const auto iter2 = std::find_if( 594 const auto iter2 = std::find_if(
243 s128_file_id.begin(), s128_file_id.end(), 595 s128_file_id.begin(), s128_file_id.end(),
244 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { 596 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
@@ -246,7 +598,30 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
246 std::tie(id, field1, field2); 598 std::tie(id, field1, field2);
247 }); 599 });
248 if (iter2 != s128_file_id.end()) 600 if (iter2 != s128_file_id.end())
249 WriteKeyToFile(false, iter2->first, key); 601 WriteKeyToFile(category, iter2->first, key);
602
603 // Variable cases
604 if (id == S128KeyType::KeyArea) {
605 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
606 "key_area_key_ocean_{:02X}",
607 "key_area_key_system_{:02X}"};
608 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
609 } else if (id == S128KeyType::Master) {
610 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
611 } else if (id == S128KeyType::Package1) {
612 WriteKeyToFile(category, fmt::format("package1_key_{:02X}", field1), key);
613 } else if (id == S128KeyType::Package2) {
614 WriteKeyToFile(category, fmt::format("package2_key_{:02X}", field1), key);
615 } else if (id == S128KeyType::Titlekek) {
616 WriteKeyToFile(category, fmt::format("titlekek_{:02X}", field1), key);
617 } else if (id == S128KeyType::Keyblob) {
618 WriteKeyToFile(category, fmt::format("keyblob_key_{:02X}", field1), key);
619 } else if (id == S128KeyType::KeyblobMAC) {
620 WriteKeyToFile(category, fmt::format("keyblob_mac_key_{:02X}", field1), key);
621 } else if (id == S128KeyType::Source && field1 == static_cast<u64>(SourceKeyType::Keyblob)) {
622 WriteKeyToFile(category, fmt::format("keyblob_key_source_{:02X}", field2), key);
623 }
624
250 s128_keys[{id, field1, field2}] = key; 625 s128_keys[{id, field1, field2}] = key;
251} 626}
252 627
@@ -260,7 +635,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
260 std::tie(id, field1, field2); 635 std::tie(id, field1, field2);
261 }); 636 });
262 if (iter != s256_file_id.end()) 637 if (iter != s256_file_id.end())
263 WriteKeyToFile(false, iter->first, key); 638 WriteKeyToFile(KeyCategory::Standard, iter->first, key);
264 s256_keys[{id, field1, field2}] = key; 639 s256_keys[{id, field1, field2}] = key;
265} 640}
266 641
@@ -290,59 +665,388 @@ void KeyManager::DeriveSDSeedLazy() {
290 SetKey(S128KeyType::SDSeed, res.get()); 665 SetKey(S128KeyType::SDSeed, res.get());
291} 666}
292 667
668static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
669 Key128 out{};
670
671 mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(),
672 key.size() * 8, source, size, out.data());
673 return out;
674}
675
676void KeyManager::DeriveBase() {
677 if (!BaseDeriveNecessary())
678 return;
679
680 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
681 return;
682
683 const auto has_bis = [this](u64 id) {
684 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
685 HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Tweak));
686 };
687
688 const auto copy_bis = [this](u64 id_from, u64 id_to) {
689 SetKey(S128KeyType::BIS,
690 GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Crypto)), id_to,
691 static_cast<u64>(BISKeyType::Crypto));
692
693 SetKey(S128KeyType::BIS,
694 GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Tweak)), id_to,
695 static_cast<u64>(BISKeyType::Tweak));
696 };
697
698 if (has_bis(2) && !has_bis(3))
699 copy_bis(2, 3);
700 else if (has_bis(3) && !has_bis(2))
701 copy_bis(3, 2);
702
703 std::bitset<32> revisions(0xFFFFFFFF);
704 for (size_t i = 0; i < revisions.size(); ++i) {
705 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i) ||
706 encrypted_keyblobs[i] == std::array<u8, 0xB0>{}) {
707 revisions.reset(i);
708 }
709 }
710
711 if (!revisions.any())
712 return;
713
714 const auto sbk = GetKey(S128KeyType::SecureBoot);
715 const auto tsec = GetKey(S128KeyType::TSEC);
716 const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
717
718 for (size_t i = 0; i < revisions.size(); ++i) {
719 if (!revisions[i])
720 continue;
721
722 // Derive keyblob key
723 const auto key = DeriveKeyblobKey(
724 sbk, tsec, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i));
725
726 SetKey(S128KeyType::Keyblob, key, i);
727
728 // Derive keyblob MAC key
729 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
730 continue;
731
732 const auto mac_key = DeriveKeyblobMACKey(
733 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
734 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
735
736 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
737 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
738 continue;
739
740 // Decrypt keyblob
741 if (keyblobs[i] == std::array<u8, 0x90>{}) {
742 keyblobs[i] = DecryptKeyblob(encrypted_keyblobs[i], key);
743 WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
744 keyblobs[i]);
745 }
746
747 Key128 package1;
748 std::memcpy(package1.data(), keyblobs[i].data() + 0x80, sizeof(Key128));
749 SetKey(S128KeyType::Package1, package1, i);
750
751 // Derive master key
752 if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master))) {
753 SetKey(S128KeyType::Master,
754 DeriveMasterKey(keyblobs[i], GetKey(S128KeyType::Source,
755 static_cast<u64>(SourceKeyType::Master))),
756 i);
757 }
758 }
759
760 revisions.set();
761 for (size_t i = 0; i < revisions.size(); ++i) {
762 if (!HasKey(S128KeyType::Master, i))
763 revisions.reset(i);
764 }
765
766 if (!revisions.any())
767 return;
768
769 for (size_t i = 0; i < revisions.size(); ++i) {
770 if (!revisions[i])
771 continue;
772
773 // Derive general purpose keys
774 DeriveGeneralPurposeKeys(i);
775 }
776
777 if (HasKey(S128KeyType::Master, 0) &&
778 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)) &&
779 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)) &&
780 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)) &&
781 HasKey(S256KeyType::HeaderSource)) {
782 const auto header_kek = GenerateKeyEncryptionKey(
783 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)),
784 GetKey(S128KeyType::Master, 0),
785 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)),
786 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)));
787 SetKey(S128KeyType::HeaderKek, header_kek);
788
789 AESCipher<Key128> header_cipher(header_kek, Mode::ECB);
790 Key256 out = GetKey(S256KeyType::HeaderSource);
791 header_cipher.Transcode(out.data(), out.size(), out.data(), Op::Decrypt);
792 SetKey(S256KeyType::Header, out);
793 }
794}
795
796void KeyManager::DeriveETicket(PartitionDataManager& data) {
797 // ETicket keys
798 const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
799 0x0100000000000033, FileSys::ContentRecordType::Program);
800
801 if (es == nullptr)
802 return;
803
804 const auto exefs = es->GetExeFS();
805 if (exefs == nullptr)
806 return;
807
808 const auto main = exefs->GetFile("main");
809 if (main == nullptr)
810 return;
811
812 const auto bytes = main->ReadAllBytes();
813
814 const auto eticket_kek = FindKeyFromHex16(bytes, eticket_source_hashes[0]);
815 const auto eticket_kekek = FindKeyFromHex16(bytes, eticket_source_hashes[1]);
816
817 const auto seed3 = data.GetRSAKekSeed3();
818 const auto mask0 = data.GetRSAKekMask0();
819
820 if (eticket_kek != Key128{})
821 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
822 if (eticket_kekek != Key128{}) {
823 SetKey(S128KeyType::Source, eticket_kekek,
824 static_cast<size_t>(SourceKeyType::ETicketKekek));
825 }
826 if (seed3 != Key128{})
827 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
828 if (mask0 != Key128{})
829 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
830 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
831 mask0 == Key128{}) {
832 return;
833 }
834
835 Key128 rsa_oaep_kek{};
836 std::transform(seed3.begin(), seed3.end(), mask0.begin(), rsa_oaep_kek.begin(),
837 std::bit_xor<>());
838
839 if (rsa_oaep_kek == Key128{})
840 return;
841
842 SetKey(S128KeyType::Source, rsa_oaep_kek,
843 static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
844
845 Key128 temp_kek{};
846 Key128 temp_kekek{};
847 Key128 eticket_final{};
848
849 // Derive ETicket RSA Kek
850 AESCipher<Key128> es_master(GetKey(S128KeyType::Master), Mode::ECB);
851 es_master.Transcode(rsa_oaep_kek.data(), rsa_oaep_kek.size(), temp_kek.data(), Op::Decrypt);
852 AESCipher<Key128> es_kekek(temp_kek, Mode::ECB);
853 es_kekek.Transcode(eticket_kekek.data(), eticket_kekek.size(), temp_kekek.data(), Op::Decrypt);
854 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
855 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
856
857 if (eticket_final == Key128{})
858 return;
859
860 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
861
862 // Titlekeys
863 data.DecryptProdInfo(GetBISKey(0));
864
865 const auto eticket_extended_kek = data.GetETicketExtendedKek();
866
867 std::vector<u8> extended_iv(0x10);
868 std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
869 std::array<u8, 0x230> extended_dec{};
870 AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
871 rsa_1.SetIV(extended_iv);
872 rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
873 extended_dec.data(), Op::Decrypt);
874
875 RSAKeyPair<2048> rsa_key{};
876 std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
877 std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
878 std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
879
880 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
881 "/system/save/80000000000000e1",
882 "rb+");
883 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
884 "/system/save/80000000000000e2",
885 "rb+");
886
887 const auto blob2 = GetTicketblob(save2);
888 auto res = GetTicketblob(save1);
889 res.insert(res.end(), blob2.begin(), blob2.end());
890
891 for (const auto& raw : res) {
892 const auto pair = ParseTicket(raw, rsa_key);
893 if (pair == boost::none)
894 continue;
895 const auto& [rid, key] = pair.value();
896 u128 rights_id;
897 std::memcpy(rights_id.data(), rid.data(), rid.size());
898 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
899 }
900}
901
902void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
903 if (key == Key128{})
904 return;
905 SetKey(id, key, field1, field2);
906}
907
908void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
909 if (key == Key256{})
910 return;
911 SetKey(id, key, field1, field2);
912}
913
914void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
915 if (!BaseDeriveNecessary())
916 return;
917
918 if (!data.HasBoot0())
919 return;
920
921 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
922 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{})
923 continue;
924 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
925 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
926 encrypted_keyblobs[i]);
927 }
928
929 SetKeyWrapped(S128KeyType::Source, data.GetPackage2KeySource(),
930 static_cast<u64>(SourceKeyType::Package2));
931 SetKeyWrapped(S128KeyType::Source, data.GetAESKekGenerationSource(),
932 static_cast<u64>(SourceKeyType::AESKekGeneration));
933 SetKeyWrapped(S128KeyType::Source, data.GetTitlekekSource(),
934 static_cast<u64>(SourceKeyType::Titlekek));
935 SetKeyWrapped(S128KeyType::Source, data.GetMasterKeySource(),
936 static_cast<u64>(SourceKeyType::Master));
937 SetKeyWrapped(S128KeyType::Source, data.GetKeyblobMACKeySource(),
938 static_cast<u64>(SourceKeyType::KeyblobMAC));
939
940 for (size_t i = 0; i < PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH; ++i) {
941 SetKeyWrapped(S128KeyType::Source, data.GetKeyblobKeySource(i),
942 static_cast<u64>(SourceKeyType::Keyblob), i);
943 }
944
945 if (data.HasFuses())
946 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
947
948 DeriveBase();
949
950 Key128 latest_master{};
951 for (s8 i = 0x1F; i >= 0; --i) {
952 if (GetKey(S128KeyType::Master, static_cast<u8>(i)) != Key128{}) {
953 latest_master = GetKey(S128KeyType::Master, static_cast<u8>(i));
954 break;
955 }
956 }
957
958 const auto masters = data.GetTZMasterKeys(latest_master);
959 for (size_t i = 0; i < masters.size(); ++i) {
960 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i))
961 SetKey(S128KeyType::Master, masters[i], i);
962 }
963
964 DeriveBase();
965
966 if (!data.HasPackage2())
967 return;
968
969 std::array<Key128, 0x20> package2_keys{};
970 for (size_t i = 0; i < package2_keys.size(); ++i) {
971 if (HasKey(S128KeyType::Package2, i))
972 package2_keys[i] = GetKey(S128KeyType::Package2, i);
973 }
974 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
975
976 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyApplicationSource(),
977 static_cast<u64>(SourceKeyType::KeyAreaKey),
978 static_cast<u64>(KeyAreaKeyType::Application));
979 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyOceanSource(),
980 static_cast<u64>(SourceKeyType::KeyAreaKey),
981 static_cast<u64>(KeyAreaKeyType::Ocean));
982 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeySystemSource(),
983 static_cast<u64>(SourceKeyType::KeyAreaKey),
984 static_cast<u64>(KeyAreaKeyType::System));
985 SetKeyWrapped(S128KeyType::Source, data.GetSDKekSource(),
986 static_cast<u64>(SourceKeyType::SDKek));
987 SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDSaveKeySource(),
988 static_cast<u64>(SDKeyType::Save));
989 SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDNCAKeySource(),
990 static_cast<u64>(SDKeyType::NCA));
991 SetKeyWrapped(S128KeyType::Source, data.GetHeaderKekSource(),
992 static_cast<u64>(SourceKeyType::HeaderKek));
993 SetKeyWrapped(S256KeyType::HeaderSource, data.GetHeaderKeySource());
994 SetKeyWrapped(S128KeyType::Source, data.GetAESKeyGenerationSource(),
995 static_cast<u64>(SourceKeyType::AESKeyGeneration));
996
997 DeriveBase();
998}
999
293const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { 1000const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
294 {"master_key_00", {S128KeyType::Master, 0, 0}},
295 {"master_key_01", {S128KeyType::Master, 1, 0}},
296 {"master_key_02", {S128KeyType::Master, 2, 0}},
297 {"master_key_03", {S128KeyType::Master, 3, 0}},
298 {"master_key_04", {S128KeyType::Master, 4, 0}},
299 {"package1_key_00", {S128KeyType::Package1, 0, 0}},
300 {"package1_key_01", {S128KeyType::Package1, 1, 0}},
301 {"package1_key_02", {S128KeyType::Package1, 2, 0}},
302 {"package1_key_03", {S128KeyType::Package1, 3, 0}},
303 {"package1_key_04", {S128KeyType::Package1, 4, 0}},
304 {"package2_key_00", {S128KeyType::Package2, 0, 0}},
305 {"package2_key_01", {S128KeyType::Package2, 1, 0}},
306 {"package2_key_02", {S128KeyType::Package2, 2, 0}},
307 {"package2_key_03", {S128KeyType::Package2, 3, 0}},
308 {"package2_key_04", {S128KeyType::Package2, 4, 0}},
309 {"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
310 {"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
311 {"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
312 {"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
313 {"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
314 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, 1001 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
315 {"key_area_key_application_00", 1002 {"eticket_rsa_kek_source",
316 {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}}, 1003 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
317 {"key_area_key_application_01", 1004 {"eticket_rsa_kekek_source",
318 {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}}, 1005 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
319 {"key_area_key_application_02", 1006 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
320 {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}}, 1007 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
321 {"key_area_key_application_03", 1008 {"rsa_oaep_kek_generation_source",
322 {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}}, 1009 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
323 {"key_area_key_application_04", 1010 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
324 {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
325 {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
326 {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
327 {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
328 {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
329 {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
330 {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
331 {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
332 {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
333 {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
334 {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
335 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
336 {"aes_kek_generation_source", 1011 {"aes_kek_generation_source",
337 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}}, 1012 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
338 {"aes_key_generation_source", 1013 {"aes_key_generation_source",
339 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, 1014 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1015 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1016 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1017 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1018 {"key_area_key_application_source",
1019 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1020 static_cast<u64>(KeyAreaKeyType::Application)}},
1021 {"key_area_key_ocean_source",
1022 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1023 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1024 {"key_area_key_system_source",
1025 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1026 static_cast<u64>(KeyAreaKeyType::System)}},
1027 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1028 {"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}},
1029 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1030 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
340 {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, 1031 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1032 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1033 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1034 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1035 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1036 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1037 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1038 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1039 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1040 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1041 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
341}; 1042};
342 1043
343const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { 1044const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
344 {"header_key", {S256KeyType::Header, 0, 0}}, 1045 {"header_key", {S256KeyType::Header, 0, 0}},
345 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, 1046 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
346 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, 1047 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1048 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1049 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1050 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
347}; 1051};
348} // namespace Core::Crypto 1052} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 978eec8dc..cccb3c0ae 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -5,11 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <map>
8#include <string> 9#include <string>
9#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
10#include <boost/optional.hpp> 11#include <boost/optional.hpp>
11#include <fmt/format.h> 12#include <fmt/format.h>
12#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/crypto/partition_data_manager.h"
15#include "core/file_sys/vfs_types.h"
16
17namespace FileUtil {
18class IOFile;
19}
13 20
14namespace Loader { 21namespace Loader {
15enum class ResultStatus : u16; 22enum class ResultStatus : u16;
@@ -22,13 +29,30 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
22using Key128 = std::array<u8, 0x10>; 29using Key128 = std::array<u8, 0x10>;
23using Key256 = std::array<u8, 0x20>; 30using Key256 = std::array<u8, 0x20>;
24using SHA256Hash = std::array<u8, 0x20>; 31using SHA256Hash = std::array<u8, 0x20>;
32using TicketRaw = std::array<u8, 0x400>;
25 33
26static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); 34static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
27static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); 35static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
36
37template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
38struct RSAKeyPair {
39 std::array<u8, byte_size> encryption_key;
40 std::array<u8, byte_size> decryption_key;
41 std::array<u8, byte_size> modulus;
42 std::array<u8, 4> exponent;
43};
44
45enum class KeyCategory : u8 {
46 Standard,
47 Title,
48 Console,
49};
28 50
29enum class S256KeyType : u64 { 51enum class S256KeyType : u64 {
30 Header, // 52 SDKey, // f1=SDKeyType
31 SDKeySource, // f1=SDKeyType 53 Header, //
54 SDKeySource, // f1=SDKeyType
55 HeaderSource, //
32}; 56};
33 57
34enum class S128KeyType : u64 { 58enum class S128KeyType : u64 {
@@ -41,6 +65,14 @@ enum class S128KeyType : u64 {
41 SDSeed, // 65 SDSeed, //
42 Titlekey, // f1=rights id LSB f2=rights id MSB 66 Titlekey, // f1=rights id LSB f2=rights id MSB
43 Source, // f1=source type, f2= sub id 67 Source, // f1=source type, f2= sub id
68 Keyblob, // f1=crypto revision
69 KeyblobMAC, // f1=crypto revision
70 TSEC, //
71 SecureBoot, //
72 BIS, // f1=partition (0-3), f2=type {crypt, tweak}
73 HeaderKek, //
74 SDKek, //
75 RSAKek, //
44}; 76};
45 77
46enum class KeyAreaKeyType : u8 { 78enum class KeyAreaKeyType : u8 {
@@ -50,9 +82,19 @@ enum class KeyAreaKeyType : u8 {
50}; 82};
51 83
52enum class SourceKeyType : u8 { 84enum class SourceKeyType : u8 {
53 SDKEK, 85 SDKek, //
54 AESKEKGeneration, 86 AESKekGeneration, //
55 AESKeyGeneration, 87 AESKeyGeneration, //
88 RSAOaepKekGeneration, //
89 Master, //
90 Keyblob, // f2=crypto revision
91 KeyAreaKey, // f2=KeyAreaKeyType
92 Titlekek, //
93 Package2, //
94 HeaderKek, //
95 KeyblobMAC, //
96 ETicketKek, //
97 ETicketKekek, //
56}; 98};
57 99
58enum class SDKeyType : u8 { 100enum class SDKeyType : u8 {
@@ -60,6 +102,16 @@ enum class SDKeyType : u8 {
60 NCA, 102 NCA,
61}; 103};
62 104
105enum class BISKeyType : u8 {
106 Crypto,
107 Tweak,
108};
109
110enum class RSAKekType : u8 {
111 Mask0,
112 Seed3,
113};
114
63template <typename KeyType> 115template <typename KeyType>
64struct KeyIndex { 116struct KeyIndex {
65 KeyType type; 117 KeyType type;
@@ -91,6 +143,8 @@ public:
91 Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; 143 Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
92 Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; 144 Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
93 145
146 Key256 GetBISKey(u8 partition_id) const;
147
94 void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 148 void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
95 void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 149 void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
96 150
@@ -100,23 +154,51 @@ public:
100 // 8*43 and the private file to exist. 154 // 8*43 and the private file to exist.
101 void DeriveSDSeedLazy(); 155 void DeriveSDSeedLazy();
102 156
157 bool BaseDeriveNecessary() const;
158 void DeriveBase();
159 void DeriveETicket(PartitionDataManager& data);
160
161 void PopulateFromPartitionData(PartitionDataManager& data);
162
103private: 163private:
104 boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys; 164 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
105 boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys; 165 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
166
167 std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
168 std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
106 169
107 bool dev_mode; 170 bool dev_mode;
108 void LoadFromFile(const std::string& filename, bool is_title_keys); 171 void LoadFromFile(const std::string& filename, bool is_title_keys);
109 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 172 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
110 const std::string& filename, bool title); 173 const std::string& filename, bool title);
111 template <std::size_t Size> 174 template <size_t Size>
112 void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); 175 void WriteKeyToFile(KeyCategory category, std::string_view keyname,
176 const std::array<u8, Size>& key);
177
178 void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
179
180 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
181 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
113 182
114 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; 183 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
115 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id; 184 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
116}; 185};
117 186
118Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 187Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
188Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source);
189Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source);
190Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source);
191std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
192 const Key128& key);
193
119boost::optional<Key128> DeriveSDSeed(); 194boost::optional<Key128> DeriveSDSeed();
120Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys); 195Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
196
197std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
198
199// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
200// 0x140-0x144 is zero)
201boost::optional<std::pair<Key128, Key128>> ParseTicket(
202 const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
121 203
122} // namespace Core::Crypto 204} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
new file mode 100644
index 000000000..25cee1f3a
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -0,0 +1,593 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5// NOTE TO FUTURE MAINTAINERS:
6// When a new version of switch cryptography is released,
7// hash the new keyblob source and master key and add the hashes to
8// the arrays below.
9
10#include <algorithm>
11#include <array>
12#include <cctype>
13#include <cstring>
14#include <mbedtls/sha256.h>
15#include "common/assert.h"
16#include "common/common_funcs.h"
17#include "common/common_types.h"
18#include "common/hex_util.h"
19#include "common/logging/log.h"
20#include "common/string_util.h"
21#include "common/swap.h"
22#include "core/crypto/key_manager.h"
23#include "core/crypto/partition_data_manager.h"
24#include "core/crypto/xts_encryption_layer.h"
25#include "core/file_sys/vfs.h"
26#include "core/file_sys/vfs_offset.h"
27
28using namespace Common;
29
30namespace Core::Crypto {
31
32struct Package2Header {
33 std::array<u8, 0x100> signature;
34 Key128 header_ctr;
35 std::array<Key128, 4> section_ctr;
36 u32_le magic;
37 u32_le base_offset;
38 INSERT_PADDING_BYTES(4);
39 u8 version_max;
40 u8 version_min;
41 INSERT_PADDING_BYTES(2);
42 std::array<u32_le, 4> section_size;
43 std::array<u32_le, 4> section_offset;
44 std::array<SHA256Hash, 4> section_hash;
45};
46static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
47
48struct INIHeader {
49 u32_le magic;
50 u32_le size;
51 u32_le process_count;
52 INSERT_PADDING_BYTES(4);
53};
54static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
55
56struct SectionHeader {
57 u32_le offset;
58 u32_le size_decompressed;
59 u32_le size_compressed;
60 u32_le attribute;
61};
62static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
63
64struct KIPHeader {
65 u32_le magic;
66 std::array<char, 12> name;
67 u64_le title_id;
68 u32_le category;
69 u8 priority;
70 u8 core;
71 INSERT_PADDING_BYTES(1);
72 u8 flags;
73 std::array<SectionHeader, 6> sections;
74 std::array<u32, 0x20> capabilities;
75};
76static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
77
78const std::array<SHA256Hash, 0x10> source_hashes{
79 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
80 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
81 "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
82 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
83 "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
84 "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
85 "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
86 "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
87 "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
88 "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
89 "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
90 "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
91 "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
92 "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
93 "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
94 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
95};
96
97const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
98 "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
99 "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
100 "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
101 "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
102 "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
103 "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
104 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
105 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
106
107 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
108 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
109 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
110 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
111 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
112 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
113 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
114 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
115
116 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
117 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
118 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
119 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
120 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
121 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
122 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
123 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
124
125 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
126 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
127 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
128 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
129 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
130 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
131 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
132 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
133};
134
135const std::array<SHA256Hash, 0x20> master_key_hashes{
136 "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
137 "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
138 "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
139 "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
140 "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
141 "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
143 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
144
145 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
146 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
147 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
148 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
149 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
150 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
151 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
152 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
153
154 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
155 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
156 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
157 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
158 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
159 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
160 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
161 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
162
163 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
164 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
165 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
166 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
167 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
168 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
169 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
170 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
171};
172
173static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
174 const auto data_size = in.size() - 0xC;
175
176 u32 compressed_size{};
177 u32 init_index{};
178 u32 additional_size{};
179 std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
180 std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
181 std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
182
183 std::vector<u8> out(in.size() + additional_size);
184
185 if (compressed_size == in.size())
186 std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
187 else
188 std::memcpy(out.data(), in.data(), compressed_size);
189
190 auto index = in.size() - init_index;
191 auto out_index = out.size();
192
193 while (out_index > 0) {
194 --index;
195 auto control = in[index];
196 for (size_t i = 0; i < 8; ++i) {
197 if ((control & 0x80) > 0) {
198 ASSERT(index >= 2);
199 index -= 2;
200 u64 segment_offset = in[index] | in[index + 1] << 8;
201 u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
202 segment_offset &= 0xFFF;
203 segment_offset += 3;
204
205 if (out_index < segment_size)
206 segment_size = out_index;
207
208 ASSERT(out_index >= segment_size);
209
210 out_index -= segment_size;
211
212 for (size_t j = 0; j < segment_size; ++j) {
213 ASSERT(out_index + j + segment_offset < out.size());
214 out[out_index + j] = out[out_index + j + segment_offset];
215 }
216 } else {
217 ASSERT(out_index >= 1);
218 --out_index;
219 --index;
220 out[out_index] = in[index];
221 }
222
223 control <<= 1;
224 if (out_index == 0)
225 return out;
226 }
227 }
228
229 return out;
230}
231
232static u8 CalculateMaxKeyblobSourceHash() {
233 for (s8 i = 0x1F; i >= 0; --i) {
234 if (keyblob_source_hashes[i] != SHA256Hash{})
235 return static_cast<u8>(i + 1);
236 }
237
238 return 0;
239}
240
241const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = CalculateMaxKeyblobSourceHash();
242
243template <size_t key_size = 0x10>
244std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary,
245 const std::array<u8, 0x20>& hash) {
246 if (binary.size() < key_size)
247 return {};
248
249 std::array<u8, 0x20> temp{};
250 for (size_t i = 0; i < binary.size() - key_size; ++i) {
251 mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
252
253 if (temp != hash)
254 continue;
255
256 std::array<u8, key_size> out{};
257 std::memcpy(out.data(), binary.data() + i, key_size);
258 return out;
259 }
260
261 return {};
262}
263
264std::array<u8, 16> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 32> hash) {
265 return FindKeyFromHex<0x10>(binary, hash);
266}
267
268static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<u8>& binary,
269 const Key128& key) {
270 if (binary.size() < 0x10)
271 return {};
272
273 SHA256Hash temp{};
274 Key128 dec_temp{};
275 std::array<Key128, 0x20> out{};
276 AESCipher<Key128> cipher(key, Mode::ECB);
277 for (size_t i = 0; i < binary.size() - 0x10; ++i) {
278 cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
279 mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
280
281 for (size_t k = 0; k < out.size(); ++k) {
282 if (temp == master_key_hashes[k]) {
283 out[k] = dec_temp;
284 break;
285 }
286 }
287 }
288
289 return out;
290}
291
292FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
293 const std::string& name) {
294 auto upper = name;
295 std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
296 for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
297 if (dir->GetFile(fname) != nullptr)
298 return dir->GetFile(fname);
299 }
300
301 return nullptr;
302}
303
304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
306 fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
308 package2({
309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
311 FindFileInDirWithNames(sysdata_dir, "BCPKG2-3-SafeMode-Main"),
312 FindFileInDirWithNames(sysdata_dir, "BCPKG2-4-SafeMode-Sub"),
313 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
315 }),
316 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
320 : secure_monitor->ReadAllBytes()),
321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
322 : package1_decrypted->ReadAllBytes()) {
323}
324
325PartitionDataManager::~PartitionDataManager() = default;
326
327bool PartitionDataManager::HasBoot0() const {
328 return boot0 != nullptr;
329}
330
331FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
332 return boot0;
333}
334
335PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
336 std::size_t index) const {
337 if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
338 return GetEncryptedKeyblobs()[index];
339 return {};
340}
341
342PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
343 if (!HasBoot0())
344 return {};
345
346 EncryptedKeyBlobs out{};
347 for (size_t i = 0; i < out.size(); ++i)
348 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
349 return out;
350}
351
352std::vector<u8> PartitionDataManager::GetSecureMonitor() const {
353 return secure_monitor_bytes;
354}
355
356std::array<u8, 16> PartitionDataManager::GetPackage2KeySource() const {
357 return FindKeyFromHex(secure_monitor_bytes, source_hashes[2]);
358}
359
360std::array<u8, 16> PartitionDataManager::GetAESKekGenerationSource() const {
361 return FindKeyFromHex(secure_monitor_bytes, source_hashes[3]);
362}
363
364std::array<u8, 16> PartitionDataManager::GetTitlekekSource() const {
365 return FindKeyFromHex(secure_monitor_bytes, source_hashes[5]);
366}
367
368std::array<std::array<u8, 16>, 32> PartitionDataManager::GetTZMasterKeys(
369 std::array<u8, 0x10> master_key) const {
370 return FindEncryptedMasterKeyFromHex(secure_monitor_bytes, master_key);
371}
372
373std::array<u8, 16> PartitionDataManager::GetRSAKekSeed3() const {
374 return FindKeyFromHex(secure_monitor_bytes, source_hashes[14]);
375}
376
377std::array<u8, 16> PartitionDataManager::GetRSAKekMask0() const {
378 return FindKeyFromHex(secure_monitor_bytes, source_hashes[15]);
379}
380
381std::vector<u8> PartitionDataManager::GetPackage1Decrypted() const {
382 return package1_decrypted_bytes;
383}
384
385std::array<u8, 16> PartitionDataManager::GetMasterKeySource() const {
386 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[1]);
387}
388
389std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
390 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
391}
392
393std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
394 if (keyblob_source_hashes[revision] == SHA256Hash{}) {
395 LOG_WARNING(Crypto,
396 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
397 revision);
398 }
399 return FindKeyFromHex(package1_decrypted_bytes, keyblob_source_hashes[revision]);
400}
401
402bool PartitionDataManager::HasFuses() const {
403 return fuses != nullptr;
404}
405
406FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const {
407 return fuses;
408}
409
410std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const {
411 if (!HasFuses())
412 return {};
413 Key128 out{};
414 fuses->Read(out.data(), out.size(), 0xA4);
415 return out;
416}
417
418bool PartitionDataManager::HasKFuses() const {
419 return kfuses != nullptr;
420}
421
422FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
423 return kfuses;
424}
425
426bool PartitionDataManager::HasPackage2(Package2Type type) const {
427 return package2.at(static_cast<size_t>(type)) != nullptr;
428}
429
430FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const {
431 return package2.at(static_cast<size_t>(type));
432}
433
434bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
435
436 const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
437 Package2Header temp = header;
438 AESCipher<Key128> cipher(key, Mode::CTR);
439 cipher.SetIV(iv);
440 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
441 Op::Decrypt);
442 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
443 header = temp;
444 return true;
445 }
446
447 return false;
448}
449
450void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
451 Package2Type type) {
452 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
453 package2[static_cast<size_t>(type)],
454 package2[static_cast<size_t>(type)]->GetSize() - 0x4000, 0x4000);
455
456 Package2Header header{};
457 if (file->ReadObject(&header) != sizeof(Package2Header))
458 return;
459
460 std::size_t revision = 0xFF;
461 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
462 for (std::size_t i = 0; i < package2_keys.size(); ++i) {
463 if (AttemptDecrypt(package2_keys[i], header)) {
464 revision = i;
465 }
466 }
467 }
468
469 if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
470 return;
471
472 const auto a = std::make_shared<FileSys::OffsetVfsFile>(
473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
474
475 auto c = a->ReadAllBytes();
476
477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480
481 INIHeader ini;
482 std::memcpy(&ini, c.data(), sizeof(INIHeader));
483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
484 return;
485
486 u64 offset = sizeof(INIHeader);
487 for (size_t i = 0; i < ini.process_count; ++i) {
488 KIPHeader kip;
489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
491 return;
492
493 const auto name =
494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
495
496 if (name != "FS" && name != "spl") {
497 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
498 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
499 continue;
500 }
501
502 const u64 initial_offset = sizeof(KIPHeader) + offset;
503 const auto text_begin = c.cbegin() + initial_offset;
504 const auto text_end = text_begin + kip.sections[0].size_compressed;
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
506
507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
509
510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
512
513 std::vector<u8> out;
514 out.reserve(text.size() + rodata.size() + data.size());
515 out.insert(out.end(), text.begin(), text.end());
516 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end());
518
519 offset += sizeof(KIPHeader) + out.size();
520
521 if (name == "FS")
522 package2_fs[static_cast<size_t>(type)] = std::move(out);
523 else if (name == "spl")
524 package2_spl[static_cast<size_t>(type)] = std::move(out);
525 }
526}
527
528const std::vector<u8>& PartitionDataManager::GetPackage2FSDecompressed(Package2Type type) const {
529 return package2_fs.at(static_cast<size_t>(type));
530}
531
532std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyApplicationSource(Package2Type type) const {
533 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[6]);
534}
535
536std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyOceanSource(Package2Type type) const {
537 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[7]);
538}
539
540std::array<u8, 16> PartitionDataManager::GetKeyAreaKeySystemSource(Package2Type type) const {
541 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[8]);
542}
543
544std::array<u8, 16> PartitionDataManager::GetSDKekSource(Package2Type type) const {
545 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[9]);
546}
547
548std::array<u8, 32> PartitionDataManager::GetSDSaveKeySource(Package2Type type) const {
549 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[10]);
550}
551
552std::array<u8, 32> PartitionDataManager::GetSDNCAKeySource(Package2Type type) const {
553 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[11]);
554}
555
556std::array<u8, 16> PartitionDataManager::GetHeaderKekSource(Package2Type type) const {
557 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[12]);
558}
559
560std::array<u8, 32> PartitionDataManager::GetHeaderKeySource(Package2Type type) const {
561 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[13]);
562}
563
564const std::vector<u8>& PartitionDataManager::GetPackage2SPLDecompressed(Package2Type type) const {
565 return package2_spl.at(static_cast<size_t>(type));
566}
567
568std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type type) const {
569 return FindKeyFromHex(package2_spl.at(static_cast<size_t>(type)), source_hashes[4]);
570}
571
572bool PartitionDataManager::HasProdInfo() const {
573 return prodinfo != nullptr;
574}
575
576FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const {
577 return prodinfo;
578}
579
580void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
581 if (prodinfo == nullptr)
582 return;
583
584 prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
585}
586
587std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
588 std::array<u8, 0x240> out{};
589 if (prodinfo_decrypted != nullptr)
590 prodinfo_decrypted->Read(out.data(), out.size(), 0x3890);
591 return out;
592}
593} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
new file mode 100644
index 000000000..0ad007c72
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.h
@@ -0,0 +1,109 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_types.h"
9#include "core/file_sys/vfs_types.h"
10
11namespace Core::Crypto {
12
13enum class Package2Type {
14 NormalMain,
15 NormalSub,
16 SafeModeMain,
17 SafeModeSub,
18 RepairMain,
19 RepairSub,
20};
21
22class PartitionDataManager {
23public:
24 static const u8 MAX_KEYBLOB_SOURCE_HASH;
25 static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32;
26 static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0;
27
28 using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>;
29 using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
30
31 explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
32 ~PartitionDataManager();
33
34 // BOOT0
35 bool HasBoot0() const;
36 FileSys::VirtualFile GetBoot0Raw() const;
37 EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
38 EncryptedKeyBlobs GetEncryptedKeyblobs() const;
39 std::vector<u8> GetSecureMonitor() const;
40 std::array<u8, 0x10> GetPackage2KeySource() const;
41 std::array<u8, 0x10> GetAESKekGenerationSource() const;
42 std::array<u8, 0x10> GetTitlekekSource() const;
43 std::array<std::array<u8, 0x10>, 0x20> GetTZMasterKeys(std::array<u8, 0x10> master_key) const;
44 std::array<u8, 0x10> GetRSAKekSeed3() const;
45 std::array<u8, 0x10> GetRSAKekMask0() const;
46 std::vector<u8> GetPackage1Decrypted() const;
47 std::array<u8, 0x10> GetMasterKeySource() const;
48 std::array<u8, 0x10> GetKeyblobMACKeySource() const;
49 std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
50
51 // Fuses
52 bool HasFuses() const;
53 FileSys::VirtualFile GetFusesRaw() const;
54 std::array<u8, 0x10> GetSecureBootKey() const;
55
56 // K-Fuses
57 bool HasKFuses() const;
58 FileSys::VirtualFile GetKFusesRaw() const;
59
60 // Package2
61 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
62 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
63 void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
64 Package2Type type);
65 const std::vector<u8>& GetPackage2FSDecompressed(
66 Package2Type type = Package2Type::NormalMain) const;
67 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
68 Package2Type type = Package2Type::NormalMain) const;
69 std::array<u8, 0x10> GetKeyAreaKeyOceanSource(
70 Package2Type type = Package2Type::NormalMain) const;
71 std::array<u8, 0x10> GetKeyAreaKeySystemSource(
72 Package2Type type = Package2Type::NormalMain) const;
73 std::array<u8, 0x10> GetSDKekSource(Package2Type type = Package2Type::NormalMain) const;
74 std::array<u8, 0x20> GetSDSaveKeySource(Package2Type type = Package2Type::NormalMain) const;
75 std::array<u8, 0x20> GetSDNCAKeySource(Package2Type type = Package2Type::NormalMain) const;
76 std::array<u8, 0x10> GetHeaderKekSource(Package2Type type = Package2Type::NormalMain) const;
77 std::array<u8, 0x20> GetHeaderKeySource(Package2Type type = Package2Type::NormalMain) const;
78 const std::vector<u8>& GetPackage2SPLDecompressed(
79 Package2Type type = Package2Type::NormalMain) const;
80 std::array<u8, 0x10> GetAESKeyGenerationSource(
81 Package2Type type = Package2Type::NormalMain) const;
82
83 // PRODINFO
84 bool HasProdInfo() const;
85 FileSys::VirtualFile GetProdInfoRaw() const;
86 void DecryptProdInfo(std::array<u8, 0x20> bis_key);
87 std::array<u8, 0x240> GetETicketExtendedKek() const;
88
89private:
90 FileSys::VirtualFile boot0;
91 FileSys::VirtualFile fuses;
92 FileSys::VirtualFile kfuses;
93 std::array<FileSys::VirtualFile, 6> package2;
94 FileSys::VirtualFile prodinfo;
95 FileSys::VirtualFile secure_monitor;
96 FileSys::VirtualFile package1_decrypted;
97
98 // Processed
99 std::array<FileSys::VirtualFile, 6> package2_decrypted;
100 FileSys::VirtualFile prodinfo_decrypted;
101 std::vector<u8> secure_monitor_bytes;
102 std::vector<u8> package1_decrypted_bytes;
103 std::array<std::vector<u8>, 6> package2_fs;
104 std::array<std::vector<u8>, 6> package2_spl;
105};
106
107std::array<u8, 0x10> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 0x20> hash);
108
109} // namespace Core::Crypto
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
17}; 17};
18 18
19std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
21 application_name.size());
21} 22}
22 23
23std::string LanguageEntry::GetDeveloperName() const { 24std::string LanguageEntry::GetDeveloperName() const {
24 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); 25 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
26 developer_name.size());
25} 27}
26 28
27NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 29NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
56 return raw->title_id; 58 return raw->title_id;
57} 59}
58 60
61u64 NACP::GetDLCBaseTitleId() const {
62 return raw->dlc_base_title_id;
63}
64
59std::string NACP::GetVersionString() const { 65std::string NACP::GetVersionString() const {
60 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size());
61} 68}
62} // namespace FileSys 69} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
79 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const;
82 std::string GetVersionString() const; 83 std::string GetVersionString() const;
83 84
84private: 85private:
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 270291631..7f0d520ca 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -12,20 +12,12 @@
12#include <vector> 12#include <vector>
13#include <boost/optional.hpp> 13#include <boost/optional.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/file_sys/vfs_types.h"
15 16
16namespace FileSys { 17namespace FileSys {
17 18
18class VfsDirectory;
19class VfsFile;
20class VfsFilesystem;
21
22enum class Mode : u32; 19enum class Mode : u32;
23 20
24// Convenience typedefs to use Vfs* interfaces
25using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
26using VirtualDir = std::shared_ptr<VfsDirectory>;
27using VirtualFile = std::shared_ptr<VfsFile>;
28
29// An enumeration representing what can be at the end of a path in a VfsFilesystem 21// An enumeration representing what can be at the end of a path in a VfsFilesystem
30enum class VfsEntryType { 22enum class VfsEntryType {
31 None, 23 None,
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h
new file mode 100644
index 000000000..6215ed7af
--- /dev/null
+++ b/src/core/file_sys/vfs_types.h
@@ -0,0 +1,21 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9namespace FileSys {
10
11class VfsDirectory;
12class VfsFile;
13class VfsFilesystem;
14
15// Declarations for Vfs* pointer types
16
17using VirtualDir = std::shared_ptr<VfsDirectory>;
18using VirtualFile = std::shared_ptr<VfsFile>;
19using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
20
21} // namespace FileSys
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index e5fa67ae8..885259618 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -22,6 +22,7 @@ enum {
22 HandleTableFull = 105, 22 HandleTableFull = 105,
23 InvalidMemoryState = 106, 23 InvalidMemoryState = 106,
24 InvalidMemoryPermissions = 108, 24 InvalidMemoryPermissions = 108,
25 InvalidMemoryRange = 110,
25 InvalidThreadPriority = 112, 26 InvalidThreadPriority = 112,
26 InvalidProcessorId = 113, 27 InvalidProcessorId = 113,
27 InvalidHandle = 114, 28 InvalidHandle = 114,
@@ -56,6 +57,7 @@ constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidA
56constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 57constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
57constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 58constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
58 ErrCodes::InvalidMemoryPermissions); 59 ErrCodes::InvalidMemoryPermissions);
60constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
59constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 61constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
60constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 62constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
61constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 63constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index d51562d92..d87a62bb9 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
25 case HandleType::Process: 25 case HandleType::Process:
26 case HandleType::AddressArbiter: 26 case HandleType::AddressArbiter:
27 case HandleType::ResourceLimit: 27 case HandleType::ResourceLimit:
28 case HandleType::CodeSet:
29 case HandleType::ClientPort: 28 case HandleType::ClientPort:
30 case HandleType::ClientSession: 29 case HandleType::ClientSession:
31 return false; 30 return false;
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 9eb72315c..c9f4d0bb3 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -26,7 +26,6 @@ enum class HandleType : u32 {
26 AddressArbiter, 26 AddressArbiter,
27 Timer, 27 Timer,
28 ResourceLimit, 28 ResourceLimit,
29 CodeSet,
30 ClientPort, 29 ClientPort,
31 ServerPort, 30 ServerPort,
32 ClientSession, 31 ClientSession,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index fb0027a71..c80b2c507 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,13 +20,7 @@
20 20
21namespace Kernel { 21namespace Kernel {
22 22
23SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) { 23CodeSet::CodeSet() = default;
24 SharedPtr<CodeSet> codeset(new CodeSet(kernel));
25 codeset->name = std::move(name);
26 return codeset;
27}
28
29CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
30CodeSet::~CodeSet() = default; 24CodeSet::~CodeSet() = default;
31 25
32SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { 26SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -224,20 +218,20 @@ void Process::FreeTLSSlot(VAddr tls_address) {
224 tls_slots[tls_page].reset(tls_slot); 218 tls_slots[tls_page].reset(tls_slot);
225} 219}
226 220
227void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 221void Process::LoadModule(CodeSet module_, VAddr base_addr) {
228 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, 222 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
229 MemoryState memory_state) { 223 MemoryState memory_state) {
230 auto vma = vm_manager 224 const auto vma = vm_manager
231 .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, 225 .MapMemoryBlock(segment.addr + base_addr, module_.memory,
232 segment.size, memory_state) 226 segment.offset, segment.size, memory_state)
233 .Unwrap(); 227 .Unwrap();
234 vm_manager.Reprotect(vma, permissions); 228 vm_manager.Reprotect(vma, permissions);
235 }; 229 };
236 230
237 // Map CodeSet segments 231 // Map CodeSet segments
238 MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 232 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
239 MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 233 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
240 MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 234 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
241} 235}
242 236
243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 237ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 590e0c73d..f2816943a 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -24,6 +24,7 @@ class ProgramMetadata;
24namespace Kernel { 24namespace Kernel {
25 25
26class KernelCore; 26class KernelCore;
27class ResourceLimit;
27 28
28struct AddressMapping { 29struct AddressMapping {
29 // Address and size must be page-aligned 30 // Address and size must be page-aligned
@@ -57,30 +58,33 @@ union ProcessFlags {
57 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). 58 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
58}; 59};
59 60
60enum class ProcessStatus { Created, Running, Exited }; 61/**
61 62 * Indicates the status of a Process instance.
62class ResourceLimit; 63 *
64 * @note These match the values as used by kernel,
65 * so new entries should only be added if RE
66 * shows that a new value has been introduced.
67 */
68enum class ProcessStatus {
69 Created,
70 CreatedWithDebuggerAttached,
71 Running,
72 WaitingForDebuggerToAttach,
73 DebuggerAttached,
74 Exiting,
75 Exited,
76 DebugBreak,
77};
63 78
64struct CodeSet final : public Object { 79struct CodeSet final {
65 struct Segment { 80 struct Segment {
66 std::size_t offset = 0; 81 std::size_t offset = 0;
67 VAddr addr = 0; 82 VAddr addr = 0;
68 u32 size = 0; 83 u32 size = 0;
69 }; 84 };
70 85
71 static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name); 86 explicit CodeSet();
72 87 ~CodeSet();
73 std::string GetTypeName() const override {
74 return "CodeSet";
75 }
76 std::string GetName() const override {
77 return name;
78 }
79
80 static const HandleType HANDLE_TYPE = HandleType::CodeSet;
81 HandleType GetHandleType() const override {
82 return HANDLE_TYPE;
83 }
84 88
85 Segment& CodeSegment() { 89 Segment& CodeSegment() {
86 return segments[0]; 90 return segments[0];
@@ -109,14 +113,7 @@ struct CodeSet final : public Object {
109 std::shared_ptr<std::vector<u8>> memory; 113 std::shared_ptr<std::vector<u8>> memory;
110 114
111 std::array<Segment, 3> segments; 115 std::array<Segment, 3> segments;
112 VAddr entrypoint; 116 VAddr entrypoint = 0;
113
114 /// Name of the process
115 std::string name;
116
117private:
118 explicit CodeSet(KernelCore& kernel);
119 ~CodeSet() override;
120}; 117};
121 118
122class Process final : public Object { 119class Process final : public Object {
@@ -219,7 +216,7 @@ public:
219 */ 216 */
220 void PrepareForTermination(); 217 void PrepareForTermination();
221 218
222 void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); 219 void LoadModule(CodeSet module_, VAddr base_addr);
223 220
224 /////////////////////////////////////////////////////////////////////////////////////////////// 221 ///////////////////////////////////////////////////////////////////////////////////////////////
225 // Memory Management 222 // Memory Management
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 3afcce3fe..7a053da1e 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -39,6 +39,73 @@ namespace {
39constexpr bool Is4KBAligned(VAddr address) { 39constexpr bool Is4KBAligned(VAddr address) {
40 return (address & 0xFFF) == 0; 40 return (address & 0xFFF) == 0;
41} 41}
42
43// Checks if address + size is greater than the given address
44// This can return false if the size causes an overflow of a 64-bit type
45// or if the given size is zero.
46constexpr bool IsValidAddressRange(VAddr address, u64 size) {
47 return address + size > address;
48}
49
50// Checks if a given address range lies within a larger address range.
51constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
52 VAddr address_range_end) {
53 const VAddr end_address = address + size - 1;
54 return address_range_begin <= address && end_address <= address_range_end - 1;
55}
56
57bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
58 return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
59 vm.GetAddressSpaceEndAddress());
60}
61
62bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
63 return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
64 vm.GetNewMapRegionEndAddress());
65}
66
67// Helper function that performs the common sanity checks for svcMapMemory
68// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
69// in the same order.
70ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
71 u64 size) {
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
73 return ERR_INVALID_ADDRESS;
74 }
75
76 if (size == 0 || !Is4KBAligned(size)) {
77 return ERR_INVALID_SIZE;
78 }
79
80 if (!IsValidAddressRange(dst_addr, size)) {
81 return ERR_INVALID_ADDRESS_STATE;
82 }
83
84 if (!IsValidAddressRange(src_addr, size)) {
85 return ERR_INVALID_ADDRESS_STATE;
86 }
87
88 if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
89 return ERR_INVALID_ADDRESS_STATE;
90 }
91
92 if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
93 return ERR_INVALID_MEMORY_RANGE;
94 }
95
96 const VAddr dst_end_address = dst_addr + size;
97 if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
98 vm_manager.GetHeapRegionEndAddress() > dst_addr) {
99 return ERR_INVALID_MEMORY_RANGE;
100 }
101
102 if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
103 vm_manager.GetMapRegionEndAddress() > dst_addr) {
104 return ERR_INVALID_MEMORY_RANGE;
105 }
106
107 return RESULT_SUCCESS;
108}
42} // Anonymous namespace 109} // Anonymous namespace
43 110
44/// Set the process heap to a given Size. It can both extend and shrink the heap. 111/// Set the process heap to a given Size. It can both extend and shrink the heap.
@@ -69,15 +136,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
69 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 136 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
70 src_addr, size); 137 src_addr, size);
71 138
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 139 auto* const current_process = Core::CurrentProcess();
73 return ERR_INVALID_ADDRESS; 140 const auto& vm_manager = current_process->VMManager();
74 }
75 141
76 if (size == 0 || !Is4KBAligned(size)) { 142 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
77 return ERR_INVALID_SIZE; 143 if (result != RESULT_SUCCESS) {
144 return result;
78 } 145 }
79 146
80 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); 147 return current_process->MirrorMemory(dst_addr, src_addr, size);
81} 148}
82 149
83/// Unmaps a region that was previously mapped with svcMapMemory 150/// Unmaps a region that was previously mapped with svcMapMemory
@@ -85,15 +152,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
85 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 152 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
86 src_addr, size); 153 src_addr, size);
87 154
88 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 155 auto* const current_process = Core::CurrentProcess();
89 return ERR_INVALID_ADDRESS; 156 const auto& vm_manager = current_process->VMManager();
90 }
91 157
92 if (size == 0 || !Is4KBAligned(size)) { 158 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
93 return ERR_INVALID_SIZE; 159 if (result != RESULT_SUCCESS) {
160 return result;
94 } 161 }
95 162
96 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); 163 return current_process->UnmapMemory(dst_addr, src_addr, size);
97} 164}
98 165
99/// Connect to an OS service given the port name, returns the handle to the port to out 166/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -303,15 +370,15 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
303 370
304struct BreakReason { 371struct BreakReason {
305 union { 372 union {
306 u64 raw; 373 u32 raw;
307 BitField<31, 1, u64> dont_kill_application; 374 BitField<31, 1, u32> signal_debugger;
308 }; 375 };
309}; 376};
310 377
311/// Break program execution 378/// Break program execution
312static void Break(u64 reason, u64 info1, u64 info2) { 379static void Break(u32 reason, u64 info1, u64 info2) {
313 BreakReason break_reason{reason}; 380 BreakReason break_reason{reason};
314 if (break_reason.dont_kill_application) { 381 if (break_reason.signal_debugger) {
315 LOG_ERROR( 382 LOG_ERROR(
316 Debug_Emulated, 383 Debug_Emulated,
317 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 384 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -322,6 +389,12 @@ static void Break(u64 reason, u64 info1, u64 info2) {
322 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 389 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
323 reason, info1, info2); 390 reason, info1, info2);
324 ASSERT(false); 391 ASSERT(false);
392
393 Core::CurrentProcess()->PrepareForTermination();
394
395 // Kill the current thread
396 GetCurrentThread()->Stop();
397 Core::System::GetInstance().PrepareReschedule();
325 } 398 }
326} 399}
327 400
@@ -1025,6 +1098,29 @@ static ResultCode ClearEvent(Handle handle) {
1025 return RESULT_SUCCESS; 1098 return RESULT_SUCCESS;
1026} 1099}
1027 1100
1101static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1102 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1103
1104 // This function currently only allows retrieving a process' status.
1105 enum class InfoType {
1106 Status,
1107 };
1108
1109 const auto& kernel = Core::System::GetInstance().Kernel();
1110 const auto process = kernel.HandleTable().Get<Process>(process_handle);
1111 if (!process) {
1112 return ERR_INVALID_HANDLE;
1113 }
1114
1115 const auto info_type = static_cast<InfoType>(type);
1116 if (info_type != InfoType::Status) {
1117 return ERR_INVALID_ENUM_VALUE;
1118 }
1119
1120 *out = static_cast<u64>(process->GetStatus());
1121 return RESULT_SUCCESS;
1122}
1123
1028namespace { 1124namespace {
1029struct FunctionDef { 1125struct FunctionDef {
1030 using Func = void(); 1126 using Func = void();
@@ -1160,7 +1256,7 @@ static const FunctionDef SVC_Table[] = {
1160 {0x79, nullptr, "CreateProcess"}, 1256 {0x79, nullptr, "CreateProcess"},
1161 {0x7A, nullptr, "StartProcess"}, 1257 {0x7A, nullptr, "StartProcess"},
1162 {0x7B, nullptr, "TerminateProcess"}, 1258 {0x7B, nullptr, "TerminateProcess"},
1163 {0x7C, nullptr, "GetProcessInfo"}, 1259 {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
1164 {0x7D, nullptr, "CreateResourceLimit"}, 1260 {0x7D, nullptr, "CreateResourceLimit"},
1165 {0x7E, nullptr, "SetResourceLimitLimitValue"}, 1261 {0x7E, nullptr, "SetResourceLimitLimitValue"},
1166 {0x7F, nullptr, "CallSecureMonitor"}, 1262 {0x7F, nullptr, "CallSecureMonitor"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 22712e64f..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -35,18 +35,18 @@ void SvcWrap() {
35 35
36template <ResultCode func(u32)> 36template <ResultCode func(u32)>
37void SvcWrap() { 37void SvcWrap() {
38 FuncReturn(func((u32)Param(0)).raw); 38 FuncReturn(func(static_cast<u32>(Param(0))).raw);
39} 39}
40 40
41template <ResultCode func(u32, u32)> 41template <ResultCode func(u32, u32)>
42void SvcWrap() { 42void SvcWrap() {
43 FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); 43 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
44} 44}
45 45
46template <ResultCode func(u32*, u32)> 46template <ResultCode func(u32*, u32)>
47void SvcWrap() { 47void SvcWrap() {
48 u32 param_1 = 0; 48 u32 param_1 = 0;
49 u32 retval = func(&param_1, (u32)Param(1)).raw; 49 u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
50 Core::CurrentArmInterface().SetReg(1, param_1); 50 Core::CurrentArmInterface().SetReg(1, param_1);
51 FuncReturn(retval); 51 FuncReturn(retval);
52} 52}
@@ -61,7 +61,7 @@ void SvcWrap() {
61 61
62template <ResultCode func(u64, s32)> 62template <ResultCode func(u64, s32)>
63void SvcWrap() { 63void SvcWrap() {
64 FuncReturn(func(Param(0), (s32)Param(1)).raw); 64 FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
65} 65}
66 66
67template <ResultCode func(u64, u32)> 67template <ResultCode func(u64, u32)>
@@ -77,21 +77,29 @@ void SvcWrap() {
77 FuncReturn(retval); 77 FuncReturn(retval);
78} 78}
79 79
80template <ResultCode func(u64*, u32, u32)>
81void SvcWrap() {
82 u64 param_1 = 0;
83 u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
84 Core::CurrentArmInterface().SetReg(1, param_1);
85 FuncReturn(retval);
86}
87
80template <ResultCode func(u32, u64)> 88template <ResultCode func(u32, u64)>
81void SvcWrap() { 89void SvcWrap() {
82 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); 90 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
83} 91}
84 92
85template <ResultCode func(u32, u32, u64)> 93template <ResultCode func(u32, u32, u64)>
86void SvcWrap() { 94void SvcWrap() {
87 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); 95 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
88} 96}
89 97
90template <ResultCode func(u32, u32*, u64*)> 98template <ResultCode func(u32, u32*, u64*)>
91void SvcWrap() { 99void SvcWrap() {
92 u32 param_1 = 0; 100 u32 param_1 = 0;
93 u64 param_2 = 0; 101 u64 param_2 = 0;
94 ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2); 102 ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
95 Core::CurrentArmInterface().SetReg(1, param_1); 103 Core::CurrentArmInterface().SetReg(1, param_1);
96 Core::CurrentArmInterface().SetReg(2, param_2); 104 Core::CurrentArmInterface().SetReg(2, param_2);
97 FuncReturn(retval.raw); 105 FuncReturn(retval.raw);
@@ -100,12 +108,12 @@ void SvcWrap() {
100template <ResultCode func(u64, u64, u32, u32)> 108template <ResultCode func(u64, u64, u32, u32)>
101void SvcWrap() { 109void SvcWrap() {
102 FuncReturn( 110 FuncReturn(
103 func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); 111 func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
104} 112}
105 113
106template <ResultCode func(u32, u64, u32)> 114template <ResultCode func(u32, u64, u32)>
107void SvcWrap() { 115void SvcWrap() {
108 FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); 116 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
109} 117}
110 118
111template <ResultCode func(u64, u64, u64)> 119template <ResultCode func(u64, u64, u64)>
@@ -115,25 +123,28 @@ void SvcWrap() {
115 123
116template <ResultCode func(u32, u64, u64, u32)> 124template <ResultCode func(u32, u64, u64, u32)>
117void SvcWrap() { 125void SvcWrap() {
118 FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); 126 FuncReturn(
127 func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
119} 128}
120 129
121template <ResultCode func(u32, u64, u64)> 130template <ResultCode func(u32, u64, u64)>
122void SvcWrap() { 131void SvcWrap() {
123 FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); 132 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
124} 133}
125 134
126template <ResultCode func(u32*, u64, u64, s64)> 135template <ResultCode func(u32*, u64, u64, s64)>
127void SvcWrap() { 136void SvcWrap() {
128 u32 param_1 = 0; 137 u32 param_1 = 0;
129 ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); 138 ResultCode retval =
139 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
130 Core::CurrentArmInterface().SetReg(1, param_1); 140 Core::CurrentArmInterface().SetReg(1, param_1);
131 FuncReturn(retval.raw); 141 FuncReturn(retval.raw);
132} 142}
133 143
134template <ResultCode func(u64, u64, u32, s64)> 144template <ResultCode func(u64, u64, u32, s64)>
135void SvcWrap() { 145void SvcWrap() {
136 FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); 146 FuncReturn(
147 func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
137} 148}
138 149
139template <ResultCode func(u64*, u64, u64, u64)> 150template <ResultCode func(u64*, u64, u64, u64)>
@@ -147,9 +158,9 @@ void SvcWrap() {
147template <ResultCode func(u32*, u64, u64, u64, u32, s32)> 158template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
148void SvcWrap() { 159void SvcWrap() {
149 u32 param_1 = 0; 160 u32 param_1 = 0;
150 u32 retval = 161 u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
151 func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) 162 static_cast<s32>(Param(5)))
152 .raw; 163 .raw;
153 Core::CurrentArmInterface().SetReg(1, param_1); 164 Core::CurrentArmInterface().SetReg(1, param_1);
154 FuncReturn(retval); 165 FuncReturn(retval);
155} 166}
@@ -172,7 +183,7 @@ void SvcWrap() {
172template <ResultCode func(u32*, u64, u64, u32)> 183template <ResultCode func(u32*, u64, u64, u32)>
173void SvcWrap() { 184void SvcWrap() {
174 u32 param_1 = 0; 185 u32 param_1 = 0;
175 u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; 186 u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
176 Core::CurrentArmInterface().SetReg(1, param_1); 187 Core::CurrentArmInterface().SetReg(1, param_1);
177 FuncReturn(retval); 188 FuncReturn(retval);
178} 189}
@@ -181,22 +192,22 @@ template <ResultCode func(Handle*, u64, u32, u32)>
181void SvcWrap() { 192void SvcWrap() {
182 u32 param_1 = 0; 193 u32 param_1 = 0;
183 u32 retval = 194 u32 retval =
184 func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; 195 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
185 Core::CurrentArmInterface().SetReg(1, param_1); 196 Core::CurrentArmInterface().SetReg(1, param_1);
186 FuncReturn(retval); 197 FuncReturn(retval);
187} 198}
188 199
189template <ResultCode func(u64, u32, s32, s64)> 200template <ResultCode func(u64, u32, s32, s64)>
190void SvcWrap() { 201void SvcWrap() {
191 FuncReturn( 202 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
192 func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) 203 static_cast<s64>(Param(3)))
193 .raw); 204 .raw);
194} 205}
195 206
196template <ResultCode func(u64, u32, s32, s32)> 207template <ResultCode func(u64, u32, s32, s32)>
197void SvcWrap() { 208void SvcWrap() {
198 FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), 209 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
199 (s32)(Param(3) & 0xFFFFFFFF)) 210 static_cast<s32>(Param(3)))
200 .raw); 211 .raw);
201} 212}
202 213
@@ -226,7 +237,7 @@ void SvcWrap() {
226 237
227template <void func(s64)> 238template <void func(s64)>
228void SvcWrap() { 239void SvcWrap() {
229 func((s64)Param(0)); 240 func(static_cast<s64>(Param(0)));
230} 241}
231 242
232template <void func(u64, u64 len)> 243template <void func(u64, u64 len)>
@@ -239,4 +250,9 @@ void SvcWrap() {
239 func(Param(0), Param(1), Param(2)); 250 func(Param(0), Param(1), Param(2));
240} 251}
241 252
253template <void func(u32, u64, u64)>
254void SvcWrap() {
255 func(static_cast<u32>(Param(0)), Param(1), Param(2));
256}
257
242} // namespace Kernel 258} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 33aed8c23..352ce1725 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -183,13 +183,10 @@ void Thread::ResumeFromWait() {
183 */ 183 */
184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, 184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
185 VAddr entry_point, u64 arg) { 185 VAddr entry_point, u64 arg) {
186 memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext)); 186 context = {};
187
188 context.cpu_registers[0] = arg; 187 context.cpu_registers[0] = arg;
189 context.pc = entry_point; 188 context.pc = entry_point;
190 context.sp = stack_top; 189 context.sp = stack_top;
191 context.pstate = 0;
192 context.fpcr = 0;
193} 190}
194 191
195ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, 192ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0ecfb5af1..518161bf7 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,8 +7,10 @@
7#include <vector> 7#include <vector>
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
10#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/partition_filesystem.h" 12#include "core/file_sys/partition_filesystem.h"
13#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
13#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
@@ -19,7 +21,7 @@
19namespace Service::AOC { 21namespace Service::AOC {
20 22
21constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 23constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
22constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; 24constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
23 25
24static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 26static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
25 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 27 return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
@@ -97,14 +99,24 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
97 99
98 ctx.WriteBuffer(out); 100 ctx.WriteBuffer(out);
99 101
100 IPC::ResponseBuilder rb{ctx, 2}; 102 IPC::ResponseBuilder rb{ctx, 3};
101 rb.Push(RESULT_SUCCESS); 103 rb.Push(RESULT_SUCCESS);
104 rb.Push(count);
102} 105}
103 106
104void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { 107void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
105 IPC::ResponseBuilder rb{ctx, 4}; 108 IPC::ResponseBuilder rb{ctx, 4};
106 rb.Push(RESULT_SUCCESS); 109 rb.Push(RESULT_SUCCESS);
107 rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); 110 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
111 FileSys::PatchManager pm{title_id};
112
113 const auto res = pm.GetControlMetadata();
114 if (res.first == nullptr) {
115 rb.Push(title_id + DLC_BASE_TO_AOC_ID);
116 return;
117 }
118
119 rb.Push(res.first->GetDLCBaseTitleId());
108} 120}
109 121
110void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { 122void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index fc6067e59..7168c6a10 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -2,8 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
5#include <cstring> 6#include <cstring>
6#include <memory> 7#include <memory>
8#include <optional>
7#include <vector> 9#include <vector>
8 10
9#include <opus.h> 11#include <opus.h>
@@ -33,7 +35,8 @@ public:
33 {1, nullptr, "SetContext"}, 35 {1, nullptr, "SetContext"},
34 {2, nullptr, "DecodeInterleavedForMultiStream"}, 36 {2, nullptr, "DecodeInterleavedForMultiStream"},
35 {3, nullptr, "SetContextForMultiStream"}, 37 {3, nullptr, "SetContextForMultiStream"},
36 {4, nullptr, "Unknown4"}, 38 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
39 "DecodeInterleavedWithPerformance"},
37 {5, nullptr, "Unknown5"}, 40 {5, nullptr, "Unknown5"},
38 {6, nullptr, "Unknown6"}, 41 {6, nullptr, "Unknown6"},
39 {7, nullptr, "Unknown7"}, 42 {7, nullptr, "Unknown7"},
@@ -59,8 +62,31 @@ private:
59 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 62 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
60 } 63 }
61 64
62 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, 65 void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
63 std::vector<opus_int16>& output) { 66 u32 consumed = 0;
67 u32 sample_count = 0;
68 u64 performance = 0;
69 std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
70 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
71 performance)) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 // TODO(ogniK): Use correct error code
74 rb.Push(ResultCode(-1));
75 return;
76 }
77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 }
84
85 bool Decoder_DecodeInterleaved(
86 u32& consumed, u32& sample_count, const std::vector<u8>& input,
87 std::vector<opus_int16>& output,
88 std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
89 const auto start_time = std::chrono::high_resolution_clock::now();
64 std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 90 std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
65 if (sizeof(OpusHeader) > input.size()) 91 if (sizeof(OpusHeader) > input.size())
66 return false; 92 return false;
@@ -80,8 +106,13 @@ private:
80 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); 106 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
81 if (out_sample_count < 0) 107 if (out_sample_count < 0)
82 return false; 108 return false;
109 const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
83 sample_count = out_sample_count; 110 sample_count = out_sample_count;
84 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); 111 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
112 if (performance_time.has_value()) {
113 performance_time->get() =
114 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
115 }
85 return true; 116 return true;
86 } 117 }
87 118
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 439e62d27..e06712603 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -361,19 +361,19 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
361 return bis_factory->GetModificationLoadRoot(title_id); 361 return bis_factory->GetModificationLoadRoot(title_id);
362} 362}
363 363
364void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { 364void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
365 if (overwrite) { 365 if (overwrite) {
366 bis_factory = nullptr; 366 bis_factory = nullptr;
367 save_data_factory = nullptr; 367 save_data_factory = nullptr;
368 sdmc_factory = nullptr; 368 sdmc_factory = nullptr;
369 } 369 }
370 370
371 auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 371 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
372 FileSys::Mode::ReadWrite); 372 FileSys::Mode::ReadWrite);
373 auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 373 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
374 FileSys::Mode::ReadWrite); 374 FileSys::Mode::ReadWrite);
375 auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 375 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
376 FileSys::Mode::ReadWrite); 376 FileSys::Mode::ReadWrite);
377 377
378 if (bis_factory == nullptr) 378 if (bis_factory == nullptr)
379 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); 379 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
@@ -383,7 +383,7 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
383 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 383 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
384} 384}
385 385
386void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { 386void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
387 romfs_factory = nullptr; 387 romfs_factory = nullptr;
388 CreateFactories(vfs, false); 388 CreateFactories(vfs, false);
389 std::make_shared<FSP_LDR>()->InstallAsService(service_manager); 389 std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 53b01bb01..2df1faeb0 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -57,9 +57,9 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
57 57
58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
59// above is called. 59// above is called.
60void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); 60void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
61 61
62void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs); 62void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs);
63 63
64// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of 64// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
65// pointers and booleans. This makes using a VfsDirectory with switch services much easier and 65// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 7555bbe7d..c41ef7058 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -15,6 +15,11 @@
15#include "video_core/renderer_base.h" 15#include "video_core/renderer_base.h"
16 16
17namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
18namespace NvErrCodes {
19enum {
20 InvalidNmapHandle = -22,
21};
22}
18 23
19nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 24nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
20nvhost_as_gpu::~nvhost_as_gpu() = default; 25nvhost_as_gpu::~nvhost_as_gpu() = default;
@@ -79,14 +84,16 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
79 std::memcpy(entries.data(), input.data(), input.size()); 84 std::memcpy(entries.data(), input.data(), input.size());
80 85
81 auto& gpu = Core::System::GetInstance().GPU(); 86 auto& gpu = Core::System::GetInstance().GPU();
82
83 for (const auto& entry : entries) { 87 for (const auto& entry : entries) {
84 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 88 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
85 entry.offset, entry.nvmap_handle, entry.pages); 89 entry.offset, entry.nvmap_handle, entry.pages);
86 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; 90 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
87
88 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 91 auto object = nvmap_dev->GetObject(entry.nvmap_handle);
89 ASSERT(object); 92 if (!object) {
93 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
94 std::memcpy(output.data(), entries.data(), output.size());
95 return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
96 }
90 97
91 ASSERT(object->status == nvmap::Object::Status::Allocated); 98 ASSERT(object->status == nvmap::Object::Status::Allocated);
92 99
@@ -167,10 +174,11 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
167 auto& system_instance = Core::System::GetInstance(); 174 auto& system_instance = Core::System::GetInstance();
168 175
169 // Remove this memory region from the rasterizer cache. 176 // Remove this memory region from the rasterizer cache.
170 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
171 itr->second.size);
172
173 auto& gpu = system_instance.GPU(); 177 auto& gpu = system_instance.GPU();
178 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
179 ASSERT(cpu_addr);
180 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
181
174 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 182 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
175 183
176 buffer_mappings.erase(itr->second.offset); 184 buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index a2287cc1b..43651d8a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,6 +11,13 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14namespace NvErrCodes {
15enum {
16 OperationNotPermitted = -1,
17 InvalidValue = -22,
18};
19}
20
14nvmap::nvmap() = default; 21nvmap::nvmap() = default;
15nvmap::~nvmap() = default; 22nvmap::~nvmap() = default;
16 23
@@ -44,7 +51,11 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
44u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 51u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
45 IocCreateParams params; 52 IocCreateParams params;
46 std::memcpy(&params, input.data(), sizeof(params)); 53 std::memcpy(&params, input.data(), sizeof(params));
54 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
47 55
56 if (!params.size) {
57 return static_cast<u32>(NvErrCodes::InvalidValue);
58 }
48 // Create a new nvmap object and obtain a handle to it. 59 // Create a new nvmap object and obtain a handle to it.
49 auto object = std::make_shared<Object>(); 60 auto object = std::make_shared<Object>();
50 object->id = next_id++; 61 object->id = next_id++;
@@ -55,8 +66,6 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
55 u32 handle = next_handle++; 66 u32 handle = next_handle++;
56 handles[handle] = std::move(object); 67 handles[handle] = std::move(object);
57 68
58 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
59
60 params.handle = handle; 69 params.handle = handle;
61 70
62 std::memcpy(output.data(), &params, sizeof(params)); 71 std::memcpy(output.data(), &params, sizeof(params));
@@ -66,9 +75,29 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
66u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 75u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
67 IocAllocParams params; 76 IocAllocParams params;
68 std::memcpy(&params, input.data(), sizeof(params)); 77 std::memcpy(&params, input.data(), sizeof(params));
78 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
79
80 if (!params.handle) {
81 return static_cast<u32>(NvErrCodes::InvalidValue);
82 }
83
84 if ((params.align - 1) & params.align) {
85 return static_cast<u32>(NvErrCodes::InvalidValue);
86 }
87
88 const u32 min_alignment = 0x1000;
89 if (params.align < min_alignment) {
90 params.align = min_alignment;
91 }
69 92
70 auto object = GetObject(params.handle); 93 auto object = GetObject(params.handle);
71 ASSERT(object); 94 if (!object) {
95 return static_cast<u32>(NvErrCodes::InvalidValue);
96 }
97
98 if (object->status == Object::Status::Allocated) {
99 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
100 }
72 101
73 object->flags = params.flags; 102 object->flags = params.flags;
74 object->align = params.align; 103 object->align = params.align;
@@ -76,8 +105,6 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
76 object->addr = params.addr; 105 object->addr = params.addr;
77 object->status = Object::Status::Allocated; 106 object->status = Object::Status::Allocated;
78 107
79 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
80
81 std::memcpy(output.data(), &params, sizeof(params)); 108 std::memcpy(output.data(), &params, sizeof(params));
82 return 0; 109 return 0;
83} 110}
@@ -88,8 +115,14 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
88 115
89 LOG_WARNING(Service_NVDRV, "called"); 116 LOG_WARNING(Service_NVDRV, "called");
90 117
118 if (!params.handle) {
119 return static_cast<u32>(NvErrCodes::InvalidValue);
120 }
121
91 auto object = GetObject(params.handle); 122 auto object = GetObject(params.handle);
92 ASSERT(object); 123 if (!object) {
124 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
125 }
93 126
94 params.id = object->id; 127 params.id = object->id;
95 128
@@ -105,7 +138,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
105 138
106 auto itr = std::find_if(handles.begin(), handles.end(), 139 auto itr = std::find_if(handles.begin(), handles.end(),
107 [&](const auto& entry) { return entry.second->id == params.id; }); 140 [&](const auto& entry) { return entry.second->id == params.id; });
108 ASSERT(itr != handles.end()); 141 if (itr == handles.end()) {
142 return static_cast<u32>(NvErrCodes::InvalidValue);
143 }
144
145 auto& object = itr->second;
146 if (object->status != Object::Status::Allocated) {
147 return static_cast<u32>(NvErrCodes::InvalidValue);
148 }
109 149
110 itr->second->refcount++; 150 itr->second->refcount++;
111 151
@@ -125,8 +165,13 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
125 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); 165 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
126 166
127 auto object = GetObject(params.handle); 167 auto object = GetObject(params.handle);
128 ASSERT(object); 168 if (!object) {
129 ASSERT(object->status == Object::Status::Allocated); 169 return static_cast<u32>(NvErrCodes::InvalidValue);
170 }
171
172 if (object->status != Object::Status::Allocated) {
173 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
174 }
130 175
131 switch (static_cast<ParamTypes>(params.param)) { 176 switch (static_cast<ParamTypes>(params.param)) {
132 case ParamTypes::Size: 177 case ParamTypes::Size:
@@ -163,9 +208,12 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
163 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 208 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
164 209
165 auto itr = handles.find(params.handle); 210 auto itr = handles.find(params.handle);
166 ASSERT(itr != handles.end()); 211 if (itr == handles.end()) {
167 212 return static_cast<u32>(NvErrCodes::InvalidValue);
168 ASSERT(itr->second->refcount > 0); 213 }
214 if (!itr->second->refcount) {
215 return static_cast<u32>(NvErrCodes::InvalidValue);
216 }
169 217
170 itr->second->refcount--; 218 itr->second->refcount--;
171 219
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 62f049660..a225cb4cb 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -197,7 +197,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
197// Module interface 197// Module interface
198 198
199/// Initialize ServiceManager 199/// Initialize ServiceManager
200void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { 200void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {
201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
202 // here and pass it into the respective InstallInterfaces functions. 202 // here and pass it into the respective InstallInterfaces functions.
203 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); 203 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -220,7 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
220 EUPLD::InstallInterfaces(*sm); 220 EUPLD::InstallInterfaces(*sm);
221 Fatal::InstallInterfaces(*sm); 221 Fatal::InstallInterfaces(*sm);
222 FGM::InstallInterfaces(*sm); 222 FGM::InstallInterfaces(*sm);
223 FileSystem::InstallInterfaces(*sm, rfs); 223 FileSystem::InstallInterfaces(*sm, vfs);
224 Friend::InstallInterfaces(*sm); 224 Friend::InstallInterfaces(*sm);
225 GRC::InstallInterfaces(*sm); 225 GRC::InstallInterfaces(*sm);
226 HID::InstallInterfaces(*sm); 226 HID::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2fc57a82e..98483ecf1 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -180,8 +180,7 @@ private:
180}; 180};
181 181
182/// Initialize ServiceManager 182/// Initialize ServiceManager
183void Init(std::shared_ptr<SM::ServiceManager>& sm, 183void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);
184 const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
185 184
186/// Shutdown ServiceManager 185/// Shutdown ServiceManager
187void Shutdown(); 186void Shutdown();
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 951fd8257..8518dddcb 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -139,14 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
141 const FileSys::VirtualFile module_file = dir->GetFile(module); 141 const FileSys::VirtualFile module_file = dir->GetFile(module);
142 if (module_file != nullptr) { 142 if (module_file == nullptr) {
143 const VAddr load_addr = next_load_addr; 143 continue;
144 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr,
145 std::strcmp(module, "rtld") == 0, pm);
146 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
147 // Register module with GDBStub
148 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
149 } 144 }
145
146 const VAddr load_addr = next_load_addr;
147 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
148 const auto tentative_next_load_addr =
149 AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
150 if (!tentative_next_load_addr) {
151 return ResultStatus::ErrorLoadingNSO;
152 }
153
154 next_load_addr = *tentative_next_load_addr;
155 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
156 // Register module with GDBStub
157 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
150 } 158 }
151 159
152 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); 160 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index e67b49fc9..6057c7f26 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,16 +9,11 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/vm_manager.h" 13#include "core/hle/kernel/vm_manager.h"
16#include "core/loader/elf.h" 14#include "core/loader/elf.h"
17#include "core/memory.h" 15#include "core/memory.h"
18 16
19using Kernel::CodeSet;
20using Kernel::SharedPtr;
21
22//////////////////////////////////////////////////////////////////////////////////////////////////// 17////////////////////////////////////////////////////////////////////////////////////////////////////
23// ELF Header Constants 18// ELF Header Constants
24 19
@@ -211,7 +206,7 @@ public:
211 u32 GetFlags() const { 206 u32 GetFlags() const {
212 return (u32)(header->e_flags); 207 return (u32)(header->e_flags);
213 } 208 }
214 SharedPtr<CodeSet> LoadInto(VAddr vaddr); 209 Kernel::CodeSet LoadInto(VAddr vaddr);
215 210
216 int GetNumSegments() const { 211 int GetNumSegments() const {
217 return (int)(header->e_phnum); 212 return (int)(header->e_phnum);
@@ -274,7 +269,7 @@ const char* ElfReader::GetSectionName(int section) const {
274 return nullptr; 269 return nullptr;
275} 270}
276 271
277SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { 272Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
278 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 273 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
279 274
280 // Should we relocate? 275 // Should we relocate?
@@ -302,8 +297,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
302 std::vector<u8> program_image(total_image_size); 297 std::vector<u8> program_image(total_image_size);
303 std::size_t current_image_position = 0; 298 std::size_t current_image_position = 0;
304 299
305 auto& kernel = Core::System::GetInstance().Kernel(); 300 Kernel::CodeSet codeset;
306 SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
307 301
308 for (unsigned int i = 0; i < header->e_phnum; ++i) { 302 for (unsigned int i = 0; i < header->e_phnum; ++i) {
309 const Elf32_Phdr* p = &segments[i]; 303 const Elf32_Phdr* p = &segments[i];
@@ -311,14 +305,14 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
311 p->p_vaddr, p->p_filesz, p->p_memsz); 305 p->p_vaddr, p->p_filesz, p->p_memsz);
312 306
313 if (p->p_type == PT_LOAD) { 307 if (p->p_type == PT_LOAD) {
314 CodeSet::Segment* codeset_segment; 308 Kernel::CodeSet::Segment* codeset_segment;
315 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 309 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
316 if (permission_flags == (PF_R | PF_X)) { 310 if (permission_flags == (PF_R | PF_X)) {
317 codeset_segment = &codeset->CodeSegment(); 311 codeset_segment = &codeset.CodeSegment();
318 } else if (permission_flags == (PF_R)) { 312 } else if (permission_flags == (PF_R)) {
319 codeset_segment = &codeset->RODataSegment(); 313 codeset_segment = &codeset.RODataSegment();
320 } else if (permission_flags == (PF_R | PF_W)) { 314 } else if (permission_flags == (PF_R | PF_W)) {
321 codeset_segment = &codeset->DataSegment(); 315 codeset_segment = &codeset.DataSegment();
322 } else { 316 } else {
323 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 317 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
324 p->p_flags); 318 p->p_flags);
@@ -345,8 +339,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
345 } 339 }
346 } 340 }
347 341
348 codeset->entrypoint = base_addr + header->e_entry; 342 codeset.entrypoint = base_addr + header->e_entry;
349 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 343 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
350 344
351 LOG_DEBUG(Loader, "Done loading."); 345 LOG_DEBUG(Loader, "Done loading.");
352 346
@@ -397,11 +391,11 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
397 391
398 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 392 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
399 ElfReader elf_reader(&buffer[0]); 393 ElfReader elf_reader(&buffer[0]);
400 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address); 394 Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
401 codeset->name = file->GetName(); 395 const VAddr entry_point = codeset.entrypoint;
402 396
403 process.LoadModule(codeset, codeset->entrypoint); 397 process.LoadModule(std::move(codeset), entry_point);
404 process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); 398 process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
405 399
406 is_loaded = true; 400 is_loaded = true;
407 return ResultStatus::Success; 401 return ResultStatus::Success;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 91659ec17..9cd0b0ccd 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 93 return "unknown";
94} 94}
95 95
96constexpr std::array<const char*, 59> RESULT_MESSAGES{ 96constexpr std::array<const char*, 60> RESULT_MESSAGES{
97 "The operation completed successfully.", 97 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 98 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 99 "The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
128 "The RomFS could not be found.", 128 "The RomFS could not be found.",
129 "The ELF file has incorrect size as determined by the header.", 129 "The ELF file has incorrect size as determined by the header.",
130 "There was a general error loading the NRO into emulated memory.", 130 "There was a general error loading the NRO into emulated memory.",
131 "There was a general error loading the NSO into emulated memory.",
131 "There is no icon available.", 132 "There is no icon available.",
132 "There is no control data available.", 133 "There is no control data available.",
133 "The NAX file has a bad header.", 134 "The NAX file has a bad header.",
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 0e0333db5..e562b3a04 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -90,6 +90,7 @@ enum class ResultStatus : u16 {
90 ErrorNoRomFS, 90 ErrorNoRomFS,
91 ErrorIncorrectELFFileSize, 91 ErrorIncorrectELFFileSize,
92 ErrorLoadingNRO, 92 ErrorLoadingNRO,
93 ErrorLoadingNSO,
93 ErrorNoIcon, 94 ErrorNoIcon,
94 ErrorNoControl, 95 ErrorNoControl,
95 ErrorBadNAXHeader, 96 ErrorBadNAXHeader,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 25dd3f04e..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,7 +14,6 @@
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
20#include "core/loader/nro.h" 19#include "core/loader/nro.h"
@@ -128,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
128 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
129} 128}
130 129
131bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { 130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
132 // Read NSO header 131 // Read NSO header
133 NroHeader nro_header{}; 132 NroHeader nro_header{};
134 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { 133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
135 return {}; 134 return {};
136 } 135 }
137 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -139,22 +138,21 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
139 } 138 }
140 139
141 // Build program image 140 // Build program image
142 auto& kernel = Core::System::GetInstance().Kernel(); 141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
143 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
144 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
145 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 142 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
146 return {}; 143 return {};
147 } 144 }
148 145
146 Kernel::CodeSet codeset;
149 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { 147 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
150 codeset->segments[i].addr = nro_header.segments[i].offset; 148 codeset.segments[i].addr = nro_header.segments[i].offset;
151 codeset->segments[i].offset = nro_header.segments[i].offset; 149 codeset.segments[i].offset = nro_header.segments[i].offset;
152 codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); 150 codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
153 } 151 }
154 152
155 if (!Settings::values.program_args.empty()) { 153 if (!Settings::values.program_args.empty()) {
156 const auto arg_data = Settings::values.program_args; 154 const auto arg_data = Settings::values.program_args;
157 codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; 155 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
158 NSOArgumentHeader args_header{ 156 NSOArgumentHeader args_header{
159 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; 157 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
160 const auto end_offset = program_image.size(); 158 const auto end_offset = program_image.size();
@@ -176,16 +174,15 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
176 // Resize program image to include .bss section and page align each section 174 // Resize program image to include .bss section and page align each section
177 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 175 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
178 } 176 }
179 codeset->DataSegment().size += bss_size; 177 codeset.DataSegment().size += bss_size;
180 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 178 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
181 179
182 // Load codeset for current process 180 // Load codeset for current process
183 codeset->name = file->GetName(); 181 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
184 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
185 Core::CurrentProcess()->LoadModule(codeset, load_base);
186 183
187 // Register module with GDBStub 184 // Register module with GDBStub
188 GDBStub::RegisterModule(codeset->name, load_base, load_base); 185 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
189 186
190 return true; 187 return true;
191} 188}
@@ -198,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
198 // Load NRO 195 // Load NRO
199 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
200 197
201 if (!LoadNro(file, base_address)) { 198 if (!LoadNro(*file, base_address)) {
202 return ResultStatus::ErrorLoadingNRO; 199 return ResultStatus::ErrorLoadingNRO;
203 } 200 }
204 201
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04b46119a..50ee5a78a 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -41,7 +41,7 @@ public:
41 bool IsRomFSUpdatable() const override; 41 bool IsRomFSUpdatable() const override;
42 42
43private: 43private:
44 bool LoadNro(FileSys::VirtualFile file, VAddr load_base); 44 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 45
46 std::vector<u8> icon_data; 46 std::vector<u8> icon_data;
47 std::unique_ptr<FileSys::NACP> nacp; 47 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 28c6dd9b7..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -12,7 +12,6 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/gdbstub/gdbstub.h" 14#include "core/gdbstub/gdbstub.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/vm_manager.h" 16#include "core/hle/kernel/vm_manager.h"
18#include "core/loader/nso.h" 17#include "core/loader/nso.h"
@@ -94,42 +93,38 @@ static constexpr u32 PageAlignSize(u32 size) {
94 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
95} 94}
96 95
97VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
98 bool should_pass_arguments, 97 bool should_pass_arguments,
99 boost::optional<FileSys::PatchManager> pm) { 98 std::optional<FileSys::PatchManager> pm) {
100 if (file == nullptr) 99 if (file.GetSize() < sizeof(NsoHeader))
101 return {};
102
103 if (file->GetSize() < sizeof(NsoHeader))
104 return {}; 100 return {};
105 101
106 NsoHeader nso_header{}; 102 NsoHeader nso_header{};
107 if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) 103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
108 return {}; 104 return {};
109 105
110 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
111 return {}; 107 return {};
112 108
113 // Build program image 109 // Build program image
114 auto& kernel = Core::System::GetInstance().Kernel(); 110 Kernel::CodeSet codeset;
115 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
116 std::vector<u8> program_image; 111 std::vector<u8> program_image;
117 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 112 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
118 std::vector<u8> data = 113 std::vector<u8> data =
119 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 114 file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
120 if (nso_header.IsSegmentCompressed(i)) { 115 if (nso_header.IsSegmentCompressed(i)) {
121 data = DecompressSegment(data, nso_header.segments[i]); 116 data = DecompressSegment(data, nso_header.segments[i]);
122 } 117 }
123 program_image.resize(nso_header.segments[i].location); 118 program_image.resize(nso_header.segments[i].location);
124 program_image.insert(program_image.end(), data.begin(), data.end()); 119 program_image.insert(program_image.end(), data.begin(), data.end());
125 codeset->segments[i].addr = nso_header.segments[i].location; 120 codeset.segments[i].addr = nso_header.segments[i].location;
126 codeset->segments[i].offset = nso_header.segments[i].location; 121 codeset.segments[i].offset = nso_header.segments[i].location;
127 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); 122 codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
128 } 123 }
129 124
130 if (should_pass_arguments && !Settings::values.program_args.empty()) { 125 if (should_pass_arguments && !Settings::values.program_args.empty()) {
131 const auto arg_data = Settings::values.program_args; 126 const auto arg_data = Settings::values.program_args;
132 codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; 127 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
133 NSOArgumentHeader args_header{ 128 NSOArgumentHeader args_header{
134 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; 129 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
135 const auto end_offset = program_image.size(); 130 const auto end_offset = program_image.size();
@@ -154,12 +149,12 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
154 // Resize program image to include .bss section and page align each section 149 // Resize program image to include .bss section and page align each section
155 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 150 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
156 } 151 }
157 codeset->DataSegment().size += bss_size; 152 codeset.DataSegment().size += bss_size;
158 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; 153 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
159 program_image.resize(image_size); 154 program_image.resize(image_size);
160 155
161 // Apply patches if necessary 156 // Apply patches if necessary
162 if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && pm->HasNSOPatch(nso_header.build_id)) {
163 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
164 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
165 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
@@ -170,12 +165,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
170 } 165 }
171 166
172 // Load codeset for current process 167 // Load codeset for current process
173 codeset->name = file->GetName(); 168 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
174 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 169 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
175 Core::CurrentProcess()->LoadModule(codeset, load_base);
176 170
177 // Register module with GDBStub 171 // Register module with GDBStub
178 GDBStub::RegisterModule(codeset->name, load_base, load_base); 172 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
179 173
180 return load_base + image_size; 174 return load_base + image_size;
181} 175}
@@ -187,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
187 181
188 // Load module 182 // Load module
189 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 183 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
190 LoadModule(file, base_address, true); 184 if (!LoadModule(*file, base_address, true)) {
185 return ResultStatus::ErrorLoadingNSO;
186 }
191 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 187 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
192 188
193 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 189 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 70ab3b718..433306139 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/file_sys/patch_manager.h" 9#include "core/file_sys/patch_manager.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
@@ -36,8 +37,9 @@ public:
36 return IdentifyType(file); 37 return IdentifyType(file);
37 } 38 }
38 39
39 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, 40 static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
40 boost::optional<FileSys::PatchManager> pm = boost::none); 41 bool should_pass_arguments,
42 std::optional<FileSys::PatchManager> pm = {});
41 43
42 ResultStatus Load(Kernel::Process& process) override; 44 ResultStatus Load(Kernel::Process& process) override;
43}; 45};
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 912e785b9..597b279b9 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -62,14 +62,16 @@ void Fermi2D::HandleSurfaceCopy() {
62 u8* dst_buffer = Memory::GetPointer(dest_cpu); 62 u8* dst_buffer = Memory::GetPointer(dest_cpu);
63 if (!regs.src.linear && regs.dst.linear) { 63 if (!regs.src.linear && regs.dst.linear) {
64 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 64 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
65 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 65 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
66 dst_bytes_per_pixel, src_buffer, dst_buffer, true, 66 src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
67 regs.src.BlockHeight()); 67 dst_buffer, true, regs.src.BlockHeight(),
68 regs.src.BlockDepth());
68 } else { 69 } else {
69 // If the input is linear and the output is tiled, swizzle the input and copy it over. 70 // If the input is linear and the output is tiled, swizzle the input and copy it over.
70 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 71 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
71 dst_bytes_per_pixel, dst_buffer, src_buffer, false, 72 src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
72 regs.dst.BlockHeight()); 73 src_buffer, false, regs.dst.BlockHeight(),
74 regs.dst.BlockDepth());
73 } 75 }
74 } 76 }
75} 77}
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 81d15c62a..2a6e8bbbb 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -36,9 +36,9 @@ public:
36 RenderTargetFormat format; 36 RenderTargetFormat format;
37 BitField<0, 1, u32> linear; 37 BitField<0, 1, u32> linear;
38 union { 38 union {
39 BitField<0, 4, u32> block_depth; 39 BitField<0, 4, u32> block_width;
40 BitField<4, 4, u32> block_height; 40 BitField<4, 4, u32> block_height;
41 BitField<8, 4, u32> block_width; 41 BitField<8, 4, u32> block_depth;
42 }; 42 };
43 u32 depth; 43 u32 depth;
44 u32 layer; 44 u32 layer;
@@ -53,10 +53,20 @@ public:
53 address_low); 53 address_low);
54 } 54 }
55 55
56 u32 BlockWidth() const {
57 // The block width is stored in log2 format.
58 return 1 << block_width;
59 }
60
56 u32 BlockHeight() const { 61 u32 BlockHeight() const {
57 // The block height is stored in log2 format. 62 // The block height is stored in log2 format.
58 return 1 << block_height; 63 return 1 << block_height;
59 } 64 }
65
66 u32 BlockDepth() const {
67 // The block depth is stored in log2 format.
68 return 1 << block_depth;
69 }
60 }; 70 };
61 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); 71 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
62 72
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 20e1884da..c8d1b6478 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -347,6 +347,16 @@ public:
347 DecrWrap = 8, 347 DecrWrap = 8,
348 }; 348 };
349 349
350 enum class MemoryLayout : u32 {
351 Linear = 0,
352 BlockLinear = 1,
353 };
354
355 enum class InvMemoryLayout : u32 {
356 BlockLinear = 0,
357 Linear = 1,
358 };
359
350 struct Cull { 360 struct Cull {
351 enum class FrontFace : u32 { 361 enum class FrontFace : u32 {
352 ClockWise = 0x0900, 362 ClockWise = 0x0900,
@@ -432,7 +442,12 @@ public:
432 u32 width; 442 u32 width;
433 u32 height; 443 u32 height;
434 Tegra::RenderTargetFormat format; 444 Tegra::RenderTargetFormat format;
435 u32 block_dimensions; 445 union {
446 BitField<0, 3, u32> block_width;
447 BitField<4, 3, u32> block_height;
448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout;
436 u32 array_mode; 451 u32 array_mode;
437 u32 layer_stride; 452 u32 layer_stride;
438 u32 base_layer; 453 u32 base_layer;
@@ -562,7 +577,12 @@ public:
562 u32 address_high; 577 u32 address_high;
563 u32 address_low; 578 u32 address_low;
564 Tegra::DepthFormat format; 579 Tegra::DepthFormat format;
565 u32 block_dimensions; 580 union {
581 BitField<0, 4, u32> block_width;
582 BitField<4, 4, u32> block_height;
583 BitField<8, 4, u32> block_depth;
584 BitField<20, 1, InvMemoryLayout> type;
585 } memory_layout;
566 u32 layer_stride; 586 u32 layer_stride;
567 587
568 GPUVAddr Address() const { 588 GPUVAddr Address() const {
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index aa7481b8c..bf2a21bb6 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -68,12 +68,14 @@ void MaxwellDMA::HandleCopy() {
68 68
69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
70 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 70 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer, 71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y,
72 dst_buffer, true, regs.src_params.BlockHeight()); 72 regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true,
73 regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
73 } else { 74 } else {
74 // If the input is linear and the output is tiled, swizzle the input and copy it over. 75 // If the input is linear and the output is tiled, swizzle the input and copy it over.
75 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer, 76 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y,
76 src_buffer, false, regs.dst_params.BlockHeight()); 77 regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false,
78 regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
77 } 79 }
78} 80}
79 81
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 311ccb616..df19e02e2 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -43,6 +43,10 @@ public:
43 u32 BlockHeight() const { 43 u32 BlockHeight() const {
44 return 1 << block_height; 44 return 1 << block_height;
45 } 45 }
46
47 u32 BlockDepth() const {
48 return 1 << block_depth;
49 }
46 }; 50 };
47 51
48 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); 52 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 550ab1148..f356f9a03 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -214,6 +214,18 @@ enum class IMinMaxExchange : u64 {
214 XHi = 3, 214 XHi = 3,
215}; 215};
216 216
217enum class VmadType : u64 {
218 Size16_Low = 0,
219 Size16_High = 1,
220 Size32 = 2,
221 Invalid = 3,
222};
223
224enum class VmadShr : u64 {
225 Shr7 = 1,
226 Shr15 = 2,
227};
228
217enum class XmadMode : u64 { 229enum class XmadMode : u64 {
218 None = 0, 230 None = 0,
219 CLo = 1, 231 CLo = 1,
@@ -255,7 +267,7 @@ enum class ControlCode : u64 {
255 GTU = 12, 267 GTU = 12,
256 NEU = 13, 268 NEU = 13,
257 GEU = 14, 269 GEU = 14,
258 // 270 T = 15,
259 OFF = 16, 271 OFF = 16,
260 LO = 17, 272 LO = 17,
261 SFF = 18, 273 SFF = 18,
@@ -452,6 +464,7 @@ union Instruction {
452 BitField<48, 16, u64> opcode; 464 BitField<48, 16, u64> opcode;
453 465
454 union { 466 union {
467 BitField<20, 16, u64> imm20_16;
455 BitField<20, 19, u64> imm20_19; 468 BitField<20, 19, u64> imm20_19;
456 BitField<20, 32, s64> imm20_32; 469 BitField<20, 32, s64> imm20_32;
457 BitField<45, 1, u64> negate_b; 470 BitField<45, 1, u64> negate_b;
@@ -493,6 +506,10 @@ union Instruction {
493 } 506 }
494 } lop3; 507 } lop3;
495 508
509 u16 GetImm20_16() const {
510 return static_cast<u16>(imm20_16);
511 }
512
496 u32 GetImm20_19() const { 513 u32 GetImm20_19() const {
497 u32 imm{static_cast<u32>(imm20_19)}; 514 u32 imm{static_cast<u32>(imm20_19)};
498 imm <<= 12; 515 imm <<= 12;
@@ -1017,6 +1034,23 @@ union Instruction {
1017 } isberd; 1034 } isberd;
1018 1035
1019 union { 1036 union {
1037 BitField<48, 1, u64> signed_a;
1038 BitField<38, 1, u64> is_byte_chunk_a;
1039 BitField<36, 2, VmadType> type_a;
1040 BitField<36, 2, u64> byte_height_a;
1041
1042 BitField<49, 1, u64> signed_b;
1043 BitField<50, 1, u64> use_register_b;
1044 BitField<30, 1, u64> is_byte_chunk_b;
1045 BitField<28, 2, VmadType> type_b;
1046 BitField<28, 2, u64> byte_height_b;
1047
1048 BitField<51, 2, VmadShr> shr;
1049 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1050 BitField<47, 1, u64> cc;
1051 } vmad;
1052
1053 union {
1020 BitField<20, 16, u64> imm20_16; 1054 BitField<20, 16, u64> imm20_16;
1021 BitField<36, 1, u64> product_shift_left; 1055 BitField<36, 1, u64> product_shift_left;
1022 BitField<37, 1, u64> merge_37; 1056 BitField<37, 1, u64> merge_37;
@@ -1083,6 +1117,7 @@ public:
1083 IPA, 1117 IPA,
1084 OUT_R, // Emit vertex/primitive 1118 OUT_R, // Emit vertex/primitive
1085 ISBERD, 1119 ISBERD,
1120 VMAD,
1086 FFMA_IMM, // Fused Multiply and Add 1121 FFMA_IMM, // Fused Multiply and Add
1087 FFMA_CR, 1122 FFMA_CR,
1088 FFMA_RC, 1123 FFMA_RC,
@@ -1320,6 +1355,7 @@ private:
1320 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 1355 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
1321 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), 1356 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1322 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), 1357 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1358 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1323 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1359 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1324 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1360 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1325 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1361 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84582c777..8d5f277e2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -286,7 +286,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
287 287
288 // Bind the buffer 288 // Bind the buffer
289 glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); 289 glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
290 offset, static_cast<GLsizeiptr>(sizeof(ubo)));
290 291
291 Shader shader{shader_cache.GetStageProgram(program)}; 292 Shader shader{shader_cache.GetStageProgram(program)};
292 293
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 56ff83eff..801d45144 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -45,7 +45,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
45 SurfaceParams params{}; 45 SurfaceParams params{};
46 params.addr = TryGetCpuAddr(config.tic.Address()); 46 params.addr = TryGetCpuAddr(config.tic.Address());
47 params.is_tiled = config.tic.IsTiled(); 47 params.is_tiled = config.tic.IsTiled();
48 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
48 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 49 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
50 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
49 params.pixel_format = 51 params.pixel_format =
50 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); 52 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value());
51 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 53 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
@@ -97,8 +99,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
97 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 99 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
98 SurfaceParams params{}; 100 SurfaceParams params{};
99 params.addr = TryGetCpuAddr(config.Address()); 101 params.addr = TryGetCpuAddr(config.Address());
100 params.is_tiled = true; 102 params.is_tiled =
101 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 103 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
104 params.block_width = 1 << config.memory_layout.block_width;
105 params.block_height = 1 << config.memory_layout.block_height;
106 params.block_depth = 1 << config.memory_layout.block_depth;
102 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 107 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
103 params.component_type = ComponentTypeFromRenderTarget(config.format); 108 params.component_type = ComponentTypeFromRenderTarget(config.format);
104 params.type = GetFormatType(params.pixel_format); 109 params.type = GetFormatType(params.pixel_format);
@@ -120,13 +125,16 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
120 return params; 125 return params;
121} 126}
122 127
123/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 128/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
124 Tegra::GPUVAddr zeta_address, 129 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
125 Tegra::DepthFormat format) { 130 u32 block_width, u32 block_height, u32 block_depth,
131 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
126 SurfaceParams params{}; 132 SurfaceParams params{};
127 params.addr = TryGetCpuAddr(zeta_address); 133 params.addr = TryGetCpuAddr(zeta_address);
128 params.is_tiled = true; 134 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
129 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 135 params.block_width = 1 << std::min(block_width, 5U);
136 params.block_height = 1 << std::min(block_height, 5U);
137 params.block_depth = 1 << std::min(block_depth, 5U);
130 params.pixel_format = PixelFormatFromDepthFormat(format); 138 params.pixel_format = PixelFormatFromDepthFormat(format);
131 params.component_type = ComponentTypeFromDepthFormat(format); 139 params.component_type = ComponentTypeFromDepthFormat(format);
132 params.type = GetFormatType(params.pixel_format); 140 params.type = GetFormatType(params.pixel_format);
@@ -148,7 +156,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
148 SurfaceParams params{}; 156 SurfaceParams params{};
149 params.addr = TryGetCpuAddr(config.Address()); 157 params.addr = TryGetCpuAddr(config.Address());
150 params.is_tiled = !config.linear; 158 params.is_tiled = !config.linear;
151 params.block_height = params.is_tiled ? config.BlockHeight() : 0, 159 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
160 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
161 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
152 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 162 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
153 params.component_type = ComponentTypeFromRenderTarget(config.format); 163 params.component_type = ComponentTypeFromRenderTarget(config.format);
154 params.type = GetFormatType(params.pixel_format); 164 params.type = GetFormatType(params.pixel_format);
@@ -221,6 +231,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
221 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 231 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
222 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 232 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
223 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 233 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
234 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
235 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
224 236
225 // Depth formats 237 // Depth formats
226 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 238 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -267,7 +279,9 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
267static bool IsPixelFormatASTC(PixelFormat format) { 279static bool IsPixelFormatASTC(PixelFormat format) {
268 switch (format) { 280 switch (format) {
269 case PixelFormat::ASTC_2D_4X4: 281 case PixelFormat::ASTC_2D_4X4:
282 case PixelFormat::ASTC_2D_5X4:
270 case PixelFormat::ASTC_2D_8X8: 283 case PixelFormat::ASTC_2D_8X8:
284 case PixelFormat::ASTC_2D_8X5:
271 return true; 285 return true;
272 default: 286 default:
273 return false; 287 return false;
@@ -278,8 +292,12 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
278 switch (format) { 292 switch (format) {
279 case PixelFormat::ASTC_2D_4X4: 293 case PixelFormat::ASTC_2D_4X4:
280 return {4, 4}; 294 return {4, 4};
295 case PixelFormat::ASTC_2D_5X4:
296 return {5, 4};
281 case PixelFormat::ASTC_2D_8X8: 297 case PixelFormat::ASTC_2D_8X8:
282 return {8, 8}; 298 return {8, 8};
299 case PixelFormat::ASTC_2D_8X5:
300 return {8, 5};
283 default: 301 default:
284 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); 302 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
285 UNREACHABLE(); 303 UNREACHABLE();
@@ -313,8 +331,8 @@ static bool IsFormatBCn(PixelFormat format) {
313} 331}
314 332
315template <bool morton_to_gl, PixelFormat format> 333template <bool morton_to_gl, PixelFormat format>
316void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, 334void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
317 VAddr addr) { 335 std::size_t gl_buffer_size, VAddr addr) {
318 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 336 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
319 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 337 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
320 338
@@ -323,7 +341,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::si
323 // pixel values. 341 // pixel values.
324 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 342 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
325 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 343 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
326 addr, tile_size, bytes_per_pixel, stride, height, block_height); 344 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
327 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 345 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
328 memcpy(gl_buffer, data.data(), size_to_copy); 346 memcpy(gl_buffer, data.data(), size_to_copy);
329 } else { 347 } else {
@@ -335,7 +353,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::si
335 } 353 }
336} 354}
337 355
338static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 356static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
339 SurfaceParams::MaxPixelFormat> 357 SurfaceParams::MaxPixelFormat>
340 morton_to_gl_fns = { 358 morton_to_gl_fns = {
341 // clang-format off 359 // clang-format off
@@ -385,6 +403,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
385 MortonCopy<true, PixelFormat::RG32UI>, 403 MortonCopy<true, PixelFormat::RG32UI>,
386 MortonCopy<true, PixelFormat::R32UI>, 404 MortonCopy<true, PixelFormat::R32UI>,
387 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 405 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
406 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
407 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
388 MortonCopy<true, PixelFormat::Z32F>, 408 MortonCopy<true, PixelFormat::Z32F>,
389 MortonCopy<true, PixelFormat::Z16>, 409 MortonCopy<true, PixelFormat::Z16>,
390 MortonCopy<true, PixelFormat::Z24S8>, 410 MortonCopy<true, PixelFormat::Z24S8>,
@@ -393,7 +413,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
393 // clang-format on 413 // clang-format on
394}; 414};
395 415
396static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 416static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
397 SurfaceParams::MaxPixelFormat> 417 SurfaceParams::MaxPixelFormat>
398 gl_to_morton_fns = { 418 gl_to_morton_fns = {
399 // clang-format off 419 // clang-format off
@@ -445,6 +465,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
445 MortonCopy<false, PixelFormat::RG32UI>, 465 MortonCopy<false, PixelFormat::RG32UI>,
446 MortonCopy<false, PixelFormat::R32UI>, 466 MortonCopy<false, PixelFormat::R32UI>,
447 nullptr, 467 nullptr,
468 nullptr,
469 nullptr,
448 MortonCopy<false, PixelFormat::Z32F>, 470 MortonCopy<false, PixelFormat::Z32F>,
449 MortonCopy<false, PixelFormat::Z16>, 471 MortonCopy<false, PixelFormat::Z16>,
450 MortonCopy<false, PixelFormat::Z24S8>, 472 MortonCopy<false, PixelFormat::Z24S8>,
@@ -780,7 +802,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
780 u32 width, u32 height) { 802 u32 width, u32 height) {
781 switch (pixel_format) { 803 switch (pixel_format) {
782 case PixelFormat::ASTC_2D_4X4: 804 case PixelFormat::ASTC_2D_4X4:
783 case PixelFormat::ASTC_2D_8X8: { 805 case PixelFormat::ASTC_2D_8X8:
806 case PixelFormat::ASTC_2D_8X5:
807 case PixelFormat::ASTC_2D_5X4: {
784 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 808 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
785 u32 block_width{}; 809 u32 block_width{};
786 u32 block_height{}; 810 u32 block_height{};
@@ -817,31 +841,23 @@ void CachedSurface::LoadGLBuffer() {
817 841
818 if (params.is_tiled) { 842 if (params.is_tiled) {
819 gl_buffer.resize(total_size); 843 gl_buffer.resize(total_size);
844 u32 depth = params.depth;
845 u32 block_depth = params.block_depth;
820 846
821 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do 847 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
822 // this for 3D textures, etc. 848 params.block_width, static_cast<u32>(params.target));
823 switch (params.target) { 849
824 case SurfaceParams::SurfaceTarget::Texture2D: 850 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
825 // Pass impl. to the fallback code below 851 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
826 break; 852 depth = 1U;
827 case SurfaceParams::SurfaceTarget::Texture2DArray: 853 block_depth = 1U;
828 case SurfaceParams::SurfaceTarget::TextureCubemap:
829 for (std::size_t index = 0; index < params.depth; ++index) {
830 const std::size_t offset{index * copy_size};
831 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
832 params.width, params.block_height, params.height, gl_buffer.data() + offset,
833 copy_size, params.addr + offset);
834 }
835 break;
836 default:
837 LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
838 static_cast<u32>(params.target));
839 UNREACHABLE();
840 } 854 }
841 855
856 const std::size_t size = copy_size * depth;
857
842 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( 858 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
843 params.width, params.block_height, params.height, gl_buffer.data(), copy_size, 859 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
844 params.addr); 860 size, params.addr);
845 } else { 861 } else {
846 const u8* const texture_src_data_end{texture_src_data + total_size}; 862 const u8* const texture_src_data_end{texture_src_data + total_size};
847 gl_buffer.assign(texture_src_data, texture_src_data_end); 863 gl_buffer.assign(texture_src_data, texture_src_data_end);
@@ -989,7 +1005,9 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
989 } 1005 }
990 1006
991 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer( 1007 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
992 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)}; 1008 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format,
1009 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
1010 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
993 1011
994 return GetSurface(depth_params, preserve_contents); 1012 return GetSurface(depth_params, preserve_contents);
995} 1013}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0b4940b3c..0b8ae3eb4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -74,19 +74,21 @@ struct SurfaceParams {
74 RG32UI = 43, 74 RG32UI = 43,
75 R32UI = 44, 75 R32UI = 44,
76 ASTC_2D_8X8 = 45, 76 ASTC_2D_8X8 = 45,
77 ASTC_2D_8X5 = 46,
78 ASTC_2D_5X4 = 47,
77 79
78 MaxColorFormat, 80 MaxColorFormat,
79 81
80 // Depth formats 82 // Depth formats
81 Z32F = 46, 83 Z32F = 48,
82 Z16 = 47, 84 Z16 = 49,
83 85
84 MaxDepthFormat, 86 MaxDepthFormat,
85 87
86 // DepthStencil formats 88 // DepthStencil formats
87 Z24S8 = 48, 89 Z24S8 = 50,
88 S8Z24 = 49, 90 S8Z24 = 51,
89 Z32FS8 = 50, 91 Z32FS8 = 52,
90 92
91 MaxDepthStencilFormat, 93 MaxDepthStencilFormat,
92 94
@@ -220,6 +222,8 @@ struct SurfaceParams {
220 1, // RG32UI 222 1, // RG32UI
221 1, // R32UI 223 1, // R32UI
222 4, // ASTC_2D_8X8 224 4, // ASTC_2D_8X8
225 4, // ASTC_2D_8X5
226 4, // ASTC_2D_5X4
223 1, // Z32F 227 1, // Z32F
224 1, // Z16 228 1, // Z16
225 1, // Z24S8 229 1, // Z24S8
@@ -282,6 +286,8 @@ struct SurfaceParams {
282 64, // RG32UI 286 64, // RG32UI
283 32, // R32UI 287 32, // R32UI
284 16, // ASTC_2D_8X8 288 16, // ASTC_2D_8X8
289 32, // ASTC_2D_8X5
290 32, // ASTC_2D_5X4
285 32, // Z32F 291 32, // Z32F
286 16, // Z16 292 16, // Z16
287 32, // Z24S8 293 32, // Z24S8
@@ -553,8 +559,12 @@ struct SurfaceParams {
553 return PixelFormat::BC6H_SF16; 559 return PixelFormat::BC6H_SF16;
554 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 560 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
555 return PixelFormat::ASTC_2D_4X4; 561 return PixelFormat::ASTC_2D_4X4;
562 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
563 return PixelFormat::ASTC_2D_5X4;
556 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 564 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
557 return PixelFormat::ASTC_2D_8X8; 565 return PixelFormat::ASTC_2D_8X8;
566 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
567 return PixelFormat::ASTC_2D_8X5;
558 case Tegra::Texture::TextureFormat::R16_G16: 568 case Tegra::Texture::TextureFormat::R16_G16:
559 switch (component_type) { 569 switch (component_type) {
560 case Tegra::Texture::ComponentType::FLOAT: 570 case Tegra::Texture::ComponentType::FLOAT:
@@ -716,9 +726,10 @@ struct SurfaceParams {
716 static SurfaceParams CreateForFramebuffer(std::size_t index); 726 static SurfaceParams CreateForFramebuffer(std::size_t index);
717 727
718 /// Creates SurfaceParams for a depth buffer configuration 728 /// Creates SurfaceParams for a depth buffer configuration
719 static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 729 static SurfaceParams CreateForDepthBuffer(
720 Tegra::GPUVAddr zeta_address, 730 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
721 Tegra::DepthFormat format); 731 u32 block_width, u32 block_height, u32 block_depth,
732 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
722 733
723 /// Creates SurfaceParams for a Fermi2D surface copy 734 /// Creates SurfaceParams for a Fermi2D surface copy
724 static SurfaceParams CreateForFermiCopySurface( 735 static SurfaceParams CreateForFermiCopySurface(
@@ -733,7 +744,9 @@ struct SurfaceParams {
733 744
734 VAddr addr; 745 VAddr addr;
735 bool is_tiled; 746 bool is_tiled;
747 u32 block_width;
736 u32 block_height; 748 u32 block_height;
749 u32 block_depth;
737 PixelFormat pixel_format; 750 PixelFormat pixel_format;
738 ComponentType component_type; 751 ComponentType component_type;
739 SurfaceType type; 752 SurfaceType type;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index c82a0dcfa..ca063d90d 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1436,7 +1436,6 @@ private:
1436 1436
1437 break; 1437 break;
1438 } 1438 }
1439
1440 case OpCode::Type::Shift: { 1439 case OpCode::Type::Shift: {
1441 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1440 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1442 std::string op_b; 1441 std::string op_b;
@@ -1478,7 +1477,6 @@ private:
1478 } 1477 }
1479 break; 1478 break;
1480 } 1479 }
1481
1482 case OpCode::Type::ArithmeticIntegerImmediate: { 1480 case OpCode::Type::ArithmeticIntegerImmediate: {
1483 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1481 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1484 std::string op_b = std::to_string(instr.alu.imm20_32.Value()); 1482 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
@@ -2626,14 +2624,14 @@ private:
2626 const std::string pred = 2624 const std::string pred =
2627 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 2625 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2628 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 2626 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2629 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 2627 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2630 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 2628 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2631 SetPredicate(instr.csetp.pred3, 2629 SetPredicate(instr.csetp.pred3,
2632 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 2630 '(' + control_code + ") " + combiner + " (" + pred + ')');
2633 } 2631 }
2634 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2632 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2635 SetPredicate(instr.csetp.pred0, 2633 SetPredicate(instr.csetp.pred0,
2636 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 2634 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2637 } 2635 }
2638 break; 2636 break;
2639 } 2637 }
@@ -2953,6 +2951,88 @@ private:
2953 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); 2951 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
2954 break; 2952 break;
2955 } 2953 }
2954 case OpCode::Id::VMAD: {
2955 const bool signed_a = instr.vmad.signed_a == 1;
2956 const bool signed_b = instr.vmad.signed_b == 1;
2957 const bool result_signed = signed_a || signed_b;
2958 boost::optional<std::string> forced_result;
2959
2960 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
2961 Tegra::Shader::VmadType type, u64 byte_height) {
2962 const std::string value = [&]() {
2963 if (!is_chunk) {
2964 const auto offset = static_cast<u32>(byte_height * 8);
2965 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
2966 }
2967 const std::string zero = "0";
2968
2969 switch (type) {
2970 case Tegra::Shader::VmadType::Size16_Low:
2971 return '(' + op + " & 0xffff)";
2972 case Tegra::Shader::VmadType::Size16_High:
2973 return '(' + op + " >> 16)";
2974 case Tegra::Shader::VmadType::Size32:
2975 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
2976 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
2977 // explanation is found: assert.
2978 UNREACHABLE_MSG("Unimplemented");
2979 return zero;
2980 case Tegra::Shader::VmadType::Invalid:
2981 // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
2982 // testing (even though it's invalid) this makes the whole instruction
2983 // assign zero to target register.
2984 forced_result = boost::make_optional(zero);
2985 return zero;
2986 default:
2987 UNREACHABLE();
2988 return zero;
2989 }
2990 }();
2991
2992 if (is_signed) {
2993 return "int(" + value + ')';
2994 }
2995 return value;
2996 };
2997
2998 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
2999 instr.vmad.is_byte_chunk_a != 0, signed_a,
3000 instr.vmad.type_a, instr.vmad.byte_height_a);
3001
3002 std::string op_b;
3003 if (instr.vmad.use_register_b) {
3004 op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3005 instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
3006 instr.vmad.byte_height_b);
3007 } else {
3008 op_b = '(' +
3009 std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
3010 : instr.alu.GetImm20_16()) +
3011 ')';
3012 }
3013
3014 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
3015
3016 std::string result;
3017 if (forced_result) {
3018 result = *forced_result;
3019 } else {
3020 result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3021
3022 switch (instr.vmad.shr) {
3023 case Tegra::Shader::VmadShr::Shr7:
3024 result = '(' + result + " >> 7)";
3025 break;
3026 case Tegra::Shader::VmadShr::Shr15:
3027 result = '(' + result + " >> 15)";
3028 break;
3029 }
3030 }
3031 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3032 instr.vmad.saturate == 1, 0, Register::Size::Word,
3033 instr.vmad.cc);
3034 break;
3035 }
2956 default: { 3036 default: {
2957 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3037 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
2958 UNREACHABLE(); 3038 UNREACHABLE();
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 3d5476e5d..18ab723f7 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -4,6 +4,7 @@
4 4
5#include <cmath> 5#include <cmath>
6#include <cstring> 6#include <cstring>
7#include "common/alignment.h"
7#include "common/assert.h" 8#include "common/assert.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "video_core/gpu.h" 10#include "video_core/gpu.h"
@@ -39,72 +40,146 @@ struct alignas(64) SwizzleTable {
39constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
40constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
41 42
42static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 43/**
43 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
44 u32 block_height) { 45 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1.
47 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
52 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
45 std::array<u8*, 2> data_ptrs; 53 std::array<u8*, 2> data_ptrs;
46 const std::size_t stride = width * bytes_per_pixel; 54 u32 z_address = tile_offset;
47 const std::size_t gobs_in_x = 64; 55 const u32 gob_size_x = 64;
48 const std::size_t gobs_in_y = 8; 56 const u32 gob_size_y = 8;
49 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 57 const u32 gob_size_z = 1;
50 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 58 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
51 for (std::size_t y = 0; y < height; ++y) { 59 for (u32 z = z_start; z < z_end; z++) {
52 const std::size_t gob_y_address = 60 u32 y_address = z_address;
53 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 61 u32 pixel_base = layer_z * z + y_start * stride_x;
54 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 62 for (u32 y = y_start; y < y_end; y++) {
55 const auto& table = legacy_swizzle_table[y % gobs_in_y]; 63 const auto& table = legacy_swizzle_table[y % gob_size_y];
56 for (std::size_t x = 0; x < width; ++x) { 64 for (u32 x = x_start; x < x_end; x++) {
57 const std::size_t gob_address = 65 const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
58 gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; 66 const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
59 const std::size_t x2 = x * bytes_per_pixel; 67 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
60 const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; 68 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
61 const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; 69 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
62 70 }
63 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 71 pixel_base += stride_x;
64 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 72 if ((y + 1) % gob_size_y == 0)
65 73 y_address += gob_size;
66 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
67 } 74 }
75 z_address += xy_block_size;
68 } 76 }
69} 77}
70 78
71static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 79/**
72 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 80 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
73 u32 block_height) { 81 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1.
83 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
88 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
74 std::array<u8*, 2> data_ptrs; 89 std::array<u8*, 2> data_ptrs;
75 const std::size_t stride{width * bytes_per_pixel}; 90 u32 z_address = tile_offset;
76 const std::size_t gobs_in_x = 64; 91 const u32 x_startb = x_start * bytes_per_pixel;
77 const std::size_t gobs_in_y = 8; 92 const u32 x_endb = x_end * bytes_per_pixel;
78 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 93 const u32 copy_size = 16;
79 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 94 const u32 gob_size_x = 64;
80 const std::size_t copy_size{16}; 95 const u32 gob_size_y = 8;
81 for (std::size_t y = 0; y < height; ++y) { 96 const u32 gob_size_z = 1;
82 const std::size_t initial_gob = 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
83 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 98 for (u32 z = z_start; z < z_end; z++) {
84 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 99 u32 y_address = z_address;
85 const std::size_t pixel_base{y * width * out_bytes_per_pixel}; 100 u32 pixel_base = layer_z * z + y_start * stride_x;
86 const auto& table = fast_swizzle_table[y % gobs_in_y]; 101 for (u32 y = y_start; y < y_end; y++) {
87 for (std::size_t xb = 0; xb < stride; xb += copy_size) { 102 const auto& table = fast_swizzle_table[y % gob_size_y];
88 const std::size_t gob_address{initial_gob + 103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
89 (xb / gobs_in_x) * gobs_size * block_height}; 104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
90 const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; 105 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
91 const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 106 const u32 pixel_index{out_x + pixel_base};
92 const std::size_t pixel_index{out_x + pixel_base}; 107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
93 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
94 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
95 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); 110 }
111 pixel_base += stride_x;
112 if ((y + 1) % gob_size_y == 0)
113 y_address += gob_size;
96 } 114 }
115 z_address += xy_block_size;
97 } 116 }
98} 117}
99 118
100void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 119/**
101 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { 120 * This function unswizzles or swizzles a texture by mapping Linear to BlockLinear Textue.
121 * The body of this function takes care of splitting the swizzled texture into blocks,
122 * and managing the extents of it. Once all the parameters of a single block are obtained,
123 * the function calls 'ProcessBlock' to process that particular Block.
124 *
125 * Documentation for the memory layout and decoding can be found at:
126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
127 */
128template <bool fast>
129void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
130 const u32 height, const u32 depth, const u32 bytes_per_pixel,
131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x;
135 const u32 gob_x_bytes = 64;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
137 const u32 gob_elements_y = 8;
138 const u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z;
146 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
147 const u32 xy_block_size = gob_size * block_height;
148 const u32 block_size = xy_block_size * block_depth;
149 u32 tile_offset = 0;
150 for (u32 zb = 0; zb < blocks_on_z; zb++) {
151 const u32 z_start = zb * block_z_elements;
152 const u32 z_end = std::min(depth, z_start + block_z_elements);
153 for (u32 yb = 0; yb < blocks_on_y; yb++) {
154 const u32 y_start = yb * block_y_elements;
155 const u32 y_end = std::min(height, y_start + block_y_elements);
156 for (u32 xb = 0; xb < blocks_on_x; xb++) {
157 const u32 x_start = xb * block_x_elements;
158 const u32 x_end = std::min(width, x_start + block_x_elements);
159 if (fast) {
160 FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
161 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
162 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
163 } else {
164 PreciseProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
165 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
166 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
167 }
168 tile_offset += block_size;
169 }
170 }
171 }
172}
173
174void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
175 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
176 bool unswizzle, u32 block_height, u32 block_depth) {
102 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 177 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
103 FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 178 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
104 unswizzled_data, unswizzle, block_height); 179 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
105 } else { 180 } else {
106 LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 181 SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
107 unswizzled_data, unswizzle, block_height); 182 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
108 } 183 }
109} 184}
110 185
@@ -125,7 +200,9 @@ u32 BytesPerPixel(TextureFormat format) {
125 case TextureFormat::R32_G32_B32: 200 case TextureFormat::R32_G32_B32:
126 return 12; 201 return 12;
127 case TextureFormat::ASTC_2D_4X4: 202 case TextureFormat::ASTC_2D_4X4:
203 case TextureFormat::ASTC_2D_5X4:
128 case TextureFormat::ASTC_2D_8X8: 204 case TextureFormat::ASTC_2D_8X8:
205 case TextureFormat::ASTC_2D_8X5:
129 case TextureFormat::A8R8G8B8: 206 case TextureFormat::A8R8G8B8:
130 case TextureFormat::A2B10G10R10: 207 case TextureFormat::A2B10G10R10:
131 case TextureFormat::BF10GF11RF11: 208 case TextureFormat::BF10GF11RF11:
@@ -152,10 +229,11 @@ u32 BytesPerPixel(TextureFormat format) {
152} 229}
153 230
154std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 231std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
155 u32 height, u32 block_height) { 232 u32 height, u32 depth, u32 block_height, u32 block_depth) {
156 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
157 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, 234 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel,
158 Memory::GetPointer(address), unswizzled_data.data(), true, block_height); 235 Memory::GetPointer(address), unswizzled_data.data(), true, block_height,
236 block_depth);
159 return unswizzled_data; 237 return unswizzled_data;
160} 238}
161 239
@@ -199,4 +277,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
199 return rgba_data; 277 return rgba_data;
200} 278}
201 279
280std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
281 u32 block_height, u32 block_depth) {
282 if (tiled) {
283 const u32 gobs_in_x = 64 / bytes_per_pixel;
284 const u32 gobs_in_y = 8;
285 const u32 gobs_in_z = 1;
286 const u32 aligned_width = Common::AlignUp(width, gobs_in_x);
287 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
288 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
289 return aligned_width * aligned_height * aligned_depth * bytes_per_pixel;
290 } else {
291 return width * height * depth * bytes_per_pixel;
292 }
293}
294
202} // namespace Tegra::Texture 295} // namespace Tegra::Texture
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 1f7b731be..aaf316947 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -14,17 +14,14 @@ namespace Tegra::Texture {
14 * Unswizzles a swizzled texture without changing its format. 14 * Unswizzles a swizzled texture without changing its format.
15 */ 15 */
16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
17 u32 height, u32 block_height = TICEntry::DefaultBlockHeight); 17 u32 height, u32 depth,
18 18 u32 block_height = TICEntry::DefaultBlockHeight,
19/** 19 u32 block_depth = TICEntry::DefaultBlockHeight);
20 * Unswizzles a swizzled depth texture without changing its format.
21 */
22std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
23 u32 block_height = TICEntry::DefaultBlockHeight);
24 20
25/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary. 21/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary.
26void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 22void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
27 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height); 23 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
24 bool unswizzle, u32 block_height, u32 block_depth);
28 25
29/** 26/**
30 * Decodes an unswizzled texture into a A8R8G8B8 texture. 27 * Decodes an unswizzled texture into a A8R8G8B8 texture.
@@ -32,4 +29,10 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
32std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 29std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
33 u32 height); 30 u32 height);
34 31
32/**
33 * This function calculates the correct size of a texture depending if it's tiled or not.
34 */
35std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
36 u32 block_height, u32 block_depth);
37
35} // namespace Tegra::Texture 38} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 8f31d825a..5947bd2b9 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -141,6 +141,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
141 141
142struct TICEntry { 142struct TICEntry {
143 static constexpr u32 DefaultBlockHeight = 16; 143 static constexpr u32 DefaultBlockHeight = 16;
144 static constexpr u32 DefaultBlockDepth = 1;
144 145
145 union { 146 union {
146 u32 raw; 147 u32 raw;
@@ -161,7 +162,9 @@ struct TICEntry {
161 BitField<21, 3, TICHeaderVersion> header_version; 162 BitField<21, 3, TICHeaderVersion> header_version;
162 }; 163 };
163 union { 164 union {
165 BitField<0, 3, u32> block_width;
164 BitField<3, 3, u32> block_height; 166 BitField<3, 3, u32> block_height;
167 BitField<6, 3, u32> block_depth;
165 168
166 // High 16 bits of the pitch value 169 // High 16 bits of the pitch value
167 BitField<0, 16, u32> pitch_high; 170 BitField<0, 16, u32> pitch_high;
@@ -202,13 +205,24 @@ struct TICEntry {
202 return depth_minus_1 + 1; 205 return depth_minus_1 + 1;
203 } 206 }
204 207
208 u32 BlockWidth() const {
209 ASSERT(IsTiled());
210 // The block height is stored in log2 format.
211 return 1 << block_width;
212 }
213
205 u32 BlockHeight() const { 214 u32 BlockHeight() const {
206 ASSERT(header_version == TICHeaderVersion::BlockLinear || 215 ASSERT(IsTiled());
207 header_version == TICHeaderVersion::BlockLinearColorKey);
208 // The block height is stored in log2 format. 216 // The block height is stored in log2 format.
209 return 1 << block_height; 217 return 1 << block_height;
210 } 218 }
211 219
220 u32 BlockDepth() const {
221 ASSERT(IsTiled());
222 // The block height is stored in log2 format.
223 return 1 << block_depth;
224 }
225
212 bool IsTiled() const { 226 bool IsTiled() const {
213 return header_version == TICHeaderVersion::BlockLinear || 227 return header_version == TICHeaderVersion::BlockLinear ||
214 header_version == TICHeaderVersion::BlockLinearColorKey; 228 header_version == TICHeaderVersion::BlockLinearColorKey;
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index cbcd5dd5f..44d423da2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,8 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 389 auto unswizzled_data =
390 *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height); 390 Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format),
391 surface_width, surface_height, 1U);
391 392
392 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
393 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e11833c5a..bef9df00d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -31,6 +31,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
31#include <QDialogButtonBox> 31#include <QDialogButtonBox>
32#include <QFileDialog> 32#include <QFileDialog>
33#include <QMessageBox> 33#include <QMessageBox>
34#include <QtConcurrent/QtConcurrent>
34#include <QtGui> 35#include <QtGui>
35#include <QtWidgets> 36#include <QtWidgets>
36#include <fmt/format.h> 37#include <fmt/format.h>
@@ -171,8 +172,11 @@ GMainWindow::GMainWindow()
171 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); 172 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
172 show(); 173 show();
173 174
175 // Gen keys if necessary
176 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
177
174 // Necessary to load titles from nand in gamelist. 178 // Necessary to load titles from nand in gamelist.
175 Service::FileSystem::CreateFactories(vfs); 179 Service::FileSystem::CreateFactories(*vfs);
176 game_list->LoadCompatibilityList(); 180 game_list->LoadCompatibilityList();
177 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 181 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
178 182
@@ -443,6 +447,8 @@ void GMainWindow::ConnectMenuEvents() {
443 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 447 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
444 448
445 // Help 449 // Help
450 connect(ui.action_Rederive, &QAction::triggered, this,
451 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
446 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 452 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
447} 453}
448 454
@@ -902,22 +908,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
902} 908}
903 909
904void GMainWindow::OnMenuLoadFile() { 910void GMainWindow::OnMenuLoadFile() {
905 QString extensions; 911 const QString extensions =
906 for (const auto& piece : game_list->supported_file_extensions) 912 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
907 extensions += "*." + piece + " "; 913 const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
908 914 "%1 is an identifier for the Switch executable file extensions.")
909 extensions += "main "; 915 .arg(extensions);
916 const QString filename = QFileDialog::getOpenFileName(
917 this, tr("Load File"), UISettings::values.roms_path, file_filter);
910 918
911 QString file_filter = tr("Switch Executable") + " (" + extensions + ")"; 919 if (filename.isEmpty()) {
912 file_filter += ";;" + tr("All Files (*.*)"); 920 return;
913
914 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
915 UISettings::values.roms_path, file_filter);
916 if (!filename.isEmpty()) {
917 UISettings::values.roms_path = QFileInfo(filename).path();
918
919 BootGame(filename);
920 } 921 }
922
923 UISettings::values.roms_path = QFileInfo(filename).path();
924 BootGame(filename);
921} 925}
922 926
923void GMainWindow::OnMenuLoadFolder() { 927void GMainWindow::OnMenuLoadFolder() {
@@ -1133,7 +1137,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1133 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1137 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1134 : FileUtil::UserPath::NANDDir, 1138 : FileUtil::UserPath::NANDDir,
1135 dir_path.toStdString()); 1139 dir_path.toStdString());
1136 Service::FileSystem::CreateFactories(vfs); 1140 Service::FileSystem::CreateFactories(*vfs);
1137 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1141 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1138 } 1142 }
1139} 1143}
@@ -1375,6 +1379,86 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1375 } 1379 }
1376} 1380}
1377 1381
1382void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1383 if (behavior == ReinitializeKeyBehavior::Warning) {
1384 const auto res = QMessageBox::information(
1385 this, tr("Confirm Key Rederivation"),
1386 tr("You are about to force rederive all of your keys. \nIf you do not know what this "
1387 "means or what you are doing, \nthis is a potentially destructive action. \nPlease "
1388 "make "
1389 "sure this is what you want \nand optionally make backups.\n\nThis will delete your "
1390 "autogenerated key files and re-run the key derivation module."),
1391 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
1392
1393 if (res == QMessageBox::Cancel)
1394 return;
1395
1396 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1397 "prod.keys_autogenerated");
1398 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1399 "console.keys_autogenerated");
1400 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1401 "title.keys_autogenerated");
1402 }
1403
1404 Core::Crypto::KeyManager keys{};
1405 if (keys.BaseDeriveNecessary()) {
1406 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
1407 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
1408
1409 const auto function = [this, &keys, &pdm] {
1410 keys.PopulateFromPartitionData(pdm);
1411 Service::FileSystem::CreateFactories(*vfs);
1412 keys.DeriveETicket(pdm);
1413 };
1414
1415 QString errors;
1416
1417 if (!pdm.HasFuses())
1418 errors += tr("- Missing fuses - Cannot derive SBK\n");
1419 if (!pdm.HasBoot0())
1420 errors += tr("- Missing BOOT0 - Cannot derive master keys\n");
1421 if (!pdm.HasPackage2())
1422 errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n");
1423 if (!pdm.HasProdInfo())
1424 errors += tr("- Missing PRODINFO - Cannot derive title keys\n");
1425
1426 if (!errors.isEmpty()) {
1427
1428 QMessageBox::warning(
1429 this, tr("Warning Missing Derivation Components"),
1430 tr("The following are missing from your configuration that may hinder key "
1431 "derivation. It will be attempted but may not complete.<br><br>") +
1432 errors +
1433 tr("<br><br>You can get all of these and dump all of your games easily by "
1434 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
1435 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1436 "to obtain all of your keys."));
1437 }
1438
1439 QProgressDialog prog;
1440 prog.setRange(0, 0);
1441 prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
1442 "system's performance."));
1443 prog.setWindowTitle(tr("Deriving Keys"));
1444
1445 prog.show();
1446
1447 auto future = QtConcurrent::run(function);
1448 while (!future.isFinished()) {
1449 QCoreApplication::processEvents();
1450 }
1451
1452 prog.close();
1453 }
1454
1455 Service::FileSystem::CreateFactories(*vfs);
1456
1457 if (behavior == ReinitializeKeyBehavior::Warning) {
1458 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1459 }
1460}
1461
1378bool GMainWindow::ConfirmClose() { 1462bool GMainWindow::ConfirmClose() {
1379 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1463 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1380 return true; 1464 return true;
@@ -1483,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {
1483 emit UpdateThemedIcons(); 1567 emit UpdateThemedIcons();
1484} 1568}
1485 1569
1486void GMainWindow::SetDiscordEnabled(bool state) { 1570void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
1487#ifdef USE_DISCORD_PRESENCE 1571#ifdef USE_DISCORD_PRESENCE
1488 if (state) { 1572 if (state) {
1489 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); 1573 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index fe0e9a50a..3663d6aed 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -41,6 +41,11 @@ enum class EmulatedDirectoryTarget {
41 SDMC, 41 SDMC,
42}; 42};
43 43
44enum class ReinitializeKeyBehavior {
45 NoWarning,
46 Warning,
47};
48
44namespace DiscordRPC { 49namespace DiscordRPC {
45class DiscordInterface; 50class DiscordInterface;
46} 51}
@@ -167,6 +172,7 @@ private slots:
167 void HideFullscreen(); 172 void HideFullscreen();
168 void ToggleWindowMode(); 173 void ToggleWindowMode();
169 void OnCoreError(Core::System::ResultStatus, std::string); 174 void OnCoreError(Core::System::ResultStatus, std::string);
175 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
170 176
171private: 177private:
172 void UpdateStatusBar(); 178 void UpdateStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index cb1664b21..9851f507d 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -103,6 +103,7 @@
103 </property> 103 </property>
104 <addaction name="action_Report_Compatibility"/> 104 <addaction name="action_Report_Compatibility"/>
105 <addaction name="separator"/> 105 <addaction name="separator"/>
106 <addaction name="action_Rederive"/>
106 <addaction name="action_About"/> 107 <addaction name="action_About"/>
107 </widget> 108 </widget>
108 <addaction name="menu_File"/> 109 <addaction name="menu_File"/>
@@ -159,6 +160,11 @@
159 <string>&amp;Stop</string> 160 <string>&amp;Stop</string>
160 </property> 161 </property>
161 </action> 162 </action>
163 <action name="action_Rederive">
164 <property name="text">
165 <string>Reinitialize keys...</string>
166 </property>
167 </action>
162 <action name="action_About"> 168 <action name="action_About">
163 <property name="text"> 169 <property name="text">
164 <string>About yuzu</string> 170 <string>About yuzu</string>
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 27aba95f6..c8b93b85b 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -175,7 +175,7 @@ int main(int argc, char** argv) {
175 175
176 Core::System& system{Core::System::GetInstance()}; 176 Core::System& system{Core::System::GetInstance()};
177 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 177 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
178 Service::FileSystem::CreateFactories(system.GetFilesystem()); 178 Service::FileSystem::CreateFactories(*system.GetFilesystem());
179 179
180 SCOPE_EXIT({ system.Shutdown(); }); 180 SCOPE_EXIT({ system.Shutdown(); });
181 181