mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-27 07:02:34 +02:00

This imports a series of patches from chromiumos for MTD support. The patches are squashed to ease review and original Change-Ids have been removed to avoid confusing Gerrit. There are a few changes to integrate the code: - Conflict resolution - Makefile changes - Remove file library usage from linux_mtd. We may revisit this and use it for other Linux interfaces later on. - Switch to using file stream functions for reads and writes. This consolidated patch is Signed-off-by: David Hendricks <dhendricks@fb.com> The first commit's message is: Initial MTD support This adds MTD support to flashrom so that we can read, erase, and write content on a NOR flash chip via MTD. BUG=chrome-os-partner:40208 BRANCH=none TEST=read, write, and erase works on Oak Signed-off-by: David Hendricks <dhendrix@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/272983 Reviewed-by: Shawn N <shawnn@chromium.org> This is the 2nd commit message: linux_mtd: Fix compilation errors This fixes compilation errors from the initial import patch. Signed-off-by: David Hendricks <dhendricks@fb.com> This is the 3rd commit message: linux_mtd: Suppress message if NOR device not found This just suppresses a message that might cause confusion for unsuspecting users. BUG=none BRANCH=none TEST=ran on veyron_mickey, "NOR type device not found" message no longer appears under normal circumstances. Signed-off-by: David Hendricks <dhendrix@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/302145 Commit-Ready: David Hendricks <dhendrix@chromium.org> Tested-by: David Hendricks <dhendrix@chromium.org> Reviewed-by: Shawn N <shawnn@chromium.org> This is the 4th commit message: linux_mtd: Support for NO_ERASE type devices Some mtd devices have the MTD_NO_ERASE flag set. This means these devices don't require an erase to write and might not have implemented an erase function. We should be conservative and skip erasing altogether, falling back to performing writes over the whole flash. BUG=b:35104688 TESTED=Zaius flash is now written correctly for the 0xff regions. Signed-off-by: William A. Kennington III <wak@google.com> Reviewed-on: https://chromium-review.googlesource.com/472128 Commit-Ready: William Kennington <wak@google.com> Tested-by: William Kennington <wak@google.com> Reviewed-by: Brian Norris <briannorris@chromium.org> This is the 5th commit message: linux_mtd: do reads in eraseblock-sized chunks It's probably not the best idea to try to do an 8MB read in one syscall. Theoretically, this should work; but MTD just relies on the SPI driver to deliver the whole read in one transfer, and many SPI drivers haven't been tested well with large transfer sizes. I'd consider this a workaround, but it's still good to have IMO. BUG=chrome-os-partner:53215 TEST=boot kevin; `flashrom --read ...` TEST=check for performance regression on oak BRANCH=none Signed-off-by: Brian Norris <briannorris@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/344006 Reviewed-by: David Hendricks <dhendrix@chromium.org> This is the 6th commit message: linux_mtd: make read/write loop chunks consistent, and documented Theoretically, there should be no maximum size for the read() and write() syscalls on an MTD (well, except for the size of the entire device). But practical concerns (i.e., bugs) have meant we don't quite do this. For reads: Bug https://b/35573113 shows that some SPI-based MTD drivers don't yet handle very large transactions. So we artificially limit this to block-sized chunks. For writes: It's not clear there is a hard limit. Some drivers will already split large writes into smaller chunks automatically. Others don't do any splitting. At any rate, using *small* chunks can actually be a problem for some devices (b:35104688), as they get worse performance (doing an internal read/modify/write). This could be fixed in other ways by advertizing their true "write chunk size" to user space somehow, but this isn't so easy. As a simpler fix, we can just increase the loop increment to match the read loop. Per David, the original implementation (looping over page chunks) was just being paranoid. So this patch: * clarifies comments in linux_mtd_read(), to note that the chunking is somewhat of a hack that ideally can be fixed (with bug reference) * simplifies the linux_mtd_write() looping to match the structure in linux_mtd_read(), including dropping several unnecessary seeks, and correcting the error messages (they referred to "reads" and had the wrong parameters) * change linux_mtd_write() to align its chunks to eraseblocks, not page sizes Note that the "->page_size" parameter is still somewhat ill-defined, and only set by the upper layers for "opaque" flash. And it's not actually used in this driver now. If we could figure out what we really want to use it for, then we could try to set it appropriately. BRANCH=none BUG=b:35104688 TEST=various flashrom tests on Kevin TEST=Reading and writing to flash works on our zaius machines over mtd Change-Id: I3d6bb282863a5cf69909e28a1fc752b35f1b9599 Signed-off-by: Brian Norris <briannorris@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/505409 Reviewed-by: David Hendricks <dhendrix@chromium.org> Reviewed-by: Martin Roth <martinroth@chromium.org> Reviewed-by: William Kennington <wak@google.com> Reviewed-on: https://review.coreboot.org/25706 Tested-by: David Hendricks <david.hendricks@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Nico Huber <nico.h@gmx.de> Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
376 lines
10 KiB
C
376 lines
10 KiB
C
/*
|
|
* This file is part of the flashrom project.
|
|
*
|
|
* Copyright (C) 2009 Carl-Daniel Hailfinger
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <strings.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "flash.h"
|
|
#include "programmer.h"
|
|
#include "hwaccess.h"
|
|
|
|
struct pci_dev *pci_dev_find_filter(struct pci_filter filter)
|
|
{
|
|
struct pci_dev *temp;
|
|
|
|
for (temp = pacc->devices; temp; temp = temp->next)
|
|
if (pci_filter_match(&filter, temp))
|
|
return temp;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t devclass)
|
|
{
|
|
struct pci_dev *temp;
|
|
struct pci_filter filter;
|
|
uint16_t tmp2;
|
|
|
|
pci_filter_init(NULL, &filter);
|
|
filter.vendor = vendor;
|
|
|
|
for (temp = pacc->devices; temp; temp = temp->next)
|
|
if (pci_filter_match(&filter, temp)) {
|
|
/* Read PCI class */
|
|
tmp2 = pci_read_word(temp, 0x0a);
|
|
if (tmp2 == devclass)
|
|
return temp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device)
|
|
{
|
|
struct pci_dev *temp;
|
|
struct pci_filter filter;
|
|
|
|
pci_filter_init(NULL, &filter);
|
|
filter.vendor = vendor;
|
|
filter.device = device;
|
|
|
|
for (temp = pacc->devices; temp; temp = temp->next)
|
|
if (pci_filter_match(&filter, temp))
|
|
return temp;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device,
|
|
uint16_t card_vendor, uint16_t card_device)
|
|
{
|
|
struct pci_dev *temp;
|
|
struct pci_filter filter;
|
|
|
|
pci_filter_init(NULL, &filter);
|
|
filter.vendor = vendor;
|
|
filter.device = device;
|
|
|
|
for (temp = pacc->devices; temp; temp = temp->next)
|
|
if (pci_filter_match(&filter, temp)) {
|
|
if ((card_vendor ==
|
|
pci_read_word(temp, PCI_SUBSYSTEM_VENDOR_ID))
|
|
&& (card_device ==
|
|
pci_read_word(temp, PCI_SUBSYSTEM_ID)))
|
|
return temp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int force_boardenable = 0;
|
|
int force_boardmismatch = 0;
|
|
|
|
#if IS_X86
|
|
void probe_superio(void)
|
|
{
|
|
probe_superio_winbond();
|
|
/* ITE probe causes SMSC LPC47N217 to power off the serial UART.
|
|
* Always probe for SMSC first, and if a SMSC Super I/O is detected
|
|
* at a given I/O port, do _not_ probe that port with the ITE probe.
|
|
* This means SMSC probing must be done before ITE probing.
|
|
*/
|
|
//probe_superio_smsc();
|
|
probe_superio_ite();
|
|
}
|
|
|
|
int superio_count = 0;
|
|
#define SUPERIO_MAX_COUNT 3
|
|
|
|
struct superio superios[SUPERIO_MAX_COUNT];
|
|
|
|
int register_superio(struct superio s)
|
|
{
|
|
if (superio_count == SUPERIO_MAX_COUNT)
|
|
return 1;
|
|
superios[superio_count++] = s;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
int is_laptop = 0;
|
|
int laptop_ok = 0;
|
|
|
|
static void internal_chip_writeb(const struct flashctx *flash, uint8_t val,
|
|
chipaddr addr);
|
|
static void internal_chip_writew(const struct flashctx *flash, uint16_t val,
|
|
chipaddr addr);
|
|
static void internal_chip_writel(const struct flashctx *flash, uint32_t val,
|
|
chipaddr addr);
|
|
static uint8_t internal_chip_readb(const struct flashctx *flash,
|
|
const chipaddr addr);
|
|
static uint16_t internal_chip_readw(const struct flashctx *flash,
|
|
const chipaddr addr);
|
|
static uint32_t internal_chip_readl(const struct flashctx *flash,
|
|
const chipaddr addr);
|
|
static void internal_chip_readn(const struct flashctx *flash, uint8_t *buf,
|
|
const chipaddr addr, size_t len);
|
|
static const struct par_master par_master_internal = {
|
|
.chip_readb = internal_chip_readb,
|
|
.chip_readw = internal_chip_readw,
|
|
.chip_readl = internal_chip_readl,
|
|
.chip_readn = internal_chip_readn,
|
|
.chip_writeb = internal_chip_writeb,
|
|
.chip_writew = internal_chip_writew,
|
|
.chip_writel = internal_chip_writel,
|
|
.chip_writen = fallback_chip_writen,
|
|
};
|
|
|
|
enum chipbustype internal_buses_supported = BUS_NONE;
|
|
|
|
int internal_init(void)
|
|
{
|
|
int ret = 0;
|
|
int force_laptop = 0;
|
|
int not_a_laptop = 0;
|
|
const char *board_vendor = NULL;
|
|
const char *board_model = NULL;
|
|
#if IS_X86
|
|
const char *cb_vendor = NULL;
|
|
const char *cb_model = NULL;
|
|
#endif
|
|
char *arg;
|
|
|
|
arg = extract_programmer_param("boardenable");
|
|
if (arg && !strcmp(arg,"force")) {
|
|
force_boardenable = 1;
|
|
} else if (arg && !strlen(arg)) {
|
|
msg_perr("Missing argument for boardenable.\n");
|
|
free(arg);
|
|
return 1;
|
|
} else if (arg) {
|
|
msg_perr("Unknown argument for boardenable: %s\n", arg);
|
|
free(arg);
|
|
return 1;
|
|
}
|
|
free(arg);
|
|
|
|
arg = extract_programmer_param("boardmismatch");
|
|
if (arg && !strcmp(arg,"force")) {
|
|
force_boardmismatch = 1;
|
|
} else if (arg && !strlen(arg)) {
|
|
msg_perr("Missing argument for boardmismatch.\n");
|
|
free(arg);
|
|
return 1;
|
|
} else if (arg) {
|
|
msg_perr("Unknown argument for boardmismatch: %s\n", arg);
|
|
free(arg);
|
|
return 1;
|
|
}
|
|
free(arg);
|
|
|
|
arg = extract_programmer_param("laptop");
|
|
if (arg && !strcmp(arg, "force_I_want_a_brick"))
|
|
force_laptop = 1;
|
|
else if (arg && !strcmp(arg, "this_is_not_a_laptop"))
|
|
not_a_laptop = 1;
|
|
else if (arg && !strlen(arg)) {
|
|
msg_perr("Missing argument for laptop.\n");
|
|
free(arg);
|
|
return 1;
|
|
} else if (arg) {
|
|
msg_perr("Unknown argument for laptop: %s\n", arg);
|
|
free(arg);
|
|
return 1;
|
|
}
|
|
free(arg);
|
|
|
|
arg = extract_programmer_param("mainboard");
|
|
if (arg && strlen(arg)) {
|
|
if (board_parse_parameter(arg, &board_vendor, &board_model)) {
|
|
free(arg);
|
|
return 1;
|
|
}
|
|
} else if (arg && !strlen(arg)) {
|
|
msg_perr("Missing argument for mainboard.\n");
|
|
free(arg);
|
|
return 1;
|
|
}
|
|
free(arg);
|
|
|
|
if (rget_io_perms())
|
|
return 1;
|
|
|
|
/* Default to Parallel/LPC/FWH flash devices. If a known host controller
|
|
* is found, the host controller init routine sets the
|
|
* internal_buses_supported bitfield.
|
|
*/
|
|
internal_buses_supported = BUS_NONSPI;
|
|
|
|
if (try_mtd() == 0)
|
|
return 0;
|
|
|
|
/* Initialize PCI access for flash enables */
|
|
if (pci_init_common() != 0)
|
|
return 1;
|
|
|
|
if (processor_flash_enable()) {
|
|
msg_perr("Processor detection/init failed.\n"
|
|
"Aborting.\n");
|
|
return 1;
|
|
}
|
|
|
|
#if IS_X86
|
|
if ((cb_parse_table(&cb_vendor, &cb_model) == 0) && (board_vendor != NULL) && (board_model != NULL)) {
|
|
if (strcasecmp(board_vendor, cb_vendor) || strcasecmp(board_model, cb_model)) {
|
|
msg_pwarn("Warning: The mainboard IDs set by -p internal:mainboard (%s:%s) do not\n"
|
|
" match the current coreboot IDs of the mainboard (%s:%s).\n",
|
|
board_vendor, board_model, cb_vendor, cb_model);
|
|
if (!force_boardmismatch)
|
|
return 1;
|
|
msg_pinfo("Continuing anyway.\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if IS_X86
|
|
dmi_init();
|
|
|
|
/* In case Super I/O probing would cause pretty explosions. */
|
|
board_handle_before_superio();
|
|
|
|
/* Probe for the Super I/O chip and fill global struct superio. */
|
|
probe_superio();
|
|
#else
|
|
/* FIXME: Enable cbtable searching on all non-x86 platforms supported
|
|
* by coreboot.
|
|
* FIXME: Find a replacement for DMI on non-x86.
|
|
* FIXME: Enable Super I/O probing once port I/O is possible.
|
|
*/
|
|
#endif
|
|
|
|
/* Check laptop whitelist. */
|
|
board_handle_before_laptop();
|
|
|
|
/* Warn if a non-whitelisted laptop is detected. */
|
|
if (is_laptop && !laptop_ok) {
|
|
msg_perr("========================================================================\n");
|
|
if (is_laptop == 1) {
|
|
msg_perr("WARNING! You seem to be running flashrom on an unsupported laptop.\n");
|
|
} else {
|
|
msg_perr("WARNING! You may be running flashrom on an unsupported laptop. We could\n"
|
|
"not detect this for sure because your vendor has not setup the SMBIOS\n"
|
|
"tables correctly. You can enforce execution by adding\n"
|
|
"'-p internal:laptop=this_is_not_a_laptop' to the command line, but\n"
|
|
"please read the following warning if you are not sure.\n\n");
|
|
}
|
|
msg_perr("Laptops, notebooks and netbooks are difficult to support and we\n"
|
|
"recommend to use the vendor flashing utility. The embedded controller\n"
|
|
"(EC) in these machines often interacts badly with flashing.\n"
|
|
"See the manpage and https://flashrom.org/Laptops for details.\n\n"
|
|
"If flash is shared with the EC, erase is guaranteed to brick your laptop\n"
|
|
"and write may brick your laptop.\n"
|
|
"Read and probe may irritate your EC and cause fan failure, backlight\n"
|
|
"failure and sudden poweroff.\n"
|
|
"You have been warned.\n"
|
|
"========================================================================\n");
|
|
|
|
if (force_laptop || (not_a_laptop && (is_laptop == 2))) {
|
|
msg_perr("Proceeding anyway because user forced us to.\n");
|
|
} else {
|
|
msg_perr("Aborting.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* try to enable it. Failure IS an option, since not all motherboards
|
|
* really need this to be done, etc., etc.
|
|
*/
|
|
ret = chipset_flash_enable();
|
|
if (ret == -2) {
|
|
msg_perr("WARNING: No chipset found. Flash detection "
|
|
"will most likely fail.\n");
|
|
} else if (ret == ERROR_FATAL)
|
|
return ret;
|
|
|
|
#if IS_X86
|
|
/* Probe unconditionally for ITE Super I/O chips. This enables LPC->SPI translation on IT87* and
|
|
* parallel writes on IT8705F. Also, this handles the manual chip select for Gigabyte's DualBIOS. */
|
|
init_superio_ite();
|
|
|
|
if (board_flash_enable(board_vendor, board_model, cb_vendor, cb_model)) {
|
|
msg_perr("Aborting to be safe.\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
register_par_master(&par_master_internal, internal_buses_supported);
|
|
return 0;
|
|
}
|
|
|
|
static void internal_chip_writeb(const struct flashctx *flash, uint8_t val,
|
|
chipaddr addr)
|
|
{
|
|
mmio_writeb(val, (void *) addr);
|
|
}
|
|
|
|
static void internal_chip_writew(const struct flashctx *flash, uint16_t val,
|
|
chipaddr addr)
|
|
{
|
|
mmio_writew(val, (void *) addr);
|
|
}
|
|
|
|
static void internal_chip_writel(const struct flashctx *flash, uint32_t val,
|
|
chipaddr addr)
|
|
{
|
|
mmio_writel(val, (void *) addr);
|
|
}
|
|
|
|
static uint8_t internal_chip_readb(const struct flashctx *flash,
|
|
const chipaddr addr)
|
|
{
|
|
return mmio_readb((void *) addr);
|
|
}
|
|
|
|
static uint16_t internal_chip_readw(const struct flashctx *flash,
|
|
const chipaddr addr)
|
|
{
|
|
return mmio_readw((void *) addr);
|
|
}
|
|
|
|
static uint32_t internal_chip_readl(const struct flashctx *flash,
|
|
const chipaddr addr)
|
|
{
|
|
return mmio_readl((void *) addr);
|
|
}
|
|
|
|
static void internal_chip_readn(const struct flashctx *flash, uint8_t *buf,
|
|
const chipaddr addr, size_t len)
|
|
{
|
|
mmio_readn((void *)addr, buf, len);
|
|
return;
|
|
}
|