Compare commits

..

48 Commits

Author SHA1 Message Date
Lioncash
bed872ed38 nso: Return an optional address from LoadModule
If a malformed NSO is attempted to be loaded, we shouldn't continue
onwards. We should be reporting an error and bailing out.
2018-10-15 17:02:11 -04:00
Lioncash
bb9cf8a127 nso: Make LoadModule take a VfsFile by const reference 2018-10-14 20:38:19 -04:00
Lioncash
0732786ddc nro: Make LoadNro take a VfsFile by const reference
This function doesn't need to care about ownership semantics, so we can
just pass it a reference to the file itself, rather than a
std::shared_ptr alias.
2018-10-14 20:24:18 -04:00
bunnei
2f8ca32020 Merge pull request #1492 from lioncash/proc
svc: Implement svcGetProcessInfo
2018-10-14 14:37:58 -04:00
bunnei
b183ce4365 Merge pull request #1495 from ogniK5377/break-stop
Stop all threads on svcBreak
2018-10-14 14:31:35 -04:00
David Marcec
92fae7e1ab Stop all threads on svcBreak
This should help diagnose crashes easier and prevent many users thinking that a game is still running when in fact it's just an audio thread still running(this is typically not killed when svcBreak is hit since the game expects us to do this)
2018-10-14 18:14:51 +11:00
Lioncash
1c7a7ed79b svc: Implement svcGetProcessInfo
A fairly basic service function, which only appears to currently support
retrieving the process state. This also alters the ProcessStatus enum to
contain all of the values that a kernel process seems to be able of
reporting with regards to state.
2018-10-13 17:00:43 -04:00
bunnei
1584fb6b38 Merge pull request #1409 from DarkLordZach/key-derivation
crypto: Add support for full key derivation
2018-10-12 22:55:49 -04:00
bunnei
c2aa4293ec Merge pull request #1483 from lioncash/codeset
kernel/process: Make CodeSet a regular non-inherited object
2018-10-12 22:52:12 -04:00
bunnei
38b027aa81 Merge pull request #1484 from FernandoS27/calculate-size
Implemented helper function to correctly calculate a texture's size
2018-10-12 21:12:53 -04:00
bunnei
ffcda6c08e Merge pull request #1481 from lioncash/typo
svc: Fix typos in sanitizing checks for MapMemory/UnmapMemory
2018-10-12 20:46:55 -04:00
FernandoS27
97b6405a17 Implemented helper function to correctly calculate a texture's size 2018-10-12 14:21:53 -04:00
bunnei
2946d4bdbe Merge pull request #1467 from ogniK5377/svcbreak-type-fix
Fixed incorrect types for svcBreak
2018-10-12 12:08:08 -04:00
Lioncash
1abed2f4c4 kernel/process: Make CodeSet a regular non-inherited object
These only exist to ferry data into a Process instance and end up going
out of scope quite early. Because of this, we can just make it a plain
struct for holding things and just std::move it into the relevant
function. There's no need to make this inherit from the kernel's Object
type.
2018-10-12 12:07:32 -04:00
bunnei
0f7ab3e21a Merge pull request #1478 from ogniK5377/remap-invalidhandle-remap
Passing an invalid nmap handle to Remap should throw an error
2018-10-12 12:07:14 -04:00
bunnei
f9d03b1d41 Merge pull request #1482 from lioncash/init
thread: Remove unnecessary memset from ResetThreadContext()
2018-10-12 12:06:51 -04:00
bunnei
dc328440c8 Merge pull request #1479 from ogniK5377/nmap-revamped
Added error codes for nvmap
2018-10-12 12:06:22 -04:00
Lioncash
b492d43e63 thread: Remove unnecessary memset from ResetThreadContext()
Regular value initialization is adequate here for zeroing out data. It
also has the benefit of not invoking undefined behavior if a non-trivial
type is ever added to the struct for whatever reason.
2018-10-12 10:57:31 -04:00
David Marcec
4d2de6564f Returned an error before processing other remaps 2018-10-12 17:10:41 +11:00
Lioncash
4ccf30dfaa svc: Fix typos in sanitizing checks for MapMemory/UnmapMemory 2018-10-12 01:48:26 -04:00
bunnei
9bf409f275 Merge pull request #1474 from ogniK5377/hwopus-decodeinterleavedwithperformance
HwOpus, Implemented DecodeInterleavedWithPerformance
2018-10-11 16:52:13 -04:00
bunnei
3fd26b7147 Merge pull request #1472 from lioncash/san
svc: Add missing address range sanitizing checks to MapMemory/UnmapMemory
2018-10-11 16:51:41 -04:00
bunnei
bc293e1751 Merge pull request #1476 from bunnei/fix-unmap-flush
nvhost_as_gpu: Flush/invalidate CPU VAddr on UnmapBuffer.
2018-10-11 16:51:28 -04:00
bunnei
83ac3e6395 Merge pull request #1477 from ReinUsesLisp/vmad
gl_shader_decompiler: Implement VMAD
2018-10-11 16:51:09 -04:00
David Marcec
5dd538cace Passing an invalid nmap handle to Remap should throw an error
Added error for invalid nmap handles
2018-10-11 20:32:21 +11:00
ReinUsesLisp
17290a4416 gl_shader_decompiler: Implement VMAD 2018-10-11 04:15:10 -03:00
bunnei
bf795edac4 nvhost_as_gpu: Flush CPU VAddr on UnmapBuffer. 2018-10-11 00:19:36 -04:00
David Marcec
fa10905e1e HwOpus, Implemented DecodeInterleavedWithPerformance
Used by sonic ages
2018-10-11 13:06:56 +11:00
Lioncash
72e9cb523e svc: Add missing address range sanitizing checks to MapMemory/UnmapMemory
This adds the missing address range checking that the service functions
do before attempting to map or unmap memory. Given that both service
functions perform the same set of checks in the same order, we can wrap
these into a function and just call it from both functions, which
deduplicates a little bit of code.
2018-10-10 20:30:49 -04:00
David Marcec
2db37ddea9 Changed all casts in svc_wrap.h to be static_cast instead 2018-10-10 12:49:08 +11:00
David Marcec
09b6dda8f0 Use a better name than "dont_kill_application"
signal_debugger seems like a more fitting name
2018-10-10 12:27:44 +11:00
David Marcec
a4412c8e22 Fixed incorrect types for svcBreak
svcBreak reason should be a u32, not a u64.
2018-10-10 12:23:50 +11:00
Zach Hilman
3ec054643e partition_data_manager: Rename system files for hekate
x
2018-10-07 13:16:23 -04:00
Zach Hilman
8f958b89e7 qt: Add rederive keyset menu option 2018-10-07 13:16:04 -04:00
Zach Hilman
3edafc6802 qt: Add key derivation progress bar on initial setup 2018-10-07 13:15:11 -04:00
Zach Hilman
29dc6f4519 crypto: Add PartitionDataManager
Keeps track of system files for key derivation
2018-10-07 13:15:11 -04:00
Zach Hilman
4aad010f7a key_manager: Add support for loading keys from partition data 2018-10-07 13:15:11 -04:00
Zach Hilman
d041d6231c key_manager: Add ETicket key derivation
Derives titlekeys
2018-10-07 13:15:11 -04:00
Zach Hilman
a57aac5772 key_manager: Add base key derivation
Derives master keys, game encryption keys, and package1/2 keys
2018-10-07 13:15:11 -04:00
Zach Hilman
d7398283e3 key_manager: Add BIS key getter 2018-10-07 13:15:11 -04:00
Zach Hilman
d6a0d5d432 key_manager: Add support for more keys
TSEC, SBK, BIS, and other Sources for proper derivation
2018-10-07 13:15:11 -04:00
Zach Hilman
c79d2ca6cf key_manager: Add keyblob support 2018-10-07 13:15:11 -04:00
Zach Hilman
e4602748d6 key_manager: Add support for crypto revisions past 04 2018-10-07 13:15:11 -04:00
Zach Hilman
9e34303fb9 key_manager: Add support for comments in keyfiles 2018-10-07 13:15:11 -04:00
Zach Hilman
1fa6ee4723 vfs: Move forward declarations to separate file 2018-10-07 13:15:11 -04:00
Zach Hilman
ce05df0a6d key_manager: Add support for console-specific keyfile 2018-10-07 13:15:11 -04:00
Zach Hilman
721632fe66 key_manager: Rename KEK to Kek 2018-10-07 13:15:11 -04:00
Zach Hilman
89ad82ce5c externals/mbedtls: Enable CMAC module
Required for keyblob verification
2018-10-07 13:15:11 -04:00
33 changed files with 2110 additions and 243 deletions

View File

