Beispiel #1
0
        private UInt32 GetTotalSize(FlashChip chip, BlockType relevantBlocks)
        {
            UInt32 result = 0;

            foreach (MemoryRange range in chip.MemoryRanges)
            {
                if (this.ShouldProcess(range, relevantBlocks))
                {
                    result += range.Size;
                }
            }

            return(result);
        }
Beispiel #2
0
        /// <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 the calibration blocks.
        /// </summary>
        private async Task <bool> Write(CancellationToken cancellationToken, byte[] image)
        {
            await this.vehicle.SendToolPresentNotification();

            BlockType relevantBlocks;

            switch (this.writeTypeIpc)
            {
            case WriteTypeIpc.Ipc:
                relevantBlocks = BlockType.IPC;
                break;

            case WriteTypeIpc.Ipc1:
                relevantBlocks = BlockType.IPC1;
                break;

            default:
                throw new InvalidDataException("Unsuppported operation type: " + this.writeTypeIpc.ToString());
            }



            // Which flash chip?
            await this.vehicle.SendToolPresentNotification();

            UInt32 chipId;

            if (image.Length == 112 * 1024)
            {
                chipId = 0x6D696B65;
            }
            else
            {
                chipId = 0x4D696B65;
            }
            FlashChip flashChip = FlashChip.Create(chipId, this.logger);

            logger.AddUserMessage("Flash chip: " + flashChip.ToString());

            if (flashChip.Size != image.Length)
            {
                this.logger.AddUserMessage(string.Format("File size {0:n0} does not match Flash Chip size {1:n0}!", image.Length, flashChip.Size));
                await this.vehicle.Cleanup();

                return(false);
            }


            // Erase and rewrite the required memory ranges.
            DateTime startTime      = DateTime.Now;
            UInt32   totalSize      = this.GetTotalSize(flashChip, relevantBlocks);
            UInt32   bytesRemaining = totalSize;

            foreach (MemoryRange range in flashChip.MemoryRanges)
            {
                // We'll send a tool-present message during the erase request.
                if (!this.ShouldProcess(range, relevantBlocks))
                {
                    continue;
                }

                this.logger.AddUserMessage(
                    string.Format(
                        "Processing range {0:X6}-{1:X6}",
                        range.Address,
                        range.Address + (range.Size - 1)));


                if (this.writeTypeIpc == WriteTypeIpc.TestWrite)
                {
                    this.logger.AddUserMessage("Pretending to write...");
                }
                else
                {
                    this.logger.AddUserMessage("Writing...");
                }

                this.logger.AddUserMessage("Address\t% Done\tTime Remaining");



                Response <bool> writeResponse = await WriteMemoryRange(
                    range,
                    image,
                    this.writeTypeIpc == WriteTypeIpc.TestWrite,
                    startTime,
                    totalSize,
                    bytesRemaining,
                    cancellationToken);



                if (writeResponse.Value)
                {
                    bytesRemaining -= range.Size;
                }
            }



            return(true);
        }
