mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
PRODINFO: Revamp blanking/write disallow policy. (#913)
* exo/fusee: hookup new prodinfo settings * fusee: new scheme doesn't need FLAGS_DEFAULT * fusee: fix c/p errors * ams.mitm: completely revamp prodinfo backup mechanism * ams.mitm: Implement revamped blanking/write policy * strat: make early boot more debuggable * exo: condense flag logic
This commit is contained in:
parent
6ac1ff6f24
commit
3bc2d79384
42 changed files with 1355 additions and 142 deletions
|
@ -5,16 +5,6 @@ stage2_mtc_path = atmosphere/fusee-mtc.bin
|
||||||
stage2_addr = 0xF0000000
|
stage2_addr = 0xF0000000
|
||||||
stage2_entrypoint = 0xF0000000
|
stage2_entrypoint = 0xF0000000
|
||||||
|
|
||||||
[exosphere]
|
|
||||||
; Note: Disabling debugmode will cause parts of ams.tma to not work, in the future.
|
|
||||||
debugmode = 1
|
|
||||||
debugmode_user = 0
|
|
||||||
; Note: Disabling usermode exception handlers will cause atmosphere to not fail gracefully under error conditions.
|
|
||||||
; Support will not be provided to users who disable these. If you do not know what you are doing, leave them on.
|
|
||||||
disable_user_exception_handlers = 0
|
|
||||||
; Note: It's currently unknown what effects enabling the usermode PMU register access may have on official code.
|
|
||||||
enable_user_pmu_access = 0
|
|
||||||
|
|
||||||
[stratosphere]
|
[stratosphere]
|
||||||
; To force-enable nogc, add nogc = 1
|
; To force-enable nogc, add nogc = 1
|
||||||
; To force-disable nogc, add nogc = 0
|
; To force-disable nogc, add nogc = 0
|
||||||
|
|
45
config_templates/exosphere.ini
Normal file
45
config_templates/exosphere.ini
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Key: debugmode, default: 1.
|
||||||
|
# Desc: Controls whether kernel is debug mode.
|
||||||
|
# Disabling this may break Atmosphere's debugger in a future release.
|
||||||
|
|
||||||
|
# Key: debugmode_user, default: 0.
|
||||||
|
# Desc: Controls whether userland is debug mode.
|
||||||
|
|
||||||
|
# Key: disable_user_exception_handlers, default: 0.
|
||||||
|
# Desc: Controls whether user exception handlers are executed on error.
|
||||||
|
# NOTE: This will cause atmosphere to not fail gracefully.
|
||||||
|
# Support may not be provided to users tho disable these.
|
||||||
|
# If you do not know what you are doing, leave them on.
|
||||||
|
|
||||||
|
# Key: enable_user_pmu_access, default: 0.
|
||||||
|
# Desc: Controls whether userland has access to the PMU registers.
|
||||||
|
# NOTE: It is unknown what effects this has on official code.
|
||||||
|
|
||||||
|
# Key: blank_prodinfo_sysmmc, default: 0.
|
||||||
|
# Desc: Controls whether PRODINFO should be blanked in sysmmc.
|
||||||
|
# This will cause the system to see dummied out keys and
|
||||||
|
# serial number information.
|
||||||
|
# NOTE: This is not known to be safe, as data may be
|
||||||
|
# cached elsewhere in the system. Usage is not encouraged.
|
||||||
|
|
||||||
|
# Key: blank_prodinfo_emummc, default: 0.
|
||||||
|
# Desc: Controls whether PRODINFO should be blanked in emummc.
|
||||||
|
# NOTE: This is not known to be safe, as data may be
|
||||||
|
# cached elsewhere in the system. Usage is not encouraged.
|
||||||
|
|
||||||
|
# Key: allow_writing_to_cal_sysmmc, default: 0.
|
||||||
|
# Desc: Controls whether PRODINFO can be written by homebrew in sysmmc.
|
||||||
|
# NOTE: Usage of this setting is strongly discouraged without
|
||||||
|
# a safe backup elsewhere. Turning this on will also cause Atmosphere
|
||||||
|
# to ensure a safe backup of calibration data is stored in unused
|
||||||
|
# mmc space, encrypted to prevent detection. This backup can be used
|
||||||
|
# to prevent unrecoverable edits in emergencies.
|
||||||
|
|
||||||
|
[exosphere]
|
||||||
|
debugmode=1
|
||||||
|
debugmode_user=0
|
||||||
|
disable_user_exception_handlers=0
|
||||||
|
enable_user_pmu_access=0
|
||||||
|
blank_prodinfo_sysmmc=0
|
||||||
|
blank_prodinfo_emummc=0
|
||||||
|
allow_writing_to_cal_sysmmc=0
|
|
@ -290,7 +290,15 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
|
||||||
break;
|
break;
|
||||||
case CONFIGITEM_HAS_RCM_BUG_PATCH:
|
case CONFIGITEM_HAS_RCM_BUG_PATCH:
|
||||||
/* UNOFFICIAL: Gets whether this unit has the RCM bug patched. */
|
/* UNOFFICIAL: Gets whether this unit has the RCM bug patched. */
|
||||||
*p_outvalue = (int)(fuse_has_rcm_bug_patch());;
|
*p_outvalue = (int)(fuse_has_rcm_bug_patch());
|
||||||
|
break;
|
||||||
|
case CONFIGITEM_SHOULD_BLANK_PRODINFO:
|
||||||
|
/* UNOFFICIAL: Gets whether this unit should simulate a "blanked" PRODINFO. */
|
||||||
|
*p_outvalue = exosphere_should_blank_prodinfo();
|
||||||
|
break;
|
||||||
|
case CONFIGITEM_ALLOW_CAL_WRITES:
|
||||||
|
/* UNOFFICIAL: Gets whether this unit should allow writing to the calibration partition. */
|
||||||
|
*p_outvalue = exosphere_should_allow_writing_to_cal();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
result = 2;
|
result = 2;
|
||||||
|
|
|
@ -45,6 +45,8 @@ typedef enum {
|
||||||
CONFIGITEM_NEEDS_SHUTDOWN = 65002,
|
CONFIGITEM_NEEDS_SHUTDOWN = 65002,
|
||||||
CONFIGITEM_EXOSPHERE_VERHASH = 65003,
|
CONFIGITEM_EXOSPHERE_VERHASH = 65003,
|
||||||
CONFIGITEM_HAS_RCM_BUG_PATCH = 65004,
|
CONFIGITEM_HAS_RCM_BUG_PATCH = 65004,
|
||||||
|
CONFIGITEM_SHOULD_BLANK_PRODINFO = 65005,
|
||||||
|
CONFIGITEM_ALLOW_CAL_WRITES = 65006,
|
||||||
} ConfigItem;
|
} ConfigItem;
|
||||||
|
|
||||||
#define REBOOT_KIND_NO_REBOOT 0
|
#define REBOOT_KIND_NO_REBOOT 0
|
||||||
|
|
|
@ -27,6 +27,9 @@ static bool g_has_loaded_config = false;
|
||||||
|
|
||||||
#define EXOSPHERE_CHECK_FLAG(flag) ((g_exosphere_cfg.flags & flag) != 0)
|
#define EXOSPHERE_CHECK_FLAG(flag) ((g_exosphere_cfg.flags & flag) != 0)
|
||||||
|
|
||||||
|
static unsigned int exosphere_is_emummc() {
|
||||||
|
return g_exosphere_cfg.emummc_cfg.base_cfg.magic == MAGIC_EMUMMC_CONFIG && g_exosphere_cfg.emummc_cfg.base_cfg.type != EMUMMC_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read config out of IRAM, return target firmware version. */
|
/* Read config out of IRAM, return target firmware version. */
|
||||||
unsigned int exosphere_load_config(void) {
|
unsigned int exosphere_load_config(void) {
|
||||||
|
@ -92,6 +95,26 @@ unsigned int exosphere_should_enable_usermode_pmu_access(void) {
|
||||||
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS);
|
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int exosphere_should_blank_prodinfo(void) {
|
||||||
|
if (!g_has_loaded_config) {
|
||||||
|
generic_panic();
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_BLANK_PRODINFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int exosphere_should_allow_writing_to_cal(void) {
|
||||||
|
if (!g_has_loaded_config) {
|
||||||
|
generic_panic();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exosphere_is_emummc()) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const exo_emummc_config_t *exosphere_get_emummc_config(void) {
|
const exo_emummc_config_t *exosphere_get_emummc_config(void) {
|
||||||
if (!g_has_loaded_config) {
|
if (!g_has_loaded_config) {
|
||||||
generic_panic();
|
generic_panic();
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||||
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
||||||
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
|
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
|
||||||
|
#define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u)
|
||||||
|
#define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u)
|
||||||
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -60,6 +62,8 @@ unsigned int exosphere_should_override_debugmode_priv(void);
|
||||||
unsigned int exosphere_should_override_debugmode_user(void);
|
unsigned int exosphere_should_override_debugmode_user(void);
|
||||||
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
|
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
|
||||||
unsigned int exosphere_should_enable_usermode_pmu_access(void);
|
unsigned int exosphere_should_enable_usermode_pmu_access(void);
|
||||||
|
unsigned int exosphere_should_blank_prodinfo(void);
|
||||||
|
unsigned int exosphere_should_allow_writing_to_cal(void);
|
||||||
|
|
||||||
const exo_emummc_config_t *exosphere_get_emummc_config(void);
|
const exo_emummc_config_t *exosphere_get_emummc_config(void);
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||||
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
||||||
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
|
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
|
||||||
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
#define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u)
|
||||||
|
#define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
|
@ -50,5 +51,18 @@ _Static_assert(sizeof(exosphere_config_t) == 0x20 + sizeof(exo_emummc_config_t),
|
||||||
#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user"
|
#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user"
|
||||||
#define EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY "disable_user_exception_handlers"
|
#define EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY "disable_user_exception_handlers"
|
||||||
#define EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY "enable_user_pmu_access"
|
#define EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY "enable_user_pmu_access"
|
||||||
|
#define EXOSPHERE_BLANK_PRODINFO_SYSMMC_KEY "blank_prodinfo_sysmmc"
|
||||||
|
#define EXOSPHERE_BLANK_PRODINFO_EMUMMC_KEY "blank_prodinfo_emummc"
|
||||||
|
#define EXOSPHERE_ALLOW_WRITING_TO_CAL_SYSMMC_KEY "allow_writing_to_cal_sysmmc"
|
||||||
|
|
||||||
#endif
|
typedef struct {
|
||||||
|
int debugmode;
|
||||||
|
int debugmode_user;
|
||||||
|
int disable_user_exception_handlers;
|
||||||
|
int enable_user_pmu_access;
|
||||||
|
int blank_prodinfo_sysmmc;
|
||||||
|
int blank_prodinfo_emummc;
|
||||||
|
int allow_writing_to_cal_sysmmc;
|
||||||
|
} exosphere_parse_cfg_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -134,38 +134,57 @@ static int emummc_ini_handler(void *user, const char *section, const char *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exosphere_ini_handler(void *user, const char *section, const char *name, const char *value) {
|
static int exosphere_ini_handler(void *user, const char *section, const char *name, const char *value) {
|
||||||
exosphere_config_t *exo_cfg = (exosphere_config_t *)user;
|
exosphere_parse_cfg_t *parse_cfg = (exosphere_parse_cfg_t *)user;
|
||||||
int tmp = 0;
|
int tmp = 0;
|
||||||
if (strcmp(section, "exosphere") == 0) {
|
if (strcmp(section, "exosphere") == 0) {
|
||||||
if (strcmp(name, EXOSPHERE_TARGETFW_KEY) == 0) {
|
if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
|
||||||
sscanf(value, "%ld", &exo_cfg->target_firmware);
|
|
||||||
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
|
|
||||||
sscanf(value, "%d", &tmp);
|
sscanf(value, "%d", &tmp);
|
||||||
if (tmp) {
|
if (tmp == 1) {
|
||||||
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV;
|
parse_cfg->debugmode = 1;
|
||||||
} else {
|
} else if (tmp == 0) {
|
||||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV);
|
parse_cfg->debugmode = 0;
|
||||||
}
|
}
|
||||||
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_USER_KEY) == 0) {
|
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_USER_KEY) == 0) {
|
||||||
sscanf(value, "%d", &tmp);
|
sscanf(value, "%d", &tmp);
|
||||||
if (tmp) {
|
if (tmp == 1) {
|
||||||
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER;
|
parse_cfg->debugmode_user = 1;
|
||||||
} else {
|
} else if (tmp == 0) {
|
||||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_USER);
|
parse_cfg->debugmode_user = 0;
|
||||||
}
|
}
|
||||||
} else if (strcmp(name, EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY) == 0) {
|
} else if (strcmp(name, EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY) == 0) {
|
||||||
sscanf(value, "%d", &tmp);
|
sscanf(value, "%d", &tmp);
|
||||||
if (tmp) {
|
if (tmp == 1) {
|
||||||
exo_cfg->flags |= EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS;
|
parse_cfg->disable_user_exception_handlers = 1;
|
||||||
} else {
|
} else if (tmp == 0) {
|
||||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
|
parse_cfg->disable_user_exception_handlers = 0;
|
||||||
}
|
}
|
||||||
} else if (strcmp(name, EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY) == 0) {
|
} else if (strcmp(name, EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY) == 0) {
|
||||||
sscanf(value, "%d", &tmp);
|
sscanf(value, "%d", &tmp);
|
||||||
if (tmp) {
|
if (tmp == 1) {
|
||||||
exo_cfg->flags |= EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS;
|
parse_cfg->enable_user_pmu_access = 1;
|
||||||
} else {
|
} else if (tmp == 0) {
|
||||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS);
|
parse_cfg->enable_user_pmu_access = 0;
|
||||||
|
}
|
||||||
|
} else if (strcmp(name, EXOSPHERE_BLANK_PRODINFO_SYSMMC_KEY) == 0) {
|
||||||
|
sscanf(value, "%d", &tmp);
|
||||||
|
if (tmp == 1) {
|
||||||
|
parse_cfg->blank_prodinfo_sysmmc = 1;
|
||||||
|
} else if (tmp == 0) {
|
||||||
|
parse_cfg->blank_prodinfo_sysmmc = 0;
|
||||||
|
}
|
||||||
|
} else if (strcmp(name, EXOSPHERE_BLANK_PRODINFO_EMUMMC_KEY) == 0) {
|
||||||
|
sscanf(value, "%d", &tmp);
|
||||||
|
if (tmp == 1) {
|
||||||
|
parse_cfg->blank_prodinfo_emummc = 1;
|
||||||
|
} else if (tmp == 0) {
|
||||||
|
parse_cfg->blank_prodinfo_emummc = 0;
|
||||||
|
}
|
||||||
|
} else if (strcmp(name, EXOSPHERE_ALLOW_WRITING_TO_CAL_SYSMMC_KEY) == 0) {
|
||||||
|
sscanf(value, "%d", &tmp);
|
||||||
|
if (tmp == 1) {
|
||||||
|
parse_cfg->allow_writing_to_cal_sysmmc = 1;
|
||||||
|
} else if (tmp == 0) {
|
||||||
|
parse_cfg->allow_writing_to_cal_sysmmc = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -348,15 +367,42 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
|
||||||
exo_cfg.target_firmware = target_firmware;
|
exo_cfg.target_firmware = target_firmware;
|
||||||
memcpy(&exo_cfg.emummc_cfg, exo_emummc_cfg, sizeof(*exo_emummc_cfg));
|
memcpy(&exo_cfg.emummc_cfg, exo_emummc_cfg, sizeof(*exo_emummc_cfg));
|
||||||
|
|
||||||
|
const bool is_emummc = exo_emummc_cfg->base_cfg.magic == MAGIC_EMUMMC_CONFIG && exo_emummc_cfg->base_cfg.type != EMUMMC_TYPE_NONE;
|
||||||
|
|
||||||
if (keygen_type) {
|
if (keygen_type) {
|
||||||
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT | EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
|
exo_cfg.flags = EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
|
||||||
} else {
|
} else {
|
||||||
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT;
|
exo_cfg.flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ini_parse_string(get_loader_ctx()->bct0, exosphere_ini_handler, &exo_cfg) < 0) {
|
/* Setup exosphere parse configuration with defaults. */
|
||||||
fatal_error("[NXBOOT] Failed to parse BCT.ini!\n");
|
exosphere_parse_cfg_t parse_cfg = {
|
||||||
|
.debugmode = 1,
|
||||||
|
.debugmode_user = 0,
|
||||||
|
.disable_user_exception_handlers = 0,
|
||||||
|
.enable_user_pmu_access = 0,
|
||||||
|
.blank_prodinfo_sysmmc = 0,
|
||||||
|
.blank_prodinfo_emummc = 0,
|
||||||
|
.allow_writing_to_cal_sysmmc = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* If we have an ini to read, parse it. */
|
||||||
|
char *exosphere_ini = calloc(1, 0x10000);
|
||||||
|
if (read_from_file(exosphere_ini, 0xFFFF, "exosphere.ini")) {
|
||||||
|
if (ini_parse_string(exosphere_ini, exosphere_ini_handler, &parse_cfg) < 0) {
|
||||||
|
fatal_error("[NXBOOT] Failed to parse exosphere.ini!\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
free(exosphere_ini);
|
||||||
|
|
||||||
|
/* Apply parse config. */
|
||||||
|
if (parse_cfg.debugmode) exo_cfg.flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV;
|
||||||
|
if (parse_cfg.debugmode_user) exo_cfg.flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER;
|
||||||
|
if (parse_cfg.disable_user_exception_handlers) exo_cfg.flags |= EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS;
|
||||||
|
if (parse_cfg.enable_user_pmu_access) exo_cfg.flags |= EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS;
|
||||||
|
if (parse_cfg.blank_prodinfo_sysmmc && !is_emummc) exo_cfg.flags |= EXOSPHERE_FLAG_BLANK_PRODINFO;
|
||||||
|
if (parse_cfg.blank_prodinfo_emummc && is_emummc) exo_cfg.flags |= EXOSPHERE_FLAG_BLANK_PRODINFO;
|
||||||
|
if (parse_cfg.allow_writing_to_cal_sysmmc) exo_cfg.flags |= EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC;
|
||||||
|
|
||||||
if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) {
|
if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) {
|
||||||
fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n");
|
fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n");
|
||||||
|
|
|
@ -22,4 +22,8 @@ namespace ams {
|
||||||
/* Will be called by libstratosphere on crash. */
|
/* Will be called by libstratosphere on crash. */
|
||||||
void CrashHandler(ThreadExceptionDump *ctx);
|
void CrashHandler(ThreadExceptionDump *ctx);
|
||||||
|
|
||||||
|
/* API for boot sysmodule. */
|
||||||
|
void InitializeForBoot();
|
||||||
|
void SetInitialRebootPayload(const void *src, size_t src_size);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,11 @@ namespace ams::exosphere {
|
||||||
|
|
||||||
bool IsRcmBugPatched();
|
bool IsRcmBugPatched();
|
||||||
|
|
||||||
|
bool ShouldBlankProdInfo();
|
||||||
|
bool ShouldAllowWritesToProdInfo();
|
||||||
|
|
||||||
|
u64 GetDeviceId();
|
||||||
|
|
||||||
void CopyToIram(uintptr_t iram_dst, const void *dram_src, size_t size);
|
void CopyToIram(uintptr_t iram_dst, const void *dram_src, size_t size);
|
||||||
void CopyFromIram(void *dram_dst, uintptr_t iram_src, size_t size);
|
void CopyFromIram(void *dram_dst, uintptr_t iram_src, size_t size);
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "settings/settings_types.hpp"
|
#include <stratosphere/settings/settings_types.hpp>
|
||||||
#include "settings/settings_fwdbg_types.hpp"
|
#include <stratosphere/settings/settings_fwdbg_types.hpp>
|
||||||
#include "settings/settings_fwdbg_api.hpp"
|
#include <stratosphere/settings/settings_fwdbg_api.hpp>
|
||||||
#include "settings/system/settings_error_report.hpp"
|
#include <stratosphere/settings/factory/settings_serial_number.hpp>
|
||||||
#include "settings/system/settings_firmware_version.hpp"
|
#include <stratosphere/settings/factory/settings_device_certificate.hpp>
|
||||||
#include "settings/system/settings_product_model.hpp"
|
#include <stratosphere/settings/system/settings_error_report.hpp>
|
||||||
#include "settings/system/settings_region.hpp"
|
#include <stratosphere/settings/system/settings_firmware_version.hpp>
|
||||||
#include "settings/system/settings_serial_number.hpp"
|
#include <stratosphere/settings/system/settings_product_model.hpp>
|
||||||
|
#include <stratosphere/settings/system/settings_region.hpp>
|
||||||
|
#include <stratosphere/settings/system/settings_serial_number.hpp>
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::settings::factory {
|
||||||
|
|
||||||
|
struct EccP256DeviceCertificate {
|
||||||
|
u8 data[0x180];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(EccP256DeviceCertificate) == 0x180);
|
||||||
|
static_assert(std::is_pod<EccP256DeviceCertificate>::value);
|
||||||
|
|
||||||
|
struct EccB233DeviceCertificate {
|
||||||
|
u8 data[0x180];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(EccB233DeviceCertificate) == 0x180);
|
||||||
|
static_assert(std::is_pod<EccB233DeviceCertificate>::value);
|
||||||
|
|
||||||
|
struct Rsa2048DeviceCertificate {
|
||||||
|
u8 data[0x240];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Rsa2048DeviceCertificate) == 0x240);
|
||||||
|
static_assert(std::is_pod<Rsa2048DeviceCertificate>::value);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::settings::factory {
|
||||||
|
|
||||||
|
struct SerialNumber {
|
||||||
|
char str[0x18];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SerialNumber) == 0x18);
|
||||||
|
static_assert(std::is_pod<SerialNumber>::value);
|
||||||
|
|
||||||
|
}
|
|
@ -27,4 +27,7 @@ namespace ams::spl {
|
||||||
bool IsMariko();
|
bool IsMariko();
|
||||||
bool IsRecoveryBoot();
|
bool IsRecoveryBoot();
|
||||||
|
|
||||||
|
Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, u32 generation, u32 option);
|
||||||
|
Result GenerateAesKey(void *dst, size_t dst_size, const AccessKey &access_key, const void *key_source, size_t key_source_size);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,6 +183,36 @@ namespace ams::spl {
|
||||||
static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!");
|
static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!");
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
enum class ConfigItem : u32 {
|
||||||
|
/* Standard config items. */
|
||||||
|
DisableProgramVerification = 1,
|
||||||
|
DramId = 2,
|
||||||
|
SecurityEngineIrqNumber = 3,
|
||||||
|
Version = 4,
|
||||||
|
HardwareType = 5,
|
||||||
|
IsRetail = 6,
|
||||||
|
IsRecoveryBoot = 7,
|
||||||
|
DeviceId = 8,
|
||||||
|
BootReason = 9,
|
||||||
|
MemoryMode = 10,
|
||||||
|
IsDebugMode = 11,
|
||||||
|
KernelConfiguration = 12,
|
||||||
|
IsChargerHiZModeEnabled = 13,
|
||||||
|
IsQuest = 14,
|
||||||
|
RegulatorType = 15,
|
||||||
|
DeviceUniqueKeyGeneration = 16,
|
||||||
|
Package2Hash = 17,
|
||||||
|
|
||||||
|
/* Extension config items for exosphere. */
|
||||||
|
ExosphereApiVersion = 65000,
|
||||||
|
ExosphereNeedsReboot = 65001,
|
||||||
|
ExosphereNeedsShutdown = 65002,
|
||||||
|
ExosphereGitCommitHash = 65003,
|
||||||
|
ExosphereHasRcmBugPatch = 65004,
|
||||||
|
ExosphereBlankProdInfo = 65005,
|
||||||
|
ExosphereAllowCalWrites = 65006,
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extensions to libnx spl config item enum. */
|
/* Extensions to libnx spl config item enum. */
|
||||||
|
@ -191,3 +221,5 @@ constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_ca
|
||||||
constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast<SplConfigItem>(65002);
|
constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast<SplConfigItem>(65002);
|
||||||
constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast<SplConfigItem>(65003);
|
constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast<SplConfigItem>(65003);
|
||||||
constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast<SplConfigItem>(65004);
|
constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast<SplConfigItem>(65004);
|
||||||
|
constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast<SplConfigItem>(65005);
|
||||||
|
constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast<SplConfigItem>(65006);
|
||||||
|
|
|
@ -24,6 +24,11 @@ NX_GENERATE_SERVICE_GUARD(amsBpc);
|
||||||
Result _amsBpcInitialize(void) {
|
Result _amsBpcInitialize(void) {
|
||||||
Handle h;
|
Handle h;
|
||||||
Result rc = svcConnectToNamedPort(&h, "bpc:ams"); /* TODO: ams:bpc */
|
Result rc = svcConnectToNamedPort(&h, "bpc:ams"); /* TODO: ams:bpc */
|
||||||
|
while (R_VALUE(rc) == KERNELRESULT(NotFound)) {
|
||||||
|
svcSleepThread(50000000ul);
|
||||||
|
rc = svcConnectToNamedPort(&h, "bpc:ams");
|
||||||
|
}
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) serviceCreate(&g_amsBpcSrv, h);
|
if (R_SUCCEEDED(rc)) serviceCreate(&g_amsBpcSrv, h);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -44,3 +49,11 @@ Result amsBpcRebootToFatalError(void *ctx) {
|
||||||
.buffers = { { ctx, 0x450 } },
|
.buffers = { { ctx, 0x450 } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Result amsBpcSetInitialPayload(const void *src, size_t src_size) {
|
||||||
|
return serviceDispatch(&g_amsBpcSrv, 65001,
|
||||||
|
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
|
||||||
|
.buffers = { { src, src_size } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ void amsBpcExit(void);
|
||||||
Service *amsBpcGetServiceSession(void);
|
Service *amsBpcGetServiceSession(void);
|
||||||
|
|
||||||
Result amsBpcRebootToFatalError(void *ctx);
|
Result amsBpcRebootToFatalError(void *ctx);
|
||||||
|
Result amsBpcSetInitialPayload(const void *src, size_t src_size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,14 @@ namespace ams {
|
||||||
|
|
||||||
extern ncm::ProgramId CurrentProgramId;
|
extern ncm::ProgramId CurrentProgramId;
|
||||||
|
|
||||||
|
void InitializeForBoot() {
|
||||||
|
R_ABORT_UNLESS(amsBpcInitialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInitialRebootPayload(const void *src, size_t src_size) {
|
||||||
|
R_ABORT_UNLESS(amsBpcSetInitialPayload(src, src_size));
|
||||||
|
}
|
||||||
|
|
||||||
void WEAK_SYMBOL ExceptionHandler(FatalErrorContext *ctx) {
|
void WEAK_SYMBOL ExceptionHandler(FatalErrorContext *ctx) {
|
||||||
R_ABORT_UNLESS(amsBpcInitialize());
|
R_ABORT_UNLESS(amsBpcInitialize());
|
||||||
R_ABORT_UNLESS(amsBpcRebootToFatalError(ctx));
|
R_ABORT_UNLESS(amsBpcRebootToFatalError(ctx));
|
||||||
|
|
|
@ -51,19 +51,32 @@ namespace ams::exosphere {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
inline Result GetRcmBugPatched(bool *out) {
|
inline u64 GetU64ConfigItem(spl::ConfigItem cfg) {
|
||||||
u64 tmp = 0;
|
u64 tmp;
|
||||||
R_TRY(spl::smc::ConvertResult(spl::smc::GetConfig(&tmp, 1, SplConfigItem_ExosphereHasRcmBugPatch)));
|
R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::GetConfig(std::addressof(tmp), 1, static_cast<::SplConfigItem>(cfg))));
|
||||||
*out = (tmp != 0);
|
return tmp;
|
||||||
return ResultSuccess();
|
}
|
||||||
|
|
||||||
|
inline bool GetBooleanConfigItem(spl::ConfigItem cfg) {
|
||||||
|
return GetU64ConfigItem(cfg) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRcmBugPatched() {
|
bool IsRcmBugPatched() {
|
||||||
bool rcm_bug_patched;
|
return GetBooleanConfigItem(spl::ConfigItem::ExosphereHasRcmBugPatch);
|
||||||
R_ABORT_UNLESS(GetRcmBugPatched(&rcm_bug_patched));
|
}
|
||||||
return rcm_bug_patched;
|
|
||||||
|
bool ShouldBlankProdInfo() {
|
||||||
|
return GetBooleanConfigItem(spl::ConfigItem::ExosphereBlankProdInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldAllowWritesToProdInfo() {
|
||||||
|
return GetBooleanConfigItem(spl::ConfigItem::ExosphereAllowCalWrites);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetDeviceId() {
|
||||||
|
return GetU64ConfigItem(spl::ConfigItem::DeviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,4 +78,15 @@ namespace ams::spl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, u32 generation, u32 option) {
|
||||||
|
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||||
|
return splCryptoGenerateAesKek(key_source, generation, option, static_cast<void *>(access_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GenerateAesKey(void *dst, size_t dst_size, const AccessKey &access_key, const void *key_source, size_t key_source_size) {
|
||||||
|
AMS_ASSERT(dst_size == crypto::AesEncryptor128::KeySize);
|
||||||
|
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||||
|
return splCryptoGenerateAesKey(std::addressof(access_key), key_source, dst);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "amsmitm_initialization.hpp"
|
#include "amsmitm_initialization.hpp"
|
||||||
#include "amsmitm_fs_utils.hpp"
|
#include "amsmitm_fs_utils.hpp"
|
||||||
|
#include "amsmitm_prodinfo_utils.hpp"
|
||||||
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
||||||
#include "set_mitm/settings_sd_kvs.hpp"
|
#include "set_mitm/settings_sd_kvs.hpp"
|
||||||
|
|
||||||
|
@ -58,10 +59,6 @@ namespace ams::mitm {
|
||||||
alignas(os::ThreadStackAlignment) u8 g_initialize_thread_stack[InitializeThreadStackSize];
|
alignas(os::ThreadStackAlignment) u8 g_initialize_thread_stack[InitializeThreadStackSize];
|
||||||
|
|
||||||
/* Console-unique data backup and protection. */
|
/* Console-unique data backup and protection. */
|
||||||
constexpr size_t CalibrationBinarySize = 0x8000;
|
|
||||||
u8 g_calibration_binary_storage_backup[CalibrationBinarySize];
|
|
||||||
u8 g_calibration_binary_file_backup[CalibrationBinarySize];
|
|
||||||
FsFile g_calibration_binary_file;
|
|
||||||
FsFile g_bis_key_file;
|
FsFile g_bis_key_file;
|
||||||
|
|
||||||
/* Emummc file protection. */
|
/* Emummc file protection. */
|
||||||
|
@ -90,57 +87,10 @@ namespace ams::mitm {
|
||||||
/* Create a backup directory, if one doesn't exist. */
|
/* Create a backup directory, if one doesn't exist. */
|
||||||
mitm::fs::CreateAtmosphereSdDirectory("/automatic_backups");
|
mitm::fs::CreateAtmosphereSdDirectory("/automatic_backups");
|
||||||
|
|
||||||
/* Read the calibration binary. */
|
/* Initialize PRODINFO and get a reference for the device. */
|
||||||
{
|
char device_reference[0x40] = {};
|
||||||
FsStorage calibration_binary_storage;
|
ON_SCOPE_EXIT { std::memset(device_reference, 0, sizeof(device_reference)); };
|
||||||
R_ABORT_UNLESS(fsOpenBisStorage(&calibration_binary_storage, FsBisPartitionId_CalibrationBinary));
|
mitm::SaveProdInfoBackupsAndWipeMemory(device_reference, sizeof(device_reference));
|
||||||
ON_SCOPE_EXIT { fsStorageClose(&calibration_binary_storage); };
|
|
||||||
|
|
||||||
R_ABORT_UNLESS(fsStorageRead(&calibration_binary_storage, 0, g_calibration_binary_storage_backup, CalibrationBinarySize));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy serial number from partition. */
|
|
||||||
/* TODO: Define the magic numbers? Structs? */
|
|
||||||
char serial_number[0x40] = {};
|
|
||||||
std::memcpy(serial_number, g_calibration_binary_storage_backup + 0x250, 0x18);
|
|
||||||
|
|
||||||
/* Backup the calibration binary. */
|
|
||||||
{
|
|
||||||
char calibration_binary_backup_name[ams::fs::EntryNameLengthMax + 1];
|
|
||||||
GetBackupFileName(calibration_binary_backup_name, sizeof(calibration_binary_backup_name), serial_number, "PRODINFO.bin");
|
|
||||||
|
|
||||||
mitm::fs::CreateAtmosphereSdFile(calibration_binary_backup_name, CalibrationBinarySize, ams::fs::CreateOption_None);
|
|
||||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(&g_calibration_binary_file, calibration_binary_backup_name, ams::fs::OpenMode_ReadWrite));
|
|
||||||
|
|
||||||
s64 file_size = 0;
|
|
||||||
R_ABORT_UNLESS(fsFileGetSize(&g_calibration_binary_file, &file_size));
|
|
||||||
|
|
||||||
bool is_file_backup_valid = file_size == CalibrationBinarySize;
|
|
||||||
if (is_file_backup_valid) {
|
|
||||||
u64 read_size = 0;
|
|
||||||
R_ABORT_UNLESS(fsFileRead(&g_calibration_binary_file, 0, g_calibration_binary_file_backup, CalibrationBinarySize, FsReadOption_None, &read_size));
|
|
||||||
AMS_ABORT_UNLESS(read_size == CalibrationBinarySize);
|
|
||||||
is_file_backup_valid &= std::memcmp(g_calibration_binary_file_backup, "CAL0", 4) == 0;
|
|
||||||
is_file_backup_valid &= std::memcmp(g_calibration_binary_file_backup + 0x250, serial_number, 0x18) == 0;
|
|
||||||
const u32 cal_bin_size = *reinterpret_cast<const u32 *>(g_calibration_binary_file_backup + 0x8);
|
|
||||||
is_file_backup_valid &= cal_bin_size + 0x40 <= CalibrationBinarySize;
|
|
||||||
if (is_file_backup_valid) {
|
|
||||||
u8 calc_hash[SHA256_HASH_SIZE];
|
|
||||||
/* TODO: ams::crypto? */
|
|
||||||
sha256CalculateHash(calc_hash, g_calibration_binary_file_backup + 0x40, cal_bin_size);
|
|
||||||
is_file_backup_valid &= std::memcmp(calc_hash, g_calibration_binary_file_backup + 0x20, sizeof(calc_hash)) == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_file_backup_valid) {
|
|
||||||
R_ABORT_UNLESS(fsFileSetSize(&g_calibration_binary_file, CalibrationBinarySize));
|
|
||||||
R_ABORT_UNLESS(fsFileWrite(&g_calibration_binary_file, 0, g_calibration_binary_storage_backup, CalibrationBinarySize, FsWriteOption_Flush));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note: g_calibration_binary_file is intentionally not closed here. This prevents any other process from opening it. */
|
|
||||||
std::memset(g_calibration_binary_file_backup, 0, CalibrationBinarySize);
|
|
||||||
std::memset(g_calibration_binary_storage_backup, 0, CalibrationBinarySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Backup BIS keys. */
|
/* Backup BIS keys. */
|
||||||
{
|
{
|
||||||
|
@ -151,6 +101,7 @@ namespace ams::mitm {
|
||||||
|
|
||||||
u8 bis_keys[4][2][0x10];
|
u8 bis_keys[4][2][0x10];
|
||||||
std::memset(bis_keys, 0xCC, sizeof(bis_keys));
|
std::memset(bis_keys, 0xCC, sizeof(bis_keys));
|
||||||
|
ON_SCOPE_EXIT { std::memset(bis_keys, 0xCC, sizeof(bis_keys)); };
|
||||||
|
|
||||||
/* TODO: Clean this up. */
|
/* TODO: Clean this up. */
|
||||||
for (size_t partition = 0; partition < 4; partition++) {
|
for (size_t partition = 0; partition < 4; partition++) {
|
||||||
|
@ -170,7 +121,7 @@ namespace ams::mitm {
|
||||||
}
|
}
|
||||||
|
|
||||||
char bis_keys_backup_name[ams::fs::EntryNameLengthMax + 1];
|
char bis_keys_backup_name[ams::fs::EntryNameLengthMax + 1];
|
||||||
GetBackupFileName(bis_keys_backup_name, sizeof(bis_keys_backup_name), serial_number, "BISKEYS.bin");
|
GetBackupFileName(bis_keys_backup_name, sizeof(bis_keys_backup_name), device_reference, "BISKEYS.bin");
|
||||||
|
|
||||||
mitm::fs::CreateAtmosphereSdFile(bis_keys_backup_name, sizeof(bis_keys), ams::fs::CreateOption_None);
|
mitm::fs::CreateAtmosphereSdFile(bis_keys_backup_name, sizeof(bis_keys), ams::fs::CreateOption_None);
|
||||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(&g_bis_key_file, bis_keys_backup_name, ams::fs::OpenMode_ReadWrite));
|
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(&g_bis_key_file, bis_keys_backup_name, ams::fs::OpenMode_ReadWrite));
|
||||||
|
@ -222,6 +173,10 @@ namespace ams::mitm {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartInitialize() {
|
void StartInitialize() {
|
||||||
|
/* Initialize prodinfo. */
|
||||||
|
mitm::InitializeProdInfoManagement();
|
||||||
|
|
||||||
|
/* Launch initialize thread. */
|
||||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm, InitializeThread)));
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm, InitializeThread)));
|
||||||
os::SetThreadNamePointer(std::addressof(g_initialize_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm, InitializeThread));
|
os::SetThreadNamePointer(std::addressof(g_initialize_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm, InitializeThread));
|
||||||
os::StartThread(std::addressof(g_initialize_thread));
|
os::StartThread(std::addressof(g_initialize_thread));
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
namespace ams::mitm {
|
namespace ams::mitm {
|
||||||
|
|
||||||
void StartInitialize();
|
void StartInitialize();
|
||||||
|
|
||||||
bool IsInitialized();
|
bool IsInitialized();
|
||||||
void WaitInitialized();
|
void WaitInitialized();
|
||||||
|
|
||||||
|
|
|
@ -95,9 +95,6 @@ void __appExit(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
/* Start initialization (sd card init, automatic backups, etc) */
|
|
||||||
mitm::StartInitialize();
|
|
||||||
|
|
||||||
/* Launch all mitm modules in sequence. */
|
/* Launch all mitm modules in sequence. */
|
||||||
mitm::LaunchAllModules();
|
mitm::LaunchAllModules();
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "fs_mitm/fsmitm_module.hpp"
|
#include "fs_mitm/fsmitm_module.hpp"
|
||||||
#include "set_mitm/setmitm_module.hpp"
|
#include "set_mitm/setmitm_module.hpp"
|
||||||
#include "bpc_mitm/bpcmitm_module.hpp"
|
#include "bpc_mitm/bpcmitm_module.hpp"
|
||||||
|
#include "bpc_mitm/bpc_ams_module.hpp"
|
||||||
#include "ns_mitm/nsmitm_module.hpp"
|
#include "ns_mitm/nsmitm_module.hpp"
|
||||||
#include "hid_mitm/hidmitm_module.hpp"
|
#include "hid_mitm/hidmitm_module.hpp"
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ namespace ams::mitm {
|
||||||
ModuleId_FsMitm,
|
ModuleId_FsMitm,
|
||||||
ModuleId_SetMitm,
|
ModuleId_SetMitm,
|
||||||
ModuleId_BpcMitm,
|
ModuleId_BpcMitm,
|
||||||
|
ModuleId_BpcAms,
|
||||||
ModuleId_NsMitm,
|
ModuleId_NsMitm,
|
||||||
ModuleId_HidMitm,
|
ModuleId_HidMitm,
|
||||||
|
|
||||||
|
@ -62,6 +64,7 @@ namespace ams::mitm {
|
||||||
GetModuleDefinition<fs::MitmModule>(),
|
GetModuleDefinition<fs::MitmModule>(),
|
||||||
GetModuleDefinition<settings::MitmModule>(),
|
GetModuleDefinition<settings::MitmModule>(),
|
||||||
GetModuleDefinition<bpc::MitmModule>(),
|
GetModuleDefinition<bpc::MitmModule>(),
|
||||||
|
GetModuleDefinition<bpc_ams::MitmModule>(),
|
||||||
GetModuleDefinition<ns::MitmModule>(),
|
GetModuleDefinition<ns::MitmModule>(),
|
||||||
GetModuleDefinition<hid::MitmModule>(),
|
GetModuleDefinition<hid::MitmModule>(),
|
||||||
};
|
};
|
||||||
|
|
639
stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.cpp
Normal file
639
stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.cpp
Normal file
|
@ -0,0 +1,639 @@
|
||||||
|
/*
|
||||||
|
* 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 <stratosphere.hpp>
|
||||||
|
#include "amsmitm_fs_utils.hpp"
|
||||||
|
#include "amsmitm_prodinfo_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline u16 Crc16InitialValue = 0x55AA;
|
||||||
|
|
||||||
|
constexpr inline u16 Crc16Table[] = {
|
||||||
|
0x0000, 0xCC01, 0xD801, 0x1400,
|
||||||
|
0xF001, 0x3C00, 0x2800, 0xE401,
|
||||||
|
0xA001, 0x6C00, 0x7800, 0xB401,
|
||||||
|
0x5000, 0x9C01, 0x8801, 0x4400,
|
||||||
|
};
|
||||||
|
|
||||||
|
u16 GetCrc16(const void *data, size_t size) {
|
||||||
|
AMS_ASSERT(data != nullptr);
|
||||||
|
AMS_ASSERT(size > 0);
|
||||||
|
|
||||||
|
const u8 *src = static_cast<const u8 *>(data);
|
||||||
|
|
||||||
|
u16 crc = Crc16InitialValue;
|
||||||
|
|
||||||
|
u16 tmp = 0;
|
||||||
|
while ((size--) > 0) {
|
||||||
|
tmp = Crc16Table[crc & 0xF];
|
||||||
|
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[*src & 0xF];
|
||||||
|
tmp = Crc16Table[crc & 0xF];
|
||||||
|
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[(*(src++) >> 4) & 0xF];
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBlank(const void *data, size_t size) {
|
||||||
|
AMS_ASSERT(data != nullptr);
|
||||||
|
AMS_ASSERT(size > 0);
|
||||||
|
|
||||||
|
const u8 *src = static_cast<const u8 *>(data);
|
||||||
|
while ((size--) > 0) {
|
||||||
|
if (*(src++) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline u32 CalibrationMagic = util::FourCC<'C','A','L','0'>::Code;
|
||||||
|
|
||||||
|
struct Sha256Hash {
|
||||||
|
u8 data[crypto::Sha256Generator::HashSize];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CalibrationInfoHeader {
|
||||||
|
u32 magic;
|
||||||
|
u32 version;
|
||||||
|
u32 body_size;
|
||||||
|
u16 model;
|
||||||
|
u16 update_count;
|
||||||
|
u8 pad[0xE];
|
||||||
|
u16 crc;
|
||||||
|
Sha256Hash body_hash;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(CalibrationInfoHeader) == 0x40);
|
||||||
|
|
||||||
|
constexpr inline size_t CalibrationInfoBodySizeMax = CalibrationBinarySize - sizeof(CalibrationInfoHeader);
|
||||||
|
|
||||||
|
struct CalibrationInfo {
|
||||||
|
CalibrationInfoHeader header;
|
||||||
|
u8 body[CalibrationInfoBodySizeMax]; /* TODO: CalibrationInfoBody body; */
|
||||||
|
|
||||||
|
template<typename Block>
|
||||||
|
Block &GetBlock() {
|
||||||
|
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
|
||||||
|
static_assert(Block::Offset < sizeof(CalibrationInfo));
|
||||||
|
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
|
||||||
|
return *static_cast<Block *>(static_cast<void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Block>
|
||||||
|
const Block &GetBlock() const {
|
||||||
|
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
|
||||||
|
static_assert(Block::Offset < sizeof(CalibrationInfo));
|
||||||
|
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
|
||||||
|
return *static_cast<const Block *>(static_cast<const void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(CalibrationInfo) == CalibrationBinarySize);
|
||||||
|
|
||||||
|
struct SecureCalibrationInfoBackup {
|
||||||
|
CalibrationInfo info;
|
||||||
|
Sha256Hash hash;
|
||||||
|
u8 pad[SecureCalibrationBinaryBackupSize - sizeof(info) - sizeof(hash)];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SecureCalibrationInfoBackup) == SecureCalibrationBinaryBackupSize);
|
||||||
|
|
||||||
|
bool IsValidSha256Hash(const Sha256Hash &hash, const void *data, size_t data_size) {
|
||||||
|
Sha256Hash calc_hash;
|
||||||
|
ON_SCOPE_EXIT { ::ams::crypto::ClearMemory(std::addressof(calc_hash), sizeof(calc_hash)); };
|
||||||
|
|
||||||
|
::ams::crypto::GenerateSha256Hash(std::addressof(calc_hash), sizeof(calc_hash), data, data_size);
|
||||||
|
return ::ams::crypto::IsSameBytes(std::addressof(calc_hash), std::addressof(hash), sizeof(Sha256Hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid(const CalibrationInfoHeader &header) {
|
||||||
|
return header.magic == CalibrationMagic && GetCrc16(std::addressof(header), OFFSETOF(CalibrationInfoHeader, crc)) == header.crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid(const CalibrationInfoHeader &header, const void *body) {
|
||||||
|
return IsValid(header) && IsValidSha256Hash(header.body_hash, body, header.body_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_CALIBRATION_CRC_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
|
||||||
|
struct _TypeName { \
|
||||||
|
static constexpr size_t Offset = _Offset; \
|
||||||
|
static constexpr size_t Size = _Size; \
|
||||||
|
static constexpr bool IsCrcBlock = true; \
|
||||||
|
static constexpr bool IsShaBlock = false; \
|
||||||
|
_Decl; \
|
||||||
|
static_assert(Size >= sizeof(_MemberName) + sizeof(u16)); \
|
||||||
|
u8 pad[Size - sizeof(_MemberName) - sizeof(u16)]; \
|
||||||
|
u16 crc; \
|
||||||
|
}; \
|
||||||
|
static_assert(sizeof(_TypeName) == _TypeName::Size)
|
||||||
|
|
||||||
|
#define DEFINE_CALIBRATION_SHA_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
|
||||||
|
struct _TypeName { \
|
||||||
|
static constexpr size_t Offset = _Offset; \
|
||||||
|
static constexpr size_t Size = _Size; \
|
||||||
|
static constexpr bool IsCrcBlock = false; \
|
||||||
|
static constexpr bool IsShaBlock = true; \
|
||||||
|
_Decl; \
|
||||||
|
static_assert(Size == sizeof(_MemberName) + sizeof(Sha256Hash)); \
|
||||||
|
Sha256Hash sha256_hash; \
|
||||||
|
}; \
|
||||||
|
static_assert(sizeof(_TypeName) == _TypeName::Size)
|
||||||
|
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(SerialNumberBlock, 0x0250, 0x020, ::ams::settings::factory::SerialNumber serial_number, serial_number);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(EccB233DeviceCertificateBlock, 0x0480, 0x190, ::ams::settings::factory::EccB233DeviceCertificate device_certificate, device_certificate);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(SslKeyBlock, 0x09B0, 0x120, u8 ssl_key[0x110], ssl_key);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(SslCertificateSizeBlock, 0x0AD0, 0x010, u64 ssl_certificate_size, ssl_certificate_size);
|
||||||
|
DEFINE_CALIBRATION_SHA_BLOCK(SslCertificateBlock, 0x0AE0, 0x820, u8 ssl_certificate[0x800], ssl_certificate);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(EcqvEcdsaAmiiboRootCertificateBlock, 0x35A0, 0x080, u8 data[0x70], data);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(EcqvBlsAmiiboRootCertificateBlock, 0x36A0, 0x0A0, u8 data[0x90], data);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(ExtendedSslKeyBlock, 0x3AE0, 0x140, u8 ssl_key[0x134], ssl_key);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceKeyBlock, 0x3D70, 0x250, u8 device_key[0x240], device_key);
|
||||||
|
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceCertificateBlock, 0x3FC0, 0x250, ::ams::settings::factory::Rsa2048DeviceCertificate device_certificate, device_certificate);
|
||||||
|
|
||||||
|
#undef DEFINE_CALIBRATION_CRC_BLOCK
|
||||||
|
#undef DEFINE_CALIBRATION_SHA_BLOCK
|
||||||
|
|
||||||
|
constexpr inline const char BlankSerialNumberString[] = "XAW00000000000";
|
||||||
|
|
||||||
|
template<typename Block>
|
||||||
|
void Blank(Block &block) {
|
||||||
|
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
|
||||||
|
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
|
||||||
|
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
|
||||||
|
std::memcpy(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString));
|
||||||
|
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||||
|
} else if constexpr (std::is_same<Block, SslCertificateBlock>::value) {
|
||||||
|
std::memset(std::addressof(block), 0, sizeof(block.ssl_certificate));
|
||||||
|
} else if constexpr (Block::IsCrcBlock) {
|
||||||
|
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
|
||||||
|
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||||
|
} else {
|
||||||
|
static_assert(Block::IsShaBlock);
|
||||||
|
std::memset(std::addressof(block), 0, Block::Size);
|
||||||
|
::ams::crypto::GenerateSha256Hash(std::addressof(block.sha256_hash), sizeof(block.sha256_hash), std::addressof(block), Block::Size - sizeof(block.sha256_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Block>
|
||||||
|
bool IsBlank(const Block &block) {
|
||||||
|
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
|
||||||
|
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
|
||||||
|
return std::memcmp(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString) - 1) == 0 || IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||||
|
} else if constexpr (Block::IsCrcBlock) {
|
||||||
|
return IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||||
|
} else {
|
||||||
|
return IsBlank(std::addressof(block), Block::Size - sizeof(block.sha256_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Block>
|
||||||
|
bool IsValid(const Block &block, size_t size = 0) {
|
||||||
|
if constexpr (Block::IsCrcBlock) {
|
||||||
|
return GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc)) == block.crc;
|
||||||
|
} else {
|
||||||
|
static_assert(Block::IsShaBlock);
|
||||||
|
return IsValidSha256Hash(block.sha256_hash, std::addressof(block), size != 0 ? size : Block::Size - sizeof(block.sha256_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Blank(CalibrationInfo &info) {
|
||||||
|
/* Set header. */
|
||||||
|
info.header.magic = CalibrationMagic;
|
||||||
|
info.header.body_size = sizeof(info.body);
|
||||||
|
info.header.crc = GetCrc16(std::addressof(info.header), OFFSETOF(CalibrationInfoHeader, crc));
|
||||||
|
|
||||||
|
/* Set blocks. */
|
||||||
|
Blank(info.GetBlock<SerialNumberBlock>());
|
||||||
|
Blank(info.GetBlock<SslCertificateSizeBlock>());
|
||||||
|
Blank(info.GetBlock<SslCertificateBlock>());
|
||||||
|
Blank(info.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>());
|
||||||
|
Blank(info.GetBlock<EcqvBlsAmiiboRootCertificateBlock>());
|
||||||
|
Blank(info.GetBlock<ExtendedSslKeyBlock>());
|
||||||
|
if (IsValid(info.GetBlock<Rsa2048DeviceKeyBlock>()) && !IsBlank(info.GetBlock<Rsa2048DeviceKeyBlock>())) Blank(info.GetBlock<Rsa2048DeviceKeyBlock>());
|
||||||
|
if (IsValid(info.GetBlock<Rsa2048DeviceCertificateBlock>()) && !IsBlank(info.GetBlock<Rsa2048DeviceCertificateBlock>())) Blank(info.GetBlock<Rsa2048DeviceCertificateBlock>());
|
||||||
|
|
||||||
|
/* Set header hash. */
|
||||||
|
crypto::GenerateSha256Hash(std::addressof(info.header.body_hash), sizeof(info.header.body_hash), std::addressof(info.body), sizeof(info.body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidHeader(const CalibrationInfo &cal) {
|
||||||
|
return IsValid(cal.header) && cal.header.body_size <= CalibrationInfoBodySizeMax && IsValid(cal.header, cal.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidSerialNumber(const char *sn) {
|
||||||
|
for (size_t i = 0; i < std::strlen(sn); i++) {
|
||||||
|
if (!std::isalnum(static_cast<unsigned char>(sn[i]))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetSerialNumber(char *dst, const CalibrationInfo &info) {
|
||||||
|
std::memcpy(dst, std::addressof(info.GetBlock<SerialNumberBlock>()), sizeof(info.GetBlock<SerialNumberBlock>().serial_number));
|
||||||
|
dst[sizeof(info.GetBlock<SerialNumberBlock>().serial_number) + 1] = '\x00';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidSerialNumber(const CalibrationInfo &cal) {
|
||||||
|
char sn[0x20] = {};
|
||||||
|
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
|
||||||
|
|
||||||
|
GetSerialNumber(sn, cal);
|
||||||
|
return IsValidSerialNumber(sn);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid(const CalibrationInfo &cal) {
|
||||||
|
return IsValidHeader(cal) &&
|
||||||
|
IsValid(cal.GetBlock<SerialNumberBlock>()) &&
|
||||||
|
IsValid(cal.GetBlock<EccB233DeviceCertificateBlock>()) &&
|
||||||
|
IsValid(cal.GetBlock<SslKeyBlock>()) &&
|
||||||
|
IsValid(cal.GetBlock<SslCertificateSizeBlock>()) &&
|
||||||
|
cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size <= sizeof(cal.GetBlock<SslCertificateBlock>().ssl_certificate) &&
|
||||||
|
IsValid(cal.GetBlock<SslCertificateBlock>(), cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size) &&
|
||||||
|
IsValid(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) &&
|
||||||
|
IsValid(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) &&
|
||||||
|
IsValid(cal.GetBlock<ExtendedSslKeyBlock>()) &&
|
||||||
|
IsValidSerialNumber(cal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContainsCorrectDeviceId(const EccB233DeviceCertificateBlock &block, u64 device_id) {
|
||||||
|
static constexpr size_t DeviceIdOffset = 0xC6;
|
||||||
|
char found_device_id_str[sizeof("0011223344556677")] = {};
|
||||||
|
ON_SCOPE_EXIT { std::memset(found_device_id_str, 0, sizeof(found_device_id_str)); };
|
||||||
|
std::memcpy(found_device_id_str, std::addressof(block.device_certificate.data[DeviceIdOffset]), sizeof(found_device_id_str) - 1);
|
||||||
|
|
||||||
|
static constexpr u64 DeviceIdLowMask = 0x00FFFFFFFFFFFFFFul;
|
||||||
|
|
||||||
|
return (std::strtoul(found_device_id_str, nullptr, 16) & DeviceIdLowMask) == (device_id & DeviceIdLowMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContainsCorrectDeviceId(const CalibrationInfo &cal) {
|
||||||
|
return ContainsCorrectDeviceId(cal.GetBlock<EccB233DeviceCertificateBlock>(), exosphere::GetDeviceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidForSecureBackup(const CalibrationInfo &cal) {
|
||||||
|
return IsValid(cal) && ContainsCorrectDeviceId(cal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBlank(const CalibrationInfo &cal) {
|
||||||
|
return IsBlank(cal.GetBlock<SerialNumberBlock>()) ||
|
||||||
|
IsBlank(cal.GetBlock<SslCertificateSizeBlock>()) ||
|
||||||
|
IsBlank(cal.GetBlock<SslCertificateBlock>()) ||
|
||||||
|
IsBlank(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) ||
|
||||||
|
IsBlank(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) ||
|
||||||
|
IsBlank(cal.GetBlock<ExtendedSslKeyBlock>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadStorageCalibrationBinary(CalibrationInfo *out) {
|
||||||
|
FsStorage calibration_binary_storage;
|
||||||
|
R_ABORT_UNLESS(fsOpenBisStorage(&calibration_binary_storage, FsBisPartitionId_CalibrationBinary));
|
||||||
|
ON_SCOPE_EXIT { fsStorageClose(&calibration_binary_storage); };
|
||||||
|
|
||||||
|
R_ABORT_UNLESS(fsStorageRead(&calibration_binary_storage, 0, out, sizeof(*out)));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline const u8 SecureCalibrationBinaryBackupIv[crypto::Aes128CtrDecryptor::IvSize] = {};
|
||||||
|
|
||||||
|
void ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(SecureCalibrationInfoBackup *out) {
|
||||||
|
FsStorage calibration_binary_storage;
|
||||||
|
R_ABORT_UNLESS(fsOpenBisStorage(&calibration_binary_storage, FsBisPartitionId_CalibrationBinary));
|
||||||
|
ON_SCOPE_EXIT { fsStorageClose(&calibration_binary_storage); };
|
||||||
|
|
||||||
|
R_ABORT_UNLESS(fsStorageRead(&calibration_binary_storage, SecureCalibrationInfoBackupOffset, out, sizeof(*out)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(const SecureCalibrationInfoBackup *src) {
|
||||||
|
FsStorage calibration_binary_storage;
|
||||||
|
R_ABORT_UNLESS(fsOpenBisStorage(&calibration_binary_storage, FsBisPartitionId_CalibrationBinary));
|
||||||
|
ON_SCOPE_EXIT { fsStorageClose(&calibration_binary_storage); };
|
||||||
|
|
||||||
|
R_ABORT_UNLESS(fsStorageWrite(&calibration_binary_storage, SecureCalibrationInfoBackupOffset, src, sizeof(*src)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateSecureCalibrationBinaryBackupKey(void *dst, size_t dst_size) {
|
||||||
|
static constexpr const u8 SecureCalibrationBinaryBackupKeySource[crypto::Aes128CtrDecryptor::KeySize] = { '|', '-', 'A', 'M', 'S', '-', 'C', 'A', 'L', '0', '-', 'K', 'E', 'Y', '-', '|' };
|
||||||
|
spl::AccessKey access_key;
|
||||||
|
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(access_key), sizeof(access_key)); };
|
||||||
|
|
||||||
|
/* Generate a personalized kek. */
|
||||||
|
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource), 0, 1));
|
||||||
|
|
||||||
|
/* Generate a personalized key. */
|
||||||
|
R_ABORT_UNLESS(spl::GenerateAesKey(dst, dst_size, access_key, SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *out) {
|
||||||
|
/* Read the data. */
|
||||||
|
ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(out);
|
||||||
|
|
||||||
|
/* Don't leak any data unless we validate. */
|
||||||
|
auto clear_guard = SCOPE_GUARD { std::memset(out, 0, sizeof(*out)); };
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Create a buffer to hold our key. */
|
||||||
|
u8 key[crypto::Aes128CtrDecryptor::KeySize];
|
||||||
|
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
|
||||||
|
|
||||||
|
/* Generate the key. */
|
||||||
|
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
|
||||||
|
|
||||||
|
/* Decrypt the data in place. */
|
||||||
|
crypto::DecryptAes128Ctr(out, sizeof(*out), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), out, sizeof(*out));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate a hash for the data. */
|
||||||
|
if (!IsValidSha256Hash(out->hash, std::addressof(out->info), sizeof(out->info))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate the backup. */
|
||||||
|
if (!IsValidForSecureBackup(out->info)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Our backup is valid. */
|
||||||
|
clear_guard.Cancel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *src) {
|
||||||
|
/* Clear the input once we've written it. */
|
||||||
|
ON_SCOPE_EXIT { std::memset(src, 0, sizeof(*src)); };
|
||||||
|
|
||||||
|
/* Ensure that the input is valid. */
|
||||||
|
AMS_ABORT_UNLESS(IsValidForSecureBackup(src->info));
|
||||||
|
|
||||||
|
/* Set the Sha256 hash. */
|
||||||
|
crypto::GenerateSha256Hash(std::addressof(src->hash), sizeof(src->hash), std::addressof(src->info), sizeof(src->info));
|
||||||
|
|
||||||
|
/* Validate the hash. */
|
||||||
|
AMS_ABORT_UNLESS(IsValidSha256Hash(src->hash, std::addressof(src->info), sizeof(src->info)));
|
||||||
|
|
||||||
|
/* Encrypt the data. */
|
||||||
|
{
|
||||||
|
/* Create a buffer to hold our key. */
|
||||||
|
u8 key[crypto::Aes128CtrDecryptor::KeySize];
|
||||||
|
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
|
||||||
|
|
||||||
|
/* Generate the key. */
|
||||||
|
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
|
||||||
|
|
||||||
|
/* Encrypt the data in place. */
|
||||||
|
crypto::EncryptAes128Ctr(src, sizeof(*src), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), src, sizeof(*src));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the encrypted data. */
|
||||||
|
WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetBackupFileName(char *dst, size_t dst_size, const CalibrationInfo &info) {
|
||||||
|
char sn[0x20] = {};
|
||||||
|
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
|
||||||
|
|
||||||
|
|
||||||
|
if (IsValidForSecureBackup(info)) {
|
||||||
|
GetSerialNumber(sn, info);
|
||||||
|
std::snprintf(dst, dst_size, "automatic_backups/%s_PRODINFO.bin", sn);
|
||||||
|
} else {
|
||||||
|
Sha256Hash hash;
|
||||||
|
crypto::GenerateSha256Hash(std::addressof(hash), sizeof(hash), std::addressof(info), sizeof(info));
|
||||||
|
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
|
||||||
|
|
||||||
|
if (IsValid(info)) {
|
||||||
|
if (IsBlank(info)) {
|
||||||
|
std::snprintf(dst, dst_size, "automatic_backups/BLANK_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||||
|
} else {
|
||||||
|
GetSerialNumber(sn, info);
|
||||||
|
std::snprintf(dst, dst_size, "automatic_backups/%s_PRODINFO_%02X%02X%02X%02X.bin", sn, hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::snprintf(dst, dst_size, "automatic_backups/INVALID_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeRead(ams::fs::fsa::IFile *file, s64 offset, void *dst, size_t size) {
|
||||||
|
size_t read_size = 0;
|
||||||
|
R_ABORT_UNLESS(file->Read(std::addressof(read_size), offset, dst, size));
|
||||||
|
AMS_ABORT_UNLESS(read_size == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
alignas(os::MemoryPageSize) CalibrationInfo g_temp_calibration_info = {};
|
||||||
|
|
||||||
|
void SaveProdInfoBackup(std::optional<ams::fs::FileStorage> *dst, const CalibrationInfo &info) {
|
||||||
|
char backup_fn[0x100];
|
||||||
|
GetBackupFileName(backup_fn, sizeof(backup_fn), info);
|
||||||
|
|
||||||
|
/* Create the file, in case it does not exist. */
|
||||||
|
mitm::fs::CreateAtmosphereSdFile(backup_fn, sizeof(CalibrationInfo), ams::fs::CreateOption_None);
|
||||||
|
|
||||||
|
/* Open the file. */
|
||||||
|
FsFile libnx_file;
|
||||||
|
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(libnx_file), backup_fn, ams::fs::OpenMode_ReadWrite));
|
||||||
|
|
||||||
|
/* Create our accessor. */
|
||||||
|
std::unique_ptr<ams::fs::fsa::IFile> file = std::make_unique<ams::fs::RemoteFile>(libnx_file);
|
||||||
|
AMS_ABORT_UNLESS(file != nullptr);
|
||||||
|
|
||||||
|
/* Check if we're valid already. */
|
||||||
|
bool valid = false;
|
||||||
|
s64 size;
|
||||||
|
R_ABORT_UNLESS(file->GetSize(std::addressof(size)));
|
||||||
|
if (size == sizeof(CalibrationInfo)) {
|
||||||
|
SafeRead(file.get(), 0, std::addressof(g_temp_calibration_info), sizeof(g_temp_calibration_info));
|
||||||
|
ON_SCOPE_EXIT { std::memset(std::addressof(g_temp_calibration_info), 0, sizeof(g_temp_calibration_info)); };
|
||||||
|
|
||||||
|
if (std::memcmp(std::addressof(info), std::addressof(g_temp_calibration_info), sizeof(CalibrationInfo)) == 0) {
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're not valid, we need to save. */
|
||||||
|
if (!valid) {
|
||||||
|
R_ABORT_UNLESS(file->Write(0, std::addressof(info), sizeof(info), ams::fs::WriteOption::Flush));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save our storage to output. */
|
||||||
|
if (dst != nullptr) {
|
||||||
|
dst->emplace(std::move(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetRandomEntropy(Sha256Hash *dst) {
|
||||||
|
AMS_ASSERT(dst != nullptr);
|
||||||
|
|
||||||
|
u64 data_buffer[3] = {};
|
||||||
|
ON_SCOPE_EXIT { crypto::ClearMemory(data_buffer, sizeof(data_buffer)); };
|
||||||
|
|
||||||
|
data_buffer[0] = os::GetSystemTick().GetInt64Value();
|
||||||
|
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 1, svc::InfoType_AliasRegionAddress, svc::PseudoHandle::CurrentProcess, 0));
|
||||||
|
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||||
|
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 2, svc::InfoType_RandomEntropy, svc::InvalidHandle, (data_buffer[0] ^ (data_buffer[1] >> 24)) & 3));
|
||||||
|
} else {
|
||||||
|
data_buffer[2] = os::GetSystemTick().GetInt64Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto::GenerateSha256Hash(dst, sizeof(*dst), data_buffer, sizeof(data_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillWithGarbage(void *dst, size_t dst_size) {
|
||||||
|
/* Get random entropy. */
|
||||||
|
Sha256Hash entropy;
|
||||||
|
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(entropy), sizeof(entropy)); };
|
||||||
|
GetRandomEntropy(std::addressof(entropy));
|
||||||
|
|
||||||
|
/* Clear dst. */
|
||||||
|
std::memset(dst, 0xCC, dst_size);
|
||||||
|
|
||||||
|
/* Encrypt dst. */
|
||||||
|
static_assert(sizeof(entropy) == crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize);
|
||||||
|
crypto::EncryptAes128Ctr(dst, dst_size, entropy.data, crypto::Aes128CtrEncryptor::KeySize, entropy.data + crypto::Aes128CtrEncryptor::KeySize, crypto::Aes128CtrEncryptor::IvSize, dst, dst_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
alignas(os::MemoryPageSize) CalibrationInfo g_calibration_info = {};
|
||||||
|
alignas(os::MemoryPageSize) CalibrationInfo g_blank_calibration_info = {};
|
||||||
|
alignas(os::MemoryPageSize) SecureCalibrationInfoBackup g_secure_calibration_info_backup = {};
|
||||||
|
|
||||||
|
std::optional<ams::fs::FileStorage> g_prodinfo_backup_file;
|
||||||
|
std::optional<ams::fs::MemoryStorage> g_blank_prodinfo_storage;
|
||||||
|
std::optional<ams::fs::MemoryStorage> g_fake_secure_backup_storage;
|
||||||
|
|
||||||
|
bool g_allow_writes = false;
|
||||||
|
bool g_has_secure_backup = false;
|
||||||
|
|
||||||
|
os::Mutex g_prodinfo_management_lock(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeProdInfoManagement() {
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
|
||||||
|
/* First, get our options. */
|
||||||
|
const bool should_blank = exosphere::ShouldBlankProdInfo();
|
||||||
|
bool allow_writes = exosphere::ShouldAllowWritesToProdInfo();
|
||||||
|
|
||||||
|
/* Next, read our prodinfo. */
|
||||||
|
ReadStorageCalibrationBinary(std::addressof(g_calibration_info));
|
||||||
|
|
||||||
|
/* Next, check if we have a secure backup. */
|
||||||
|
bool has_secure_backup = ReadStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
|
||||||
|
|
||||||
|
/* Only allow writes if we have a secure backup. */
|
||||||
|
if (allow_writes && !has_secure_backup) {
|
||||||
|
/* If we can make a secure backup, great. */
|
||||||
|
if (IsValidForSecureBackup(g_calibration_info)) {
|
||||||
|
g_secure_calibration_info_backup.info = g_calibration_info;
|
||||||
|
WriteStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
|
||||||
|
g_secure_calibration_info_backup.info = g_calibration_info;
|
||||||
|
has_secure_backup = true;
|
||||||
|
} else {
|
||||||
|
/* Don't allow writes if we can't make a secure backup. */
|
||||||
|
allow_writes = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure our preconditions are met. */
|
||||||
|
AMS_ABORT_UNLESS(!allow_writes || has_secure_backup);
|
||||||
|
|
||||||
|
/* Set globals. */
|
||||||
|
g_allow_writes = allow_writes;
|
||||||
|
g_has_secure_backup = has_secure_backup;
|
||||||
|
|
||||||
|
/* If we should blank, do so. */
|
||||||
|
if (should_blank) {
|
||||||
|
g_blank_calibration_info = g_calibration_info;
|
||||||
|
Blank(g_blank_calibration_info);
|
||||||
|
g_blank_prodinfo_storage.emplace(std::addressof(g_blank_calibration_info), sizeof(g_blank_calibration_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure that we have a blank file only if we need one. */
|
||||||
|
AMS_ABORT_UNLESS(should_blank == static_cast<bool>(g_blank_prodinfo_storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size) {
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
|
||||||
|
ON_SCOPE_EXIT {
|
||||||
|
FillWithGarbage(std::addressof(g_calibration_info), sizeof(g_calibration_info));
|
||||||
|
FillWithGarbage(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Save our backup. We always prefer to save a secure copy of data over a non-secure one. */
|
||||||
|
if (g_has_secure_backup) {
|
||||||
|
GetSerialNumber(out_name, g_secure_calibration_info_backup.info);
|
||||||
|
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_secure_calibration_info_backup.info);
|
||||||
|
} else {
|
||||||
|
if (IsValid(g_calibration_info) && !IsBlank(g_calibration_info)) {
|
||||||
|
GetSerialNumber(out_name, g_calibration_info);
|
||||||
|
} else {
|
||||||
|
Sha256Hash hash;
|
||||||
|
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
|
||||||
|
crypto::GenerateSha256Hash(std::addressof(hash), sizeof(hash), std::addressof(g_calibration_info), sizeof(g_calibration_info));
|
||||||
|
|
||||||
|
std::snprintf(out_name, out_name_size, "%02X%02X%02X%02X", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||||
|
}
|
||||||
|
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_calibration_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure we made our backup. */
|
||||||
|
AMS_ABORT_UNLESS(g_prodinfo_backup_file);
|
||||||
|
|
||||||
|
/* Setup our memory storage. */
|
||||||
|
g_fake_secure_backup_storage.emplace(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||||
|
|
||||||
|
/* Ensure that we have a fake storage. */
|
||||||
|
AMS_ABORT_UNLESS(static_cast<bool>(g_fake_secure_backup_storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldReadBlankCalibrationBinary() {
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
return static_cast<bool>(g_blank_prodinfo_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWriteToCalibrationBinaryAllowed() {
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
return g_allow_writes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size) {
|
||||||
|
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||||
|
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
R_ABORT_UNLESS(g_blank_prodinfo_storage->Read(offset, dst, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size) {
|
||||||
|
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||||
|
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
R_ABORT_UNLESS(g_blank_prodinfo_storage->Write(offset, src, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size) {
|
||||||
|
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||||
|
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
R_ABORT_UNLESS(g_fake_secure_backup_storage->Read(offset, dst, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size) {
|
||||||
|
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||||
|
|
||||||
|
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||||
|
R_ABORT_UNLESS(g_fake_secure_backup_storage->Write(offset, src, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.hpp
Normal file
39
stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.hpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::mitm {
|
||||||
|
|
||||||
|
constexpr inline size_t CalibrationBinarySize = 0x8000;
|
||||||
|
|
||||||
|
constexpr inline s64 SecureCalibrationInfoBackupOffset = 3_MB;
|
||||||
|
constexpr inline size_t SecureCalibrationBinaryBackupSize = 0xC000;
|
||||||
|
|
||||||
|
void InitializeProdInfoManagement();
|
||||||
|
|
||||||
|
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size);
|
||||||
|
|
||||||
|
bool ShouldReadBlankCalibrationBinary();
|
||||||
|
bool IsWriteToCalibrationBinaryAllowed();
|
||||||
|
|
||||||
|
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size);
|
||||||
|
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size);
|
||||||
|
|
||||||
|
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size);
|
||||||
|
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size);
|
||||||
|
|
||||||
|
}
|
46
stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.cpp
Normal file
46
stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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 "../amsmitm_initialization.hpp"
|
||||||
|
#include "bpc_ams_module.hpp"
|
||||||
|
#include "bpc_ams_service.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::bpc_ams {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr sm::ServiceName AtmosphereServiceName = sm::ServiceName::Encode("bpc:ams");
|
||||||
|
constexpr size_t AtmosphereMaxSessions = 4;
|
||||||
|
|
||||||
|
constexpr size_t MaxServers = 1;
|
||||||
|
constexpr size_t MaxSessions = AtmosphereMaxSessions;
|
||||||
|
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
||||||
|
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MitmModule::ThreadFunction(void *arg) {
|
||||||
|
/* Create bpc:ams. */
|
||||||
|
{
|
||||||
|
Handle bpcams_h;
|
||||||
|
R_ABORT_UNLESS(svcManageNamedPort(&bpcams_h, AtmosphereServiceName.name, AtmosphereMaxSessions));
|
||||||
|
g_server_manager.RegisterServer<bpc::AtmosphereService>(bpcams_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop forever, servicing our services. */
|
||||||
|
g_server_manager.LoopProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.hpp
Normal file
24
stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "../amsmitm_module.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::bpc_ams {
|
||||||
|
|
||||||
|
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(bpc, IpcServer));
|
||||||
|
|
||||||
|
}
|
|
@ -97,6 +97,21 @@ namespace ams::mitm::bpc {
|
||||||
DoRebootToPayload(ctx);
|
DoRebootToPayload(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetInitialRebootPayload(const void *payload, size_t payload_size) {
|
||||||
|
/* Clear payload buffer */
|
||||||
|
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
||||||
|
|
||||||
|
/* Ensure valid. */
|
||||||
|
AMS_ABORT_UNLESS(payload != nullptr && payload_size <= sizeof(g_reboot_payload));
|
||||||
|
|
||||||
|
/* Copy in payload. */
|
||||||
|
std::memcpy(g_reboot_payload, payload, payload_size);
|
||||||
|
|
||||||
|
/* NOTE: Preferred reboot type will be parsed from settings later on. */
|
||||||
|
g_reboot_type = RebootType::ToPayload;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Result LoadRebootPayload() {
|
Result LoadRebootPayload() {
|
||||||
/* Clear payload buffer */
|
/* Clear payload buffer */
|
||||||
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace ams::mitm::bpc {
|
||||||
void ShutdownSystem();
|
void ShutdownSystem();
|
||||||
|
|
||||||
/* Atmosphere power utilities. */
|
/* Atmosphere power utilities. */
|
||||||
|
void SetInitialRebootPayload(const void *payload, size_t payload_size);
|
||||||
Result LoadRebootPayload();
|
Result LoadRebootPayload();
|
||||||
Result DetectPreferredRebootFunctionality();
|
Result DetectPreferredRebootFunctionality();
|
||||||
void RebootForFatalError(const ams::FatalErrorContext *ctx);
|
void RebootForFatalError(const ams::FatalErrorContext *ctx);
|
||||||
|
|
|
@ -13,13 +13,32 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#include "../amsmitm_initialization.hpp"
|
||||||
#include "bpc_ams_service.hpp"
|
#include "bpc_ams_service.hpp"
|
||||||
#include "bpc_ams_power_utils.hpp"
|
#include "bpc_ams_power_utils.hpp"
|
||||||
|
|
||||||
namespace ams::mitm::bpc {
|
namespace ams::mitm::bpc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool g_set_initial_payload = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void AtmosphereService::RebootToFatalError(const ams::FatalErrorContext &ctx) {
|
void AtmosphereService::RebootToFatalError(const ams::FatalErrorContext &ctx) {
|
||||||
bpc::RebootForFatalError(&ctx);
|
bpc::RebootForFatalError(&ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AtmosphereService::SetInitialRebootPayload(const ams::sf::InBuffer &payload) {
|
||||||
|
if (!g_set_initial_payload) {
|
||||||
|
g_set_initial_payload = true;
|
||||||
|
|
||||||
|
/* Set the initial reboot payload. */
|
||||||
|
bpc::SetInitialRebootPayload(payload.GetPointer(), payload.GetSize());
|
||||||
|
|
||||||
|
/* Start the initialization process. */
|
||||||
|
::ams::mitm::StartInitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,16 @@ namespace ams::mitm::bpc {
|
||||||
class AtmosphereService final : public sf::IServiceObject {
|
class AtmosphereService final : public sf::IServiceObject {
|
||||||
private:
|
private:
|
||||||
enum class CommandId {
|
enum class CommandId {
|
||||||
RebootToFatalError = 65000,
|
RebootToFatalError = 65000,
|
||||||
|
SetInitialRebootPayload = 65001,
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
void RebootToFatalError(const ams::FatalErrorContext &ctx);
|
void RebootToFatalError(const ams::FatalErrorContext &ctx);
|
||||||
|
void SetInitialRebootPayload(const ams::sf::InBuffer &payload);
|
||||||
public:
|
public:
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||||
MAKE_SERVICE_COMMAND_META(RebootToFatalError),
|
MAKE_SERVICE_COMMAND_META(RebootToFatalError),
|
||||||
|
MAKE_SERVICE_COMMAND_META(SetInitialRebootPayload),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
#include "../amsmitm_initialization.hpp"
|
#include "../amsmitm_initialization.hpp"
|
||||||
#include "bpcmitm_module.hpp"
|
#include "bpcmitm_module.hpp"
|
||||||
#include "bpc_mitm_service.hpp"
|
#include "bpc_mitm_service.hpp"
|
||||||
#include "bpc_ams_service.hpp"
|
|
||||||
#include "bpc_ams_power_utils.hpp"
|
|
||||||
|
|
||||||
namespace ams::mitm::bpc {
|
namespace ams::mitm::bpc {
|
||||||
|
|
||||||
|
@ -27,11 +25,8 @@ namespace ams::mitm::bpc {
|
||||||
constexpr sm::ServiceName DeprecatedMitmServiceName = sm::ServiceName::Encode("bpc:c");
|
constexpr sm::ServiceName DeprecatedMitmServiceName = sm::ServiceName::Encode("bpc:c");
|
||||||
constexpr size_t MitmServiceMaxSessions = 13;
|
constexpr size_t MitmServiceMaxSessions = 13;
|
||||||
|
|
||||||
constexpr sm::ServiceName AtmosphereServiceName = sm::ServiceName::Encode("bpc:ams");
|
constexpr size_t MaxServers = 1;
|
||||||
constexpr size_t AtmosphereMaxSessions = 3;
|
constexpr size_t MaxSessions = MitmServiceMaxSessions;
|
||||||
|
|
||||||
constexpr size_t MaxServers = 2;
|
|
||||||
constexpr size_t MaxSessions = MitmServiceMaxSessions + AtmosphereMaxSessions;
|
|
||||||
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
||||||
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
||||||
|
|
||||||
|
@ -41,13 +36,6 @@ namespace ams::mitm::bpc {
|
||||||
/* Wait until initialization is complete. */
|
/* Wait until initialization is complete. */
|
||||||
mitm::WaitInitialized();
|
mitm::WaitInitialized();
|
||||||
|
|
||||||
/* Create bpc:ams. */
|
|
||||||
{
|
|
||||||
Handle bpcams_h;
|
|
||||||
R_ABORT_UNLESS(svcManageNamedPort(&bpcams_h, AtmosphereServiceName.name, AtmosphereMaxSessions));
|
|
||||||
g_server_manager.RegisterServer<bpc::AtmosphereService>(bpcams_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create bpc mitm. */
|
/* Create bpc mitm. */
|
||||||
const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_2_0_0) ? MitmServiceName : DeprecatedMitmServiceName;
|
const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_2_0_0) ? MitmServiceName : DeprecatedMitmServiceName;
|
||||||
R_ABORT_UNLESS(g_server_manager.RegisterMitmServer<BpcMitmService>(service_name));
|
R_ABORT_UNLESS(g_server_manager.RegisterMitmServer<BpcMitmService>(service_name));
|
||||||
|
|
|
@ -14,9 +14,11 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "../amsmitm_fs_utils.hpp"
|
#include "../amsmitm_fs_utils.hpp"
|
||||||
|
#include "../amsmitm_initialization.hpp"
|
||||||
#include "fs_shim.h"
|
#include "fs_shim.h"
|
||||||
#include "fs_mitm_service.hpp"
|
#include "fs_mitm_service.hpp"
|
||||||
#include "fsmitm_boot0storage.hpp"
|
#include "fsmitm_boot0storage.hpp"
|
||||||
|
#include "fsmitm_calibration_binary_storage.hpp"
|
||||||
#include "fsmitm_layered_romfs_storage.hpp"
|
#include "fsmitm_layered_romfs_storage.hpp"
|
||||||
#include "fsmitm_save_utils.hpp"
|
#include "fsmitm_save_utils.hpp"
|
||||||
#include "fsmitm_readonly_layered_filesystem.hpp"
|
#include "fsmitm_readonly_layered_filesystem.hpp"
|
||||||
|
@ -252,7 +254,6 @@ namespace ams::mitm::fs {
|
||||||
const bool is_sysmodule = ncm::IsSystemProgramId(this->client_info.program_id);
|
const bool is_sysmodule = ncm::IsSystemProgramId(this->client_info.program_id);
|
||||||
const bool is_hbl = this->client_info.override_status.IsHbl();
|
const bool is_hbl = this->client_info.override_status.IsHbl();
|
||||||
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
|
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
|
||||||
const bool can_read_cal = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_cal_read"));
|
|
||||||
|
|
||||||
/* Allow HBL to write to boot1 (safe firm) + package2. */
|
/* Allow HBL to write to boot1 (safe firm) + package2. */
|
||||||
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
|
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
|
||||||
|
@ -265,15 +266,7 @@ namespace ams::mitm::fs {
|
||||||
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
|
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
|
||||||
out.SetValue(std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->client_info)), target_object_id);
|
out.SetValue(std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->client_info)), target_object_id);
|
||||||
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
|
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
|
||||||
/* PRODINFO should *never* be writable. */
|
out.SetValue(std::make_shared<IStorageInterface>(new CalibrationBinaryStorage(bis_storage, this->client_info)), target_object_id);
|
||||||
/* If we have permissions, create a read only storage. */
|
|
||||||
if (can_read_cal) {
|
|
||||||
out.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id);
|
|
||||||
} else {
|
|
||||||
/* If we can't read cal, return permission denied. */
|
|
||||||
fsStorageClose(&bis_storage);
|
|
||||||
return fs::ResultPermissionDenied();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (can_write_bis || can_write_bis_for_choi_support) {
|
if (can_write_bis || can_write_bis_for_choi_support) {
|
||||||
/* We can write, so create a writable storage. */
|
/* We can write, so create a writable storage. */
|
||||||
|
|
|
@ -54,6 +54,11 @@ namespace ams::mitm::fs {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We want to mitm settings, to intercept CAL0. */
|
||||||
|
if (program_id == ncm::SystemProgramId::Settings) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
|
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
|
||||||
if (program_id == ncm::SystemProgramId::Sdb) {
|
if (program_id == ncm::SystemProgramId::Sdb) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* 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 <stratosphere.hpp>
|
||||||
|
#include "fsmitm_calibration_binary_storage.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::fs {
|
||||||
|
|
||||||
|
using namespace ams::fs;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
os::Mutex g_cal0_access_mutex(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
Result CalibrationBinaryStorage::Read(s64 offset, void *_buffer, size_t size) {
|
||||||
|
/* Acquire exclusive calibration binary access. */
|
||||||
|
std::scoped_lock lk(g_cal0_access_mutex);
|
||||||
|
|
||||||
|
/* Get u8 buffer. */
|
||||||
|
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||||
|
|
||||||
|
/* Succeed on zero-size. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Handle the blank region. */
|
||||||
|
if (this->read_blank) {
|
||||||
|
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||||
|
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||||
|
mitm::ReadFromBlankCalibrationBinary(offset, buffer, blank_size);
|
||||||
|
size -= blank_size;
|
||||||
|
buffer += blank_size;
|
||||||
|
offset += blank_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Succeed if we're done. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Handle any in-between data. */
|
||||||
|
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||||
|
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||||
|
R_TRY(Base::Read(offset, buffer, mid_size));
|
||||||
|
size -= mid_size;
|
||||||
|
buffer += mid_size;
|
||||||
|
offset += mid_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Succeed if we're done. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Handle the secure region. */
|
||||||
|
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||||
|
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||||
|
mitm::ReadFromFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||||
|
size -= fake_size;
|
||||||
|
buffer += fake_size;
|
||||||
|
offset += fake_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Succeed if we're done. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Handle any remaining data. */
|
||||||
|
return Base::Read(offset, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CalibrationBinaryStorage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||||
|
/* Acquire exclusive calibration binary access. */
|
||||||
|
std::scoped_lock lk(g_cal0_access_mutex);
|
||||||
|
|
||||||
|
/* Get const u8 buffer. */
|
||||||
|
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||||
|
|
||||||
|
/* Succeed on zero-size. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Only allow writes if we should. */
|
||||||
|
R_UNLESS(this->allow_writes, fs::ResultUnsupportedOperation());
|
||||||
|
|
||||||
|
/* Handle the blank region. */
|
||||||
|
if (this->read_blank) {
|
||||||
|
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||||
|
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||||
|
mitm::WriteToBlankCalibrationBinary(offset, buffer, blank_size);
|
||||||
|
size -= blank_size;
|
||||||
|
buffer += blank_size;
|
||||||
|
offset += blank_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Succeed if we're done. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Handle any in-between data. */
|
||||||
|
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||||
|
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||||
|
R_TRY(Base::Write(offset, buffer, mid_size));
|
||||||
|
size -= mid_size;
|
||||||
|
buffer += mid_size;
|
||||||
|
offset += mid_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Succeed if we're done. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Handle the secure region. */
|
||||||
|
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||||
|
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||||
|
mitm::WriteToFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||||
|
size -= fake_size;
|
||||||
|
buffer += fake_size;
|
||||||
|
offset += fake_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Succeed if we're done. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Handle any remaining data. */
|
||||||
|
return Base::Write(offset, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "fsmitm_boot0storage.hpp"
|
||||||
|
#include "../amsmitm_prodinfo_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::fs {
|
||||||
|
|
||||||
|
/* Represents a protected calibration binary partition. */
|
||||||
|
class CalibrationBinaryStorage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||||
|
public:
|
||||||
|
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||||
|
|
||||||
|
static constexpr s64 BlankStartOffset = 0x0;
|
||||||
|
static constexpr s64 BlankSize = static_cast<s64>(CalibrationBinarySize);
|
||||||
|
static constexpr s64 BlankEndOffset = BlankStartOffset + BlankSize;
|
||||||
|
|
||||||
|
static constexpr s64 FakeSecureStartOffset = SecureCalibrationInfoBackupOffset;
|
||||||
|
static constexpr s64 FakeSecureSize = static_cast<s64>(SecureCalibrationBinaryBackupSize);
|
||||||
|
static constexpr s64 FakeSecureEndOffset = FakeSecureStartOffset + FakeSecureSize;
|
||||||
|
private:
|
||||||
|
sm::MitmProcessInfo client_info;
|
||||||
|
bool read_blank;
|
||||||
|
bool allow_writes;
|
||||||
|
public:
|
||||||
|
CalibrationBinaryStorage(FsStorage &s, const sm::MitmProcessInfo &c)
|
||||||
|
: Base(s), client_info(c),
|
||||||
|
read_blank(mitm::ShouldReadBlankCalibrationBinary()),
|
||||||
|
allow_writes(mitm::IsWriteToCalibrationBinaryAllowed())
|
||||||
|
{
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||||
|
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -328,10 +328,6 @@ namespace ams::settings::fwdbg {
|
||||||
/* This is probably undesirable for normal usage. */
|
/* This is probably undesirable for normal usage. */
|
||||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_hbl_bis_write", "u8!0x0"));
|
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_hbl_bis_write", "u8!0x0"));
|
||||||
|
|
||||||
/* Enable HBL to read the CAL0 partition. */
|
|
||||||
/* This is probably undesirable for normal usage. */
|
|
||||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_hbl_cal_read", "u8!0x0"));
|
|
||||||
|
|
||||||
/* Controls whether dmnt cheats should be toggled on or off by */
|
/* Controls whether dmnt cheats should be toggled on or off by */
|
||||||
/* default. 1 = toggled on by default, 0 = toggled off by default. */
|
/* default. 1 = toggled on by default, 0 = toggled off by default. */
|
||||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", "u8!0x1"));
|
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", "u8!0x1"));
|
||||||
|
|
|
@ -111,6 +111,12 @@ int main(int argc, char **argv)
|
||||||
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(boot, Main));
|
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(boot, Main));
|
||||||
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(boot, Main));
|
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(boot, Main));
|
||||||
|
|
||||||
|
/* Perform atmosphere-specific init. */
|
||||||
|
ams::InitializeForBoot();
|
||||||
|
|
||||||
|
/* Set the reboot payload with ams.mitm. */
|
||||||
|
boot::SetInitialRebootPayload();
|
||||||
|
|
||||||
/* Change voltage from 3.3v to 1.8v for select devices. */
|
/* Change voltage from 3.3v to 1.8v for select devices. */
|
||||||
boot::ChangeGpioVoltageTo1_8v();
|
boot::ChangeGpioVoltageTo1_8v();
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,10 @@ namespace ams::boot {
|
||||||
DoRebootToPayload(nullptr);
|
DoRebootToPayload(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetInitialRebootPayload() {
|
||||||
|
::ams::SetInitialRebootPayload(fusee_primary_bin, fusee_primary_bin_size);
|
||||||
|
}
|
||||||
|
|
||||||
void RebootForFatalError(ams::FatalErrorContext *ctx) {
|
void RebootForFatalError(ams::FatalErrorContext *ctx) {
|
||||||
DoRebootToPayload(ctx);
|
DoRebootToPayload(ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace ams::boot {
|
||||||
void ShutdownSystem();
|
void ShutdownSystem();
|
||||||
|
|
||||||
/* Atmosphere power utilities. */
|
/* Atmosphere power utilities. */
|
||||||
|
void SetInitialRebootPayload();
|
||||||
void RebootForFatalError(ams::FatalErrorContext *ctx);
|
void RebootForFatalError(ams::FatalErrorContext *ctx);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ namespace {
|
||||||
constexpr size_t GeneralMaxSessions = 7;
|
constexpr size_t GeneralMaxSessions = 7;
|
||||||
|
|
||||||
constexpr sm::ServiceName CryptoServiceName = sm::ServiceName::Encode("spl:mig");
|
constexpr sm::ServiceName CryptoServiceName = sm::ServiceName::Encode("spl:mig");
|
||||||
constexpr size_t CryptoMaxSessions = 6;
|
constexpr size_t CryptoMaxSessions = 7;
|
||||||
|
|
||||||
constexpr sm::ServiceName SslServiceName = sm::ServiceName::Encode("spl:ssl");
|
constexpr sm::ServiceName SslServiceName = sm::ServiceName::Encode("spl:ssl");
|
||||||
constexpr size_t SslMaxSessions = 2;
|
constexpr size_t SslMaxSessions = 2;
|
||||||
|
|
Loading…
Reference in a new issue