Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84c776fa6b | ||
|
|
a21b5d453a | ||
|
|
0ef3368893 | ||
|
|
4827fd71b4 | ||
|
|
00aa283a54 | ||
|
|
ef373d954f | ||
|
|
d452d6f89d | ||
|
|
a51d355707 | ||
|
|
a79f4cf6f6 | ||
|
|
903789cf6e | ||
|
|
49ba3a86e2 | ||
|
|
021d84ff04 | ||
|
|
8ecf68cb65 | ||
|
|
bbed78149c | ||
|
|
3fa973f430 | ||
|
|
bd76e73b25 | ||
|
|
fc426a06b2 | ||
|
|
ef17dc16fa | ||
|
|
ed4a999caa | ||
|
|
6b3662d047 | ||
|
|
33b7e227d4 | ||
|
|
be5b58d033 | ||
|
|
8d3b8354c3 | ||
|
|
25956c4fa1 | ||
|
|
5201803685 | ||
|
|
c6003ff530 | ||
|
|
72f028efae | ||
|
|
72a2c10896 | ||
|
|
49ad66e478 |
5
Makefile
5
Makefile
@@ -42,9 +42,10 @@ dist: all
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
|
||||
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/fusee-secondary.bin
|
||||
cp common/defaults/BCT.ini atmosphere-$(AMSVER)/BCT.ini
|
||||
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin
|
||||
cp common/defaults/BCT.ini atmosphere-$(AMSVER)/atmosphere/BCT.ini
|
||||
cp common/defaults/loader.ini atmosphere-$(AMSVER)/atmosphere/loader.ini
|
||||
cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches
|
||||
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp
|
||||
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp
|
||||
cp stratosphere/set_mitm/set_mitm.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
BCT0
|
||||
[stage1]
|
||||
stage2_path = fusee-secondary.bin
|
||||
stage2_path = atmosphere/fusee-secondary.bin
|
||||
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
|
||||
|
||||
[stratosphere]
|
||||
; To force-enable nogc, add nogc = 1
|
||||
; To force-disable nogc, add nogc = 0
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -19,6 +19,6 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 8
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 2
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,34 @@
|
||||
# Changelog
|
||||
## 0.8.2
|
||||
+ A number of bugs were fixed causing users to sometimes see `Key Derivation Failed!`.
|
||||
+ KFUSE clock enable timings have been adjusted to allow time to stabilize before TSEC is granted access.
|
||||
+ A race condition was fixed that could cause wrong key data to be used on 6.2.0
|
||||
+ The TSEC firmware is now retried on failure, fixing a failure affecting ~1/50 boots on 6.2.0.
|
||||
+ A bug was fixed causing some modules to not work on firmware 1.0.0.
|
||||
+ A bug was fixed causing sleep mode to not work with debugmode enabled.
|
||||
+ As a result, debugmode is now enabled in the default BCT.ini.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.8.1
|
||||
+ A bug was fixed causing users to see `Failed to enable SMMU!` if fusee had previously rebooted.
|
||||
+ This message will still occur sporadically if fusee is not launched from coldboot, but it can never happen twice in a row.
|
||||
+ A race condition was fixed in Atmosphere `bis_protect` functionality that could cause NS to be able to overwrite BCT public keys.
|
||||
+ This sometimes broke AutoRCM protection, the current fix has been tested on hardware and verified to work.
|
||||
+ Support was added for enabling `debugmode` based on the `exosphere` section of `BCT.ini`:
|
||||
+ Setting `debugmode = 1` will cause exosphere to tell the kernel that debugmode is active.
|
||||
+ Setting `debugmode_user = 1` will cause exosphere to tell userland that debugmode is active.
|
||||
+ These are completely independent of one another, allowing fine control of system behavior.
|
||||
+ Support was added for `nogc` functionality; thanks to @rajkosto for the patches.
|
||||
+ By default, `nogc` patches will automatically apply if the user is booting into 4.0.0+ with fuses from <= 3.0.2.
|
||||
+ Users can override this functionality via the `nogc` entry in the `stratosphere` section of `BCT.ini`:
|
||||
+ Setting `nogc = 1` will force enable `nogc` patches.
|
||||
+ Setting `nogc = 0` will force disable `nogc` patches.
|
||||
+ If patches are enabled but not found for the booting system, a fatal error will be thrown.
|
||||
+ This should prevent running FS without `nogc` patches after updating to an unsupported system version.
|
||||
+ An extension was added to `exosphere` allowing userland applications to cause the system to reboot into RCM:
|
||||
+ This is done by calling smcSetConfig(id=65001, value=<nonzero>); user homebrew can use splSetConfig for this.
|
||||
+ On fatal error, the user can now choose to perform a standard reboot via the power button, or a reboot into RCM via either volume button.
|
||||
+ A custom message was added to `fatal` for when an Atmosphère API version mismatch is detected (2495-1623).
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.8.0
|
||||
+ A custom `fatal` system module was added.
|
||||
+ This re-implements and extends Nintendo's fatal module, with the following features:
|
||||
@@ -27,6 +57,7 @@
|
||||
+ To facilitate this, `fs.mitm` now mitms all sessions for non-system modules; content overriding has been made separate from service interception.
|
||||
+ Please note: these protections are basic, and sufficiently malicious homebrew ++can defeat them++.
|
||||
+ Please be careful to only run homebrew software from sources that you trust.
|
||||
+ A bug involving HDCP titles crashing on newer firmwares was fixed.
|
||||
+ Support was added for system version 6.2.0; our thanks to @motezazer for his invaluable help.
|
||||
+ By default, new keys will automatically be derived without user input.
|
||||
+ Support is also present for loading new keys from `atmosphere/prod.keys` or `atmosphere/dev.keys`
|
||||
|
||||
12
docs/flags.md
Normal file
12
docs/flags.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Flags
|
||||
Atmosphère supports customizing CFW behavior based on the presence of `flags` on the SD card.
|
||||
|
||||
The following flags are supported on a per-title basis, by placing `<flag_name>.flag` inside `/atmosphere/titles/<title_id>/flags/`:
|
||||
+ `boot2`, which indicates to PM that the title should be launched during the `boot2` process.
|
||||
+ `fsmitm`, which indicates that `fs.mitm` should override contents for the title even if it otherwise wouldn't.
|
||||
+ `fsmitm_disable`, which indicates that `fs.mitm` should not override contents for the title, even it it otherwise would.
|
||||
+ `bis_write`, which indicates that `fs.mitm` should allow the title to write to BIS partitions.
|
||||
+ `cal_read`, which indicates that `fs.mitm` should allow the title to read the CAL0/PRODINFO partition.
|
||||
|
||||
The following global flags are supported, by placing `<flag name>.flag` inside `/atmosphere/flags/`:
|
||||
+ `hbl_bis_write` and `hbl_cal_read` enable the BIS write and CAL0 read functionality for HBL, without needing to specify its title id.
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "configitem.h"
|
||||
#include "timers.h"
|
||||
#include "misc.h"
|
||||
#include "uart.h"
|
||||
#include "bpmp.h"
|
||||
#include "sysreg.h"
|
||||
#include "interrupt.h"
|
||||
@@ -213,6 +214,11 @@ void bootup_misc_mmio(void) {
|
||||
}
|
||||
|
||||
if (!g_has_booted_up) {
|
||||
/* N doesn't do this, but we should for compatibility. */
|
||||
uart_select(UART_A);
|
||||
clkrst_reboot(CARDEVICE_UARTA);
|
||||
uart_init(UART_A, 115200);
|
||||
|
||||
intr_register_handler(INTERRUPT_ID_SECURITY_ENGINE, se_operation_completed);
|
||||
if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) {
|
||||
intr_register_handler(INTERRUPT_ID_ACTIVITY_MONITOR_4X, actmon_interrupt_handler);
|
||||
|
||||
@@ -28,19 +28,31 @@
|
||||
#include "exocfg.h"
|
||||
|
||||
static bool g_battery_profile = false;
|
||||
static bool g_debugmode_override_user = false, g_debugmode_override_priv = false;
|
||||
|
||||
uint32_t configitem_set(ConfigItem item, uint64_t value) {
|
||||
if (item != CONFIGITEM_BATTERYPROFILE) {
|
||||
return 2;
|
||||
uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
|
||||
switch (item) {
|
||||
case CONFIGITEM_BATTERYPROFILE:
|
||||
g_battery_profile = (value != 0);
|
||||
break;
|
||||
case CONFIGITEM_NEEDS_REBOOT_TO_RCM:
|
||||
/* Force a reboot to RCM, if requested. */
|
||||
if (value != 0) {
|
||||
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2;
|
||||
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10;
|
||||
while (1) { }
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
|
||||
g_battery_profile = (value != 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool configitem_is_recovery_boot(void) {
|
||||
uint64_t is_recovery_boot;
|
||||
if (configitem_get(CONFIGITEM_ISRECOVERYBOOT, &is_recovery_boot) != 0) {
|
||||
if (configitem_get(true, CONFIGITEM_ISRECOVERYBOOT, &is_recovery_boot) != 0) {
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
@@ -49,7 +61,7 @@ bool configitem_is_recovery_boot(void) {
|
||||
|
||||
bool configitem_is_retail(void) {
|
||||
uint64_t is_retail;
|
||||
if (configitem_get(CONFIGITEM_ISRETAIL, &is_retail) != 0) {
|
||||
if (configitem_get(true, CONFIGITEM_ISRETAIL, &is_retail) != 0) {
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
@@ -60,15 +72,29 @@ bool configitem_should_profile_battery(void) {
|
||||
return g_battery_profile;
|
||||
}
|
||||
|
||||
bool configitem_is_debugmode_priv(void) {
|
||||
uint64_t debugmode = 0;
|
||||
if (configitem_get(true, CONFIGITEM_ISDEBUGMODE, &debugmode) != 0) {
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
return debugmode != 0;
|
||||
}
|
||||
|
||||
uint64_t configitem_get_hardware_type(void) {
|
||||
uint64_t hardware_type;
|
||||
if (configitem_get(CONFIGITEM_HARDWARETYPE, &hardware_type) != 0) {
|
||||
if (configitem_get(true, CONFIGITEM_HARDWARETYPE, &hardware_type) != 0) {
|
||||
generic_panic();
|
||||
}
|
||||
return hardware_type;
|
||||
}
|
||||
|
||||
uint32_t configitem_get(ConfigItem item, uint64_t *p_outvalue) {
|
||||
void configitem_set_debugmode_override(bool user, bool priv) {
|
||||
g_debugmode_override_user = user;
|
||||
g_debugmode_override_priv = priv;
|
||||
}
|
||||
|
||||
uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) {
|
||||
uint32_t result = 0;
|
||||
switch (item) {
|
||||
case CONFIGITEM_DISABLEPROGRAMVERIFICATION:
|
||||
@@ -109,7 +135,11 @@ uint32_t configitem_get(ConfigItem item, uint64_t *p_outvalue) {
|
||||
*p_outvalue = bootconfig_get_memory_arrangement();
|
||||
break;
|
||||
case CONFIGITEM_ISDEBUGMODE:
|
||||
*p_outvalue = (int)(bootconfig_is_debug_mode());
|
||||
if ((privileged && g_debugmode_override_priv) || (!privileged && g_debugmode_override_user)) {
|
||||
*p_outvalue = 1;
|
||||
} else {
|
||||
*p_outvalue = (int)(bootconfig_is_debug_mode());
|
||||
}
|
||||
break;
|
||||
case CONFIGITEM_KERNELMEMORYCONFIGURATION:
|
||||
*p_outvalue = bootconfig_get_kernel_memory_configuration();
|
||||
@@ -157,6 +187,10 @@ uint32_t configitem_get(ConfigItem item, uint64_t *p_outvalue) {
|
||||
((uint64_t)(exosphere_get_target_firmware() & 0xFF) << 8ull) |
|
||||
((uint64_t)(mkey_get_revision() & 0xFF) << 0ull);
|
||||
break;
|
||||
case CONFIGITEM_NEEDS_REBOOT_TO_RCM:
|
||||
/* UNOFFICIAL: The fact that we are executing means we aren't in the process of rebooting to rcm. */
|
||||
*p_outvalue = 0;
|
||||
break;
|
||||
default:
|
||||
result = 2;
|
||||
break;
|
||||
|
||||
@@ -40,15 +40,19 @@ typedef enum {
|
||||
CONFIGITEM_PACKAGE2HASH_5X = 17,
|
||||
|
||||
/* These are unofficial, for usage by Exosphere. */
|
||||
CONFIGITEM_EXOSPHERE_VERSION = 65000
|
||||
CONFIGITEM_EXOSPHERE_VERSION = 65000,
|
||||
CONFIGITEM_NEEDS_REBOOT_TO_RCM = 65001,
|
||||
} ConfigItem;
|
||||
|
||||
uint32_t configitem_set(ConfigItem item, uint64_t value);
|
||||
uint32_t configitem_get(ConfigItem item, uint64_t *p_outvalue);
|
||||
uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value);
|
||||
uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue);
|
||||
|
||||
bool configitem_is_recovery_boot(void);
|
||||
bool configitem_is_retail(void);
|
||||
bool configitem_should_profile_battery(void);
|
||||
bool configitem_is_debugmode_priv(void);
|
||||
|
||||
void configitem_set_debugmode_override(bool user, bool priv);
|
||||
|
||||
uint64_t configitem_get_hardware_type(void);
|
||||
|
||||
|
||||
@@ -67,3 +67,19 @@ unsigned int exosphere_should_perform_620_keygen(void) {
|
||||
|
||||
return g_exosphere_cfg.target_firmware >= EXOSPHERE_TARGET_FIRMWARE_620 && EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_PERFORM_620_KEYGEN);
|
||||
}
|
||||
|
||||
unsigned int exosphere_should_override_debugmode_priv(void) {
|
||||
if (!g_has_loaded_config) {
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV);
|
||||
}
|
||||
|
||||
unsigned int exosphere_should_override_debugmode_user(void) {
|
||||
if (!g_has_loaded_config) {
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_IS_DEBUGMODE_USER);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
|
||||
#define EXOSPHERE_FLAGS_DEFAULT 0x00000000
|
||||
#define EXOSPHERE_FLAG_PERFORM_620_KEYGEN (1 << 0u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic;
|
||||
@@ -60,6 +62,8 @@ typedef struct {
|
||||
unsigned int exosphere_load_config(void);
|
||||
unsigned int exosphere_get_target_firmware(void);
|
||||
unsigned int exosphere_should_perform_620_keygen(void);
|
||||
unsigned int exosphere_should_override_debugmode_priv(void);
|
||||
unsigned int exosphere_should_override_debugmode_user(void);
|
||||
|
||||
static inline unsigned int exosphere_get_target_firmware_for_init(void) {
|
||||
const unsigned int magic = MAILBOX_EXOSPHERE_CONFIG_PHYS.magic;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "smc_api.h"
|
||||
#include "timers.h"
|
||||
#include "misc.h"
|
||||
#include "uart.h"
|
||||
#include "exocfg.h"
|
||||
|
||||
#define u8 uint8_t
|
||||
@@ -241,7 +242,7 @@ void save_se_and_power_down_cpu(void) {
|
||||
save_se_state();
|
||||
|
||||
if (!configitem_is_retail()) {
|
||||
/* TODO: uart_log("OYASUMI"); */
|
||||
uart_send(UART_A, "OYASUMI", 8);
|
||||
}
|
||||
|
||||
finalize_powerdown();
|
||||
|
||||
@@ -509,6 +509,7 @@ uintptr_t get_pk2ldr_stack_address(void) {
|
||||
void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
|
||||
/* Load Exosphere-specific config. */
|
||||
exosphere_load_config();
|
||||
configitem_set_debugmode_override(exosphere_should_override_debugmode_user() != 0, exosphere_should_override_debugmode_priv() != 0);
|
||||
|
||||
/* Setup the Security Engine. */
|
||||
setup_se();
|
||||
|
||||
@@ -33,7 +33,7 @@ static const uint8_t g_seal_key_sources[CRYPTOUSECASE_MAX_5X][0x10] = {
|
||||
{0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D},
|
||||
{0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE},
|
||||
{0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9},
|
||||
{0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9}
|
||||
{0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B}
|
||||
};
|
||||
|
||||
bool usecase_is_invalid(unsigned int usecase) {
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
#define DEBUG_PANIC_ON_FAILURE 0
|
||||
|
||||
/* User SMC prototypes */
|
||||
uint32_t smc_set_config(smc_args_t *args);
|
||||
uint32_t smc_get_config(smc_args_t *args);
|
||||
uint32_t smc_set_config_user(smc_args_t *args);
|
||||
uint32_t smc_get_config_user(smc_args_t *args);
|
||||
uint32_t smc_check_status(smc_args_t *args);
|
||||
uint32_t smc_get_result(smc_args_t *args);
|
||||
uint32_t smc_exp_mod(smc_args_t *args);
|
||||
@@ -71,7 +71,7 @@ uint32_t smc_decrypt_or_import_rsa_key(smc_args_t *args);
|
||||
uint32_t smc_cpu_suspend(smc_args_t *args);
|
||||
uint32_t smc_cpu_off(smc_args_t *args);
|
||||
uint32_t smc_cpu_on(smc_args_t *args);
|
||||
/* uint32_t smc_get_config(smc_args_t *args); */
|
||||
uint32_t smc_get_config_priv(smc_args_t *args);
|
||||
uint32_t smc_get_random_bytes_for_priv(smc_args_t *args);
|
||||
uint32_t smc_panic(smc_args_t *args);
|
||||
uint32_t smc_configure_carveout(smc_args_t *args);
|
||||
@@ -89,8 +89,8 @@ typedef struct {
|
||||
|
||||
static smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = {
|
||||
{0, NULL},
|
||||
{0xC3000401, smc_set_config},
|
||||
{0xC3000002, smc_get_config},
|
||||
{0xC3000401, smc_set_config_user},
|
||||
{0xC3000002, smc_get_config_user},
|
||||
{0xC3000003, smc_check_status},
|
||||
{0xC3000404, smc_get_result},
|
||||
{0xC3000E05, smc_exp_mod},
|
||||
@@ -114,7 +114,7 @@ static smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = {
|
||||
{0xC4000001, smc_cpu_suspend},
|
||||
{0x84000002, smc_cpu_off},
|
||||
{0xC4000003, smc_cpu_on},
|
||||
{0xC3000004, smc_get_config}, /* NOTE: Same function as for USER */
|
||||
{0xC3000004, smc_get_config_priv},
|
||||
{0xC3000005, smc_get_random_bytes_for_priv},
|
||||
{0xC3000006, smc_panic},
|
||||
{0xC3000007, smc_configure_carveout},
|
||||
@@ -329,15 +329,15 @@ uint32_t smc_wrapper_async(smc_args_t *args, uint32_t (*handler)(smc_args_t *),
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t smc_set_config(smc_args_t *args) {
|
||||
uint32_t smc_set_config_user(smc_args_t *args) {
|
||||
/* Actual value presumed in X3 on hardware. */
|
||||
return configitem_set((ConfigItem)args->X[1], args->X[3]);
|
||||
return configitem_set(false, (ConfigItem)args->X[1], args->X[3]);
|
||||
}
|
||||
|
||||
uint32_t smc_get_config(smc_args_t *args) {
|
||||
uint32_t smc_get_config_user(smc_args_t *args) {
|
||||
uint64_t out_item = 0;
|
||||
uint32_t result;
|
||||
result = configitem_get((ConfigItem)args->X[1], &out_item);
|
||||
result = configitem_get(false, (ConfigItem)args->X[1], &out_item);
|
||||
args->X[1] = out_item;
|
||||
return result;
|
||||
}
|
||||
@@ -534,6 +534,14 @@ uint32_t smc_cpu_suspend(smc_args_t *args) {
|
||||
return smc_wrapper_sync(args, cpu_suspend_wrapper);
|
||||
}
|
||||
|
||||
uint32_t smc_get_config_priv(smc_args_t *args) {
|
||||
uint64_t out_item = 0;
|
||||
uint32_t result;
|
||||
result = configitem_get(true, (ConfigItem)args->X[1], &out_item);
|
||||
args->X[1] = out_item;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t smc_get_random_bytes_for_priv(smc_args_t *args) {
|
||||
/* This is an interesting SMC. */
|
||||
/* The kernel must NEVER be unable to get random bytes, if it needs them */
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "car.h"
|
||||
#include "i2c.h"
|
||||
#include "misc.h"
|
||||
#include "uart.h"
|
||||
#include "interrupt.h"
|
||||
|
||||
#include "pmc.h"
|
||||
@@ -55,8 +56,16 @@ void __attribute__((noreturn)) warmboot_main(void) {
|
||||
|
||||
/* On warmboot (not cpu_on) only */
|
||||
if (VIRT_MC_SECURITY_CFG3 == 0) {
|
||||
/* N only does this on dev units, but we will do it unconditionally. */
|
||||
{
|
||||
uart_select(UART_A);
|
||||
clkrst_reboot(CARDEVICE_UARTA);
|
||||
uart_init(UART_A, 115200);
|
||||
}
|
||||
|
||||
if (!configitem_is_retail()) {
|
||||
/* TODO: uart_log("OHAYO"); */
|
||||
uart_send(UART_A, "OHAYO", 6);
|
||||
uart_wait_idle(UART_A, UART_VENDOR_STATE_TX_IDLE);
|
||||
}
|
||||
|
||||
/* Sanity check the Security Engine. */
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "car.h"
|
||||
#include "timers.h"
|
||||
#include "utils.h"
|
||||
|
||||
static inline uint32_t get_clk_source_reg(CarDevice dev) {
|
||||
@@ -121,7 +122,15 @@ void clkrst_disable(CarDevice dev) {
|
||||
|
||||
void clkrst_reboot(CarDevice dev) {
|
||||
clkrst_disable(dev);
|
||||
clkrst_enable(dev);
|
||||
if (dev == CARDEVICE_KFUSE) {
|
||||
/* Workaround for KFUSE clock. */
|
||||
clk_enable(dev);
|
||||
udelay(100);
|
||||
rst_disable(dev);
|
||||
udelay(200);
|
||||
} else {
|
||||
clkrst_enable(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void clkrst_enable_fuse_regs(bool enable) {
|
||||
|
||||
@@ -40,12 +40,12 @@ static char g_bct0_buffer[BCTO_MAX_SIZE];
|
||||
#define DEFAULT_BCT0_FOR_DEBUG \
|
||||
"BCT0\n"\
|
||||
"[stage1]\n"\
|
||||
"stage2_path = fusee-secondary.bin\n"\
|
||||
"stage2_path = atmosphere/fusee-secondary.bin\n"\
|
||||
"stage2_addr = 0xF0000000\n"\
|
||||
"stage2_entrypoint = 0xF0000000\n"
|
||||
|
||||
static const char *load_config(void) {
|
||||
if (!read_from_file(g_bct0_buffer, BCTO_MAX_SIZE, "BCT.ini")) {
|
||||
if (!read_from_file(g_bct0_buffer, BCTO_MAX_SIZE, "atmosphere/BCT.ini")) {
|
||||
print(SCREEN_LOG_LEVEL_DEBUG, "Failed to read BCT0 from SD!\n");
|
||||
print(SCREEN_LOG_LEVEL_DEBUG, "Using default BCT0!\n");
|
||||
memcpy(g_bct0_buffer, DEFAULT_BCT0_FOR_DEBUG, sizeof(DEFAULT_BCT0_FOR_DEBUG));
|
||||
@@ -108,7 +108,7 @@ static void setup_env(void) {
|
||||
|
||||
/* Set up the exception handlers. */
|
||||
setup_exception_handlers();
|
||||
|
||||
|
||||
/* Mount the SD card. */
|
||||
mount_sd();
|
||||
}
|
||||
@@ -133,6 +133,9 @@ int main(void) {
|
||||
uint32_t stage2_version = 0;
|
||||
ScreenLogLevel log_level = SCREEN_LOG_LEVEL_MANDATORY;
|
||||
|
||||
/* Override the global logging level. */
|
||||
log_set_log_level(log_level);
|
||||
|
||||
/* Initialize the display, console, etc. */
|
||||
setup_env();
|
||||
|
||||
@@ -144,9 +147,6 @@ int main(void) {
|
||||
fatal_error("Failed to parse BCT.ini!\n");
|
||||
}
|
||||
|
||||
/* Override the global logging level. */
|
||||
log_set_log_level(log_level);
|
||||
|
||||
/* Say hello. */
|
||||
print(SCREEN_LOG_LEVEL_MANDATORY, "Welcome to Atmosph\xe8re Fus\xe9" "e!\n");
|
||||
print(SCREEN_LOG_LEVEL_DEBUG, "Using color linear framebuffer at 0x%p!\n", g_framebuffer);
|
||||
@@ -159,7 +159,7 @@ int main(void) {
|
||||
strcpy(g_chainloader_arg_data, stage2_path);
|
||||
stage2_args = (stage2_args_t *)(g_chainloader_arg_data + strlen(stage2_path) + 1); /* May be unaligned. */
|
||||
memcpy(&stage2_args->version, &stage2_version, 4);
|
||||
stage2_args->log_level = log_level;
|
||||
memcpy(&stage2_args->log_level, &log_level, sizeof(log_level));
|
||||
stage2_args->display_initialized = false;
|
||||
strcpy(stage2_args->bct0, bct0);
|
||||
g_chainloader_argc = 2;
|
||||
|
||||
@@ -59,21 +59,12 @@ __attribute__((noreturn)) void pmc_reboot(uint32_t scratch0) {
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void car_reboot(void) {
|
||||
/* Reset the processor. */
|
||||
car_get_regs()->rst_dev_l |= 1<<2;
|
||||
|
||||
while (true) {
|
||||
/* Wait for reboot. */
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void wait_for_button_and_reboot(void) {
|
||||
uint32_t button;
|
||||
while (true) {
|
||||
button = btn_read();
|
||||
if (button & BTN_POWER) {
|
||||
car_reboot();
|
||||
pmc_reboot(1 << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,6 @@ void hexdump(const void* data, size_t size, uintptr_t addrbase);
|
||||
|
||||
__attribute__((noreturn)) void watchdog_reboot(void);
|
||||
__attribute__((noreturn)) void pmc_reboot(uint32_t scratch0);
|
||||
__attribute__((noreturn)) void car_reboot(void);
|
||||
__attribute__((noreturn)) void wait_for_button_and_reboot(void);
|
||||
|
||||
__attribute__((noreturn)) void generic_panic(void);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "car.h"
|
||||
#include "timers.h"
|
||||
#include "utils.h"
|
||||
|
||||
static inline uint32_t get_clk_source_reg(CarDevice dev) {
|
||||
@@ -121,7 +122,15 @@ void clkrst_disable(CarDevice dev) {
|
||||
|
||||
void clkrst_reboot(CarDevice dev) {
|
||||
clkrst_disable(dev);
|
||||
clkrst_enable(dev);
|
||||
if (dev == CARDEVICE_KFUSE) {
|
||||
/* Workaround for KFUSE clock. */
|
||||
clk_enable(dev);
|
||||
udelay(100);
|
||||
rst_disable(dev);
|
||||
udelay(200);
|
||||
} else {
|
||||
clkrst_enable(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void clkrst_enable_fuse_regs(bool enable) {
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
#define EXOSPHERE_FLAGS_DEFAULT 0x00000000
|
||||
#define EXOSPHERE_FLAG_PERFORM_620_KEYGEN (1 << 0u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic;
|
||||
@@ -47,5 +49,7 @@ typedef struct {
|
||||
#define MAILBOX_EXOSPHERE_CONFIGURATION ((volatile exosphere_config_t *)(0x40002E40))
|
||||
|
||||
#define EXOSPHERE_TARGETFW_KEY "target_firmware"
|
||||
#define EXOSPHERE_DEBUGMODE_PRIV_KEY "debugmode"
|
||||
#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user"
|
||||
|
||||
#endif
|
||||
@@ -33,6 +33,53 @@
|
||||
#define IPS32_MAGIC "IPS32"
|
||||
#define IPS32_TAIL "EEOF"
|
||||
|
||||
#define NOGC_PATCH_DIR "default_nogc"
|
||||
static bool g_enable_nogc_patches = false;
|
||||
|
||||
void kip_patches_set_enable_nogc(void) {
|
||||
g_enable_nogc_patches = true;
|
||||
}
|
||||
|
||||
static bool should_ignore_default_patch(const char *patch_dir) {
|
||||
/* This function will ensure that select default patches only get loaded if enabled. */
|
||||
if (!g_enable_nogc_patches && strcmp(patch_dir, NOGC_PATCH_DIR) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool has_patch(const char *dir, const char *subdir, const void *hash, size_t hash_size) {
|
||||
char path[0x301] = {0};
|
||||
int cur_len = 0;
|
||||
cur_len += snprintf(path + cur_len, sizeof(path) - cur_len, "%s/", dir);
|
||||
if (subdir != NULL) {
|
||||
cur_len += snprintf(path + cur_len, sizeof(path) - cur_len, "%s/", subdir);
|
||||
}
|
||||
for (size_t i = 0; i < hash_size; i++) {
|
||||
cur_len += snprintf(path + cur_len, sizeof(path) - cur_len, "%02X", ((const uint8_t *)hash)[i]);
|
||||
}
|
||||
cur_len += snprintf(path + cur_len, sizeof(path) - cur_len, ".ips");
|
||||
if (cur_len >= sizeof(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (f != NULL) {
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool has_needed_default_kip_patches(uint64_t title_id, const void *hash, size_t hash_size) {
|
||||
if (title_id == 0x0100000000000000ULL && g_enable_nogc_patches) {
|
||||
return has_patch("atmosphere/kip_patches", NOGC_PATCH_DIR, hash, hash_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Applies an IPS/IPS32 patch to memory, disregarding writes to the first prot_size bytes. */
|
||||
static void apply_ips_patch(uint8_t *mem, size_t mem_size, size_t prot_size, bool is_ips32, FILE *f_ips) {
|
||||
uint8_t buffer[4];
|
||||
@@ -156,6 +203,11 @@ static bool has_ips_patches(const char *dir, const void *hash, size_t hash_size)
|
||||
if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (should_ignore_default_patch(pdir_ent->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path) - 1, "%s/%s", dir, pdir_ent->d_name);
|
||||
DIR *patch_dir = opendir(path);
|
||||
struct dirent *ent;
|
||||
@@ -165,6 +217,7 @@ static bool has_ips_patches(const char *dir, const void *hash, size_t hash_size)
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t name_len = strlen(ent->d_name);
|
||||
if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && name_matches_hash(ent->d_name, name_len, hash, hash_size)) {
|
||||
snprintf(path, sizeof(path) - 1, "%s/%s/%s", dir, pdir_ent->d_name, ent->d_name);
|
||||
@@ -200,6 +253,11 @@ static void apply_ips_patches(const char *dir, void *mem, size_t mem_size, size_
|
||||
if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (should_ignore_default_patch(pdir_ent->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path) - 1, "%s/%s", dir, pdir_ent->d_name);
|
||||
DIR *patch_dir = opendir(path);
|
||||
struct dirent *ent;
|
||||
@@ -209,6 +267,7 @@ static void apply_ips_patches(const char *dir, void *mem, size_t mem_size, size_
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t name_len = strlen(ent->d_name);
|
||||
if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && name_matches_hash(ent->d_name, name_len, hash, hash_size)) {
|
||||
snprintf(path, sizeof(path) - 1, "%s/%s/%s", dir, pdir_ent->d_name, ent->d_name);
|
||||
@@ -316,7 +375,11 @@ static kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
|
||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size) {
|
||||
uint8_t hash[0x20];
|
||||
se_calculate_sha256(hash, kip, kip_size);
|
||||
|
||||
|
||||
if (!has_needed_default_kip_patches(kip->title_id, hash, sizeof(hash))) {
|
||||
fatal_error("[NXBOOT]: Missing default patch for KIP %08x%08x...\n", (uint32_t)(kip->title_id >> 32), (uint32_t)kip->title_id);
|
||||
}
|
||||
|
||||
if (!has_ips_patches("atmosphere/kip_patches", hash, sizeof(hash))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -24,4 +24,6 @@
|
||||
void apply_kernel_ips_patches(void *kernel, size_t kernel_size);
|
||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size);
|
||||
|
||||
void kip_patches_set_enable_nogc(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -442,6 +442,11 @@ static const kernel_hook_t g_kernel_hooks_600[] = {
|
||||
|
||||
/* Kernel Infos. */
|
||||
static const kernel_info_t g_kernel_infos[] = {
|
||||
{ /* 1.0.0-7. */
|
||||
.hash = {0x64, 0x44, 0x07, 0x2F, 0x56, 0x44, 0x73, 0xDD, 0xD5, 0x46, 0x1B, 0x8C, 0xDC, 0xEF, 0x54, 0x98, 0x16, 0xDA, 0x81, 0xDE, 0x5B, 0x1C, 0x9D, 0xD7, 0x5A, 0x13, 0x91, 0xD9, 0x53, 0xAB, 0x8D, 0x8D},
|
||||
.free_code_space_offset = 0x4797C,
|
||||
KERNEL_HOOKS(100)
|
||||
},
|
||||
{ /* 1.0.0. */
|
||||
.hash = {0xB8, 0xC5, 0x0C, 0x68, 0x25, 0xA9, 0xB9, 0x5B, 0xD2, 0x4D, 0x2C, 0x7C, 0x81, 0x7F, 0xE6, 0x96, 0xF2, 0x42, 0x4E, 0x1D, 0x78, 0xDF, 0x3B, 0xCA, 0x3D, 0x6B, 0x68, 0x12, 0xDD, 0xA9, 0xCB, 0x9C},
|
||||
.free_code_space_offset = 0x4797C,
|
||||
|
||||
@@ -28,7 +28,10 @@
|
||||
#include "mc.h"
|
||||
#include "se.h"
|
||||
#include "pmc.h"
|
||||
#include "fuse.h"
|
||||
#include "i2c.h"
|
||||
#include "ips.h"
|
||||
#include "stratosphere.h"
|
||||
#include "max77620.h"
|
||||
#include "cluster.h"
|
||||
#include "flow.h"
|
||||
@@ -53,9 +56,43 @@
|
||||
|
||||
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;
|
||||
int tmp = 0;
|
||||
if (strcmp(section, "exosphere") == 0) {
|
||||
if (strcmp(name, EXOSPHERE_TARGETFW_KEY) == 0) {
|
||||
sscanf(value, "%d", &exo_cfg->target_firmware);
|
||||
}
|
||||
if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
|
||||
sscanf(value, "%d", &tmp);
|
||||
if (tmp) {
|
||||
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV;
|
||||
} else {
|
||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV);
|
||||
}
|
||||
}
|
||||
if (strcmp(name, EXOSPHERE_DEBUGMODE_USER_KEY) == 0) {
|
||||
sscanf(value, "%d", &tmp);
|
||||
if (tmp) {
|
||||
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER;
|
||||
} else {
|
||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_USER);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int stratosphere_ini_handler(void *user, const char *section, const char *name, const char *value) {
|
||||
stratosphere_cfg_t *strat_cfg = (stratosphere_cfg_t *)user;
|
||||
int tmp = 0;
|
||||
if (strcmp(section, "stratosphere") == 0) {
|
||||
if (strcmp(name, STRATOSPHERE_NOGC_KEY) == 0) {
|
||||
strat_cfg->has_nogc_config = true;
|
||||
sscanf(value, "%d", &tmp);
|
||||
strat_cfg->enable_nogc = tmp != 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@@ -114,6 +151,25 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
|
||||
*(MAILBOX_EXOSPHERE_CONFIGURATION) = exo_cfg;
|
||||
}
|
||||
|
||||
static void nxboot_configure_stratosphere(uint32_t target_firmware) {
|
||||
stratosphere_cfg_t strat_cfg = {0};
|
||||
if (ini_parse_string(get_loader_ctx()->bct0, stratosphere_ini_handler, &strat_cfg) < 0) {
|
||||
fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n");
|
||||
}
|
||||
|
||||
/* Enable NOGC patches if the user requested it, or if the user is booting into 4.0.0+ with 3.0.2- fuses. */
|
||||
if (strat_cfg.has_nogc_config) {
|
||||
if (strat_cfg.enable_nogc) {
|
||||
kip_patches_set_enable_nogc();
|
||||
}
|
||||
} else {
|
||||
/* Check if fuses are < 4.0.0, but firmware is >= 4.0.0 */
|
||||
if (target_firmware >= EXOSPHERE_TARGET_FIRMWARE_400 && !(fuse_get_reserved_odm(7) & ~0x0000000F)) {
|
||||
kip_patches_set_enable_nogc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nxboot_set_bootreason() {
|
||||
boot_reason_t boot_reason = {0};
|
||||
FILE *boot0;
|
||||
@@ -408,6 +464,9 @@ uint32_t nxboot_main(void) {
|
||||
}
|
||||
|
||||
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Rebuilding package2...\n");
|
||||
|
||||
/* Parse stratosphere config. */
|
||||
nxboot_configure_stratosphere(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
||||
|
||||
/* Patch package2, adding Thermosphère + custom KIPs. */
|
||||
package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "timers.h"
|
||||
#include "tsec.h"
|
||||
|
||||
#define TSEC_KEYGEN_MAX_RETRIES 25
|
||||
|
||||
void *smmu_heap = (void *)SMMU_HEAP_BASE_ADDR;
|
||||
|
||||
static void safe_memcpy(void *dst, void *src, uint32_t sz) {
|
||||
@@ -168,14 +170,6 @@ void smmu_emulate_tsec(void *tsec_keys, const void *package1, size_t package1_si
|
||||
/* Copy package1 into IRAM. */
|
||||
memcpy((void *)0x40010000, package1, package1_size);
|
||||
|
||||
/* Load the TSEC firmware from IRAM. */
|
||||
if (tsec_load_fw((void *)(0x40010000 + 0xE00), 0x2900) < 0) {
|
||||
fatal_error("[SMMU]: Failed to load TSEC firmware!\n");
|
||||
}
|
||||
|
||||
/* Disable the aperture since it has precedence over the SMMU. */
|
||||
mc_disable_ahb_redirect();
|
||||
|
||||
/* Setup TSEC's address space. */
|
||||
uint32_t *pdir = smmu_setup_tsec_as(1);
|
||||
|
||||
@@ -189,21 +183,6 @@ void smmu_emulate_tsec(void *tsec_keys, const void *package1, size_t package1_si
|
||||
volatile uint32_t *iram_pages = smmu_alloc_page(48);
|
||||
volatile uint32_t *expv_page = smmu_alloc_page(1);
|
||||
|
||||
/* Copy CAR, MC and FUSE. */
|
||||
safe_memcpy((void *)car_page, (void *)0x60006000, 0x1000);
|
||||
safe_memcpy((void *)mc_page, (void *)0x70019000, 0x1000);
|
||||
safe_memcpy((void *)&fuse_page[0x800/4], (void *)0x7000F800, 0x400);
|
||||
|
||||
/* Copy IRAM. */
|
||||
memcpy((void *)iram_pages, (void *)0x40010000, 0x30000);
|
||||
|
||||
/* TSEC wants CLK_RST_CONTROLLER_CLK_SOURCE_TSEC_0 to be equal to 2. */
|
||||
car_page[0x1F4/4] = 2;
|
||||
|
||||
/* TSEC wants the aperture fully open. */
|
||||
mc_page[0x65C/4] = 0;
|
||||
mc_page[0x660/4] = 0x80000000;
|
||||
|
||||
/* Map all necessary pages. */
|
||||
smmu_map(pdir, 0x60006000, (uint32_t)car_page, 1, _READABLE | _WRITABLE | _NONSECURE);
|
||||
smmu_map(pdir, 0x7000F000, (uint32_t)fuse_page, 1, _READABLE | _NONSECURE);
|
||||
@@ -217,19 +196,69 @@ void smmu_emulate_tsec(void *tsec_keys, const void *package1, size_t package1_si
|
||||
/* Enable the SMMU. */
|
||||
smmu_enable();
|
||||
|
||||
/* Run the TSEC firmware. */
|
||||
tsec_run_fw();
|
||||
|
||||
/* Extract the keys from SE. */
|
||||
/* Loop retrying TSEC firmware execution, in case we lose the SE keydata race. */
|
||||
uint32_t key_buf[0x20/4] = {0};
|
||||
volatile uint32_t *key_data = (volatile uint32_t *)((void *)se_page + 0x320);
|
||||
uint32_t old_key_data = *key_data;
|
||||
uint32_t buf_counter = 0;
|
||||
while (!(tsec->FALCON_CPUCTL & 0x10)) {
|
||||
if (*key_data != old_key_data) {
|
||||
old_key_data = *key_data;
|
||||
key_buf[buf_counter] = *key_data;
|
||||
buf_counter++;
|
||||
unsigned int retries = 0;
|
||||
while (true) {
|
||||
if (retries++ > TSEC_KEYGEN_MAX_RETRIES) {
|
||||
fatal_error("[SMMU] TSEC key generation race was lost too many times!");
|
||||
}
|
||||
|
||||
/* Load the TSEC firmware from IRAM. */
|
||||
if (tsec_load_fw((void *)(0x40010000 + 0xE00), 0x2900) < 0) {
|
||||
fatal_error("[SMMU]: Failed to load TSEC firmware!\n");
|
||||
}
|
||||
|
||||
/* Disable the aperture since it has precedence over the SMMU. */
|
||||
mc_disable_ahb_redirect();
|
||||
|
||||
/* Clear all pages. */
|
||||
memset((void *)car_page, 0, SMMU_PAGE_SIZE);
|
||||
memset((void *)fuse_page, 0, SMMU_PAGE_SIZE);
|
||||
memset((void *)pmc_page, 0, SMMU_PAGE_SIZE);
|
||||
memset((void *)flow_page, 0, SMMU_PAGE_SIZE);
|
||||
memset((void *)se_page, 0, SMMU_PAGE_SIZE);
|
||||
memset((void *)mc_page, 0, SMMU_PAGE_SIZE);
|
||||
memset((void *)iram_pages, 0, 48 * SMMU_PAGE_SIZE);
|
||||
memset((void *)expv_page, 0, SMMU_PAGE_SIZE);
|
||||
|
||||
/* Copy CAR, MC and FUSE. */
|
||||
safe_memcpy((void *)car_page, (void *)0x60006000, 0x1000);
|
||||
safe_memcpy((void *)mc_page, (void *)0x70019000, 0x1000);
|
||||
safe_memcpy((void *)&fuse_page[0x800/4], (void *)0x7000F800, 0x400);
|
||||
|
||||
/* Copy IRAM. */
|
||||
memcpy((void *)iram_pages, (void *)0x40010000, 0x30000);
|
||||
|
||||
/* TSEC wants CLK_RST_CONTROLLER_CLK_SOURCE_TSEC_0 to be equal to 2. */
|
||||
car_page[0x1F4/4] = 2;
|
||||
|
||||
/* TSEC wants the aperture fully open. */
|
||||
mc_page[0x65C/4] = 0;
|
||||
mc_page[0x660/4] = 0x80000000;
|
||||
|
||||
|
||||
/* Run the TSEC firmware. */
|
||||
tsec_run_fw();
|
||||
|
||||
/* Extract the keys from SE. */
|
||||
volatile uint32_t *key_data = (volatile uint32_t *)((void *)se_page + 0x320);
|
||||
uint32_t old_key_data = *key_data;
|
||||
uint32_t buf_counter = 0;
|
||||
while (!(tsec->FALCON_CPUCTL & 0x10)) {
|
||||
const uint32_t new_key_data = *key_data;
|
||||
if (new_key_data != old_key_data) {
|
||||
old_key_data = new_key_data;
|
||||
key_buf[buf_counter] = new_key_data;
|
||||
buf_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable back the aperture. */
|
||||
mc_enable_ahb_redirect();
|
||||
|
||||
if (buf_counter == 8) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,9 +277,6 @@ void smmu_emulate_tsec(void *tsec_keys, const void *package1, size_t package1_si
|
||||
/* Clear TSEC's address space. */
|
||||
smmu_clear_tsec_as(1);
|
||||
|
||||
/* Enable back the aperture. */
|
||||
mc_enable_ahb_redirect();
|
||||
|
||||
/* Return the decrypted package1 from emulated IRAM. */
|
||||
memcpy(package1_dec, (void *)iram_pages, package1_size);
|
||||
|
||||
|
||||
@@ -31,4 +31,11 @@ void stratosphere_free_ini1(void);
|
||||
|
||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis);
|
||||
|
||||
typedef struct {
|
||||
bool has_nogc_config;
|
||||
bool enable_nogc;
|
||||
} stratosphere_cfg_t;
|
||||
|
||||
#define STRATOSPHERE_NOGC_KEY "nogc"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -58,21 +58,12 @@ __attribute__((noreturn)) void pmc_reboot(uint32_t scratch0) {
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void car_reboot(void) {
|
||||
/* Reset the processor. */
|
||||
car_get_regs()->rst_dev_l |= 1<<2;
|
||||
|
||||
while (true) {
|
||||
/* Wait for reboot. */
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void wait_for_button_and_reboot(void) {
|
||||
uint32_t button;
|
||||
while (true) {
|
||||
button = btn_read();
|
||||
if (button & BTN_POWER) {
|
||||
car_reboot();
|
||||
pmc_reboot(1 << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,6 @@ void hexdump(const void* data, size_t size, uintptr_t addrbase);
|
||||
|
||||
__attribute__((noreturn)) void watchdog_reboot(void);
|
||||
__attribute__((noreturn)) void pmc_reboot(uint32_t scratch0);
|
||||
__attribute__((noreturn)) void car_reboot(void);
|
||||
__attribute__((noreturn)) void wait_for_button_and_reboot(void);
|
||||
void wait_for_button(void);
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"title_id_range_min": "0x0100000000000034",
|
||||
"title_id_range_max": "0x0100000000000034",
|
||||
"main_thread_stack_size": "0x00010000",
|
||||
"main_thread_priority": 37,
|
||||
"main_thread_priority": 15,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 0,
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 3,
|
||||
"address_space_type": 1,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
@@ -83,11 +83,12 @@
|
||||
"svcGetDebugThreadContext": "0x67",
|
||||
"svcQueryDebugProcessMemory": "0x69",
|
||||
"svcReadDebugProcessMemory": "0x6a",
|
||||
"svcGetDebugThreadParam": "0x6d"
|
||||
"svcGetDebugThreadParam": "0x6d",
|
||||
"svcCallSecureMonitor": "0x7f"
|
||||
}
|
||||
}, {
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0060"
|
||||
"value": "0x0030"
|
||||
}, {
|
||||
"type": "handle_table_size",
|
||||
"value": 128
|
||||
|
||||
@@ -58,9 +58,9 @@ static void SetupConfigLanguages() {
|
||||
config->error_desc = u8"Please call 1-800-875-1852 for service.\n";
|
||||
} else {
|
||||
config->error_desc = u8"An error has occured.\n\n"
|
||||
u8"Please press the POWER Button to restart the console. If you are\n"
|
||||
u8"unable to restart the console, hold the POWER Button for 12 seconds\n"
|
||||
u8"to turn the console off.\n\n"
|
||||
u8"Please press the POWER Button to restart the console, or a VOL button\n"
|
||||
u8"to restart the console in RCM mode. If you are unable to restart the\n"
|
||||
u8"console, hold the POWER Button for 12 seconds to turn the console off.\n\n"
|
||||
u8"If the problem persists, refer to the Nintendo Support Website.\n"
|
||||
u8"support.nintendo.com/switch/error\n";
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ void __appInit(void) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
rc = fsInitialize();
|
||||
rc = gpioInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
std::abort();
|
||||
}
|
||||
@@ -141,6 +141,7 @@ void __appExit(void) {
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
plExit();
|
||||
gpioExit();
|
||||
spsmExit();
|
||||
psmExit();
|
||||
lblExit();
|
||||
|
||||
@@ -52,7 +52,7 @@ static bool InRepairWithoutVolHeld() {
|
||||
|
||||
/* Sleep for 100 ms. */
|
||||
svcSleepThread(100000000UL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -94,14 +94,45 @@ void PowerButtonObserveTask::WaitForPowerButton() {
|
||||
|
||||
/* Force a reboot after some time if kiosk unit. */
|
||||
const FatalConfig *config = GetFatalConfig();
|
||||
TimeoutHelper reboot_helper(config->quest_reboot_interval_second * 1000000000UL);
|
||||
TimeoutHelper reboot_helper(config->quest_reboot_interval_second * 1000000000UL);
|
||||
|
||||
bool check_vol_up = true, check_vol_down = true;
|
||||
GpioPadSession vol_up_btn, vol_down_btn;
|
||||
if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) {
|
||||
check_vol_up = false;
|
||||
}
|
||||
if (R_FAILED(gpioOpenSession(&vol_down_btn, GpioPadName_ButtonVolDown))) {
|
||||
check_vol_down = false;
|
||||
}
|
||||
|
||||
/* Ensure we close on early return. */
|
||||
ON_SCOPE_EXIT { if (check_vol_up) { gpioPadClose(&vol_up_btn); } };
|
||||
ON_SCOPE_EXIT { if (check_vol_down) { gpioPadClose(&vol_down_btn); } };
|
||||
|
||||
/* Set direction input. */
|
||||
if (check_vol_up) {
|
||||
gpioPadSetDirection(&vol_up_btn, GpioDirection_Input);
|
||||
}
|
||||
if (check_vol_down) {
|
||||
gpioPadSetDirection(&vol_down_btn, GpioDirection_Input);
|
||||
}
|
||||
|
||||
BpcSleepButtonState state;
|
||||
GpioValue val;
|
||||
while (true) {
|
||||
Result rc = 0;
|
||||
|
||||
if (check_vol_up && R_SUCCEEDED((rc = gpioPadGetValue(&vol_up_btn, &val))) && val == GpioValue_Low) {
|
||||
/* Tell exosphere to reboot to RCM. */
|
||||
RebootToRcm();
|
||||
}
|
||||
|
||||
Result rc = bpcGetSleepButtonState(&state);
|
||||
if ((R_SUCCEEDED(rc) && state == BpcSleepButtonState_Held) || (config->quest_flag && reboot_helper.TimedOut())) {
|
||||
if (check_vol_down && R_SUCCEEDED((rc = gpioPadGetValue(&vol_down_btn, &val))) && val == GpioValue_Low) {
|
||||
/* Tell exosphere to reboot to RCM. */
|
||||
RebootToRcm();
|
||||
}
|
||||
|
||||
if ((R_SUCCEEDED(rc = bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held) || (config->quest_flag && reboot_helper.TimedOut())) {
|
||||
bpcRebootSystem();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,17 @@ Result ShowFatalTask::ShowFatal() {
|
||||
FontManager::AddSpacingLines(0.5f);
|
||||
FontManager::PrintFormatLine(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)", GetFatalConfig()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision());
|
||||
FontManager::AddSpacingLines(1.5f);
|
||||
FontManager::Print(config->error_desc);
|
||||
if (this->ctx->error_code != 0xCAFEF) {
|
||||
FontManager::Print(config->error_desc);
|
||||
} else {
|
||||
/* Print a special message for atmosphere version mismatch. */
|
||||
FontManager::Print(u8"Atmosphère version mismatch detected.\n\n"
|
||||
u8"Please press the POWER Button to restart the console, or a VOL button\n"
|
||||
u8"to restart the console in RCM mode. If you are unable to restart the\n"
|
||||
u8"console, hold the POWER Button for 12 seconds to turn the console off.\n\n"
|
||||
u8"Please ensure that all Atmosphère components are updated.\n"
|
||||
u8"github.com/Atmosphere-NX/Atmosphere/releases\n");
|
||||
}
|
||||
|
||||
/* Add a line. */
|
||||
for (size_t x = 32; x < FatalScreenWidth - 32; x++) {
|
||||
|
||||
@@ -88,7 +88,7 @@ Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) {
|
||||
if (R_FAILED((rc = ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset)))) {
|
||||
return rc;
|
||||
}
|
||||
size -= diff;
|
||||
size = diff;
|
||||
}
|
||||
|
||||
/* Read in the current BCT region. */
|
||||
|
||||
@@ -89,6 +89,12 @@ int main(int argc, char **argv)
|
||||
Thread hid_initializer_thread = {0};
|
||||
consoleDebugInit(debugDevice_SVC);
|
||||
|
||||
/* TODO: What's a good timeout value to use here? */
|
||||
auto server_manager = new FsMitmManager(5);
|
||||
|
||||
/* Create fsp-srv mitm. */
|
||||
AddMitmServerToManager<FsMitmService>(server_manager, "fsp-srv", 61);
|
||||
|
||||
if (R_FAILED(threadCreate(&sd_initializer_thread, &Utils::InitializeSdThreadFunc, NULL, 0x4000, 0x15, 0))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
@@ -102,12 +108,6 @@ int main(int argc, char **argv)
|
||||
if (R_FAILED(threadStart(&hid_initializer_thread))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
|
||||
/* TODO: What's a good timeout value to use here? */
|
||||
auto server_manager = new FsMitmManager(5);
|
||||
|
||||
/* Create fsp-srv mitm. */
|
||||
AddMitmServerToManager<FsMitmService>(server_manager, "fsp-srv", 61);
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
server_manager->Process();
|
||||
|
||||
@@ -69,7 +69,7 @@ void Utils::InitializeSdThreadFunc(void *args) {
|
||||
|
||||
/* Mount SD. */
|
||||
while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) {
|
||||
svcSleepThread(1000ULL);
|
||||
svcSleepThread(1000000ULL);
|
||||
}
|
||||
|
||||
/* Back up CAL0, if it's not backed up already. */
|
||||
|
||||
Submodule stratosphere/libstratosphere updated: 0bec72ca36...05015b9354
@@ -123,7 +123,22 @@ static void MountSdCard() {
|
||||
fsdevMountSdmc();
|
||||
}
|
||||
|
||||
void EmbeddedBoot2::Main() {
|
||||
void EmbeddedBoot2::Main() {
|
||||
/* Wait until fs.mitm has installed itself. We want this to happen as early as possible. */
|
||||
bool fs_mitm_installed = false;
|
||||
|
||||
Result rc = smManagerAmsInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
std::abort();
|
||||
}
|
||||
while (R_FAILED((rc = smManagerAmsHasMitm(&fs_mitm_installed, "fsp-srv"))) || !fs_mitm_installed) {
|
||||
if (R_FAILED(rc)) {
|
||||
std::abort();
|
||||
}
|
||||
svcSleepThread(1000ull);
|
||||
}
|
||||
smManagerAmsExit();
|
||||
|
||||
/* psc, bus, pcv is the minimal set of required titles to get SD card. */
|
||||
/* bus depends on pcie, and pcv depends on settings. */
|
||||
/* Launch psc. */
|
||||
|
||||
@@ -102,7 +102,6 @@ void __appInit(void) {
|
||||
rc = smManagerAmsInitialize();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
smManagerAmsEndInitialDefers();
|
||||
smManagerAmsExit();
|
||||
} else {
|
||||
fatalSimple(0xCAFE << 4 | 2);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 3,
|
||||
"address_space_type": 1,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
@@ -99,7 +99,7 @@
|
||||
},
|
||||
{
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0060"
|
||||
"value": "0x0030"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -31,3 +31,7 @@ void ManagerService::AtmosphereEndInitDefers() {
|
||||
Registration::EndInitDefers();
|
||||
}
|
||||
|
||||
void ManagerService::AtmosphereHasMitm(Out<bool> out, SmServiceName service) {
|
||||
out.SetValue(Registration::HasMitm(smEncodeName(service.name)));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "sm_types.hpp"
|
||||
|
||||
enum ManagerServiceCmd {
|
||||
Manager_Cmd_RegisterProcess = 0,
|
||||
@@ -24,6 +25,7 @@ enum ManagerServiceCmd {
|
||||
|
||||
|
||||
Manager_Cmd_AtmosphereEndInitDefers = 65000,
|
||||
Manager_Cmd_AtmosphereHasMitm = 65001,
|
||||
};
|
||||
|
||||
class ManagerService final : public IServiceObject {
|
||||
@@ -32,11 +34,13 @@ class ManagerService final : public IServiceObject {
|
||||
virtual Result RegisterProcess(u64 pid, InBuffer<u8> acid_sac, InBuffer<u8> aci0_sac);
|
||||
virtual Result UnregisterProcess(u64 pid);
|
||||
virtual void AtmosphereEndInitDefers();
|
||||
virtual void AtmosphereHasMitm(Out<bool> out, SmServiceName service);
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MakeServiceCommandMeta<Manager_Cmd_RegisterProcess, &ManagerService::RegisterProcess>(),
|
||||
MakeServiceCommandMeta<Manager_Cmd_UnregisterProcess, &ManagerService::UnregisterProcess>(),
|
||||
|
||||
MakeServiceCommandMeta<Manager_Cmd_AtmosphereEndInitDefers, &ManagerService::AtmosphereEndInitDefers>(),
|
||||
MakeServiceCommandMeta<Manager_Cmd_AtmosphereHasMitm, &ManagerService::AtmosphereHasMitm>(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -180,6 +180,9 @@ bool Registration::IsInitialProcess(u64 pid) {
|
||||
|
||||
u64 Registration::GetInitialProcessId() {
|
||||
CacheInitialProcessIdLimits();
|
||||
if (IsInitialProcess(1)) {
|
||||
return 1;
|
||||
}
|
||||
return g_initial_process_id_low;
|
||||
}
|
||||
|
||||
@@ -219,6 +222,11 @@ bool Registration::HasService(u64 service) {
|
||||
return std::any_of(g_service_list.begin(), g_service_list.end(), member_equals_fn(&Service::service_name, service));
|
||||
}
|
||||
|
||||
bool Registration::HasMitm(u64 service) {
|
||||
Registration::Service *target_service = GetService(service);
|
||||
return target_service != NULL && target_service->mitm_pid != 0;
|
||||
}
|
||||
|
||||
Result Registration::GetServiceHandle(u64 pid, u64 service, Handle *out) {
|
||||
Registration::Service *target_service = GetService(service);
|
||||
if (target_service == NULL || ShouldInitDefer(service) || target_service->mitm_waiting_ack) {
|
||||
|
||||
@@ -69,6 +69,7 @@ class Registration {
|
||||
|
||||
/* Service management. */
|
||||
static bool HasService(u64 service);
|
||||
static bool HasMitm(u64 service);
|
||||
static Result GetServiceHandle(u64 pid, u64 service, Handle *out);
|
||||
static Result GetServiceForPid(u64 pid, u64 service, Handle *out);
|
||||
static Result RegisterServiceForPid(u64 pid, u64 service, u64 max_sessions, bool is_light, Handle *out);
|
||||
|
||||
Reference in New Issue
Block a user