aboutsummaryrefslogtreecommitdiffstats
path: root/flashrom.c
diff options
context:
space:
mode:
Diffstat (limited to 'flashrom.c')
-rw-r--r--flashrom.c309
1 files changed, 304 insertions, 5 deletions
diff --git a/flashrom.c b/flashrom.c
index 273eb997..3be3d321 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -409,6 +409,7 @@ const struct programmer_entry programmer_table[] = {
#define SHUTDOWN_MAXFN 32
static int shutdown_fn_count = 0;
+/** @private */
struct shutdown_func_data {
int (*func) (void *data);
void *data;
@@ -1835,7 +1836,7 @@ static int erase_block(struct flashctx *const flashctx,
* @return 0 on success,
* 1 if all available erase functions failed.
*/
-int erase_by_layout(struct flashctx *const flashctx)
+static int erase_by_layout(struct flashctx *const flashctx)
{
struct walk_info info = { 0 };
return walk_by_layout(flashctx, &info, &erase_block);
@@ -1946,8 +1947,8 @@ _free_ret:
* @return 0 on success,
* 1 if anything has gone wrong.
*/
-int write_by_layout(struct flashctx *const flashctx,
- void *const curcontents, const void *const newcontents)
+static int write_by_layout(struct flashctx *const flashctx,
+ void *const curcontents, const void *const newcontents)
{
struct walk_info info;
info.curcontents = curcontents;
@@ -1968,8 +1969,8 @@ int write_by_layout(struct flashctx *const flashctx,
* 1 if reading failed,
* 3 if the contents don't match.
*/
-int verify_by_layout(struct flashctx *const flashctx,
- void *const curcontents, const uint8_t *const newcontents)
+static int verify_by_layout(struct flashctx *const flashctx,
+ void *const curcontents, const uint8_t *const newcontents)
{
const struct flashrom_layout *const layout = get_layout(flashctx);
@@ -2484,3 +2485,301 @@ out:
free(newcontents);
return ret;
}
+
+/** @private */
+static int prepare_flash_access(struct flashctx *const flash,
+ const bool read_it, const bool write_it,
+ const bool erase_it, const bool verify_it)
+{
+ if (chip_safety_check(flash, flash->flags.force, read_it, write_it, erase_it, verify_it)) {
+ msg_cerr("Aborting.\n");
+ return 1;
+ }
+
+ if (flash->layout == get_global_layout() && normalize_romentries(flash)) {
+ msg_cerr("Requested regions can not be handled. Aborting.\n");
+ return 1;
+ }
+
+ if (map_flash(flash) != 0)
+ return 1;
+
+ /* Given the existence of read locks, we want to unlock for read,
+ erase and write. */
+ if (flash->chip->unlock)
+ flash->chip->unlock(flash);
+
+ return 0;
+}
+
+/** @private */
+static void finalize_flash_access(struct flashctx *const flash)
+{
+ unmap_flash(flash);
+}
+
+/**
+ * @addtogroup flashrom-flash
+ * @{
+ */
+
+/**
+ * @brief Erase the specified ROM chip.
+ *
+ * If a layout is set in the given flash context, only included regions
+ * will be erased.
+ *
+ * @param flashctx The context of the flash chip to erase.
+ * @return 0 on success.
+ */
+int flashrom_flash_erase(struct flashctx *const flashctx)
+{
+ if (prepare_flash_access(flashctx, false, false, true, false))
+ return 1;
+
+ const int ret = erase_by_layout(flashctx);
+
+ finalize_flash_access(flashctx);
+
+ return ret;
+}
+
+/** @} */ /* end flashrom-flash */
+
+/**
+ * @defgroup flashrom-ops Operations
+ * @{
+ */
+
+/**
+ * @brief Read the current image from the specified ROM chip.
+ *
+ * If a layout is set in the specified flash context, only included regions
+ * will be read.
+ *
+ * @param flashctx The context of the flash chip.
+ * @param buffer Target buffer to write image to.
+ * @param buffer_len Size of target buffer in bytes.
+ * @return 0 on success,
+ * 2 if buffer_len is too short for the flash chip's contents,
+ * or 1 on any other failure.
+ */
+int flashrom_image_read(struct flashctx *const flashctx, void *const buffer, const size_t buffer_len)
+{
+ const size_t flash_size = flashctx->chip->total_size * 1024;
+
+ if (flash_size > buffer_len)
+ return 2;
+
+ if (prepare_flash_access(flashctx, true, false, false, false))
+ return 1;
+
+ msg_cinfo("Reading flash... ");
+
+ int ret = 1;
+ if (read_by_layout(flashctx, buffer)) {
+ msg_cerr("Read operation failed!\n");
+ msg_cinfo("FAILED.\n");
+ goto _finalize_ret;
+ }
+ msg_cinfo("done.\n");
+ ret = 0;
+
+_finalize_ret:
+ finalize_flash_access(flashctx);
+ return ret;
+}
+
+static void combine_image_by_layout(const struct flashctx *const flashctx,
+ uint8_t *const newcontents, const uint8_t *const oldcontents)
+{
+ const struct flashrom_layout *const layout = get_layout(flashctx);
+
+ size_t i;
+ for (i = 0; i < layout->num_entries; ++i) {
+ if (layout->entries[i].included)
+ continue;
+
+ const chipoff_t region_start = layout->entries[i].start;
+ const chipsize_t region_len = layout->entries[i].end - layout->entries[i].start + 1;
+
+ memcpy(newcontents + region_start, oldcontents + region_start, region_len);
+ }
+}
+
+/**
+ * @brief Write the specified image to the ROM chip.
+ *
+ * If a layout is set in the specified flash context, only erase blocks
+ * containing included regions will be touched.
+ *
+ * @param flashctx The context of the flash chip.
+ * @param buffer Source buffer to read image from.
+ * @param buffer_len Size of source buffer in bytes.
+ * @return 0 on success,
+ * 4 if buffer_len doesn't match the size of the flash chip,
+ * 3 if write was tried but nothing has changed,
+ * 2 if write failed and flash contents changed,
+ * or 1 on any other failure.
+ */
+int flashrom_image_write(struct flashctx *const flashctx, void *const buffer, const size_t buffer_len)
+{
+ const size_t flash_size = flashctx->chip->total_size * 1024;
+ const bool verify_all = flashctx->flags.verify_whole_chip;
+ const bool verify = flashctx->flags.verify_after_write;
+
+ if (buffer_len != flash_size)
+ return 4;
+
+ int ret = 1;
+
+ uint8_t *const newcontents = buffer;
+ uint8_t *const curcontents = malloc(flash_size);
+ uint8_t *oldcontents = NULL;
+ if (verify_all)
+ oldcontents = malloc(flash_size);
+ if (!curcontents || (verify_all && !oldcontents)) {
+ msg_gerr("Out of memory!\n");
+ goto _free_ret;
+ }
+
+#if CONFIG_INTERNAL == 1
+ if (programmer == PROGRAMMER_INTERNAL && cb_check_image(newcontents, flash_size) < 0) {
+ if (flashctx->flags.force_boardmismatch) {
+ msg_pinfo("Proceeding anyway because user forced us to.\n");
+ } else {
+ msg_perr("Aborting. You can override this with "
+ "-p internal:boardmismatch=force.\n");
+ goto _free_ret;
+ }
+ }
+#endif
+
+ if (prepare_flash_access(flashctx, false, true, false, verify))
+ goto _free_ret;
+
+ /*
+ * Read the whole chip to be able to check whether regions need to be
+ * erased and to give better diagnostics in case write fails.
+ * The alternative is to read only the regions which are to be
+ * preserved, but in that case we might perform unneeded erase which
+ * takes time as well.
+ */
+ msg_cinfo("Reading old flash chip contents... ");
+ if (verify_all) {
+ if (flashctx->chip->read(flashctx, oldcontents, 0, flash_size)) {
+ msg_cinfo("FAILED.\n");
+ goto _finalize_ret;
+ }
+ memcpy(curcontents, oldcontents, flash_size);
+ } else {
+ if (read_by_layout(flashctx, curcontents)) {
+ msg_cinfo("FAILED.\n");
+ goto _finalize_ret;
+ }
+ }
+ msg_cinfo("done.\n");
+
+ if (write_by_layout(flashctx, curcontents, newcontents)) {
+ msg_cerr("Uh oh. Erase/write failed. ");
+ ret = 2;
+ if (verify_all) {
+ msg_cerr("Checking if anything has changed.\n");
+ msg_cinfo("Reading current flash chip contents... ");
+ if (!flashctx->chip->read(flashctx, curcontents, 0, flash_size)) {
+ msg_cinfo("done.\n");
+ if (!memcmp(oldcontents, curcontents, flash_size)) {
+ nonfatal_help_message();
+ goto _finalize_ret;
+ }
+ msg_cerr("Apparently at least some data has changed.\n");
+ } else
+ msg_cerr("Can't even read anymore!\n");
+ emergency_help_message();
+ goto _finalize_ret;
+ } else {
+ msg_cerr("\n");
+ }
+ emergency_help_message();
+ goto _finalize_ret;
+ }
+
+ /* Verify only if we actually changed something. */
+ if (verify && !all_skipped) {
+ const struct flashrom_layout *const layout_bak = flashctx->layout;
+
+ msg_cinfo("Verifying flash... ");
+
+ /* Work around chips which need some time to calm down. */
+ programmer_delay(1000*1000);
+
+ if (verify_all) {
+ combine_image_by_layout(flashctx, newcontents, oldcontents);
+ flashctx->layout = NULL;
+ }
+ ret = verify_by_layout(flashctx, curcontents, newcontents);
+ flashctx->layout = layout_bak;
+ /* If we tried to write, and verification now fails, we
+ might have an emergency situation. */
+ if (ret)
+ emergency_help_message();
+ else
+ msg_cinfo("VERIFIED.\n");
+ } else {
+ /* We didn't change anything. */
+ ret = 0;
+ }
+
+_finalize_ret:
+ finalize_flash_access(flashctx);
+_free_ret:
+ free(oldcontents);
+ free(curcontents);
+ return ret;
+}
+
+/**
+ * @brief Verify the ROM chip's contents with the specified image.
+ *
+ * If a layout is set in the specified flash context, only included regions
+ * will be verified.
+ *
+ * @param flashctx The context of the flash chip.
+ * @param buffer Source buffer to verify with.
+ * @param buffer_len Size of source buffer in bytes.
+ * @return 0 on success,
+ * 3 if the chip's contents don't match,
+ * 2 if buffer_len doesn't match the size of the flash chip,
+ * or 1 on any other failure.
+ */
+int flashrom_image_verify(struct flashctx *const flashctx, const void *const buffer, const size_t buffer_len)
+{
+ const size_t flash_size = flashctx->chip->total_size * 1024;
+
+ if (buffer_len != flash_size)
+ return 2;
+
+ const uint8_t *const newcontents = buffer;
+ uint8_t *const curcontents = malloc(flash_size);
+ if (!curcontents) {
+ msg_gerr("Out of memory!\n");
+ return 1;
+ }
+
+ int ret = 1;
+
+ if (prepare_flash_access(flashctx, false, false, false, true))
+ goto _free_ret;
+
+ msg_cinfo("Verifying flash... ");
+ ret = verify_by_layout(flashctx, curcontents, newcontents);
+ if (!ret)
+ msg_cinfo("VERIFIED.\n");
+
+ finalize_flash_access(flashctx);
+_free_ret:
+ free(curcontents);
+ return ret;
+}
+
+/** @} */ /* end flashrom-ops */