From e4a5ac4114177293e3a1b555ee767377b21d4432 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Sat, 23 Mar 2024 04:25:21 +0100 Subject: Add support for Port DFU device restore --- src/Makefile.am | 1 + src/ace3.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ace3.h | 17 ++++ src/common.c | 1 + src/common.h | 2 + src/dfu.c | 83 ++++++++++++++++++ src/dfu.h | 4 + src/idevicerestore.c | 157 +++++++++++++++++++++++++++++++++ 8 files changed, 509 insertions(+) create mode 100644 src/ace3.c create mode 100644 src/ace3.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 019424b..722487a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,7 @@ idevicerestore_SOURCES = \ restore.c restore.h \ asr.c asr.h \ fdr.c fdr.h \ + ace3.c ace3.h \ limera1n_payload.h \ limera1n.c limera1n.h \ download.c download.h \ diff --git a/src/ace3.c b/src/ace3.c new file mode 100644 index 0000000..b96e6b4 --- /dev/null +++ b/src/ace3.c @@ -0,0 +1,244 @@ +/* + * ace3.c + * Functions to handle Ace3/uarp firmware format + * + * Copyright (c) 2024 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "common.h" +#include "ace3.h" +#include "endianness.h" + +static uint32_t crc_buffer(const unsigned char* buffer, unsigned int bufsize, unsigned int salt) +{ + uint32_t result; + unsigned int i; + unsigned int j; + + if ( !buffer ) + return 0xFFFFFFFF; + result = salt; + for (i = 0; i < bufsize; ++i) { + for (j = 0; j != 8; ++j) { + unsigned int tmp0 = 2 * result; + unsigned int tmp1 = *(unsigned char*)(buffer + i); + unsigned int tmp2 = ((unsigned int)result >> 31) ^ ((tmp1 >> j) & 1); + result = (tmp2 + 2 * result) ^ 0x4C11DB6; + if (!tmp2) + result = tmp0; + } + } + return result; +} + +int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size) +{ + struct ace3bin_header { + uint32_t magic; // 0xACE00003 + uint32_t unk4; // 0x00203400 + uint32_t unk8; // 0x00002800 + uint32_t header_size; // 0x00000040 + uint32_t data1_size; + uint32_t data2_size; + uint32_t im4m_offset; + uint32_t im4m_dl_size; + uint32_t content_size; + uint32_t crc; + uint64_t fill1; // 0xFFFFFFFFFFFFFFFF + uint64_t fill2; // 0xFFFFFFFFFFFFFFFF + uint64_t fill3; // 0xFFFFFFFFFFFFFFFF + }; + + struct uarp_header { + uint32_t unk_00; // BE 0x00000002 + uint32_t header_size; // BE usually 0x0000002C + uint32_t plist_offset; // BE + uint32_t unk_0c; // 0 + uint32_t unk_10; // 0 + uint32_t unk_14; // 0 + uint32_t unk_18; // 0 + uint32_t c_offset; // BE + uint32_t unk_20; // 0 + uint32_t toc_offset; // BE usually 0x0000002c + uint32_t toc_size; // BE + }; + struct uarp_toc_entry { + uint32_t this_size; // BE usually 0x28 + uint32_t fourcc; // 'PT01' or similar + uint32_t index; // BE starting with 0, increment+1 for each entry + uint32_t unk_0c; // BE usually not zero + uint32_t unk_10; // BE usually 0 + uint32_t unk_14; // BE usually 0 + uint32_t unk_18; // BE other offset, not sure + uint32_t unk_1c; // BE usually 0 + uint32_t offset; // BE + uint32_t size; // + }; + + plist_t p_im4m = plist_dict_get_item(tss, "USBPortController1,Ticket"); + uint64_t im4m_size = 0; + const char* im4m = plist_get_data_ptr(p_im4m, &im4m_size); + + struct uarp_header* uarp_hdr = (struct uarp_header*)uarp_fw; + uint32_t uarp_hdr_size = be32toh(uarp_hdr->header_size); + uint32_t plist_offset = be32toh(uarp_hdr->plist_offset); + uint32_t plist_size = uarp_size - plist_offset; + nskeyedarchive_t ka = nskeyedarchive_new_from_data(uarp_fw + plist_offset, plist_size); + if (!ka) { + return -1; + } + plist_t uarp_dict = nskeyedarchive_to_plist(ka); + nskeyedarchive_free(ka); + + // find the corresponding entries for given BoardID+PREV + + char* payload_4cc = NULL; + char* data_payload_4ccs = NULL; + + plist_t sb_payloads = plist_dict_get_item(uarp_dict, "SuperBinary Payloads"); + if (PLIST_IS_ARRAY(sb_payloads)) { + plist_array_iter iter = NULL; + plist_array_new_iter(sb_payloads, &iter); + plist_t payload = NULL; + do { + plist_array_next_item(sb_payloads, iter, &payload); + if (!payload) { + break; + } + plist_t meta = plist_dict_get_item(payload, "Payload MetaData"); + if (!PLIST_IS_DICT(meta)) { + continue; + } + plist_t prefix = plist_dict_get_item(meta, "Personalization Manifest Prefix"); + if (!PLIST_IS_STRING(prefix)) { + continue; + } + if (strcmp(plist_get_string_ptr(prefix, NULL), "USBPortController") != 0) { + continue; + } + plist_t p_boardid = plist_dict_get_item(meta, "Personalization Board ID (64 bits)"); + if (!PLIST_IS_INT(p_boardid)) { + continue; + } + uint64_t boardid = 0; + plist_get_uint_val(p_boardid, &boardid); + if (boardid == bdid) { + plist_t p4cc = plist_dict_get_item(payload, "Payload 4CC"); + plist_get_string_val(p4cc, &payload_4cc); + plist_t matching = plist_dict_get_item(meta, "Personalization Matching Data"); + if (PLIST_IS_ARRAY(matching)) { + plist_array_iter iter2 = NULL; + plist_array_new_iter(matching, &iter2); + plist_t match = NULL; + do { + plist_array_next_item(matching, iter2, &match); + if (!PLIST_IS_DICT(match)) { + break; + } + uint64_t minrev = 0; + plist_t p_min = plist_dict_get_item(match, "Personalization Matching Data Product Revision Minimum"); + plist_get_uint_val(p_min, &minrev); + uint64_t maxrev = 0; + plist_t p_max = plist_dict_get_item(match, "Personalization Matching Data Product Revision Maximum"); + plist_get_uint_val(p_max, &maxrev); + if (prev >= minrev && prev <= maxrev) { + plist_t tags = plist_dict_get_item(match, "Personalization Matching Data Payload Tags"); + plist_get_string_val(tags, &data_payload_4ccs); + break; + } + } while (match); + plist_mem_free(iter2); + } + break; + } + } while (payload); + plist_mem_free(iter); + } + if (!payload_4cc) { + printf("Failed to get payload 4cc\n"); + return -1; + } + if (!data_payload_4ccs) { + printf("Failed to get data payload 4ccs\n"); + return -1; + } + + // now find the blobs in UARP data + uint32_t dl_offset = 0; + uint32_t dl_size = 0; + uint32_t data1_offset = 0; + uint32_t data1_size = 0; + uint32_t data2_offset = 0; + uint32_t data2_size = 0; + uint32_t toc_offset = be32toh(uarp_hdr->toc_offset); + uint32_t toc_size = be32toh(uarp_hdr->toc_size); + const unsigned char* p = uarp_fw + uarp_hdr_size; + while (p < uarp_fw + toc_size) { + struct uarp_toc_entry* entry = (struct uarp_toc_entry*)p; + uint32_t te_size = be32toh(entry->this_size); + if (strncmp((char*)&(entry->fourcc), payload_4cc, 4) == 0) { + dl_offset = be32toh(entry->offset); + dl_size = be32toh(entry->size); + } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs, 4) == 0) { + data1_offset = be32toh(entry->offset); + data1_size = be32toh(entry->size); + } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs+5, 4) == 0) { + data2_offset = be32toh(entry->offset); + data2_size = be32toh(entry->size); + } + p += te_size; + } + + uint32_t content_size = data1_size + data2_size + im4m_size + dl_size; + + *bin_out = (unsigned char*)malloc(0x40 + content_size); + struct ace3bin_header* hdr = (struct ace3bin_header*)(*bin_out); + hdr->magic = htole32(0xACE00003); + hdr->unk4 = htole32(0x00203400); + hdr->unk8 = htole32(0x00002800); + hdr->header_size = htole32(0x40); + hdr->data1_size = htole32(data1_size); + hdr->data2_size = htole32(data2_size);; + hdr->im4m_offset = htole32(0x40 + data1_size + data2_size); + hdr->im4m_dl_size = htole32(im4m_size + dl_size); + hdr->content_size = htole32(content_size); + hdr->crc = 0; + hdr->fill1 = 0xFFFFFFFFFFFFFFFFLL; + hdr->fill2 = 0xFFFFFFFFFFFFFFFFLL; + hdr->fill3 = 0xFFFFFFFFFFFFFFFFLL; + + // write data1 payload + memcpy(*bin_out + 0x40, uarp_fw + data1_offset, data1_size); + // write data2 payload + memcpy(*bin_out + 0x40 + data1_size, uarp_fw + data2_offset, data2_size); + // write IM4M + memcpy(*bin_out + 0x40 + data1_size + data2_size, im4m, im4m_size); + // write dl payload + memcpy(*bin_out + 0x40 + data1_size + data2_size + im4m_size, uarp_fw + dl_offset, dl_size); + + // calculate CRC and update header + hdr->crc = htole32(crc_buffer(*bin_out + 0x40, content_size, 0xFFFFFFFF)); + + *bin_size = 0x40 + content_size; + + return 0; +} diff --git a/src/ace3.h b/src/ace3.h new file mode 100644 index 0000000..2ef65a7 --- /dev/null +++ b/src/ace3.h @@ -0,0 +1,17 @@ +#ifndef IDEVICERESTORE_ACE3_H +#define IDEVICERESTORE_ACE3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/common.c b/src/common.c index 9a73994..499509d 100644 --- a/src/common.c +++ b/src/common.c @@ -63,6 +63,7 @@ struct idevicerestore_mode_t idevicerestore_modes[] = { { 3, "Recovery" }, { 4, "Restore" }, { 5, "Normal" }, + { 6, "Port DFU" }, }; int idevicerestore_debug = 0; diff --git a/src/common.h b/src/common.h index 493b1df..9b3c1e3 100644 --- a/src/common.h +++ b/src/common.h @@ -47,6 +47,7 @@ extern "C" { #define _MODE_RECOVERY 3 #define _MODE_RESTORE 4 #define _MODE_NORMAL 5 +#define _MODE_PORTDFU 6 #define MODE_UNKNOWN &idevicerestore_modes[_MODE_UNKNOWN] #define MODE_WTF &idevicerestore_modes[_MODE_WTF] @@ -54,6 +55,7 @@ extern "C" { #define MODE_RECOVERY &idevicerestore_modes[_MODE_RECOVERY] #define MODE_RESTORE &idevicerestore_modes[_MODE_RESTORE] #define MODE_NORMAL &idevicerestore_modes[_MODE_NORMAL] +#define MODE_PORTDFU &idevicerestore_modes[_MODE_PORTDFU] #define FLAG_QUIT 1 diff --git a/src/dfu.c b/src/dfu.c index 8602bc0..62a3dc0 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -118,6 +118,21 @@ int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffe return 0; } +int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options) +{ + irecv_error_t err = 0; + + info("Sending data (%d bytes)...\n", size); + + err = irecv_send_buffer(client->dfu->client, buffer, size, irecv_options); + if (err != IRECV_E_SUCCESS) { + error("ERROR: Unable to send data: %s\n", irecv_strerror(err)); + return -1; + } + + return 0; +} + int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component) { char* path = NULL; @@ -204,6 +219,24 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide return 0; } +int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + + *bdid = device_info->bdid; + + return 0; +} + int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) { if(client->dfu == NULL) { @@ -222,6 +255,27 @@ int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) return 0; } +int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + char* ptr = strstr(device_info->serial_string, "PREV:"); + if (ptr) { + sscanf(ptr, "PREV:%x", prev); + return 0; + } + return -1; +} + + int dfu_is_image4_supported(struct idevicerestore_client_t* client) { if(client->dfu == NULL) { @@ -238,6 +292,35 @@ int dfu_is_image4_supported(struct idevicerestore_client_t* client) return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE); } +int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + + if (device_info->ap_nonce && device_info->ap_nonce_size > 0) { + *nonce = (unsigned char*)malloc(device_info->ap_nonce_size); + if (!*nonce) { + return -1; + } + *nonce_size = device_info->ap_nonce_size; + // The nonce is backwards, so we have to swap the bytes + unsigned int i = 0; + for (i = 0; i < *nonce_size; i++) { + (*nonce)[(*nonce_size)-1-i] = device_info->ap_nonce[i]; + } + } + + return 0; +} + int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->dfu == NULL) { diff --git a/src/dfu.h b/src/dfu.h index f6174ab..4590dbd 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -41,9 +41,13 @@ int dfu_client_new(struct idevicerestore_client_t* client); void dfu_client_free(struct idevicerestore_client_t* client); irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client); int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size); +int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options); int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); +int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid); int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid); +int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev); int dfu_is_image4_supported(struct idevicerestore_client_t* client); +int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity); diff --git a/src/idevicerestore.c b/src/idevicerestore.c index e71dea4..ad02f7f 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -47,6 +47,7 @@ #include +#include "ace3.h" #include "dfu.h" #include "tss.h" #include "img3.h" @@ -298,6 +299,9 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata) case IRECV_K_DFU_MODE: client->mode = MODE_DFU; break; + case IRECV_K_PORT_DFU_MODE: + client->mode = MODE_PORTDFU; + break; case IRECV_K_RECOVERY_MODE_1: case IRECV_K_RECOVERY_MODE_2: case IRECV_K_RECOVERY_MODE_3: @@ -316,6 +320,12 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata) mutex_lock(&client->device_event_mutex); client->mode = MODE_UNKNOWN; debug("%s: device %016" PRIx64 " (udid: %s) disconnected\n", __func__, client->ecid, (client->udid) ? client->udid : "N/A"); + if (event->mode == IRECV_K_PORT_DFU_MODE) { + // We have to reset the ECID here if a port DFU device disconnects, + // because when the device reconnects in a different mode, it will + // have the actual device ECID and wouldn't get detected. + client->ecid = 0; + } cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } @@ -683,6 +693,152 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } } + if (client->mode == MODE_PORTDFU) { + unsigned int pdfu_bdid = 0; + unsigned int pdfu_cpid = 0; + unsigned int prev = 0; + + if (dfu_get_bdid(client, &pdfu_bdid) < 0) { + error("ERROR: Failed to get bdid for Port DFU device!\n"); + return -1; + } + if (dfu_get_cpid(client, &pdfu_cpid) < 0) { + error("ERROR: Failed to get cpid for Port DFU device!\n"); + return -1; + } + if (dfu_get_prev(client, &prev) < 0) { + error("ERROR: Failed to get PREV for Port DFU device!\n"); + return -1; + } + + unsigned char* pdfu_nonce = NULL; + unsigned int pdfu_nsize = 0; + if (dfu_get_portdfu_nonce(client, &pdfu_nonce, &pdfu_nsize) < 0) { + error("ERROR: Failed to get nonce for Port DFU device!\n"); + return -1; + } + + plist_t build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_ERASE_INSTALL, 0); + if (!build_identity) { + error("ERORR: Failed to get build identity\n"); + return -1; + } + + unsigned int b_pdfu_cpid = (unsigned int)_plist_dict_get_uint(build_identity, "USBPortController1,ChipID"); + if (b_pdfu_cpid != pdfu_cpid) { + error("ERROR: cpid 0x%02x doesn't match USBPortController1,ChipID in build identity (0x%02x)\n", pdfu_cpid, b_pdfu_cpid); + return -1; + } + unsigned int b_pdfu_bdid = (unsigned int)_plist_dict_get_uint(build_identity, "USBPortController1,BoardID"); + if (b_pdfu_bdid != pdfu_bdid) { + error("ERROR: bdid 0x%x doesn't match USBPortController1,BoardID in build identity (0x%x)\n", pdfu_bdid, b_pdfu_bdid); + return -1; + } + + plist_t parameters = plist_new_dict(); + plist_dict_set_item(parameters, "@USBPortController1,Ticket", plist_new_bool(1)); + plist_dict_set_item(parameters, "USBPortController1,ECID", plist_new_int(client->ecid)); + _plist_dict_copy_item(parameters, build_identity, "USBPortController1,BoardID", NULL); + _plist_dict_copy_item(parameters, build_identity, "USBPortController1,ChipID", NULL); + _plist_dict_copy_item(parameters, build_identity, "USBPortController1,SecurityDomain", NULL); + plist_dict_set_item(parameters, "USBPortController1,SecurityMode", plist_new_bool(1)); + plist_dict_set_item(parameters, "USBPortController1,ProductionMode", plist_new_bool(1)); + plist_t usbf = plist_access_path(build_identity, 2, "Manifest", "USBPortController1,USBFirmware"); + if (!usbf) { + plist_free(parameters); + error("ERROR: Unable to find USBPortController1,USBFirmware in build identity\n"); + return -1; + } + plist_t p_fwpath = plist_access_path(usbf, 2, "Info", "Path"); + if (!p_fwpath) { + plist_free(parameters); + error("ERROR: Unable to find path of USBPortController1,USBFirmware component\n"); + return -1; + } + const char* fwpath = plist_get_string_ptr(p_fwpath, NULL); + if (!fwpath) { + plist_free(parameters); + error("ERROR: Unable to get path of USBPortController1,USBFirmware component\n"); + return -1; + } + unsigned char* uarp_buf = NULL; + unsigned int uarp_size = 0; + if (ipsw_extract_to_memory(client->ipsw, fwpath, &uarp_buf, &uarp_size) < 0) { + plist_free(parameters); + error("ERROR: Unable to extract '%s' from IPSW\n", fwpath); + return -1; + } + usbf = plist_copy(usbf); + plist_dict_remove_item(usbf, "Info"); + plist_dict_set_item(parameters, "USBPortController1,USBFirmware", usbf); + plist_dict_set_item(parameters, "USBPortController1,Nonce", plist_new_data((const char*)pdfu_nonce, pdfu_nsize)); + + plist_t request = tss_request_new(NULL); + if (request == NULL) { + plist_free(parameters); + error("ERROR: Unable to create TSS request\n"); + return -1; + } + plist_dict_merge(&request, parameters); + plist_free(parameters); + + // send request and grab response + plist_t response = tss_request_send(request, client->tss_url); + plist_free(request); + if (response == NULL) { + error("ERROR: Unable to send TSS request\n"); + return -1; + } + info("Received USBPortController1,Ticket\n"); + + info("Creating Ace3Binary\n"); + unsigned char* ace3bin = NULL; + size_t ace3bin_size = 0; + if (ace3_create_binary(uarp_buf, uarp_size, pdfu_bdid, prev, response, &ace3bin, &ace3bin_size) < 0) { + error("ERROR: Could not create Ace3Binary\n"); + return -1; + } + plist_free(response); + free(uarp_buf); + + if (idevicerestore_keep_pers) { + write_file("Ace3Binary", (const char*)ace3bin, ace3bin_size); + } + + if (dfu_send_buffer_with_options(client, ace3bin, ace3bin_size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH | IRECV_SEND_OPT_DFU_SMALL_PKT) < 0) { + error("ERROR: Could not send Ace3Buffer to device\n"); + return -1; + } + + debug("Waiting for device to disconnect...\n"); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000); + if (client->mode != MODE_UNKNOWN || (client->flags & FLAG_QUIT)) { + mutex_unlock(&client->device_event_mutex); + + if (!(client->flags & FLAG_QUIT)) { + error("ERROR: Device did not disconnect. Port DFU failed.\n"); + } + return -2; + } + debug("Waiting for device to reconnect in DFU mode...\n"); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000); + if (client->mode != MODE_DFU || (client->flags & FLAG_QUIT)) { + mutex_unlock(&client->device_event_mutex); + if (!(client->flags & FLAG_QUIT)) { + error("ERROR: Device did not reconnect in DFU mode. Port DFU failed.\n"); + } + return -2; + } + mutex_unlock(&client->device_event_mutex); + + if (client->flags & FLAG_NOACTION) { + info("Port DFU restore successful.\n"); + return 0; + } else { + info("Port DFU restore successful. Continuing.\n"); + } + } + idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8); /* check if device type is supported by the given build manifest */ @@ -1773,6 +1929,7 @@ irecv_device_t get_irecv_device(struct idevicerestore_client_t *client) return normal_get_irecv_device(client); case _MODE_DFU: + case _MODE_PORTDFU: case _MODE_RECOVERY: return dfu_get_irecv_device(client); -- cgit v1.1-32-gdbae