/// <summary> /// Write the calibration blocks. /// </summary> private async Task <bool> Write(CancellationToken cancellationToken, byte[] image, WriteType writeType) { await this.vehicle.SendToolPresentNotification(); BlockType relevantBlocks; switch (writeType) { case WriteType.Compare: relevantBlocks = BlockType.All; break; case WriteType.TestWrite: relevantBlocks = BlockType.Calibration; break; case WriteType.Calibration: relevantBlocks = BlockType.Calibration; break; case WriteType.Parameters: relevantBlocks = BlockType.Parameter; break; case WriteType.OsAndCalibration: relevantBlocks = BlockType.Calibration | BlockType.OperatingSystem; break; case WriteType.Full: // Overwriting parameter blocks would break the EBCM pairing, // which is not what most users want. They just want a new OS // and the calibration to go along with it. // // The cast seems redundant, but C# converts the enum values // to ints when it does arithmetic. relevantBlocks = (BlockType)(BlockType.All - BlockType.Parameter); break; default: throw new InvalidDataException("Unsuppported operation type: " + writeType.ToString()); } // Which flash chip? await this.vehicle.SendToolPresentNotification(); await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadProperty); Query <UInt32> chipIdQuery = this.vehicle.CreateQuery <UInt32>( this.protocol.CreateFlashMemoryTypeQuery, this.protocol.ParseFlashMemoryType, cancellationToken); Response <UInt32> chipIdResponse = await chipIdQuery.Execute(); if (chipIdResponse.Status != ResponseStatus.Success) { logger.AddUserMessage("Unable to determine which flash chip is in this PCM"); return(false); } // TODO: Move the device types lookup to a function in Misc/FlashChips.cs // known chips in the P01 and P59 // http://ftp1.digi.com/support/documentation/jtag_v410_flashes.pdf string Amd = "AMD"; // 0001 string Intel = "Intel"; // 0089 string I4471 = "28F400B5-B 512Kb"; // 4471 string A2258 = "Am29F800B 1Mbyte"; // 2258 string I889D = "28F800B5-B 1Mbyte"; // 889D string unknown = "unknown"; // default case logger.AddUserMessage("Flash memory ID code: " + chipIdResponse.Value.ToString("X8")); switch ((chipIdResponse.Value >> 16)) { case 0x0001: logger.AddUserMessage("Flash memory manufactuer: " + Amd); break; case 0x0089: logger.AddUserMessage("Flash memory manufactuer: " + Intel); break; default: logger.AddUserMessage("Flash memory manufactuer: " + unknown); break; } switch (chipIdResponse.Value & 0xFFFF) { case 0x4471: logger.AddUserMessage("Flash memory type: " + I4471); break; case 0x2258: logger.AddUserMessage("Flash memory type: " + A2258); break; case 0x889D: logger.AddUserMessage("Flash memory type: " + I889D); break; default: logger.AddUserMessage("Flash memory type: " + unknown); break; } await this.vehicle.SendToolPresentNotification(); IList <MemoryRange> ranges = FlashChips.GetMemoryRanges(chipIdResponse.Value, this.logger); if (ranges == null) { return(false); } CKernelVerifier verifier = new CKernelVerifier( image, ranges, this.vehicle, this.protocol, this.logger); bool allRangesMatch = false; bool writeAttempted = false; for (int attempt = 1; attempt <= 5; attempt++) { if (await verifier.CompareRanges( ranges, image, relevantBlocks, cancellationToken)) { allRangesMatch = true; // Don't stop here if the user just wants to test their cable. if (writeType == WriteType.TestWrite) { if (attempt == 1) { this.logger.AddUserMessage("Beginning test."); } } else { this.logger.AddUserMessage("All ranges are identical."); return(true); } } if ((writeType == WriteType.TestWrite) && (attempt > 1)) { // TODO: the app should know if any errors were encountered. The user shouldn't need to check. this.logger.AddUserMessage("Test complete. Were any errors logged above?"); return(true); } // Stop now if the user only requested a comparison. if (writeType == WriteType.Compare) { this.logger.AddUserMessage("Note that mismatched Parameter blocks are to be expected."); this.logger.AddUserMessage("Parameter data can change every time the PCM is used."); return(true); } writeAttempted = true; // Erase and rewrite the required memory ranges. await this.vehicle.SetDeviceTimeout(TimeoutScenario.Maximum); foreach (MemoryRange range in ranges) { // We'll send a tool-present message during the erase request. if ((range.ActualCrc == range.DesiredCrc) && (writeType != WriteType.TestWrite)) { continue; } if ((range.Type & relevantBlocks) == 0) { continue; } this.logger.AddUserMessage( string.Format( "Processing range {0:X6}-{1:X6}", range.Address, range.Address + (range.Size - 1))); if (writeType == WriteType.TestWrite) { this.logger.AddUserMessage("Pretending to erase."); } else { if (!await this.EraseMemoryRange(range, cancellationToken)) { return(false); } } if (writeType == WriteType.TestWrite) { this.logger.AddUserMessage("Pretending to write..."); } else { this.logger.AddUserMessage("Writing..."); } await this.vehicle.SendToolPresentNotification(); await WriteMemoryRange( range, image, writeType == WriteType.TestWrite, cancellationToken); } } if (!writeAttempted) { this.logger.AddUserMessage("Assertion failed. WriteAttempted should be true."); } if (allRangesMatch) { this.logger.AddUserMessage("Flash successful!"); return(true); } this.logger.AddUserMessage("==============================================="); this.logger.AddUserMessage("THE CHANGES WERE -NOT- WRITTEN SUCCESSFULLY"); this.logger.AddUserMessage("==============================================="); if (writeType == WriteType.Calibration) { this.logger.AddUserMessage("Erasing Calibration to force recovery mode."); this.logger.AddUserMessage(""); foreach (MemoryRange range in ranges) { if (range.Type == BlockType.Calibration) { await this.EraseMemoryRange(range, cancellationToken); } } } if (cancellationToken.IsCancellationRequested) { this.logger.AddUserMessage(""); this.logger.AddUserMessage("The operation was cancelled."); this.logger.AddUserMessage("This PCM is probably not usable in its current state."); this.logger.AddUserMessage(""); } else { this.logger.AddUserMessage("This may indicate a hardware problem on the PCM."); this.logger.AddUserMessage("We tried, and re-tried, and it still didn't work."); this.logger.AddUserMessage(""); this.logger.AddUserMessage("Please start a new thread at pcmhacking.net, and"); this.logger.AddUserMessage("include the contents of the debug tab."); this.RequestDebugLogs(cancellationToken); } return(false); }
/// <summary> /// Write the calibration blocks. /// </summary> private async Task <bool> Write(CancellationToken cancellationToken, byte[] image, WriteType writeType, ToolPresentNotifier notifier) { await notifier.Notify(); BlockType relevantBlocks; switch (writeType) { case WriteType.Compare: relevantBlocks = BlockType.All; break; case WriteType.TestWrite: relevantBlocks = BlockType.Calibration; break; case WriteType.Calibration: relevantBlocks = BlockType.Calibration; break; case WriteType.Parameters: relevantBlocks = BlockType.Parameter; break; case WriteType.OsAndCalibration: relevantBlocks = BlockType.Calibration | BlockType.OperatingSystem; break; case WriteType.Full: relevantBlocks = BlockType.All; break; default: throw new InvalidDataException("Unsuppported operation type: " + writeType.ToString()); } // Which flash chip? await notifier.Notify(); await this.device.SetTimeout(TimeoutScenario.ReadProperty); Query <UInt32> chipIdQuery = new Query <uint>( this.device, this.protocol.CreateFlashMemoryTypeQuery, this.protocol.ParseFlashMemoryType, this.logger, cancellationToken, notifier); Response <UInt32> chipIdResponse = await chipIdQuery.Execute(); if (chipIdResponse.Status != ResponseStatus.Success) { logger.AddUserMessage("Unable to determine which flash chip is in this PCM"); return(false); } logger.AddUserMessage("Flash memory type: " + chipIdResponse.Value.ToString("X8")); // known chips // http://ftp1.digi.com/support/documentation/jtag_v410_flashes.pdf string Amd = "Amd"; // 0001 string Intel = "Intel"; // 0089 string I4471 = "28F400B5-B 512Kb"; // 4471 string A2258 = "Am29F800B 1Mbyte"; // 2258 string I889D = "28F800B5-B 1Mbyte"; // 889D logger.AddUserMessage("Flash memory ID: " + chipIdResponse.Value.ToString("X8")); if ((chipIdResponse.Value >> 16) == 0x0001) { logger.AddUserMessage("Flash memory manufactuer: " + Amd); } if ((chipIdResponse.Value >> 16) == 0x0089) { logger.AddUserMessage("Flash memory manufactuer: " + Intel); } if ((chipIdResponse.Value & 0xFFFF) == 0x4471) { logger.AddUserMessage("Flash memory type: " + I4471); } if ((chipIdResponse.Value & 0xFFFF) == 0x2258) { logger.AddUserMessage("Flash memory type: " + A2258); } if ((chipIdResponse.Value & 0xFFFF) == 0x889D) { logger.AddUserMessage("Flash memory type: " + I889D); } await notifier.Notify(); IList <MemoryRange> ranges = this.GetMemoryRanges(chipIdResponse.Value); if (ranges == null) { return(false); } logger.AddUserMessage("Computing CRCs from local file..."); this.GetCrcFromImage(ranges, image); if (await this.CompareRanges( ranges, image, relevantBlocks, notifier, cancellationToken)) { // Don't stop here if the user just wants to test their cable. if (writeType != WriteType.TestWrite) { this.logger.AddUserMessage("All ranges are identical."); return(true); } } // Stop now if the user only requested a comparison. if (writeType == WriteType.Compare) { this.logger.AddUserMessage("Note that mismatched Parameter blocks are to be expected."); this.logger.AddUserMessage("Parameter data can change every time the PCM is used."); return(true); } // Erase and rewrite the required memory ranges. await this.device.SetTimeout(TimeoutScenario.Maximum); foreach (MemoryRange range in ranges) { // We'll send a tool-present message during the erase request. if ((range.ActualCrc == range.DesiredCrc) && (writeType != WriteType.TestWrite)) { continue; } if ((range.Type & relevantBlocks) == 0) { continue; } this.logger.AddUserMessage( string.Format( "Processing range {0:X6}-{1:X6}", range.Address, range.Address + (range.Size - 1))); if (writeType != WriteType.TestWrite) { this.logger.AddUserMessage("Erasing"); Query <byte> eraseRequest = new Query <byte>( this.device, () => this.protocol.CreateFlashEraseBlockRequest(range.Address), this.protocol.ParseFlashEraseBlock, this.logger, cancellationToken, notifier); eraseRequest.MaxTimeouts = 5; // Reduce this when we know how many are likely to be needed. Response <byte> eraseResponse = await eraseRequest.Execute(); if (eraseResponse.Status != ResponseStatus.Success) { this.logger.AddUserMessage("Unable to erase flash memory: " + eraseResponse.Status.ToString()); this.RequestDebugLogs(cancellationToken); return(false); } if (eraseResponse.Value != 0x80) { this.logger.AddUserMessage("Unable to erase flash memory. Code: " + eraseResponse.Value.ToString("X2")); this.RequestDebugLogs(cancellationToken); return(false); } } if (writeType == WriteType.TestWrite) { this.logger.AddUserMessage("Testing..."); } else { this.logger.AddUserMessage("Writing..."); } await notifier.Notify(); await WriteMemoryRange( range, image, writeType == WriteType.TestWrite, notifier, cancellationToken); } bool match = await this.CompareRanges(ranges, image, relevantBlocks, notifier, cancellationToken); if (writeType == WriteType.TestWrite) { // TODO: the app should know if any errors were encountered. The user shouldn't need to check. this.logger.AddUserMessage("Test complete. Were any errors logged above?"); return(true); } if (match) { this.logger.AddUserMessage("Flash successful!"); return(true); } this.logger.AddUserMessage("==============================================="); this.logger.AddUserMessage("THE CHANGES WERE -NOT- WRITTEN SUCCESSFULLY"); this.logger.AddUserMessage("==============================================="); if (cancellationToken.IsCancellationRequested) { this.logger.AddUserMessage(""); this.logger.AddUserMessage("The operation was cancelled."); this.logger.AddUserMessage("This PCM is probably not usable in its current state."); this.logger.AddUserMessage(""); } else { this.logger.AddUserMessage("Don't panic. Also, don't try to drive this car."); this.logger.AddUserMessage("Please try flashing again. Preferably now."); this.logger.AddUserMessage("In most cases, the second try will succeed."); this.logger.AddUserMessage(""); this.logger.AddUserMessage("If this happens three times in a row, please"); this.logger.AddUserMessage("start a new thread at pcmhacking.net, and"); this.logger.AddUserMessage("include the contents of the debug tab."); this.logger.AddUserMessage(""); this.RequestDebugLogs(cancellationToken); } return(false); }