@@ -18,6 +18,8 @@ add_library(core STATIC
crypto/encryption_layer.h
crypto/key_manager.cpp
crypto/key_manager.h
crypto/partition_data_manager.cpp
crypto/partition_data_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
@@ -70,6 +72,7 @@ add_library(core STATIC
file_sys/vfs_real.cpp
file_sys/vfs_real.h
file_sys/vfs_static.h
file_sys/vfs_types.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp

View File

@@ -4,23 +4,56 @@
#include <algorithm>
#include <array>
#include <bitset>
#include <cctype>
#include <fstream>
#include <locale>
#include <map>
#include <sstream>
#include <string_view>
#include <tuple>
#include <vector>
#include <mbedtls/bignum.h>
#include <mbedtls/cipher.h>
#include <mbedtls/cmac.h>
#include <mbedtls/sha256.h>
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace Core::Crypto {
constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
using namespace Common;
const std::array<SHA256Hash, 2> eticket_source_hashes{
"B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
"E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
};
const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
{{S128KeyType::Master, 0}, "master_key_"},
{{S128KeyType::Package1, 0}, "package1_key_"},
{{S128KeyType::Package2, 0}, "package2_key_"},
{{S128KeyType::Titlekek, 0}, "titlekek_"},
{{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
{{S128KeyType::Keyblob, 0}, "keyblob_key_"},
{{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
Key128 out{};
@@ -37,6 +70,77 @@ Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, K
return out;
}
Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source) {
AESCipher<Key128> sbk_cipher(sbk, Mode::ECB);
AESCipher<Key128> tsec_cipher(tsec, Mode::ECB);
tsec_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
sbk_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
return source;
}
Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source) {
Key128 master_root;
std::memcpy(master_root.data(), keyblob.data(), sizeof(Key128));
AESCipher<Key128> master_cipher(master_root, Mode::ECB);
Key128 master{};
master_cipher.Transcode(master_source.data(), master_source.size(), master.data(), Op::Decrypt);
return master;
}
std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
const Key128& key) {
std::array<u8, 0x90> keyblob;
AESCipher<Key128> cipher(key, Mode::CTR);
cipher.SetIV(std::vector<u8>(encrypted_keyblob.data() + 0x10, encrypted_keyblob.data() + 0x20));
cipher.Transcode(encrypted_keyblob.data() + 0x20, keyblob.size(), keyblob.data(), Op::Decrypt);
return keyblob;
}
void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) {
const auto kek_generation_source =
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
const auto key_generation_source =
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
if (HasKey(S128KeyType::Master, crypto_revision)) {
for (auto kak_type :
{KeyAreaKeyType::Application, KeyAreaKeyType::Ocean, KeyAreaKeyType::System}) {
if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(kak_type))) {
const auto source =
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(kak_type));
const auto kek =
GenerateKeyEncryptionKey(source, GetKey(S128KeyType::Master, crypto_revision),
kek_generation_source, key_generation_source);
SetKey(S128KeyType::KeyArea, kek, crypto_revision, static_cast<u64>(kak_type));
}
}
AESCipher<Key128> master_cipher(GetKey(S128KeyType::Master, crypto_revision), Mode::ECB);
for (auto key_type : {SourceKeyType::Titlekek, SourceKeyType::Package2}) {
if (HasKey(S128KeyType::Source, static_cast<u64>(key_type))) {
Key128 key{};
master_cipher.Transcode(
GetKey(S128KeyType::Source, static_cast<u64>(key_type)).data(), key.size(),
key.data(), Op::Decrypt);
SetKey(key_type == SourceKeyType::Titlekek ? S128KeyType::Titlekek
: S128KeyType::Package2,
key, crypto_revision);
}
}
}
}
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
Key128 mac_key{};
mac_cipher.Transcode(mac_source.data(), mac_key.size(), mac_key.data(), Op::Decrypt);
return mac_key;
}
boost::optional<Key128> DeriveSDSeed() {
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000043",
@@ -71,23 +175,24 @@ boost::optional<Key128> DeriveSDSeed() {
return seed;
}
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) {
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)))
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)))
return Loader::ResultStatus::ErrorMissingSDKEKSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)))
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)))
return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
const auto sd_kek_source =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK));
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
const auto aes_kek_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration));
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
const auto aes_key_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
const auto master_00 = keys.GetKey(S128KeyType::Master);
const auto sd_kek =
GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
keys.SetKey(S128KeyType::SDKek, sd_kek);
if (!keys.HasKey(S128KeyType::SDSeed))
return Loader::ResultStatus::ErrorMissingSDSeed;
@@ -118,9 +223,141 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
return source; ///< Return unaltered source to satisfy output requirement.
});
keys.SetKey(S256KeyType::SDKey, sd_keys[0], static_cast<u64>(SDKeyType::Save));
keys.SetKey(S256KeyType::SDKey, sd_keys[1], static_cast<u64>(SDKeyType::NCA));
return Loader::ResultStatus::Success;
}
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
if (!ticket_save.IsOpen())
return {};
std::vector<u8> buffer(ticket_save.GetSize());
ticket_save.ReadBytes(buffer.data(), buffer.size());
std::vector<TicketRaw> out;
u32 magic{};
for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
buffer[offset + 3] == 0x0) {
out.emplace_back();
auto& next = out.back();
std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
offset += next.size();
}
}
return out;
}
template <size_t size>
static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
const std::array<u8, size>& rhs) {
std::array<u8, size> out{};
std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(), std::bit_xor<>());
return out;
}
template <size_t target_size, size_t in_size>
static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
std::array<u8, in_size + 4> seed_exp{};
std::memcpy(seed_exp.data(), seed.data(), in_size);
std::vector<u8> out;
size_t i = 0;
while (out.size() < target_size) {
out.resize(out.size() + 0x20);
seed_exp[in_size + 3] = i;
mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
++i;
}
std::array<u8, target_size> target;
std::memcpy(target.data(), out.data(), target_size);
return target;
}
template <size_t size>
static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
u64 offset = 0;
for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
if (data[i] == 0x1) {
offset = i + 1;
break;
} else if (data[i] != 0x0) {
return boost::none;
}
}
return offset;
}
boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& key) {
u32 cert_authority;
std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
if (cert_authority == 0)
return boost::none;
if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't'))
LOG_INFO(Crypto,
"Attempting to parse ticket with non-standard certificate authority {:08X}.",
cert_authority);
Key128 rights_id;
std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
if (rights_id == Key128{})
return boost::none;
Key128 key_temp{};
if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
return std::make_pair(rights_id, key_temp);
}
mbedtls_mpi D; // RSA Private Exponent
mbedtls_mpi N; // RSA Modulus
mbedtls_mpi S; // Input
mbedtls_mpi M; // Output
mbedtls_mpi_init(&D);
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&S);
mbedtls_mpi_init(&M);
mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
std::array<u8, 0x100> rsa_step;
mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
u8 m_0 = rsa_step[0];
std::array<u8, 0x20> m_1;
std::memcpy(m_1.data(), rsa_step.data() + 0x01, m_1.size());
std::array<u8, 0xDF> m_2;
std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
if (m_0 != 0)
return boost::none;
m_1 = m_1 ^ MGF1<0x20>(m_2);
m_2 = m_2 ^ MGF1<0xDF>(m_1);
const auto offset = FindTicketOffset(m_2);
if (offset == boost::none)
return boost::none;
ASSERT(offset.get() > 0);
std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size());
return std::make_pair(rights_id, key_temp);
}
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -137,6 +374,15 @@ KeyManager::KeyManager() {
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
}
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
if (base.size() < begin + length)
return false;
return std::all_of(base.begin() + begin, base.begin() + begin + length,
[](u8 c) { return std::isdigit(c); });
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -158,6 +404,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
if (out[0].compare(0, 1, "#") == 0)
continue;
if (is_title_keys) {
auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
u128 rights_id{};
@@ -174,6 +423,50 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
const auto index = s256_file_id.at(out[0]);
Key256 key = Common::HexStringToArray<32>(out[1]);
s256_keys[{index.type, index.field1, index.field2}] = key;
} else if (out[0].compare(0, 8, "keyblob_") == 0 &&
out[0].compare(0, 9, "keyblob_k") != 0) {
if (!ValidCryptoRevisionString(out[0], 8, 2))
continue;
const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
} else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
if (!ValidCryptoRevisionString(out[0], 18, 2))
continue;
const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
} else {
for (const auto& kv : KEYS_VARIABLE_LENGTH) {
if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
continue;
if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
const auto index =
std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
const auto sub = kv.first.second;
if (sub == 0) {
s128_keys[{kv.first.first, index, 0}] =
Common::HexStringToArray<16>(out[1]);
} else {
s128_keys[{kv.first.first, kv.first.second, index}] =
Common::HexStringToArray<16>(out[1]);
}
break;
}
}
static constexpr std::array<const char*, 3> kak_names = {
"key_area_key_application_", "key_area_key_ocean_", "key_area_key_system_"};
for (size_t j = 0; j < kak_names.size(); ++j) {
const auto& match = kak_names[j];
if (out[0].compare(0, std::strlen(match), match) == 0) {
const auto index =
std::stoul(out[0].substr(std::strlen(match), 2), nullptr, 16);
s128_keys[{S128KeyType::KeyArea, index, j}] =
Common::HexStringToArray<16>(out[1]);
}
}
}
}
}
@@ -187,6 +480,28 @@ void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string&
LoadFromFile(dir2 + DIR_SEP + filename, title);
}
bool KeyManager::BaseDeriveNecessary() const {
const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
return !HasKey(key_type, index1, index2);
};
if (check_key_existence(S256KeyType::Header))
return true;
for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
if (check_key_existence(S128KeyType::Master, i) ||
check_key_existence(S128KeyType::KeyArea, i,
static_cast<u64>(KeyAreaKeyType::Application)) ||
check_key_existence(S128KeyType::KeyArea, i, static_cast<u64>(KeyAreaKeyType::Ocean)) ||
check_key_existence(S128KeyType::KeyArea, i,
static_cast<u64>(KeyAreaKeyType::System)) ||
check_key_existence(S128KeyType::Titlekek, i))
return true;
}
return false;
}
bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
return s128_keys.find({id, field1, field2}) != s128_keys.end();
}
@@ -207,13 +522,30 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.at({id, field1, field2});
}
template <std::size_t Size>
void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
Key256 KeyManager::GetBISKey(u8 partition_id) const {
Key256 out{};
for (const auto& bis_type : {BISKeyType::Crypto, BISKeyType::Tweak}) {
if (HasKey(S128KeyType::BIS, partition_id, static_cast<u64>(bis_type))) {
std::memcpy(
out.data() + sizeof(Key128) * static_cast<u64>(bis_type),
s128_keys.at({S128KeyType::BIS, partition_id, static_cast<u64>(bis_type)}).data(),
sizeof(Key128));
}
}
return out;
}
template <size_t Size>
void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key) {
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
std::string filename = "title.keys_autogenerated";
if (!title_key)
if (category == KeyCategory::Standard)
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
else if (category == KeyCategory::Console)
filename = "console.keys_autogenerated";
const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
@@ -227,7 +559,7 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
}
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
@@ -237,8 +569,15 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
}
auto category = KeyCategory::Standard;
if (id == S128KeyType::Keyblob || id == S128KeyType::KeyblobMAC || id == S128KeyType::TSEC ||
id == S128KeyType::SecureBoot || id == S128KeyType::SDSeed || id == S128KeyType::BIS) {
category = KeyCategory::Console;
}
const auto iter2 = std::find_if(
s128_file_id.begin(), s128_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
@@ -246,7 +585,30 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
std::tie(id, field1, field2);
});
if (iter2 != s128_file_id.end())
WriteKeyToFile(false, iter2->first, key);
WriteKeyToFile(category, iter2->first, key);
// Variable cases
if (id == S128KeyType::KeyArea) {
static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
"key_area_key_ocean_{:02X}",
"key_area_key_system_{:02X}"};
WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
} else if (id == S128KeyType::Master) {
WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
} else if (id == S128KeyType::Package1) {
WriteKeyToFile(category, fmt::format("package1_key_{:02X}", field1), key);
} else if (id == S128KeyType::Package2) {
WriteKeyToFile(category, fmt::format("package2_key_{:02X}", field1), key);
} else if (id == S128KeyType::Titlekek) {
WriteKeyToFile(category, fmt::format("titlekek_{:02X}", field1), key);
} else if (id == S128KeyType::Keyblob) {
WriteKeyToFile(category, fmt::format("keyblob_key_{:02X}", field1), key);
} else if (id == S128KeyType::KeyblobMAC) {
WriteKeyToFile(category, fmt::format("keyblob_mac_key_{:02X}", field1), key);
} else if (id == S128KeyType::Source && field1 == static_cast<u64>(SourceKeyType::Keyblob)) {
WriteKeyToFile(category, fmt::format("keyblob_key_source_{:02X}", field2), key);
}
s128_keys[{id, field1, field2}] = key;
}
@@ -260,7 +622,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
std::tie(id, field1, field2);
});
if (iter != s256_file_id.end())
WriteKeyToFile(false, iter->first, key);
WriteKeyToFile(KeyCategory::Standard, iter->first, key);
s256_keys[{id, field1, field2}] = key;
}
@@ -290,59 +652,388 @@ void KeyManager::DeriveSDSeedLazy() {
SetKey(S128KeyType::SDSeed, res.get());
}
static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
Key128 out{};
mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(),
key.size() * 8, source, size, out.data());
return out;
}
void KeyManager::DeriveBase() {
if (!BaseDeriveNecessary())
return;
if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
return;
const auto has_bis = [this](u64 id) {
return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Tweak));
};
const auto copy_bis = [this](u64 id_from, u64 id_to) {
SetKey(S128KeyType::BIS,
GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Crypto)), id_to,
static_cast<u64>(BISKeyType::Crypto));
SetKey(S128KeyType::BIS,
GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Tweak)), id_to,
static_cast<u64>(BISKeyType::Tweak));
};
if (has_bis(2) && !has_bis(3))
copy_bis(2, 3);
else if (has_bis(3) && !has_bis(2))
copy_bis(3, 2);
std::bitset<32> revisions(0xFFFFFFFF);
for (size_t i = 0; i < revisions.size(); ++i) {
if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i) ||
encrypted_keyblobs[i] == std::array<u8, 0xB0>{}) {
revisions.reset(i);
}
}
if (!revisions.any())
return;
const auto sbk = GetKey(S128KeyType::SecureBoot);
const auto tsec = GetKey(S128KeyType::TSEC);
const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
for (size_t i = 0; i < revisions.size(); ++i) {
if (!revisions[i])
continue;
// Derive keyblob key
const auto key = DeriveKeyblobKey(
sbk, tsec, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i));
SetKey(S128KeyType::Keyblob, key, i);
// Derive keyblob MAC key
if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
continue;
const auto mac_key = DeriveKeyblobMACKey(
key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
SetKey(S128KeyType::KeyblobMAC, mac_key, i);
Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
continue;
// Decrypt keyblob
if (keyblobs[i] == std::array<u8, 0x90>{}) {
keyblobs[i] = DecryptKeyblob(encrypted_keyblobs[i], key);
WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
keyblobs[i]);
}
Key128 package1;
std::memcpy(package1.data(), keyblobs[i].data() + 0x80, sizeof(Key128));
SetKey(S128KeyType::Package1, package1, i);
// Derive master key
if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master))) {
SetKey(S128KeyType::Master,
DeriveMasterKey(keyblobs[i], GetKey(S128KeyType::Source,
static_cast<u64>(SourceKeyType::Master))),
i);
}
}
revisions.set();
for (size_t i = 0; i < revisions.size(); ++i) {
if (!HasKey(S128KeyType::Master, i))
revisions.reset(i);
}
if (!revisions.any())
return;
for (size_t i = 0; i < revisions.size(); ++i) {
if (!revisions[i])
continue;
// Derive general purpose keys
DeriveGeneralPurposeKeys(i);
}
if (HasKey(S128KeyType::Master, 0) &&
HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)) &&
HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)) &&
HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)) &&
HasKey(S256KeyType::HeaderSource)) {
const auto header_kek = GenerateKeyEncryptionKey(
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)),
GetKey(S128KeyType::Master, 0),
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)),
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)));
SetKey(S128KeyType::HeaderKek, header_kek);
AESCipher<Key128> header_cipher(header_kek, Mode::ECB);
Key256 out = GetKey(S256KeyType::HeaderSource);
header_cipher.Transcode(out.data(), out.size(), out.data(), Op::Decrypt);
SetKey(S256KeyType::Header, out);
}
}
void KeyManager::DeriveETicket(PartitionDataManager& data) {
// ETicket keys
const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
0x0100000000000033, FileSys::ContentRecordType::Program);
if (es == nullptr)
return;
const auto exefs = es->GetExeFS();
if (exefs == nullptr)
return;
const auto main = exefs->GetFile("main");
if (main == nullptr)
return;
const auto bytes = main->ReadAllBytes();
const auto eticket_kek = FindKeyFromHex16(bytes, eticket_source_hashes[0]);
const auto eticket_kekek = FindKeyFromHex16(bytes, eticket_source_hashes[1]);
const auto seed3 = data.GetRSAKekSeed3();
const auto mask0 = data.GetRSAKekMask0();
if (eticket_kek != Key128{})
SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
if (eticket_kekek != Key128{}) {
SetKey(S128KeyType::Source, eticket_kekek,
static_cast<size_t>(SourceKeyType::ETicketKekek));
}
if (seed3 != Key128{})
SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
if (mask0 != Key128{})
SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
mask0 == Key128{}) {
return;
}
Key128 rsa_oaep_kek{};
std::transform(seed3.begin(), seed3.end(), mask0.begin(), rsa_oaep_kek.begin(),
std::bit_xor<>());
if (rsa_oaep_kek == Key128{})
return;
SetKey(S128KeyType::Source, rsa_oaep_kek,
static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
Key128 temp_kek{};
Key128 temp_kekek{};
Key128 eticket_final{};
// Derive ETicket RSA Kek
AESCipher<Key128> es_master(GetKey(S128KeyType::Master), Mode::ECB);
es_master.Transcode(rsa_oaep_kek.data(), rsa_oaep_kek.size(), temp_kek.data(), Op::Decrypt);
AESCipher<Key128> es_kekek(temp_kek, Mode::ECB);
es_kekek.Transcode(eticket_kekek.data(), eticket_kekek.size(), temp_kekek.data(), Op::Decrypt);
AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
if (eticket_final == Key128{})
return;
SetKey(S128KeyType::ETicketRSAKek, eticket_final);
// Titlekeys
data.DecryptProdInfo(GetBISKey(0));
const auto eticket_extended_kek = data.GetETicketExtendedKek();
std::vector<u8> extended_iv(0x10);
std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
std::array<u8, 0x230> extended_dec{};
AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
rsa_1.SetIV(extended_iv);
rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
extended_dec.data(), Op::Decrypt);
RSAKeyPair<2048> rsa_key{};
std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/80000000000000e1",
"rb+");
const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/80000000000000e2",
"rb+");
auto res = GetTicketblob(save1);
const auto res2 = GetTicketblob(save2);
std::copy(res2.begin(), res2.end(), std::back_inserter(res));
for (const auto& raw : res) {
const auto pair = ParseTicket(raw, rsa_key);
if (pair == boost::none)
continue;
const auto& [rid, key] = pair.value();
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
}
}
void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
if (key == Key128{})
return;
SetKey(id, key, field1, field2);
}
void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
if (key == Key256{})
return;
SetKey(id, key, field1, field2);
}
void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
if (!BaseDeriveNecessary())
return;
if (!data.HasBoot0())
return;
for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{})
continue;
encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
encrypted_keyblobs[i]);
}
SetKeyWrapped(S128KeyType::Source, data.GetPackage2KeySource(),
static_cast<u64>(SourceKeyType::Package2));
SetKeyWrapped(S128KeyType::Source, data.GetAESKekGenerationSource(),
static_cast<u64>(SourceKeyType::AESKekGeneration));
SetKeyWrapped(S128KeyType::Source, data.GetTitlekekSource(),
static_cast<u64>(SourceKeyType::Titlekek));
SetKeyWrapped(S128KeyType::Source, data.GetMasterKeySource(),
static_cast<u64>(SourceKeyType::Master));
SetKeyWrapped(S128KeyType::Source, data.GetKeyblobMACKeySource(),
static_cast<u64>(SourceKeyType::KeyblobMAC));
for (size_t i = 0; i < PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH; ++i) {
SetKeyWrapped(S128KeyType::Source, data.GetKeyblobKeySource(i),
static_cast<u64>(SourceKeyType::Keyblob), i);
}
if (data.HasFuses())
SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
DeriveBase();
Key128 latest_master{};
for (s8 i = 0x1F; i >= 0; --i) {
if (GetKey(S128KeyType::Master, static_cast<u8>(i)) != Key128{}) {
latest_master = GetKey(S128KeyType::Master, static_cast<u8>(i));
break;
}
}
const auto masters = data.GetTZMasterKeys(latest_master);
for (size_t i = 0; i < masters.size(); ++i) {
if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i))
SetKey(S128KeyType::Master, masters[i], i);
}
DeriveBase();
if (!data.HasPackage2())
return;
std::array<Key128, 0x20> package2_keys{};
for (size_t i = 0; i < package2_keys.size(); ++i) {
if (HasKey(S128KeyType::Package2, i))
package2_keys[i] = GetKey(S128KeyType::Package2, i);
}
data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyApplicationSource(),
static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Application));
SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyOceanSource(),
static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Ocean));
SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeySystemSource(),
static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::System));
SetKeyWrapped(S128KeyType::Source, data.GetSDKekSource(),
static_cast<u64>(SourceKeyType::SDKek));
SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDSaveKeySource(),
static_cast<u64>(SDKeyType::Save));
SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDNCAKeySource(),
static_cast<u64>(SDKeyType::NCA));
SetKeyWrapped(S128KeyType::Source, data.GetHeaderKekSource(),
static_cast<u64>(SourceKeyType::HeaderKek));
SetKeyWrapped(S256KeyType::HeaderSource, data.GetHeaderKeySource());
SetKeyWrapped(S128KeyType::Source, data.GetAESKeyGenerationSource(),
static_cast<u64>(SourceKeyType::AESKeyGeneration));
DeriveBase();
}
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"master_key_00", {S128KeyType::Master, 0, 0}},
{"master_key_01", {S128KeyType::Master, 1, 0}},
{"master_key_02", {S128KeyType::Master, 2, 0}},
{"master_key_03", {S128KeyType::Master, 3, 0}},
{"master_key_04", {S128KeyType::Master, 4, 0}},
{"package1_key_00", {S128KeyType::Package1, 0, 0}},
{"package1_key_01", {S128KeyType::Package1, 1, 0}},
{"package1_key_02", {S128KeyType::Package1, 2, 0}},
{"package1_key_03", {S128KeyType::Package1, 3, 0}},
{"package1_key_04", {S128KeyType::Package1, 4, 0}},
{"package2_key_00", {S128KeyType::Package2, 0, 0}},
{"package2_key_01", {S128KeyType::Package2, 1, 0}},
{"package2_key_02", {S128KeyType::Package2, 2, 0}},
{"package2_key_03", {S128KeyType::Package2, 3, 0}},
{"package2_key_04", {S128KeyType::Package2, 4, 0}},
{"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
{"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
{"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
{"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
{"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
{"key_area_key_application_00",
{S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_01",
{S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_02",
{S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_03",
{S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_04",
{S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
{"eticket_rsa_kek_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
{"eticket_rsa_kekek_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
{"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
{"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
{"rsa_oaep_kek_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
{"aes_kek_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}},
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
{"aes_key_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
{"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
{"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
{"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
{"key_area_key_application_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_ocean_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_system_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::System)}},
{"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
{"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}},
{"tsec_key", {S128KeyType::TSEC, 0, 0}},
{"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
{"sd_seed", {S128KeyType::SDSeed, 0, 0}},
{"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
{"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
{"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
{"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
{"header_kek", {S128KeyType::HeaderKek, 0, 0}},
{"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
};
const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
{"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
{"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
{"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
{"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
};
} // namespace Core::Crypto

View File

@@ -5,11 +5,18 @@
#pragma once
#include <array>
#include <map>
#include <string>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/common_types.h"
#include "core/crypto/partition_data_manager.h"
#include "core/file_sys/vfs_types.h"
namespace FileUtil {
class IOFile;
}
namespace Loader {
enum class ResultStatus : u16;
@@ -22,13 +29,30 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
using TicketRaw = std::array<u8, 0x400>;
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
struct RSAKeyPair {
std::array<u8, byte_size> encryption_key;
std::array<u8, byte_size> decryption_key;
std::array<u8, byte_size> modulus;
std::array<u8, 4> exponent;
};
enum class KeyCategory : u8 {
Standard,
Title,
Console,
};
enum class S256KeyType : u64 {
Header, //
SDKeySource, // f1=SDKeyType
SDKey, // f1=SDKeyType
Header, //
SDKeySource, // f1=SDKeyType
HeaderSource, //
};
enum class S128KeyType : u64 {
@@ -41,6 +65,14 @@ enum class S128KeyType : u64 {
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
Source, // f1=source type, f2= sub id
Keyblob, // f1=crypto revision
KeyblobMAC, // f1=crypto revision
TSEC, //
SecureBoot, //
BIS, // f1=partition (0-3), f2=type {crypt, tweak}
HeaderKek, //
SDKek, //
RSAKek, //
};
enum class KeyAreaKeyType : u8 {
@@ -50,9 +82,19 @@ enum class KeyAreaKeyType : u8 {
};
enum class SourceKeyType : u8 {
SDKEK,
AESKEKGeneration,
AESKeyGeneration,
SDKek, //
AESKekGeneration, //
AESKeyGeneration, //
RSAOaepKekGeneration, //
Master, //
Keyblob, // f2=crypto revision
KeyAreaKey, // f2=KeyAreaKeyType
Titlekek, //
Package2, //
HeaderKek, //
KeyblobMAC, //
ETicketKek, //
ETicketKekek, //
};
enum class SDKeyType : u8 {
@@ -60,6 +102,16 @@ enum class SDKeyType : u8 {
NCA,
};
enum class BISKeyType : u8 {
Crypto,
Tweak,
};
enum class RSAKekType : u8 {
Mask0,
Seed3,
};
template <typename KeyType>
struct KeyIndex {
KeyType type;
@@ -91,6 +143,8 @@ public:
Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetBISKey(u8 partition_id) const;
void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
@@ -100,23 +154,51 @@ public:
// 8*43 and the private file to exist.
void DeriveSDSeedLazy();
bool BaseDeriveNecessary() const;
void DeriveBase();
void DeriveETicket(PartitionDataManager& data);
void PopulateFromPartitionData(PartitionDataManager& data);
private:
boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys;
boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys;
std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
template <std::size_t Size>
void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);
template <size_t Size>
void WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key);
void DeriveGeneralPurposeKeys(u8 crypto_revision);
void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source);
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source);
Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source);
std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
const Key128& key);
boost::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys);
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
// 0x140-0x144 is zero)
boost::optional<std::pair<Key128, Key128>> ParseTicket(
const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
} // namespace Core::Crypto

View File

@@ -0,0 +1,601 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// NOTE TO FUTURE MAINTAINERS:
// When a new version of switch cryptography is released,
// hash the new keyblob source and master key and add the hashes to
// the arrays below.
#include <algorithm>
#include <array>
#include <cctype>
#include <cstring>
#include <boost/optional/optional.hpp>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/crypto/ctr_encryption_layer.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
#include "core/crypto/xts_encryption_layer.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
using namespace Common;
namespace Core::Crypto {
struct Package2Header {
std::array<u8, 0x100> signature;
Key128 header_ctr;
std::array<Key128, 4> section_ctr;
u32_le magic;
u32_le base_offset;
INSERT_PADDING_BYTES(4);
u8 version_max;
u8 version_min;
INSERT_PADDING_BYTES(2);
std::array<u32_le, 4> section_size;
std::array<u32_le, 4> section_offset;
std::array<SHA256Hash, 4> section_hash;
};
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
struct INIHeader {
u32_le magic;
u32_le size;
u32_le process_count;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
struct SectionHeader {
u32_le offset;
u32_le size_decompressed;
u32_le size_compressed;
u32_le attribute;
};
static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
struct KIPHeader {
u32_le magic;
std::array<char, 12> name;
u64_le title_id;
u32_le category;
u8 priority;
u8 core;
INSERT_PADDING_BYTES(1);
u8 flags;
std::array<SectionHeader, 6> sections;
std::array<u32, 0x20> capabilities;
};
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
const std::array<SHA256Hash, 0x10> source_hashes{
"B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
"7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
"21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
"FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
"FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
"C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
"04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
"FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
"1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
"6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
"D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
"2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
"1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
"8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
"D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
"FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
};
const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
"8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
"2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
"61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
"8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
"95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
"3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
};
const std::array<SHA256Hash, 0x20> master_key_hashes{
"0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
"4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
"79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
"4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
"75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
"EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
};
static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
const auto data_size = in.size() - 0xC;
u32 compressed_size{};
u32 init_index{};
u32 additional_size{};
std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
std::vector<u8> out(in.size() + additional_size);
if (compressed_size == in.size())
std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
else
std::memcpy(out.data(), in.data(), compressed_size);
auto index = in.size() - init_index;
auto out_index = out.size();
while (out_index > 0) {
--index;
auto control = in[index];
for (size_t i = 0; i < 8; ++i) {
if ((control & 0x80) > 0) {
ASSERT(index >= 2);
index -= 2;
u64 segment_offset = in[index] | in[index + 1] << 8;
u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
segment_offset &= 0xFFF;
segment_offset += 3;
if (out_index < segment_size)
segment_size = out_index;
ASSERT(out_index >= segment_size);
out_index -= segment_size;
for (size_t j = 0; j < segment_size; ++j) {
ASSERT(out_index + j + segment_offset < out.size());
out[out_index + j] = out[out_index + j + segment_offset];
}
} else {
ASSERT(out_index >= 1);
--out_index;
--index;
out[out_index] = in[index];
}
control <<= 1;
if (out_index == 0)
return out;
}
}
return out;
}
static u8 CalculateMaxKeyblobSourceHash() {
for (s8 i = 0x1F; i >= 0; --i) {
if (keyblob_source_hashes[i] != SHA256Hash{})
return static_cast<u8>(i + 1);
}
return 0;
}
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = CalculateMaxKeyblobSourceHash();
template <size_t key_size = 0x10>
std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary,
const std::array<u8, 0x20>& hash) {
if (binary.size() < key_size)
return {};
std::array<u8, 0x20> temp{};
for (size_t i = 0; i < binary.size() - key_size; ++i) {
mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
if (temp != hash)
continue;
std::array<u8, key_size> out{};
std::memcpy(out.data(), binary.data() + i, key_size);
return out;
}
return {};
}
std::array<u8, 16> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 32> hash) {
return FindKeyFromHex<0x10>(binary, hash);
}
static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<u8>& binary,
const Key128& key) {
if (binary.size() < 0x10)
return {};
SHA256Hash temp{};
Key128 dec_temp{};
std::array<Key128, 0x20> out{};
AESCipher<Key128> cipher(key, Mode::ECB);
for (size_t i = 0; i < binary.size() - 0x10; ++i) {
cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
for (size_t k = 0; k < out.size(); ++k) {
if (temp == master_key_hashes[k]) {
out[k] = dec_temp;
break;
}
}
}
return out;
}
FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
const std::string& name) {
auto upper = name;
std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
if (dir->GetFile(fname) != nullptr)
return dir->GetFile(fname);
}
return nullptr;
}
PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir)
: boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
fuses(FindFileInDirWithNames(sysdata_dir, "fuse")),
kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")),
package2({
FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-3-SafeMode-Main"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-4-SafeMode-Sub"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
}),
secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
: secure_monitor->ReadAllBytes()),
package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
: package1_decrypted->ReadAllBytes()),
prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {}
PartitionDataManager::~PartitionDataManager() = default;
bool PartitionDataManager::HasBoot0() const {
return boot0 != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
return boot0;
}
std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const {
if (HasBoot0() && index < 32)
return GetEncryptedKeyblobs()[index];
return {};
}
std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const {
if (!HasBoot0())
return {};
std::array<std::array<u8, 176>, 32> out{};
for (size_t i = 0; i < 0x20; ++i)
boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
return out;
}
std::vector<u8> PartitionDataManager::GetSecureMonitor() const {
return secure_monitor_bytes;
}
std::array<u8, 16> PartitionDataManager::GetPackage2KeySource() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[2]);
}
std::array<u8, 16> PartitionDataManager::GetAESKekGenerationSource() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[3]);
}
std::array<u8, 16> PartitionDataManager::GetTitlekekSource() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[5]);
}
std::array<std::array<u8, 16>, 32> PartitionDataManager::GetTZMasterKeys(
std::array<u8, 0x10> master_key) const {
return FindEncryptedMasterKeyFromHex(secure_monitor_bytes, master_key);
}
std::array<u8, 16> PartitionDataManager::GetRSAKekSeed3() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[14]);
}
std::array<u8, 16> PartitionDataManager::GetRSAKekMask0() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[15]);
}
std::vector<u8> PartitionDataManager::GetPackage1Decrypted() const {
return package1_decrypted_bytes;
}
std::array<u8, 16> PartitionDataManager::GetMasterKeySource() const {
return FindKeyFromHex(package1_decrypted_bytes, source_hashes[1]);
}
std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
}
std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const {
if (keyblob_source_hashes[revision] == SHA256Hash{}) {
LOG_WARNING(Crypto,
"No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
revision);
}
return FindKeyFromHex(package1_decrypted_bytes, keyblob_source_hashes[revision]);
}
bool PartitionDataManager::HasFuses() const {
return fuses != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const {
return fuses;
}
std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const {
if (!HasFuses())
return {};
Key128 out{};
fuses->Read(out.data(), out.size(), 0xA4);
return out;
}
bool PartitionDataManager::HasKFuses() const {
return kfuses != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
return kfuses;
}
bool PartitionDataManager::HasPackage2(Package2Type type) const {
return package2.at(static_cast<size_t>(type)) != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const {
return package2.at(static_cast<size_t>(type));
}
bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
Package2Header temp = header;
AESCipher<Key128> cipher(key, Mode::CTR);
cipher.SetIV(iv);
cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
Op::Decrypt);
if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
header = temp;
return true;
}
return false;
}
void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys,
Package2Type type) {
FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
package2[static_cast<size_t>(type)],
package2[static_cast<size_t>(type)]->GetSize() - 0x4000, 0x4000);
Package2Header header{};
if (file->ReadObject(&header) != sizeof(Package2Header))
return;
u8 revision = 0xFF;
if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
for (size_t i = 0; i < package2_keys.size(); ++i) {
if (AttemptDecrypt(package2_keys[i], header))
revision = i;
}
}
if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
return;
const std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end());
const auto a = std::make_shared<FileSys::OffsetVfsFile>(
file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
auto c = a->ReadAllBytes();
AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
cipher.SetIV(s1_iv);
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
// package2_decrypted[static_cast<size_t>(type)] = s1;
INIHeader ini;
std::memcpy(&ini, c.data(), sizeof(INIHeader));
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
return;
std::map<u64, KIPHeader> kips{};
u64 offset = sizeof(INIHeader);
for (size_t i = 0; i < ini.process_count; ++i) {
KIPHeader kip;
std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
return;
kips.emplace(offset, kip);
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
if (name != "FS" && name != "spl") {
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
continue;
}
std::vector<u8> text(kip.sections[0].size_compressed);
std::vector<u8> rodata(kip.sections[1].size_compressed);
std::vector<u8> data(kip.sections[2].size_compressed);
u64 offset_sec = sizeof(KIPHeader) + offset;
std::memcpy(text.data(), c.data() + offset_sec, text.size());
offset_sec += text.size();
std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size());
offset_sec += rodata.size();
std::memcpy(data.data(), c.data() + offset_sec, data.size());
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
text = DecompressBLZ(text);
rodata = DecompressBLZ(rodata);
data = DecompressBLZ(data);
std::vector<u8> out(text.size() + rodata.size() + data.size());
std::memcpy(out.data(), text.data(), text.size());
std::memcpy(out.data() + text.size(), rodata.data(), rodata.size());
std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size());
if (name == "FS")
package2_fs[static_cast<size_t>(type)] = out;
else if (name == "spl")
package2_spl[static_cast<size_t>(type)] = out;
}
}
const std::vector<u8>& PartitionDataManager::GetPackage2FSDecompressed(Package2Type type) const {
return package2_fs.at(static_cast<size_t>(type));
}
std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyApplicationSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[6]);
}
std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyOceanSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[7]);
}
std::array<u8, 16> PartitionDataManager::GetKeyAreaKeySystemSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[8]);
}
std::array<u8, 16> PartitionDataManager::GetSDKekSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[9]);
}
std::array<u8, 32> PartitionDataManager::GetSDSaveKeySource(Package2Type type) const {
return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[10]);
}
std::array<u8, 32> PartitionDataManager::GetSDNCAKeySource(Package2Type type) const {
return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[11]);
}
std::array<u8, 16> PartitionDataManager::GetHeaderKekSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[12]);
}
std::array<u8, 32> PartitionDataManager::GetHeaderKeySource(Package2Type type) const {
return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[13]);
}
const std::vector<u8>& PartitionDataManager::GetPackage2SPLDecompressed(Package2Type type) const {
return package2_spl.at(static_cast<size_t>(type));
}
std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type type) const {
return FindKeyFromHex(package2_spl.at(static_cast<size_t>(type)), source_hashes[4]);
}
bool PartitionDataManager::HasProdInfo() const {
return prodinfo != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const {
return prodinfo;
}
void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
if (prodinfo == nullptr)
return;
prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
}
std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
std::array<u8, 0x240> out{};
if (prodinfo_decrypted != nullptr)
prodinfo_decrypted->Read(out.data(), out.size(), 0x3890);
return out;
}
} // namespace Core::Crypto

