/* Copyright 2018 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * A reference implementation for AP (and supporting images) firmware updater.
 */

#ifndef VBOOT_REFERENCE_FUTILITY_UPDATER_H_
#define VBOOT_REFERENCE_FUTILITY_UPDATER_H_

#include "futility.h"
#include "updater_utils.h"

/* FMAP section names. */
static const char * const FMAP_RO = "WP_RO",
		  * const FMAP_RO_FMAP = "FMAP",
		  * const FMAP_RO_FRID = "RO_FRID",
		  * const FMAP_RO_SECTION = "RO_SECTION",
		  * const FMAP_RO_CBFS = "COREBOOT",
		  * const FMAP_RO_GBB = "GBB",
		  * const FMAP_RO_GSCVD = "RO_GSCVD",
		  * const FMAP_RO_VPD = "RO_VPD",
		  * const FMAP_RW_VBLOCK_A = "VBLOCK_A",
		  * const FMAP_RW_VBLOCK_B = "VBLOCK_B",
		  * const FMAP_RW_FW_MAIN_A = "FW_MAIN_A",
		  * const FMAP_RW_FW_MAIN_B = "FW_MAIN_B",
		  * const FMAP_RW_SECTION_A = "RW_SECTION_A",
		  * const FMAP_RW_SECTION_B = "RW_SECTION_B",
		  * const FMAP_RW_FWID = "RW_FWID",
		  * const FMAP_RW_FWID_A = "RW_FWID_A",
		  * const FMAP_RW_FWID_B = "RW_FWID_B",
		  * const FMAP_RW_SHARED = "RW_SHARED",
		  * const FMAP_RW_LEGACY = "RW_LEGACY",
		  * const FMAP_RW_VPD = "RW_VPD",
		  * const FMAP_RW_DIAG_NVRAM = "DIAG_NVRAM",
		  * const FMAP_SI_DESC = "SI_DESC",
		  * const FMAP_SI_ME = "SI_ME";

struct updater_config;
struct quirk_entry {
	const char *name;
	const char *help;
	int (*apply)(struct updater_config *cfg);
	int value;
};

enum quirk_types {
	/* Platform-independent quirks */
	QUIRK_NO_CHECK_PLATFORM,
	QUIRK_NO_VERIFY,
	QUIRK_ENLARGE_IMAGE,
	QUIRK_MIN_PLATFORM_VERSION,
	QUIRK_EXTRA_RETRIES,
	/* Arch-specific quirks */
	QUIRK_EC_PARTIAL_RECOVERY,
	QUIRK_CLEAR_MRC_DATA,
	QUIRK_PRESERVE_ME,
	/* Platform-specific quirks (removed after AUE) */
	QUIRK_OVERRIDE_CUSTOM_LABEL,
	QUIRK_EVE_SMM_STORE,
	QUIRK_UNLOCK_CSME_EVE,
	QUIRK_UNLOCK_CSME,
	/* End of quirks */
	QUIRK_MAX,
};

/* Return values from QUIRK_EC_PARTIAL_RECOVERY. */
enum {
	EC_RECOVERY_FULL = 0,  /* Must be 0 as default value of quirks. */
	EC_RECOVERY_RO,
	EC_RECOVERY_DONE
};

enum try_update_type {
	TRY_UPDATE_OFF = 0,
	TRY_UPDATE_AUTO,
	TRY_UPDATE_DEFERRED_HOLD,
	TRY_UPDATE_DEFERRED_APPLY,
};

struct updater_config {
	struct firmware_image image, image_current;
	struct firmware_image ec_image;
	struct dut_property dut_properties[DUT_PROP_MAX];
	struct quirk_entry quirks[QUIRK_MAX];
	struct u_archive *archive;
	struct tempfile tempfiles;
	enum try_update_type try_update;
	int force_update;
	int legacy_update;
	int factory_update;
	int check_platform;
	int use_diff_image;
	int do_verify;
	int verbosity;
	const char *emulation;
	char *emulation_programmer;
	const char *original_programmer;
	const char *prepare_ctrl_name;
	int override_gbb_flags;
	uint32_t gbb_flags;
	bool detect_model;
	bool dut_is_remote;
	bool output_only;
};

