diff options
| author | 2023-12-14 01:13:00 +0100 | |
|---|---|---|
| committer | 2025-09-06 19:55:49 +0200 | |
| commit | 52a81e111664cb267bd5b646ece9cb90c1a6fa94 (patch) | |
| tree | a7c3e1d8fcded4c3fa700972c28875394dea2c79 /src | |
| parent | 3fa36c5a7a745fd334bed8a3d5e432c626677910 (diff) | |
| download | libirecovery-52a81e111664cb267bd5b646ece9cb90c1a6fa94.tar.gz libirecovery-52a81e111664cb267bd5b646ece9cb90c1a6fa94.tar.bz2 | |
Add iOS 1 support
Diffstat (limited to 'src')
| -rw-r--r-- | src/libirecovery.c | 200 |
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) | ||
| 632 | typedef 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 | |||
| 631 | static THREAD_T th_event_handler = THREAD_T_NULL; | 649 | static THREAD_T th_event_handler = THREAD_T_NULL; |
| 632 | struct collection listeners; | 650 | struct collection listeners; |
| 633 | static mutex_t listener_mutex; | 651 | static 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 | |||
| 1520 | static 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 | ||
| 1624 | IRECV_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 |
| 1537 | static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service) | 1656 | 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 | |||
| 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); |