View File

@@ -0,0 +1,105 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs_types.h"
namespace Core::Crypto {
enum class Package2Type {
NormalMain,
NormalSub,
SafeModeMain,
SafeModeSub,
RepairMain,
RepairSub,
};
class PartitionDataManager {
public:
static const u8 MAX_KEYBLOB_SOURCE_HASH;
explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir);
~PartitionDataManager();
// BOOT0
bool HasBoot0() const;
FileSys::VirtualFile GetBoot0Raw() const;
std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const;
std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() const;
std::vector<u8> GetSecureMonitor() const;
std::array<u8, 0x10> GetPackage2KeySource() const;
std::array<u8, 0x10> GetAESKekGenerationSource() const;
std::array<u8, 0x10> GetTitlekekSource() const;
std::array<std::array<u8, 0x10>, 0x20> GetTZMasterKeys(std::array<u8, 0x10> master_key) const;
std::array<u8, 0x10> GetRSAKekSeed3() const;
std::array<u8, 0x10> GetRSAKekMask0() const;
std::vector<u8> GetPackage1Decrypted() const;
std::array<u8, 0x10> GetMasterKeySource() const;
std::array<u8, 0x10> GetKeyblobMACKeySource() const;
std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const;
// Fuses
bool HasFuses() const;
FileSys::VirtualFile GetFusesRaw() const;
std::array<u8, 0x10> GetSecureBootKey() const;
// K-Fuses
bool HasKFuses() const;
FileSys::VirtualFile GetKFusesRaw() const;
// Package2
bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type);
const std::vector<u8>& GetPackage2FSDecompressed(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetKeyAreaKeyOceanSource(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetKeyAreaKeySystemSource(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetSDKekSource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x20> GetSDSaveKeySource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x20> GetSDNCAKeySource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetHeaderKekSource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x20> GetHeaderKeySource(Package2Type type = Package2Type::NormalMain) const;
const std::vector<u8>& GetPackage2SPLDecompressed(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetAESKeyGenerationSource(
Package2Type type = Package2Type::NormalMain) const;
// PRODINFO
bool HasProdInfo() const;
FileSys::VirtualFile GetProdInfoRaw() const;
void DecryptProdInfo(std::array<u8, 0x20> bis_key);
std::array<u8, 0x240> GetETicketExtendedKek() const;
private:
FileSys::VirtualFile boot0;
FileSys::VirtualFile fuses;
FileSys::VirtualFile kfuses;
std::array<FileSys::VirtualFile, 6> package2;
FileSys::VirtualFile prodinfo;
FileSys::VirtualFile secure_monitor;
FileSys::VirtualFile package1_decrypted;
// Processed
std::array<FileSys::VirtualFile, 6> package2_decrypted;
FileSys::VirtualFile prodinfo_decrypted;
std::vector<u8> secure_monitor_bytes;
std::vector<u8> package1_decrypted_bytes;
std::array<std::vector<u8>, 6> package2_fs;
std::array<std::vector<u8>, 6> package2_spl;
};
std::array<u8, 0x10> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 0x20> hash);
} // namespace Core::Crypto

View File

@@ -12,20 +12,12 @@
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
namespace FileSys {
class VfsDirectory;
class VfsFile;
class VfsFilesystem;
enum class Mode : u32;
// Convenience typedefs to use Vfs* interfaces
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
None,

View File

@@ -0,0 +1,21 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
namespace FileSys {
class VfsDirectory;
class VfsFile;
class VfsFilesystem;
// Declarations for Vfs* pointer types
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
} // namespace FileSys

View File

@@ -22,6 +22,7 @@ enum {
HandleTableFull = 105,
InvalidMemoryState = 106,
InvalidMemoryPermissions = 108,
InvalidMemoryRange = 110,
InvalidThreadPriority = 112,
InvalidProcessorId = 113,
InvalidHandle = 114,
@@ -56,6 +57,7 @@ constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidA
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
ErrCodes::InvalidMemoryPermissions);
constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);

View File

@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;

View File

@@ -26,7 +26,6 @@ enum class HandleType : u32 {
AddressArbiter,
Timer,
ResourceLimit,
CodeSet,
ClientPort,
ServerPort,
ClientSession,

View File

@@ -20,13 +20,7 @@
namespace Kernel {
SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet(kernel));
codeset->name = std::move(name);
return codeset;
}
CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -224,20 +218,20 @@ void Process::FreeTLSSlot(VAddr tls_address) {
tls_slots[tls_page].reset(tls_slot);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
segment.size, memory_state)
.Unwrap();
const auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
segment.offset, segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
};
// Map CodeSet segments
MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {

View File

@@ -24,6 +24,7 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
class ResourceLimit;
struct AddressMapping {
// Address and size must be page-aligned
@@ -57,30 +58,33 @@ union ProcessFlags {
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
};
enum class ProcessStatus { Created, Running, Exited };
/**
* Indicates the status of a Process instance.
*
* @note These match the values as used by kernel,
* so new entries should only be added if RE
* shows that a new value has been introduced.
*/
enum class ProcessStatus {
Created,
CreatedWithDebuggerAttached,
Running,
WaitingForDebuggerToAttach,
DebuggerAttached,
Exiting,
Exited,
DebugBreak,
};
class ResourceLimit;
struct CodeSet final : public Object {
struct CodeSet final {
struct Segment {
std::size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name);
std::string GetTypeName() const override {
return "CodeSet";
}
std::string GetName() const override {
return name;
}
static const HandleType HANDLE_TYPE = HandleType::CodeSet;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
explicit CodeSet();
~CodeSet();
Segment& CodeSegment() {
return segments[0];
@@ -109,14 +113,7 @@ struct CodeSet final : public Object {
std::shared_ptr<std::vector<u8>> memory;
std::array<Segment, 3> segments;
VAddr entrypoint;
/// Name of the process
std::string name;
private:
explicit CodeSet(KernelCore& kernel);
~CodeSet() override;
VAddr entrypoint = 0;
};
class Process final : public Object {
@@ -219,7 +216,7 @@ public:
*/
void PrepareForTermination();
void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
void LoadModule(CodeSet module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management

View File

@@ -39,6 +39,73 @@ namespace {
constexpr bool Is4KBAligned(VAddr address) {
return (address & 0xFFF) == 0;
}
// Checks if address + size is greater than the given address
// This can return false if the size causes an overflow of a 64-bit type
// or if the given size is zero.
constexpr bool IsValidAddressRange(VAddr address, u64 size) {
return address + size > address;
}
// Checks if a given address range lies within a larger address range.
constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
VAddr address_range_end) {
const VAddr end_address = address + size - 1;
return address_range_begin <= address && end_address <= address_range_end - 1;
}
bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
vm.GetAddressSpaceEndAddress());
}
bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
vm.GetNewMapRegionEndAddress());
}
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(dst_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsValidAddressRange(src_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
return ERR_INVALID_MEMORY_RANGE;
}
const VAddr dst_end_address = dst_addr + size;
if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
vm_manager.GetHeapRegionEndAddress() > dst_addr) {
return ERR_INVALID_MEMORY_RANGE;
}
if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
vm_manager.GetMapRegionEndAddress() > dst_addr) {
return ERR_INVALID_MEMORY_RANGE;
}
return RESULT_SUCCESS;
}
} // Anonymous namespace
/// 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) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
auto* const current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
return result;
}
if (size == 0 || !Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
return current_process->MirrorMemory(dst_addr, src_addr, size);
}
/// Unmaps a region that was previously mapped with svcMapMemory
@@ -85,15 +152,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
auto* const current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
return result;
}
if (size == 0 || !Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
return current_process->UnmapMemory(dst_addr, src_addr, size);
}
/// 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) {
struct BreakReason {
union {
u64 raw;
BitField<31, 1, u64> dont_kill_application;
u32 raw;
BitField<31, 1, u32> signal_debugger;
};
};
/// Break program execution
static void Break(u64 reason, u64 info1, u64 info2) {
static void Break(u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
if (break_reason.dont_kill_application) {
if (break_reason.signal_debugger) {
LOG_ERROR(
Debug_Emulated,
"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) {
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
ASSERT(false);
Core::CurrentProcess()->PrepareForTermination();
// Kill the current thread
GetCurrentThread()->Stop();
Core::System::GetInstance().PrepareReschedule();
}
}
@@ -1025,6 +1098,29 @@ static ResultCode ClearEvent(Handle handle) {
return RESULT_SUCCESS;
}
static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
// This function currently only allows retrieving a process' status.
enum class InfoType {
Status,
};
const auto& kernel = Core::System::GetInstance().Kernel();
const auto process = kernel.HandleTable().Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
const auto info_type = static_cast<InfoType>(type);
if (info_type != InfoType::Status) {
return ERR_INVALID_ENUM_VALUE;
}
*out = static_cast<u64>(process->GetStatus());
return RESULT_SUCCESS;
}
namespace {
struct FunctionDef {
using Func = void();
@@ -1160,7 +1256,7 @@ static const FunctionDef SVC_Table[] = {
{0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
{0x7C, nullptr, "GetProcessInfo"},
{0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
{0x7D, nullptr, "CreateResourceLimit"},
{0x7E, nullptr, "SetResourceLimitLimitValue"},
{0x7F, nullptr, "CallSecureMonitor"},

View File

@@ -35,18 +35,18 @@ void SvcWrap() {
template <ResultCode func(u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0)).raw);
FuncReturn(func(static_cast<u32>(Param(0))).raw);
}
template <ResultCode func(u32, u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), (u32)Param(1)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
}
template <ResultCode func(u32*, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, (u32)Param(1)).raw;
u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -61,7 +61,7 @@ void SvcWrap() {
template <ResultCode func(u64, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), (s32)Param(1)).raw);
FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
}
template <ResultCode func(u64, u32)>
@@ -77,21 +77,29 @@ void SvcWrap() {
FuncReturn(retval);
}
template <ResultCode func(u64*, u32, u32)>
void SvcWrap() {
u64 param_1 = 0;
u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
}
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
}
template <ResultCode func(u32, u32*, u64*)>
void SvcWrap() {
u32 param_1 = 0;
u64 param_2 = 0;
ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2);
ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
Core::CurrentArmInterface().SetReg(1, param_1);
Core::CurrentArmInterface().SetReg(2, param_2);
FuncReturn(retval.raw);
@@ -100,12 +108,12 @@ void SvcWrap() {
template <ResultCode func(u64, u64, u32, u32)>
void SvcWrap() {
FuncReturn(
func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw);
func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(u32, u64, u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u64, u64, u64)>
@@ -115,25 +123,28 @@ void SvcWrap() {
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw);
FuncReturn(
func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;
ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3));
ResultCode retval =
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval.raw);
}
template <ResultCode func(u64, u64, u32, s64)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw);
FuncReturn(
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
}
template <ResultCode func(u64*, u64, u64, u64)>
@@ -147,9 +158,9 @@ void SvcWrap() {
template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval =
func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF))
.raw;
u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
static_cast<s32>(Param(5)))
.raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -172,7 +183,7 @@ void SvcWrap() {
template <ResultCode func(u32*, u64, u64, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw;
u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -181,22 +192,22 @@ template <ResultCode func(Handle*, u64, u32, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval =
func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw;
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u64, u32, s32, s64)>
void SvcWrap() {
FuncReturn(
func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3))
.raw);
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s64>(Param(3)))
.raw);
}
template <ResultCode func(u64, u32, s32, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF),
(s32)(Param(3) & 0xFFFFFFFF))
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s32>(Param(3)))
.raw);
}
@@ -226,7 +237,7 @@ void SvcWrap() {
template <void func(s64)>
void SvcWrap() {
func((s64)Param(0));
func(static_cast<s64>(Param(0)));
}
template <void func(u64, u64 len)>
@@ -239,4 +250,9 @@ void SvcWrap() {
func(Param(0), Param(1), Param(2));
}
template <void func(u32, u64, u64)>
void SvcWrap() {
func(static_cast<u32>(Param(0)), Param(1), Param(2));
}
} // namespace Kernel