struct updater_config_arguments {
	char *image, *ec_image;
	char *archive, *quirks, *mode;
	const char *programmer, *write_protection;
	char *model;
	char *emulation, *sys_props;
	char *output_dir;
	char *repack, *unpack;
	int is_factory, try_update, force_update, do_manifest, host_only;
	int fast_update;
	int verbosity;
	int override_gbb_flags;
	int detect_servo;
	int use_flash;
	uint32_t gbb_flags;
	bool detect_model_only;
	bool unlock_me;
};

/*
 * Shared getopt arguments controlling flash behaviour.
 * These are shared by multiple commands.
 */
enum {
	OPT_CCD = 0x100,
	OPT_EMULATE,
	OPT_SERVO,
	OPT_SERVO_PORT,
};

#ifdef USE_FLASHROM
#define SHARED_FLASH_ARGS_SHORTOPTS "p:"

#define SHARED_FLASH_ARGS_LONGOPTS                                             \
	{"programmer", 1, NULL, 'p'},                                          \
	{"ccd_without_servod", 2, NULL, OPT_CCD},                              \
	{"servo", 0, NULL, OPT_SERVO},                                         \
	{"servo_port", 1, NULL, OPT_SERVO_PORT},                               \
	{"emulate", 1, NULL, OPT_EMULATE},

#define SHARED_FLASH_ARGS_HELP                                                 \
	"-p, --programmer=PRG\tChange AP (host) flashrom programmer\n"         \
	"    --ccd_without_servod[=SERIAL] \tFlash via CCD without servod\n"   \
	"    --emulate=FILE  \tEmulate system firmware using file\n"           \
	"    --servo         \tFlash using Servo (v2, v4, micro, ...)\n"       \
	"    --servo_port=PRT\tOverride servod port, implies --servo\n"
#else
#define SHARED_FLASH_ARGS_HELP
#define SHARED_FLASH_ARGS_LONGOPTS
#define SHARED_FLASH_ARGS_SHORTOPTS
#endif /* USE_FLASHROM */

struct patch_config {
	char *rootkey;
	char *vblock_a;
	char *vblock_b;
	char *gscvd;
};

struct model_config {
	char *name;
	char *image, *ec_image;
	struct patch_config patches;
	bool has_custom_label;
};

struct manifest {
	int num;
	struct model_config *models;
	struct u_archive *archive;
	int default_model;
};

enum updater_error_codes {
	UPDATE_ERR_DONE,
	UPDATE_ERR_NEED_RO_UPDATE,
	UPDATE_ERR_NO_IMAGE,
	UPDATE_ERR_SYSTEM_IMAGE,
	UPDATE_ERR_INVALID_IMAGE,
	UPDATE_ERR_SET_COOKIES,
	UPDATE_ERR_WRITE_FIRMWARE,
	UPDATE_ERR_PLATFORM,
	UPDATE_ERR_TARGET,
	UPDATE_ERR_ROOT_KEY,
	UPDATE_ERR_TPM_ROLLBACK,
	UPDATE_ERR_UNLOCK_CSME,
	UPDATE_ERR_UNKNOWN,
};

/* Messages explaining enum updater_error_codes. */
extern const char * const updater_error_messages[];

/*
 * Returns a valid root key from GBB header, or NULL on failure.
 */
const struct vb2_packed_key *get_rootkey(
		const struct vb2_gbb_header *gbb);

/*
 * The main updater to update system firmware using the configuration parameter.
 * Returns UPDATE_ERR_DONE if success, otherwise failure.
 */
enum updater_error_codes update_firmware(struct updater_config *cfg);

/*
 * Allocates and initializes a updater_config object with default values.
 * Returns the newly allocated object, or NULL on error.
 */
struct updater_config *updater_new_config(void);

/*
 * Releases all resources in an updater configuration object.
 */
void updater_delete_config(struct updater_config *cfg);

/*
 * Handle an argument if it is a shared updater option.
 * Returns 1 if argument was used.
 */
int handle_flash_argument(struct updater_config_arguments *args, int opt,
			  char *optarg);

/**
 * Helper function to setup an allocated updater_config object.
 * Returns number of failures, or 0 on success.
 * @param[out]  updater_config,
 * @param[int]  updater_config_arguments,
 */
int updater_setup_config(struct updater_config *cfg,
			 const struct updater_config_arguments *arg);

/**
 * Helper function to determine if to perform a update.
 * Returns true to perform update otherwise false.
 * @param[in]  updater_config_arguments,
 */
bool updater_should_update(const struct updater_config_arguments *arg);

