Stefan Reinauer (stefan.reinauer@coreboot.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/5988
-gerrit
commit 2952fcef3ca2c8019b1df7fc411d9223af41b59a Author: Patrick Georgi patrick.georgi@secunet.com Date: Tue Jul 19 16:40:06 2011 +0200
Add flashupdate command
This command allows the user to load a coreboot image, configure its cmos.default file and write it to flash, all from FILO.
Change-Id: Ia011deb64bcf75aebe8b67554ad9d9738d90fe59 Signed-off-by: Patrick Georgi patrick.georgi@secunet.com --- Kconfig | 18 ++- Makefile | 1 + flashupdate/Makefile.inc | 29 +++++ flashupdate/flashrom-bridge.c | 73 +++++++++++ flashupdate/flashupdate.c | 277 ++++++++++++++++++++++++++++++++++++++++++ main/grub/builtins.c | 19 ++- 6 files changed, 413 insertions(+), 4 deletions(-)
diff --git a/Kconfig b/Kconfig index 21a718d..90fa2f4 100644 --- a/Kconfig +++ b/Kconfig @@ -200,8 +200,7 @@ config FLASHROM_LOCKDOWN default n help Enable system flash memory write protections and lock them down prior - starting the kernel. Flash memory lockdown can be disabled per boot - entry with the new command 'flashrom_unlock'. + starting the kernel.
NOTE: Only supported on selected hardware:
@@ -210,6 +209,21 @@ config FLASHROM_LOCKDOWN o Intel Cougar Point / Panther Point PCH (FWH + SPI) o AMD SB600 (SPI by locking the flash chip itself)
+config FLASHROM_UNLOCK + bool "Provide flashrom_unlock command" + default n + depends on FLASHROM_LOCKDOWN + help + Flashrom lockdown can be disabled per boot entry with the new + command 'flashrom_unlock'. + +config FLASHUPDATE + bool "Provide flashupdate command" + default n + help + Add "flashupdate" command based on http://www.flashrom.org/ to + FILO. + endmenu
menu "Filesystems" diff --git a/Makefile b/Makefile index 1a9430d..7426395 100644 --- a/Makefile +++ b/Makefile @@ -119,6 +119,7 @@ CFLAGS += $(call cc-option, -fno-stack-protector,)
LIBS := $(LIBPAYLOAD) $(LIBGCC)
+SUBDIRS-$(CONFIG_USE_GRUB) += flashupdate SUBDIRS-y += main fs drivers $(ARCHDIR-y)
$(foreach subdir,$(SUBDIRS-y),$(eval include $(subdir)/Makefile.inc)) diff --git a/flashupdate/Makefile.inc b/flashupdate/Makefile.inc new file mode 100644 index 0000000..319add3 --- /dev/null +++ b/flashupdate/Makefile.inc @@ -0,0 +1,29 @@ +# +# Copyright (C) 2011 secunet AG +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +LIBFLASHROM_PREFIX ?= $(obj)/libflashrom +LIBFLASHROM = $(LIBFLASHROM_PREFIX)/lib/libflashrom.a +INCFLASHROM = $(LIBFLASHROM_PREFIX)/include + +ifeq ($(CONFIG_FLASHUPDATE),y) +CPPFLAGS += -I$(INCFLASHROM) +LIBS += $(LIBFLASHROM) +endif + +TARGETS-$(CONFIG_FLASHUPDATE) += flashupdate/flashupdate.o +TARGETS-$(CONFIG_FLASHUPDATE) += flashupdate/flashrom-bridge.o diff --git a/flashupdate/flashrom-bridge.c b/flashupdate/flashrom-bridge.c new file mode 100644 index 0000000..0940389 --- /dev/null +++ b/flashupdate/flashrom-bridge.c @@ -0,0 +1,73 @@ +#include <stdarg.h> +#include <string.h> + +#include "flashchips.h" +#include "flash.h" +#include "programmer.h" + +int new_rom_size; +void *new_rom_data; + +int old_rom_size; +void *old_rom_data; + +// for flashrom +int print(int type, const char *fmt, ...) __attribute__((format(printf, 2, 3))); +int print(int type, const char *fmt, ...) +{ +#ifdef DEBUG + if (type > MSG_INFO) return 0; + int ret; + va_list args; + va_start(args, fmt); + ret = vprintf(fmt, args); + va_end(args); + return ret; +#else + return 0; +#endif +} + +/* returned memory must be freed by caller */ +char *flashbuses_to_text(enum chipbustype bustype) +{ + return strdup("Unknown"); +} + +int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename) +{ + // we need at least enough space for our image. + if (size < new_rom_size) + return 1; + + // top-align our image into the chip + memcpy(buf + size - new_rom_size, new_rom_data, new_rom_size); + + return 0; +} + +int read_flash_to_file(struct flashctx *flash, const char *filename) +{ + int ret = 0; + unsigned long size = flash->total_size * 1024; + if (size > old_rom_size) { + msg_cerr("old rom buffer not large enough for flash\n"); + ret = 1; + goto out_free; + } + + if (!flash->read) { + msg_cerr("No read function available for this flash chip.\n"); + ret = 1; + goto out_free; + } + if (flash->read(flash, old_rom_data, 0, size)) { + msg_cerr("Read operation failed!\n"); + ret = 1; + goto out_free; + } + +out_free: + return ret; +} + diff --git a/flashupdate/flashupdate.c b/flashupdate/flashupdate.c new file mode 100644 index 0000000..c0f337d --- /dev/null +++ b/flashupdate/flashupdate.c @@ -0,0 +1,277 @@ +#include <stdlib.h> +#include <getopt.h> +#include <libpayload.h> +#include <coreboot_tables.h> +#include <cbfs.h> +#include <grub/shared.h> +#include <fs.h> +#include <pci/pci.h> + +/* flashrom defines */ +#define CONFIG_INTERNAL 1 +#include "flashchips.h" +#include "flash.h" +#include "programmer.h" + +extern int new_rom_size; +extern void *new_rom_data; + +struct flashctx flashchip; + +static void beep_success() +{ + int i; + for (i = 0; i < 5; i++) { + speaker_tone(440, 200); mdelay(300); + speaker_tone(660, 200); mdelay(300); + speaker_tone(880, 200); mdelay(300); + mdelay(3*1000); + } +} + +static void beep_fail() +{ + int i; + for (i = 0; i < 5; i++) { + speaker_tone(1200, 200); mdelay(300); + speaker_tone( 660, 200); mdelay(300); + mdelay(3*1000); + } +} + +static int init_flash(const char* flashtype) +{ + int j; + verbose++; + if (programmer_init(PROGRAMMER_INTERNAL, NULL)) { + grub_printf("Could not initialize programmer\n"); + return -1; + } + for (j = 0; j < registered_programmer_count; j++) { + int current_chip = -1; + do { + current_chip = probe_flash(®istered_programmers[j], ++current_chip, &flashchip, 0); + if ((current_chip != -1) && ((flashtype == NULL) || (strcasecmp(flashchip.name, flashtype) == 0))) { + return 0; + } + } while (current_chip != -1); + } + + grub_printf("Could not find flash chip\n"); + return -1; +} + +static int write_flash(void *imgdata, int size) +{ + return doit(&flashchip, 1, imgdata, 0, 1, 0, 1); +} + +static int test_id_section(void *romarea, unsigned int romsize, int offset, char **vendor, char **model, char **version) +{ + /* data[-1]: romsize + data[-2]: offset top-of-rom to board model + data[-3]: offset top-of-rom to board vendor + data[-4]: offset top-of-rom to build version */ + unsigned int *data = romarea+romsize-offset; + *version = romarea+romsize-data[-4]; + *vendor = romarea+romsize-data[-3]; + *model = romarea+romsize-data[-2]; + /* Assume that data[-1] matches filesize, and that vendor and model are laid out without extra space. + DO NOT assume that model aligns to the offsets, there may be future additions. */ + return ((data[-1] == romsize) && (*vendor+strnlen(*vendor, data[-2])+1 == *model)); +} + +int flashupdate_func(char *arg, int flags) +{ + int result = -1; + int force = 0; + char *chiptype = NULL; + char *vendor = NULL; + char *board = NULL; + char *region = NULL; + + char **argv; + int argc = to_argc_argv(arg, &argv); + + new_rom_data = NULL; + + char opt; + optreset = optind = 1; + while ((opt = getopt(argc, argv, "fc:v:b:i:")) != -1) { + switch (opt) { + case 'f': + force = 1; + break; + case 'c': + chiptype = optarg; + break; + case 'v': + vendor = optarg; + break; + case 'b': + board = optarg; + break; + case 'i': + region = optarg; + break; + default: + grub_printf("unsupported option -%c.\n", opt); + goto out; + break; + } + } + + if (optind >= argc) { + grub_printf("No filename specified.\n"); + goto out; + } + const char *filename = argv[optind]; + + if (vendor && board && + ((strcmp(vendor, cb_mb_vendor_string(lib_sysinfo.mainboard)) != 0) || + (strcmp(board, cb_mb_part_string(lib_sysinfo.mainboard)) != 0))) { + /* since flashupdate semantics is: iff board matches, write flash; + it's a success not to flash on another board. */ + result = 0; + goto out; + } + + if (region) { + register_include_arg(region); + layout_use_ifd(); + } + + /* Step 0: Init programmer/flash to find out if we _can_ flash (we should) */ + if (init_flash(chiptype) == -1) { + grub_printf("Could not initialize flash programmer.\n"); + goto out; + } + + /* Step 1: read requested image file */ + if (!file_open(filename)) { + grub_printf("Could not open file '%s'\n", filename); + goto out; + } + + /* 16K of extra space, since we don't have feof() */ + new_rom_size = flashchip.total_size * 1024 + 16384; + new_rom_data = malloc(new_rom_size); + if (new_rom_data == NULL) { + grub_printf("Could not allocate memory.\n"); + goto out; + } + void *readbuf = new_rom_data; + int len; + grub_printf("Loading image file... "); + while ((len = file_read(readbuf, 16384)) != 0) { + readbuf += len; + /* We can abort if we cut into the last 16K: + * it's more than the flash is able to carry + */ + if (readbuf - new_rom_data > new_rom_size - 16384) { + file_close(); + grub_printf("File too large for flash\n"); + goto out; + } + } + new_rom_size = readbuf - new_rom_data; + file_close(); + grub_printf("done\n"); + + /* retarget all CBFS accesses to new image */ + setup_cbfs_from_ram(new_rom_data, new_rom_size); + + /* Step 2: Check Board IDs (that we work on the right image) */ + const char *cur_version = lib_sysinfo.cb_version; + const char *cur_vendor = cb_mb_vendor_string(lib_sysinfo.mainboard); + const char *cur_board = cb_mb_part_string(lib_sysinfo.mainboard); + + /* Step 2a: check if this is a coreboot image by looking for a CBFS master header */ + if (get_cbfs_header() != 0xffffffff) { + /* Step 2b: look for .id section in new ROM */ + /* There are currently two locations where .id can reside: top-0x10 and top-0x80. There's + no indicator which one is used in any given image, though it should be stable on any given + board. As a heuristic, we can check if the data is sane at each location. */ + char *new_vendor; + char *new_board; + char *new_version; + if (!test_id_section(new_rom_data, new_rom_size, 0x10, &new_vendor, &new_board, &new_version)) + if (!test_id_section(new_rom_data, new_rom_size, 0x80, &new_vendor, &new_board, &new_version)) { + grub_printf("Could not detect if file supports this system.\n"); + goto out; + } + if ((strcmp(cur_vendor, new_vendor) != 0) || (strcmp(cur_board, new_board) != 0)) { + grub_printf("File (%s %s) seems to be incompatible with current system (%s %s).\n", + new_vendor, new_board, cur_vendor, cur_board); + goto out; + } + + /* Step 3: Check image version (so that we don't reflash the same image again and again) */ + if (strcmp(cur_version, new_version) == 0) { + grub_printf("No need to update to the same version '%s'.\n", cur_version); + /* TODO: exit successfully. right? */ + result = 0; + goto out; + } + + /* Step 4: Merge current CMOS data with cmos.defaults */ + /* Step 4a: Load new CMOS data */ + void *cmos_defaults = cbfs_find_file("cmos.default", CBFS_COMPONENT_CMOS_DEFAULT); + void *cmos_layout = cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT); + /* Step 4a1: Point use_mem to cmos.default. */ + mem_accessor_base = cmos_defaults; + if (cmos_defaults && cmos_layout) { + /* Step 4b: Check if current CMOS data is valid. */ + if (options_checksum_valid(use_nvram)) { + /* Step 4c: Iterate over current CMOS data */ + struct cb_cmos_entries *cmos_entry = first_cmos_entry(get_system_option_table()); + do { + /* Step 4c1: Attempt to update new CMOS data with current values. Ignore failures */ + char *val = NULL; + get_option_as_string(use_nvram, get_system_option_table(), &val, cmos_entry->name); + if (val) { + set_option_from_string(use_mem, cmos_layout, val, cmos_entry->name); + } + } while (cmos_entry = next_cmos_entry(cmos_entry)); + } + + /* Step 5: Write merged CMOS image to CMOS */ + /* TODO: do we need to skip 0x32 (RTC century)? */ + int i; + for (i = 14; i < 256; i++) + nvram_write(((u8*)cmos_defaults)[i], i); + + /* Step 6: Set cmos_defaults_loaded=no in CMOS data using new layout */ + set_option_from_string(use_nvram, cmos_layout, "No", "cmos_defaults_loaded"); + + /* Step 6a: Set cmos_defaults_loaded=Yes in cmos.default in ROM image. */ + set_option_from_string(use_mem, cmos_layout, "Yes", "cmos_defaults_loaded"); + } + } else { + if (!force) { + grub_printf("This is a not a coreboot image. Aborting.\n"); + goto out; + } + } + + grub_printf("Writing image to flash, please wait.\n"); + /* Step 7: Flash image */ + if (write_flash(new_rom_data, new_rom_size) == 0) { + grub_printf("Flash was updated successfully.\n"); + result = 0; + beep_success(); + } else { + grub_printf("WARNING: Error while updating flash!!\n"); + beep_fail(); + } + + /* success */ + programmer_shutdown(); + +out: + if (new_rom_data) free(new_rom_data); + new_rom_data = NULL; + if (argv) free(argv); + return result; +} + diff --git a/main/grub/builtins.c b/main/grub/builtins.c index f53fe79..e6651d5 100644 --- a/main/grub/builtins.c +++ b/main/grub/builtins.c @@ -551,7 +551,7 @@ static struct builtin builtin_find = { }; #endif
-#ifdef CONFIG_FLASHROM_LOCKDOWN +#ifdef CONFIG_FLASHROM_UNLOCK /* flashrom_unlock */ /* Disable lockdown of flash memory on boot */ static int flashrom_unlock_func(char *arg, int flags) @@ -570,6 +570,18 @@ static struct builtin builtin_flashrom_unlock = { }; #endif
+#ifdef CONFIG_FLASHUPDATE +int flashupdate_func(char *arg, int flags); + +static struct builtin builtin_flashupdate = { + "flashupdate", + flashupdate_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "flashupdate DEVICE", + "Update flash ROM from a file loaded from DEVICE (selected by the user)." +}; +#endif + /* help */ #define MAX_SHORT_DOC_LEN 39 #define MAX_LONG_DOC_LEN 66 @@ -1936,9 +1948,12 @@ struct builtin *builtin_table[] = { #ifdef CONFIG_EXPERIMENTAL &builtin_find, #endif -#ifdef CONFIG_FLASHROM_LOCKDOWN +#ifdef CONFIG_FLASHROM_UNLOCK &builtin_flashrom_unlock, #endif +#ifdef CONFIG_FLASHUPDATE + &builtin_flashupdate, +#endif &builtin_help, &builtin_hiddenmenu, &builtin_initrd,