View File

@@ -183,13 +183,10 @@ void Thread::ResumeFromWait() {
*/
static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext));
context = {};
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
context.pstate = 0;
context.fpcr = 0;
}
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,

View File

@@ -2,8 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <chrono>
#include <cstring>
#include <memory>
#include <optional>
#include <vector>
#include <opus.h>
@@ -33,7 +35,8 @@ public:
{1, nullptr, "SetContext"},
{2, nullptr, "DecodeInterleavedForMultiStream"},
{3, nullptr, "SetContextForMultiStream"},
{4, nullptr, "Unknown4"},
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
"DecodeInterleavedWithPerformance"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
@@ -59,8 +62,31 @@ private:
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
std::vector<opus_int16>& output) {
void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
u32 consumed = 0;
u32 sample_count = 0;
u64 performance = 0;
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
performance)) {
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
return;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(consumed);
rb.Push<u64>(performance);
rb.Push<u32>(sample_count);
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
bool Decoder_DecodeInterleaved(
u32& consumed, u32& sample_count, const std::vector<u8>& input,
std::vector<opus_int16>& output,
std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
const auto start_time = std::chrono::high_resolution_clock::now();
std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
if (sizeof(OpusHeader) > input.size())
return false;
@@ -80,8 +106,13 @@ private:
(static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
if (out_sample_count < 0)
return false;
const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
sample_count = out_sample_count;
consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
if (performance_time.has_value()) {
performance_time->get() =
std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
}
return true;
}

View File

@@ -15,6 +15,11 @@
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
namespace NvErrCodes {
enum {
InvalidNmapHandle = -22,
};
}
nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu::~nvhost_as_gpu() = default;
@@ -79,14 +84,16 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
std::memcpy(entries.data(), input.data(), input.size());
auto& gpu = Core::System::GetInstance().GPU();
for (const auto& entry : entries) {
LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
auto object = nvmap_dev->GetObject(entry.nvmap_handle);
ASSERT(object);
if (!object) {
LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
std::memcpy(output.data(), entries.data(), output.size());
return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
}
ASSERT(object->status == nvmap::Object::Status::Allocated);
@@ -167,10 +174,11 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
auto& system_instance = Core::System::GetInstance();
// Remove this memory region from the rasterizer cache.
system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
itr->second.size);
auto& gpu = system_instance.GPU();
auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
ASSERT(cpu_addr);
system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);

View File

@@ -139,14 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const FileSys::VirtualFile module_file = dir->GetFile(module);
if (module_file != nullptr) {
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr,
std::strcmp(module, "rtld") == 0, pm);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
if (module_file == nullptr) {
continue;
}
const VAddr load_addr = next_load_addr;
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
const auto tentative_next_load_addr =
AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
if (!tentative_next_load_addr) {
return ResultStatus::ErrorLoadingNSO;
}
next_load_addr = *tentative_next_load_addr;
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
}
process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());

