Exemple #1
0
 /// <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;
         }
     }
 }
Exemple #2
0
        /// <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));
        }
Exemple #3
0
        /// <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);
            }
        }
Exemple #5
0
        /// <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);
        }
Exemple #10
0
        /// <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();
            }
        }
Exemple #11
0
        /// <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);
        }
Exemple #12
0
        /// <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);
        }