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