View File

@@ -9,16 +9,11 @@
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/elf.h"
#include "core/memory.h"
using Kernel::CodeSet;
using Kernel::SharedPtr;
////////////////////////////////////////////////////////////////////////////////////////////////////
// ELF Header Constants
@@ -211,7 +206,7 @@ public:
u32 GetFlags() const {
return (u32)(header->e_flags);
}
SharedPtr<CodeSet> LoadInto(VAddr vaddr);
Kernel::CodeSet LoadInto(VAddr vaddr);
int GetNumSegments() const {
return (int)(header->e_phnum);
@@ -274,7 +269,7 @@ const char* ElfReader::GetSectionName(int section) const {
return nullptr;
}
SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
// Should we relocate?
@@ -302,8 +297,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
std::vector<u8> program_image(total_image_size);
std::size_t current_image_position = 0;
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
Kernel::CodeSet codeset;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
@@ -311,14 +305,14 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
p->p_vaddr, p->p_filesz, p->p_memsz);
if (p->p_type == PT_LOAD) {
CodeSet::Segment* codeset_segment;
Kernel::CodeSet::Segment* codeset_segment;
u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
if (permission_flags == (PF_R | PF_X)) {
codeset_segment = &codeset->CodeSegment();
codeset_segment = &codeset.CodeSegment();
} else if (permission_flags == (PF_R)) {
codeset_segment = &codeset->RODataSegment();
codeset_segment = &codeset.RODataSegment();
} else if (permission_flags == (PF_R | PF_W)) {
codeset_segment = &codeset->DataSegment();
codeset_segment = &codeset.DataSegment();
} else {
LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
p->p_flags);
@@ -345,8 +339,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
}
}
codeset->entrypoint = base_addr + header->e_entry;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
codeset.entrypoint = base_addr + header->e_entry;
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
LOG_DEBUG(Loader, "Done loading.");
@@ -397,11 +391,11 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
ElfReader elf_reader(&buffer[0]);
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address);
codeset->name = file->GetName();
Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
const VAddr entry_point = codeset.entrypoint;
process.LoadModule(codeset, codeset->entrypoint);
process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE);
process.LoadModule(std::move(codeset), entry_point);
process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown";
}
constexpr std::array<const char*, 59> RESULT_MESSAGES{
constexpr std::array<const char*, 60> RESULT_MESSAGES{
"The operation completed successfully.",
"The loader requested to load is already loaded.",
"The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
"The RomFS could not be found.",
"The ELF file has incorrect size as determined by the header.",
"There was a general error loading the NRO into emulated memory.",
"There was a general error loading the NSO into emulated memory.",
"There is no icon available.",
"There is no control data available.",
"The NAX file has a bad header.",

View File

@@ -90,6 +90,7 @@ enum class ResultStatus : u16 {
ErrorNoRomFS,
ErrorIncorrectELFFileSize,
ErrorLoadingNRO,
ErrorLoadingNSO,
ErrorNoIcon,
ErrorNoControl,
ErrorBadNAXHeader,

View File

@@ -14,7 +14,6 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nro.h"
@@ -128,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
// Read NSO header
NroHeader nro_header{};
if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
return {};
}
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -139,22 +138,21 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
}
// Build program image
auto& kernel = Core::System::GetInstance().Kernel();
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
return {};
}
Kernel::CodeSet codeset;
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
codeset->segments[i].addr = nro_header.segments[i].offset;
codeset->segments[i].offset = nro_header.segments[i].offset;
codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size);
codeset.segments[i].addr = nro_header.segments[i].offset;
codeset.segments[i].offset = nro_header.segments[i].offset;
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
}
if (!Settings::values.program_args.empty()) {
const auto arg_data = Settings::values.program_args;
codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
const auto end_offset = program_image.size();
@@ -176,16 +174,15 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
codeset->DataSegment().size += bss_size;
codeset.DataSegment().size += bss_size;
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
codeset->name = file->GetName();
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
GDBStub::RegisterModule(codeset->name, load_base, load_base);
GDBStub::RegisterModule(file.GetName(), load_base, load_base);
return true;
}
@@ -198,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
// Load NRO
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
if (!LoadNro(file, base_address)) {
if (!LoadNro(*file, base_address)) {
return ResultStatus::ErrorLoadingNRO;
}

View File

@@ -41,7 +41,7 @@ public:
bool IsRomFSUpdatable() const override;
private:
bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
std::vector<u8> icon_data;
std::unique_ptr<FileSys::NACP> nacp;

View File

@@ -12,7 +12,6 @@
#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nso.h"
@@ -94,42 +93,38 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
bool should_pass_arguments,
boost::optional<FileSys::PatchManager> pm) {
if (file == nullptr)
return {};
if (file->GetSize() < sizeof(NsoHeader))
std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm) {
if (file.GetSize() < sizeof(NsoHeader))
return {};
NsoHeader nso_header{};
if (sizeof(NsoHeader) != file->ReadObject(&nso_header))
if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
return {};
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return {};
// Build program image
auto& kernel = Core::System::GetInstance().Kernel();
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
Kernel::CodeSet codeset;
std::vector<u8> program_image;
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> data =
file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
if (nso_header.IsSegmentCompressed(i)) {
data = DecompressSegment(data, nso_header.segments[i]);
}
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
codeset->segments[i].addr = nso_header.segments[i].location;
codeset->segments[i].offset = nso_header.segments[i].location;
codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
codeset.segments[i].addr = nso_header.segments[i].location;
codeset.segments[i].offset = nso_header.segments[i].location;
codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
}
if (should_pass_arguments && !Settings::values.program_args.empty()) {
const auto arg_data = Settings::values.program_args;
codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
const auto end_offset = program_image.size();
@@ -154,12 +149,12 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
codeset->DataSegment().size += bss_size;
codeset.DataSegment().size += bss_size;
const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
program_image.resize(image_size);
// Apply patches if necessary
if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) {
if (pm && pm->HasNSOPatch(nso_header.build_id)) {
std::vector<u8> pi_header(program_image.size() + 0x100);
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
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,
}
// Load codeset for current process
codeset->name = file->GetName();
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
GDBStub::RegisterModule(codeset->name, load_base, load_base);
GDBStub::RegisterModule(file.GetName(), load_base, load_base);
return load_base + image_size;
}
@@ -187,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
// Load module
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
LoadModule(file, base_address, true);
if (!LoadModule(*file, base_address, true)) {
return ResultStatus::ErrorLoadingNSO;
}
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <optional>
#include "common/common_types.h"
#include "core/file_sys/patch_manager.h"
#include "core/loader/linker.h"
@@ -36,8 +37,9 @@ public:
return IdentifyType(file);
}
static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments,
boost::optional<FileSys::PatchManager> pm = boost::none);
static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm = {});
ResultStatus Load(Kernel::Process& process) override;
};

