diff --git a/ios_mcp/source/imports.h b/ios_mcp/source/imports.h index 3baec66..80414a5 100644 --- a/ios_mcp/source/imports.h +++ b/ios_mcp/source/imports.h @@ -2,6 +2,7 @@ #include #include +#include #define LONG_CALL __attribute__((long_call)) @@ -14,6 +15,63 @@ typedef struct { uint32_t paddr; } IOSVec_t; +typedef struct __attribute__((__packed__)) { + uint32_t command; + int32_t result; + int32_t handle; + uint32_t flags; + uint32_t cpuId; + uint32_t processId; + uint64_t titleId; + uint32_t groupId; + + union { + uint32_t args[5]; + + struct __attribute__((__packed__)) { + const char* device; + uint32_t nameLen; + uint32_t mode; + uint64_t caps; + } open; + + struct { + uint32_t unknown; + } close; + + struct { + void* data; + uint32_t length; + } read; + + struct { + const void* data; + uint32_t length; + } write; + + struct { + uint32_t offset; + uint32_t origin; + } seek; + + struct { + uint32_t request; + const void* inputData; + uint32_t inputLength; + void* outputData; + uint32_t outputLength; + } ioctl; + + struct { + uint32_t request; + uint32_t numVecIn; + uint32_t numVecOut; + IOSVec_t* vecs; + } ioctlv; + }; +} IOSIpcRequest_t; +static_assert(sizeof(IOSIpcRequest_t) == 0x38); + enum { UC_DATA_TYPE_U8 = 1, UC_DATA_TYPE_U16 = 2, diff --git a/ios_mcp/source/mcp_install.c b/ios_mcp/source/mcp_install.c index e3a0c8a..e2004be 100644 --- a/ios_mcp/source/mcp_install.c +++ b/ios_mcp/source/mcp_install.c @@ -82,20 +82,24 @@ int MCP_InstallTitle(int handle, const char* path) return res; } -int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue, void* msg) +int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue) { - uint8_t* buf = allocIoBuf(0x27f + sizeof(IOSVec_t)); + uint8_t* buf = allocIoBuf(0x27f + sizeof(IOSVec_t) + sizeof(MCPAsyncReply)); - char* path_buf = (char*) buf + sizeof(IOSVec_t); + char* path_buf = (char*) buf + sizeof(IOSVec_t) + sizeof(MCPAsyncReply); strncpy(path_buf, path, 0x27f); IOSVec_t* vecs = (IOSVec_t*) buf; vecs[0].ptr = path_buf; vecs[0].len = 0x27f; - int res = IOS_IoctlvAsync(handle, 0x81, 1, 0, vecs, callbackQueue, msg); + MCPAsyncReply* reply = (MCPAsyncReply*) (buf + sizeof(IOSVec_t)); + reply->ioBuf = buf; - freeIoBuf(buf); + int res = IOS_IoctlvAsync(handle, 0x81, 1, 0, vecs, callbackQueue, &reply->reply); + if (res < 0) { + freeIoBuf(buf); + } return res; } diff --git a/ios_mcp/source/mcp_install.h b/ios_mcp/source/mcp_install.h index ae65950..55a812e 100644 --- a/ios_mcp/source/mcp_install.h +++ b/ios_mcp/source/mcp_install.h @@ -1,5 +1,6 @@ #pragma once +#include "imports.h" #include typedef struct __attribute__((packed)) { @@ -16,6 +17,11 @@ typedef struct __attribute__((packed)) { uint32_t contentsProgress; } MCPInstallProgress; +typedef struct __attribute__((packed)) { + IOSIpcRequest_t reply; + void* ioBuf; +} MCPAsyncReply; + int MCP_InstallGetInfo(int handle, const char* path, MCPInstallInfo* out_info); int MCP_InstallSetTargetUsb(int handle, int target); @@ -24,6 +30,6 @@ int MCP_InstallSetTargetDevice(int handle, int device); int MCP_InstallTitle(int handle, const char* path); -int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue, void* msg); +int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue); int MCP_InstallGetProgress(int handle, MCPInstallProgress* progress); diff --git a/ios_mcp/source/options/InstallWUP.c b/ios_mcp/source/options/InstallWUP.c index 300af73..021b56c 100644 --- a/ios_mcp/source/options/InstallWUP.c +++ b/ios_mcp/source/options/InstallWUP.c @@ -5,16 +5,35 @@ #include "mcp_install.h" #include "imports.h" #include "utils.h" +#include + +static int callbackQueue = -1; +static uint8_t callbackThreadStack[0x200] __attribute__((aligned(0x20))); +static int asyncPending = 0; +static int asyncResult = -1; + +static int callbackThread(void* arg) +{ + MCPAsyncReply* reply = NULL; + int ret = IOS_ReceiveMessage(callbackQueue, (uint32_t*)&reply, IOS_MESSAGE_FLAGS_NONE); + if (ret >= 0 && reply) { + asyncResult = reply->reply.result; + asyncPending = 0; + + // Free original request + IOS_HeapFree(CROSS_PROCESS_HEAP_ID, reply->ioBuf); + } + + return 0; +} void option_InstallWUP(void) { gfx_clear(COLOR_BACKGROUND); - drawTopBar("Installing WUP"); + drawTopBar("Install WUP"); setNotificationLED(NOTIF_LED_RED_BLINKING, 0); uint32_t index = 16 + 8 + 2 + 8; - gfx_print(16, index, 0, "Make sure to place a valid signed WUP directly in 'sd:/install'"); - index += CHAR_SIZE_DRC_Y + 4; int mcpHandle = IOS_Open("/dev/mcp", 0); if (mcpHandle < 0) { @@ -29,11 +48,33 @@ void option_InstallWUP(void) int res = MCP_InstallGetInfo(mcpHandle, "/vol/storage_recovsd/install", &info); if (res < 0) { IOS_Close(mcpHandle); + gfx_print(16, index, 0, "Make sure to place a valid signed WUP directly in 'sd:/install'"); + index += CHAR_SIZE_DRC_Y + 4; printf_error(index, "Failed to get install info: %x", res); return; } - gfx_printf(16, index, 0, "Installing title: %08lx-%08lx...", + index = gfx_printf(16, index, 0, "Found title: %08lx-%08lx\nDo you want to install this title?", + (uint32_t)(info.titleId >> 32), (uint32_t)(info.titleId & 0xFFFFFFFFU)); + index += CHAR_SIZE_DRC_Y + 4; + + static const Menu installWupOptions[] = { + {"Cancel", {0} }, + {"Install", {0} }, + }; + int selected = drawMenu("Install WUP", + installWupOptions, ARRAY_SIZE(installWupOptions), 0, + MenuFlag_NoClearScreen, 16, index); + if (selected == 0) { + IOS_Close(mcpHandle); + return; + } + + gfx_clear(COLOR_BACKGROUND); + drawTopBar("Install WUP"); + index = 16 + 8 + 2 + 8; + + gfx_printf(16, index, 0, "Starting install for title: %08lx-%08lx", (uint32_t)(info.titleId >> 32), (uint32_t)(info.titleId & 0xFFFFFFFFU)); index += CHAR_SIZE_DRC_Y + 4; @@ -51,18 +92,65 @@ void option_InstallWUP(void) return; } - // TODO: async installations - res = MCP_InstallTitle(mcpHandle, "/vol/storage_recovsd/install"); + uint32_t messages[5]; + callbackQueue = IOS_CreateMessageQueue(messages, sizeof(messages) / sizeof(uint32_t)); + if (callbackQueue < 0) { + IOS_Close(mcpHandle); + printf_error(index, "IOS_CreateMessageQueue: %x", res); + return; + } + + int callbackThreadId = IOS_CreateThread(callbackThread, NULL, callbackThreadStack + sizeof(callbackThreadStack), sizeof(callbackThreadStack), IOS_GetThreadPriority(0), IOS_THREAD_FLAGS_NONE); + if (callbackThreadId < 0) { + IOS_DestroyMessageQueue(callbackQueue); + IOS_Close(mcpHandle); + printf_error(index, "IOS_CreateThread: %x", res); + return; + } + + if (IOS_StartThread(callbackThreadId) < 0) { + IOS_DestroyMessageQueue(callbackQueue); + IOS_Close(mcpHandle); + printf_error(index, "IOS_StartThread failed"); + return; + } + + asyncResult = -1; + asyncPending = 1; + res = MCP_InstallTitleAsync(mcpHandle, "/vol/storage_recovsd/install", callbackQueue); if (res < 0) { + // Send a NULL message and wait for the thread to stop + IOS_SendMessage(callbackQueue, 0, IOS_MESSAGE_FLAGS_NONE); + IOS_JoinThread(callbackThreadId, NULL); + + IOS_DestroyMessageQueue(callbackQueue); IOS_Close(mcpHandle); - printf_error(index, "Failed to install: %x", res); + printf_error(index, "Failed to start install: %x", res); return; } - setNotificationLED(NOTIF_LED_PURPLE, 0); - gfx_set_font_color(COLOR_SUCCESS); - gfx_print(16, index, 0, "Done!"); - waitButtonInput(); + while (asyncPending) { + MCPInstallProgress progress; + res = MCP_InstallGetProgress(mcpHandle, &progress); + if (res >= 0) { + gfx_printf(16, index, GfxPrintFlag_ClearBG, "Installing... (%lu KiB / %lu KiB)", (uint32_t) (progress.sizeProgress / 1024llu), (uint32_t) (progress.sizeTotal / 1024llu)); + } + + usleep(50 * 1000); + } + index += CHAR_SIZE_DRC_Y + 4; + + if (asyncResult < 0) { + printf_error(index, "Failed to install: %x", asyncResult); + } else { + setNotificationLED(NOTIF_LED_PURPLE, 0); + gfx_set_font_color(COLOR_SUCCESS); + gfx_printf(16, index, 0, "Done!"); + waitButtonInput(); + } + + IOS_JoinThread(callbackThreadId, NULL); + IOS_DestroyMessageQueue(callbackQueue); IOS_Close(mcpHandle); } diff --git a/ios_mcp/source/utils.c b/ios_mcp/source/utils.c index b972a9c..219412c 100644 --- a/ios_mcp/source/utils.c +++ b/ios_mcp/source/utils.c @@ -12,7 +12,7 @@ enum { }; static int asyncThreadHandle = -1; -static uint8_t asyncThreadStack[0x400] __attribute__((aligned(0x20))); +static uint8_t asyncThreadStack[0x200] __attribute__((aligned(0x20))); static uint32_t asyncMessageQueueBuf[0x20]; static int asyncMessageQueue = -1; static int ledTimer = -1;