예제 #1
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);
        }
예제 #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();
            }
        }
예제 #3
0
        /// <summary>
        /// Write the calibration blocks.
        /// </summary>
        private async Task <bool> Write(CancellationToken cancellationToken, byte[] image, WriteType writeType)
        {
            await this.vehicle.SendToolPresentNotification();

            BlockType relevantBlocks;

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

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

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

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

            case WriteType.OsAndCalibration:
                relevantBlocks = BlockType.Calibration | BlockType.OperatingSystem;
                break;

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

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

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

            await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadProperty);

            Query <UInt32> chipIdQuery = this.vehicle.CreateQuery <UInt32>(
                this.protocol.CreateFlashMemoryTypeQuery,
                this.protocol.ParseFlashMemoryType,
                cancellationToken);
            Response <UInt32> chipIdResponse = await chipIdQuery.Execute();

            if (chipIdResponse.Status != ResponseStatus.Success)
            {
                logger.AddUserMessage("Unable to determine which flash chip is in this PCM");
                return(false);
            }

            // TODO: Move the device types lookup to a function in Misc/FlashChips.cs
            // known chips in the P01 and P59
            // http://ftp1.digi.com/support/documentation/jtag_v410_flashes.pdf
            string Amd     = "AMD";               // 0001
            string Intel   = "Intel";             // 0089
            string I4471   = "28F400B5-B 512Kb";  // 4471
            string A2258   = "Am29F800B 1Mbyte";  // 2258
            string I889D   = "28F800B5-B 1Mbyte"; // 889D
            string unknown = "unknown";           // default case

            logger.AddUserMessage("Flash memory ID code: " + chipIdResponse.Value.ToString("X8"));

            switch ((chipIdResponse.Value >> 16))
            {
            case 0x0001: logger.AddUserMessage("Flash memory manufactuer: " + Amd);
                break;

            case 0x0089: logger.AddUserMessage("Flash memory manufactuer: " + Intel);
                break;

            default: logger.AddUserMessage("Flash memory manufactuer: " + unknown);
                break;
            }

            switch (chipIdResponse.Value & 0xFFFF)
            {
            case 0x4471: logger.AddUserMessage("Flash memory type: " + I4471);
                break;

            case 0x2258: logger.AddUserMessage("Flash memory type: " + A2258);
                break;

            case 0x889D: logger.AddUserMessage("Flash memory type: " + I889D);
                break;

            default: logger.AddUserMessage("Flash memory type: " + unknown);
                break;
            }

            await this.vehicle.SendToolPresentNotification();

            IList <MemoryRange> ranges = FlashChips.GetMemoryRanges(chipIdResponse.Value, this.logger);

            if (ranges == null)
            {
                return(false);
            }

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

            bool allRangesMatch = false;
            bool writeAttempted = false;

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

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

                if ((writeType == WriteType.TestWrite) && (attempt > 1))
                {
                    // TODO: the app should know if any errors were encountered. The user shouldn't need to check.
                    this.logger.AddUserMessage("Test complete. Were any errors logged above?");
                    return(true);
                }

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

                writeAttempted = true;

                // Erase and rewrite the required memory ranges.
                await this.vehicle.SetDeviceTimeout(TimeoutScenario.Maximum);

                foreach (MemoryRange range in ranges)
                {
                    // We'll send a tool-present message during the erase request.
                    if ((range.ActualCrc == range.DesiredCrc) && (writeType != WriteType.TestWrite))
                    {
                        continue;
                    }

                    if ((range.Type & relevantBlocks) == 0)
                    {
                        continue;
                    }

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

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

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

                    await this.vehicle.SendToolPresentNotification();

                    await WriteMemoryRange(
                        range,
                        image,
                        writeType == WriteType.TestWrite,
                        cancellationToken);
                }
            }

            if (!writeAttempted)
            {
                this.logger.AddUserMessage("Assertion failed. WriteAttempted should be true.");
            }

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

            this.logger.AddUserMessage("===============================================");
            this.logger.AddUserMessage("THE CHANGES WERE -NOT- WRITTEN SUCCESSFULLY");
            this.logger.AddUserMessage("===============================================");

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

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

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

            return(false);
        }