/* Prints the name and description from all supported quirks. */
void updater_list_config_quirks(const struct updater_config *cfg);

/*
 * Registers known quirks to a updater_config object.
 */
void updater_register_quirks(struct updater_config *cfg);

/* Gets the value (setting) of specified quirks from updater configuration. */
int get_config_quirk(enum quirk_types quirk, const struct updater_config *cfg);

/*
 * Gets the default quirk config string from target image name.
 * Returns a string (in same format as --quirks) to load or NULL if no quirks.
 */
const char * const updater_get_model_quirks(struct updater_config *cfg);

/*
 * Gets the quirk config string from target image CBFS.
 * Returns a string (in same format as --quirks) to load or NULL if no quirks.
 */
char * updater_get_cbfs_quirks(struct updater_config *cfg);

/*
 * Overrides the custom label config if the device was shipped with known
 * special rootkey.
 */
const struct model_config *quirk_override_custom_label(
		struct updater_config *cfg,
		const struct manifest *manifest,
		const struct model_config *model);

/* Functions from updater_archive.c */

/*
 * Opens an archive from given path.
 * The type of archive will be determined automatically.
 * Returns a pointer to reference to archive (must be released by archive_close
 * when not used), otherwise NULL on error.
 */
struct u_archive *archive_open(const char *path);

/*
 * Closes an archive reference.
 * Returns 0 on success, otherwise non-zero as failure.
 */
int archive_close(struct u_archive *ar);

/*
 * Checks if an entry (either file or directory) exists in archive.
 * Returns 1 if exists, otherwise 0
 */
int archive_has_entry(struct u_archive *ar, const char *name);

/*
 * Reads a file from archive.
 * Returns 0 on success (data and size reflects the file content),
 * otherwise non-zero as failure.
 */
int archive_read_file(struct u_archive *ar, const char *fname,
		      uint8_t **data, uint32_t *size, int64_t *mtime);

/*
 * Writes a file into archive.
 * If entry name (fname) is an absolute path (/file), always write into real
 * file system.
 * Returns 0 on success, otherwise non-zero as failure.
 */
int archive_write_file(struct u_archive *ar, const char *fname,
		       uint8_t *data, uint32_t size, int64_t mtime);

/*
 * Traverses all files within archive (directories are ignored).
 * For every entry, the path (relative the archive root) will be passed to
 * callback function, until the callback returns non-zero.
 * The arg argument will also be passed to callback.
 * Returns 0 on success otherwise non-zero as failure.
 */
int archive_walk(struct u_archive *ar, void *arg,
		 int (*callback)(const char *path, void *arg));

/*
 * Copies all entries from one archive to another.
 * Returns 0 on success, otherwise non-zero as failure.
 */
int archive_copy(struct u_archive *from, struct u_archive *to);

/*
 * Creates a new manifest object by scanning files in archive.
 * Returns the manifest on success, otherwise NULL for failure.
 */
struct manifest *new_manifest_from_archive(struct u_archive *archive);

/* Releases all resources allocated by given manifest object. */
void delete_manifest(struct manifest *manifest);

/* Prints the information of objects in manifest (models and images) in JSON. */
void print_json_manifest(const struct manifest *manifest);

/*
 * Modifies a firmware image from patch information specified in model config.
 * Returns 0 on success, otherwise number of failures.
 */
int patch_image_by_model(
		struct firmware_image *image, const struct model_config *model,
		struct u_archive *archive);

/*
 * Finds the existing model_config from manifest that best matches current
 * system (as defined by model_name).
 * Returns a model_config from manifest, or NULL if not found.
 */
const struct model_config *manifest_find_model(struct updater_config *cfg,
					       const struct manifest *manifest,
					       const char *model_name);

/*
 * Finds the first existing model_config from manifest that matches current
 * system by reading RO_FRID from the existing host firmware.
 * Returns a model_config from manifest, or NULL if not found.
 */
const struct model_config *
manifest_detect_model_from_frid(struct updater_config *cfg,
				struct manifest *manifest);

/*
 * Finds the custom label model config from the base model + system tag.
 * The system tag came from the firmware VPD section.
 * Returns the matched model_config, base if no applicable custom label data,
 * or NULL for any critical error.
 */
const struct model_config *manifest_find_custom_label_model(
		struct updater_config *cfg,
		const struct manifest *manifest,
		const struct model_config *base_model);

#endif  /* VBOOT_REFERENCE_FUTILITY_UPDATER_H_ */