View File

@@ -214,6 +214,18 @@ enum class IMinMaxExchange : u64 {
XHi = 3,
};
enum class VmadType : u64 {
Size16_Low = 0,
Size16_High = 1,
Size32 = 2,
Invalid = 3,
};
enum class VmadShr : u64 {
Shr7 = 1,
Shr15 = 2,
};
enum class XmadMode : u64 {
None = 0,
CLo = 1,
@@ -452,6 +464,7 @@ union Instruction {
BitField<48, 16, u64> opcode;
union {
BitField<20, 16, u64> imm20_16;
BitField<20, 19, u64> imm20_19;
BitField<20, 32, s64> imm20_32;
BitField<45, 1, u64> negate_b;
@@ -493,6 +506,10 @@ union Instruction {
}
} lop3;
u16 GetImm20_16() const {
return static_cast<u16>(imm20_16);
}
u32 GetImm20_19() const {
u32 imm{static_cast<u32>(imm20_19)};
imm <<= 12;
@@ -1016,6 +1033,23 @@ union Instruction {
BitField<47, 2, IsberdShift> shift;
} isberd;
union {
BitField<48, 1, u64> signed_a;
BitField<38, 1, u64> is_byte_chunk_a;
BitField<36, 2, VmadType> type_a;
BitField<36, 2, u64> byte_height_a;
BitField<49, 1, u64> signed_b;
BitField<50, 1, u64> use_register_b;
BitField<30, 1, u64> is_byte_chunk_b;
BitField<28, 2, VmadType> type_b;
BitField<28, 2, u64> byte_height_b;
BitField<51, 2, VmadShr> shr;
BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
BitField<47, 1, u64> cc;
} vmad;
union {
BitField<20, 16, u64> imm20_16;
BitField<36, 1, u64> product_shift_left;
@@ -1083,6 +1117,7 @@ public:
IPA,
OUT_R, // Emit vertex/primitive
ISBERD,
VMAD,
FFMA_IMM, // Fused Multiply and Add
FFMA_CR,
FFMA_RC,
@@ -1320,6 +1355,7 @@ private:
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),

View File

@@ -2953,6 +2953,88 @@ private:
LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
break;
}
case OpCode::Id::VMAD: {
const bool signed_a = instr.vmad.signed_a == 1;
const bool signed_b = instr.vmad.signed_b == 1;
const bool result_signed = signed_a || signed_b;
boost::optional<std::string> forced_result;
auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
Tegra::Shader::VmadType type, u64 byte_height) {
const std::string value = [&]() {
if (!is_chunk) {
const auto offset = static_cast<u32>(byte_height * 8);
return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
}
const std::string zero = "0";
switch (type) {
case Tegra::Shader::VmadType::Size16_Low:
return '(' + op + " & 0xffff)";
case Tegra::Shader::VmadType::Size16_High:
return '(' + op + " >> 16)";
case Tegra::Shader::VmadType::Size32:
// TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
// this type is used (1 * 1 + 0 == 0x5b800000). Until a better
// explanation is found: assert.
UNREACHABLE_MSG("Unimplemented");
return zero;
case Tegra::Shader::VmadType::Invalid:
// Note(Rodrigo): This flag is invalid according to nvdisasm. From my
// testing (even though it's invalid) this makes the whole instruction
// assign zero to target register.
forced_result = boost::make_optional(zero);
return zero;
default:
UNREACHABLE();
return zero;
}
}();
if (is_signed) {
return "int(" + value + ')';
}
return value;
};
const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
instr.vmad.is_byte_chunk_a != 0, signed_a,
instr.vmad.type_a, instr.vmad.byte_height_a);
std::string op_b;
if (instr.vmad.use_register_b) {
op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
instr.vmad.byte_height_b);
} else {
op_b = '(' +
std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
: instr.alu.GetImm20_16()) +
')';
}
const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
std::string result;
if (forced_result) {
result = *forced_result;
} else {
result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
switch (instr.vmad.shr) {
case Tegra::Shader::VmadShr::Shr7:
result = '(' + result + " >> 7)";
break;
case Tegra::Shader::VmadShr::Shr15:
result = '(' + result + " >> 15)";
break;
}
}
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
instr.vmad.saturate == 1, 0, Register::Size::Word,
instr.vmad.cc);
break;
}
default: {
LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
UNREACHABLE();

View File

@@ -4,6 +4,7 @@
#include <cmath>
#include <cstring>
#include "common/alignment.h"
#include "common/assert.h"
#include "core/memory.h"
#include "video_core/gpu.h"
@@ -199,4 +200,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
return rgba_data;
}
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth) {
if (tiled) {
const u32 gobs_in_x = 64 / bytes_per_pixel;
const u32 gobs_in_y = 8;
const u32 gobs_in_z = 1;
const u32 aligned_width = Common::AlignUp(width, gobs_in_x);
const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
return aligned_width * aligned_height * aligned_depth * bytes_per_pixel;
} else {
return width * height * depth * bytes_per_pixel;
}
}
} // namespace Tegra::Texture

