Hello,
My name is Łukasz Dmitrowski and I am one of coreboot/flashrom GSoC students this year. You can read about my project here: http://blogs.coreboot.org/blog/author/dmitro/.
One of my first steps is updating and extending Nico Hubers libflashrom patch. Work is still in progress, but some changes are already made, so I thought that it will be good idea to send a patch just for review to know if I am going in a good direction. I want to improve my skills and provide best code quality as I can, so I am open for criticism - I am here to learn :)
Patch summary:
Makefile: I added cli_common.o and print.o to LIB_OBJS - it solves undefined references in current libflashrom version, but spoils standard flashrom build with 'make', I did not investigated it yet as my focus is to extend library with new functions and update already existing, need to discuss it with my mentor
cli_classic.c, cli_output.c, flash.h: Only Nico Hubers changes here, didn't touch it
libflashrom.c, libflashrom.h: I added 8 functions and made a few changes to existing ones, as some of used functions are static, or does not exist.
New functions:
int fl_supported_programmers(const char **supported_programmers); int fl_supported_flash_chips(fl_flashchip_info_t *fchips); int fl_supported_boards(fl_board_info_t *boards); int fl_supported_chipsets(fl_chipset_info_t *chipsets); int fl_supported_programmers_number(); int fl_supported_flash_chips_number(); int fl_supported_boards_number(); int fl_supported_chipsets_number();
New types: fl_flashchip_info_t - smaller version of struct flashchip fl_board_info_t - smaller version of struct board_info fl_chipset_info_t - smaller version of struct penable fl_test_state - copy of enum test_state
So we have 4 functions that return a list of supported programmers, chips, boards and chipsets. Next 4 functions return number of supported hardware of certain type. For example, to obtain a list of supported boards, you can create an array of structures fl_board_info of suitable size (the size is known - fl_supported_boards_number()), then pass it to fl_supported_boards(fl_board_info_t *boards) and you will have it filled with data.
Regarding changes in Nico Hubers functions: I made temporary comments for every change and marked it with LD_CHANGE_NOTE. Every comment contains previous code and reason why it has been commented out.
Thanks in advance for your insights!
Regards, Lukasz Dmitrowski
---
Signed-off-by: Łukasz Dmitrowski lukasz.dmitrowski@gmail.com
Index: Makefile =================================================================== --- Makefile (revision 1891) +++ Makefile (working copy) @@ -374,7 +374,7 @@ ############################################################################### # Library code.
-LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o +LIB_OBJS = libflashrom.o layout.o flashrom.o udelay.o programmer.o helpers.o cli_common.o print.o
############################################################################### # Frontend related stuff. Index: cli_classic.c =================================================================== --- cli_classic.c (revision 1891) +++ cli_classic.c (working copy) @@ -30,6 +30,7 @@ #include "flash.h" #include "flashchips.h" #include "programmer.h" +#include "libflashrom.h"
static void cli_classic_usage(const char *name) { @@ -135,6 +136,8 @@ char *tempstr = NULL; char *pparam = NULL;
+ fl_set_log_callback((fl_log_callback_t *)&fl_print_cb); + print_version(); print_banner();
Index: cli_output.c =================================================================== --- cli_output.c (revision 1891) +++ cli_output.c (working copy) @@ -71,9 +71,8 @@ #endif /* !STANDALONE */
/* Please note that level is the verbosity, not the importance of the message. */ -int print(enum msglevel level, const char *fmt, ...) +int fl_print_cb(enum msglevel level, const char *fmt, va_list ap) { - va_list ap; int ret = 0; FILE *output_type = stdout;
@@ -81,9 +80,7 @@ output_type = stderr;
if (level <= verbose_screen) { - va_start(ap, fmt); ret = vfprintf(output_type, fmt, ap); - va_end(ap); /* msg_*spew often happens inside chip accessors in possibly * time-critical operations. Don't slow them down by flushing. */ if (level != MSG_SPEW) @@ -91,9 +88,7 @@ } #ifndef STANDALONE if ((level <= verbose_logfile) && logfile) { - va_start(ap, fmt); ret = vfprintf(logfile, fmt, ap); - va_end(ap); if (level != MSG_SPEW) fflush(logfile); } Index: flash.h =================================================================== --- flash.h (revision 1891) +++ flash.h (working copy) @@ -31,6 +31,7 @@ #include <stdint.h> #include <stddef.h> #include <stdbool.h> +#include <stdarg.h> #if IS_WINDOWS #include <windows.h> #undef min @@ -317,6 +318,7 @@ MSG_DEBUG2 = 4, MSG_SPEW = 5, }; +int fl_print_cb(enum msglevel level, const char *fmt, va_list ap); /* Let gcc and clang check for correct printf-style format strings. */ int print(enum msglevel level, const char *fmt, ...) #ifdef __MINGW32__ Index: libflashrom.c =================================================================== --- libflashrom.c (revision 0) +++ libflashrom.c (working copy) @@ -0,0 +1,603 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2012 secunet Security Networks AG + * (Written by Nico Huber nico.huber@secunet.com for secunet) + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * @mainpage + * + * Have a look at the Modules section for a function reference. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "flash.h" +#include "programmer.h" +#include "libflashrom.h" + +/** + * @defgroup fl-general General + * @{ + */ + +/** Pointer to log callback function. */ +static fl_log_callback_t *fl_log_callback = NULL; + +/** + * @brief Initialize libflashrom. + * + * @param perform_selfcheck If not zero, perform a self check. + * @return 0 on success + */ +int fl_init(const int perform_selfcheck) +{ + if (perform_selfcheck && selfcheck()) + return 1; + myusec_calibrate_delay(); + return 0; +} + +/** + * @brief Shut down libflashrom. + * @return 0 on success + */ +int fl_shutdown(void) +{ + return 0; /* TODO: nothing to do? */ +} + +/* TODO: fl_set_loglevel()? do we need it? + For now, let the user decide in his callback. */ + +/** + * @brief Set the log callback function. + * + * Set a callback function which will be invoked whenever libflashrom wants + * to output messages. This allows frontends to do whatever they see fit with + * such messages, e.g. write them to syslog, or to file, or print them in a + * GUI window, etc. + * + * @param log_callback Pointer to the new log callback function. + */ +void fl_set_log_callback(fl_log_callback_t *const log_callback) +{ + fl_log_callback = log_callback; +} +/** @private */ +int print(const enum msglevel level, const char *const fmt, ...) +{ + if (fl_log_callback) { + int ret; + va_list args; + va_start(args, fmt); + ret = fl_log_callback(level, fmt, args); + va_end(args); + return ret; + } + return 0; +} + +/** @} */ /* end fl-general */ + + + +/** + * @defgroup fl-query Querying + * @{ + */ + +int fl_supported_programmers(const char **supported_programmers) +{ + int ret = 0; + enum programmer p = 0; + + if (supported_programmers != NULL) { + for (; p < PROGRAMMER_INVALID; ++p) + supported_programmers[p] = programmer_table[p].name; + } else { + ret = 1; + } + + return ret; +} + +int fl_supported_programmers_number() +{ + return PROGRAMMER_INVALID; +} + +int fl_supported_flash_chips(fl_flashchip_info_t *fchips) +{ + int ret = 0; + int i = 0; + + if (fchips != NULL) { + for (; i < flashchips_size; ++i) { + fchips[i].vendor = flashchips[i].vendor; + fchips[i].name = flashchips[i].name; + fchips[i].tested.erase = flashchips[i].tested.erase; + fchips[i].tested.probe = flashchips[i].tested.probe; + fchips[i].tested.read = flashchips[i].tested.read; + fchips[i].tested.write = flashchips[i].tested.write; + fchips[i].total_size = flashchips[i].total_size; + } + } else { + ret = 1; + } + + return ret; +} + +int fl_supported_flash_chips_number() +{ + return flashchips_size; +} + +int fl_supported_boards(fl_board_info_t *boards) +{ + int ret = 0; + const struct board_info *binfo = boards_known; + + if (boards != NULL) { + while (binfo->vendor != NULL) { + boards->vendor = binfo->vendor; + boards->name = binfo->name; + boards->working = binfo->working; + ++binfo; + ++boards; + } + } else { + ret = 1; + } + + return ret; +} + +int fl_supported_boards_number() +{ + int boards_number = 0; + const struct board_info *binfo = boards_known; + + while (binfo->vendor != NULL) { + ++boards_number; + ++binfo; + } + + return boards_number; +} + +int fl_supported_chipsets(fl_chipset_info_t *chipsets) +{ + int ret = 0; + const struct penable *chipset = chipset_enables; + + if (chipsets != NULL) { + while (chipset->vendor_name != NULL) { + chipsets->vendor = chipset->vendor_name; + chipsets->chipset = chipset->device_name; + chipsets->status = chipset->status; + ++chipset; + ++chipsets; + } + } else { + return ret; + } + + return ret; +} + +int fl_supported_chipsets_number() +{ + int chipsets_number = 0; + const struct penable *chipset = chipset_enables; + + while (chipset->vendor_name != NULL) { + ++chipsets_number; + ++chipset; + } + + return chipsets_number; +} + +/** @} */ /* end fl-query */ + + + +/** + * @defgroup fl-prog Programmers + * @{ + */ + +/** + * @brief Initialize the specified programmer. + * + * @param prog_name Name of the programmer to initialize. + * @param prog_param Pointer to programmer specific parameters. + * @return 0 on success + */ +int fl_programmer_init(const char *const prog_name, const char *const prog_param) +{ + unsigned prog; + + for (prog = 0; prog < PROGRAMMER_INVALID; prog++) { + if (strcmp(prog_name, programmer_table[prog].name) == 0) + break; + } + if (prog >= PROGRAMMER_INVALID) { + msg_ginfo("Error: Unknown programmer "%s". Valid choices are:\n", prog_name); + list_programmers_linebreak(0, 80, 0); + return 1; + } + return programmer_init(prog, prog_param); +} + +/** + * @brief Shut down the initialized programmer. + * + * @return 0 on success + */ +int fl_programmer_shutdown(void) +{ + return programmer_shutdown(); +} + +/* TODO: fl_programmer_capabilities()? */ + +/** @} */ /* end fl-prog */ + + + +/** + * @defgroup fl-flash Flash chips + * @{ + */ + +/** + * @brief Probe for a flash chip. + * + * Probes for a flash chip and returns a flash context, that can be used + * later with flash chip and @ref fl-ops "image operations", if exactly one + * matching chip is found. + * + * @param[out] flashctx Points to a pointer of type fl_flashctx_t that will + * be set if exactly one chip is found. *flashctx has + * to be freed by the caller with @ref fl_flash_release. + * @param[in] chip_name Name of a chip to probe for, or NULL to probe for + * all known chips. + * @return 0 on success, + * 3 if multiple chips were found, + * 2 if no chip was found, + * or 1 on any other error. + */ +int fl_flash_probe(fl_flashctx_t **const flashctx, const char *const chip_name) +{ + int i, ret = 2; + fl_flashctx_t second_flashctx = { 0, }; + + chip_to_probe = chip_name; /* chip_to_probe is global in flashrom.c */ + + *flashctx = malloc(sizeof(**flashctx)); + if (!*flashctx) + return 1; + memset(*flashctx, 0, sizeof(**flashctx)); + + /* LD_CHANGE_NOTE for (i = 0; i < registered_programmer_count; ++i) { + * + * Reason of change: registered_programmer_count does not exist, I assume that a proper one is + * now registered_master_count - I will check and confirm + */ + for (i = 0; i < registered_master_count; ++i) { + int flash_idx = -1; + /* LD_CHANGE_NOTE if (!ret || (flash_idx = probe_flash(®istered_programmers[i], 0, *flashctx, 0)) != -1) { + * + * Reason of change: registered_programmers does not exist, I assume that a proper one is + * now registered_masters - I will check and confirm + */ + if (!ret || (flash_idx = probe_flash(®istered_masters[i], 0, *flashctx, 0)) != -1) { + ret = 0; + /* We found one chip, now check that there is no second match. */ + /* LD_CHANGE_NOTE if (probe_flash(®istered_programmers[i], flash_idx + 1, &second_flashctx, 0) != -1) { + * + * Reason of change: registered_programmers does not exist, I assume that a proper one is + * now registered_masters - I will check and confirm + * + */ + if (probe_flash(®istered_masters[i], flash_idx + 1, &second_flashctx, 0) != -1) { + ret = 3; + break; + } + } + } + if (ret) { + free(*flashctx); + *flashctx = NULL; + } + return ret; +} + +/** + * @brief Returns the size of the specified flash chip in bytes. + * + * @param flashctx The queried flash context. + * @return Size of flash chip in bytes. + */ +size_t fl_flash_getsize(const fl_flashctx_t *const flashctx) +{ + return flashctx->chip->total_size << 10; +} + +/** @private */ +int erase_and_write_flash(struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents); +/** @private */ +/* LD_CHANGE_NOTE void emergency_help_message(void); + * + * Reason of change: This has been commented out as emergency_help_message is static + */ +/** + * @brief Erase the specified ROM chip. + * + * @param flashctx The context of the flash chip to erase. + * @return 0 on success. + */ +int fl_flash_erase(fl_flashctx_t *const flashctx) +{ + const size_t flash_size = flashctx->chip->total_size * 1024; + + int ret = 0; + + uint8_t *const newcontents = malloc(flash_size); + if (!newcontents) { + msg_gerr("Out of memory!\n"); + return 1; + } + uint8_t *const oldcontents = malloc(flash_size); + if (!oldcontents) { + msg_gerr("Out of memory!\n"); + free(newcontents); + return 1; + } + + if (flashctx->chip->unlock) + flashctx->chip->unlock(flashctx); + + /* Assume worst case for old contents: All bits are 0. */ + memset(oldcontents, 0x00, flash_size); + /* Assume best case for new contents: All bits should be 1. */ + memset(newcontents, 0xff, flash_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_and_write_flash(flashctx, oldcontents, newcontents)) { + /* 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. + */ + /* LD_CHANGE_NOTE emergency_help_message(); + * + * Reason of change: This function is static + */ + ret = 1; + } + + free(oldcontents); + free(newcontents); + return ret; +} + +/** + * @brief Free a flash context. + * + * @param flashctx Flash context to free. + */ +void fl_flash_release(fl_flashctx_t *const flashctx) +{ + free(flashctx); +} + +/** @} */ /* end fl-flash */ + + + +/** + * @defgroup fl-ops Operations + * @{ + */ + +/** + * @brief Read the current image from the specified ROM chip. + * + * @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 to short for the flash chip's contents, + * or 1 on any other failure. + */ +int fl_image_read(fl_flashctx_t *const flashctx, void *const buffer, const size_t buffer_len) +{ + const size_t flash_size = flashctx->chip->total_size * 1024; + + int ret = 0; + + if (flashctx->chip->unlock) + flashctx->chip->unlock(flashctx); + + msg_cinfo("Reading flash... "); + if (flash_size > buffer_len) { + msg_cerr("Buffer to short for this flash chip (%u < %u).\n", + (unsigned int)buffer_len, (unsigned int)flash_size); + ret = 2; + goto _out; + } + if (!flashctx->chip->read) { + msg_cerr("No read function available for this flash chip.\n"); + ret = 1; + goto _out; + } + if (flashctx->chip->read(flashctx, buffer, 0, flash_size)) { + msg_cerr("Read operation failed!\n"); + ret = 1; + goto _out; + } +_out: + msg_cinfo("%s.\n", ret ? "FAILED" : "done"); + return ret; +} + +/** @private */ +/* LD_CHANGE_NOTE void nonfatal_help_message(void); + * + * Reason of change: This function is static + */ +/** + * @brief Write the specified image to the ROM chip. + * + * @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 was tried, but flash contents changed, + * or 1 on any other failure. + */ +int fl_image_write(fl_flashctx_t *const flashctx, void *const buffer, const size_t buffer_len) +{ + const size_t flash_size = flashctx->chip->total_size * 1024; + + int ret = 0; + + if (buffer_len != flash_size) { + msg_cerr("Buffer size doesn't match size of flash chip (%u != %u)\n.", + (unsigned int)buffer_len, (unsigned int)flash_size); + return 4; + } + + uint8_t *const newcontents = buffer; + uint8_t *const oldcontents = malloc(flash_size); + if (!oldcontents) { + msg_gerr("Out of memory!\n"); + return 1; + } + if (fl_image_read(flashctx, oldcontents, flash_size)) { + ret = 1; + goto _free_out; + } + + /* LD_CHANGE_NOTE handle_romentries(flashctx, oldcontents, newcontents); + * + * Reason of change: This function does not exist. There is a need to investigate what was + * its purpose and how it can be replaced. + */ + + if (erase_and_write_flash(flashctx, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if anything changed.\n"); + if (!flashctx->chip->read(flashctx, newcontents, 0, flash_size)) { + if (!memcmp(oldcontents, newcontents, flash_size)) { + msg_cinfo("Good. It seems nothing was changed.\n"); + /* LD_CHANGE_NOTE nonfatal_help_message(); + * + * Reason of change: This function is static + */ + ret = 3; + goto _free_out; + } + } + /* LD_CHANGE_NOTE emergency_help_message(); + * + * Reason of change: This function is static + */ + ret = 2; + goto _free_out; + } + +_free_out: + free(oldcontents); + return ret; +} + +/** @private */ +/* LD_CHANGE_NOTE int compare_range(const uint8_t *wantbuf, const uint8_t *havebuf, unsigned int start, unsigned int len); + * + * Reason of change: This function is static + */ +/** + * @brief Verify the ROM chip's contents with the specified image. + * + * @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, + * 2 if buffer_len doesn't match the size of the flash chip, + * or 1 on any other failure. + */ +int fl_image_verify(fl_flashctx_t *const flashctx, void *const buffer, const size_t buffer_len) +{ + const size_t flash_size = flashctx->chip->total_size * 1024; + + int ret = 0; + + if (buffer_len != flash_size) { + msg_cerr("Buffer size doesn't match size of flash chip (%u != %u)\n.", + (unsigned int)buffer_len, (unsigned int)flash_size); + return 2; + } + + /* LD_CHANGE_NOTE uint8_t *const newcontents = buffer; - used only in handle_romentries() function + * + * Reason of change: This pointer is used only in handle_romentries function, which has been + * commented out + */ + uint8_t *const oldcontents = malloc(flash_size); + if (!oldcontents) { + msg_gerr("Out of memory!\n"); + return 1; + } + if (fl_image_read(flashctx, oldcontents, flash_size)) { + ret = 1; + goto _free_out; + } + + /* LD_CHANGE_NOTE handle_romentries(flashctx, oldcontents, newcontents); + * + * Reason of change: This function does not exist + */ + + msg_cinfo("Verifying flash... "); + + /* LD_CHANGE_NOTE ret = compare_range(newcontents, oldcontents, 0, flash_size); + * + * Reason of change: This function is static. For now replaced with ret = 1 as ret must be used + */ + ret = 1; + if (!ret) + msg_cinfo("VERIFIED.\n"); + +_free_out: + free(oldcontents); + return ret; +} + +/** @} */ /* end fl-ops */ Index: libflashrom.h =================================================================== --- libflashrom.h (revision 0) +++ libflashrom.h (working copy) @@ -0,0 +1,97 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2012 secunet Security Networks AG + * (Written by Nico Huber nico.huber@secunet.com for secunet) + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LIBFLASHROM_H__ +#define __LIBFLASHROM_H__ 1 + +#include <stdarg.h> + +int fl_init(int perform_selfcheck); +int fl_shutdown(void); +/** @ingroup fl-general */ +typedef enum { /* This has to match enum msglevel. */ + FL_MSG_ERROR = 0, + FL_MSG_INFO = 1, + FL_MSG_DEBUG = 2, + FL_MSG_DEBUG2 = 3, + FL_MSG_SPEW = 4, +} fl_log_level_t; +/** @ingroup fl-general */ +typedef int(fl_log_callback_t)(fl_log_level_t, const char *format, va_list); +void fl_set_log_callback(fl_log_callback_t *); + +typedef enum { + FL_TESTED_OK = 0, + FL_TESTED_NT = 1, + FL_TESTED_BAD = 2, + FL_TESTED_DEP = 3, + FL_TESTED_NA = 4, +} fl_test_state; + +typedef struct { + const char *vendor; + const char *name; + unsigned int total_size; + + struct fl_tested { + fl_test_state probe; + fl_test_state read; + fl_test_state erase; + fl_test_state write; + } tested; +} fl_flashchip_info_t; + +typedef struct { + const char *vendor; + const char *name; + fl_test_state working; +} fl_board_info_t; + +typedef struct { + const char *vendor; + const char *chipset; + fl_test_state status; +} fl_chipset_info_t; + +int fl_supported_programmers(const char **supported_programmers); +int fl_supported_flash_chips(fl_flashchip_info_t *fchips); +int fl_supported_boards(fl_board_info_t *boards); +int fl_supported_chipsets(fl_chipset_info_t *chipsets); +int fl_supported_programmers_number(); +int fl_supported_flash_chips_number(); +int fl_supported_boards_number(); +int fl_supported_chipsets_number(); + +int fl_programmer_init(const char *prog_name, const char *prog_params); +int fl_programmer_shutdown(void); + +struct flashctx; +typedef struct flashctx fl_flashctx_t; +int fl_flash_probe(fl_flashctx_t **, const char *chip_name); +size_t fl_flash_getsize(const fl_flashctx_t *); +int fl_flash_erase(fl_flashctx_t *); +void fl_flash_release(fl_flashctx_t *); + +int fl_image_read(fl_flashctx_t *, void *buffer, size_t buffer_len); +int fl_image_write(fl_flashctx_t *, void *buffer, size_t buffer_len); +int fl_image_verify(fl_flashctx_t *, void *buffer, size_t buffer_len); + +#endif /* !__LIBFLASHROM_H__ */