diff --git a/troposphere/reboot_to_payload/source/ams_bpc.c b/troposphere/reboot_to_payload/source/ams_bpc.c
new file mode 100644
index 000000000..7b434c9ea
--- /dev/null
+++ b/troposphere/reboot_to_payload/source/ams_bpc.c
@@ -0,0 +1,45 @@
+/*
+ * 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 .
+ */
+#include
+#include
+#include "ams_bpc.h"
+#include "service_guard.h"
+
+static Service g_amsBpcSrv;
+
+NX_GENERATE_SERVICE_GUARD(amsBpc);
+
+Result _amsBpcInitialize(void) {
+ Handle h;
+ Result rc = svcConnectToNamedPort(&h, "bpc:ams"); /* TODO: ams:bpc */
+ if (R_SUCCEEDED(rc)) serviceCreate(&g_amsBpcSrv, h);
+ return rc;
+}
+
+void _amsBpcCleanup(void) {
+ serviceClose(&g_amsBpcSrv);
+}
+
+Service *amsBpcGetServiceSession(void) {
+ return &g_amsBpcSrv;
+}
+
+Result amsBpcSetRebootPayload(const void *src, size_t src_size) {
+ return serviceDispatch(&g_amsBpcSrv, 65001,
+ .buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
+ .buffers = { { src, src_size } },
+ );
+}
diff --git a/troposphere/reboot_to_payload/source/ams_bpc.h b/troposphere/reboot_to_payload/source/ams_bpc.h
new file mode 100644
index 000000000..945b929d9
--- /dev/null
+++ b/troposphere/reboot_to_payload/source/ams_bpc.h
@@ -0,0 +1,31 @@
+/*
+ * 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 .
+ */
+#pragma once
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Result amsBpcInitialize();
+void amsBpcExit();
+Service *amsBpcGetServiceSession(void);
+
+Result amsBpcSetRebootPayload(const void *src, size_t src_size);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/troposphere/reboot_to_payload/source/main.c b/troposphere/reboot_to_payload/source/main.c
index c111c7fd0..ebae553ea 100644
--- a/troposphere/reboot_to_payload/source/main.c
+++ b/troposphere/reboot_to_payload/source/main.c
@@ -3,51 +3,26 @@
#include
#include
+#include "ams_bpc.h"
#define IRAM_PAYLOAD_MAX_SIZE 0x24000
-#define IRAM_PAYLOAD_BASE 0x40010000
+static u8 g_reboot_payload[IRAM_PAYLOAD_MAX_SIZE];
-static alignas(0x1000) u8 g_reboot_payload[IRAM_PAYLOAD_MAX_SIZE];
-static alignas(0x1000) u8 g_ff_page[0x1000];
-static alignas(0x1000) u8 g_work_page[0x1000];
-
-void do_iram_dram_copy(void *buf, uintptr_t iram_addr, size_t size, int option) {
- memcpy(g_work_page, buf, size);
-
- SecmonArgs args = {0};
- args.X[0] = 0xF0000201; /* smcAmsIramCopy */
- args.X[1] = (uintptr_t)g_work_page; /* DRAM Address */
- args.X[2] = iram_addr; /* IRAM Address */
- args.X[3] = size; /* Copy size */
- args.X[4] = option; /* 0 = Read, 1 = Write */
- svcCallSecureMonitor(&args);
-
- memcpy(buf, g_work_page, size);
-}
-
-void copy_to_iram(uintptr_t iram_addr, void *buf, size_t size) {
- do_iram_dram_copy(buf, iram_addr, size, 1);
-}
-
-void copy_from_iram(void *buf, uintptr_t iram_addr, size_t size) {
- do_iram_dram_copy(buf, iram_addr, size, 0);
-}
-
-static void clear_iram(void) {
- memset(g_ff_page, 0xFF, sizeof(g_ff_page));
- for (size_t i = 0; i < IRAM_PAYLOAD_MAX_SIZE; i += sizeof(g_ff_page)) {
- copy_to_iram(IRAM_PAYLOAD_BASE + i, g_ff_page, sizeof(g_ff_page));
- }
+void userAppExit(void)
+{
+ amsBpcExit();
+ setsysExit();
+ spsmExit();
}
static void reboot_to_payload(void) {
- clear_iram();
-
- for (size_t i = 0; i < IRAM_PAYLOAD_MAX_SIZE; i += 0x1000) {
- copy_to_iram(IRAM_PAYLOAD_BASE + i, &g_reboot_payload[i], 0x1000);
+ Result rc = amsBpcSetRebootPayload(g_reboot_payload, IRAM_PAYLOAD_MAX_SIZE);
+ if (R_FAILED(rc)) {
+ printf("Failed to set reboot payload: 0x%x\n", rc);
+ }
+ else {
+ spsmShutdown(true);
}
-
- splSetConfig((SplConfigItem)65001, 2);
}
int main(int argc, char **argv)
@@ -59,12 +34,36 @@ int main(int argc, char **argv)
PadState pad;
padInitializeAny(&pad);
+ Result rc = 0;
bool can_reboot = true;
- Result rc = splInitialize();
- if (R_FAILED(rc)) {
- printf("Failed to initialize spl: 0x%x\n", rc);
+
+ if (R_FAILED(rc = setsysInitialize())) {
+ printf("Failed to initialize set:sys: 0x%x\n", rc);
can_reboot = false;
- } else {
+ }
+ else {
+ SetSysProductModel model;
+ setsysGetProductModel(&model);
+ if (model != SetSysProductModel_Nx && model != SetSysProductModel_Copper) {
+ printf("Reboot to payload cannot be used on a Mariko system\n");
+ can_reboot = false;
+ }
+ }
+
+ if (can_reboot && R_FAILED(rc = spsmInitialize())) {
+ printf("Failed to initialize spsm: 0x%x\n", rc);
+ can_reboot = false;
+ }
+
+ if (can_reboot) {
+ smExit(); //Required to connect to ams:bpc
+ if R_FAILED(rc = amsBpcInitialize()) {
+ printf("Failed to initialize ams:bpc: 0x%x\n", rc);
+ can_reboot = false;
+ }
+ }
+
+ if (can_reboot) {
FILE *f = fopen("sdmc:/atmosphere/reboot_payload.bin", "rb");
if (f == NULL) {
printf("Failed to open atmosphere/reboot_payload.bin!\n");
@@ -92,11 +91,6 @@ int main(int argc, char **argv)
consoleUpdate(NULL);
}
- if (can_reboot) {
- splExit();
- }
-
consoleExit(NULL);
return 0;
}
-
diff --git a/troposphere/reboot_to_payload/source/service_guard.h b/troposphere/reboot_to_payload/source/service_guard.h
new file mode 100644
index 000000000..5fbc5fca9
--- /dev/null
+++ b/troposphere/reboot_to_payload/source/service_guard.h
@@ -0,0 +1,56 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+typedef struct ServiceGuard {
+ Mutex mutex;
+ u32 refCount;
+} ServiceGuard;
+
+NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g)
+{
+ mutexLock(&g->mutex);
+ return (g->refCount++) == 0;
+}
+
+NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void))
+{
+ if (R_FAILED(rc)) {
+ cleanupFunc();
+ --g->refCount;
+ }
+ mutexUnlock(&g->mutex);
+ return rc;
+}
+
+NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
+{
+ mutexLock(&g->mutex);
+ if (g->refCount && (--g->refCount) == 0)
+ cleanupFunc();
+ mutexUnlock(&g->mutex);
+}
+
+#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
+\
+static ServiceGuard g_##name##Guard; \
+NX_INLINE Result _##name##Initialize _paramdecl; \
+static void _##name##Cleanup(void); \
+\
+Result name##Initialize _paramdecl \
+{ \
+ Result rc = 0; \
+ if (serviceGuardBeginInit(&g_##name##Guard)) \
+ rc = _##name##Initialize _parampass; \
+ return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
+} \
+\
+void name##Exit(void) \
+{ \
+ serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
+}
+
+#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
\ No newline at end of file