/* Copyright 2016 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.
 */

#ifndef __CROS_EC_UPDATE_FW_H
#define __CROS_EC_UPDATE_FW_H

#include <stddef.h>


/*
 * This file contains structures used to facilitate EC firmware updates
 * over USB (and over TPM for cr50).
 *
 * 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
 * first_response_pdu structure below.
 */

#define UPDATE_PROTOCOL_VERSION 6

/*
 * This is the format of the update PDU header.
 *
 * block digest: the first four bytes of the sha1 digest of the rest of the
 *               structure (can be 0 on boards where digest is ignored).
 * block_base:   offset of this PDU into the flash SPI.
 */
struct update_command {
	uint32_t  block_digest;
	uint32_t  block_base;
	/* The actual payload goes here. */
} __packed;

/*
 * 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 (starting at the
 * cmd field below), and puts its reply into the same buffer the PDU was in.
 */
struct update_frame_header {
	uint32_t block_size; /* Total frame size, including this field. */
	struct update_command cmd;
};

/*
 * A convenience structure which allows to group together various revision
 * fields of the header created by the signer (cr50-specific).
 *
 * These fields are compared when deciding if versions of two images are the
 * same or when deciding which one of the available images to run.
 */
struct signed_header_version {
	uint32_t minor;
	uint32_t major;
	uint32_t epoch;
};

/*
 * 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 first_response_pdu {
	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;

	/* Must be UPDATE_PROTOCOL_VERSION */
	uint16_t protocol_version;

	/* In version 6 and up, a board-specific header follows. */
	union {
		/* cr50 (header_type = UPDATE_HEADER_TYPE_CR50) */
		struct {
			/* The below fields are present in versions 3 and up. */
			uint32_t  backup_ro_offset;
			uint32_t  backup_rw_offset;

			/* The below fields are present in versions 4 and up. */
			/*
			 * Versions of the currently active RO and RW sections.
			 */
			struct signed_header_version shv[2];

			/* The below fields are present in versions 5 and up */
			/* keyids of the currently active RO and RW sections. */
			uint32_t keyid[2];
		} cr50;
		/* Common code (header_type = UPDATE_HEADER_TYPE_COMMON) */
		struct {
			/* Maximum PDU size */
			uint32_t maximum_pdu_size;

			/* Flash protection status */
			uint32_t flash_protection;

			/* Offset of the other region */
			uint32_t offset;

			/* Version string of the other region */
			char version[32];

			/* Minimum rollback version that RO will accept */
			int32_t min_rollback;

			/* RO public key version */
			uint32_t key_version;
		} common;
	};
};

enum first_response_pdu_header_type {
	UPDATE_HEADER_TYPE_CR50 = 0, /* Must be 0 for backwards compatibility */
	UPDATE_HEADER_TYPE_COMMON = 1,
};

/* TODO: Handle this in update_fw.c, not usb_update.c */
#define UPDATE_DONE          0xB007AB1E
#define UPDATE_EXTRA_CMD     0xB007AB1F

enum update_extra_command {
	UPDATE_EXTRA_CMD_IMMEDIATE_RESET = 0,
	UPDATE_EXTRA_CMD_JUMP_TO_RW = 1,
	UPDATE_EXTRA_CMD_STAY_IN_RO = 2,
	UPDATE_EXTRA_CMD_UNLOCK_RW = 3,
	UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK = 4,
	UPDATE_EXTRA_CMD_INJECT_ENTROPY = 5,
	UPDATE_EXTRA_CMD_PAIR_CHALLENGE = 6,
};

/*
 * Pair challenge (from host), note that the packet, with header, must fit
 * in a single USB packet (64 bytes), so its maximum length is 50 bytes.
 */
struct pair_challenge {
	uint8_t host_public[32]; /* X22519 public key from host */
	uint8_t nonce[16]; /* nonce to be used for HMAC */
};

/*
 * Pair challenge response (from device).
 */
struct pair_challenge_response {
	uint8_t status; /* = EC_RES_SUCCESS */
	uint8_t device_public[32]; /* X22519 device public key of device */
	/*
	 * Truncated output of
	 * HMAC_SHA256(x25519(device_private, host_public), nonce)
	 */
	uint8_t authenticator[16];
} __packed;

void fw_update_command_handler(void *body,
			       size_t cmd_size,
			       size_t *response_size);

/* Used to tell fw update the update ran successfully and is finished */
void fw_update_complete(void);

/* Verify integrity of the PDU received. */
int update_pdu_valid(struct update_command *cmd_body, size_t cmd_size);

/* Various update command return values. */
enum {
	UPDATE_SUCCESS = 0,
	UPDATE_BAD_ADDR = 1,
	UPDATE_ERASE_FAILURE = 2,
	UPDATE_DATA_ERROR = 3,
	UPDATE_WRITE_FAILURE = 4,
	UPDATE_VERIFY_ERROR = 5,
	UPDATE_GEN_ERROR = 6,
	UPDATE_MALLOC_ERROR = 7,
	UPDATE_ROLLBACK_ERROR = 8,
	UPDATE_RATE_LIMIT_ERROR = 9,
	UPDATE_RWSIG_BUSY = 10,
};

#endif  /* ! __CROS_EC_UPDATE_FW_H */
