mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-27 23:22:37 +02:00
Kill doit()
No words can describe this feeling. v2: Rejoice while removing more, orphaned code (layout.c). Change-Id: Id81177c50b4410e68dcf8ebab48386a94cd9b714 Signed-off-by: Nico Huber <nico.huber@secunet.com> Reviewed-on: https://review.coreboot.org/17949 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: David Hendricks <david.hendricks@gmail.com>
This commit is contained in:
parent
1878110848
commit
899e4ec810
@ -530,24 +530,28 @@ int main(int argc, char *argv[])
|
|||||||
goto out_shutdown;
|
goto out_shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always verify write operations unless -n is used. */
|
if (layoutfile)
|
||||||
if (write_it && !dont_verify_it)
|
flashrom_layout_set(fill_flash, get_global_layout());
|
||||||
verify_it = 1;
|
|
||||||
|
|
||||||
/* Map the selected flash chip again. */
|
flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE, !!force);
|
||||||
if (map_flash(fill_flash) != 0) {
|
flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE_BOARDMISMATCH, !!force_boardmismatch);
|
||||||
ret = 1;
|
flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_AFTER_WRITE, !dont_verify_it);
|
||||||
goto out_shutdown;
|
flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, true);
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: We should issue an unconditional chip reset here. This can be
|
/* FIXME: We should issue an unconditional chip reset here. This can be
|
||||||
* done once we have a .reset function in struct flashchip.
|
* done once we have a .reset function in struct flashchip.
|
||||||
* Give the chip time to settle.
|
* Give the chip time to settle.
|
||||||
*/
|
*/
|
||||||
programmer_delay(100000);
|
programmer_delay(100000);
|
||||||
ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
|
if (read_it)
|
||||||
|
ret = do_read(fill_flash, filename);
|
||||||
|
else if (erase_it)
|
||||||
|
ret = do_erase(fill_flash);
|
||||||
|
else if (write_it)
|
||||||
|
ret = do_write(fill_flash, filename);
|
||||||
|
else if (verify_it)
|
||||||
|
ret = do_verify(fill_flash, filename);
|
||||||
|
|
||||||
unmap_flash(fill_flash);
|
|
||||||
out_shutdown:
|
out_shutdown:
|
||||||
programmer_shutdown();
|
programmer_shutdown();
|
||||||
out:
|
out:
|
||||||
|
6
flash.h
6
flash.h
@ -285,9 +285,12 @@ void print_buildinfo(void);
|
|||||||
void print_banner(void);
|
void print_banner(void);
|
||||||
void list_programmers_linebreak(int startcol, int cols, int paren);
|
void list_programmers_linebreak(int startcol, int cols, int paren);
|
||||||
int selfcheck(void);
|
int selfcheck(void);
|
||||||
int doit(struct flashctx *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it);
|
|
||||||
int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename);
|
int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename);
|
||||||
int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *filename);
|
int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *filename);
|
||||||
|
int do_read(struct flashctx *, const char *filename);
|
||||||
|
int do_erase(struct flashctx *);
|
||||||
|
int do_write(struct flashctx *, const char *const filename);
|
||||||
|
int do_verify(struct flashctx *, const char *const filename);
|
||||||
|
|
||||||
/* Something happened that shouldn't happen, but we can go on. */
|
/* Something happened that shouldn't happen, but we can go on. */
|
||||||
#define ERROR_NONFATAL 0x100
|
#define ERROR_NONFATAL 0x100
|
||||||
@ -354,7 +357,6 @@ int register_include_arg(char *name);
|
|||||||
int process_include_args(void);
|
int process_include_args(void);
|
||||||
int read_romlayout(const char *name);
|
int read_romlayout(const char *name);
|
||||||
int normalize_romentries(const struct flashctx *flash);
|
int normalize_romentries(const struct flashctx *flash);
|
||||||
int build_new_image(struct flashctx *flash, bool oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents);
|
|
||||||
void layout_cleanup(void);
|
void layout_cleanup(void);
|
||||||
|
|
||||||
/* spi.c */
|
/* spi.c */
|
||||||
|
395
flashrom.c
395
flashrom.c
@ -1374,6 +1374,7 @@ out:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int read_by_layout(struct flashctx *, uint8_t *);
|
||||||
int read_flash_to_file(struct flashctx *flash, const char *filename)
|
int read_flash_to_file(struct flashctx *flash, const char *filename)
|
||||||
{
|
{
|
||||||
unsigned long size = flash->chip->total_size * 1024;
|
unsigned long size = flash->chip->total_size * 1024;
|
||||||
@ -1391,7 +1392,7 @@ int read_flash_to_file(struct flashctx *flash, const char *filename)
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
if (flash->chip->read(flash, buf, 0, size)) {
|
if (read_by_layout(flash, buf)) {
|
||||||
msg_cerr("Read operation failed!\n");
|
msg_cerr("Read operation failed!\n");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto out_free;
|
goto out_free;
|
||||||
@ -1470,97 +1471,6 @@ static int selfcheck_eraseblocks(const struct flashchip *chip)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int erase_and_write_block_helper(struct flashctx *flash,
|
|
||||||
unsigned int start, unsigned int len,
|
|
||||||
uint8_t *curcontents,
|
|
||||||
uint8_t *newcontents,
|
|
||||||
int (*erasefn) (struct flashctx *flash,
|
|
||||||
unsigned int addr,
|
|
||||||
unsigned int len))
|
|
||||||
{
|
|
||||||
unsigned int starthere = 0, lenhere = 0;
|
|
||||||
int ret = 0, skip = 1, writecount = 0;
|
|
||||||
enum write_granularity gran = flash->chip->gran;
|
|
||||||
|
|
||||||
/* curcontents and newcontents are opaque to walk_eraseregions, and
|
|
||||||
* need to be adjusted here to keep the impression of proper abstraction
|
|
||||||
*/
|
|
||||||
curcontents += start;
|
|
||||||
newcontents += start;
|
|
||||||
msg_cdbg(":");
|
|
||||||
if (need_erase(curcontents, newcontents, len, gran)) {
|
|
||||||
msg_cdbg("E");
|
|
||||||
ret = erasefn(flash, start, len);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
if (check_erased_range(flash, start, len)) {
|
|
||||||
msg_cerr("ERASE FAILED!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Erase was successful. Adjust curcontents. */
|
|
||||||
memset(curcontents, 0xff, len);
|
|
||||||
skip = 0;
|
|
||||||
}
|
|
||||||
/* get_next_write() sets starthere to a new value after the call. */
|
|
||||||
while ((lenhere = get_next_write(curcontents + starthere,
|
|
||||||
newcontents + starthere,
|
|
||||||
len - starthere, &starthere, gran))) {
|
|
||||||
if (!writecount++)
|
|
||||||
msg_cdbg("W");
|
|
||||||
/* Needs the partial write function signature. */
|
|
||||||
ret = flash->chip->write(flash, newcontents + starthere,
|
|
||||||
start + starthere, lenhere);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
starthere += lenhere;
|
|
||||||
skip = 0;
|
|
||||||
}
|
|
||||||
if (skip)
|
|
||||||
msg_cdbg("S");
|
|
||||||
else
|
|
||||||
all_skipped = false;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int walk_eraseregions(struct flashctx *flash, int erasefunction,
|
|
||||||
int (*do_something) (struct flashctx *flash,
|
|
||||||
unsigned int addr,
|
|
||||||
unsigned int len,
|
|
||||||
uint8_t *param1,
|
|
||||||
uint8_t *param2,
|
|
||||||
int (*erasefn) (
|
|
||||||
struct flashctx *flash,
|
|
||||||
unsigned int addr,
|
|
||||||
unsigned int len)),
|
|
||||||
void *param1, void *param2)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
unsigned int start = 0;
|
|
||||||
unsigned int len;
|
|
||||||
struct block_eraser eraser = flash->chip->block_erasers[erasefunction];
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_ERASEREGIONS; i++) {
|
|
||||||
/* count==0 for all automatically initialized array
|
|
||||||
* members so the loop below won't be executed for them.
|
|
||||||
*/
|
|
||||||
len = eraser.eraseblocks[i].size;
|
|
||||||
for (j = 0; j < eraser.eraseblocks[i].count; j++) {
|
|
||||||
/* Print this for every block except the first one. */
|
|
||||||
if (i || j)
|
|
||||||
msg_cdbg(", ");
|
|
||||||
msg_cdbg("0x%06x-0x%06x", start,
|
|
||||||
start + len - 1);
|
|
||||||
if (do_something(flash, start, len, param1, param2,
|
|
||||||
eraser.block_erase)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
start += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg_cdbg("\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int check_block_eraser(const struct flashctx *flash, int k, int log)
|
static int check_block_eraser(const struct flashctx *flash, int k, int log)
|
||||||
{
|
{
|
||||||
struct block_eraser eraser = flash->chip->block_erasers[k];
|
struct block_eraser eraser = flash->chip->block_erasers[k];
|
||||||
@ -1586,71 +1496,6 @@ static int check_block_eraser(const struct flashctx *flash, int k, int log)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int erase_and_write_flash(struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents)
|
|
||||||
{
|
|
||||||
int k, ret = 1;
|
|
||||||
uint8_t *curcontents;
|
|
||||||
unsigned long size = flash->chip->total_size * 1024;
|
|
||||||
unsigned int usable_erasefunctions = count_usable_erasers(flash);
|
|
||||||
|
|
||||||
msg_cinfo("Erasing and writing flash chip... ");
|
|
||||||
curcontents = malloc(size);
|
|
||||||
if (!curcontents) {
|
|
||||||
msg_gerr("Out of memory!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* Copy oldcontents to curcontents to avoid clobbering oldcontents. */
|
|
||||||
memcpy(curcontents, oldcontents, size);
|
|
||||||
|
|
||||||
for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
|
|
||||||
if (k != 0)
|
|
||||||
msg_cinfo("Looking for another erase function.\n");
|
|
||||||
if (!usable_erasefunctions) {
|
|
||||||
msg_cinfo("No usable erase functions left.\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
msg_cdbg("Trying erase function %i... ", k);
|
|
||||||
if (check_block_eraser(flash, k, 1))
|
|
||||||
continue;
|
|
||||||
usable_erasefunctions--;
|
|
||||||
ret = walk_eraseregions(flash, k, &erase_and_write_block_helper,
|
|
||||||
curcontents, newcontents);
|
|
||||||
/* If everything is OK, don't try another erase function. */
|
|
||||||
if (!ret)
|
|
||||||
break;
|
|
||||||
/* Write/erase failed, so try to find out what the current chip
|
|
||||||
* contents are. If no usable erase functions remain, we can
|
|
||||||
* skip this: the next iteration will break immediately anyway.
|
|
||||||
*/
|
|
||||||
if (!usable_erasefunctions)
|
|
||||||
continue;
|
|
||||||
/* Reading the whole chip may take a while, inform the user even
|
|
||||||
* in non-verbose mode.
|
|
||||||
*/
|
|
||||||
msg_cinfo("Reading current flash chip contents... ");
|
|
||||||
if (flash->chip->read(flash, curcontents, 0, size)) {
|
|
||||||
/* Now we are truly screwed. Read failed as well. */
|
|
||||||
msg_cerr("Can't read anymore! Aborting.\n");
|
|
||||||
/* We have no idea about the flash chip contents, so
|
|
||||||
* retrying with another erase function is pointless.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
msg_cinfo("done. ");
|
|
||||||
}
|
|
||||||
/* Free the scratchpad. */
|
|
||||||
free(curcontents);
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
msg_cerr("FAILED!\n");
|
|
||||||
} else {
|
|
||||||
if (all_skipped)
|
|
||||||
msg_cinfo("\nWarning: Chip content is identical to the requested image.\n");
|
|
||||||
msg_cinfo("Erase/write done.\n");
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct flashrom_layout *get_layout(const struct flashctx *const flashctx)
|
static const struct flashrom_layout *get_layout(const struct flashctx *const flashctx)
|
||||||
{
|
{
|
||||||
if (flashctx->layout && flashctx->layout->num_entries)
|
if (flashctx->layout && flashctx->layout->num_entries)
|
||||||
@ -2323,170 +2168,6 @@ int chip_safety_check(const struct flashctx *flash, int force, int read_it, int
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function signature is horrible. We need to design a better interface,
|
|
||||||
* but right now it allows us to split off the CLI code.
|
|
||||||
* Besides that, the function itself is a textbook example of abysmal code flow.
|
|
||||||
*/
|
|
||||||
int doit(struct flashctx *flash, int force, const char *filename, int read_it,
|
|
||||||
int write_it, int erase_it, int verify_it)
|
|
||||||
{
|
|
||||||
uint8_t *oldcontents;
|
|
||||||
uint8_t *newcontents;
|
|
||||||
int ret = 0;
|
|
||||||
unsigned long size = flash->chip->total_size * 1024;
|
|
||||||
int read_all_first = 1; /* FIXME: Make this configurable. */
|
|
||||||
|
|
||||||
if (chip_safety_check(flash, force, read_it, write_it, erase_it, verify_it)) {
|
|
||||||
msg_cerr("Aborting.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (normalize_romentries(flash)) {
|
|
||||||
msg_cerr("Requested regions can not be handled. Aborting.\n");
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (read_it) {
|
|
||||||
return read_flash_to_file(flash, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldcontents = malloc(size);
|
|
||||||
if (!oldcontents) {
|
|
||||||
msg_gerr("Out of memory!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* Assume worst case: All bits are 0. */
|
|
||||||
memset(oldcontents, 0x00, size);
|
|
||||||
newcontents = malloc(size);
|
|
||||||
if (!newcontents) {
|
|
||||||
msg_gerr("Out of memory!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* Assume best case: All bits should be 1. */
|
|
||||||
memset(newcontents, 0xff, size);
|
|
||||||
/* Side effect of the assumptions above: Default write action is erase
|
|
||||||
* because newcontents looks like a completely erased chip, and
|
|
||||||
* oldcontents being completely 0x00 means we have to erase everything
|
|
||||||
* before we can write.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (erase_it) {
|
|
||||||
/* FIXME: Do we really want the scary warning if erase failed?
|
|
||||||
* After all, after erase the chip is either blank or partially
|
|
||||||
* blank or it has the old contents. A blank chip won't boot,
|
|
||||||
* so if the user wanted erase and reboots afterwards, the user
|
|
||||||
* knows very well that booting won't work.
|
|
||||||
*/
|
|
||||||
if (erase_and_write_flash(flash, oldcontents, newcontents)) {
|
|
||||||
emergency_help_message();
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_it || verify_it) {
|
|
||||||
if (read_buf_from_file(newcontents, size, filename)) {
|
|
||||||
ret = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if CONFIG_INTERNAL == 1
|
|
||||||
if (programmer == PROGRAMMER_INTERNAL && cb_check_image(newcontents, size) < 0) {
|
|
||||||
if (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");
|
|
||||||
ret = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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.
|
|
||||||
*/
|
|
||||||
if (read_all_first) {
|
|
||||||
msg_cinfo("Reading old flash chip contents... ");
|
|
||||||
if (flash->chip->read(flash, oldcontents, 0, size)) {
|
|
||||||
ret = 1;
|
|
||||||
msg_cinfo("FAILED.\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg_cinfo("done.\n");
|
|
||||||
|
|
||||||
/* Build a new image taking the given layout into account. */
|
|
||||||
if (build_new_image(flash, read_all_first, oldcontents, newcontents)) {
|
|
||||||
msg_gerr("Could not prepare the data to be written, aborting.\n");
|
|
||||||
ret = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (write_it && erase_and_write_flash(flash, oldcontents, newcontents)) {
|
|
||||||
msg_cerr("Uh oh. Erase/write failed. ");
|
|
||||||
if (read_all_first) {
|
|
||||||
msg_cerr("Checking if anything has changed.\n");
|
|
||||||
msg_cinfo("Reading current flash chip contents... ");
|
|
||||||
if (!flash->chip->read(flash, newcontents, 0, size)) {
|
|
||||||
msg_cinfo("done.\n");
|
|
||||||
if (!memcmp(oldcontents, newcontents, size)) {
|
|
||||||
nonfatal_help_message();
|
|
||||||
ret = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
msg_cerr("Apparently at least some data has changed.\n");
|
|
||||||
} else
|
|
||||||
msg_cerr("Can't even read anymore!\n");
|
|
||||||
emergency_help_message();
|
|
||||||
ret = 1;
|
|
||||||
goto out;
|
|
||||||
} else
|
|
||||||
msg_cerr("\n");
|
|
||||||
emergency_help_message();
|
|
||||||
ret = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verify only if we either did not try to write (verify operation) or actually changed something. */
|
|
||||||
if (verify_it && (!write_it || !all_skipped)) {
|
|
||||||
msg_cinfo("Verifying flash... ");
|
|
||||||
|
|
||||||
if (write_it) {
|
|
||||||
/* Work around chips which need some time to calm down. */
|
|
||||||
programmer_delay(1000*1000);
|
|
||||||
ret = verify_range(flash, newcontents, 0, size);
|
|
||||||
/* If we tried to write, and verification now fails, we
|
|
||||||
* might have an emergency situation.
|
|
||||||
*/
|
|
||||||
if (ret)
|
|
||||||
emergency_help_message();
|
|
||||||
} else {
|
|
||||||
ret = compare_range(newcontents, oldcontents, 0, size);
|
|
||||||
}
|
|
||||||
if (!ret)
|
|
||||||
msg_cinfo("VERIFIED.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
free(oldcontents);
|
|
||||||
free(newcontents);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @private */
|
|
||||||
static int prepare_flash_access(struct flashctx *const flash,
|
static int prepare_flash_access(struct flashctx *const flash,
|
||||||
const bool read_it, const bool write_it,
|
const bool read_it, const bool write_it,
|
||||||
const bool erase_it, const bool verify_it)
|
const bool erase_it, const bool verify_it)
|
||||||
@ -2512,7 +2193,6 @@ static int prepare_flash_access(struct flashctx *const flash,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
|
||||||
static void finalize_flash_access(struct flashctx *const flash)
|
static void finalize_flash_access(struct flashctx *const flash)
|
||||||
{
|
{
|
||||||
unmap_flash(flash);
|
unmap_flash(flash);
|
||||||
@ -2783,3 +2463,74 @@ _free_ret:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @} */ /* end flashrom-ops */
|
/** @} */ /* end flashrom-ops */
|
||||||
|
|
||||||
|
int do_read(struct flashctx *const flash, const char *const filename)
|
||||||
|
{
|
||||||
|
if (prepare_flash_access(flash, true, false, false, false))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
const int ret = read_flash_to_file(flash, filename);
|
||||||
|
|
||||||
|
finalize_flash_access(flash);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_erase(struct flashctx *const flash)
|
||||||
|
{
|
||||||
|
const int ret = flashrom_flash_erase(flash);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: Do we really want the scary warning if erase failed?
|
||||||
|
* After all, after erase the chip is either blank or partially
|
||||||
|
* blank or it has the old contents. A blank chip won't boot,
|
||||||
|
* so if the user wanted erase and reboots afterwards, the user
|
||||||
|
* knows very well that booting won't work.
|
||||||
|
*/
|
||||||
|
if (ret)
|
||||||
|
emergency_help_message();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_write(struct flashctx *const flash, const char *const filename)
|
||||||
|
{
|
||||||
|
const size_t flash_size = flash->chip->total_size * 1024;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
uint8_t *const newcontents = malloc(flash_size);
|
||||||
|
if (!newcontents) {
|
||||||
|
msg_gerr("Out of memory!\n");
|
||||||
|
goto _free_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_buf_from_file(newcontents, flash_size, filename))
|
||||||
|
goto _free_ret;
|
||||||
|
|
||||||
|
ret = flashrom_image_write(flash, newcontents, flash_size);
|
||||||
|
|
||||||
|
_free_ret:
|
||||||
|
free(newcontents);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_verify(struct flashctx *const flash, const char *const filename)
|
||||||
|
{
|
||||||
|
const size_t flash_size = flash->chip->total_size * 1024;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
uint8_t *const newcontents = malloc(flash_size);
|
||||||
|
if (!newcontents) {
|
||||||
|
msg_gerr("Out of memory!\n");
|
||||||
|
goto _free_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_buf_from_file(newcontents, flash_size, filename))
|
||||||
|
goto _free_ret;
|
||||||
|
|
||||||
|
ret = flashrom_image_verify(flash, newcontents, flash_size);
|
||||||
|
|
||||||
|
_free_ret:
|
||||||
|
free(newcontents);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
84
layout.c
84
layout.c
@ -202,33 +202,6 @@ void layout_cleanup(void)
|
|||||||
layout.num_entries = 0;
|
layout.num_entries = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct romentry *get_next_included_romentry(unsigned int start)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned int best_start = UINT_MAX;
|
|
||||||
struct romentry *best_entry = NULL;
|
|
||||||
struct romentry *cur;
|
|
||||||
|
|
||||||
/* First come, first serve for overlapping regions. */
|
|
||||||
for (i = 0; i < layout.num_entries; i++) {
|
|
||||||
cur = &layout.entries[i];
|
|
||||||
if (!cur->included)
|
|
||||||
continue;
|
|
||||||
/* Already past the current entry? */
|
|
||||||
if (start > cur->end)
|
|
||||||
continue;
|
|
||||||
/* Inside the current entry? */
|
|
||||||
if (start >= cur->start)
|
|
||||||
return cur;
|
|
||||||
/* Entry begins after start. */
|
|
||||||
if (best_start > cur->start) {
|
|
||||||
best_start = cur->start;
|
|
||||||
best_entry = cur;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate and - if needed - normalize layout entries. */
|
/* Validate and - if needed - normalize layout entries. */
|
||||||
int normalize_romentries(const struct flashctx *flash)
|
int normalize_romentries(const struct flashctx *flash)
|
||||||
{
|
{
|
||||||
@ -252,60 +225,3 @@ int normalize_romentries(const struct flashctx *flash)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int copy_old_content(struct flashctx *flash, int oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents, unsigned int start, unsigned int size)
|
|
||||||
{
|
|
||||||
if (!oldcontents_valid) {
|
|
||||||
/* oldcontents is a zero-filled buffer. By reading the current data into oldcontents here, we
|
|
||||||
* avoid a rewrite of identical regions even if an initial full chip read didn't happen. */
|
|
||||||
msg_gdbg2("Read a chunk starting at 0x%06x (len=0x%06x).\n", start, size);
|
|
||||||
int ret = flash->chip->read(flash, oldcontents + start, start, size);
|
|
||||||
if (ret != 0) {
|
|
||||||
msg_gerr("Failed to read chunk 0x%06x-0x%06x.\n", start, start + size - 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memcpy(newcontents + start, oldcontents + start, size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modify @newcontents so that it contains the data that should be on the chip eventually. In the case the user
|
|
||||||
* wants to update only parts of it, copy the chunks to be preserved from @oldcontents to @newcontents. If
|
|
||||||
* @oldcontents is not valid, we need to fetch the current data from the chip first.
|
|
||||||
*/
|
|
||||||
int build_new_image(struct flashctx *flash, bool oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents)
|
|
||||||
{
|
|
||||||
unsigned int start = 0;
|
|
||||||
struct romentry *entry;
|
|
||||||
unsigned int size = flash->chip->total_size * 1024;
|
|
||||||
|
|
||||||
/* If no regions were specified for inclusion, assume
|
|
||||||
* that the user wants to write the complete new image.
|
|
||||||
*/
|
|
||||||
if (num_include_args == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Non-included romentries are ignored.
|
|
||||||
* The union of all included romentries is used from the new image.
|
|
||||||
*/
|
|
||||||
while (start < size) {
|
|
||||||
entry = get_next_included_romentry(start);
|
|
||||||
/* No more romentries for remaining region? */
|
|
||||||
if (!entry) {
|
|
||||||
copy_old_content(flash, oldcontents_valid, oldcontents, newcontents, start,
|
|
||||||
size - start);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* For non-included region, copy from old content. */
|
|
||||||
if (entry->start > start)
|
|
||||||
copy_old_content(flash, oldcontents_valid, oldcontents, newcontents, start,
|
|
||||||
entry->start - start);
|
|
||||||
/* Skip to location after current romentry. */
|
|
||||||
start = entry->end + 1;
|
|
||||||
/* Catch overflow. */
|
|
||||||
if (!start)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user