From d65c2ec94bea3b080bb15b6c1368842162711d7e Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sat, 15 Dec 2018 16:34:23 -0500 Subject: [PATCH] Added support for tmem with AppletStorage, and added HandleStorage support. applet IPC improvements. --- nx/include/switch/services/applet.h | 52 +++++- nx/source/services/applet.c | 256 ++++++++++++++++++++++++---- 2 files changed, 270 insertions(+), 38 deletions(-) diff --git a/nx/include/switch/services/applet.h b/nx/include/switch/services/applet.h index c6367a19..71d4605c 100644 --- a/nx/include/switch/services/applet.h +++ b/nx/include/switch/services/applet.h @@ -7,6 +7,7 @@ #pragma once #include "../types.h" #include "../services/sm.h" +#include "../kernel/tmem.h" typedef enum { AppletType_None = -2, @@ -64,6 +65,8 @@ struct AppletHookCookie /// applet IStorage typedef struct { Service s; + TransferMemory tmem; + bool isHandleStorage; } AppletStorage; Result appletInitialize(void); @@ -123,14 +126,41 @@ Result appletSetScreenShotImageOrientation(s32 val); */ Result appletCreateStorage(AppletStorage *s, s64 size); -/// Closes the storage object. +/** + * @brief Creates a TransferMemory storage. + * @param s Storage object. + * @param size Size of storage. + * @param writable Controls whether writing to the storage is allowed with \ref appletStorageWrite. + */ +Result appletCreateTransferMemoryStorage(AppletStorage *s, s64 size, bool writable); + +/** + * @brief Creates a HandleStorage. Only available on 2.0.0+. + * @param s Storage object. + * @param inval Arbitrary input value. + * @param handle Arbitrary input handle. + */ +Result appletCreateHandleStorage(AppletStorage *s, s64 inval, Handle handle); + +/** + * @brief Creates a HandleStorage using TransferMemory. Wrapper for \ref appletCreateHandleStorage. + * @param s Storage object. + * @param size Size of storage. + */ +Result appletCreateHandleStorageTmem(AppletStorage *s, s64 size); + +/// Closes the storage object. TransferMemory closing is seperate, see \ref appletStorageCloseTmem. void appletStorageClose(AppletStorage *s); -/// Gets the size of the storage. +/// Closes the TransferMemory in the storage object. For TransferMemory storage created by the current process, this must be called after the LibraryApplet finishes using it (if sent to one). +void appletStorageCloseTmem(AppletStorage *s); + +/// Gets the size of the storage. For HandleStorage, this returns the input s64 originally from /ref appletCreateHandleStorage / \ref appletCreateHandleStorageTmem. Result appletStorageGetSize(AppletStorage *s, s64 *size); /** * @brief Writes to a storage. offset(+size) must be within the actual storage size. + * @note This is not usable with HandleStorage. * @param s Storage object. * @param offset Offset in storage. * @param buffer Input data. @@ -140,6 +170,7 @@ Result appletStorageWrite(AppletStorage *s, s64 offset, const void* buffer, size /** * @brief Reads from a storage. offset(+size) must be within the actual storage size. + * @note This is not usable with HandleStorage. * @param s Storage object. * @param offset Offset in storage. * @param buffer Input data. @@ -147,6 +178,23 @@ Result appletStorageWrite(AppletStorage *s, s64 offset, const void* buffer, size */ Result appletStorageRead(AppletStorage *s, s64 offset, void* buffer, size_t size); +/** + * @brief Gets data for a HandleStorage originally from \ref appletCreateHandleStorage input. + * @note Only available on 2.0.0+. + * @param out Same as \ref appletStorageGetSize. + * @param handle Output handle. + */ +Result appletStorageGetHandle(AppletStorage *s, s64 *out, Handle *handle); + +/** + * @brief Maps TransferMemory for a HandleStorage. Wrapper for \ref appletCreateHandleStorage. + * @note The TransferMemory can be unmapped with \ref appletStorageCloseTmem. + * @param s Storage object. + * @param addr Output mapped address (optional). + * @param size Output size (optional). + */ +Result appletStorageMap(AppletStorage *s, void** addr, size_t *size); + /** * @brief Processes the current applet status. Generally used within a main loop. * @return Whether the application should continue running. diff --git a/nx/source/services/applet.c b/nx/source/services/applet.c index 8681e1c1..a62cce21 100644 --- a/nx/source/services/applet.c +++ b/nx/source/services/applet.c @@ -655,6 +655,50 @@ static Result _appletCmdNoInOut64(Service* srv, u64 *out, u64 cmd_id) { return rc; } +static Result _appletCmdInHandle64(Service* srv, Service* srv_out, u64 cmd_id, Handle handle, u64 inval) { + IpcCommand c; + ipcInitialize(&c); + + ipcSendHandleCopy(&c, handle); + + struct { + u64 magic; + u64 cmd_id; + u64 inval; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->inval = inval; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && srv_out) { + serviceCreateSubservice(srv_out, srv, &r, 0); + } + } + + return rc; +} + +static Result _appletCmdInTmem(Service* srv, Service* srv_out, u64 cmd_id, TransferMemory *tmem) { + return _appletCmdInHandle64(srv, srv_out, cmd_id, tmem->handle, tmem->size); +} + // IWindowController static Result _appletGetAppletResourceUserId(u64 *out) { @@ -809,45 +853,13 @@ Result appletIsGamePlayRecordingSupported(bool *flag) { } static Result _appletInitializeGamePlayRecording(TransferMemory *tmem) { - IpcCommand c; - ipcInitialize(&c); - if (!serviceIsActive(&g_appletSrv) || !_appletIsRegularApplication()) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (!kernelAbove300()) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); - ipcSendHandleCopy(&c, tmem->handle); - - struct { - u64 magic; - u64 cmd_id; - u64 size; - } *raw; - - raw = serviceIpcPrepareHeader(&g_appletIFunctions, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 66; - raw->size = tmem->size; - - Result rc = serviceIpcDispatch(&g_appletIFunctions); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - } *resp; - - serviceIpcParse(&g_appletIFunctions, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - } - - return rc; + return _appletCmdInTmem(&g_appletIFunctions, NULL, 66, tmem); } Result appletSetGamePlayRecordingState(bool state) { @@ -1340,13 +1352,101 @@ static Result _appletExitProcessAndReturn(void) { // ILibraryAppletCreator Result appletCreateStorage(AppletStorage *s, s64 size) { + memset(s, 0, sizeof(AppletStorage)); + s->isHandleStorage = false; + return _appletGetSessionIn64(&g_appletILibraryAppletCreator, &s->s, 10, size); } +static Result _appletCreateTransferMemoryStorage(Service* srv_out, TransferMemory *tmem, bool writable) { + IpcCommand c; + ipcInitialize(&c); + + ipcSendHandleCopy(&c, tmem->handle); + + struct { + u64 magic; + u64 cmd_id; + u8 writable; + u64 size; + } *raw; + + raw = serviceIpcPrepareHeader(&g_appletILibraryAppletCreator, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 11; + raw->writable = writable!=0; + raw->size = tmem->size; + + Result rc = serviceIpcDispatch(&g_appletILibraryAppletCreator); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_appletILibraryAppletCreator, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && srv_out) { + serviceCreateSubservice(srv_out, &g_appletILibraryAppletCreator, &r, 0); + } + } + + return rc; +} + +Result appletCreateTransferMemoryStorage(AppletStorage *s, s64 size, bool writable) { + Result rc=0; + + memset(s, 0, sizeof(AppletStorage)); + s->isHandleStorage = false; + + rc = tmemCreate(&s->tmem, size, Perm_None); + if (R_FAILED(rc)) return rc; + + rc = _appletCreateTransferMemoryStorage(&s->s, &s->tmem, writable); + if (R_FAILED(rc)) tmemClose(&s->tmem); + + return rc; +} + +Result appletCreateHandleStorage(AppletStorage *s, s64 inval, Handle handle) { + if (!kernelAbove200()) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + s->isHandleStorage = true; + + return _appletCmdInHandle64(&g_appletILibraryAppletCreator, &s->s, 12, handle, inval); +} + +Result appletCreateHandleStorageTmem(AppletStorage *s, s64 size) { + Result rc=0; + + memset(s, 0, sizeof(AppletStorage)); + s->isHandleStorage = true; + + rc = tmemCreate(&s->tmem, size, Perm_None); + if (R_FAILED(rc)) return rc; + + rc = appletCreateHandleStorage(s, s->tmem.size, s->tmem.handle); + if (R_FAILED(rc)) tmemClose(&s->tmem); + + return rc; +} + void appletStorageClose(AppletStorage *s) { serviceClose(&s->s); } +void appletStorageCloseTmem(AppletStorage *s) { + tmemClose(&s->tmem); +} + static Result _appletStorageAccessorRW(Service* srv, size_t ipcbufsize, s64 offset, void* buffer, size_t size, bool rw) { IpcCommand c; ipcInitialize(&c); @@ -1386,12 +1486,19 @@ static Result _appletStorageAccessorRW(Service* srv, size_t ipcbufsize, s64 offs Result appletStorageGetSize(AppletStorage *s, s64 *size) { Result rc=0; - Service tmp_srv;//IStorageAccessor + Service tmp_srv;//IStorageAccessor / ITransferStorageAccessor if (!serviceIsActive(&s->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); - rc = _appletGetSession(&s->s, &tmp_srv, 0);//Open + if (!s->isHandleStorage) rc = _appletGetSession(&s->s, &tmp_srv, 0);//Open + if (s->isHandleStorage) { + if (!kernelAbove200()) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + rc = _appletGetSession(&s->s, &tmp_srv, 1);//OpenTransferStorage + } + if (R_FAILED(rc)) return rc; rc = _appletCmdNoInOut64(&tmp_srv, (u64*)size, 0); @@ -1405,7 +1512,7 @@ static Result _appletStorageRW(AppletStorage *s, s64 offset, void* buffer, size_ size_t ipcbufsize=0; Service tmp_srv;//IStorageAccessor - if (!serviceIsActive(&s->s)) + if (!serviceIsActive(&s->s) || s->isHandleStorage) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); rc = _appletGetSession(&s->s, &tmp_srv, 0);//Open @@ -1427,6 +1534,83 @@ Result appletStorageRead(AppletStorage *s, s64 offset, void* buffer, size_t size return _appletStorageRW(s, offset, buffer, size, false); } +static Result _appletStorageGetHandle(Service* srv, s64 *out, Handle *handle) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + s64 out; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + if (out) *out = resp->out; + if (handle) *handle = r.Handles[0]; + } + } + + return rc; +} + +Result appletStorageGetHandle(AppletStorage *s, s64 *out, Handle *handle) { + Result rc=0; + Service tmp_srv;//ITransferStorageAccessor + + if (!serviceIsActive(&s->s) || !s->isHandleStorage) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (!kernelAbove200()) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + rc = _appletGetSession(&s->s, &tmp_srv, 1);//OpenTransferStorage + if (R_FAILED(rc)) return rc; + + rc = _appletStorageGetHandle(&tmp_srv, out, handle); + serviceClose(&tmp_srv); + + return rc; +} + +Result appletStorageMap(AppletStorage *s, void** addr, size_t *size) { + Result rc=0; + s64 tmpsize=0; + Handle tmphandle=0; + + rc = appletStorageGetHandle(s, &tmpsize, &tmphandle); + if (R_FAILED(rc)) return rc; + + tmemLoadRemote(&s->tmem, tmphandle, tmpsize, Perm_None); + rc = tmemMap(&s->tmem); + if (R_FAILED(rc)) tmemClose(&s->tmem); + + if (R_SUCCEEDED(rc)) { + if (addr) *addr = s->tmem.map_addr; + if (size) *size = s->tmem.size; + } + + return rc; +} + // state / other u8 appletGetOperationMode(void) {