/// <summary> /// Compare the two operating system IDs, report on the ramifications, set a flag if the write should be halted. /// </summary> public static void ReportIpcOperatingSystems(UInt32 fileOs, UInt32 pcmOs, WriteTypeIpc writeTypeIpc, ILogger logger, out bool shouldHalt) { shouldHalt = false; if (fileOs == pcmOs) { logger.AddUserMessage("PCM and image file are both operating system " + fileOs); } else { if ((writeTypeIpc == WriteTypeIpc.OsPlusCalibrationPlusBoot) || (writeTypeIpc == WriteTypeIpc.Full)) { logger.AddUserMessage("Changing PCM to operating system " + fileOs); } else if (writeTypeIpc == WriteTypeIpc.TestWrite) { logger.AddUserMessage("PCM and image file are different operating systems."); logger.AddUserMessage("But we'll ignore that because this is just a test write."); } else { logger.AddUserMessage("Flashing this file could render your PCM unusable."); shouldHalt = true; } } }
/// <summary> /// Writes a block of data to the PCM /// Requires an unlocked PCM /// </summary> private async Task <Response <bool> > WriteBlock(byte block, byte[] data) { Message m; Message ok = new Message(new byte[] { 0x6C, DeviceId.Tool, DeviceId.Pcm, 0x7B, block }); switch (data.Length) { case 6: m = new Message(new byte[] { 0x6C, DeviceId.Pcm, DeviceId.Tool, 0x3B, block, data[0], data[1], data[2], data[3], data[4], data[5] }); break; default: logger.AddDebugMessage("Cant write block size " + data.Length); return(Response.Create(ResponseStatus.Error, false)); } if (!await this.device.SendMessage(m)) { logger.AddUserMessage("Failed to write block " + block + ", communications failure"); return(Response.Create(ResponseStatus.Error, false)); } logger.AddDebugMessage("Successful write to block " + block); return(Response.Create(ResponseStatus.Success, true)); }
/// <summary> /// Writes a block of data to the PCM /// Requires an unlocked PCM /// </summary> public async Task <Response <bool> > WriteBlock(byte block, byte[] data) { Message tx; Message ok = new Message(new byte[] { 0x6C, DeviceId.Tool, DeviceId.Pcm, 0x7B, block }); switch (data.Length) { case 6: tx = new Message(new byte[] { 0x6C, DeviceId.Pcm, DeviceId.Tool, 0x3B, block, data[0], data[1], data[2], data[3], data[4], data[5] }); break; default: logger.AddDebugMessage("Cant write block size " + data.Length); return(Response.Create(ResponseStatus.Error, false)); } Response <Message> rx = await this.device.SendRequest(tx); if (rx.Status != ResponseStatus.Success) { logger.AddUserMessage("Failed to write block " + block + ", communications failure"); return(Response.Create(ResponseStatus.Error, false)); } if (!Utility.CompareArrays(rx.Value.GetBytes(), ok.GetBytes())) { logger.AddUserMessage("Failed to write block " + block + ", PCM rejected attempt"); return(Response.Create(ResponseStatus.Error, false)); } logger.AddDebugMessage("Successful write to block " + block); return(Response.Create(ResponseStatus.Success, true)); }
public static Device CreateSerialDevice(string serialPortName, string serialPortDeviceType, ILogger logger) { try { IPort port; if (string.Equals(MockPort.PortName, serialPortName)) { port = new MockPort(logger); } else if (string.Equals(HttpPort.PortName, serialPortName)) { port = new HttpPort(logger); } else { port = new StandardPort(serialPortName); } Device device; switch (serialPortDeviceType) { case OBDXProDevice.DeviceType: device = new OBDXProDevice(port, logger); break; case AvtDevice.DeviceType: device = new AvtDevice(port, logger); break; case AvtDevice2.DeviceType: device = new AvtDevice2(port, logger); break; case MockDevice.DeviceType: device = new MockDevice(port, logger); break; case ElmDevice.DeviceType: device = new ElmDevice(port, logger); break; default: device = null; break; } if (device == null) { return(null); } return(device); } catch (Exception exception) { logger.AddUserMessage($"Unable to create {serialPortDeviceType} on {serialPortName}."); logger.AddDebugMessage(exception.ToString()); return(null); } }
/// <summary> /// Print out the number of retries, and beg the user to share. /// </summary> public static void ReportRetryCount(string operation, int retryCount, UInt32 flashChipSize, ILogger logger) { if (retryCount == 0) { logger.AddUserMessage("All " + operation.ToLower() + "-request messages succeeded on the first try. You have an excellent connection to the PCM."); } else if (retryCount < 3) { logger.AddUserMessage(operation + "-request messages had to be re-sent " + (retryCount == 1 ? "once." : "twice.")); } else { logger.AddUserMessage(operation + "-request messages had to be re-sent " + retryCount + " times."); } logger.AddUserMessage("We're not sure how much retrying is normal for a " + operation.ToLower() + " operation on a " + (flashChipSize / 1024).ToString() + "kb PCM."); logger.AddUserMessage("Please help by sharing your results in the PCM Hammer thread at pcmhacking.net."); }
/// <summary> /// Read the full contents of the PCM. /// Assumes the PCM is unlocked and we're ready to go. /// </summary> public async Task <Response <Stream> > ReadContents(CancellationToken cancellationToken) { try { // Start with known state. await this.vehicle.ForceSendToolPresentNotification(); this.vehicle.ClearDeviceMessageQueue(); // Switch to 4x, if possible. But continue either way. if (Configuration.Enable4xReadWrite) { // 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)); } } else { this.logger.AddUserMessage("4X communications disabled by configuration."); } await this.vehicle.SendToolPresentNotification(); // execute read kernel Response <byte[]> response = await vehicle.LoadKernelFromFile("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..."); // Which flash chip? await this.vehicle.SendToolPresentNotification(); UInt32 chipId = await this.vehicle.QueryFlashChipId(cancellationToken); FlashChip flashChip = FlashChip.Create(chipId, this.logger); logger.AddUserMessage("Flash chip: " + flashChip.ToString()); await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadMemoryBlock); this.logger.AddUserMessage("Address\t% Done\tTime Remaining"); byte[] image = new byte[flashChip.Size]; int retryCount = 0; int startAddress = 0; int endAddress = (int)flashChip.Size; int bytesRemaining = (int)flashChip.Size; int blockSize = this.vehicle.DeviceMaxReceiveSize - 10 - 2; // allow space for the header and block checksum DateTime startTime = DateTime.MaxValue; 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 (startTime == DateTime.MaxValue) { startTime = DateTime.Now; } Response <bool> readResponse = await TryReadBlock( image, blockSize, startAddress, startTime, cancellationToken); if (readResponse.Status != ResponseStatus.Success) { 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; retryCount += readResponse.RetryCount; } logger.AddUserMessage("Read complete."); Utility.ReportRetryCount("Read", retryCount, flashChip.Size, this.logger); logger.AddUserMessage("Starting verification..."); CKernelVerifier verifier = new CKernelVerifier( image, flashChip.MemoryRanges, this.vehicle, this.protocol, this.logger); if (await verifier.CompareRanges( image, BlockType.All, cancellationToken)) { logger.AddUserMessage("The contents of the file match the contents of the PCM."); } else { logger.AddUserMessage("##############################################################################"); logger.AddUserMessage("There are errors in the data that was read from the PCM. Do not use this file."); logger.AddUserMessage("##############################################################################"); } 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> /// Write changes to the PCM's flash memory, or just test writing (Without /// making changes) to evaluate the connection quality. /// </summary> public async Task <bool> Write( byte[] image, UInt32 kernelVersion, FileValidator validator, bool needToCheckOperatingSystem, CancellationToken cancellationToken) { bool success = false; try { // Start with known state. await this.vehicle.ForceSendToolPresentNotification(); this.vehicle.ClearDeviceMessageQueue(); // TODO: install newer version if available. if (kernelVersion == 0) { // Switch to 4x, if possible. But continue either way. if (Configuration.Enable4xReadWrite) { // 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(false); } } else { this.logger.AddUserMessage("4X communications disabled by configuration."); } Response <byte[]> response = await this.vehicle.LoadKernelFromFile("kernel.bin"); if (response.Status != ResponseStatus.Success) { logger.AddUserMessage("Failed to load kernel from file."); return(false); } if (cancellationToken.IsCancellationRequested) { return(false); } // TODO: instead of this hard-coded address, get the base address from the PcmInfo object. if (!await this.vehicle.PCMExecute(response.Value, 0xFF8000, cancellationToken)) { logger.AddUserMessage("Failed to upload kernel to PCM"); return(false); } logger.AddUserMessage("Kernel uploaded to PCM succesfully."); } // Confirm operating system match await this.vehicle.SendToolPresentNotification(); await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadProperty); Response <UInt32> osidResponse = await this.vehicle.QueryOperatingSystemIdFromKernel(cancellationToken); if (needToCheckOperatingSystem && (osidResponse.Status != ResponseStatus.Success)) { // The kernel seems broken. This shouldn't happen, but if it does, halt. this.logger.AddUserMessage("The kernel did not respond to operating system ID query."); return(false); } bool shouldHalt; Utility.ReportOperatingSystems(validator.GetOsidFromImage(), osidResponse.Value, this.writeType, this.logger, out shouldHalt); if (needToCheckOperatingSystem && shouldHalt) { return(false); } success = await this.Write(cancellationToken, image); // We only do cleanup after a successful write. // If the kernel remains running, the user can try to flash again without rebooting and reloading. // TODO: kernel version should be stored at a fixed location in the bin file. // TODO: app should check kernel version (not just "is present") and reload only if version is lower than version in kernel file. if (success) { await this.vehicle.Cleanup(); } return(success); } catch (Exception exception) { if (!success) { switch (this.writeType) { case WriteType.None: case WriteType.Compare: case WriteType.TestWrite: await this.vehicle.Cleanup(); this.logger.AddUserMessage("Something has gone wrong. Please report this error."); this.logger.AddUserMessage("Errors during comparisons or test writes indicate a"); this.logger.AddUserMessage("problem with the PCM, interface, cable, or app. Don't"); this.logger.AddUserMessage("try to do any actual writing until you are certain that"); this.logger.AddUserMessage("the underlying problem has been completely corrected."); break; default: this.logger.AddUserMessage("Something went wrong. " + exception.Message); this.logger.AddUserMessage("Do not power off the PCM! Do not exit this program!"); this.logger.AddUserMessage("Try flashing again. If errors continue, seek help online."); break; } this.logger.AddUserMessage("https://pcmhacking.net/forums/viewtopic.php?f=42&t=6080"); this.logger.AddUserMessage(string.Empty); this.logger.AddUserMessage(exception.ToString()); } return(success); } }
/// <summary> /// Write changes to the IPC's flash memory, or just test writing (Without /// making changes) to evaluate the connection quality. /// </summary> public async Task <bool> Write( byte[] image, UInt32 kernelVersion, FileValidator validator, bool needToCheckOperatingSystem, CancellationToken cancellationToken) { bool success = false; try { // Start with known state. await this.vehicle.ForceSendToolPresentNotification(); this.vehicle.ClearDeviceMessageQueue(); // TODO: install newer version if available. if (kernelVersion == 0) { // Switch to 4x, if possible. But continue either way. if (Configuration.Enable4xReadWrite) { // 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(false); } } else { this.logger.AddUserMessage("4X communications disabled by configuration."); } } ///var sweepResponse = await this.vehicle.startProgram(); // TODO: instead of this hard-coded address, get the base address from the PcmInfo object. int address1 = 0x020000; int claimedSize1 = 0x000002; if (!await this.vehicle.PCMStartProgram(address1, claimedSize1, cancellationToken)) { logger.AddUserMessage("Start Program to IPC denied"); return(false); } logger.AddUserMessage("Start Program to IPC allowed."); /// var modeResponse = await this.vehicle.requestmode34(); // TODO: instead of this hard-coded address, get the base address from the PcmInfo object. int address = 0x00E000; int claimedSize = 0x0400; if (!await this.vehicle.PCMExecute1(address, claimedSize, cancellationToken)) { logger.AddUserMessage("Upload to IPC denied"); return(false); } logger.AddUserMessage("Upload to IPC allowed."); success = await this.Write(cancellationToken, image); // We only do cleanup after a successful write. // If the kernel remains running, the user can try to flash again without rebooting and reloading. // TODO: kernel version should be stored at a fixed location in the bin file. // TODO: app should check kernel version (not just "is present") and reload only if version is lower than version in kernel file. if (success) { await this.vehicle.Cleanup(); } return(success); } catch (Exception exception) { if (!success) { switch (this.writeTypeIpc) { case WriteTypeIpc.None: case WriteTypeIpc.Compare: case WriteTypeIpc.TestWrite: await this.vehicle.Cleanup(); this.logger.AddUserMessage("Something has gone wrong. Please report this error."); this.logger.AddUserMessage("Errors during comparisons or test writes indicate a"); this.logger.AddUserMessage("problem with the PCM, interface, cable, or app. Don't"); this.logger.AddUserMessage("try to do any actual writing until you are certain that"); this.logger.AddUserMessage("the underlying problem has been completely corrected."); break; default: this.logger.AddUserMessage("Something went wrong. " + exception.Message); this.logger.AddUserMessage("Do not power off the PCM! Do not exit this program!"); this.logger.AddUserMessage("Try flashing again. If errors continue, seek help online."); break; } ///this.logger.AddUserMessage("https://pcmhacking.net/forums/viewtopic.php?f=42&t=6080"); this.logger.AddUserMessage(string.Empty); this.logger.AddUserMessage(exception.ToString()); } return(success); } }
/// <summary> /// Compare CRCs from the file to CRCs from the PCM. /// </summary> public async Task <bool> CompareRanges( IList <MemoryRange> ranges, byte[] image, BlockType blockTypes, CancellationToken cancellationToken) { logger.AddUserMessage("Calculating CRCs from file..."); this.GetCrcFromImage(); logger.AddUserMessage("Requesting CRCs from PCM..."); await this.vehicle.SendToolPresentNotification(); // The kernel will remember (and return) the CRC value of the last block it // was asked about, which leads to misleading results if you only rewrite // a single block. So we send a a bogus query to reset the last-used CRC // value in the kernel. Query <UInt32> crcReset = this.vehicle.CreateQuery <uint>( () => this.protocol.CreateCrcQuery(0, 0), (message) => this.protocol.ParseCrc(message, 0, 0), cancellationToken); await crcReset.Execute(); await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadCrc); bool successForAllRanges = true; logger.AddUserMessage("\tRange\t\tFile CRC\t\tPCM CRC\tVerdict\tPurpose"); foreach (MemoryRange range in ranges) { string formatString = "{0:X6}-{1:X6}\t{2:X8}\t{3:X8}\t{4}\t{5}"; if ((range.Type & blockTypes) == 0) { this.logger.AddUserMessage( string.Format( formatString, range.Address, range.Address + (range.Size - 1), "not needed", "not needed", "n/a", range.Type)); continue; } await this.vehicle.SendToolPresentNotification(); this.vehicle.ClearDeviceMessageQueue(); bool success = false; UInt32 crc = 0; // You might think that a shorter retry delay would speed things up, // but 1500ms delay gets CRC results in about 3.5 seconds. // A 1000ms delay resulted in 4+ second CRC responses, and a 750ms // delay resulted in 5 second CRC responses. The PCM needs to spend // its time caculating CRCs rather than responding to messages. int retryDelay = 1500; Message query = this.protocol.CreateCrcQuery(range.Address, range.Size); for (int attempts = 0; attempts < 10; attempts++) { if (cancellationToken.IsCancellationRequested) { break; } await this.vehicle.SendToolPresentNotification(); if (!await this.vehicle.SendMessage(query)) { // This delay is fast because we're waiting for the bus to be available, // rather than waiting for the PCM's CPU to finish computing the CRC as // with the other two delays below. await Task.Delay(100); continue; } Message response = await this.vehicle.ReceiveMessage(); if (response == null) { await Task.Delay(retryDelay); continue; } Response <UInt32> crcResponse = this.protocol.ParseCrc(response, range.Address, range.Size); if (crcResponse.Status != ResponseStatus.Success) { await Task.Delay(retryDelay); continue; } success = true; crc = crcResponse.Value; break; } this.vehicle.ClearDeviceMessageQueue(); if (!success) { this.logger.AddUserMessage("Unable to get CRC for memory range " + range.Address.ToString("X8") + " / " + range.Size.ToString("X8")); successForAllRanges = false; continue; } range.ActualCrc = crc; this.logger.AddUserMessage( string.Format( formatString, range.Address, range.Address + (range.Size - 1), range.DesiredCrc, range.ActualCrc, range.DesiredCrc == range.ActualCrc ? "Same" : "Different", range.Type)); } await this.vehicle.SendToolPresentNotification(); foreach (MemoryRange range in ranges) { if ((range.Type & blockTypes) == 0) { continue; } if (range.ActualCrc != range.DesiredCrc) { return(false); } } this.vehicle.ClearDeviceMessageQueue(); return(successForAllRanges); }
/// <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> /// /// </summary> /// <param name="chipId"></param> /// <returns></returns> public static IList <MemoryRange> GetMemoryRanges(UInt32 chipId, ILogger logger) { IList <MemoryRange> result = null; switch (chipId) { // This is only here as a warning to anyone adding ranges for another chip. // Please read the comments carefully. See case 0x00894471 for the real deal. case 0xFFFF4471: var unused = new MemoryRange[] { // These numbers and descriptions are straight from the intel 28F400B data sheet. // Notice that if you convert the 16 bit word sizes to decimal, they're all // half as big as the description here, in bytes, indicates. new MemoryRange(0x30000, 0x10000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x20000, 0x10000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x10000, 0x10000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x04000, 0x0C000, BlockType.Calibration), // 96kb main block new MemoryRange(0x03000, 0x01000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x02000, 0x01000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x02000, BlockType.Boot), // 16kb boot block }; return(null); // Intel 28F800B case 0x0089889D: result = new MemoryRange[] { // These addresses are for a bottom fill chip (B) in byte mode (not word) new MemoryRange(0xE0000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0xC0000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0xA0000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x80000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x60000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x40000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x20000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x08000, 0x18000, BlockType.Calibration), // 96kb main block new MemoryRange(0x06000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x04000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x04000, BlockType.Boot), // 16kb boot block }; break; // Intel 28F400B case 0x00894471: result = new MemoryRange[] { // These addresses are for a bottom fill chip (B) in byte mode (not word) new MemoryRange(0x60000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x40000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x20000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x08000, 0x18000, BlockType.Calibration), // 96kb main block new MemoryRange(0x06000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x04000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x04000, BlockType.Boot), // 16kb boot block }; break; case 0x00012258: result = new MemoryRange[] { new MemoryRange(0xF0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xE0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xD0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xC0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xB0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xA0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x90000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x80000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x70000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x60000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x50000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x40000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x30000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x20000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x10000, 0x10000, BlockType.Calibration), // 64kb calibration block new MemoryRange(0x08000, 0x08000, BlockType.Calibration), // 32kb calibration block new MemoryRange(0x06000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x04000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x04000, BlockType.Boot), // 16kb boot block }; break; default: logger.AddUserMessage( "Unsupported flash chip ID " + chipId.ToString("X8") + ". " + Environment.NewLine + "The flash memory in this PCM is not supported by this version of PCM Hammer." + Environment.NewLine + "Please look for a thread about this at pcmhacking.net, or create one if necessary." + Environment.NewLine + "We do aim to support for all flash chips over time."); return(null); } // Sanity check the memory ranges UInt32 lastStart = UInt32.MaxValue; for (int index = 0; index < result.Count; index++) { if (index == 0) { UInt32 top = result[index].Address + result[index].Size; if ((top != 512 * 1024) && (top != 1024 * 1024)) { throw new InvalidOperationException("Upper end of memory range must be 512k or 1024k."); } } if (index == result.Count - 1) { if (result[index].Address != 0) { throw new InvalidOperationException("Memory ranges must start at zero."); } } if (lastStart != UInt32.MaxValue) { if (lastStart != result[index].Address + result[index].Size) { throw new InvalidDataException("Top of this range must match base of range above."); } } lastStart = result[index].Address; } return(result); }
/// <summary> /// Factory method. Selects the memory configuration, size, and description. /// </summary> public static FlashChip Create(UInt32 chipId, ILogger logger) { IList <MemoryRange> memoryRanges = null; string description; UInt32 size; switch (chipId) { // This is only here as a warning to anyone adding ranges for another chip. // Please read the comments carefully. See case 0x00894471 for the real data. case 0xFFFF4471: var unused = new MemoryRange[] { // These numbers and descriptions are straight from the intel 28F400B data sheet. // Notice that if you convert the 16 bit word sizes to decimal, they're all // half as big as the description here, in bytes, indicates. new MemoryRange(0x30000, 0x10000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x20000, 0x10000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x10000, 0x10000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x04000, 0x0C000, BlockType.Calibration), // 96kb main block new MemoryRange(0x03000, 0x01000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x02000, 0x01000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x02000, BlockType.Boot), // 16kb boot block }; throw new InvalidOperationException("This flash chip ID was not supposed to exist in the wild."); // Intel 28F800B case 0x0089889D: size = 1024 * 1024; description = "Intel 28F800B, 1mb"; memoryRanges = new MemoryRange[] { // These addresses are for a bottom fill chip (B) in byte mode (not word) new MemoryRange(0xE0000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0xC0000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0xA0000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x80000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x60000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x40000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x20000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x08000, 0x18000, BlockType.Calibration), // 96kb main block new MemoryRange(0x06000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x04000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x04000, BlockType.Boot), // 16kb boot block }; break; // Intel 28F400B case 0x00894471: size = 512 * 1024; description = "Intel 28F400B, 512kb"; memoryRanges = new MemoryRange[] { // These addresses are for a bottom fill chip (B) in byte mode (not word) new MemoryRange(0x60000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x40000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x20000, 0x20000, BlockType.OperatingSystem), // 128kb main block new MemoryRange(0x08000, 0x18000, BlockType.Calibration), // 96kb main block new MemoryRange(0x06000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x04000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x04000, BlockType.Boot), // 16kb boot block }; break; // AM29F800BB case 0x00012258: size = 1024 * 1024; description = "AMD AM29F800BB, 1mb"; memoryRanges = new MemoryRange[] { new MemoryRange(0xF0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xE0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xD0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xC0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xB0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0xA0000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x90000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x80000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x70000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x60000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x50000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x40000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x30000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x20000, 0x10000, BlockType.OperatingSystem), // 64kb main block new MemoryRange(0x10000, 0x10000, BlockType.Calibration), // 64kb calibration block new MemoryRange(0x08000, 0x08000, BlockType.Calibration), // 32kb calibration block new MemoryRange(0x06000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x04000, 0x02000, BlockType.Parameter), // 8kb parameter block new MemoryRange(0x00000, 0x04000, BlockType.Boot), // 16kb boot block }; break; // IPC case 0x4D696B65: size = 16 * 1024; description = "IPC Calibration"; memoryRanges = new MemoryRange[] { // These addresses are for a bottom fill chip (B) in byte mode (not word) new MemoryRange(0x00000, 0x04000, BlockType.OperatingSystem), // 128kb main block }; break; // IPC1 case 0x6D696B65: size = 112 * 1024; description = "IPC OS + Calibration"; memoryRanges = new MemoryRange[] { new MemoryRange(0x00000, 0x1C000, BlockType.OperatingSystem), }; break; // Both of these have eight 8kb blocks at the low end, the rest are // 64kb. Not sure if they're actually used in any PCMs though. case 0x00898893: // Intel 2F008B3 case 0x008988C1: // Intel 2F800C3 default: string manufacturer; switch ((chipId >> 16)) { case 0x0001: manufacturer = "AMD"; break; case 0x0089: manufacturer = "Intel"; break; default: manufacturer = "Unknown"; break; } logger.AddUserMessage( "Unsupported flash chip ID " + chipId.ToString("X8") + ". Manufacturer: " + manufacturer + Environment.NewLine + "The flash memory in this PCM is not supported by this version of PCM Hammer." + Environment.NewLine + "Please look for a thread about this at pcmhacking.net, or create one if necessary." + Environment.NewLine + "We do aim to add support for all flash chips eventually."); throw new ApplicationException(); } // Sanity check the memory ranges; UInt32 lastStart = UInt32.MaxValue; string chipIdString = chipId.ToString("X8"); for (int index = 0; index < memoryRanges.Count; index++) { if (index == 0) { UInt32 top = memoryRanges[index].Address + memoryRanges[index].Size; if ((top != 16 * 1024) && (top != 112 * 1024) && (top != 512 * 1024) && (top != 1024 * 1024)) { throw new InvalidOperationException(chipIdString + " - Upper end of memory range must be 512k or 1024k, is " + top.ToString("X8")); } if (size != top) { throw new InvalidOperationException(chipIdString + " - Size does not match upper memory block."); } } if (index == memoryRanges.Count - 1) { if (memoryRanges[index].Address != 0) { throw new InvalidOperationException(chipIdString + " - Memory ranges must start at zero."); } } if (lastStart != UInt32.MaxValue) { if (lastStart != memoryRanges[index].Address + memoryRanges[index].Size) { throw new InvalidDataException(chipIdString + " - Top of range " + index + " must match base of range above."); } } lastStart = memoryRanges[index].Address; } return(new FlashChip(chipId, description, size, memoryRanges)); }
/// <summary> /// Compare CRCs from the file to CRCs from the PCM. /// </summary> public async Task <bool> CompareRanges( byte[] image, BlockType blockTypes, CancellationToken cancellationToken) { logger.AddUserMessage("Calculating CRCs from file..."); this.GetCrcFromImage(); logger.AddUserMessage("Requesting CRCs from PCM..."); await this.vehicle.SendToolPresentNotification(); // The kernel will remember (and return) the CRC value of the last block it // was asked about, which leads to misleading results if you only rewrite // a single block. So we send a a bogus query to reset the last-used CRC // value in the kernel. Query <UInt32> crcReset = this.vehicle.CreateQuery <uint>( () => this.protocol.CreateCrcQuery(0, 0), (message) => this.protocol.ParseCrc(message, 0, 0), cancellationToken); await crcReset.Execute(); await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadCrc); bool successForAllRanges = true; logger.AddUserMessage("\tRange\t\tFile CRC\t\tPCM CRC\tVerdict\tPurpose"); foreach (MemoryRange range in this.ranges) { string formatString = "{0:X6}-{1:X6}\t{2:X8}\t{3:X8}\t{4}\t{5}"; if ((range.Type & blockTypes) == 0) { this.logger.AddUserMessage( string.Format( formatString, range.Address, range.Address + (range.Size - 1), "not needed", "not needed", "n/a", range.Type)); continue; } await this.vehicle.SendToolPresentNotification(); this.vehicle.ClearDeviceMessageQueue(); bool success = false; UInt32 crc = 0; // Each poll of the pcm causes it to CRC 16kb of segment data. // When the segment sum is available it is returned. int retryDelay = 50; Message query = this.protocol.CreateCrcQuery(range.Address, range.Size); for (int attempts = 0; attempts < 20; attempts++) { if (cancellationToken.IsCancellationRequested) { break; } await this.vehicle.SendToolPresentNotification(); if (!await this.vehicle.SendMessage(query)) { continue; } Message response = await this.vehicle.ReceiveMessage(); if (response == null) { await Task.Delay(retryDelay); continue; } Response <UInt32> crcResponse = this.protocol.ParseCrc(response, range.Address, range.Size); if (crcResponse.Status != ResponseStatus.Success) { await Task.Delay(retryDelay); continue; } success = true; crc = crcResponse.Value; break; } this.vehicle.ClearDeviceMessageQueue(); if (!success) { this.logger.AddUserMessage("Unable to get CRC for memory range " + range.Address.ToString("X8") + " / " + range.Size.ToString("X8")); successForAllRanges = false; continue; } range.ActualCrc = crc; this.logger.AddUserMessage( string.Format( formatString, range.Address, range.Address + (range.Size - 1), range.DesiredCrc, range.ActualCrc, range.DesiredCrc == range.ActualCrc ? "Same" : "Different", range.Type)); } await this.vehicle.SendToolPresentNotification(); foreach (MemoryRange range in this.ranges) { if ((range.Type & blockTypes) == 0) { continue; } if (range.ActualCrc != range.DesiredCrc) { return(false); } } this.vehicle.ClearDeviceMessageQueue(); return(successForAllRanges); }