From 52a81e111664cb267bd5b646ece9cb90c1a6fa94 Mon Sep 17 00:00:00 2001 From: tihmstar Date: Thu, 14 Dec 2023 01:13:00 +0100 Subject: Add iOS 1 support --- include/libirecovery.h | 1 + src/libirecovery.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 198 insertions(+), 3 deletions(-) diff --git a/include/libirecovery.h b/include/libirecovery.h index 5c3ad80..c9c1960 100644 --- a/include/libirecovery.h +++ b/include/libirecovery.h @@ -154,6 +154,7 @@ IRECV_API irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int c IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface); IRECV_API int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout); IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); +IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); /* events */ typedef void(*irecv_device_event_cb_t)(const irecv_device_event_t* event, void *user_data); diff --git a/src/libirecovery.c b/src/libirecovery.c index f544fbd..dd07110 100644 --- a/src/libirecovery.c +++ b/src/libirecovery.c @@ -628,6 +628,24 @@ typedef struct { #pragma pack() #endif +#pragma pack(1) +typedef struct { + uint16_t cmdcode; +#define MSG_VERSION_QUERY 0x000 +#define MSG_ECHO 0x801 +#define MSG_DUMP_BUFFER 0x802 +#define MSG_SEND_COMMAND 0x803 +#define MSG_READ_FILE 0x804 +#define MSG_SEND_FILE 0x805 +#define MSG_CRC 0x807 +#define MSG_ACK 0x808 +#define MSG_REJECT 0x809 + uint16_t magic; // always 0x1234 + uint32_t size; + uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files +} legacyCMD; +#pragma pack() + static THREAD_T th_event_handler = THREAD_T_NULL; struct collection listeners; static mutex_t listener_mutex; @@ -777,6 +795,9 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons ptr = strstr(iboot_string, "CPID:"); if (ptr != NULL) { sscanf(ptr, "CPID:%x", &client->device_info.cpid); + } else { + // early iOS 1 doesn't identify itself + client->device_info.cpid = 0x8900; } ptr = strstr(iboot_string, "CPRV:"); @@ -1495,6 +1516,74 @@ static int iokit_usb_bulk_transfer(irecv_client_t client, return IRECV_E_USB_INTERFACE; } + +static int iokit_usb_interrupt_transfer(irecv_client_t client, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout) +{ + IOReturn result; + IOUSBInterfaceInterface300 **intf = client->usbInterface; + UInt32 size = length; + UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0; + UInt8 numEndpoints; + + if (!intf) return IRECV_E_USB_INTERFACE; + + result = (*intf)->GetNumEndpoints(intf, &numEndpoints); + + if (result != kIOReturnSuccess) + return IRECV_E_USB_INTERFACE; + + for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) { + UInt8 direction = 0; + UInt8 number = 0; + UInt8 transferType = 0; + UInt16 maxPacketSize = 0; + UInt8 interval = 0; + + result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval); + if (result != kIOReturnSuccess) + continue; + + if (direction == 3) + direction = isUSBIn; + + if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn) + continue; + + // Just because + result = (*intf)->GetPipeStatus(intf, pipeRef); + switch (result) { + case kIOReturnSuccess: break; + case kIOReturnNoDevice: return IRECV_E_NO_DEVICE; + case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT; + default: return IRECV_E_USB_STATUS; + } + + // Do the transfer + if (isUSBIn) { + result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout); + if (result != kIOReturnSuccess) + return IRECV_E_PIPE; + *transferred = size; + + return IRECV_E_SUCCESS; + } + else { + result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout); + if (result != kIOReturnSuccess) + return IRECV_E_PIPE; + *transferred = size; + + return IRECV_E_SUCCESS; + } + } + + return IRECV_E_USB_INTERFACE; +} #endif #endif @@ -1532,6 +1621,36 @@ int irecv_usb_bulk_transfer(irecv_client_t client, #endif } +IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout) +{ +#ifdef USE_DUMMY + return IRECV_E_UNSUPPORTED; +#else + int ret; + +#ifndef _WIN32 +#ifdef HAVE_IOKIT + return iokit_usb_interrupt_transfer(client, endpoint, data, length, transferred, timeout); +#else + ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout); + if (ret < 0) { + libusb_clear_halt(client->handle, endpoint); + } +#endif +#else + // win32 + return IRECV_E_UNSUPPORTED; +#endif + + return ret; +#endif +} + #ifndef USE_DUMMY #ifdef HAVE_IOKIT static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service) @@ -3112,6 +3231,35 @@ static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* c return IRECV_E_INVALID_INPUT; } + if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) { + int bytes = 0; + irecv_error_t error = 0; +#ifdef DEBUG + uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be + if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error; + if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR; +#endif + char cmdstr[0x100] = {}; + if (length & 0xf) { + length &= ~0xf; + length += 0x10; + } + snprintf(cmdstr, sizeof(cmdstr), "%s\n",command); + legacyCMD cmd = { + MSG_SEND_COMMAND, + 0x1234, //magic + (uint32_t)length, //zero terminated? + 0x0 + }; + if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR; + if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error; + sleep(1); //go easy on this old device + return IRECV_E_SUCCESS; + } + if (length > 0) { irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT); } @@ -3323,6 +3471,18 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un irecv_error_t error = 0; int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)); + int legacy_recovery_mode = 0; + + if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) { +#ifdef DEBUG + uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be + int bytes = 0; + if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error; + if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR; +#endif + legacy_recovery_mode = 1; + } if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; @@ -3331,6 +3491,7 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10}; int dfu_crc = 1; int packet_size = recovery_mode ? 0x8000 : 0x800; + if (legacy_recovery_mode) packet_size = 0x200; if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) { packet_size = 0x40; dfu_crc = 0; @@ -3345,7 +3506,24 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un } /* initiate transfer */ - if (recovery_mode) { + if (legacy_recovery_mode) { + int bytes = 0; + uint32_t loadaddr0x8900 = 0x09000000; + const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR"); + if (ios1_overwrite_loadaddr) { + sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900); + debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900); + } + legacyCMD cmd = { + MSG_SEND_FILE, + 0x1234, //magic + (uint32_t)length, + loadaddr0x8900 + }; + if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR; + } else if (recovery_mode) { error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT); } else { uint8_t state = 0; @@ -3382,10 +3560,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un for (i = 0; i < packets; i++) { int size = (i + 1) < packets ? packet_size : last; - /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ - if (recovery_mode) { + if (legacy_recovery_mode) { + // Use interrupt transfer for legacy devices + error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); + } else if (recovery_mode) { + // Use bulk transfer for recovery mode error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); } else { + // Use control transfer for DFU and WTF mode if (dfu_crc) { int j; for (j = 0; j < size; j++) { @@ -3493,6 +3675,13 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un irecv_reset(client); } + if (legacy_recovery_mode) { + irecv_reconnect(client, 0); + char cmdstr[0x100] = {}; + snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length); + irecv_send_command(client, cmdstr); + } + return IRECV_E_SUCCESS; #endif } @@ -3548,6 +3737,11 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v return IRECV_E_INVALID_INPUT; } + if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) { + debug("iOS 1 doesn't support getenv\n"); + return IRECV_E_UNSUPPORTED; + } + memset(command, '\0', sizeof(command)); snprintf(command, sizeof(command)-1, "getenv %s", variable); irecv_error_t error = irecv_send_command_raw(client, command, 0); -- cgit v1.1-32-gdbae