Beispiel #4
0
        /// <summary>
        /// Write the calibration blocks.
        /// </summary>
        private async Task <bool> Write(CancellationToken cancellationToken, byte[] image)
        {
            await this.vehicle.SendToolPresentNotification();

            BlockType relevantBlocks;

            switch (this.writeType)
            {
            case WriteType.Compare:
                relevantBlocks = BlockType.All;
                break;

            case WriteType.TestWrite:
                relevantBlocks = BlockType.Calibration;
                break;

            case WriteType.Calibration:
                relevantBlocks = BlockType.Calibration;
                break;

            case WriteType.Parameters:
                relevantBlocks = BlockType.Parameter;
                break;

            case WriteType.OsPlusCalibrationPlusBoot:
                // Overwriting parameter blocks would break the EBCM pairing,
                // which is not what most users want. They just want a new OS
                // and the calibration to go along with it.
                //
                // The cast seems redundant, but C# converts the enum values
                // to ints when it does arithmetic.
                relevantBlocks = (BlockType)(BlockType.All - BlockType.Parameter);
                break;

            case WriteType.Full:
                relevantBlocks = BlockType.All;
                break;

            case WriteType.IPC:
                relevantBlocks = BlockType.IPC;
                break;

            default:
                throw new InvalidDataException("Unsuppported operation type: " + this.writeType.ToString());
            }

            // 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());

            if (flashChip.Size != image.Length)
            {
                this.logger.AddUserMessage(string.Format("File size {0:n0} does not match Flash Chip size {1:n0}!", image.Length, flashChip.Size));
                await this.vehicle.Cleanup();

                return(false);
            }

            CKernelVerifier verifier = new CKernelVerifier(
                image,
                flashChip.MemoryRanges,
                this.vehicle,
                this.protocol,
                this.logger);

            bool allRangesMatch    = false;
            int  messageRetryCount = 0;

            await this.vehicle.SendToolPresentNotification();

            for (int attempt = 1; attempt <= 5; attempt++)
            {
                if (await verifier.CompareRanges(
                        image,
                        relevantBlocks,
                        cancellationToken))
                {
                    allRangesMatch = true;

                    // Don't stop here if the user just wants to test their cable.
                    if (this.writeType == WriteType.TestWrite)
                    {
                        if (attempt == 1)
                        {
                            this.logger.AddUserMessage("Beginning test.");
                        }
                    }
                    else
                    {
                        this.logger.AddUserMessage("All relevant ranges are identical.");
                        if (attempt > 1)
                        {
                            Utility.ReportRetryCount("Write", messageRetryCount, flashChip.Size, this.logger);
                        }
                        return(true);
                    }
                }

                // For test writes, report results after the first iteration, then we're done.
                if ((this.writeType == WriteType.TestWrite) && (attempt > 1))
                {
                    logger.AddUserMessage("Test write complete.");
                    Utility.ReportRetryCount("Write", messageRetryCount, flashChip.Size, this.logger);
                    return(true);
                }

                // Stop now if the user only requested a comparison.
                if (this.writeType == WriteType.Compare)
                {
                    this.logger.AddUserMessage("Note that mismatched Parameter blocks are to be expected.");
                    this.logger.AddUserMessage("Parameter data can change every time the PCM is used.");
                    return(true);
                }

                // Erase and rewrite the required memory ranges.
                DateTime startTime      = DateTime.Now;
                UInt32   totalSize      = this.GetTotalSize(flashChip, relevantBlocks);
                UInt32   bytesRemaining = totalSize;
                foreach (MemoryRange range in flashChip.MemoryRanges)
                {
                    // We'll send a tool-present message during the erase request.
                    if (!this.ShouldProcess(range, relevantBlocks))
                    {
                        continue;
                    }

                    this.logger.AddUserMessage(
                        string.Format(
                            "Processing range {0:X6}-{1:X6}",
                            range.Address,
                            range.Address + (range.Size - 1)));

                    if (this.writeType == WriteType.TestWrite)
                    {
                        this.logger.AddUserMessage("Pretending to erase.");
                    }
                    else
                    {
                        if (!await this.EraseMemoryRange(range, cancellationToken))
                        {
                            return(false);
                        }
                    }

                    if (this.writeType == WriteType.TestWrite)
                    {
                        this.logger.AddUserMessage("Pretending to write...");
                    }
                    else
                    {
                        this.logger.AddUserMessage("Writing...");
                    }

                    this.logger.AddUserMessage("Address\t% Done\tTime Remaining");

                    Response <bool> writeResponse = await WriteMemoryRange(
                        range,
                        image,
                        this.writeType == WriteType.TestWrite,
                        startTime,
                        totalSize,
                        bytesRemaining,
                        cancellationToken);

                    if (writeResponse.RetryCount > 0)
                    {
                        this.logger.AddUserMessage("Retry count for this block: " + writeResponse.RetryCount);
                        messageRetryCount += writeResponse.RetryCount;
                    }

                    if (writeResponse.Value)
                    {
                        bytesRemaining -= range.Size;
                    }
                }
            }

            if (allRangesMatch)
            {
                this.logger.AddUserMessage("Flash successful!");

                if (messageRetryCount > 2)
                {
                    logger.AddUserMessage("Write request messages had to be re-sent " + messageRetryCount + " times.");
                }

                return(true);
            }

            // During a test write, we will return from the middle of the loop above.
            // So if we made it here, a real write has failed.
            this.logger.AddUserMessage("===============================================");
            this.logger.AddUserMessage("THE CHANGES WERE -NOT- WRITTEN SUCCESSFULLY");
            this.logger.AddUserMessage("===============================================");

            if (this.writeType == WriteType.Calibration)
            {
                this.logger.AddUserMessage("Erasing Calibration to force recovery mode.");
                this.logger.AddUserMessage("");

                foreach (MemoryRange range in flashChip.MemoryRanges)
                {
                    if (range.Type == BlockType.Calibration)
                    {
                        await this.EraseMemoryRange(range, cancellationToken);
                    }
                }
            }

            if (cancellationToken.IsCancellationRequested)
            {
                this.logger.AddUserMessage("");
                this.logger.AddUserMessage("The operation was cancelled.");
                this.logger.AddUserMessage("This PCM is probably not usable in its current state.");
                this.logger.AddUserMessage("");
            }
            else
            {
                this.logger.AddUserMessage("This may indicate a hardware problem on the PCM.");
                this.logger.AddUserMessage("We tried, and re-tried, and it still didn't work.");
                this.logger.AddUserMessage("");
                this.logger.AddUserMessage("Please start a new thread at pcmhacking.net, and");
                this.logger.AddUserMessage("include the contents of the debug tab.");
                this.RequestDebugLogs(cancellationToken);
            }

            return(false);
        }
        /// <summary>
        /// This was used to test the kernel's CRC code.
        /// </summary>
        private async Task InvestigateCrc(CancellationToken cancellationToken)
        {
            ICollection <MemoryRange> ranges = FlashChip.Create(0x00894471, this.logger).MemoryRanges;

            logger.AddUserMessage("Requesting CRCs from PCM...");
            foreach (MemoryRange range in ranges)
            {
                this.device.ClearMessageQueue();
                bool   success = false;
                UInt32 crc     = 0;
                await this.device.SetTimeout(TimeoutScenario.ReadCrc);

                // 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 < 100; attempts++)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    if (!await this.device.SendMessage(query))
                    {
                        await Task.Delay(retryDelay);

                        continue;
                    }

                    Message response = await this.device.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.device.ClearMessageQueue();

                if (!success)
                {
                    this.logger.AddUserMessage("Unable to get CRC for memory range " + range.Address.ToString("X8") + " / " + range.Size.ToString("X8"));
                    continue;
                }

                range.ActualCrc = crc;

                this.logger.AddUserMessage(
                    string.Format(
                        "Range {0:X6}-{1:X6} - Local: {2:X8} - PCM: {3:X8} - {4}",
                        range.Address,
                        range.Address + (range.Size - 1),
                        range.DesiredCrc,
                        range.ActualCrc,
                        range.Type));
            }
        }