blob: a02eafa1f8beb4977053f88d25658f65b0ca8677 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file contains structures used to facilitate EC firmware updates
// over USB. Note that many contents in this file are copied from EC overlay,
// and it might be trickier to include them directly.
//
// The firmware update protocol consists of two phases: connection
// establishment and actual image transfer.
//
// Image transfer is done in 1K blocks. The host supplying the image
// encapsulates blocks in PDUs by prepending a header including the flash
// offset where the block is destined and its digest.
//
// The EC device responds to each PDU with a confirmation which is 1 byte
// response. Zero value means success, non zero value is the error code
// reported by EC.
//
// To establish the connection, the host sends a different PDU, which
// contains no data and is destined to offset 0. Receiving such a PDU
// signals the EC that the host intends to transfer a new image.
//
// The connection establishment response is described by the
// FirstResponsePDU structure below.
#ifndef HAMMERD_UPDATE_FW_H_
#define HAMMERD_UPDATE_FW_H_
#include <stdint.h>
#include <string>
#include <vector>
#include <base/macros.h>
#include "hammerd/usb_utils.h"
namespace hammerd {
const int kUpdateProtocolVersion = 6;
const uint32_t kUpdateDoneCmd = 0xB007AB1E;
const uint32_t kUpdateExtraCmd = 0xB007AB1F;
enum class FirstResponsePDUHeaderType {
kCR50 = 0,
kCommon = 1,
};
// The extra vendor subcommand.
enum class UpdateExtraCommand : uint16_t {
kImmediateReset = 0,
kJumpToRW = 1,
kStayInRO = 2,
kUnlockRW = 3,
};
// This is the frame format the host uses when sending update PDUs over USB.
//
// The PDUs are up to 1K bytes in size, they are fragmented into USB chunks of
// 64 bytes each and reassembled on the receive side before being passed to
// the flash update function.
//
// The flash update function receives the unframed PDU body, and puts its reply
// into the same buffer the PDU was in.
struct UpdateFrameHeader {
uint32_t block_size; // Total frame size, including this field.
uint32_t block_digest;
uint32_t block_base;
};
// Response to the connection establishment request.
//
// When responding to the very first packet of the update sequence, the
// original USB update implementation was responding with a four byte value,
// just as to any other block of the transfer sequence.
//
// It became clear that there is a need to be able to enhance the update
// protocol, while staying backwards compatible.
//
// All newer protocol versions (starting with version 2) respond to the very
// first packet with an 8 byte or larger response, where the first 4 bytes are
// a version specific data, and the second 4 bytes - the protocol version
// number.
//
// This way the host receiving of a four byte value in response to the first
// packet is considered an indication of the target running the 'legacy'
// protocol, version 1. Receiving of an 8 byte or longer response would
// communicates the protocol version in the second 4 bytes.
struct FirstResponsePDU {
uint32_t return_value;
// The below fields are present in versions 2 and up.
// Type of header following (one of first_response_pdu_header_type)
uint16_t header_type;
uint16_t protocol_version; // Must be kUpdateProtocolVersion
uint32_t maximum_pdu_size; // Maximum PDU size
uint32_t flash_protection; // Flash protection status
uint32_t offset; // Offset of the other region
char version[32]; // Version string of the other region
int32_t min_rollback; // Minimum rollback version that RO will accept
uint32_t key_version; // RO public key version
};
// This array describes all four sections of the new image.
struct SectionInfo {
std::string name;
uint32_t offset;
uint32_t size;
char version[32];
int32_t rollback;
int32_t key_version;
explicit SectionInfo(std::string name);
};
// Implement the core logic of updating firmware.
// It contains the data of the original transfer_descriptor.
class FirmwareUpdater {
public:
FirmwareUpdater();
~FirmwareUpdater();
// Scans the new image and retrieve versions of RO and RW sections.
bool LoadImage(const std::string& image);
// Prints the information of the RO and RW sections.
void ShowHeadersVersions();
// Transfer the image to the target section.
bool TransferImage(const std::string& section_name);
// Send the external command through USB. The format of the payload is:
// 4 bytes 4 bytes 4 bytes 2 bytes variable size
// +-----------+--------------+---------------+-----------+------~~~-------+
// + total size| block digest | EXT_CMD | Vend. sub.| data |
// +-----------+--------------+---------------+-----------+------~~~-------+
//
// Where 'Vend. sub' is the vendor subcommand, and data field is subcommand
// dependent. The target tells between update PDUs and encapsulated vendor
// subcommands by looking at the EXT_CMD value - it is kUpdateExtraCmd and
// as such is guaranteed not to be a valid update PDU destination address.
bool SendSubcommand(UpdateExtraCommand subcommand);
private:
// Setup the connection with the EC firmware by sending the first PDU.
// Returns true if successfully setup the connection.
bool SendFirstPDU();
// Indicate to the target that update image transfer has been completed. Upon
// receiveing of this message the target state machine transitions into the
// 'rx_idle' state. The host may send an extension command to reset the target
// after this.
void SendDone();
bool TransferSection(const uint8_t* data_ptr,
uint32_t section_addr,
size_t data_len);
bool TransferBlock(UpdateFrameHeader* ufh,
const uint8_t* transfer_data_ptr,
size_t payload_size);
// The USB endpoint to the hammer EC.
UsbEndpoint uep_;
// The information of the first response PDU.
FirstResponsePDU targ_;
// The image data to be updated.
std::string image_;
// The information of the RO and RW sections in the image data.
std::vector<SectionInfo> sections_;
DISALLOW_COPY_AND_ASSIGN(FirmwareUpdater);
};
} // namespace hammerd
#endif // HAMMERD_UPDATE_FW_H_