View File

@@ -32,4 +32,10 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
u32 height);
/**
* This function calculates the correct size of a texture depending if it's tiled or not.
*/
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth);
} // namespace Tegra::Texture

View File

@@ -31,6 +31,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QMessageBox>
#include <QtConcurrent/QtConcurrent>
#include <QtGui>
#include <QtWidgets>
#include <fmt/format.h>
@@ -171,6 +172,9 @@ GMainWindow::GMainWindow()
.arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
show();
// Gen keys if necessary
OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
// Necessary to load titles from nand in gamelist.
Service::FileSystem::CreateFactories(vfs);
game_list->LoadCompatibilityList();
@@ -443,6 +447,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help
connect(ui.action_Rederive, &QAction::triggered, this,
std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
}
@@ -1375,6 +1381,82 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
}
}
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (behavior == ReinitializeKeyBehavior::Warning) {
const auto res = QMessageBox::information(
this, tr("Confirm Key Rederivation"),
tr("You are about to force rederive all of your keys. \nIf you do not know what this "
"means or what you are doing, \nthis is a potentially destructive action. \nPlease "
"make "
"sure this is what you want \nand optionally make backups.\n\nThis will delete your "
"autogenerated key files and re-run the key derivation module."),
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
if (res == QMessageBox::Cancel)
return;
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
"prod.keys_autogenerated");
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
"console.keys_autogenerated");
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
"title.keys_autogenerated");
}
Core::Crypto::KeyManager keys{};
if (keys.BaseDeriveNecessary()) {
Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
const auto function = [this, &keys, &pdm] {
keys.PopulateFromPartitionData(pdm);
Service::FileSystem::CreateFactories(vfs);
keys.DeriveETicket(pdm);
};
QString errors;
if (!pdm.HasFuses())
errors += tr("- Missing fuses - Cannot derive SBK\n");
if (!pdm.HasBoot0())
errors += tr("- Missing BOOT0 - Cannot derive master keys\n");
if (!pdm.HasPackage2())
errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n");
if (!pdm.HasProdInfo())
errors += tr("- Missing PRODINFO - Cannot derive title keys\n");
if (!errors.isEmpty()) {
QMessageBox::warning(
this, tr("Warning Missing Derivation Components"),
tr("The following are missing from your configuration that may hinder key "
"derivation. It will be attempted but may not complete.\n\n") +
errors);
}
QProgressDialog prog;
prog.setRange(0, 0);
prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
"system's performance."));
prog.setWindowTitle(tr("Deriving Keys"));
prog.show();
auto future = QtConcurrent::run(function);
while (!future.isFinished()) {
QCoreApplication::processEvents();
}
prog.close();
}
Service::FileSystem::CreateFactories(vfs);
if (behavior == ReinitializeKeyBehavior::Warning) {
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
}
}
bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true;

View File

@@ -41,6 +41,11 @@ enum class EmulatedDirectoryTarget {
SDMC,
};
enum class ReinitializeKeyBehavior {
NoWarning,
Warning,
};
namespace DiscordRPC {
class DiscordInterface;
}
@@ -167,6 +172,7 @@ private slots:
void HideFullscreen();
void ToggleWindowMode();
void OnCoreError(Core::System::ResultStatus, std::string);
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
private:
void UpdateStatusBar();

View File

@@ -103,6 +103,7 @@
</property>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_Rederive"/>
<addaction name="action_About"/>
</widget>
<addaction name="menu_File"/>
@@ -159,6 +160,11 @@
<string>&amp;Stop</string>
</property>
</action>
<action name="action_Rederive">
<property name="text">
<string>Reinitialize keys...</string>
</property>
</action>
<action name="action_About">
<property name="text">
<string>About yuzu</string>