mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
exosphere: update to support 10.0.0
This commit is contained in:
parent
6719abec65
commit
aa4c79cd9c
15 changed files with 344 additions and 149 deletions
|
@ -263,19 +263,20 @@ uint32_t fuse_get_5x_key_generation(void) {
|
||||||
/* Returns the fuse version expected for the firmware. */
|
/* Returns the fuse version expected for the firmware. */
|
||||||
uint32_t fuse_get_expected_fuse_version(uint32_t target_firmware) {
|
uint32_t fuse_get_expected_fuse_version(uint32_t target_firmware) {
|
||||||
static const uint8_t expected_versions[ATMOSPHERE_TARGET_FIRMWARE_COUNT+1] = {
|
static const uint8_t expected_versions[ATMOSPHERE_TARGET_FIRMWARE_COUNT+1] = {
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_100] = 1,
|
[ATMOSPHERE_TARGET_FIRMWARE_100] = 1,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_200] = 2,
|
[ATMOSPHERE_TARGET_FIRMWARE_200] = 2,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_300] = 3,
|
[ATMOSPHERE_TARGET_FIRMWARE_300] = 3,
|
||||||
/* [ATMOSPHERE_TARGET_FIRMWARE_302] = 4, */
|
/* [ATMOSPHERE_TARGET_FIRMWARE_302] = 4, */
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_400] = 5,
|
[ATMOSPHERE_TARGET_FIRMWARE_400] = 5,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_500] = 6,
|
[ATMOSPHERE_TARGET_FIRMWARE_500] = 6,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_600] = 7,
|
[ATMOSPHERE_TARGET_FIRMWARE_600] = 7,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_620] = 8,
|
[ATMOSPHERE_TARGET_FIRMWARE_620] = 8,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_700] = 9,
|
[ATMOSPHERE_TARGET_FIRMWARE_700] = 9,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_800] = 9,
|
[ATMOSPHERE_TARGET_FIRMWARE_800] = 9,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_810] = 10,
|
[ATMOSPHERE_TARGET_FIRMWARE_810] = 10,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_900] = 11,
|
[ATMOSPHERE_TARGET_FIRMWARE_900] = 11,
|
||||||
[ATMOSPHERE_TARGET_FIRMWARE_910] = 12,
|
[ATMOSPHERE_TARGET_FIRMWARE_910] = 12,
|
||||||
|
[ATMOSPHERE_TARGET_FIRMWARE_1000] = 13,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (target_firmware > ATMOSPHERE_TARGET_FIRMWARE_COUNT) {
|
if (target_firmware > ATMOSPHERE_TARGET_FIRMWARE_COUNT) {
|
||||||
|
|
|
@ -149,6 +149,7 @@ static void setup_se(void) {
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_1000:
|
||||||
derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY);
|
derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -338,7 +339,7 @@ static bool validate_package2_metadata(package2_meta_t *metadata) {
|
||||||
|
|
||||||
/* Perform version checks. */
|
/* Perform version checks. */
|
||||||
/* We will be compatible with all package2s released before current, but not newer ones. */
|
/* We will be compatible with all package2s released before current, but not newer ones. */
|
||||||
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_910_CURRENT) {
|
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1000_CURRENT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +467,7 @@ static void copy_warmboot_bin_to_dram() {
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_1000:
|
||||||
warmboot_src = (uint8_t *)0x4003E000;
|
warmboot_src = (uint8_t *)0x4003E000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -551,6 +553,9 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
MAKE_REG32(PMC_BASE + 0x360) = 0x18C;
|
MAKE_REG32(PMC_BASE + 0x360) = 0x18C;
|
||||||
break;
|
break;
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_1000:
|
||||||
|
MAKE_REG32(PMC_BASE + 0x360) = 0x1AD;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,8 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) {
|
||||||
#define PACKAGE2_MAXVER_700_800 0xA
|
#define PACKAGE2_MAXVER_700_800 0xA
|
||||||
#define PACKAGE2_MAXVER_810 0xB
|
#define PACKAGE2_MAXVER_810 0xB
|
||||||
#define PACKAGE2_MAXVER_900 0xC
|
#define PACKAGE2_MAXVER_900 0xC
|
||||||
#define PACKAGE2_MAXVER_910_CURRENT 0xD
|
#define PACKAGE2_MAXVER_910_920 0xD
|
||||||
|
#define PACKAGE2_MAXVER_1000_CURRENT 0xE
|
||||||
|
|
||||||
#define PACKAGE2_MINVER_100 0x3
|
#define PACKAGE2_MINVER_100 0x3
|
||||||
#define PACKAGE2_MINVER_200 0x4
|
#define PACKAGE2_MINVER_200 0x4
|
||||||
|
@ -86,7 +87,8 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) {
|
||||||
#define PACKAGE2_MINVER_700_800 0xB
|
#define PACKAGE2_MINVER_700_800 0xB
|
||||||
#define PACKAGE2_MINVER_810 0xC
|
#define PACKAGE2_MINVER_810 0xC
|
||||||
#define PACKAGE2_MINVER_900 0xD
|
#define PACKAGE2_MINVER_900 0xD
|
||||||
#define PACKAGE2_MINVER_910_CURRENT 0xE
|
#define PACKAGE2_MINVER_910_920 0xE
|
||||||
|
#define PACKAGE2_MINVER_1000_CURRENT 0xF
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
|
|
20
exosphere/src/rsa_common.c
Normal file
20
exosphere/src/rsa_common.c
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rsa_common.h"
|
||||||
|
|
||||||
|
/* Instantiate the shared RSA data inside a single translation unit. */
|
||||||
|
rsa_shared_data_t g_rsa_shared_data = {};
|
36
exosphere/src/rsa_common.h
Normal file
36
exosphere/src/rsa_common.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXOSPHERE_RSA_COMMON_H
|
||||||
|
#define EXOSPHERE_RSA_COMMON_H
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint8_t user_data[0x100];
|
||||||
|
} storage_exp_mod;
|
||||||
|
struct {
|
||||||
|
uint32_t master_key_rev;
|
||||||
|
uint32_t type;
|
||||||
|
uint64_t expected_label_hash[4];
|
||||||
|
} unwrap_titlekey;
|
||||||
|
} rsa_shared_data_t __attribute__((aligned(4)));
|
||||||
|
|
||||||
|
_Static_assert(sizeof(rsa_shared_data_t) == 0x100);
|
||||||
|
|
||||||
|
extern rsa_shared_data_t g_rsa_shared_data;
|
||||||
|
|
||||||
|
#endif
|
|
@ -338,7 +338,7 @@ void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, ui
|
||||||
se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x66, false, callback);
|
se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x66, false, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)) {
|
void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int (*callback)(void)) {
|
||||||
volatile tegra_se_t *se = se_get_regs();
|
volatile tegra_se_t *se = se_get_regs();
|
||||||
uint8_t stack_buf[KEYSIZE_RSA_MAX];
|
uint8_t stack_buf[KEYSIZE_RSA_MAX];
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*cal
|
||||||
|
|
||||||
/* Endian swap the input. */
|
/* Endian swap the input. */
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
stack_buf[i] = *((uint8_t *)buf + size - i - 1);
|
stack_buf[i] = *((const uint8_t *)buf + size - i - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
se->SE_CONFIG = (ALG_RSA | DST_RSAREG);
|
se->SE_CONFIG = (ALG_RSA | DST_RSAREG);
|
||||||
|
|
|
@ -213,7 +213,7 @@ void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, co
|
||||||
void se_calculate_sha256(void *dst, const void *src, size_t src_size);
|
void se_calculate_sha256(void *dst, const void *src, size_t src_size);
|
||||||
|
|
||||||
/* RSA API */
|
/* RSA API */
|
||||||
void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void));
|
void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int (*callback)(void));
|
||||||
void se_get_exp_mod_output(void *buf, size_t size);
|
void se_get_exp_mod_output(void *buf, size_t size);
|
||||||
void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size);
|
void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||||
bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size);
|
bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size);
|
||||||
|
|
|
@ -188,6 +188,7 @@ void set_version_specific_smcs(void) {
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_1000:
|
||||||
/* No more LoadSecureExpModKey. */
|
/* No more LoadSecureExpModKey. */
|
||||||
g_smc_user_table[0xE].handler = NULL;
|
g_smc_user_table[0xE].handler = NULL;
|
||||||
g_smc_user_table[0xC].id = 0xC300D60C;
|
g_smc_user_table[0xC].id = 0xC300D60C;
|
||||||
|
@ -433,19 +434,18 @@ uint32_t smc_get_result(smc_args_t *args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t smc_exp_mod_get_result(void *buf, uint64_t size) {
|
uint32_t smc_exp_mod_get_result(void *buf, uint64_t size) {
|
||||||
if (get_exp_mod_done() != 1) {
|
uint32_t res = get_exp_mod_result();
|
||||||
return 3;
|
if (res == 0) {
|
||||||
|
if (size == 0x100) {
|
||||||
|
se_get_exp_mod_output(buf, 0x100);
|
||||||
|
/* smc_exp_mod is done now. */
|
||||||
|
clear_user_smc_in_progress();
|
||||||
|
res = 0;
|
||||||
|
} else {
|
||||||
|
res = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
if (size != 0x100) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
se_get_exp_mod_output(buf, 0x100);
|
|
||||||
|
|
||||||
/* smc_exp_mod is done now. */
|
|
||||||
clear_user_smc_in_progress();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t smc_exp_mod(smc_args_t *args) {
|
uint32_t smc_exp_mod(smc_args_t *args) {
|
||||||
|
@ -508,30 +508,31 @@ uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey_get_result(void *buf, uint64_t siz
|
||||||
uint8_t aes_wrapped_titlekey[0x10];
|
uint8_t aes_wrapped_titlekey[0x10];
|
||||||
uint8_t titlekey[0x10];
|
uint8_t titlekey[0x10];
|
||||||
uint64_t sealed_titlekey[2];
|
uint64_t sealed_titlekey[2];
|
||||||
if (get_exp_mod_done() != 1) {
|
uint32_t res = get_exp_mod_result();
|
||||||
return 3;
|
if (res == 0) {
|
||||||
|
if (size == 0x10) {
|
||||||
|
se_get_exp_mod_output(rsa_wrapped_titlekey, 0x100);
|
||||||
|
if (tkey_rsa_oaep_unwrap(aes_wrapped_titlekey, 0x10, rsa_wrapped_titlekey, 0x100) == 0x10) {
|
||||||
|
tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10);
|
||||||
|
seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10);
|
||||||
|
|
||||||
|
p_sealed_key[0] = sealed_titlekey[0];
|
||||||
|
p_sealed_key[1] = sealed_titlekey[1];
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
} else {
|
||||||
|
/* Failed to extract RSA OAEP wrapped key. */
|
||||||
|
res = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* smc_unwrap_rsa_oaep_wrapped_titlekey is done now. */
|
||||||
|
clear_user_smc_in_progress();
|
||||||
|
} else {
|
||||||
|
res = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size != 0x10) {
|
return res;
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
se_get_exp_mod_output(rsa_wrapped_titlekey, 0x100);
|
|
||||||
if (tkey_rsa_oaep_unwrap(aes_wrapped_titlekey, 0x10, rsa_wrapped_titlekey, 0x100) != 0x10) {
|
|
||||||
/* Failed to extract RSA OAEP wrapped key. */
|
|
||||||
clear_user_smc_in_progress();
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10);
|
|
||||||
seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10);
|
|
||||||
|
|
||||||
p_sealed_key[0] = sealed_titlekey[0];
|
|
||||||
p_sealed_key[1] = sealed_titlekey[1];
|
|
||||||
|
|
||||||
/* smc_unwrap_rsa_oaep_wrapped_titlekey is done now. */
|
|
||||||
clear_user_smc_in_progress();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) {
|
uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) {
|
||||||
|
|
|
@ -34,12 +34,93 @@
|
||||||
|
|
||||||
/* Globals. */
|
/* Globals. */
|
||||||
static bool g_crypt_aes_done = false;
|
static bool g_crypt_aes_done = false;
|
||||||
static bool g_exp_mod_done = false;
|
static uint32_t g_exp_mod_result = 0;
|
||||||
|
|
||||||
static uint8_t g_imported_exponents[4][0x100];
|
static uint8_t g_imported_exponents[4][0x100];
|
||||||
|
static uint8_t g_imported_moduli[4][0x100];
|
||||||
|
static bool g_is_modulus_verified[4];
|
||||||
|
|
||||||
|
static const uint8_t g_rsa_public_key[4] = { 0x00, 0x01, 0x00, 0x01 };
|
||||||
|
|
||||||
|
static const uint8_t g_rsa_test_vector[0x100] = {
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
|
||||||
|
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D'
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t g_test_exp_mod_keyslot = 0;
|
||||||
|
static uint32_t g_test_exp_mod_usecase = 0;
|
||||||
|
static bool g_test_exp_mod_in_progress = false;
|
||||||
|
|
||||||
static uint8_t g_rsausecase_to_cryptousecase[5] = {1, 2, 3, 5, 6};
|
static uint8_t g_rsausecase_to_cryptousecase[5] = {1, 2, 3, 5, 6};
|
||||||
|
|
||||||
|
static void import_rsa_exponent(unsigned int which, const uint8_t *exponent, uint64_t size) {
|
||||||
|
g_is_modulus_verified[which] = false;
|
||||||
|
for (unsigned int i = 0; i < 0x100; i++) {
|
||||||
|
g_imported_exponents[which][i] = exponent[i];
|
||||||
|
g_imported_moduli[which][i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void import_rsa_modulus(unsigned int which, const uint8_t *modulus, uint64_t size) {
|
||||||
|
uint64_t clamped_size = 0x100;
|
||||||
|
if (size <= 0x100) {
|
||||||
|
clamped_size = size;
|
||||||
|
}
|
||||||
|
if (clamped_size != 0) {
|
||||||
|
/* The official secure monitor implements this via bit-fiddling, */
|
||||||
|
/* and to prevent accidental inaccuracy we will too. */
|
||||||
|
/* It's probably done to prevent errors on negative sizes. */
|
||||||
|
uint64_t remaining = 0x100;
|
||||||
|
if (size != 0x100 && (~size >= ~0xFFFFFFFFFFFFFEFFULL)) {
|
||||||
|
remaining = size;
|
||||||
|
}
|
||||||
|
memcpy(&g_imported_moduli[which][0], modulus, remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool load_imported_rsa_keypair(unsigned int keyslot, unsigned int which) {
|
||||||
|
if (!g_is_modulus_verified[which]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
set_rsa_keyslot(keyslot, g_imported_moduli[which], 0x100, g_imported_exponents[which], 0x100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rsa_modulus_public(unsigned int which, unsigned int keyslot, const uint8_t *modulus, uint64_t modulus_size, unsigned int (*callback)(void)) {
|
||||||
|
import_rsa_modulus(which, modulus, modulus_size);
|
||||||
|
set_rsa_keyslot(keyslot, modulus, modulus_size, g_rsa_public_key, 0x4);
|
||||||
|
se_exp_mod(keyslot, g_rsa_test_vector, 0x100, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rsa_modulus_private(unsigned int which, unsigned int keyslot, unsigned int (*callback)(void)) {
|
||||||
|
uint8_t exponentiated_data[0x100];
|
||||||
|
se_get_exp_mod_output(exponentiated_data, sizeof(exponentiated_data));
|
||||||
|
set_rsa_keyslot(keyslot, g_imported_moduli[which], 0x100, g_imported_exponents[which], 0x100);
|
||||||
|
se_exp_mod(keyslot, exponentiated_data, 0x100, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void validate_rsa_result(unsigned int which) {
|
||||||
|
char result[0x100];
|
||||||
|
se_get_exp_mod_output(result, sizeof(result));
|
||||||
|
if (memcmp(result, g_rsa_test_vector, sizeof(result)) == 0) {
|
||||||
|
g_is_modulus_verified[which] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_user_keyslot_valid(unsigned int keyslot) {
|
static bool is_user_keyslot_valid(unsigned int keyslot) {
|
||||||
switch (exosphere_get_target_firmware()) {
|
switch (exosphere_get_target_firmware()) {
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_100:
|
case ATMOSPHERE_TARGET_FIRMWARE_100:
|
||||||
|
@ -55,27 +136,45 @@ static bool is_user_keyslot_valid(unsigned int keyslot) {
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_1000:
|
||||||
default:
|
default:
|
||||||
return keyslot <= 5;
|
return keyslot <= 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_exp_mod_done(bool done) {
|
void set_exp_mod_result(uint32_t result) {
|
||||||
g_exp_mod_done = done;
|
g_exp_mod_result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_exp_mod_done(void) {
|
uint32_t get_exp_mod_result(void) {
|
||||||
return g_exp_mod_done;
|
return g_exp_mod_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t exp_mod_done_handler(void) {
|
uint32_t exp_mod_done_handler(void) {
|
||||||
set_exp_mod_done(true);
|
set_exp_mod_result(0);
|
||||||
|
|
||||||
se_trigger_interrupt();
|
se_trigger_interrupt();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t test_exp_mod_done_handler(void) {
|
||||||
|
if (g_test_exp_mod_in_progress) {
|
||||||
|
g_test_exp_mod_in_progress = false;
|
||||||
|
test_rsa_modulus_private(g_test_exp_mod_usecase, g_test_exp_mod_keyslot, test_exp_mod_done_handler);
|
||||||
|
} else {
|
||||||
|
validate_rsa_result(g_test_exp_mod_usecase);
|
||||||
|
if (load_imported_rsa_keypair(g_test_exp_mod_keyslot, g_test_exp_mod_usecase)) {
|
||||||
|
se_exp_mod(g_test_exp_mod_keyslot, g_rsa_shared_data.storage_exp_mod.user_data, 0x100, exp_mod_done_handler);
|
||||||
|
} else {
|
||||||
|
set_exp_mod_result(2);
|
||||||
|
se_trigger_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t user_exp_mod(smc_args_t *args) {
|
uint32_t user_exp_mod(smc_args_t *args) {
|
||||||
uint8_t modulus[0x100];
|
uint8_t modulus[0x100];
|
||||||
uint8_t exponent[0x100];
|
uint8_t exponent[0x100];
|
||||||
|
@ -108,7 +207,8 @@ uint32_t user_exp_mod(smc_args_t *args) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_exp_mod_done(false);
|
set_exp_mod_result(3);
|
||||||
|
|
||||||
/* Hardcode RSA keyslot 0. */
|
/* Hardcode RSA keyslot 0. */
|
||||||
set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size);
|
set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size);
|
||||||
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
|
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
|
||||||
|
@ -650,10 +750,21 @@ uint32_t user_secure_exp_mod(smc_args_t *args) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_exp_mod_done(false);
|
set_exp_mod_result(3);
|
||||||
|
|
||||||
/* Hardcode RSA keyslot 0. */
|
/* Hardcode RSA keyslot 0. */
|
||||||
set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[exponent_id], 0x100);
|
if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_1000) {
|
||||||
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
|
set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[exponent_id], 0x100);
|
||||||
|
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
|
||||||
|
} else if (load_imported_rsa_keypair(0, exponent_id)) {
|
||||||
|
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
|
||||||
|
} else {
|
||||||
|
memcpy(g_rsa_shared_data.storage_exp_mod.user_data, input, 0x100);
|
||||||
|
g_test_exp_mod_keyslot = 0;
|
||||||
|
g_test_exp_mod_usecase = exponent_id;
|
||||||
|
g_test_exp_mod_in_progress = true;
|
||||||
|
test_rsa_modulus_public(exponent_id, 0, modulus, 0x100, test_exp_mod_done_handler);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -700,7 +811,7 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_exp_mod_done(false);
|
set_exp_mod_result(3);
|
||||||
|
|
||||||
/* Expected label_hash occupies args->X[3] to args->X[6]. */
|
/* Expected label_hash occupies args->X[3] to args->X[6]. */
|
||||||
tkey_set_expected_label_hash(&args->X[3]);
|
tkey_set_expected_label_hash(&args->X[3]);
|
||||||
|
@ -879,6 +990,7 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int exponent_id;
|
unsigned int exponent_id;
|
||||||
|
bool import_modulus;
|
||||||
|
|
||||||
switch (usecase) {
|
switch (usecase) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -888,22 +1000,33 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) {
|
||||||
return 0;
|
return 0;
|
||||||
case 1:
|
case 1:
|
||||||
exponent_id = 1;
|
exponent_id = 1;
|
||||||
|
import_modulus = false;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
exponent_id = 0;
|
exponent_id = 0;
|
||||||
|
import_modulus = true;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
exponent_id = 2;
|
exponent_id = 2;
|
||||||
|
import_modulus = false;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
exponent_id = 3;
|
exponent_id = 3;
|
||||||
|
import_modulus = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
generic_panic();
|
generic_panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy key to global. */
|
/* Modulus import isn't implemented on < 10.0.0. */
|
||||||
memcpy(g_imported_exponents[exponent_id], user_data, 0x100);
|
import_modulus &= (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_1000);
|
||||||
|
|
||||||
|
/* Import the key. */
|
||||||
|
import_rsa_exponent(exponent_id, user_data, 0x100);
|
||||||
|
if (import_modulus) {
|
||||||
|
import_rsa_modulus(exponent_id, user_data + 0x100, 0x100);
|
||||||
|
g_is_modulus_verified[exponent_id] = true;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args);
|
||||||
void set_crypt_aes_done(bool done);
|
void set_crypt_aes_done(bool done);
|
||||||
bool get_crypt_aes_done(void);
|
bool get_crypt_aes_done(void);
|
||||||
|
|
||||||
void set_exp_mod_done(bool done);
|
void set_exp_mod_result(uint32_t result);
|
||||||
bool get_exp_mod_done(void);
|
uint32_t get_exp_mod_result(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*expected_label_hash
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,14 +25,10 @@
|
||||||
#include "masterkey.h"
|
#include "masterkey.h"
|
||||||
#include "se.h"
|
#include "se.h"
|
||||||
|
|
||||||
static uint64_t g_tkey_expected_label_hash[4];
|
|
||||||
static unsigned int g_tkey_master_key_rev = MASTERKEY_REVISION_MAX;
|
|
||||||
static unsigned int g_tkey_type = 0;
|
|
||||||
|
|
||||||
/* Set the expected db prefix. */
|
/* Set the expected db prefix. */
|
||||||
void tkey_set_expected_label_hash(uint64_t *label_hash) {
|
void tkey_set_expected_label_hash(uint64_t *label_hash) {
|
||||||
for (unsigned int i = 0; i < 4; i++) {
|
for (unsigned int i = 0; i < 4; i++) {
|
||||||
g_tkey_expected_label_hash[i] = label_hash[i];
|
g_rsa_shared_data.unwrap_titlekey.expected_label_hash[i] = label_hash[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +36,7 @@ void tkey_set_master_key_rev(unsigned int master_key_rev) {
|
||||||
if (master_key_rev >= MASTERKEY_REVISION_MAX) {
|
if (master_key_rev >= MASTERKEY_REVISION_MAX) {
|
||||||
generic_panic();
|
generic_panic();
|
||||||
}
|
}
|
||||||
g_tkey_master_key_rev = master_key_rev;
|
g_rsa_shared_data.unwrap_titlekey.master_key_rev = master_key_rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tkey_validate_type(unsigned int type) {
|
static void tkey_validate_type(unsigned int type) {
|
||||||
|
@ -51,7 +47,7 @@ static void tkey_validate_type(unsigned int type) {
|
||||||
|
|
||||||
void tkey_set_type(unsigned int type) {
|
void tkey_set_type(unsigned int type) {
|
||||||
tkey_validate_type(type);
|
tkey_validate_type(type);
|
||||||
g_tkey_type = type;
|
g_rsa_shared_data.unwrap_titlekey.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reference for MGF1 can be found here: https://en.wikipedia.org/wiki/Mask_generation_function#MGF1 */
|
/* Reference for MGF1 can be found here: https://en.wikipedia.org/wiki/Mask_generation_function#MGF1 */
|
||||||
|
@ -116,7 +112,7 @@ size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_si
|
||||||
uint8_t *db = message + 0x21;
|
uint8_t *db = message + 0x21;
|
||||||
|
|
||||||
/* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */
|
/* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */
|
||||||
uint8_t *expected_label_hash = (uint8_t *)(&g_tkey_expected_label_hash[0]);
|
uint8_t *expected_label_hash = (uint8_t *)(&g_rsa_shared_data.unwrap_titlekey.expected_label_hash[0]);
|
||||||
|
|
||||||
/* Unmask the salt. */
|
/* Unmask the salt. */
|
||||||
calculate_mgf1_and_xor(salt, 0x20, db, 0xDF);
|
calculate_mgf1_and_xor(salt, 0x20, db, 0xDF);
|
||||||
|
@ -171,13 +167,13 @@ static const uint8_t titlekek_sources[TITLEKEY_TYPE_MAX+1][0x10] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
if (g_tkey_master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) {
|
if (g_rsa_shared_data.unwrap_titlekey.master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) {
|
||||||
generic_panic();
|
generic_panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generate the appropriate titlekek into keyslot 9. */
|
/* Generate the appropriate titlekek into keyslot 9. */
|
||||||
unsigned int master_keyslot = mkey_get_keyslot(g_tkey_master_key_rev);
|
unsigned int master_keyslot = mkey_get_keyslot(g_rsa_shared_data.unwrap_titlekey.master_key_rev);
|
||||||
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_sources[g_tkey_type], 0x10);
|
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_sources[g_rsa_shared_data.unwrap_titlekey.type], 0x10);
|
||||||
|
|
||||||
/* Unwrap the titlekey using the titlekek. */
|
/* Unwrap the titlekey using the titlekek. */
|
||||||
se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10);
|
se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define EXOSPHERE_TITLEKEY_H
|
#define EXOSPHERE_TITLEKEY_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "rsa_common.h"
|
||||||
|
|
||||||
#define TITLEKEY_TYPE_MAX 0x1
|
#define TITLEKEY_TYPE_MAX 0x1
|
||||||
|
|
||||||
|
|
|
@ -66,9 +66,13 @@ namespace ams::hos {
|
||||||
break;
|
break;
|
||||||
case exosphere::TargetFirmware_900:
|
case exosphere::TargetFirmware_900:
|
||||||
g_hos_version = hos::Version_9_0_0;
|
g_hos_version = hos::Version_9_0_0;
|
||||||
|
break;
|
||||||
case exosphere::TargetFirmware_910:
|
case exosphere::TargetFirmware_910:
|
||||||
g_hos_version = hos::Version_9_1_0;
|
g_hos_version = hos::Version_9_1_0;
|
||||||
break;
|
break;
|
||||||
|
case exosphere::TargetFirmware_1000:
|
||||||
|
g_hos_version = hos::Version_10_0_0;
|
||||||
|
break;
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +143,11 @@ namespace ams::hos {
|
||||||
minor = 1;
|
minor = 1;
|
||||||
micro = 0;
|
micro = 0;
|
||||||
break;
|
break;
|
||||||
|
case hos::Version_10_0_0:
|
||||||
|
major = 10;
|
||||||
|
minor = 0;
|
||||||
|
micro = 0;
|
||||||
|
break;
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
}
|
}
|
||||||
hosversionSet(MAKEHOSVERSION(major, minor, micro));
|
hosversionSet(MAKEHOSVERSION(major, minor, micro));
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 10
|
#define ATMOSPHERE_RELEASE_VERSION_MINOR 10
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 5
|
#define ATMOSPHERE_RELEASE_VERSION_MICRO 6
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||||
|
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 9
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 10
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 2
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
||||||
|
|
|
@ -15,20 +15,21 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_100 1
|
#define ATMOSPHERE_TARGET_FIRMWARE_100 1
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_200 2
|
#define ATMOSPHERE_TARGET_FIRMWARE_200 2
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_300 3
|
#define ATMOSPHERE_TARGET_FIRMWARE_300 3
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_400 4
|
#define ATMOSPHERE_TARGET_FIRMWARE_400 4
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_500 5
|
#define ATMOSPHERE_TARGET_FIRMWARE_500 5
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_600 6
|
#define ATMOSPHERE_TARGET_FIRMWARE_600 6
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_620 7
|
#define ATMOSPHERE_TARGET_FIRMWARE_620 7
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_700 8
|
#define ATMOSPHERE_TARGET_FIRMWARE_700 8
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_800 9
|
#define ATMOSPHERE_TARGET_FIRMWARE_800 9
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_810 10
|
#define ATMOSPHERE_TARGET_FIRMWARE_810 10
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_900 11
|
#define ATMOSPHERE_TARGET_FIRMWARE_900 11
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_910 12
|
#define ATMOSPHERE_TARGET_FIRMWARE_910 12
|
||||||
|
#define ATMOSPHERE_TARGET_FIRMWARE_1000 13
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_910
|
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_1000
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE_100
|
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE_100
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||||
|
|
Loading…
Reference in a new issue