summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar tihmstar2023-12-14 01:13:00 +0100
committerGravatar Nikias Bassen2025-09-06 19:55:49 +0200
commit52a81e111664cb267bd5b646ece9cb90c1a6fa94 (patch)
treea7c3e1d8fcded4c3fa700972c28875394dea2c79 /src
parent3fa36c5a7a745fd334bed8a3d5e432c626677910 (diff)
downloadlibirecovery-52a81e111664cb267bd5b646ece9cb90c1a6fa94.tar.gz
libirecovery-52a81e111664cb267bd5b646ece9cb90c1a6fa94.tar.bz2
Add iOS 1 support
Diffstat (limited to 'src')
-rw-r--r--src/libirecovery.c200
1 files changed, 197 insertions, 3 deletions
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 {
628#pragma pack() 628#pragma pack()
629#endif 629#endif
630 630
631#pragma pack(1)
632typedef struct {
633 uint16_t cmdcode;
634#define MSG_VERSION_QUERY 0x000
635#define MSG_ECHO 0x801
636#define MSG_DUMP_BUFFER 0x802
637#define MSG_SEND_COMMAND 0x803
638#define MSG_READ_FILE 0x804
639#define MSG_SEND_FILE 0x805
640#define MSG_CRC 0x807
641#define MSG_ACK 0x808
642#define MSG_REJECT 0x809
643 uint16_t magic; // always 0x1234
644 uint32_t size;
645 uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files
646} legacyCMD;
647#pragma pack()
648
631static THREAD_T th_event_handler = THREAD_T_NULL; 649static THREAD_T th_event_handler = THREAD_T_NULL;
632struct collection listeners; 650struct collection listeners;
633static mutex_t listener_mutex; 651static mutex_t listener_mutex;
@@ -777,6 +795,9 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons
777 ptr = strstr(iboot_string, "CPID:"); 795 ptr = strstr(iboot_string, "CPID:");
778 if (ptr != NULL) { 796 if (ptr != NULL) {
779 sscanf(ptr, "CPID:%x", &client->device_info.cpid); 797 sscanf(ptr, "CPID:%x", &client->device_info.cpid);
798 } else {
799 // early iOS 1 doesn't identify itself
800 client->device_info.cpid = 0x8900;
780 } 801 }
781 802
782 ptr = strstr(iboot_string, "CPRV:"); 803 ptr = strstr(iboot_string, "CPRV:");
@@ -1495,6 +1516,74 @@ static int iokit_usb_bulk_transfer(irecv_client_t client,
1495 1516
1496 return IRECV_E_USB_INTERFACE; 1517 return IRECV_E_USB_INTERFACE;
1497} 1518}
1519
1520static int iokit_usb_interrupt_transfer(irecv_client_t client,
1521 unsigned char endpoint,
1522 unsigned char *data,
1523 int length,
1524 int *transferred,
1525 unsigned int timeout)
1526{
1527 IOReturn result;
1528 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1529 UInt32 size = length;
1530 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1531 UInt8 numEndpoints;
1532
1533 if (!intf) return IRECV_E_USB_INTERFACE;
1534
1535 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1536
1537 if (result != kIOReturnSuccess)
1538 return IRECV_E_USB_INTERFACE;
1539
1540 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1541 UInt8 direction = 0;
1542 UInt8 number = 0;
1543 UInt8 transferType = 0;
1544 UInt16 maxPacketSize = 0;
1545 UInt8 interval = 0;
1546
1547 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1548 if (result != kIOReturnSuccess)
1549 continue;
1550
1551 if (direction == 3)
1552 direction = isUSBIn;
1553
1554 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1555 continue;
1556
1557 // Just because
1558 result = (*intf)->GetPipeStatus(intf, pipeRef);
1559 switch (result) {
1560 case kIOReturnSuccess: break;
1561 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1562 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1563 default: return IRECV_E_USB_STATUS;
1564 }
1565
1566 // Do the transfer
1567 if (isUSBIn) {
1568 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1569 if (result != kIOReturnSuccess)
1570 return IRECV_E_PIPE;
1571 *transferred = size;
1572
1573 return IRECV_E_SUCCESS;
1574 }
1575 else {
1576 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1577 if (result != kIOReturnSuccess)
1578 return IRECV_E_PIPE;
1579 *transferred = size;
1580
1581 return IRECV_E_SUCCESS;
1582 }
1583 }
1584
1585 return IRECV_E_USB_INTERFACE;
1586}
1498#endif 1587#endif
1499#endif 1588#endif
1500 1589
@@ -1532,6 +1621,36 @@ int irecv_usb_bulk_transfer(irecv_client_t client,
1532#endif 1621#endif
1533} 1622}
1534 1623
1624IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client,
1625 unsigned char endpoint,
1626 unsigned char *data,
1627 int length,
1628 int *transferred,
1629 unsigned int timeout)
1630{
1631#ifdef USE_DUMMY
1632 return IRECV_E_UNSUPPORTED;
1633#else
1634 int ret;
1635
1636#ifndef _WIN32
1637#ifdef HAVE_IOKIT
1638 return iokit_usb_interrupt_transfer(client, endpoint, data, length, transferred, timeout);
1639#else
1640 ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout);
1641 if (ret < 0) {
1642 libusb_clear_halt(client->handle, endpoint);
1643 }
1644#endif
1645#else
1646 // win32
1647 return IRECV_E_UNSUPPORTED;
1648#endif
1649
1650 return ret;
1651#endif
1652}
1653
1535#ifndef USE_DUMMY 1654#ifndef USE_DUMMY
1536#ifdef HAVE_IOKIT 1655#ifdef HAVE_IOKIT
1537static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service) 1656static 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
3112 return IRECV_E_INVALID_INPUT; 3231 return IRECV_E_INVALID_INPUT;
3113 } 3232 }
3114 3233
3234 if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3235 int bytes = 0;
3236 irecv_error_t error = 0;
3237#ifdef DEBUG
3238 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
3239 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
3240 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
3241 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3242#endif
3243 char cmdstr[0x100] = {};
3244 if (length & 0xf) {
3245 length &= ~0xf;
3246 length += 0x10;
3247 }
3248 snprintf(cmdstr, sizeof(cmdstr), "%s\n",command);
3249 legacyCMD cmd = {
3250 MSG_SEND_COMMAND,
3251 0x1234, //magic
3252 (uint32_t)length, //zero terminated?
3253 0x0
3254 };
3255 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3256 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3257 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3258 if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error;
3259 sleep(1); //go easy on this old device
3260 return IRECV_E_SUCCESS;
3261 }
3262
3115 if (length > 0) { 3263 if (length > 0) {
3116 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT); 3264 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
3117 } 3265 }
@@ -3323,6 +3471,18 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3323 3471
3324 irecv_error_t error = 0; 3472 irecv_error_t error = 0;
3325 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)); 3473 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
3474 int legacy_recovery_mode = 0;
3475
3476 if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3477#ifdef DEBUG
3478 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
3479 int bytes = 0;
3480 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
3481 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
3482 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3483#endif
3484 legacy_recovery_mode = 1;
3485 }
3326 3486
3327 if (check_context(client) != IRECV_E_SUCCESS) 3487 if (check_context(client) != IRECV_E_SUCCESS)
3328 return IRECV_E_NO_DEVICE; 3488 return IRECV_E_NO_DEVICE;
@@ -3331,6 +3491,7 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3331 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10}; 3491 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
3332 int dfu_crc = 1; 3492 int dfu_crc = 1;
3333 int packet_size = recovery_mode ? 0x8000 : 0x800; 3493 int packet_size = recovery_mode ? 0x8000 : 0x800;
3494 if (legacy_recovery_mode) packet_size = 0x200;
3334 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) { 3495 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
3335 packet_size = 0x40; 3496 packet_size = 0x40;
3336 dfu_crc = 0; 3497 dfu_crc = 0;
@@ -3345,7 +3506,24 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3345 } 3506 }
3346 3507
3347 /* initiate transfer */ 3508 /* initiate transfer */
3348 if (recovery_mode) { 3509 if (legacy_recovery_mode) {
3510 int bytes = 0;
3511 uint32_t loadaddr0x8900 = 0x09000000;
3512 const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR");
3513 if (ios1_overwrite_loadaddr) {
3514 sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900);
3515 debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900);
3516 }
3517 legacyCMD cmd = {
3518 MSG_SEND_FILE,
3519 0x1234, //magic
3520 (uint32_t)length,
3521 loadaddr0x8900
3522 };
3523 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3524 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3525 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3526 } else if (recovery_mode) {
3349 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT); 3527 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
3350 } else { 3528 } else {
3351 uint8_t state = 0; 3529 uint8_t state = 0;
@@ -3382,10 +3560,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3382 for (i = 0; i < packets; i++) { 3560 for (i = 0; i < packets; i++) {
3383 int size = (i + 1) < packets ? packet_size : last; 3561 int size = (i + 1) < packets ? packet_size : last;
3384 3562
3385 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ 3563 if (legacy_recovery_mode) {
3386 if (recovery_mode) { 3564 // Use interrupt transfer for legacy devices
3565 error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
3566 } else if (recovery_mode) {
3567 // Use bulk transfer for recovery mode
3387 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); 3568 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
3388 } else { 3569 } else {
3570 // Use control transfer for DFU and WTF mode
3389 if (dfu_crc) { 3571 if (dfu_crc) {
3390 int j; 3572 int j;
3391 for (j = 0; j < size; j++) { 3573 for (j = 0; j < size; j++) {
@@ -3493,6 +3675,13 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3493 irecv_reset(client); 3675 irecv_reset(client);
3494 } 3676 }
3495 3677
3678 if (legacy_recovery_mode) {
3679 irecv_reconnect(client, 0);
3680 char cmdstr[0x100] = {};
3681 snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length);
3682 irecv_send_command(client, cmdstr);
3683 }
3684
3496 return IRECV_E_SUCCESS; 3685 return IRECV_E_SUCCESS;
3497#endif 3686#endif
3498} 3687}
@@ -3548,6 +3737,11 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v
3548 return IRECV_E_INVALID_INPUT; 3737 return IRECV_E_INVALID_INPUT;
3549 } 3738 }
3550 3739
3740 if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3741 debug("iOS 1 doesn't support getenv\n");
3742 return IRECV_E_UNSUPPORTED;
3743 }
3744
3551 memset(command, '\0', sizeof(command)); 3745 memset(command, '\0', sizeof(command));
3552 snprintf(command, sizeof(command)-1, "getenv %s", variable); 3746 snprintf(command, sizeof(command)-1, "getenv %s", variable);
3553 irecv_error_t error = irecv_send_command_raw(client, command, 0); 3747 irecv_error_t error = irecv_send_command_raw(client, command, 0);