diff --git a/nx/include/switch.h b/nx/include/switch.h index b6219077..cfebdb41 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -30,6 +30,7 @@ extern "C" { #include #include #include +#include #include #include diff --git a/nx/include/switch/ipc.h b/nx/include/switch/ipc.h index 0ddef2c3..545970e7 100644 --- a/nx/include/switch/ipc.h +++ b/nx/include/switch/ipc.h @@ -258,6 +258,40 @@ static inline Result ipcParseResponse(IpcCommandResponse* r) { return 0; } +static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) { + u32* buf = armGetTls(); + + buf[0] = 5; + buf[1] = 8; + buf[2] = 0; + buf[3] = 0; + buf[4] = SFCI_MAGIC; + buf[5] = 0; + buf[6] = 3; + buf[7] = 0; + + Result rc = ipcDispatch(session); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + u32 size; + } *raw = r.Raw; + + rc = raw->result; + + if (R_SUCCEEDED(rc)) { + *size = raw->size & 0xffff; + } + } + + return rc; +} + // Domain shit static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) { u32* buf = armGetTls(); diff --git a/nx/include/switch/services/binder.h b/nx/include/switch/services/binder.h index e4c849b3..cddbbb0e 100644 --- a/nx/include/switch/services/binder.h +++ b/nx/include/switch/services/binder.h @@ -6,6 +6,9 @@ typedef struct { s32 ID; Handle nativehandle; + + size_t IpcBufferSize; + bool TransactAuto; } binderSession; //binderExitSession will not close the sessionhandle since it's user-specified via binderCreateSession and may be used elsewhere. diff --git a/nx/include/switch/services/nv.h b/nx/include/switch/services/nv.h new file mode 100644 index 00000000..ac33c21b --- /dev/null +++ b/nx/include/switch/services/nv.h @@ -0,0 +1,49 @@ +//The below defines are from Linux kernel ioctl.h. + +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +/* + * Direction bits. + */ +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +/* used to decode ioctl numbers.. */ + +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +typedef enum { + NVSERVTYPE_Default = -1, + NVSERVTYPE_Application = 0, + NVSERVTYPE_Applet = 1, + NVSERVTYPE_Sysmodule = 2, + NVSERVTYPE_T = 3, +} nvServiceType; + +Result nvInitialize(nvServiceType servicetype, size_t sharedmem_size); +void nvExit(void); + +Result nvOpen(u32 *fd, const char *devicepath); +Result nvIoctl(u32 fd, u32 request, void* argp); +Result nvClose(u32 fd); + diff --git a/nx/source/gfx/gfx.c b/nx/source/gfx/gfx.c index e853040d..868722f3 100644 --- a/nx/source/gfx/gfx.c +++ b/nx/source/gfx/gfx.c @@ -11,6 +11,8 @@ static s32 g_gfxNativeWindow_ID; static binderSession g_gfxBinderSession; static s32 g_gfxCurrentBuffer = 0; +extern u32 __nx_applet_type; + static u32 g_gfxQueueBufferData[0x5c>>2] = { 0x54, 0x0, 0x0, 0x0, //u64 timestamp @@ -90,7 +92,7 @@ static Result _gfxQueueBuffer(s32 buf) { return rc; } -static Result _gfxInit(viServiceType servicetype, const char *DisplayName, u32 LayerFlags, u64 LayerId) { +static Result _gfxInit(viServiceType servicetype, const char *DisplayName, u32 LayerFlags, u64 LayerId, nvServiceType nv_servicetype, size_t nv_transfermem_size) { Result rc=0; s32 tmp=0; u32 i=0; @@ -120,6 +122,8 @@ static Result _gfxInit(viServiceType servicetype, const char *DisplayName, u32 L rc = binderInitSession(&g_gfxBinderSession, 0x0f); } + if (R_SUCCEEDED(rc)) rc = nvInitialize(nv_servicetype, nv_transfermem_size); + if (R_SUCCEEDED(rc)) rc = gfxproducerInitialize(&g_gfxBinderSession); if (R_SUCCEEDED(rc)) rc = gfxproducerConnect(2, 0); @@ -152,6 +156,8 @@ static Result _gfxInit(viServiceType servicetype, const char *DisplayName, u32 L if (R_SUCCEEDED(rc)) rc = _gfxDequeueBuffer(); if (R_FAILED(rc)) { + gfxproducerExit(); + nvExit(); binderExitSession(&g_gfxBinderSession); viCloseLayer(&g_gfxLayer); viCloseDisplay(&g_gfxDisplay); @@ -164,7 +170,24 @@ static Result _gfxInit(viServiceType servicetype, const char *DisplayName, u32 L } void gfxInitDefault(void) { - Result rc = _gfxInit(VILAYERFLAGS_Default, "Default", VILAYERFLAGS_Default, 0); + nvServiceType nv_servicetype = NVSERVTYPE_Default; + + if(__nx_applet_type != APPLET_TYPE_None) { + switch(__nx_applet_type) { + case APPLET_TYPE_Application: + case APPLET_TYPE_SystemApplication: + nv_servicetype = NVSERVTYPE_Application; + break; + + case APPLET_TYPE_SystemApplet: + case APPLET_TYPE_LibraryApplet: + case APPLET_TYPE_OverlayApplet: + nv_servicetype = NVSERVTYPE_Applet; + break; + } + } + + Result rc = _gfxInit(VILAYERFLAGS_Default, "Default", VILAYERFLAGS_Default, 0, nv_servicetype, 0x300000); if (R_FAILED(rc)) fatalSimple(rc); } @@ -173,6 +196,8 @@ void gfxExit(void) { gfxproducerExit(); + nvExit(); + binderExitSession(&g_gfxBinderSession); viCloseLayer(&g_gfxLayer); diff --git a/nx/source/services/binder.c b/nx/source/services/binder.c index 58110aed..b86cd1c7 100644 --- a/nx/source/services/binder.c +++ b/nx/source/services/binder.c @@ -5,6 +5,7 @@ void binderCreateSession(binderSession *session, Handle sessionhandle, s32 ID) { memset(session, 0, sizeof(binderSession)); session->sessionhandle = sessionhandle; session->ID = ID; + session->TransactAuto = 0; session->initialized = 1; } @@ -23,7 +24,12 @@ Result binderInitSession(binderSession *session, u32 nativehandle_inval) { //When the output nativehandle is 0 the binderSession ID is probably invalid. if(session->nativehandle == 0) return MAKERESULT(MODULE_LIBNX, LIBNX_BADINPUT); - return 0; + rc = ipcQueryPointerBufferSize(session->sessionhandle, &session->IpcBufferSize); + if (R_FAILED(rc)) return rc; + + if (kernelAbove300()) session->TransactAuto = 1;//Call this to check whether TransactParcelAuto is available, since sysmodule would close the session on invalid cmdid. + + return rc; } Result binderExitSession(binderSession *session) { @@ -84,10 +90,71 @@ static Result _binderTransactParcel(binderSession *session, u32 code, void* parc return rc; } -//TODO: Use TransactParcelAuto when it's available. +static Result _binderTransactParcelAuto(binderSession *session, u32 code, void* parcel_data, size_t parcel_data_size, void* parcel_reply, size_t parcel_reply_size, u32 flags) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + s32 ID; + u32 code; + u32 flags; + } *raw; + + void* buf_static[2] = {parcel_data, parcel_reply}; + void* buf_transfer[2] = {parcel_data, parcel_reply}; + size_t buf_static_size[2] = {parcel_data_size, parcel_reply_size}; + size_t buf_transfer_size[2] = {parcel_data_size, parcel_reply_size}; + + if(session->IpcBufferSize!=0 && buf_static_size[0] <= session->IpcBufferSize) { + buf_transfer[0] = NULL; + buf_transfer[1] = NULL; + buf_transfer_size[0] = 0; + buf_transfer_size[1] = 0; + } + else { + buf_static[0] = NULL; + buf_static[1] = NULL; + buf_static_size[0] = 0; + buf_static_size[1] = 0; + } + + ipcAddSendBuffer(&c, buf_transfer[0], buf_transfer_size[0], 0); + ipcAddRecvBuffer(&c, buf_transfer[1], buf_transfer_size[1], 0); + + ipcAddSendStatic(&c, buf_static[0], buf_static_size[0], 0); + ipcAddRecvStatic(&c, buf_static[1], buf_static_size[1], 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 3; + raw->ID = session->ID; + raw->code = code; + raw->flags = flags; + + Result rc = ipcDispatch(session->sessionhandle); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} Result binderTransactParcel(binderSession *session, u32 code, void* parcel_data, size_t parcel_data_size, void* parcel_reply, size_t parcel_reply_size, u32 flags) { - return _binderTransactParcel(session, code, parcel_data, parcel_data_size, parcel_reply, parcel_reply_size, flags); + Result rc = 0; + if (session->TransactAuto==0) rc = _binderTransactParcel(session, code, parcel_data, parcel_data_size, parcel_reply, parcel_reply_size, flags); + if (session->TransactAuto) rc = _binderTransactParcelAuto(session, code, parcel_data, parcel_data_size, parcel_reply, parcel_reply_size, flags); + return rc; } Result binderAdjustRefcount(binderSession *session, s32 addval, s32 type) { diff --git a/nx/source/services/nv.c b/nx/source/services/nv.c new file mode 100644 index 00000000..03a1619c --- /dev/null +++ b/nx/source/services/nv.c @@ -0,0 +1,273 @@ +#include +#include + +static Handle g_nvServiceSession = INVALID_HANDLE; +static size_t g_nvIpcBufferSize = 0; +static u32 g_nvServiceType = -1; +static TransferMemory g_nvTransfermem; + +static Result _nvInitialize(Handle proc, Handle sharedmem, u32 transfermem_size); +static Result _nvSetClientPID(u64 AppletResourceUserId); + +Result nvInitialize(nvServiceType servicetype, size_t transfermem_size) { + if(g_nvServiceType!=-1)return MAKERESULT(MODULE_LIBNX, LIBNX_ALREADYINITIALIZED); + + Result rc = 0; + u64 AppletResourceUserId = 0; + + if (servicetype==NVSERVTYPE_Default || servicetype==NVSERVTYPE_Application) { + rc = smGetService(&g_nvServiceSession, "nvdrv"); + g_nvServiceType = 0; + } + + if ((servicetype==NVSERVTYPE_Default && R_FAILED(rc)) || servicetype==NVSERVTYPE_Applet) { + rc = smGetService(&g_nvServiceSession, "nvdrv:a"); + g_nvServiceType = 1; + } + + if ((servicetype==NVSERVTYPE_Default && R_FAILED(rc)) || servicetype==NVSERVTYPE_Sysmodule) + { + rc = smGetService(&g_nvServiceSession, "nvdrv:s"); + g_nvServiceType = 2; + } + + if ((servicetype==NVSERVTYPE_Default && R_FAILED(rc)) || servicetype==NVSERVTYPE_T) + { + rc = smGetService(&g_nvServiceSession, "nvdrv:t"); + g_nvServiceType = 3; + } + + if (R_SUCCEEDED(rc)) { + g_nvIpcBufferSize = 0; + rc = ipcQueryPointerBufferSize(g_nvServiceSession, &g_nvIpcBufferSize); + + if (R_SUCCEEDED(rc)) rc = tmemCreate(&g_nvTransfermem, transfermem_size, PERM_NONE); + + if (R_SUCCEEDED(rc)) rc = _nvInitialize(CUR_PROCESS_HANDLE, g_nvTransfermem.MemHandle, transfermem_size); + + //Officially ipc control DuplicateSessionEx would be used here. + + if (R_SUCCEEDED(rc)) rc = appletGetAppletResourceUserId(&AppletResourceUserId);//TODO: How do sysmodules handle this? + + if (R_SUCCEEDED(rc)) rc = _nvSetClientPID(AppletResourceUserId); + } + + if (R_FAILED(rc)) { + g_nvServiceType = -1; + + if(g_nvServiceSession != INVALID_HANDLE) + { + svcCloseHandle(g_nvServiceSession); + g_nvServiceSession = INVALID_HANDLE; + } + + tmemClose(&g_nvTransfermem); + } + + return rc; +} + +void nvExit(void) +{ + if(g_nvServiceType==-1)return; + + g_nvServiceType = -1; + + if(g_nvServiceSession != INVALID_HANDLE) + { + svcCloseHandle(g_nvServiceSession); + g_nvServiceSession = INVALID_HANDLE; + } + + tmemClose(&g_nvTransfermem); +} + +static Result _nvInitialize(Handle proc, Handle sharedmem, u32 transfermem_size) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 transfermem_size; + } *raw; + + ipcSendHandleCopy(&c, proc); + ipcSendHandleCopy(&c, sharedmem); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 3; + raw->transfermem_size = transfermem_size; + + Result rc = ipcDispatch(g_nvServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _nvSetClientPID(u64 AppletResourceUserId) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 AppletResourceUserId; + } *raw; + + ipcSendPid(&c); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 8; + raw->AppletResourceUserId = AppletResourceUserId; + + Result rc = ipcDispatch(g_nvServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result nvOpen(u32 *fd, const char *devicepath) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + ipcAddSendBuffer(&c, devicepath, strlen(devicepath), 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = ipcDispatch(g_nvServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + u32 fd; + } *resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc)) *fd = resp->fd; + } + + return rc; +} + +Result nvIoctl(u32 fd, u32 request, void* argp) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 fd; + u32 request; + } *raw; + + size_t bufsize = _IOC_SIZE(request); + + void* buf_static = argp, *buf_transfer = argp; + size_t buf_static_size = bufsize, buf_transfer_size = bufsize; + + if(g_nvIpcBufferSize!=0 && bufsize <= g_nvIpcBufferSize) { + buf_transfer = NULL; + buf_transfer_size = 0; + } + else { + buf_static = NULL; + buf_static_size = 0; + } + + ipcAddSendBuffer(&c, buf_transfer, buf_transfer_size, 0); + ipcAddRecvBuffer(&c, buf_transfer, buf_transfer_size, 0); + + ipcAddSendStatic(&c, buf_static, buf_static_size, 0); + ipcAddRecvStatic(&c, buf_static, buf_static_size, 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + raw->fd = fd; + raw->request = request; + + Result rc = ipcDispatch(g_nvServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result nvClose(u32 fd) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 fd; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + raw->fd = fd; + + Result rc = ipcDispatch(g_nvServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} +