/// <summary> /// Update the VIN. /// </summary> private async void modifyVinButton_Click(object sender, EventArgs e) { try { Response <uint> osidResponse = await this.Vehicle.QueryOperatingSystemId(CancellationToken.None); if (osidResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Operating system query failed: " + osidResponse.Status); return; } PcmInfo info = new PcmInfo(osidResponse.Value); var vinResponse = await this.Vehicle.QueryVin(); if (vinResponse.Status != ResponseStatus.Success) { this.AddUserMessage("VIN query failed: " + vinResponse.Status.ToString()); return; } DialogBoxes.VinForm vinForm = new DialogBoxes.VinForm(); vinForm.Vin = vinResponse.Value; DialogResult dialogResult = vinForm.ShowDialog(); if (dialogResult == DialogResult.OK) { bool unlocked = await this.Vehicle.UnlockEcu(info.KeyAlgorithm); if (!unlocked) { this.AddUserMessage("Unable to unlock PCM."); return; } Response <bool> vinmodified = await this.Vehicle.UpdateVin(vinForm.Vin.Trim()); if (vinmodified.Value) { this.AddUserMessage("VIN successfully updated to " + vinForm.Vin); MessageBox.Show("VIN updated to " + vinForm.Vin + " successfully.", "Good news.", MessageBoxButtons.OK); } else { MessageBox.Show("Unable to change the VIN to " + vinForm.Vin + ". Error: " + vinmodified.Status, "Bad news.", MessageBoxButtons.OK); } } } catch (Exception exception) { this.AddUserMessage("VIN change failed: " + exception.ToString()); } }
/// <summary> /// Write the contents of the flash. /// </summary> private async void writeFullContentsButton_Click(object sender, EventArgs e) { if (this.vehicle == null) { // This shouldn't be possible - it would mean the buttons // were enabled when they shouldn't be. return; } Response <uint> osidResponse = await this.vehicle.QueryOperatingSystemId(); if (osidResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Operating system query failed: " + osidResponse.Status); return; } PcmInfo info = new PcmInfo(osidResponse.Value); bool unlocked = await this.vehicle.UnlockEcu(info.KeyAlgorithm); if (!unlocked) { this.AddUserMessage("Unlock was not successful."); return; } this.AddUserMessage("Unlock succeeded."); string path = this.ShowOpenDialog(); if (path == null) { return; } this.AddUserMessage("Pretending to update PCM with content from " + path); try { using (Stream stream = File.OpenRead(path)) { await this.vehicle.WriteContents(stream); } } catch (IOException exception) { this.AddUserMessage(exception.ToString()); } }
/// <summary> /// Write changes to the PCM's flash memory. /// </summary> private async void write_BackgroundThread(WriteType writeType) { using (new AwayMode()) { try { this.currentWriteType = writeType; if (this.Vehicle == null) { // This shouldn't be possible - it would mean the buttons // were enabled when they shouldn't be. return; } this.cancellationTokenSource = new CancellationTokenSource(); string path = null; this.Invoke((MethodInvoker) delegate() { this.DisableUserInput(); this.cancelButton.Enabled = true; path = this.ShowOpenDialog(); }); if (path == null) { return; } this.AddUserMessage(path); byte[] image; using (Stream stream = File.OpenRead(path)) { image = new byte[stream.Length]; int bytesRead = await stream.ReadAsync(image, 0, (int)stream.Length); if (bytesRead != stream.Length) { // If this happens too much, we should try looping rather than reading the whole file in one shot. this.AddUserMessage("Unable to load file."); return; } } // Sanity checks. FileValidator validator = new FileValidator(image, this); if (!validator.IsValid()) { this.AddUserMessage("This file is corrupt. It would render your PCM unusable."); return; } UInt32 kernelVersion = 0; bool needUnlock; int keyAlgorithm = 1; UInt32 pcmOperatingSystemId = 0; bool needToCheckOperatingSystem = writeType != WriteType.Full; this.AddUserMessage("Requesting operating system ID..."); Response <uint> osidResponse = await this.Vehicle.QueryOperatingSystemId(this.cancellationTokenSource.Token); if (osidResponse.Status == ResponseStatus.Success) { pcmOperatingSystemId = osidResponse.Value; PcmInfo info = new PcmInfo(pcmOperatingSystemId); keyAlgorithm = info.KeyAlgorithm; needUnlock = true; if (needToCheckOperatingSystem && !validator.IsSameOperatingSystem(pcmOperatingSystemId)) { this.AddUserMessage("Flashing this file could render your PCM unusable."); return; } needToCheckOperatingSystem = false; } else { if (this.cancellationTokenSource.Token.IsCancellationRequested) { return; } this.AddUserMessage("Operating system request failed, checking for a live kernel..."); kernelVersion = await this.Vehicle.GetKernelVersion(); if (kernelVersion == 0) { this.AddUserMessage("Checking for recovery mode..."); bool recoveryMode = await this.Vehicle.IsInRecoveryMode(); if (recoveryMode) { this.AddUserMessage("PCM is in recovery mode."); needUnlock = true; } else { this.AddUserMessage("PCM is not responding to OSID, kernel version, or recovery mode checks."); this.AddUserMessage("Unlock may not work, but we'll try..."); needUnlock = true; } } else { needUnlock = false; this.AddUserMessage("Kernel version: " + kernelVersion.ToString("X8")); this.AddUserMessage("Asking kernel for the PCM's operating system ID..."); if (needToCheckOperatingSystem && !await this.Vehicle.IsSameOperatingSystemAccordingToKernel(validator, this.cancellationTokenSource.Token)) { this.AddUserMessage("Flashing this file could render your PCM unusable."); return; } needToCheckOperatingSystem = false; } } await this.Vehicle.SuppressChatter(); if (needUnlock) { bool unlocked = await this.Vehicle.UnlockEcu(keyAlgorithm); if (!unlocked) { this.AddUserMessage("Unlock was not successful."); return; } this.AddUserMessage("Unlock succeeded."); } DateTime start = DateTime.Now; CKernelWriter writer = new CKernelWriter( this.Vehicle, new Protocol(), this); await writer.Write( image, writeType, kernelVersion, validator, needToCheckOperatingSystem, this.cancellationTokenSource.Token); this.AddUserMessage("Elapsed time " + DateTime.Now.Subtract(start)); } catch (IOException exception) { this.AddUserMessage(exception.ToString()); } finally { this.currentWriteType = WriteType.None; this.Invoke((MethodInvoker) delegate() { this.EnableUserInput(); this.cancelButton.Enabled = false; }); // The token / token-source can only be cancelled once, so we need to make sure they won't be re-used. this.cancellationTokenSource = null; } } }
/// <summary> /// Read the entire contents of the flash. /// </summary> private async void readFullContents_BackgroundThread() { using (new AwayMode()) { try { this.Invoke((MethodInvoker) delegate() { this.DisableUserInput(); this.cancelButton.Enabled = true; }); if (this.Vehicle == null) { // This shouldn't be possible - it would mean the buttons // were enabled when they shouldn't be. return; } // Get the path to save the image to. string path = ""; this.Invoke((MethodInvoker) delegate() { path = this.ShowSaveAsDialog(); }); if (path == null) { this.AddUserMessage("Save canceled."); return; } this.AddUserMessage("Will save to " + path); this.cancellationTokenSource = new CancellationTokenSource(); this.AddUserMessage("Querying operating system of current PCM."); Response <uint> osidResponse = await this.Vehicle.QueryOperatingSystemId(this.cancellationTokenSource.Token); if (osidResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Operating system query failed, will retry: " + osidResponse.Status); await this.Vehicle.ExitKernel(); osidResponse = await this.Vehicle.QueryOperatingSystemId(this.cancellationTokenSource.Token); if (osidResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Operating system query failed: " + osidResponse.Status); } } PcmInfo info; if (osidResponse.Status == ResponseStatus.Success) { // Look up the information about this PCM, based on the OSID; this.AddUserMessage("OSID: " + osidResponse.Value); info = new PcmInfo(osidResponse.Value); } else { // TODO: prompt the user - 512kb or 1mb? this.AddUserMessage("Will assume this is a 512kb PCM in recovery mode."); info = new PcmInfo(0); } await this.Vehicle.SuppressChatter(); bool unlocked = await this.Vehicle.UnlockEcu(info.KeyAlgorithm); if (!unlocked) { this.AddUserMessage("Unlock was not successful."); return; } this.AddUserMessage("Unlock succeeded."); if (cancellationTokenSource.Token.IsCancellationRequested) { return; } // Do the actual reading. DateTime start = DateTime.Now; CKernelReader reader = new CKernelReader( this.Vehicle, this); Response <Stream> readResponse = await reader.ReadContents( info, cancellationTokenSource.Token); this.AddUserMessage("Elapsed time " + DateTime.Now.Subtract(start)); if (readResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Read failed, " + readResponse.Status.ToString()); return; } // Save the contents to the path that the user provided. bool success = false; do { try { this.AddUserMessage("Saving contents to " + path); readResponse.Value.Position = 0; using (Stream output = File.Open(path, FileMode.Create)) { await readResponse.Value.CopyToAsync(output); } success = true; } catch (IOException exception) { this.AddUserMessage("Unable to save file: " + exception.Message); this.AddDebugMessage(exception.ToString()); this.Invoke((MethodInvoker) delegate() { path = this.ShowSaveAsDialog(); }); if (path == null) { this.AddUserMessage("Save canceled."); return; } } } while (!success); } catch (Exception exception) { this.AddUserMessage("Read failed: " + exception.ToString()); } finally { this.Invoke((MethodInvoker) delegate() { this.EnableUserInput(); this.cancelButton.Enabled = false; }); // The token / token-source can only be cancelled once, so we need to make sure they won't be re-used. this.cancellationTokenSource = null; } } }
/// <summary> /// For testing prototype kernels. /// </summary> public async Task <bool> ExitKernel(bool kernelRunning, bool recoveryMode, CancellationToken cancellationToken, Stream unused) { try { this.device.ClearMessageQueue(); Response <byte[]> response = await LoadKernelFromFile("test-kernel.bin"); if (response.Status != ResponseStatus.Success) { // The cleanup code in the finally block will exit the kernel. return(true); } logger.AddUserMessage("Test kernel found."); UInt32 kernelVersion = 0; int keyAlgorithm = 1; // default, will work for most factory operating systems. Response <uint> osidResponse = await this.QueryOperatingSystemId(cancellationToken); if (osidResponse.Status != ResponseStatus.Success) { kernelVersion = await this.GetKernelVersion(); // TODO: Check for recovery mode. // TODO: Load the tiny kernel, then use that to load the test kernel. // Just to see whether we could potentially use that technique to assist // a user whose flash is corrupted and regular flashing isn't working. } else { PcmInfo info = new PcmInfo(osidResponse.Value); keyAlgorithm = info.KeyAlgorithm; } this.logger.AddUserMessage("Unlocking PCM..."); bool unlocked = await this.UnlockEcu(keyAlgorithm); if (!unlocked) { this.logger.AddUserMessage("Unlock was not successful."); return(false); } this.logger.AddUserMessage("Unlock succeeded."); if (cancellationToken.IsCancellationRequested) { return(false); } if (!await PCMExecute(response.Value, 0xFF8000, cancellationToken)) { logger.AddUserMessage("Failed to upload kernel to PCM"); return(false); } logger.AddUserMessage("Kernel uploaded to PCM succesfully."); //await this.InvestigateDataCorruption(cancellationToken); //await this.InvestigateKernelVersionQueryTiming(); //await this.InvestigateCrc(cancellationToken); //await this.InvestigateDataRelayCorruption(cancellationToken); await this.InvestigateFlashChipId(); return(true); } catch (Exception exception) { this.logger.AddUserMessage("Something went wrong. " + exception.Message); this.logger.AddDebugMessage(exception.ToString()); return(false); } finally { this.logger.AddUserMessage("Halting kernel."); await this.Cleanup(); } }
/// <summary> /// Read the full contents of the PCM. /// Assumes the PCM is unlocked an were ready to go /// </summary> public async Task <Response <Stream> > ReadContents(PcmInfo info, CancellationToken cancellationToken) { try { await this.vehicle.SendToolPresentNotification(); this.vehicle.ClearDeviceMessageQueue(); // switch to 4x, if possible. But continue either way. // if the vehicle bus switches but the device does not, the bus will need to time out to revert back to 1x, and the next steps will fail. if (!await this.vehicle.VehicleSetVPW4x(VpwSpeed.FourX)) { this.logger.AddUserMessage("Stopping here because we were unable to switch to 4X."); return(Response.Create(ResponseStatus.Error, (Stream)null)); } await this.vehicle.SendToolPresentNotification(); // execute read kernel Response <byte[]> response = await vehicle.LoadKernelFromFile("read-kernel.bin"); if (response.Status != ResponseStatus.Success) { logger.AddUserMessage("Failed to load kernel from file."); return(new Response <Stream>(response.Status, null)); } if (cancellationToken.IsCancellationRequested) { return(Response.Create(ResponseStatus.Cancelled, (Stream)null)); } await this.vehicle.SendToolPresentNotification(); // TODO: instead of this hard-coded 0xFF9150, get the base address from the PcmInfo object. // TODO: choose kernel at run time? Because now it's FF8000... if (!await this.vehicle.PCMExecute(response.Value, 0xFF8000, cancellationToken)) { logger.AddUserMessage("Failed to upload kernel to PCM"); return(new Response <Stream>( cancellationToken.IsCancellationRequested ? ResponseStatus.Cancelled : ResponseStatus.Error, null)); } logger.AddUserMessage("kernel uploaded to PCM succesfully. Requesting data..."); await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadMemoryBlock); int startAddress = info.ImageBaseAddress; int endAddress = info.ImageBaseAddress + info.ImageSize; int bytesRemaining = info.ImageSize; int blockSize = this.vehicle.DeviceMaxReceiveSize - 10 - 2; // allow space for the header and block checksum byte[] image = new byte[info.ImageSize]; while (startAddress < endAddress) { if (cancellationToken.IsCancellationRequested) { return(Response.Create(ResponseStatus.Cancelled, (Stream)null)); } // The read kernel needs a short message here for reasons unknown. Without it, it will RX 2 messages then drop one. await this.vehicle.ForceSendToolPresentNotification(); if (startAddress + blockSize > endAddress) { blockSize = endAddress - startAddress; } if (blockSize < 1) { this.logger.AddUserMessage("Image download complete"); break; } if (!await TryReadBlock(image, blockSize, startAddress, cancellationToken)) { this.logger.AddUserMessage( string.Format( "Unable to read block from {0} to {1}", startAddress, (startAddress + blockSize) - 1)); return(new Response <Stream>(ResponseStatus.Error, null)); } startAddress += blockSize; } await this.vehicle.Cleanup(); // Not sure why this does not get called in the finally block on successfull read? MemoryStream stream = new MemoryStream(image); return(new Response <Stream>(ResponseStatus.Success, stream)); } catch (Exception exception) { this.logger.AddUserMessage("Something went wrong. " + exception.Message); this.logger.AddDebugMessage(exception.ToString()); return(new Response <Stream>(ResponseStatus.Error, null)); } finally { // Sending the exit command at both speeds and revert to 1x. await this.vehicle.Cleanup(); } }
/// <summary> /// Read the entire contents of the flash. /// </summary> private async void readFullContents_Routine() { try { this.Invoke((MethodInvoker) delegate() { this.DisableUserInput(); this.cancelButton.Enabled = true; }); this.cancellationTokenSource = new CancellationTokenSource(); if (this.vehicle == null) { // This shouldn't be possible - it would mean the buttons // were enabled when they shouldn't be. return; } DelayDialogBox dialogBox = new DelayDialogBox(); DialogResult dialogResult = dialogBox.ShowDialog(); if (dialogResult == DialogResult.Cancel) { return; } this.AddUserMessage("Querying operating system of current PCM."); Response <uint> osidResponse = await this.vehicle.QueryOperatingSystemId(); if (osidResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Operating system query failed, will retry: " + osidResponse.Status); await this.vehicle.ExitKernel(); osidResponse = await this.vehicle.QueryOperatingSystemId(); if (osidResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Operating system query failed, giving up: " + osidResponse.Status); return; } } // Look up the information about this PCM, based on the OSID; this.AddUserMessage("OSID: " + osidResponse.Value); PcmInfo info = new PcmInfo(osidResponse.Value); await this.vehicle.SuppressChatter(); bool unlocked = await this.vehicle.UnlockEcu(info.KeyAlgorithm); if (!unlocked) { this.AddUserMessage("Unlock was not successful."); return; } this.AddUserMessage("Unlock succeeded."); if (this.cancellationTokenSource.Token.IsCancellationRequested) { return; } // Do the actual reading. Response <Stream> readResponse = await this.vehicle.ReadContents(info, this.cancellationTokenSource.Token); if (readResponse.Status != ResponseStatus.Success) { this.AddUserMessage("Read failed, " + readResponse.Status.ToString()); return; } // Get the path to save the image to. // // TODO: remember this value and offer to re-use it, in case // the read fails and the user has to try again. // string path = ""; this.Invoke((MethodInvoker) delegate() { path = this.ShowSaveAsDialog(); }); if (path == null) { this.AddUserMessage("Save canceled."); return; } this.AddUserMessage("Will save to " + path); // Save the contents to the path that the user provided. try { this.AddUserMessage("Saving contents to " + path); readResponse.Value.Position = 0; using (Stream output = File.OpenWrite(path)) { await readResponse.Value.CopyToAsync(output); } } catch (IOException exception) { this.AddUserMessage("Unable to save file: " + exception.Message); this.AddDebugMessage(exception.ToString()); } } catch (Exception exception) { this.AddUserMessage("Read failed: " + exception.ToString()); } finally { this.Invoke((MethodInvoker) delegate() { this.EnableUserInput(); this.cancelButton.Enabled = false; }); // This should not get used again. If it does, that would // indicate a bug, so let's make sure that any attempt to // use it won't go un-noticed. this.cancellationTokenSource = null; } }