diff --git a/device/dell/x86_64-dell_s6100_c2538-r0/bin/S6100-BIOS-3.25.0.2-7.bin b/device/dell/x86_64-dell_s6100_c2538-r0/bin/S6100-BIOS-3.25.0.2-7.bin new file mode 100644 index 000000000000..e6371a2602d6 Binary files /dev/null and b/device/dell/x86_64-dell_s6100_c2538-r0/bin/S6100-BIOS-3.25.0.2-7.bin differ diff --git a/platform/broadcom/platform-modules-dell.mk b/platform/broadcom/platform-modules-dell.mk index 1d0c41956bcf..34e404cfae45 100644 --- a/platform/broadcom/platform-modules-dell.mk +++ b/platform/broadcom/platform-modules-dell.mk @@ -36,3 +36,6 @@ $(DELL_S5232F_PLATFORM_MODULE)_PLATFORM = x86_64-dellemc_s5232f_c3538-r0 $(eval $(call add_extra_package,$(DELL_Z9100_PLATFORM_MODULE),$(DELL_S5232F_PLATFORM_MODULE))) SONIC_STRETCH_DEBS += $(DELL_Z9100_PLATFORM_MODULE) + +#flashrom tool +$(shell ./$(PLATFORM_PATH)/sonic-platform-modules-dell/tools/flashrom.sh > /dev/null 2>&1) diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.install b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.install index b9b6abbb35e0..b0c27702cb4a 100644 --- a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.install +++ b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.install @@ -12,3 +12,4 @@ s6100/scripts/platform_watchdog_disable.sh usr/local/bin s6100/scripts/sensors usr/bin s6100/systemd/platform-modules-s6100.service etc/systemd/system s6100/systemd/s6100-lpc-monitor.service etc/systemd/system +tools/flashrom/flashrom usr/local/bin/ diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py index 53d4eadaec0a..d03eee255d3c 100755 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py @@ -10,6 +10,13 @@ try: import os + import sys + import click + import subprocess + import glob + import sonic_device_util + from commands import getstatusoutput + from sonic_platform_base.platform_base import PlatformBase from sonic_platform_base.chassis_base import ChassisBase from sonic_platform.sfp import Sfp from sonic_platform.psu import Psu @@ -76,6 +83,8 @@ class Chassis(ChassisBase): power_reason_dict[33] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC power_reason_dict[44] = ChassisBase.REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED + _component_name_list = ["BIOS", "CPLD1", "CPLD2", "FPGA"] + def __init__(self): ChassisBase.__init__(self) @@ -141,6 +150,19 @@ def _get_pmc_register(self, reg_name): rv = rv.lstrip(" ") return rv + # Run bash command and print output to stdout + def run_command(self, command): + click.echo(click.style("Command: ", fg='cyan') + + click.style(command, fg='green')) + + proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + (out, err) = proc.communicate() + + click.echo(out) + + if proc.returncode != 0: + sys.exit(proc.returncode) + def get_name(self): """ Retrieves the name of the device @@ -230,3 +252,127 @@ def get_reboot_cause(self): return (self.reset_reason_dict[reset_reason], None) return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Invalid Reason") + + def get_component_name_list(self): + """ + Retrieves chassis components list such as BIOS, CPLD, FPGA, etc. + + Returns: + A list containing component name. + """ + return self._component_name_list + + def get_firmware_version(self, component_name): + + version = None + + if component_name in self._component_name_list: + + if component_name == self._component_name_list[0]: # BIOS + status, version = getstatusoutput( + "dmidecode -s system-version") + + elif component_name == self._component_name_list[1]: # CPLD1 + version = None + + elif component_name == self._component_name_list[2]: # CPLD2 + version = None + + elif component_name == self._component_name_list[3]: # SMF + version = None + + return version + + def install_component_firmware(self, component_name, image_path): + + bios_image = None + bios_version = "3.25.0." + bios_file_name = "S6100*BIOS*" + flashrom = "/usr/local/bin/flashrom" + PLATFORM_ROOT_PATH = '/usr/share/sonic/device' + machine_info = sonic_device_util.get_machine_info() + platform = sonic_device_util.get_platform_info(machine_info) + platform_path = "/".join([PLATFORM_ROOT_PATH, platform, "bin"]) + + warning = """ + ******************************************************************** + * Warning - Upgrading BIOS is inherently risky and should only be * + * attempted when necessary. A failure at this upgrade may cause * + * a board RMA. Proceed with caution ! * + ******************************************************************** + """ + + if component_name in self._component_name_list: + if component_name == self._component_name_list[0]: # BIOS + + # current BIOS version + current_bios_version = self.get_firmware_version("BIOS") + + # Construct BIOS image path + if image_path is not None: + image_path = image_path + platform_path + for name in glob.glob( + os.path.join(image_path, bios_file_name)): + bios_image = image_path = name + + if not bios_image: + print "BIOS image file not found:", image_path + return False + + # Extract BIOS image version + bios_image = os.path.basename(bios_image) + bios_image = bios_image.strip('S6100-BIOS-') + bios_image_version = bios_image.strip('.bin') + + if bios_image_version.startswith(bios_version): + bios_image_minor = bios_image_version.replace( + bios_image_version[:7], '') + if bios_image_minor.startswith("2"): + bios_image_minor = bios_image_minor.split("-")[1] + + if current_bios_version.startswith(bios_version): + current_bios_minor = current_bios_version.replace( + current_bios_version[:7], '') + if current_bios_minor.startswith("2"): + current_bios_minor = current_bios_minor.split("-")[1] + + # BIOS version check + if bios_image_minor > current_bios_minor: + + print warning + prompt_text = "New BIOS image " + bios_image_version + \ + " available to install, continue?" + yes = click.confirm(prompt_text) + + elif current_bios_minor > bios_image_minor: + + print warning + prompt_text = "Do you want to downgrade BIOS image from " \ + + current_bios_version + " to " + \ + bios_image_version + " continue?" + + yes = click.confirm(prompt_text) + + else: + print("BIOS is already with {} latest version".format( + current_bios_version)) + return True + + if yes: + command = flashrom + " -p" + " internal" + " -w " + \ + image_path + self.run_command(command) + + elif component_name == self._component_name_list[1]: # CPLD1 + return False + + elif component_name == self._component_name_list[2]: # CPLD2 + return False + + elif component_name == self._component_name_list[3]: # SMF + return False + else: + print "Invalid component Name:", component_name + + return True + diff --git a/platform/broadcom/sonic-platform-modules-dell/tools/0002-Flashrom-support-for-Intel-Rangeley-and-Denverton-CP.patch b/platform/broadcom/sonic-platform-modules-dell/tools/0002-Flashrom-support-for-Intel-Rangeley-and-Denverton-CP.patch new file mode 100644 index 000000000000..ab77df9c4d3b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/tools/0002-Flashrom-support-for-Intel-Rangeley-and-Denverton-CP.patch @@ -0,0 +1,8510 @@ +From 482e06109190eb918208820fd94c8f4a6963f77e Mon Sep 17 00:00:00 2001 +From: paavaanan +Date: Tue, 21 May 2019 10:40:59 -0400 +Subject: [PATCH] Intel-Rangeley-stripped-code-changes + +--- + 82802ab.c | 1 + + Makefile | 109 +++- + README | 26 +- + at45db.c | 562 ++++++++++++++++ + atahpt.c | 2 + + board_enable.c | 19 +- + buspirate_spi.c | 7 +- + cbtable.c | 7 +- + chipdrivers.h | 21 +- + chipset_enable.c | 610 +++++++++++------ + cli_classic.c | 40 +- + dmi.c | 332 ++++++++-- + dnv_smi_spi.c | 185 ++++++ + drkaiser.c | 14 +- + flash.h | 56 +- + flashchips.c | 791 +++++++++++++++++++---- + flashchips.h | 27 +- + flashrom.c | 162 ++++- + ft2232_spi.c | 6 + + gfxnvidia.c | 14 +- + hwaccess.h | 6 + + ich_descriptors.c | 104 +++ + ich_descriptors.h | 104 +++ + ichspi.c | 557 ++++++++++++++-- + internal.c | 5 +- + it85spi.c | 3 + + it87spi.c | 47 +- + layout.c | 126 +++- + linux_spi.c | 12 +- + mcp6x_spi.c | 15 +- + nic3com.c | 2 + + nicintel.c | 29 +- + nicintel_spi.c | 26 +- + nicnatsemi.c | 2 + + nicrealtek.c | 2 + + ogp_spi.c | 14 +- + pcidev.c | 2 +- + physmap.c | 170 +++-- + pony_spi.c | 1 + + print.c | 15 +- + programmer.h | 28 +- + rayer_spi.c | 196 ++++-- + satamv.c | 19 +- + satasii.c | 18 +- + sb600spi.c | 208 +++++- + serial.c | 113 ++-- + serprog.c | 165 +++-- + spi25.c | 35 +- + spi25_statusreg.c | 2 +- + stm50.c | 115 ++++ + udelay.c | 10 +- + util/getrevision.sh | 311 +++++++++ + util/ich_descriptors_tool/ich_descriptors_tool.c | 8 +- + util/z60_flashrom.rules | 4 + + 54 files changed, 4553 insertions(+), 912 deletions(-) + create mode 100644 at45db.c + create mode 100644 dnv_smi_spi.c + create mode 100644 stm50.c + create mode 100755 util/getrevision.sh + +diff --git a/82802ab.c b/82802ab.c +index 608995d..2a43813 100644 +--- a/82802ab.c ++++ b/82802ab.c +@@ -89,6 +89,7 @@ int probe_82802ab(struct flashctx *flash) + return 1; + } + ++/* FIXME: needs timeout */ + uint8_t wait_82802ab(struct flashctx *flash) + { + uint8_t status; +diff --git a/Makefile b/Makefile +index 6e41e5d..d66039a 100644 +--- a/Makefile ++++ b/Makefile +@@ -39,6 +39,7 @@ CFLAGS ?= -Os -Wall -Wshadow + EXPORTDIR ?= . + AR ?= ar + RANLIB ?= ranlib ++DOSLIBS_BASE ?= .. + # The following parameter changes the default programmer that will be used if there is no -p/--programmer + # argument given when running flashrom. The predefined setting does not enable any default so that every + # user has to declare the programmer he wants to use on every run. The rationale for this to be not set +@@ -50,7 +51,8 @@ RANLIB ?= ranlib + # values are those specified in enum programmer in programmer.h (which depend on other CONFIG_* options + # evaluated below, namely those that enable/disable the various programmers). + # Compilation will fail for unspecified values. +-CONFIG_DEFAULT_PROGRAMMER ?= PROGRAMMER_INVALID ++#CONFIG_DEFAULT_PROGRAMMER ?= PROGRAMMER_INVALID ++CONFIG_DEFAULT_PROGRAMMER ?= PROGRAMMER_INTERNAL + + # If your compiler spits out excessive warnings, run make WARNERROR=no + # You shouldn't have to change this flag. +@@ -61,10 +63,13 @@ CFLAGS += -Werror + endif + + ############################################################################### +-# General OS/architecture specific settings. ++# General OS-specific settings. ++# 1. Prepare for later by gathering information about host and target OS ++# 2. Set compiler flags and parameters according to OSes ++# 3. Likewise verify user-supplied CONFIG_* variables. + + # HOST_OS is only used to work around local toolchain issues. +-HOST_OS ?= $(shell uname) ++HOST_OS ?= $(shell uname) + ifeq ($(HOST_OS), MINGW32_NT-5.1) + # Explicitly set CC = gcc on MinGW, otherwise: "cc: command not found". + CC = gcc +@@ -94,13 +99,24 @@ CPPFLAGS += -I/usr/local/include + LDFLAGS += -L/usr/local/lib + endif + ++ifeq ($(TARGET_OS), NetBSD) ++CPPFLAGS += -I/usr/pkg/include ++LDFLAGS += -L/usr/pkg/lib ++endif ++ ++ifeq ($(TARGET_OS), DragonFlyBSD) ++CPPFLAGS += -I/usr/pkg/include ++LDFLAGS += -L/usr/pkg/lib ++endif ++ + ifeq ($(TARGET_OS), DOS) + EXEC_SUFFIX := .exe +-CPPFLAGS += -I../libgetopt ++CPPFLAGS += -I$(DOSLIBS_BASE)/libgetopt + # DJGPP has odd uint*_t definitions which cause lots of format string warnings. + CFLAGS += -Wno-format + # FIXME Check if we can achieve the same effect with -L../libgetopt -lgetopt +-LIBS += ../libgetopt/libgetopt.a ++LIBS += -lgetopt ++LDFLAGS += -L$(DOSLIBS_BASE)/libgetopt/ + # Bus Pirate, Serprog and PonyProg are not supported under DOS (missing serial support). + ifeq ($(CONFIG_BUSPIRATE_SPI), yes) + UNSUPPORTED_FEATURES += CONFIG_BUSPIRATE_SPI=yes +@@ -215,6 +231,10 @@ endif + endif + + ifeq ($(TARGET_OS), libpayload) ++ifeq ($(MAKECMDGOALS),) ++.DEFAULT_GOAL := libflashrom.a ++$(info Setting default goal to libflashrom.a) ++endif + FLASHROM_CFLAGS += -DSTANDALONE + ifeq ($(CONFIG_DUMMY), yes) + UNSUPPORTED_FEATURES += CONFIG_DUMMY=yes +@@ -263,6 +283,10 @@ override CONFIG_LINUX_SPI = no + endif + endif + ++############################################################################### ++# General architecture-specific settings. ++# Like above for the OS, below we verify user-supplied options depending on the target architecture. ++ + # Determine the destination processor architecture. + # IMPORTANT: The following line must be placed before ARCH is ever used + # (of course), but should come after any lines setting CC because the line +@@ -307,10 +331,10 @@ endif + ############################################################################### + # Flash chip drivers and bus support infrastructure. + +-CHIP_OBJS = jedec.o stm50flw0x0x.o w39.o w29ee011.o \ ++CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \ + sst28sf040.o m29f400bt.o 82802ab.o pm49fl00x.o \ + sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o spi25_statusreg.o \ +- opaque.o sfdp.o en29lv640b.o ++ opaque.o sfdp.o en29lv640b.o at45db.o + + ############################################################################### + # Library code. +@@ -322,18 +346,22 @@ LIB_OBJS = layout.o flashrom.o udelay.o programmer.o + + CLI_OBJS = cli_classic.o cli_output.o print.o + +-# Set the flashrom version string from the highest revision number +-# of the checked out flashrom files. ++# Set the flashrom version string from the highest revision number of the checked out flashrom files. + # Note to packagers: Any tree exported with "make export" or "make tarball" + # will not require subversion. The downloadable snapshots are already exported. +-SVNVERSION := $(shell LC_ALL=C svnversion -cn . 2>/dev/null | sed -e "s/.*://" -e "s/\([0-9]*\).*/\1/" | grep "[0-9]" || LC_ALL=C svn info . 2>/dev/null | awk '/^Revision:/ {print $$2 }' | grep "[0-9]" || LC_ALL=C git svn info . 2>/dev/null | awk '/^Revision:/ {print $$2 }' | grep "[0-9]" || echo unknown) ++SVNVERSION := r1781 + + RELEASE := 0.9.7 +-VERSION := $(RELEASE)-r$(SVNVERSION) ++VERSION := $(RELEASE)-$(SVNVERSION) + RELEASENAME ?= $(VERSION) + + SVNDEF := -D'FLASHROM_VERSION="$(VERSION)"' + ++F10FLAGS := -DFORCE10_SPI_CHANGE ++ ++############################################################################### ++# Default settings of CONFIG_* variables. ++ + # Always enable internal/onboard support for now. + CONFIG_INTERNAL ?= yes + +@@ -425,16 +453,39 @@ endif + endif + + ############################################################################### ++# Handle CONFIG_* variables that depend on others set (and verified) above. ++ ++# The external DMI decoder (dmidecode) does not work in libpayload. Bail out if the internal one got disabled. ++ifeq ($(TARGET_OS), libpayload) ++ifeq ($(CONFIG_INTERNAL), yes) ++ifeq ($(CONFIG_INTERNAL_DMI), no) ++UNSUPPORTED_FEATURES += CONFIG_INTERNAL_DMI=no ++else ++override CONFIG_INTERNAL_DMI = yes ++endif ++endif ++endif ++ ++# Use internal DMI/SMBIOS decoder by default instead of relying on dmidecode. ++CONFIG_INTERNAL_DMI ?= yes ++ ++############################################################################### + # Programmer drivers and programmer support infrastructure. ++# Depending on the CONFIG_* variables set and verified above we set compiler flags and parameters below. ++ ++FEATURE_CFLAGS += -DDELL_DENVERTON_SUPPORT -DDELL_AVOTON_SUPPORT + + FEATURE_CFLAGS += -D'CONFIG_DEFAULT_PROGRAMMER=$(CONFIG_DEFAULT_PROGRAMMER)' + + ifeq ($(CONFIG_INTERNAL), yes) + FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1' +-PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o ++PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o internal.o + ifeq ($(ARCH), x86) + PROGRAMMER_OBJS += it87spi.o it85spi.o sb600spi.o amd_imc.o wbsio_spi.o mcp6x_spi.o +-PROGRAMMER_OBJS += ichspi.o ich_descriptors.o ++PROGRAMMER_OBJS += ichspi.o ich_descriptors.o dmi.o dnv_smi_spi.o ++ifeq ($(CONFIG_INTERNAL_DMI), yes) ++FEATURE_CFLAGS += -D'CONFIG_INTERNAL_DMI=1' ++endif + else + endif + NEED_PCI := yes +@@ -597,9 +648,9 @@ PCILIBS += -lpciutils -lpci + PCILIBS += -l$(shell uname -p) + else + ifeq ($(TARGET_OS), DOS) +-# FIXME There needs to be a better way to do this +-CPPFLAGS += -I../libpci/include +-PCILIBS += ../libpci/lib/libpci.a ++CPPFLAGS += -I$(DOSLIBS_BASE)/libpci/include ++LDFLAGS += -L$(DOSLIBS_BASE)/libpci/lib/ ++PCILIBS += -lpci + else + PCILIBS += -lpci + ifeq ($(TARGET_OS), OpenBSD) +@@ -635,10 +686,11 @@ FEATURE_LIBS += $(shell LC_ALL=C grep -q "NEEDLIBZ := yes" .libdeps && printf "% + LIBFLASHROM_OBJS = $(CHIP_OBJS) $(PROGRAMMER_OBJS) $(LIB_OBJS) + OBJS = $(CLI_OBJS) $(LIBFLASHROM_OBJS) + +-all: hwlibs features $(PROGRAM)$(EXEC_SUFFIX) ++all: hwlibs features $(PROGRAM)$(EXEC_SUFFIX) $(PROGRAM).8 + ifeq ($(ARCH), x86) + @+$(MAKE) -C util/ich_descriptors_tool/ TARGET_OS=$(TARGET_OS) EXEC_SUFFIX=$(EXEC_SUFFIX) + endif ++default: all + + $(PROGRAM)$(EXEC_SUFFIX): $(OBJS) + $(CC) $(LDFLAGS) -o $(PROGRAM)$(EXEC_SUFFIX) $(OBJS) $(LIBS) $(PCILIBS) $(FEATURE_LIBS) $(USBLIBS) +@@ -653,13 +705,13 @@ libflashrom.a: $(LIBFLASHROM_OBJS) + TAROPTIONS = $(shell LC_ALL=C tar --version|grep -q GNU && echo "--owner=root --group=root") + + %.o: %.c .features +- $(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FLASHROM_CFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) -o $@ -c $< ++ $(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FLASHROM_CFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) $(F10FLAGS) -o $@ -c $< + + # Make sure to add all names of generated binaries here. + # This includes all frontends and libflashrom. + # We don't use EXEC_SUFFIX here because we want to clean everything. + clean: +- rm -f $(PROGRAM) $(PROGRAM).exe libflashrom.a *.o *.d ++ rm -f $(PROGRAM) $(PROGRAM).exe libflashrom.a *.o *.d $(PROGRAM).8 + @+$(MAKE) -C util/ich_descriptors_tool/ clean + + distclean: clean +@@ -698,11 +750,20 @@ compiler: featuresavailable + @echo $(TARGET_OS)|wc -w|grep -q '^[[:blank:]]*1[[:blank:]]*$$' || \ + ( echo "unknown. Aborting."; exit 1) + @printf "%s\n" '$(TARGET_OS)' ++ifeq ($(TARGET_OS), libpayload) ++ @$(CC) --version 2>&1 | grep -q coreboot || \ ++ ( echo "Warning: It seems you are not using coreboot's reference compiler."; \ ++ echo "This might work but usually does not, please beware." ) ++endif + + define LIBPCI_TEST + /* Avoid a failing test due to libpci header symbol shadowing breakage */ + #define index shadow_workaround_index ++#if !defined __NetBSD__ && !defined __DragonFly__ + #include ++#else ++#include ++#endif + struct pci_access *pacc; + int main(int argc, char **argv) + { +@@ -853,16 +914,20 @@ endif + @$(DIFF) -q .features.tmp .features >/dev/null 2>&1 && rm .features.tmp || mv .features.tmp .features + @rm -f .featuretest.c .featuretest$(EXEC_SUFFIX) + +-install: $(PROGRAM)$(EXEC_SUFFIX) ++$(PROGRAM).8: $(PROGRAM).8.tmpl ++ @sed -e '1 s#".*".*#"$(shell ./util/getrevision.sh -d $(PROGRAM).8.tmpl)" "$(VERSION)"#' <$< >$@ ++ ++install: $(PROGRAM)$(EXEC_SUFFIX) $(PROGRAM).8 + mkdir -p $(DESTDIR)$(PREFIX)/sbin + mkdir -p $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -m 0755 $(PROGRAM)$(EXEC_SUFFIX) $(DESTDIR)$(PREFIX)/sbin + $(INSTALL) -m 0644 $(PROGRAM).8 $(DESTDIR)$(MANDIR)/man8 + +-export: ++export: $(PROGRAM).8 + @rm -rf $(EXPORTDIR)/flashrom-$(RELEASENAME) + @svn export -r BASE . $(EXPORTDIR)/flashrom-$(RELEASENAME) + @sed "s/^SVNVERSION.*/SVNVERSION := $(SVNVERSION)/" Makefile >$(EXPORTDIR)/flashrom-$(RELEASENAME)/Makefile ++ @cp $(PROGRAM).8 "$(EXPORTDIR)/flashrom-$(RELEASENAME)/$(PROGRAM).8" + @LC_ALL=C svn log >$(EXPORTDIR)/flashrom-$(RELEASENAME)/ChangeLog + @echo Exported $(EXPORTDIR)/flashrom-$(RELEASENAME)/ + +@@ -876,6 +941,6 @@ djgpp-dos: clean + libpayload: clean + make CC="CC=i386-elf-gcc lpgcc" AR=i386-elf-ar RANLIB=i386-elf-ranlib + +-.PHONY: all clean distclean compiler hwlibs features export tarball dos featuresavailable ++.PHONY: all install clean distclean compiler hwlibs features export tarball dos featuresavailable + + -include $(OBJS:.o=.d) +diff --git a/README b/README +index 7f24cca..b3e50a6 100644 +--- a/README ++++ b/README +@@ -80,11 +80,9 @@ To compile on Solaris, use: + + gmake LDFLAGS="-L$pathtolibpci" CC="gcc -I$pathtopciheaders" CFLAGS=-O2 + +-To compile on NetBSD or DragonFly BSD, use: ++To compile on NetBSD or DragonFly BSD (with pciutils, libftdi, libusb installed in /usr/pkg/), use: + +- ln -s /usr/pkg/include/pciutils pci +- gmake CPPFLAGS="-I. -I/usr/pkg/include" \ +- LDFLAGS="-L/usr/pkg/lib -Wl,-rpath-link,/usr/pkg/lib" ++ gmake + + To compile on OpenBSD, use: + +@@ -105,20 +103,24 @@ To cross-compile on Linux for DOS: + djcrx-2.04pre_20090725-13ap.i386.rpm + The cross toolchain packages for your distribution may have slightly different + names (look for packages named *djgpp*). +- Download pciutils 3.1.5 and apply http://assembler.cz/flashrom/pciutils.patch +- Download and compile http://assembler.cz/flashrom/libgetopt/ ++ ++ You will need the following library source trees containing their compiled ++ static libraries either in the parent directory of the flashrom source or ++ specify the base folder on compile time with the DOSLIBS_BASE parameter. ++ The default as described above is equal to calling ++ 'make djgpp-dos DOSLIBS_BASE=..' ++ ++ To get and build said libraries... ++ Download pciutils 3.1.5 and apply http://flashrom.org/File:Pciutils.patch.gz + Compile pciutils, see README.DJGPP for instructions. ++ Download and compile http://flashrom.org/File:Libgetopt.tar.gz + Enter the flashrom directory. +- ../libpci should contain pciutils source and binaries. +- ../libgetopt should contain getopt.a from libgetopt. + Run either (change settings where appropriate) + make CC=i586-pc-msdosdjgpp-gcc STRIP=i586-pc-msdosdjgpp-strip + or (above settings hardcoded) + make djgpp-dos +- You might have to add WARNERROR=no to the make command line. +- To run flashrom.exe, download and unpack +- http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi7b.zip and make sure +- CWSDPMI.EXE is in the current directory. ++ To run flashrom.exe, download http://flashrom.org/File:Csdpmi7b.zip and ++ unpack CWSDPMI.EXE into the current directory or one in PATH. + + To cross-compile on Linux for Windows: + +diff --git a/at45db.c b/at45db.c +new file mode 100644 +index 0000000..5c90418 +--- /dev/null ++++ b/at45db.c +@@ -0,0 +1,562 @@ ++/* ++ * Support for Atmel AT45DB series DataFlash chips. ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2012 Aidan Thornton ++ * Copyright (C) 2013 Stefan Tauner ++ * ++ * 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; version 2 of the License. ++ * ++ * 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 ++ */ ++ ++#include ++#include "flash.h" ++#include "chipdrivers.h" ++#include "programmer.h" ++#include "spi.h" ++ ++/* Status register bits */ ++#define AT45DB_READY (1<<7) ++#define AT45DB_CMP (1<<6) ++#define AT45DB_PROT (1<<1) ++#define AT45DB_POWEROF2 (1<<0) ++ ++/* Opcodes */ ++#define AT45DB_STATUS 0xD7 /* NB: this is a block erase command on most other chips(!). */ ++#define AT45DB_DISABLE_PROTECT 0x3D, 0x2A, 0x7F, 0x9A ++#define AT45DB_READ_ARRAY 0xE8 ++#define AT45DB_READ_PROTECT 0x32 ++#define AT45DB_READ_LOCKDOWN 0x35 ++#define AT45DB_PAGE_ERASE 0x81 ++#define AT45DB_BLOCK_ERASE 0x50 ++#define AT45DB_SECTOR_ERASE 0x7C ++#define AT45DB_CHIP_ERASE 0xC7 ++#define AT45DB_CHIP_ERASE_ADDR 0x94809A /* Magic address. See usage. */ ++#define AT45DB_BUFFER1_WRITE 0x84 ++#define AT45DB_BUFFER1_PAGE_PROGRAM 0x88 ++/* Buffer 2 is unused yet. ++#define AT45DB_BUFFER2_WRITE 0x87 ++#define AT45DB_BUFFER2_PAGE_PROGRAM 0x89 ++*/ ++ ++static uint8_t at45db_read_status_register(struct flashctx *flash, uint8_t *status) ++{ ++ static const uint8_t cmd[] = { AT45DB_STATUS }; ++ ++ int ret = spi_send_command(flash, sizeof(cmd), 1, cmd, status); ++ if (ret != 0) ++ msg_cerr("Reading the status register failed!\n"); ++ else ++ msg_cspew("Status register: 0x%02x.\n", *status); ++ return ret; ++} ++ ++int spi_disable_blockprotect_at45db(struct flashctx *flash) ++{ ++ static const uint8_t cmd[4] = { AT45DB_DISABLE_PROTECT }; /* NB: 4 bytes magic number */ ++ int ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); ++ if (ret != 0) { ++ msg_cerr("Sending disable lockdown failed!\n"); ++ return ret; ++ } ++ uint8_t status; ++ ret = at45db_read_status_register(flash, &status); ++ if (ret != 0 || ((status & AT45DB_PROT) != 0)) { ++ msg_cerr("Disabling lockdown failed!\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static unsigned int at45db_get_sector_count(struct flashctx *flash) ++{ ++ unsigned int i, j; ++ unsigned int cnt = 0; ++ for (i = 0; i < NUM_ERASEFUNCTIONS; i++) { ++ if (flash->chip->block_erasers[i].block_erase == &spi_erase_at45db_sector) { ++ for (j = 0; j < NUM_ERASEREGIONS; j++) { ++ cnt += flash->chip->block_erasers[i].eraseblocks[j].count; ++ } ++ } ++ } ++ msg_cspew("%s: number of sectors=%u\n", __func__, cnt); ++ return cnt; ++} ++ ++/* Reads and prettyprints protection/lockdown registers. ++ * Some elegance of the printouts had to be cut down a bit to share this code. */ ++static uint8_t at45db_prettyprint_protection_register(struct flashctx *flash, uint8_t opcode, const char *regname) ++{ ++ const uint8_t cmd[] = { opcode, 0, 0, 0 }; ++ /* The first two sectors share the first result byte. */ ++ uint8_t buf[at45db_get_sector_count(flash) - 1]; ++ ++ int ret = spi_send_command(flash, sizeof(cmd), sizeof(buf), cmd, buf); ++ if (ret != 0) { ++ msg_cerr("Reading the %s register failed!\n", regname); ++ return ret; ++ } ++ ++ unsigned int i; ++ for (i = 0; i < sizeof(buf); i++) { ++ if (buf[i] != 0x00) ++ break; ++ if (i == sizeof(buf) - 1) { ++ msg_cdbg("No Sector is %sed.\n", regname); ++ return 0; ++ } ++ } ++ ++ /* TODO: print which addresses are mapped to (un)locked sectors. */ ++ msg_cdbg("Sector 0a is %s%sed.\n", ((buf[0] & 0xC0) == 0x00) ? "un" : "", regname); ++ msg_cdbg("Sector 0b is %s%sed.\n", ((buf[0] & 0x30) == 0x00) ? "un" : "", regname); ++ for (i = 1; i < sizeof(buf); i++) ++ msg_cdbg("Sector %2u is %s%sed.\n", i, (buf[i] == 0x00) ? "un" : "", regname); ++ ++ return 0; ++} ++ ++/* bit 7: busy flag ++ * bit 6: memory/buffer compare result ++ * bit 5-2: density (encoding see below) ++ * bit 1: protection enabled (soft or hard) ++ * bit 0: "power of 2" page size indicator (e.g. 1 means 256B; 0 means 264B) ++ * ++ * 5-2 encoding: bit 2 is always 1, bits 3-5 encode the density as "2^(bits - 1)" in Mb e.g.: ++ * AT45DB161D 1011 16Mb */ ++int spi_prettyprint_status_register_at45db(struct flashctx *flash) ++{ ++ uint8_t status; ++ if (at45db_read_status_register(flash, &status) != 0) { ++ return 1; ++ } ++ ++ /* AT45DB321C does not support lockdown or a page size of a power of 2... */ ++ const bool isAT45DB321C = (strcmp(flash->chip->name, "AT45DB321C") == 0); ++ msg_cdbg("Chip status register is 0x%02x\n", status); ++ msg_cdbg("Chip status register: Bit 7 / Ready is %sset\n", (status & AT45DB_READY) ? "" : "not "); ++ msg_cdbg("Chip status register: Bit 6 / Compare match is %sset\n", (status & AT45DB_CMP) ? "" : "not "); ++ spi_prettyprint_status_register_bit(status, 5); ++ spi_prettyprint_status_register_bit(status, 4); ++ spi_prettyprint_status_register_bit(status, 3); ++ spi_prettyprint_status_register_bit(status, 2); ++ const uint8_t dens = (status >> 3) & 0x7; /* Bit 2 is always 1, we use the other bits only */ ++ msg_cdbg("Chip status register: Density is %u Mb\n", 1 << (dens - 1)); ++ msg_cdbg("Chip status register: Bit 1 / Protection is %sset\n", (status & AT45DB_PROT) ? "" : "not "); ++ ++ if (isAT45DB321C) ++ spi_prettyprint_status_register_bit(status, 0); ++ else ++ msg_cdbg("Chip status register: Bit 0 / \"Power of 2\" is %sset\n", ++ (status & AT45DB_POWEROF2) ? "" : "not "); ++ ++ if (status & AT45DB_PROT) ++ at45db_prettyprint_protection_register(flash, AT45DB_READ_PROTECT, "protect"); ++ ++ if (!isAT45DB321C) ++ at45db_prettyprint_protection_register(flash, AT45DB_READ_LOCKDOWN, "lock"); ++ ++ return 0; ++} ++ ++/* Probe function for AT45DB* chips that support multiple page sizes. */ ++int probe_spi_at45db(struct flashctx *flash) ++{ ++ uint8_t status; ++ struct flashchip *chip = flash->chip; ++ ++ if (!probe_spi_rdid(flash)) ++ return 0; ++ ++ /* Some AT45DB* chips support two different page sizes each (e.g. 264 and 256 B). In order to tell which ++ * page size this chip has we need to read the status register. */ ++ if (at45db_read_status_register(flash, &status) != 0) ++ return 0; ++ ++ /* We assume sane power-of-2 page sizes and adjust the chip attributes in case this is not the case. */ ++ if ((status & AT45DB_POWEROF2) == 0) { ++ chip->total_size = (chip->total_size / 32) * 33; ++ chip->page_size = (chip->page_size / 32) * 33; ++ ++ unsigned int i, j; ++ for (i = 0; i < NUM_ERASEFUNCTIONS; i++) { ++ struct block_eraser *eraser = &chip->block_erasers[i]; ++ for (j = 0; j < NUM_ERASEREGIONS; j++) { ++ eraser->eraseblocks[j].size = (eraser->eraseblocks[j].size / 32) * 33; ++ } ++ } ++ } ++ ++ switch (chip->page_size) { ++ case 256: chip->gran = write_gran_256bytes; break; ++ case 264: chip->gran = write_gran_264bytes; break; ++ case 512: chip->gran = write_gran_512bytes; break; ++ case 528: chip->gran = write_gran_528bytes; break; ++ case 1024: chip->gran = write_gran_1024bytes; break; ++ case 1056: chip->gran = write_gran_1056bytes; break; ++ default: ++ msg_cerr("%s: unknown page size %d.\n", __func__, chip->page_size); ++ return 0; ++ } ++ ++ msg_cdbg2("%s: total size %i kB, page size %i B\n", __func__, chip->total_size * 1024, chip->page_size); ++ ++ return 1; ++} ++ ++/* Returns the minimum number of bits needed to represent the given address. ++ * FIXME: use mind-blowing implementation. ++ * FIXME: move to utility module. */ ++static uint32_t address_to_bits(uint32_t addr) ++{ ++ unsigned int lzb = 0; ++ while (((1 << (31 - lzb)) & ~addr) != 0) ++ lzb++; ++ return 32 - lzb; ++} ++ ++/* In case of non-power-of-two page sizes we need to convert the address flashrom uses to the address the ++ * DataFlash chips use. The latter uses a segmented address space where the page address is encoded in the ++ * more significant bits and the offset within the page is encoded in the less significant bits. The exact ++ * partition depends on the page size. ++ */ ++static unsigned int at45db_convert_addr(unsigned int addr, unsigned int page_size) ++{ ++ unsigned int page_bits = address_to_bits(page_size - 1); ++ unsigned int at45db_addr = ((addr / page_size) << page_bits) | (addr % page_size); ++ msg_cspew("%s: addr=0x%x, page_size=%u, page_bits=%u -> at45db_addr=0x%x\n", ++ __func__, addr, page_size, page_bits, at45db_addr); ++ return at45db_addr; ++} ++ ++int spi_read_at45db(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ const unsigned int total_size = flash->chip->total_size * 1024; ++ if ((addr + len) > total_size) { ++ msg_cerr("%s: tried to read beyond flash boundary: addr=%u, len=%u, size=%u\n", ++ __func__, addr, len, total_size); ++ return 1; ++ } ++ ++ /* We have to split this up into chunks to fit within the programmer's read size limit, but those ++ * chunks can cross page boundaries. */ ++ const unsigned int max_data_read = flash->pgm->spi.max_data_read; ++ const unsigned int max_chunk = (max_data_read > 0) ? max_data_read : page_size; ++ while (addr < len) { ++ unsigned int chunk = min(max_chunk, len); ++ int ret = spi_nbyte_read(flash, at45db_convert_addr(addr, page_size), buf + addr, chunk); ++ if (ret) { ++ msg_cerr("%s: error sending read command!\n", __func__); ++ return ret; ++ } ++ addr += chunk; ++ } ++ ++ return 0; ++} ++ ++/* Legacy continuous read, used where spi_read_at45db() is not available. ++ * The first 4 (dummy) bytes read need to be discarded. */ ++int spi_read_at45db_e8(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ const unsigned int total_size = flash->chip->total_size * 1024; ++ if ((addr + len) > total_size) { ++ msg_cerr("%s: tried to read beyond flash boundary: addr=%u, len=%u, size=%u\n", ++ __func__, addr, len, total_size); ++ return 1; ++ } ++ ++ /* We have to split this up into chunks to fit within the programmer's read size limit, but those ++ * chunks can cross page boundaries. */ ++ const unsigned int max_data_read = flash->pgm->spi.max_data_read; ++ const unsigned int max_chunk = (max_data_read > 0) ? max_data_read : page_size; ++ while (addr < len) { ++ const unsigned int addr_at45 = at45db_convert_addr(addr, page_size); ++ const unsigned char cmd[] = { ++ AT45DB_READ_ARRAY, ++ (addr_at45 >> 16) & 0xff, ++ (addr_at45 >> 8) & 0xff, ++ (addr_at45 >> 0) & 0xff ++ }; ++ /* We need to leave place for 4 dummy bytes and handle them explicitly. */ ++ unsigned int chunk = min(max_chunk, len + 4); ++ uint8_t tmp[chunk]; ++ int ret = spi_send_command(flash, sizeof(cmd), chunk, cmd, tmp); ++ if (ret) { ++ msg_cerr("%s: error sending read command!\n", __func__); ++ return ret; ++ } ++ /* Copy result without dummy bytes into buf and advance address counter respectively. */ ++ memcpy(buf + addr, tmp + 4, chunk - 4); ++ addr += chunk - 4; ++ } ++ return 0; ++} ++ ++/* Returns 0 when ready, 1 on errors and timeouts. */ ++static int at45db_wait_ready (struct flashctx *flash, unsigned int us, unsigned int retries) ++{ ++ while (true) { ++ uint8_t status; ++ int ret = at45db_read_status_register(flash, &status); ++ if ((status & AT45DB_READY) == AT45DB_READY) ++ return 0; ++ if (ret != 0 || retries-- == 0) ++ return 1; ++ programmer_delay(us); ++ } ++} ++ ++static int at45db_erase(struct flashctx *flash, uint8_t opcode, unsigned int at45db_addr, unsigned int stepsize, unsigned int retries) ++{ ++ const uint8_t cmd[] = { ++ opcode, ++ (at45db_addr >> 16) & 0xff, ++ (at45db_addr >> 8) & 0xff, ++ (at45db_addr >> 0) & 0xff ++ }; ++ ++ /* Send erase command. */ ++ int ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); ++ if (ret != 0) { ++ msg_cerr("%s: error sending erase command!\n", __func__); ++ return ret; ++ } ++ ++ /* Wait for completion. */ ++ ret = at45db_wait_ready(flash, stepsize, retries); ++ if (ret != 0) ++ msg_cerr("%s: chip did not became ready again after sending the erase command!\n", __func__); ++ ++ return ret; ++} ++ ++int spi_erase_at45db_page(struct flashctx *flash, unsigned int addr, unsigned int blocklen) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ const unsigned int total_size = flash->chip->total_size * 1024; ++ ++ if ((addr % page_size) != 0 || (blocklen % page_size) != 0) { ++ msg_cerr("%s: cannot erase partial pages: addr=%u, blocklen=%u\n", __func__, addr, blocklen); ++ return 1; ++ } ++ ++ if ((addr + blocklen) > total_size) { ++ msg_cerr("%s: tried to erase a block beyond flash boundary: addr=%u, blocklen=%u, size=%u\n", ++ __func__, addr, blocklen, total_size); ++ return 1; ++ } ++ ++ /* Needs typically about 35 ms for completion, so let's wait 100 ms in 500 us steps. */ ++ return at45db_erase(flash, AT45DB_PAGE_ERASE, at45db_convert_addr(addr, page_size), 500, 200); ++} ++ ++int spi_erase_at45db_block(struct flashctx *flash, unsigned int addr, unsigned int blocklen) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ const unsigned int total_size = flash->chip->total_size * 1024; ++ ++ if ((addr % page_size) != 0 || (blocklen % page_size) != 0) { // FIXME: should check blocks not pages ++ msg_cerr("%s: cannot erase partial pages: addr=%u, blocklen=%u\n", __func__, addr, blocklen); ++ return 1; ++ } ++ ++ if ((addr + blocklen) > total_size) { ++ msg_cerr("%s: tried to erase a block beyond flash boundary: addr=%u, blocklen=%u, size=%u\n", ++ __func__, addr, blocklen, total_size); ++ return 1; ++ } ++ ++ /* Needs typically between 20 and 100 ms for completion, so let's wait 300 ms in 1 ms steps. */ ++ return at45db_erase(flash, AT45DB_BLOCK_ERASE, at45db_convert_addr(addr, page_size), 1000, 300); ++} ++ ++int spi_erase_at45db_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ const unsigned int total_size = flash->chip->total_size * 1024; ++ ++ if ((addr % page_size) != 0 || (blocklen % page_size) != 0) { // FIXME: should check sectors not pages ++ msg_cerr("%s: cannot erase partial pages: addr=%u, blocklen=%u\n", __func__, addr, blocklen); ++ return 1; ++ } ++ ++ if ((addr + blocklen) > total_size) { ++ msg_cerr("%s: tried to erase a sector beyond flash boundary: addr=%u, blocklen=%u, size=%u\n", ++ __func__, addr, blocklen, total_size); ++ return 1; ++ } ++ ++ /* Needs typically about 5 s for completion, so let's wait 20 seconds in 200 ms steps. */ ++ return at45db_erase(flash, AT45DB_SECTOR_ERASE, at45db_convert_addr(addr, page_size), 200000, 100); ++} ++ ++int spi_erase_at45db_chip(struct flashctx *flash, unsigned int addr, unsigned int blocklen) ++{ ++ const unsigned int total_size = flash->chip->total_size * 1024; ++ ++ if ((addr + blocklen) > total_size) { ++ msg_cerr("%s: tried to erase beyond flash boundary: addr=%u, blocklen=%u, size=%u\n", ++ __func__, addr, blocklen, total_size); ++ return 1; ++ } ++ ++ /* Needs typically from about 5 to over 60 s for completion, so let's wait 100 s in 500 ms steps. ++ * NB: the address is not a real address but a magic number. This hack allows to share code. */ ++ return at45db_erase(flash, AT45DB_CHIP_ERASE, AT45DB_CHIP_ERASE_ADDR, 500000, 200); ++} ++ ++/* This one is really special and works only for AT45CS1282. It uses two different opcodes depending on the ++ * address and has an asymmetric layout. */ ++int spi_erase_at45cs_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ const unsigned int total_size = flash->chip->total_size * 1024; ++ const struct block_eraser be = flash->chip->block_erasers[0]; ++ const unsigned int sec_0a_top = be.eraseblocks[0].size; ++ const unsigned int sec_0b_top = be.eraseblocks[0].size + be.eraseblocks[1].size; ++ ++ if ((addr + blocklen) > total_size) { ++ msg_cerr("%s: tried to erase a sector beyond flash boundary: addr=%u, blocklen=%u, size=%u\n", ++ __func__, addr, blocklen, total_size); ++ return 1; ++ } ++ ++ bool partial_range = false; ++ uint8_t opcode = 0x7C; /* Used for all but sector 0a. */ ++ if (addr < sec_0a_top) { ++ opcode = 0x50; ++ /* One single sector of 8 pages at address 0. */ ++ if (addr != 0 || blocklen != (8 * page_size)) ++ partial_range = true; ++ } else if (addr < sec_0b_top) { ++ /* One single sector of 248 pages adjacent to the first. */ ++ if (addr != sec_0a_top || blocklen != (248 * page_size)) ++ partial_range = true; ++ } else { ++ /* The rest is filled by 63 aligned sectors of 256 pages. */ ++ if ((addr % (256 * page_size)) != 0 || (blocklen % (256 * page_size)) != 0) ++ partial_range = true; ++ } ++ if (partial_range) { ++ msg_cerr("%s: cannot erase partial sectors: addr=%u, blocklen=%u\n", __func__, addr, blocklen); ++ return 1; ++ } ++ ++ /* Needs up to 4 s for completion, so let's wait 20 seconds in 200 ms steps. */ ++ return at45db_erase(flash, opcode, at45db_convert_addr(addr, page_size), 200000, 100); ++} ++ ++static int at45db_fill_buffer1(struct flashctx *flash, uint8_t *bytes, unsigned int off, unsigned int len) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ if ((off + len) > page_size) { ++ msg_cerr("Tried to write %u bytes at offset %u into a buffer of only %u B.\n", ++ len, off, page_size); ++ return 1; ++ } ++ ++ /* Create a suitable buffer to store opcode, address and data chunks for buffer1. */ ++ const unsigned int max_data_write = flash->pgm->spi.max_data_write; ++ const unsigned int max_chunk = (max_data_write > 0 && max_data_write <= page_size) ? ++ max_data_write : page_size; ++ uint8_t buf[4 + max_chunk]; ++ ++ buf[0] = AT45DB_BUFFER1_WRITE; ++ while (off < page_size) { ++ unsigned int cur_chunk = min(max_chunk, page_size - off); ++ buf[1] = (off >> 16) & 0xff; ++ buf[2] = (off >> 8) & 0xff; ++ buf[3] = (off >> 0) & 0xff; ++ memcpy(&buf[4], bytes + off, cur_chunk); ++ int ret = spi_send_command(flash, 4 + cur_chunk, 0, buf, NULL); ++ if (ret != 0) { ++ msg_cerr("%s: error sending buffer write!\n", __func__); ++ return ret; ++ } ++ off += cur_chunk; ++ } ++ return 0; ++} ++ ++static int at45db_commit_buffer1(struct flashctx *flash, unsigned int at45db_addr) ++{ ++ const uint8_t cmd[] = { ++ AT45DB_BUFFER1_PAGE_PROGRAM, ++ (at45db_addr >> 16) & 0xff, ++ (at45db_addr >> 8) & 0xff, ++ (at45db_addr >> 0) & 0xff ++ }; ++ ++ /* Send buffer to device. */ ++ int ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); ++ if (ret != 0) { ++ msg_cerr("%s: error sending buffer to main memory command!\n", __func__); ++ return ret; ++ } ++ ++ /* Wait for completion (typically a few ms). */ ++ ret = at45db_wait_ready(flash, 250, 200); // 50 ms ++ if (ret != 0) { ++ msg_cerr("%s: chip did not became ready again!\n", __func__); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int at45db_program_page(struct flashctx *flash, uint8_t *buf, unsigned int at45db_addr) ++{ ++ int ret = at45db_fill_buffer1(flash, buf, 0, flash->chip->page_size); ++ if (ret != 0) { ++ msg_cerr("%s: filling the buffer failed!\n", __func__); ++ return ret; ++ } ++ ++ ret = at45db_commit_buffer1(flash, at45db_addr); ++ if (ret != 0) { ++ msg_cerr("%s: committing page failed!\n", __func__); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int spi_write_at45db(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len) ++{ ++ const unsigned int page_size = flash->chip->page_size; ++ const unsigned int total_size = flash->chip->total_size; ++ ++ if ((start % page_size) != 0 || (len % page_size) != 0) { ++ msg_cerr("%s: cannot write partial pages: start=%u, len=%u\n", __func__, start, len); ++ return 1; ++ } ++ ++ if ((start + len) > (total_size * 1024)) { ++ msg_cerr("%s: tried to write beyond flash boundary: start=%u, len=%u, size=%u\n", ++ __func__, start, len, total_size); ++ return 1; ++ } ++ ++ unsigned int i; ++ for (i = 0; i < len; i += page_size) { ++ if (at45db_program_page(flash, buf + i, at45db_convert_addr(start + i, page_size)) != 0) { ++ msg_cerr("Writing page %u failed!\n", i); ++ return 1; ++ } ++ } ++ return 0; ++} +diff --git a/atahpt.c b/atahpt.c +index f8be8c4..242e14a 100644 +--- a/atahpt.c ++++ b/atahpt.c +@@ -69,6 +69,8 @@ int atahpt_init(void) + return 1; + + io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_4); ++ if (!io_base_addr) ++ return 1; + + /* Enable flash access. */ + reg32 = pci_read_long(dev, REG_FLASH_ACCESS); +diff --git a/board_enable.c b/board_enable.c +index 074cabb..e2a5fe2 100644 +--- a/board_enable.c ++++ b/board_enable.c +@@ -1488,6 +1488,7 @@ static int intel_ich_gpio_set(int gpio, int raise) + {0x24D0, 0x58, 0x1BFF0000, 0x00030305, 0}, /* 82801EB/ER (ICH5/ICH5R) */ + {0x2640, 0x48, 0x1BFF0000, 0x00030307, 0}, /* 82801FB/FR (ICH6/ICH6R) */ + {0x2641, 0x48, 0x1BFF0000, 0x00030307, 0}, /* 82801FBM (ICH6M) */ ++ {0x27B0, 0x48, 0xFFFFFFFF, 0x000300FF, 0}, /* 82801GDH (ICH7 DH) */ + {0x27B8, 0x48, 0xFFFFFFFF, 0x000300FF, 0}, /* 82801GB/GR (ICH7 Family) */ + {0x27B9, 0x48, 0xFFEBFFFE, 0x000300FE, 0}, /* 82801GBM (ICH7-M) */ + {0x27BD, 0x48, 0xFFEBFFFE, 0x000300FE, 0}, /* 82801GHM (ICH7-M DH) */ +@@ -1665,6 +1666,7 @@ static int intel_ich_gpio_set(int gpio, int raise) + * - abit IP35 Pro: Intel P35 + ICH9R + * - ASUS P5LD2 + * - ASUS P5LD2-VM ++ * - ASUS P5LD2-VM DH + */ + static int intel_ich_gpio16_raise(void) + { +@@ -2266,13 +2268,13 @@ static int p2_whitelist_laptop(void) + * NOTE: Please add boards that _don't_ need such enables or don't work yet + * to the respective tables in print.c. Thanks! + * +- * We use 2 sets of IDs here, you're free to choose which is which. This ++ * We use 2 sets of PCI IDs here, you're free to choose which is which. This + * is to provide a very high degree of certainty when matching a board on + * the basis of subsystem/card IDs. As not every vendor handles + * subsystem/card IDs in a sane manner. + * + * Keep the second set NULLed if it should be ignored. Keep the subsystem IDs +- * NULLed if they don't identify the board fully and if you can't use DMI. ++ * and the dmi identifier NULLed if they don't identify the board fully to disable autodetection. + * But please take care to provide an as complete set of pci ids as possible; + * autodetection is the preferred behaviour and we would like to make sure that + * matches are unique. +@@ -2306,6 +2308,7 @@ const struct board_match board_matches[] = { + #if defined(__i386__) || defined(__x86_64__) + {0x10DE, 0x0547, 0x147B, 0x1C2F, 0x10DE, 0x0548, 0x147B, 0x1C2F, NULL, NULL, NULL, P3, "abit", "AN-M2", 0, NT, nvidia_mcp_gpio2_raise}, + {0x1106, 0x0282, 0x147B, 0x1415, 0x1106, 0x3227, 0x147B, 0x1415, "^AV8 ", NULL, NULL, P3, "abit", "AV8", 0, OK, board_abit_av8}, ++ {0x8086, 0x7190, 0, 0, 0x8086, 0x7110, 0, 0, NULL /* "^I440BX-W977$" */, "abit", "bf6", P3, "abit", "BF6", 0, OK, intel_piix4_gpo26_lower}, + {0x8086, 0x7190, 0, 0, 0x8086, 0x7110, 0, 0, "^i440BX-W977 (BM6)$", NULL, NULL, P3, "abit", "BM6", 0, OK, intel_piix4_gpo26_lower}, + {0x8086, 0x24d3, 0x147b, 0x1014, 0x8086, 0x2578, 0x147b, 0x1014, NULL, NULL, NULL, P3, "abit", "IC7", 0, NT, intel_ich_gpio23_raise}, + {0x8086, 0x2930, 0x147b, 0x1084, 0x11ab, 0x4364, 0x147b, 0x1084, NULL, NULL, NULL, P3, "abit", "IP35", 0, OK, intel_ich_gpio16_raise}, +@@ -2330,6 +2333,7 @@ const struct board_match board_matches[] = { + {0x8086, 0x2570, 0x1849, 0x2570, 0x8086, 0x24d3, 0x1849, 0x24d0, NULL, NULL, NULL, P3, "ASRock", "775i65G", 0, OK, intel_ich_gpio23_raise}, + {0x10DE, 0x0060, 0x1043, 0x80AD, 0x10DE, 0x01E0, 0x1043, 0x80C0, NULL, NULL, NULL, P3, "ASUS", "A7N8X-VM/400", 0, OK, it8712f_gpio12_raise}, + {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3065, 0x1043, 0x80ED, NULL, NULL, NULL, P3, "ASUS", "A7V600-X", 0, OK, it8712f_gpio31_raise}, ++ {0x1106, 0x3177, 0x1043, 0x80F9, 0x1106, 0x3205, 0x1043, 0x80F9, NULL, NULL, NULL, P3, "ASUS", "A7V8X-MX", 0, OK, w836xx_memw_enable_2e}, + {0x1106, 0x3177, 0x1043, 0x80A1, 0x1106, 0x3205, 0x1043, 0x8118, NULL, NULL, NULL, P3, "ASUS", "A7V8X-MX SE", 0, OK, w836xx_memw_enable_2e}, + {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3177, 0x1043, 0x808C, NULL, NULL, NULL, P3, "ASUS", "A7V8X", 0, OK, it8703f_gpio51_raise}, + {0x1106, 0x3099, 0x1043, 0x807F, 0x1106, 0x3147, 0x1043, 0x808C, NULL, NULL, NULL, P3, "ASUS", "A7V333", 0, OK, it8703f_gpio51_raise}, +@@ -2372,12 +2376,14 @@ const struct board_match board_matches[] = { + {0x8086, 0x27b8, 0x1043, 0x2a22, 0x8086, 0x2770, 0x1043, 0x2a22, "^P5LP-LE$", NULL, NULL, P3, "ASUS", "P5LP-LE (Epson OEM)", 0, OK, intel_ich_gpio34_raise}, + {0x8086, 0x27da, 0x1043, 0x8179, 0x8086, 0x27b8, 0x1043, 0x8179, "^P5LD2$", NULL, NULL, P3, "ASUS", "P5LD2", 0, NT, intel_ich_gpio16_raise}, + {0x8086, 0x27da, 0x1043, 0x8179, 0x8086, 0x27b8, 0x1043, 0x8179, "^P5LD2-VM$", NULL, NULL, P3, "ASUS", "P5LD2-VM", 0, NT, intel_ich_gpio16_raise}, ++ {0x8086, 0x27b0, 0x1043, 0x8179, 0x8086, 0x2770, 0x1043, 0x817a, "^P5LD2-VM DH$", NULL, NULL, P3, "ASUS", "P5LD2-VM DH", 0, OK, intel_ich_gpio16_raise}, + {0x10DE, 0x0030, 0x1043, 0x818a, 0x8086, 0x100E, 0x1043, 0x80EE, NULL, NULL, NULL, P3, "ASUS", "P5ND2-SLI Deluxe", 0, OK, nvidia_mcp_gpio10_raise}, + {0x10DE, 0x0260, 0x1043, 0x81BC, 0x10DE, 0x026C, 0x1043, 0x829E, "^P5N-D$", NULL, NULL, P3, "ASUS", "P5N-D", 0, OK, it8718f_gpio63_raise}, + {0x10DE, 0x0260, 0x1043, 0x81BC, 0x10DE, 0x026C, 0x1043, 0x8249, "^P5N-E SLI$",NULL, NULL, P3, "ASUS", "P5N-E SLI", 0, NT, it8718f_gpio63_raise}, + {0x8086, 0x24dd, 0x1043, 0x80a6, 0x8086, 0x2570, 0x1043, 0x8157, NULL, NULL, NULL, P3, "ASUS", "P5PE-VM", 0, OK, intel_ich_gpio21_raise}, + {0x8086, 0x2443, 0x1043, 0x8027, 0x8086, 0x1130, 0x1043, 0x8027, "^CUSL2-C", NULL, NULL, P3, "ASUS", "CUSL2-C", 0, OK, intel_ich_gpio21_raise}, + {0x8086, 0x2443, 0x1043, 0x8027, 0x8086, 0x1130, 0x1043, 0x8027, "^TUSL2-C", NULL, NULL, P3, "ASUS", "TUSL2-C", 0, NT, intel_ich_gpio21_raise}, ++ {0x1106, 0x3059, 0x1106, 0x4161, 0x1106, 0x3065, 0x1106, 0x0102, NULL, NULL, NULL, P3, "Bcom/Clientron", "WinNET P680", 0, OK, w836xx_memw_enable_2e}, + {0x1106, 0x3177, 0x1106, 0x3177, 0x1106, 0x3116, 0x1106, 0x3116, "^KM266-8235$", "biostar", "m7viq", P3, "Biostar", "M7VIQ", 0, NT, w83697xx_memw_enable_2e}, + {0x10b7, 0x9055, 0x1028, 0x0082, 0x8086, 0x7190, 0, 0, NULL, NULL, NULL, P3, "Dell", "OptiPlex GX1", 0, OK, intel_piix4_gpo30_lower}, + {0x8086, 0x3590, 0x1028, 0x016c, 0x1000, 0x0030, 0x1028, 0x016c, NULL, NULL, NULL, P3, "Dell", "PowerEdge 1850", 0, OK, intel_ich_gpio23_raise}, +@@ -2452,6 +2458,12 @@ const struct board_match board_matches[] = { + {0x1106, 0x0259, 0x1106, 0xAA07, 0x1106, 0x3227, 0x1106, 0xAA07, NULL, NULL, NULL, P3, "VIA", "EPIA EK", 0, NT, via_vt823x_gpio9_raise}, + {0x1106, 0x3177, 0x1106, 0xAA01, 0x1106, 0x3123, 0x1106, 0xAA01, NULL, NULL, NULL, P3, "VIA", "EPIA M/MII/...", 0, OK, via_vt823x_gpio15_raise}, + {0x1106, 0x0259, 0x1106, 0x3227, 0x1106, 0x3065, 0x1106, 0x3149, NULL, NULL, NULL, P3, "VIA", "EPIA-N/NL", 0, OK, via_vt823x_gpio9_raise}, ++#ifdef DELL_AVOTON_SUPPORT ++ {0x8086, 0x1f38, 0x8086, 0x7270, 0, 0, 0, 0, NULL, NULL, NULL, P2, "DELL", "AVOTON", 0, OK, p2_not_a_laptop}, ++#endif ++#if DELL_DENVERTON_SUPPORT == 1 ++ {0x8086, 0x19dc, 0x8086, 0x7270, 0, 0, 0, 0, NULL, NULL, NULL, P2, "DELL", "DENVERTON", 0, OK, p2_not_a_laptop}, ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + #endif + { 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, P3, NULL, NULL, 0, NT, NULL}, /* end marker */ + }; +@@ -2559,6 +2571,7 @@ const static struct board_match *board_match_pci_ids(enum board_match_phase phas + } + } + ++#if defined(__i386__) || defined(__x86_64__) + if (board->dmi_pattern) { + if (!has_dmi_support) { + msg_pwarn("Warning: Can't autodetect %s %s, DMI info unavailable.\n", +@@ -2571,7 +2584,7 @@ const static struct board_match *board_match_pci_ids(enum board_match_phase phas + continue; + } + } +- ++#endif // defined(__i386__) || defined(__x86_64__) + return board; + } + +diff --git a/buspirate_spi.c b/buspirate_spi.c +index 9d229f4..f0e84bc 100644 +--- a/buspirate_spi.c ++++ b/buspirate_spi.c +@@ -250,6 +250,7 @@ int buspirate_spi_init(void) + if (!bp_commbuf) { + bp_commbufsize = 0; + msg_perr("Out of memory!\n"); ++ free(dev); + return ERROR_OOM; + } + bp_commbufsize = DEFAULT_BUFSIZE; +@@ -263,8 +264,12 @@ int buspirate_spi_init(void) + return ret; + } + +- if (register_shutdown(buspirate_spi_shutdown, NULL)) ++ if (register_shutdown(buspirate_spi_shutdown, NULL) != 0) { ++ bp_commbufsize = 0; ++ free(bp_commbuf); ++ bp_commbuf = NULL; + return 1; ++ } + + /* This is the brute force version, but it should work. + * It is likely to fail if a previous flashrom run was aborted during a write with the new SPI commands +diff --git a/cbtable.c b/cbtable.c +index e262a84..c100bbb 100644 +--- a/cbtable.c ++++ b/cbtable.c +@@ -261,7 +261,7 @@ int cb_parse_table(const char **vendor, const char **model) + #else + start = 0x0; + #endif +- table_area = physmap_try_ro("low megabyte", start, BYTES_TO_MAP - start); ++ table_area = physmap_ro_unaligned("low megabyte", start, BYTES_TO_MAP - start); + if (ERROR_PTR == table_area) { + msg_perr("Failed getting access to coreboot low tables.\n"); + return -1; +@@ -276,8 +276,9 @@ int cb_parse_table(const char **vendor, const char **model) + if (forward->tag == LB_TAG_FORWARD) { + start = forward->forward; + start &= ~(getpagesize() - 1); +- physunmap(table_area, BYTES_TO_MAP); +- table_area = physmap_try_ro("high tables", start, BYTES_TO_MAP); ++ physunmap_unaligned(table_area, BYTES_TO_MAP); ++ // FIXME: table_area is never unmapped below, nor is it unmapped above in the no-forward case ++ table_area = physmap_ro_unaligned("high tables", start, BYTES_TO_MAP); + if (ERROR_PTR == table_area) { + msg_perr("Failed getting access to coreboot high tables.\n"); + return -1; +diff --git a/chipdrivers.h b/chipdrivers.h +index 091d14c..851e90a 100644 +--- a/chipdrivers.h ++++ b/chipdrivers.h +@@ -64,6 +64,7 @@ int spi_write_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, + /* spi25_statusreg.c */ + uint8_t spi_read_status_register(struct flashctx *flash); + int spi_write_status_register(struct flashctx *flash, int status); ++void spi_prettyprint_status_register_bit(uint8_t status, int bit); + int spi_prettyprint_status_register_plain(struct flashctx *flash); + int spi_prettyprint_status_register_default_welwip(struct flashctx *flash); + int spi_prettyprint_status_register_default_bp1(struct flashctx *flash); +@@ -109,6 +110,19 @@ int read_opaque(struct flashctx *flash, uint8_t *buf, unsigned int start, unsign + int write_opaque(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); + int erase_opaque(struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen); + ++/* at45db.c */ ++int probe_spi_at45db(struct flashctx *flash); ++int spi_prettyprint_status_register_at45db(struct flashctx *flash); ++int spi_disable_blockprotect_at45db(struct flashctx *flash); ++int spi_read_at45db(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); ++int spi_read_at45db_e8(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); ++int spi_write_at45db(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); ++int spi_erase_at45db_page(struct flashctx *flash, unsigned int addr, unsigned int blocklen); ++int spi_erase_at45db_block(struct flashctx *flash, unsigned int addr, unsigned int blocklen); ++int spi_erase_at45db_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); ++int spi_erase_at45db_chip(struct flashctx *flash, unsigned int addr, unsigned int blocklen); ++int spi_erase_at45cs_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); ++ + /* 82802ab.c */ + uint8_t wait_82802ab(struct flashctx *flash); + int probe_82802ab(struct flashctx *flash); +@@ -179,9 +193,10 @@ int printlock_at49f(struct flashctx *flash); + /* w29ee011.c */ + int probe_w29ee011(struct flashctx *flash); + +-/* stm50flw0x0x.c */ +-int erase_sector_stm50flw0x0x(struct flashctx *flash, unsigned int block, unsigned int blocksize); +-int unlock_stm50flw0x0x(struct flashctx *flash); ++/* stm50.c */ ++int erase_sector_stm50(struct flashctx *flash, unsigned int block, unsigned int blocksize); ++int unlock_stm50_uniform(struct flashctx *flash); ++int unlock_stm50_nonuniform(struct flashctx *flash); + + /* en29lv640b.c */ + int probe_en29lv640b(struct flashctx *flash); +diff --git a/chipset_enable.c b/chipset_enable.c +index a2df263..8a8ab66 100644 +--- a/chipset_enable.c ++++ b/chipset_enable.c +@@ -41,6 +41,17 @@ + + #if defined(__i386__) || defined(__x86_64__) + ++#if DELL_AVOTON_SUPPORT == 1 ++uint8_t is_avoton = 0; ++#endif /* DELL_AVOTON_SUPPORT == 1 */ ++ ++#if DELL_DENVERTON_SUPPORT == 1 ++uint8_t is_dnv = 0; ++uint32_t dnv_bios_region_start = 0x00; ++uint32_t dnv_bios_region_size = 0x00; ++uint32_t dnv_bios_region_limit = 0x00; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + static int enable_flash_ali_m1533(struct pci_dev *dev, const char *name) + { + uint8_t tmp; +@@ -260,20 +271,44 @@ static int enable_flash_piix4(struct pci_dev *dev, const char *name) + return 0; + } + +-/* +- * See ie. page 375 of "Intel I/O Controller Hub 7 (ICH7) Family Datasheet" +- * http://download.intel.com/design/chipsets/datashts/30701303.pdf +- */ +-static int enable_flash_ich(struct pci_dev *dev, const char *name, uint8_t bios_cntl) ++/* Note: the ICH0-ICH5 BIOS_CNTL register is actually 16 bit wide, in Poulsbo, Tunnel Creek and other Atom ++ * chipsets/SoCs it is even 32b, but just treating it as 8 bit wide seems to work fine in practice. */ ++static int enable_flash_ich_bios_cntl(struct pci_dev *dev, enum ich_chipset ich_generation, uint8_t bios_cntl) + { + uint8_t old, new, wanted; + +- /* +- * Note: the ICH0-ICH5 BIOS_CNTL register is actually 16 bit wide, in Tunnel Creek it is even 32b, but +- * just treating it as 8 bit wide seems to work fine in practice. +- */ +- wanted = old = pci_read_byte(dev, bios_cntl); ++ switch (ich_generation) { ++ case CHIPSET_ICH_UNKNOWN: ++ return ERROR_FATAL; ++ /* Non-SPI-capable */ ++ case CHIPSET_ICH: ++ case CHIPSET_ICH2345: ++ break; ++ /* Atom chipsets are special: The second byte of BIOS_CNTL (D9h) contains a prefetch bit similar to what ++ * other SPI-capable chipsets have at DCh. ++ * The Tunnel Creek datasheet contains a lot of details about the SPI controller, among other things it ++ * mentions that the prefetching and caching does only happen for direct memory reads. ++ * Therefore - at least for Tunnel Creek - it should not matter to flashrom because we use the ++ * programmed access only and not memory mapping. */ ++ case CHIPSET_TUNNEL_CREEK: ++ case CHIPSET_POULSBO: ++ case CHIPSET_CENTERTON: ++ old = pci_read_byte(dev, bios_cntl + 1); ++ msg_pdbg("BIOS Prefetch Enable: %sabled, ", (old & 1) ? "en" : "dis"); ++ break; ++ case CHIPSET_ICH7: ++ default: /* Future version might behave the same */ ++ old = (pci_read_byte(dev, bios_cntl) >> 2) & 0x3; ++ msg_pdbg("SPI Read Configuration: "); ++ if (old == 3) ++ msg_pdbg("invalid prefetching/caching settings, "); ++ else ++ msg_pdbg("prefetching %sabled, caching %sabled, ", ++ (old & 0x2) ? "en" : "dis", ++ (old & 0x1) ? "dis" : "en"); ++ } + ++ wanted = old = pci_read_byte(dev, bios_cntl); + /* + * Quote from the 6 Series datasheet (Document Number: 324645-004): + * "Bit 5: SMM BIOS Write Protect Disable (SMM_BWP) +@@ -283,11 +318,23 @@ static int enable_flash_ich(struct pci_dev *dev, const char *name, uint8_t bios_ + * + * Try to unset it in any case. + * It won't hurt and makes sense in some cases according to Stefan Reinauer. ++ * ++ * At least in Centerton aforementioned bit is located at bit 7. It is unspecified in all other Atom ++ * and Desktop chipsets before Ibex Peak/5 Series, but we reset bit 5 anyway. + */ +- wanted &= ~(1 << 5); ++ int smm_bwp_bit; ++ if (ich_generation == CHIPSET_CENTERTON) ++ smm_bwp_bit = 7; ++ else ++ smm_bwp_bit = 5; ++ wanted &= ~(1 << smm_bwp_bit); + +- /* Set BIOS Write Enable */ +- wanted |= (1 << 0); ++ /* Tunnel Creek has a cache disable at bit 2 of the lowest BIOS_CNTL byte. */ ++ if (ich_generation == CHIPSET_TUNNEL_CREEK) ++ wanted |= (1 << 2); ++ ++ wanted |= (1 << 0); /* Set BIOS Write Enable */ ++ wanted &= ~(1 << 1); /* Disable lock (futile) */ + + /* Only write the register if it's necessary */ + if (wanted != old) { +@@ -299,64 +346,79 @@ static int enable_flash_ich(struct pci_dev *dev, const char *name, uint8_t bios_ + msg_pdbg("\nBIOS_CNTL = 0x%02x: ", new); + msg_pdbg("BIOS Lock Enable: %sabled, ", (new & (1 << 1)) ? "en" : "dis"); + msg_pdbg("BIOS Write Enable: %sabled\n", (new & (1 << 0)) ? "en" : "dis"); +- if (new & (1 << 5)) ++ if (new & (1 << smm_bwp_bit)) + msg_pwarn("Warning: BIOS region SMM protection is enabled!\n"); + +- + if (new != wanted) +- msg_pwarn("Warning: Setting Bios Control at 0x%x from 0x%02x to 0x%02x on %s failed.\n" +- "New value is 0x%02x.\n", bios_cntl, old, wanted, name, new); ++ msg_pwarn("Warning: Setting Bios Control at 0x%x from 0x%02x to 0x%02x failed.\n" ++ "New value is 0x%02x.\n", bios_cntl, old, wanted, new); + +- /* Return an error if we could not set the write enable */ ++ /* Return an error if we could not set the write enable only. */ + if (!(new & (1 << 0))) + return -1; + + return 0; + } + +-static int enable_flash_ich_4e(struct pci_dev *dev, const char *name) ++static int enable_flash_ich_fwh_decode(struct pci_dev *dev, enum ich_chipset ich_generation) + { +- /* +- * Note: ICH5 has registers similar to FWH_SEL1, FWH_SEL2 and +- * FWH_DEC_EN1, but they are called FB_SEL1, FB_SEL2, FB_DEC_EN1 and +- * FB_DEC_EN2. +- */ +- internal_buses_supported = BUS_FWH; +- return enable_flash_ich(dev, name, 0x4e); +-} +- +-static int enable_flash_ich_dc(struct pci_dev *dev, const char *name) +-{ +- uint32_t fwh_conf; +- int i, tmp; +- char *idsel = NULL; +- int max_decode_fwh_idsel = 0, max_decode_fwh_decode = 0; +- int contiguous = 1; ++ uint8_t fwh_sel1 = 0, fwh_sel2 = 0, fwh_dec_en_lo = 0, fwh_dec_en_hi = 0; /* silence compilers */ ++ bool implemented = 0; ++ switch (ich_generation) { ++ case CHIPSET_ICH: ++ /* FIXME: Unlike later chipsets, ICH and ICH-0 do only support mapping of the top-most 4MB ++ * and therefore do only feature FWH_DEC_EN (E3h, different default too) and FWH_SEL (E8h). */ ++ break; ++ case CHIPSET_ICH2345: ++ fwh_sel1 = 0xe8; ++ fwh_sel2 = 0xee; ++ fwh_dec_en_lo = 0xf0; ++ fwh_dec_en_hi = 0xe3; ++ implemented = 1; ++ break; ++ case CHIPSET_POULSBO: ++ case CHIPSET_TUNNEL_CREEK: ++ /* FIXME: Similar to ICH and ICH-0, Tunnel Creek and Poulsbo do only feature one register each, ++ * FWH_DEC_EN (D7h) and FWH_SEL (D0h). */ ++ break; ++ case CHIPSET_CENTERTON: ++ /* FIXME: Similar to above FWH_DEC_EN (D4h) and FWH_SEL (D0h). */ ++ break; ++ case CHIPSET_ICH6: ++ case CHIPSET_ICH7: ++ default: /* Future version might behave the same */ ++ fwh_sel1 = 0xd0; ++ fwh_sel2 = 0xd4; ++ fwh_dec_en_lo = 0xd8; ++ fwh_dec_en_hi = 0xd9; ++ implemented = 1; ++ break; ++ } + +- idsel = extract_programmer_param("fwh_idsel"); ++ char *idsel = extract_programmer_param("fwh_idsel"); + if (idsel && strlen(idsel)) { +- uint64_t fwh_idsel_old, fwh_idsel; ++ if (!implemented) { ++ msg_perr("Error: fwh_idsel= specified, but (yet) unsupported on this chipset.\n"); ++ goto idsel_garbage_out; ++ } + errno = 0; + /* Base 16, nothing else makes sense. */ +- fwh_idsel = (uint64_t)strtoull(idsel, NULL, 16); ++ uint64_t fwh_idsel = (uint64_t)strtoull(idsel, NULL, 16); + if (errno) { +- msg_perr("Error: fwh_idsel= specified, but value could " +- "not be converted.\n"); ++ msg_perr("Error: fwh_idsel= specified, but value could not be converted.\n"); + goto idsel_garbage_out; + } + if (fwh_idsel & 0xffff000000000000ULL) { +- msg_perr("Error: fwh_idsel= specified, but value had " +- "unused bits set.\n"); ++ msg_perr("Error: fwh_idsel= specified, but value had unused bits set.\n"); + goto idsel_garbage_out; + } +- fwh_idsel_old = pci_read_long(dev, 0xd0); ++ uint64_t fwh_idsel_old = pci_read_long(dev, fwh_sel1); + fwh_idsel_old <<= 16; +- fwh_idsel_old |= pci_read_word(dev, 0xd4); +- msg_pdbg("\nSetting IDSEL from 0x%012" PRIx64 " to " +- "0x%012" PRIx64 " for top 16 MB.", fwh_idsel_old, +- fwh_idsel); +- rpci_write_long(dev, 0xd0, (fwh_idsel >> 16) & 0xffffffff); +- rpci_write_word(dev, 0xd4, fwh_idsel & 0xffff); ++ fwh_idsel_old |= pci_read_word(dev, fwh_sel2); ++ msg_pdbg("\nSetting IDSEL from 0x%012" PRIx64 " to 0x%012" PRIx64 " for top 16 MB.", ++ fwh_idsel_old, fwh_idsel); ++ rpci_write_long(dev, fwh_sel1, (fwh_idsel >> 16) & 0xffffffff); ++ rpci_write_word(dev, fwh_sel2, fwh_idsel & 0xffff); + /* FIXME: Decode settings are not changed. */ + } else if (idsel) { + msg_perr("Error: fwh_idsel= specified, but no value given.\n"); +@@ -366,15 +428,23 @@ idsel_garbage_out: + } + free(idsel); + ++ if (!implemented) { ++ msg_pdbg2("FWH IDSEL handling is not implemented on this chipset."); ++ return 0; ++ } ++ + /* Ignore all legacy ranges below 1 MB. + * We currently only support flashing the chip which responds to + * IDSEL=0. To support IDSEL!=0, flashbase and decode size calculations + * have to be adjusted. + */ ++ int max_decode_fwh_idsel = 0, max_decode_fwh_decode = 0; ++ bool contiguous = 1; ++ uint32_t fwh_conf = pci_read_long(dev, fwh_sel1); ++ int i; + /* FWH_SEL1 */ +- fwh_conf = pci_read_long(dev, 0xd0); + for (i = 7; i >= 0; i--) { +- tmp = (fwh_conf >> (i * 4)) & 0xf; ++ int tmp = (fwh_conf >> (i * 4)) & 0xf; + msg_pdbg("\n0x%08x/0x%08x FWH IDSEL: 0x%x", + (0x1ff8 + i) * 0x80000, + (0x1ff0 + i) * 0x80000, +@@ -386,9 +456,9 @@ idsel_garbage_out: + } + } + /* FWH_SEL2 */ +- fwh_conf = pci_read_word(dev, 0xd4); ++ fwh_conf = pci_read_word(dev, fwh_sel2); + for (i = 3; i >= 0; i--) { +- tmp = (fwh_conf >> (i * 4)) & 0xf; ++ int tmp = (fwh_conf >> (i * 4)) & 0xf; + msg_pdbg("\n0x%08x/0x%08x FWH IDSEL: 0x%x", + (0xff4 + i) * 0x100000, + (0xff0 + i) * 0x100000, +@@ -401,9 +471,11 @@ idsel_garbage_out: + } + contiguous = 1; + /* FWH_DEC_EN1 */ +- fwh_conf = pci_read_word(dev, 0xd8); ++ fwh_conf = pci_read_byte(dev, fwh_dec_en_hi); ++ fwh_conf <<= 8; ++ fwh_conf |= pci_read_byte(dev, fwh_dec_en_lo); + for (i = 7; i >= 0; i--) { +- tmp = (fwh_conf >> (i + 0x8)) & 0x1; ++ int tmp = (fwh_conf >> (i + 0x8)) & 0x1; + msg_pdbg("\n0x%08x/0x%08x FWH decode %sabled", + (0x1ff8 + i) * 0x80000, + (0x1ff0 + i) * 0x80000, +@@ -415,7 +487,7 @@ idsel_garbage_out: + } + } + for (i = 3; i >= 0; i--) { +- tmp = (fwh_conf >> i) & 0x1; ++ int tmp = (fwh_conf >> i) & 0x1; + msg_pdbg("\n0x%08x/0x%08x FWH decode %sabled", + (0xff4 + i) * 0x100000, + (0xff0 + i) * 0x100000, +@@ -429,99 +501,66 @@ idsel_garbage_out: + max_rom_decode.fwh = min(max_decode_fwh_idsel, max_decode_fwh_decode); + msg_pdbg("\nMaximum FWH chip size: 0x%x bytes", max_rom_decode.fwh); + +- /* If we're called by enable_flash_ich_dc_spi, it will override +- * internal_buses_supported anyway. +- */ +- internal_buses_supported = BUS_FWH; +- return enable_flash_ich(dev, name, 0xdc); ++ return 0; + } + +-static int enable_flash_poulsbo(struct pci_dev *dev, const char *name) ++static int enable_flash_ich_fwh(struct pci_dev *dev, enum ich_chipset ich_generation, uint8_t bios_cntl) + { +- uint16_t old, new; + int err; + +- if ((err = enable_flash_ich(dev, name, 0xd8)) != 0) ++ /* Configure FWH IDSEL decoder maps. */ ++ if ((err = enable_flash_ich_fwh_decode(dev, ich_generation)) != 0) + return err; + +- old = pci_read_byte(dev, 0xd9); +- msg_pdbg("BIOS Prefetch Enable: %sabled, ", +- (old & 1) ? "en" : "dis"); +- new = old & ~1; +- +- if (new != old) +- rpci_write_byte(dev, 0xd9, new); +- + internal_buses_supported = BUS_FWH; +- return 0; ++ return enable_flash_ich_bios_cntl(dev, ich_generation, bios_cntl); + } + +-static int enable_flash_tunnelcreek(struct pci_dev *dev, const char *name) ++static int enable_flash_ich0(struct pci_dev *dev, const char *name) + { +- uint16_t old, new; +- uint32_t tmp, bnt; +- void *rcrb; +- int ret; +- +- /* Enable Flash Writes */ +- ret = enable_flash_ich(dev, name, 0xd8); +- if (ret == ERROR_FATAL) +- return ret; +- +- /* Make sure BIOS prefetch mechanism is disabled */ +- old = pci_read_byte(dev, 0xd9); +- msg_pdbg("BIOS Prefetch Enable: %sabled, ", (old & 1) ? "en" : "dis"); +- new = old & ~1; +- if (new != old) +- rpci_write_byte(dev, 0xd9, new); +- +- /* Get physical address of Root Complex Register Block */ +- tmp = pci_read_long(dev, 0xf0) & 0xffffc000; +- msg_pdbg("\nRoot Complex Register Block address = 0x%x\n", tmp); +- +- /* Map RCBA to virtual memory */ +- rcrb = physmap("ICH RCRB", tmp, 0x4000); +- +- /* Test Boot BIOS Strap Status */ +- bnt = mmio_readl(rcrb + 0x3410); +- if (bnt & 0x02) { +- /* If strapped to LPC, no SPI initialization is required */ +- internal_buses_supported = BUS_FWH; +- return 0; +- } ++ return enable_flash_ich_fwh(dev, CHIPSET_ICH, 0x4e); ++} + +- /* This adds BUS_SPI */ +- if (ich_init_spi(dev, tmp, rcrb, 7) != 0) { +- if (!ret) +- ret = ERROR_NONFATAL; +- } ++static int enable_flash_ich2345(struct pci_dev *dev, const char *name) ++{ ++ return enable_flash_ich_fwh(dev, CHIPSET_ICH2345, 0x4e); ++} + +- return ret; ++static int enable_flash_ich6(struct pci_dev *dev, const char *name) ++{ ++ return enable_flash_ich_fwh(dev, CHIPSET_ICH6, 0xdc); + } + +-static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name, +- enum ich_chipset ich_generation) ++static int enable_flash_poulsbo(struct pci_dev *dev, const char *name) + { +- int ret, ret_spi; +- uint8_t bbs, buc; +- uint32_t tmp, gcs; +- void *rcrb; +- const char *const *straps_names; ++ return enable_flash_ich_fwh(dev, CHIPSET_POULSBO, 0xd8); ++} + ++static int enable_flash_ich_spi(struct pci_dev *dev, enum ich_chipset ich_generation, uint8_t bios_cntl) ++{ + static const char *const straps_names_EP80579[] = { "SPI", "reserved", "reserved", "LPC" }; + static const char *const straps_names_ich7_nm10[] = { "reserved", "SPI", "PCI", "LPC" }; ++ static const char *const straps_names_tunnel_creek[] = { "SPI", "LPC" }; + static const char *const straps_names_ich8910[] = { "SPI", "SPI", "PCI", "LPC" }; + static const char *const straps_names_pch567[] = { "LPC", "reserved", "PCI", "SPI" }; + static const char *const straps_names_pch8[] = { "LPC", "reserved", "reserved", "SPI" }; +- static const char *const straps_names_pch8_lp[] = { "SPI", "LPC", "unknown", "unknown" }; ++ static const char *const straps_names_pch8_lp[] = { "SPI", "LPC" }; + static const char *const straps_names_unknown[] = { "unknown", "unknown", "unknown", "unknown" }; ++#ifdef DELL_AVOTON_SUPPORT ++ static const char *const straps_names_avoton[] = { "LPC", "reserved", "reserved", "SPI" }; ++ uint32_t tmp; ++ int ret_fwh = 0; ++ uint32_t new, old; ++ void *spibar; ++#endif + ++ const char *const *straps_names; + switch (ich_generation) { + case CHIPSET_ICH7: + /* EP80579 may need further changes, but this is the least + * intrusive way to get correct BOOT Strap printing without + * changing the rest of its code path). */ +- if (strcmp(name, "EP80579") == 0) ++ if (dev->device_id == 0x5031) + straps_names = straps_names_EP80579; + else + straps_names = straps_names_ich7_nm10; +@@ -531,6 +570,9 @@ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name, + case CHIPSET_ICH10: + straps_names = straps_names_ich8910; + break; ++ case CHIPSET_TUNNEL_CREEK: ++ straps_names = straps_names_tunnel_creek; ++ break; + case CHIPSET_5_SERIES_IBEX_PEAK: + case CHIPSET_6_SERIES_COUGAR_POINT: + case CHIPSET_7_SERIES_PANTHER_POINT: +@@ -543,33 +585,41 @@ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name, + straps_names = straps_names_pch8_lp; + break; + case CHIPSET_8_SERIES_WELLSBURG: // FIXME: check datasheet ++ case CHIPSET_CENTERTON: // FIXME: Datasheet does not mention GCS at all + straps_names = straps_names_unknown; + break; ++#ifdef DELL_AVOTON_SUPPORT ++ case CHIPSET_AVOTON: ++ straps_names = straps_names_avoton; ++ break; ++#endif + default: +- msg_gerr("%s: unknown ICH generation. Please report!\n", +- __func__); ++ msg_gerr("%s: unknown ICH generation. Please report!\n", __func__); + straps_names = straps_names_unknown; + break; + } + +- /* Enable Flash Writes */ +- ret = enable_flash_ich_dc(dev, name); +- if (ret == ERROR_FATAL) +- return ret; +- + /* Get physical address of Root Complex Register Block */ +- tmp = pci_read_long(dev, 0xf0) & 0xffffc000; +- msg_pdbg("Root Complex Register Block address = 0x%x\n", tmp); ++ uint32_t rcra = pci_read_long(dev, 0xf0) & 0xffffc000; ++ msg_pdbg("Root Complex Register Block address = 0x%x\n", rcra); + + /* Map RCBA to virtual memory */ +- rcrb = physmap("ICH RCRB", tmp, 0x4000); ++ void *rcrb = rphysmap("ICH RCRB", rcra, 0x4000); ++ if (rcrb == ERROR_PTR) ++ return ERROR_FATAL; + +- gcs = mmio_readl(rcrb + 0x3410); ++#ifdef DELL_AVOTON_SUPPORT ++ if (ich_generation != CHIPSET_AVOTON) { ++#endif ++ uint32_t gcs = mmio_readl(rcrb + 0x3410); + msg_pdbg("GCS = 0x%x: ", gcs); +- msg_pdbg("BIOS Interface Lock-Down: %sabled, ", +- (gcs & 0x1) ? "en" : "dis"); ++ msg_pdbg("BIOS Interface Lock-Down: %sabled, ", (gcs & 0x1) ? "en" : "dis"); + ++ uint8_t bbs; + switch (ich_generation) { ++ case CHIPSET_TUNNEL_CREEK: ++ bbs = (gcs >> 1) & 0x1; ++ break; + case CHIPSET_8_SERIES_LYNX_POINT_LP: + case CHIPSET_8_SERIES_WELLSBURG: // FIXME: check datasheet + /* Lynx Point LP uses a single bit for GCS */ +@@ -582,91 +632,187 @@ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name, + } + msg_pdbg("Boot BIOS Straps: 0x%x (%s)\n", bbs, straps_names[bbs]); + +- buc = mmio_readb(rcrb + 0x3414); +- msg_pdbg("Top Swap : %s\n", +- (buc & 1) ? "enabled (A16 inverted)" : "not enabled"); ++ if (ich_generation != CHIPSET_TUNNEL_CREEK && ich_generation != CHIPSET_CENTERTON) { ++ uint8_t buc = mmio_readb(rcrb + 0x3414); ++ msg_pdbg("Top Swap : %s\n", (buc & 1) ? "enabled (A16(+) inverted)" : "not enabled"); ++ } + +- /* It seems the ICH7 does not support SPI and LPC chips at the same +- * time. At least not with our current code. So we prevent searching +- * on ICH7 when the southbridge is strapped to LPC +- */ +- internal_buses_supported = BUS_FWH; +- if (ich_generation == CHIPSET_ICH7) { +- if (bbs == 0x03) { +- /* If strapped to LPC, no further SPI initialization is +- * required. */ +- return ret; +- } else { +- /* Disable LPC/FWH if strapped to PCI or SPI */ +- internal_buses_supported = BUS_NONE; +- } ++ /* Handle FWH-related parameters and initialization */ ++#ifdef DELL_AVOTON_SUPPORT ++ ret_fwh = enable_flash_ich_fwh(dev, ich_generation, bios_cntl); ++#else ++ int ret_fwh = enable_flash_ich_fwh(dev, ich_generation, bios_cntl); ++#endif ++ if (ret_fwh == ERROR_FATAL) ++ return ret_fwh; ++ ++ /* SPIBAR is at RCRB+0x3020 for ICH[78], Tunnel Creek and Centerton, and RCRB+0x3800 for ICH9. */ ++ uint16_t spibar_offset; ++ switch (ich_generation) { ++ case CHIPSET_ICH_UNKNOWN: ++ return ERROR_FATAL; ++ case CHIPSET_ICH7: ++ case CHIPSET_ICH8: ++ case CHIPSET_TUNNEL_CREEK: ++ case CHIPSET_CENTERTON: ++ spibar_offset = 0x3020; ++ break; ++ case CHIPSET_ICH9: ++ default: /* Future version might behave the same */ ++ spibar_offset = 0x3800; ++ break; + } ++ msg_pdbg("SPIBAR = 0x%0*" PRIxPTR " + 0x%04x\n", PRIxPTR_WIDTH, (uintptr_t)rcrb, spibar_offset); ++#ifndef DELL_AVOTON_SUPPORT ++ void *spibar = rcrb + spibar_offset; ++#else ++ spibar = rcrb + spibar_offset; ++ } else { ++ tmp = pci_read_long(dev, 0x54) & 0xFFFFFE00; ++ spibar = rphysmap("ICH SPIBAR", tmp, 0x4000); ++ msg_pdbg("SPIBAR = 0x%x\n", tmp); ++ ++ /* BIOS Control Register */ ++ tmp = mmio_readl(spibar + bios_cntl); ++ msg_pdbg("0xFC: 0x%08x (BIOS_CONTROL_REGISTER_BIOS : BCR)\n", tmp); ++ msg_pdbg("BIOS Write Protect Disable : %sabled, \n", ++ (tmp & (1 << 0)) ? "en" : "dis"); ++ msg_pdbg("\nBIOS Lock Enable: %sabled, \n", ++ (tmp & (1 << 1)) ? "en" : "dis"); ++ if (tmp != 1) { ++ mmio_writel(0x1, (spibar + bios_cntl)); ++ } ++ tmp = mmio_readl(spibar + bios_cntl); ++ msg_pdbg("0xFC: 0x%08x (BIOS_CONTROL_REGISTER_BIOS : BCR)\n", tmp); ++ msg_pdbg("BIOS Write Protect Disable : %sabled, \n", ++ (tmp & (1 << 0)) ? "en" : "dis"); ++ if (tmp != 1) { ++ msg_perr("Reboot and change BIOS-> IntelRCSetup -> Relax Security Config -> Disabled to Enabled\n"); ++ return ERROR_FATAL; /* Signal error */ ++ } ++ old = mmio_readb(spibar + 0xFC); ++ msg_pdbg("SPI Read Configuration: \n"); ++ new = (old >> 2) & 0x3; ++ switch (new) { ++ case 0: ++ case 1: ++ case 2: ++ msg_pdbg("prefetching %sabled, caching %sabled, \n", ++ (new & 0x2) ? "en" : "dis", ++ (new & 0x1) ? "dis" : "en"); ++ break; ++ default: ++ msg_pdbg("invalid prefetching/caching settings, \n"); ++ break; ++ } ++ } ++#endif ++ + + /* This adds BUS_SPI */ +- ret_spi = ich_init_spi(dev, tmp, rcrb, ich_generation); ++ int ret_spi = ich_init_spi(dev, spibar, ich_generation); + if (ret_spi == ERROR_FATAL) + return ret_spi; + +- if (ret || ret_spi) +- ret = ERROR_NONFATAL; ++ if (ret_fwh || ret_spi) ++ return ERROR_NONFATAL; + +- return ret; ++ return 0; ++} ++ ++static int enable_flash_tunnelcreek(struct pci_dev *dev, const char *name) ++{ ++ return enable_flash_ich_spi(dev, CHIPSET_TUNNEL_CREEK, 0xd8); ++} ++ ++static int enable_flash_s12x0(struct pci_dev *dev, const char *name) ++{ ++ return enable_flash_ich_spi(dev, CHIPSET_CENTERTON, 0xd8); + } + + static int enable_flash_ich7(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH7); ++ return enable_flash_ich_spi(dev, CHIPSET_ICH7, 0xdc); + } + + static int enable_flash_ich8(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH8); ++ return enable_flash_ich_spi(dev, CHIPSET_ICH8, 0xdc); + } + + static int enable_flash_ich9(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH9); ++ return enable_flash_ich_spi(dev, CHIPSET_ICH9, 0xdc); + } + + static int enable_flash_ich10(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH10); ++ return enable_flash_ich_spi(dev, CHIPSET_ICH10, 0xdc); + } + ++#ifdef DELL_AVOTON_SUPPORT ++static int enable_flash_c2000(struct pci_dev *dev, const char *name) ++{ ++ is_avoton = 1; ++ return enable_flash_ich_spi(dev, CHIPSET_AVOTON, 0xfc); ++} ++#endif ++ ++#if DELL_DENVERTON_SUPPORT == 1 ++static int enable_flash_denverton(struct pci_dev *dev, const char *name) ++{ ++ void *spibar; ++ int32_t ret_spi; ++ uint32_t sbase; ++ enum ich_chipset ich_generation = CHIPSET_DENVERTON; ++ ++ is_dnv = 1; ++ internal_buses_supported = BUS_FWH; ++ ++ /* GET physical address of SPI Base Address and map it */ ++ sbase = pci_read_long(dev, 0x10) & 0xfffffe00; ++ msg_pdbg("SPI_BASE_ADDRESS = 0x%x\n", sbase); ++ spibar = rphysmap("DENVERTON SBASE", sbase, 512); ++ enable_flash_ich_bios_cntl(dev, ich_generation, 0xdc); ++ ret_spi = ich_init_spi(dev, spibar, ich_generation); ++ return ret_spi; ++} ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + /* Ibex Peak aka. 5 series & 3400 series */ + static int enable_flash_pch5(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_5_SERIES_IBEX_PEAK); ++ return enable_flash_ich_spi(dev, CHIPSET_5_SERIES_IBEX_PEAK, 0xdc); + } + + /* Cougar Point aka. 6 series & c200 series */ + static int enable_flash_pch6(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_6_SERIES_COUGAR_POINT); ++ return enable_flash_ich_spi(dev, CHIPSET_6_SERIES_COUGAR_POINT, 0xdc); + } + + /* Panther Point aka. 7 series */ + static int enable_flash_pch7(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_7_SERIES_PANTHER_POINT); ++ return enable_flash_ich_spi(dev, CHIPSET_7_SERIES_PANTHER_POINT, 0xdc); + } + + /* Lynx Point aka. 8 series */ + static int enable_flash_pch8(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_8_SERIES_LYNX_POINT); ++ return enable_flash_ich_spi(dev, CHIPSET_8_SERIES_LYNX_POINT, 0xdc); + } + +-/* Lynx Point aka. 8 series low-power */ ++/* Lynx Point LP aka. 8 series low-power */ + static int enable_flash_pch8_lp(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_8_SERIES_LYNX_POINT_LP); ++ return enable_flash_ich_spi(dev, CHIPSET_8_SERIES_LYNX_POINT_LP, 0xdc); + } + + /* Wellsburg (for Haswell-EP Xeons) */ + static int enable_flash_pch8_wb(struct pci_dev *dev, const char *name) + { +- return enable_flash_ich_dc_spi(dev, name, CHIPSET_8_SERIES_WELLSBURG); ++ return enable_flash_ich_spi(dev, CHIPSET_8_SERIES_WELLSBURG, 0xdc); + } + + static int via_no_byte_merge(struct pci_dev *dev, const char *name) +@@ -737,10 +883,8 @@ static int enable_flash_vt_vx(struct pci_dev *dev, const char *name) + case 0x8410: /* VX900 */ + mmio_base = pci_read_long(dev, 0xbc) << 8; + mmio_base_physmapped = physmap("VIA VX MMIO register", mmio_base, SPI_CNTL_LEN); +- if (mmio_base_physmapped == ERROR_PTR) { +- physunmap(mmio_base_physmapped, SPI_CNTL_LEN); ++ if (mmio_base_physmapped == ERROR_PTR) + return ERROR_FATAL; +- } + + /* Offset 0 - Bit 0 holds SPI Bus0 Enable Bit. */ + spi_cntl = mmio_readl(mmio_base_physmapped) + 0x00; +@@ -882,22 +1026,29 @@ static int enable_flash_sc1100(struct pci_dev *dev, const char *name) + return 0; + } + +-/* Works for AMD-8111, VIA VT82C586A/B, VIA VT82C686A/B. */ +-static int enable_flash_amd8111(struct pci_dev *dev, const char *name) ++/* Works for AMD-768, AMD-8111, VIA VT82C586A/B, VIA VT82C596, VIA VT82C686A/B. ++ * ++ * ROM decode control register matrix ++ * AMD-768 AMD-8111 VT82C586A/B VT82C596 VT82C686A/B ++ * 7 FFC0_0000h–FFFF_FFFFh <- FFFE0000h-FFFEFFFFh <- <- ++ * 6 FFB0_0000h–FFBF_FFFFh <- FFF80000h-FFFDFFFFh <- <- ++ * 5 00E8... <- <- FFF00000h-FFF7FFFFh <- ++ */ ++static int enable_flash_amd_via(struct pci_dev *dev, const char *name, uint8_t decode_val) + { + #define AMD_MAPREG 0x43 + #define AMD_ENREG 0x40 + uint8_t old, new; + +- /* Enable decoding at 0xffb00000 to 0xffffffff. */ + old = pci_read_byte(dev, AMD_MAPREG); +- new = old | 0xC0; ++ new = old | decode_val; + if (new != old) { + rpci_write_byte(dev, AMD_MAPREG, new); + if (pci_read_byte(dev, AMD_MAPREG) != new) { +- msg_pinfo("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n", ++ msg_pwarn("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n", + AMD_MAPREG, new, name); +- } ++ } else ++ msg_pdbg("Changed ROM decode range to 0x%02x successfully.\n", new); + } + + /* Enable 'ROM write' bit. */ +@@ -908,14 +1059,37 @@ static int enable_flash_amd8111(struct pci_dev *dev, const char *name) + rpci_write_byte(dev, AMD_ENREG, new); + + if (pci_read_byte(dev, AMD_ENREG) != new) { +- msg_pinfo("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n", ++ msg_pwarn("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n", + AMD_ENREG, new, name); +- return -1; ++ return ERROR_NONFATAL; + } ++ msg_pdbg2("Set ROM enable bit successfully.\n"); + + return 0; + } + ++static int enable_flash_amd_768_8111(struct pci_dev *dev, const char *name) ++{ ++ /* Enable decoding of 0xFFB00000 to 0xFFFFFFFF (5 MB). */ ++ max_rom_decode.lpc = 5 * 1024 * 1024; ++ return enable_flash_amd_via(dev, name, 0xC0); ++} ++ ++static int enable_flash_vt82c586(struct pci_dev *dev, const char *name) ++{ ++ /* Enable decoding of 0xFFF80000 to 0xFFFFFFFF. (512 kB) */ ++ max_rom_decode.parallel = 512 * 1024; ++ return enable_flash_amd_via(dev, name, 0xC0); ++} ++ ++/* Works for VT82C686A/B too. */ ++static int enable_flash_vt82c596(struct pci_dev *dev, const char *name) ++{ ++ /* Enable decoding of 0xFFF80000 to 0xFFFFFFFF. (1 MB) */ ++ max_rom_decode.parallel = 1024 * 1024; ++ return enable_flash_amd_via(dev, name, 0xE0); ++} ++ + static int enable_flash_sb600(struct pci_dev *dev, const char *name) + { + uint32_t prot; +@@ -1257,6 +1431,8 @@ static int get_flashbase_sc520(struct pci_dev *dev, const char *name) + + /* 1. Map MMCR */ + mmcr = physmap("Elan SC520 MMCR", 0xfffef000, getpagesize()); ++ if (mmcr == ERROR_PTR) ++ return ERROR_FATAL; + + /* 2. Scan PAR0 (0x88) - PAR15 (0xc4) for + * BOOTCS region (PARx[31:29] = 100b)e +@@ -1302,8 +1478,8 @@ const struct penable chipset_enables[] = { + {0x1022, 0x2080, OK, "AMD", "CS5536", enable_flash_cs5536}, + {0x1022, 0x2090, OK, "AMD", "CS5536", enable_flash_cs5536}, + {0x1022, 0x3000, OK, "AMD", "Elan SC520", get_flashbase_sc520}, +- {0x1022, 0x7440, OK, "AMD", "AMD-768", enable_flash_amd8111}, +- {0x1022, 0x7468, OK, "AMD", "AMD8111", enable_flash_amd8111}, ++ {0x1022, 0x7440, OK, "AMD", "AMD-768", enable_flash_amd_768_8111}, ++ {0x1022, 0x7468, OK, "AMD", "AMD-8111", enable_flash_amd_768_8111}, + {0x1022, 0x780e, OK, "AMD", "FCH", enable_flash_sb600}, + {0x1039, 0x0406, NT, "SiS", "501/5101/5501", enable_flash_sis501}, + {0x1039, 0x0496, NT, "SiS", "85C496+497", enable_flash_sis85c496}, +@@ -1388,9 +1564,9 @@ const struct penable chipset_enables[] = { + {0x1106, 0x0691, OK, "VIA", "VT82C69x", via_no_byte_merge}, + {0x1106, 0x8601, NT, "VIA", "VT8601T", via_no_byte_merge}, + /* VIA southbridges */ +- {0x1106, 0x0586, OK, "VIA", "VT82C586A/B", enable_flash_amd8111}, +- {0x1106, 0x0596, OK, "VIA", "VT82C596", enable_flash_amd8111}, +- {0x1106, 0x0686, OK, "VIA", "VT82C686A/B", enable_flash_amd8111}, ++ {0x1106, 0x0586, OK, "VIA", "VT82C586A/B", enable_flash_vt82c586}, ++ {0x1106, 0x0596, OK, "VIA", "VT82C596", enable_flash_vt82c596}, ++ {0x1106, 0x0686, OK, "VIA", "VT82C686A/B", enable_flash_vt82c596}, + {0x1106, 0x3074, OK, "VIA", "VT8233", enable_flash_vt823x}, + {0x1106, 0x3147, OK, "VIA", "VT8233A", enable_flash_vt823x}, + {0x1106, 0x3177, OK, "VIA", "VT8235", enable_flash_vt823x}, +@@ -1405,6 +1581,7 @@ const struct penable chipset_enables[] = { + {0x1166, 0x0200, OK, "Broadcom", "OSB4", enable_flash_osb4}, + {0x1166, 0x0205, OK, "Broadcom", "HT-1000", enable_flash_ht1000}, + {0x17f3, 0x6030, OK, "RDC", "R8610/R3210", enable_flash_rdc_r8610}, ++ {0x8086, 0x0c60, NT, "Intel", "S12x0", enable_flash_s12x0}, + {0x8086, 0x122e, OK, "Intel", "PIIX", enable_flash_piix4}, + {0x8086, 0x1234, NT, "Intel", "MPIIX", enable_flash_piix4}, + {0x8086, 0x1c44, OK, "Intel", "Z68", enable_flash_pch6}, +@@ -1441,21 +1618,21 @@ const struct penable chipset_enables[] = { + {0x8086, 0x1e5f, NT, "Intel", "NM70", enable_flash_pch7}, + {0x8086, 0x2310, NT, "Intel", "DH89xxCC", enable_flash_pch7}, + {0x8086, 0x2390, NT, "Intel", "Coleto Creek", enable_flash_pch7}, +- {0x8086, 0x2410, OK, "Intel", "ICH", enable_flash_ich_4e}, +- {0x8086, 0x2420, OK, "Intel", "ICH0", enable_flash_ich_4e}, +- {0x8086, 0x2440, OK, "Intel", "ICH2", enable_flash_ich_4e}, +- {0x8086, 0x244c, OK, "Intel", "ICH2-M", enable_flash_ich_4e}, +- {0x8086, 0x2450, NT, "Intel", "C-ICH", enable_flash_ich_4e}, +- {0x8086, 0x2480, OK, "Intel", "ICH3-S", enable_flash_ich_4e}, +- {0x8086, 0x248c, OK, "Intel", "ICH3-M", enable_flash_ich_4e}, +- {0x8086, 0x24c0, OK, "Intel", "ICH4/ICH4-L", enable_flash_ich_4e}, +- {0x8086, 0x24cc, OK, "Intel", "ICH4-M", enable_flash_ich_4e}, +- {0x8086, 0x24d0, OK, "Intel", "ICH5/ICH5R", enable_flash_ich_4e}, +- {0x8086, 0x25a1, OK, "Intel", "6300ESB", enable_flash_ich_4e}, +- {0x8086, 0x2640, OK, "Intel", "ICH6/ICH6R", enable_flash_ich_dc}, +- {0x8086, 0x2641, OK, "Intel", "ICH6-M", enable_flash_ich_dc}, +- {0x8086, 0x2642, NT, "Intel", "ICH6W/ICH6RW", enable_flash_ich_dc}, +- {0x8086, 0x2670, OK, "Intel", "631xESB/632xESB/3100", enable_flash_ich_dc}, ++ {0x8086, 0x2410, OK, "Intel", "ICH", enable_flash_ich0}, ++ {0x8086, 0x2420, OK, "Intel", "ICH0", enable_flash_ich0}, ++ {0x8086, 0x2440, OK, "Intel", "ICH2", enable_flash_ich2345}, ++ {0x8086, 0x244c, OK, "Intel", "ICH2-M", enable_flash_ich2345}, ++ {0x8086, 0x2450, NT, "Intel", "C-ICH", enable_flash_ich2345}, ++ {0x8086, 0x2480, OK, "Intel", "ICH3-S", enable_flash_ich2345}, ++ {0x8086, 0x248c, OK, "Intel", "ICH3-M", enable_flash_ich2345}, ++ {0x8086, 0x24c0, OK, "Intel", "ICH4/ICH4-L", enable_flash_ich2345}, ++ {0x8086, 0x24cc, OK, "Intel", "ICH4-M", enable_flash_ich2345}, ++ {0x8086, 0x24d0, OK, "Intel", "ICH5/ICH5R", enable_flash_ich2345}, ++ {0x8086, 0x25a1, OK, "Intel", "6300ESB", enable_flash_ich2345}, ++ {0x8086, 0x2640, OK, "Intel", "ICH6/ICH6R", enable_flash_ich6}, ++ {0x8086, 0x2641, OK, "Intel", "ICH6-M", enable_flash_ich6}, ++ {0x8086, 0x2642, NT, "Intel", "ICH6W/ICH6RW", enable_flash_ich6}, ++ {0x8086, 0x2670, OK, "Intel", "631xESB/632xESB/3100", enable_flash_ich6}, + {0x8086, 0x27b0, OK, "Intel", "ICH7DH", enable_flash_ich7}, + {0x8086, 0x27b8, OK, "Intel", "ICH7/ICH7R", enable_flash_ich7}, + {0x8086, 0x27b9, OK, "Intel", "ICH7M", enable_flash_ich7}, +@@ -1570,6 +1747,12 @@ const struct penable chipset_enables[] = { + {0x8086, 0x8d5d, NT, "Intel", "Wellsburg", enable_flash_pch8_wb}, + {0x8086, 0x8d5e, NT, "Intel", "Wellsburg", enable_flash_pch8_wb}, + {0x8086, 0x8d5f, NT, "Intel", "Wellsburg", enable_flash_pch8_wb}, ++#ifdef DELL_AVOTON_SUPPORT ++ {0x8086, 0x1f38, OK, "Intel", "C2000", enable_flash_c2000}, ++#endif ++#if DELL_DENVERTON_SUPPORT == 1 ++ {0x8086, 0x19e0, OK, "Intel", "Denverton", enable_flash_denverton}, ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + #endif + {0}, + }; +@@ -1597,14 +1780,23 @@ int chipset_flash_enable(void) + chipset_enables[i].device_name); + continue; + } ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("Found chipset \"%s %s\"", ++ chipset_enables[i].vendor_name, ++ chipset_enables[i].device_name); ++#else + msg_pinfo("Found chipset \"%s %s\"", + chipset_enables[i].vendor_name, + chipset_enables[i].device_name); ++#endif + msg_pdbg(" with PCI ID %04x:%04x", + chipset_enables[i].vendor_id, + chipset_enables[i].device_id); ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg(". "); ++#else + msg_pinfo(". "); +- ++#endif + if (chipset_enables[i].status == NT) { + msg_pinfo("\nThis chipset is marked as untested. If " + "you are using an up-to-date version\nof " +@@ -1614,20 +1806,44 @@ int chipset_flash_enable(void) + "flashrom@flashrom.org including a verbose " + "(-V) log.\nThank you!\n"); + } ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("Enabling flash write... "); ++#else + msg_pinfo("Enabling flash write... "); ++#endif + ret = chipset_enables[i].doit(dev, + chipset_enables[i].device_name); + if (ret == NOT_DONE_YET) { + ret = -2; ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("OK - searching further chips.\n"); ++#else + msg_pinfo("OK - searching further chips.\n"); ++#endif + } else if (ret < 0) ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("FAILED!\n"); ++#else + msg_pinfo("FAILED!\n"); ++#endif + else if (ret == 0) ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("OK.\n"); ++#else + msg_pinfo("OK.\n"); ++#endif + else if (ret == ERROR_NONFATAL) ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("PROBLEMS, continuing anyway\n"); ++#else + msg_pinfo("PROBLEMS, continuing anyway\n"); ++#endif + if (ret == ERROR_FATAL) { ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("FATAL ERROR!\n"); ++#else + msg_perr("FATAL ERROR!\n"); ++#endif + return ret; + } + } +diff --git a/cli_classic.c b/cli_classic.c +index 4c71d07..09565f4 100644 +--- a/cli_classic.c ++++ b/cli_classic.c +@@ -31,6 +31,10 @@ + #include "flashchips.h" + #include "programmer.h" + ++#if DELL_DENVERTON_SUPPORT == 1 ++extern int8_t is_dnv; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + static void cli_classic_usage(const char *name) + { + printf("Please note that the command line interface for flashrom has changed between\n" +@@ -136,9 +140,10 @@ int main(int argc, char *argv[]) + char *tempstr = NULL; + char *pparam = NULL; + ++#ifndef FORCE10_SPI_CHANGE + print_version(); + print_banner(); +- ++#endif + if (selfcheck()) + exit(1); + +@@ -221,8 +226,6 @@ int main(int argc, char *argv[]) + free(tempstr); + cli_classic_abort_usage(); + } +- /* FIXME: A pointer to the image name is saved in a static array (of size MAX_ROMLAYOUT) +- * by register_include_arg() and needs to be freed after processing them. */ + break; + case 'L': + if (++operation_specified > 1) { +@@ -295,6 +298,9 @@ int main(int argc, char *argv[]) + "specified. Aborting.\n"); + cli_classic_abort_usage(); + } ++#ifdef FORCE10_SPI_CHANGE ++ print_version(); ++#endif + exit(0); + break; + case 'h': +@@ -372,6 +378,12 @@ int main(int argc, char *argv[]) + ret = 1; + goto out; + } ++ if (layoutfile != NULL && !write_it) { ++ msg_gerr("Layout files are currently supported for write operations only.\n"); ++ ret = 1; ++ goto out; ++ } ++ + if (process_include_args()) { + ret = 1; + goto out; +@@ -393,8 +405,13 @@ int main(int argc, char *argv[]) + if (prog == PROGRAMMER_INVALID) { + if (CONFIG_DEFAULT_PROGRAMMER != PROGRAMMER_INVALID) { + prog = CONFIG_DEFAULT_PROGRAMMER; ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("Using default programmer \"%s\".\n", ++ programmer_table[CONFIG_DEFAULT_PROGRAMMER].name); ++#else + msg_pinfo("Using default programmer \"%s\".\n", + programmer_table[CONFIG_DEFAULT_PROGRAMMER].name); ++#endif + } else { + msg_perr("Please select a programmer with the --programmer parameter.\n" + "Previously this was not necessary because there was a default set.\n" +@@ -489,6 +506,10 @@ int main(int argc, char *argv[]) + tempstr = flashbuses_to_text(flashes[0].chip->bustype); + msg_gdbg("Found %s flash chip \"%s\" (%d kB, %s).\n", + flashes[0].chip->vendor, flashes[0].chip->name, flashes[0].chip->total_size, tempstr); ++ if ((flashes[0].chip->total_size > flashes[0].bios_size) && (flashes[0].bios_size != 0)) { ++ msg_gdbg("BIOS Size in flash chip: %d kB\n", flashes[0].bios_size); ++ flashes[0].chip->total_size = flashes[0].bios_size; ++ } + free(tempstr); + } + +@@ -517,7 +538,19 @@ int main(int argc, char *argv[]) + * Give the chip time to settle. + */ + programmer_delay(100000); ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ dnv_smi_open_res(); ++ dnv_smi_doit(0, NULL, SMI_ENABLE_SPI); ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it); ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ dnv_smi_doit(0, NULL, SMI_DISABLE_SPI); ++ dnv_smi_release_res(); ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + /* Note: doit() already calls programmer_shutdown(). */ + goto out; + +@@ -527,6 +560,7 @@ out: + for (i = 0; i < chipcount; i++) + free(flashes[i].chip); + ++ layout_cleanup(); + free(filename); + free(layoutfile); + free(pparam); +diff --git a/dmi.c b/dmi.c +index 242889f..25ddd93 100644 +--- a/dmi.c ++++ b/dmi.c +@@ -1,7 +1,10 @@ + /* + * This file is part of the flashrom project. + * ++ * Copyright (C) 2000-2002 Alan Cox ++ * Copyright (C) 2002-2010 Jean Delvare + * Copyright (C) 2009,2010 Michael Karcher ++ * Copyright (C) 2011-2013 Stefan Tauner + * + * 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 +@@ -26,33 +29,28 @@ + #include "flash.h" + #include "programmer.h" + +-int has_dmi_support = 0; +- +-#if STANDALONE ++#if defined(__i386__) || defined(__x86_64__) + +-/* Stub to indicate missing DMI functionality. +- * has_dmi_support is 0 by default, so nothing to do here. +- * Because dmidecode is not available on all systems, the goal is to implement +- * the DMI subset we need directly in this file. +- */ +-void dmi_init(void) +-{ +-} ++/* Enable SMBIOS decoding. Currently legacy DMI decoding is enough. */ ++#define SM_SUPPORT 0 + +-int dmi_match(const char *pattern) +-{ +- return 0; +-} ++/* Strings longer than 4096 in DMI are just insane. */ ++#define DMI_MAX_ANSWER_LEN 4096 + +-#else /* STANDALONE */ ++int has_dmi_support = 0; + +-static const char *dmidecode_names[] = { +- "system-manufacturer", +- "system-product-name", +- "system-version", +- "baseboard-manufacturer", +- "baseboard-product-name", +- "baseboard-version", ++static struct { ++ const char *const keyword; ++ const uint8_t type; ++ const uint8_t offset; ++ char *value; ++} dmi_strings[] = { ++ { "system-manufacturer", 1, 0x04, NULL }, ++ { "system-product-name", 1, 0x05, NULL }, ++ { "system-version", 1, 0x06, NULL }, ++ { "baseboard-manufacturer", 2, 0x04, NULL }, ++ { "baseboard-product-name", 2, 0x05, NULL }, ++ { "baseboard-version", 2, 0x06, NULL }, + }; + + /* This list is used to identify supposed laptops. The is_laptop field has the +@@ -66,9 +64,9 @@ static const char *dmidecode_names[] = { + * The types below are the most common ones. + */ + static const struct { +- unsigned char type; +- unsigned char is_laptop; +- const char *name; ++ uint8_t type; ++ uint8_t is_laptop; ++ char *name; + } dmi_chassis_types[] = { + {0x01, 2, "Other"}, + {0x02, 2, "Unknown"}, +@@ -86,20 +84,206 @@ static const struct { + {0x18, 0, "Sealed-case PC"}, /* used by Supermicro (X8SIE) */ + }; + +-#define DMI_COMMAND_LEN_MAX 260 +-static const char *dmidecode_command = "dmidecode"; ++#if CONFIG_INTERNAL_DMI == 1 ++#if defined(__DJGPP__) || defined(FORCE10_SPI_CHANGE) /* There is no strnlen in DJGPP. FIXME: Move this to a common utility file. */ ++size_t strnlen(const char *str, size_t n) ++{ ++ size_t i; ++ for (i = 0; i < n && str[i] != '\0'; i++) ++ ; ++ return i; ++} ++#endif + +-static char *dmistrings[ARRAY_SIZE(dmidecode_names)]; ++static bool dmi_checksum(const uint8_t * const buf, size_t len) ++{ ++ uint8_t sum = 0; ++ size_t a; + +-/* Strings longer than 4096 in DMI are just insane. */ +-#define DMI_MAX_ANSWER_LEN 4096 ++ for (a = 0; a < len; a++) ++ sum += buf[a]; ++ return (sum == 0); ++} ++ ++static char *dmi_string(uint8_t *buf, uint8_t string_id, uint8_t *limit) ++{ ++ size_t i, len; ++ ++ if (string_id == 0) ++ return "Not Specified"; ++ ++ while (string_id > 1 && string_id--) { ++ if (buf > limit) { ++ msg_perr("DMI table is broken (string portion out of bounds)!\n"); ++ return ""; ++ } ++ buf += strnlen((char *)buf, limit - buf) + 1; ++ } ++ ++ if (!*buf) /* as long as the current byte we're on isn't null */ ++ return ""; ++ ++ len = strnlen((char *)buf, limit - buf); ++ if (len > DMI_MAX_ANSWER_LEN) ++ len = DMI_MAX_ANSWER_LEN; ++ ++ /* fix junk bytes in the string */ ++ for (i = 0; i < len && buf[i] != '\0'; i++) ++ if (buf[i] < 32 || buf[i] >= 127) ++ buf[i] = ' '; ++ ++ return (char *)buf; ++} ++ ++static void dmi_chassis_type(uint8_t code) ++{ ++ int i; ++ code &= 0x7f; /* bits 6:0 are chassis type, 7th bit is the lock bit */ ++ is_laptop = 2; ++ for (i = 0; i < ARRAY_SIZE(dmi_chassis_types); i++) { ++ if (code == dmi_chassis_types[i].type) { ++ msg_pdbg("DMI string chassis-type: \"%s\"\n", dmi_chassis_types[i].name); ++ is_laptop = dmi_chassis_types[i].is_laptop; ++ break; ++ } ++ } ++} ++ ++static void dmi_table(uint32_t base, uint16_t len, uint16_t num) ++{ ++ int i = 0, j = 0; ++ ++ uint8_t *dmi_table_mem = physmap_ro("DMI Table", base, len); ++ if (dmi_table_mem == NULL) { ++ msg_perr("Unable to access DMI Table\n"); ++ return; ++ } ++ ++ uint8_t *data = dmi_table_mem; ++ uint8_t *limit = dmi_table_mem + len; ++ ++ /* SMBIOS structure header is always 4 B long and contains: ++ * - uint8_t type; // see dmi_chassis_types's type ++ * - uint8_t length; // data section w/ header w/o strings ++ * - uint16_t handle; ++ */ ++ while (i < num && data + 4 <= limit) { ++ /* - If a short entry is found (less than 4 bytes), not only it ++ * is invalid, but we cannot reliably locate the next entry. ++ * - If the length value indicates that this structure spreads ++ * accross the table border, something is fishy too. ++ * Better stop at this point, and let the user know his/her ++ * table is broken. ++ */ ++ if (data[1] < 4 || data + data[1] > limit) { ++ msg_perr("DMI table is broken (bogus header)!\n"); ++ break; ++ } ++ ++ if(data[0] == 3) { ++ if (data + 5 <= limit) ++ dmi_chassis_type(data[5]); ++ /* else the table is broken, but laptop detection is optional, hence continue. */ ++ } else ++ for (j = 0; j < ARRAY_SIZE(dmi_strings); j++) { ++ uint8_t offset = dmi_strings[j].offset; ++ uint8_t type = dmi_strings[j].type; ++ ++ if (data[0] != type) ++ continue; ++ ++ if (data[1] <= offset || data+offset > limit) { ++ msg_perr("DMI table is broken (offset out of bounds)!\n"); ++ goto out; ++ } ++ ++ /* Table will be unmapped, hence fill the struct with duplicated strings. */ ++ dmi_strings[j].value = strdup(dmi_string(data + data[1], data[offset], limit)); ++ } ++ /* Find next structure by skipping data and string sections */ ++ data += data[1]; ++ while (data + 1 <= limit) { ++ if (data[0] == 0 && data[1] == 0) ++ break; ++ data++; ++ } ++ data += 2; ++ i++; ++ } ++out: ++ physunmap(dmi_table_mem, len); ++} ++ ++#if SM_SUPPORT ++static int smbios_decode(uint8_t *buf) ++{ ++ /* TODO: other checks mentioned in the conformance guidelines? */ ++ if (!dmi_checksum(buf, buf[0x05]) || ++ (memcmp(buf + 0x10, "_DMI_", 5) != 0) || ++ !dmi_checksum(buf + 0x10, 0x0F)) ++ return 0; ++ ++ dmi_table(mmio_readl(buf + 0x18), mmio_readw(buf + 0x16), mmio_readw(buf + 0x1C)); ++ ++ return 1; ++} ++#endif ++ ++static int legacy_decode(uint8_t *buf) ++{ ++ if (!dmi_checksum(buf, 0x0F)) ++ return 1; ++ ++ dmi_table(mmio_readl(buf + 0x08), mmio_readw(buf + 0x06), mmio_readw(buf + 0x0C)); ++ ++ return 0; ++} ++ ++int dmi_fill(void) ++{ ++ size_t fp; ++ uint8_t *dmi_mem; ++ int ret = 1; ++ ++ msg_pdbg("Using Internal DMI decoder.\n"); ++ /* There are two ways specified to gain access to the SMBIOS table: ++ * - EFI's configuration table contains a pointer to the SMBIOS table. On linux it can be obtained from ++ * sysfs. EFI's SMBIOS GUID is: {0xeb9d2d31,0x2d88,0x11d3,0x9a,0x16,0x0,0x90,0x27,0x3f,0xc1,0x4d} ++ * - Scanning physical memory address range 0x000F0000h to 0x000FFFFF for the anchor-string(s). */ ++ dmi_mem = physmap_ro("DMI", 0xF0000, 0x10000); ++ if (dmi_mem == ERROR_PTR) ++ return ret; ++ ++ for (fp = 0; fp <= 0xFFF0; fp += 16) { ++#if SM_SUPPORT ++ if (memcmp(dmi_mem + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { ++ if (smbios_decode(dmi_mem + fp)) // FIXME: length check ++ goto out; ++ } else ++#endif ++ if (memcmp(dmi_mem + fp, "_DMI_", 5) == 0) ++ if (legacy_decode(dmi_mem + fp) == 0) { ++ ret = 0; ++ goto out; ++ } ++ } ++ msg_pinfo("No DMI table found.\n"); ++out: ++ physunmap(dmi_mem, 0x10000); ++ return ret; ++} ++ ++#else /* CONFIG_INTERNAL_DMI */ ++ ++#define DMI_COMMAND_LEN_MAX 300 ++static const char *dmidecode_command = "dmidecode"; + + static char *get_dmi_string(const char *string_name) + { + FILE *dmidecode_pipe; + char *result; +- char answerbuf[DMI_MAX_ANSWER_LEN]; +- char commandline[DMI_COMMAND_LEN_MAX + 40]; ++ char answerbuf[DMI_MAX_ANSWER_LEN + 1] = {'\0'}; ++ char commandline[DMI_COMMAND_LEN_MAX + 1] = {'\0'}; + + snprintf(commandline, sizeof(commandline), + "%s -s %s", dmidecode_command, string_name); +@@ -138,46 +322,31 @@ static char *get_dmi_string(const char *string_name) + /* Chomp trailing newline. */ + if (answerbuf[0] != 0 && answerbuf[strlen(answerbuf) - 1] == '\n') + answerbuf[strlen(answerbuf) - 1] = 0; +- msg_pdbg("DMI string %s: \"%s\"\n", string_name, answerbuf); + + result = strdup(answerbuf); +- if (!result) ++ if (result == NULL) + msg_pwarn("Warning: Out of memory - DMI support fails"); + + return result; + } + +-static int dmi_shutdown(void *data) +-{ +- int i; +- for (i = 0; i < ARRAY_SIZE(dmistrings); i++) { +- free(dmistrings[i]); +- dmistrings[i] = NULL; +- } +- return 0; +-} +- +-void dmi_init(void) ++int dmi_fill(void) + { + int i; + char *chassis_type; + +- if (register_shutdown(dmi_shutdown, NULL)) +- return; +- +- has_dmi_support = 1; +- for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++) { +- dmistrings[i] = get_dmi_string(dmidecode_names[i]); +- if (!dmistrings[i]) { +- has_dmi_support = 0; +- return; +- } ++ msg_pdbg("Using External DMI decoder.\n"); ++ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) { ++ dmi_strings[i].value = get_dmi_string(dmi_strings[i].keyword); ++ if (dmi_strings[i].value == NULL) ++ return 1; + } + + chassis_type = get_dmi_string("chassis-type"); + if (chassis_type == NULL) +- return; ++ return 0; /* chassis-type handling is optional anyway */ + ++ msg_pdbg("DMI string chassis-type: \"%s\"\n", chassis_type); + is_laptop = 2; + for (i = 0; i < ARRAY_SIZE(dmi_chassis_types); i++) { + if (strcasecmp(chassis_type, dmi_chassis_types[i].name) == 0) { +@@ -185,6 +354,33 @@ void dmi_init(void) + break; + } + } ++ free(chassis_type); ++ return 0; ++} ++ ++#endif /* CONFIG_INTERNAL_DMI */ ++ ++static int dmi_shutdown(void *data) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) { ++ free(dmi_strings[i].value); ++ dmi_strings[i].value = NULL; ++ } ++ return 0; ++} ++ ++void dmi_init(void) ++{ ++ /* Register shutdown function before we allocate anything. */ ++ if (register_shutdown(dmi_shutdown, NULL)) { ++ msg_pwarn("Warning: Could not register DMI shutdown function - continuing without DMI info.\n"); ++ return; ++ } ++ ++ /* dmi_fill fills the dmi_strings array, and if possible sets the global is_laptop variable. */ ++ if (dmi_fill() != 0) ++ return; + + switch (is_laptop) { + case 1: +@@ -194,7 +390,13 @@ void dmi_init(void) + msg_pdbg("DMI chassis-type is not specific enough.\n"); + break; + } +- free(chassis_type); ++ ++ has_dmi_support = 1; ++ int i; ++ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) { ++ msg_pdbg("DMI string %s: \"%s\"\n", dmi_strings[i].keyword, ++ (dmi_strings[i].value == NULL) ? "" : dmi_strings[i].value); ++ } + } + + /** +@@ -204,8 +406,8 @@ void dmi_init(void) + * at the beginning and '$' at the end. So you can look for "^prefix", + * "suffix$", "substring" or "^complete string$". + * +- * @param value The string to check. +- * @param pattern The pattern. ++ * @param value The non-NULL string to check. ++ * @param pattern The non-NULL pattern. + * @return Nonzero if pattern matches. + */ + static int dmi_compare(const char *value, const char *pattern) +@@ -252,11 +454,15 @@ int dmi_match(const char *pattern) + if (!has_dmi_support) + return 0; + +- for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++) +- if (dmi_compare(dmistrings[i], pattern)) ++ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) { ++ if (dmi_strings[i].value == NULL) ++ continue; ++ ++ if (dmi_compare(dmi_strings[i].value, pattern)) + return 1; ++ } + + return 0; + } + +-#endif /* STANDALONE */ ++#endif // defined(__i386__) || defined(__x86_64__) +diff --git a/dnv_smi_spi.c b/dnv_smi_spi.c +new file mode 100644 +index 0000000..0317cc7 +--- /dev/null ++++ b/dnv_smi_spi.c +@@ -0,0 +1,185 @@ ++ ++/************************************************************************ ++* LEGALESE: "Copyright (c) 2018, Dell Inc. All rights reserved." ++* ++* This source code is confidential, proprietary, and contains trade ++* secrets that are the sole property of Dell Inc. ++* Copy and/or distribution of this source code or disassembly or reverse ++* engineering of the resultant object code are strictly forbidden without ++* the written consent of Dell Inc. ++* ++************************************************************************/ ++#if DELL_DENVERTON_SUPPORT == 1 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "flash.h" ++ ++#define MEM_MODULE "/dev/mem" ++ ++DELL_SPI_MAILBOX *mailbox; ++uint64_t phy_addr; ++ ++/** ++* @brief unmap a pagesize of physical memory into virtual memory. ++* @param[in] addr - virtual memory address that is unmaped. ++*/ ++static void free_virt_mapping(void *addr) ++{ ++ munmap(addr, getpagesize()); ++} ++ ++/** ++* @brief map a pagesize of physical memory into virtual memory. ++* @param[in] addr - physical memory address. ++* @param[out] buf - pointer to buffer which storages data. ++* @return = 0 for success, otherwise for error. ++*/ ++static uint32_t getData_phy_to_virt(uint32_t addr, void** buf) ++{ ++ uint8_t *tmp; ++ uint32_t pagesize, offset, index, fd; ++ ++ pagesize = getpagesize(); ++ if ( (fd = open(MEM_MODULE, O_RDWR)) < 0 ) { ++ printf("open() %s error\n", MEM_MODULE); ++ return 1; ++ } ++ ++ offset = addr & ~(pagesize - 1); ++ index = addr & (pagesize - 1); ++ if ( (tmp = mmap(0, pagesize*2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED ) { ++ printf("mmap() error\n"); ++ close(fd); ++ return 1; ++ } ++ *buf = tmp + index; ++ close(fd); ++ return 0; ++} ++ ++/** ++* @brief send "get communication buffer" requirement to SMM ++*/ ++uint32_t dnv_smi_open_res() ++{ ++ uint32_t mailboxAddress; ++ ++ ioperm(0xB2, 3, 1); ++ __asm__ __volatile__ ("push %%rcx" : :); ++ outb(0x27, 0xB2); ++ __asm__ __volatile__ ("movl %%ecx, %0" : "=r" (mailboxAddress)); ++ __asm__ __volatile__ ("pop %%rcx" : :); ++ ioperm(0xB2, 3, 0); ++ ++ getData_phy_to_virt(mailboxAddress, (void **)&mailbox); ++ phy_addr = mailboxAddress; ++ return 0; ++} ++ ++/** ++* @brief send "release communication buffer" requirement to SMM ++*/ ++void dnv_smi_release_res() ++{ ++ free_virt_mapping((void *)mailbox); ++} ++ ++/** ++* @brief disable SPI write protection. ++*/ ++static void dnv_trigger_smi_spi_unlock() ++{ ++ ioperm(0xB2, 3, 1); ++ __asm__ __volatile__ ("push %%rcx" : :); ++ __asm__ __volatile__ ("push %%rbx" : :); ++ __asm__ __volatile__ ("movl %0, %%ecx" : : "a" ((uint32_t)0)); ++ __asm__ __volatile__ ("movl %0, %%ebx" : : "a" ((uint32_t)phy_addr)); ++ outb(0x20, 0xB2); ++ __asm__ __volatile__ ("pop %%rbx" : :); ++ __asm__ __volatile__ ("pop %%rcx" : :); ++ ioperm(0xB2, 3, 0); ++} ++ ++/** ++* @brief enable SPI write protection. ++*/ ++static void dnv_trigger_smi_spi_lock() ++{ ++ ioperm(0xB2, 3, 1); ++ __asm__ __volatile__ ("push %%rcx" : :); ++ __asm__ __volatile__ ("push %%rbx" : :); ++ __asm__ __volatile__ ("movl %0, %%ecx" : : "a" ((uint32_t)0)); ++ __asm__ __volatile__ ("movl %0, %%ebx" : : "a" ((uint32_t)phy_addr)); ++ outb(0x24, 0xB2); ++ __asm__ __volatile__ ("pop %%rbx" : :); ++ __asm__ __volatile__ ("pop %%rcx" : :); ++ ioperm(0xB2, 3, 0); ++} ++ ++/** ++* @brief trigger smi ++*/ ++void run() ++{ ++ ioperm(0xB2, 3, 1); ++ outb(0x28, 0xB2); ++ ioperm(0xB2, 3, 0); ++} ++ ++/** ++* @brief This function is used to read/write and erase BIOS. ++* trigger SMI and send information to SMM(like requirement, address,and data) ++* @param[in] bios_offset - offset of bios region [0~size of bios region] ++* @param[in] buf - pointer to write data if cmd is "SMI_READ_SPI" ++* @param[in] cmd - enum SPI_SMI_CMD ++* @param[out] buf - pointer to read data if cmd is "SMI_WRITE_SPI" ++* @returns ret. This ret is returned from SMM. ++*/ ++int32_t dnv_smi_doit(uint32_t bios_offset, uint8_t *buf, SPI_SMI_CMD cmd) ++{ ++ ++ if (cmd == SMI_READ_SPI) { ++ mailbox->cmd = 0x1; ++ mailbox->offset = bios_offset; ++ mailbox->dlen = DELL_DENVERTON_BLOCK_SIZE; ++ run(); ++ memcpy (buf, mailbox->data, DELL_DENVERTON_BLOCK_SIZE); ++ return mailbox->ret; ++ } else if (cmd == SMI_ERASE_SPI) { ++ mailbox->cmd = 0x2; ++ mailbox->offset = bios_offset; ++ mailbox->dlen = DELL_DENVERTON_BLOCK_SIZE; ++ run(); ++ return mailbox->ret; ++ } else if (cmd == SMI_WRITE_SPI) { ++ memcpy (mailbox->data, buf, DELL_DENVERTON_BLOCK_SIZE); ++ mailbox->cmd = 0x3; ++ mailbox->offset = bios_offset; ++ mailbox->dlen = DELL_DENVERTON_BLOCK_SIZE; ++ run(); ++ return mailbox->ret; ++ } ++ ++ switch (cmd) ++ { ++ case SMI_ENABLE_SPI: ++ dnv_trigger_smi_spi_unlock(); ++ break; ++ case SMI_DISABLE_SPI: ++ dnv_trigger_smi_spi_lock(); ++ break; ++ default: ++ return -1; ++ } ++ ++ return 0; ++} ++ ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ +diff --git a/drkaiser.c b/drkaiser.c +index b94d6dd..0cf1fdb 100644 +--- a/drkaiser.c ++++ b/drkaiser.c +@@ -56,12 +56,6 @@ static const struct par_programmer par_programmer_drkaiser = { + .chip_writen = fallback_chip_writen, + }; + +-static int drkaiser_shutdown(void *data) +-{ +- physunmap(drkaiser_bar, DRKAISER_MEMMAP_SIZE); +- return 0; +-} +- + int drkaiser_init(void) + { + struct pci_dev *dev = NULL; +@@ -75,15 +69,15 @@ int drkaiser_init(void) + return 1; + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); ++ if (!addr) ++ return 1; + + /* Write magic register to enable flash write. */ + rpci_write_word(dev, PCI_MAGIC_DRKAISER_ADDR, PCI_MAGIC_DRKAISER_VALUE); + + /* Map 128kB flash memory window. */ +- drkaiser_bar = physmap("Dr. Kaiser PC-Waechter flash memory", +- addr, DRKAISER_MEMMAP_SIZE); +- +- if (register_shutdown(drkaiser_shutdown, NULL)) ++ drkaiser_bar = rphysmap("Dr. Kaiser PC-Waechter flash memory", addr, DRKAISER_MEMMAP_SIZE); ++ if (drkaiser_bar == ERROR_PTR) + return 1; + + max_rom_decode.parallel = 128 * 1024; +diff --git a/flash.h b/flash.h +index 63701ed..79fe8f4 100644 +--- a/flash.h ++++ b/flash.h +@@ -45,6 +45,14 @@ + typedef uintptr_t chipaddr; + #define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2)) + ++/* Types and macros regarding the maximum flash space size supported by generic code. */ ++typedef uint32_t chipoff_t; /* Able to store any addressable offset within a supported flash memory. */ ++typedef uint32_t chipsize_t; /* Able to store the number of bytes of any supported flash memory. */ ++#define FL_MAX_CHIPADDR_BITS (24) ++#define FL_MAX_CHIPADDR ((chipoff_t)(1ULL< 0) ++ programmer_table[programmer].delay(usecs); + } + + void map_flash_registers(struct flashctx *flash) +@@ -619,12 +629,29 @@ static unsigned int count_usable_erasers(const struct flashctx *flash) + return usable_erasefunctions; + } + ++#if DELL_DENVERTON_SUPPORT == 1 ++int8_t is_erase = 0; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + int compare_range(uint8_t *wantbuf, uint8_t *havebuf, unsigned int start, unsigned int len) + { + int ret = 0, failcount = 0; + unsigned int i; + for (i = 0; i < len; i++) { + if (wantbuf[i] != havebuf[i]) { ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv && is_erase) { ++ // _FVH ++ // At offset 0x28~0x2b of NVRAM, Main Bios and Boot Bios ++ // We can't read correct data after "erasing". ++ // But, We verified bios through SF100. The erase feature is work. ++ if (i == _FVH_OFFSET && ++ memcmp(&havebuf[i], _FVH_SIGNATURE, sizeof(_FVH_SIGNATURE) - 1) == 0) { ++ i += sizeof(_FVH_SIGNATURE) - 1; ++ continue; ++ } ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + /* Only print the first failure. */ + if (!failcount++) + msg_cerr("FAILED at 0x%08x! Expected=0x%02x, Found=0x%02x,", +@@ -651,7 +678,15 @@ int check_erased_range(struct flashctx *flash, unsigned int start, + exit(1); + } + memset(cmpbuf, 0xff, len); ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) ++ is_erase = 1; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + ret = verify_range(flash, cmpbuf, start, len); ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) ++ is_erase = 0; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + free(cmpbuf); + return ret; + } +@@ -695,6 +730,22 @@ int verify_range(struct flashctx *flash, uint8_t *cmpbuf, unsigned int start, un + return ret; + } + ++#if DELL_DENVERTON_SUPPORT == 1 ++ // NVRAM will be recovery even if use afulnx. ++ // skip NVRAM region. ++ if (is_dnv) { ++ // Do not hard-code the NVRAM region start address. It is located at different addresses for 16MB and 32MB BIOS image. ++ // For Denverton platform, NVRAM region start address is always located at dnv_bios_region_start. ++ // NVRAM backup region is located at (dnv_bios_region_start + DELL_DENVERTON_MAIN_NVRAM_LENGTH). ++ if ((start >= dnv_bios_region_start && ++ start < (dnv_bios_region_start + DELL_DENVERTON_MAIN_NVRAM_LENGTH)) || ++ (start >= (dnv_bios_region_start + DELL_DENVERTON_MAIN_NVRAM_LENGTH) && ++ start < ((dnv_bios_region_start + DELL_DENVERTON_MAIN_NVRAM_LENGTH) + DELL_DENVERTON_BACKUP_NVRAM_LENGTH))) { ++ return ret; ++ } ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + ret = compare_range(cmpbuf, readbuf, start, len); + out_free: + free(readbuf); +@@ -770,6 +821,11 @@ int need_erase(uint8_t *have, uint8_t *want, unsigned int len, enum write_granul + case write_gran_1056bytes: + result = need_erase_gran_bytes(have, want, len, 1056); + break; ++#if DELL_DENVERTON_SUPPORT == 1 ++ case write_gran_4096bytes: ++ result = need_erase_gran_bytes(have, want, len, 4096); ++ break; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); +@@ -831,6 +887,11 @@ static unsigned int get_next_write(uint8_t *have, uint8_t *want, unsigned int le + case write_gran_1056bytes: + stride = 1056; + break; ++#if DELL_DENVERTON_SUPPORT == 1 ++ case write_gran_4096bytes: ++ stride = 4096; ++ break; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); +@@ -1140,6 +1201,12 @@ notfound: + if (!flash->chip) + return -1; + ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ flash->chip->gran = write_gran_4096bytes; ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + #if CONFIG_INTERNAL == 1 + if (programmer_table[programmer].map_flash_region == physmap) + snprintf(location, sizeof(location), "at physical address 0x%lx", base); +@@ -1148,8 +1215,13 @@ notfound: + snprintf(location, sizeof(location), "on %s", programmer_table[programmer].name); + + tmp = flashbuses_to_text(flash->chip->bustype); ++#ifdef FORCE10_SPI_CHANGE ++ msg_cdbg("%s %s flash chip \"%s\" (%d kB, %s) %s.\n", force ? "Assuming" : "Found", ++ flash->chip->vendor, flash->chip->name, flash->chip->total_size, tmp, location); ++#else + msg_cinfo("%s %s flash chip \"%s\" (%d kB, %s) %s.\n", force ? "Assuming" : "Found", + flash->chip->vendor, flash->chip->name, flash->chip->total_size, tmp, location); ++#endif + free(tmp); + + /* Flash registers will not be mapped if the chip was forced. Lock info +@@ -1166,6 +1238,10 @@ notfound: + int read_buf_from_file(unsigned char *buf, unsigned long size, + const char *filename) + { ++#ifdef __LIBPAYLOAD__ ++ msg_gerr("Error: No file I/O support in libpayload\n"); ++ return 1; ++#else + unsigned long numbytes; + FILE *image; + struct stat image_stat; +@@ -1196,11 +1272,16 @@ int read_buf_from_file(unsigned char *buf, unsigned long size, + return 1; + } + return 0; ++#endif + } + + int write_buf_to_file(unsigned char *buf, unsigned long size, + const char *filename) + { ++#ifdef __LIBPAYLOAD__ ++ msg_gerr("Error: No file I/O support in libpayload\n"); ++ return 1; ++#else + unsigned long numbytes; + FILE *image; + +@@ -1221,6 +1302,7 @@ int write_buf_to_file(unsigned char *buf, unsigned long size, + return 1; + } + return 0; ++#endif + } + + int read_flash_to_file(struct flashctx *flash, const char *filename) +@@ -1229,6 +1311,16 @@ int read_flash_to_file(struct flashctx *flash, const char *filename) + unsigned char *buf = calloc(size, sizeof(char)); + int ret = 0; + ++#if DELL_DENVERTON_SUPPORT == 1 ++ uint64_t bios_start = 0; ++ uint64_t bios_size = size; ++ ++ if (is_dnv) { ++ bios_size = dnv_bios_region_size; ++ bios_start = dnv_bios_region_start; ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + msg_cinfo("Reading flash... "); + if (!buf) { + msg_gerr("Memory allocation failed!\n"); +@@ -1240,7 +1332,11 @@ int read_flash_to_file(struct flashctx *flash, const char *filename) + ret = 1; + goto out_free; + } ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (flash->chip->read(flash, (buf + bios_start), bios_start, bios_size)) { ++#else + if (flash->chip->read(flash, buf, 0, size)) { ++#endif /* #if DELL_DENVERTON__SUPPORT == 1 */ + msg_cerr("Read operation failed!\n"); + ret = 1; + goto out_free; +@@ -1388,6 +1484,11 @@ static int walk_eraseregions(struct flashctx *flash, int erasefunction, + { + int i, j; + unsigned int start = 0; ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ start = dnv_bios_region_start; ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + unsigned int len; + struct block_eraser eraser = flash->chip->block_erasers[erasefunction]; + +@@ -1897,6 +1998,10 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, + uint8_t *newcontents; + int ret = 0; + unsigned long size = flash->chip->total_size * 1024; ++#if DELL_DENVERTON_SUPPORT == 1 ++ uint32_t bios_size = size; ++ uint32_t bios_start = 0; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + + if (chip_safety_check(flash, force, read_it, write_it, erase_it, verify_it)) { + msg_cerr("Aborting.\n"); +@@ -1904,6 +2009,12 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, + goto out_nofree; + } + ++ if (normalize_romentries(flash)) { ++ msg_cerr("Requested regions can not be handled. Aborting.\n"); ++ ret = 1; ++ goto out_nofree; ++ } ++ + /* Given the existence of read locks, we want to unlock for read, + * erase and write. + */ +@@ -1936,6 +2047,13 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, + */ + + if (erase_it) { ++ ++#if 0 // Since we dont have a layout, why set newcontents to 0?? ++#ifdef FORCE10_SPI_CHANGE ++ /* Build a new image taking the given layout into account. */ ++ build_new_image(flash, oldcontents, newcontents); ++#endif ++#endif + /* 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, +@@ -1975,25 +2093,47 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, + * preserved, but in that case we might perform unneeded erase which + * takes time as well. + */ ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ bios_size = dnv_bios_region_size; ++ bios_start = dnv_bios_region_start; ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++#ifdef FORCE10_SPI_CHANGE ++ msg_cdbg("Reading old flash chip contents... "); ++#else + msg_cinfo("Reading old flash chip contents... "); ++#endif ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (flash->chip->read(flash, oldcontents + bios_start, bios_start, bios_size)) { ++#else + if (flash->chip->read(flash, oldcontents, 0, size)) { ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + ret = 1; + msg_cinfo("FAILED.\n"); + goto out; + } ++#ifdef FORCE10_SPI_CHANGE ++ msg_cdbg("done.\n"); ++#else + msg_cinfo("done.\n"); ++#endif + +- // This should be moved into each flash part's code to do it +- // cleanly. This does the job. +- handle_romentries(flash, oldcontents, newcontents); +- ++#if 0 ++ /* Build a new image taking the given layout into account. */ ++ build_new_image(flash, oldcontents, newcontents); ++#endif + // //////////////////////////////////////////////////////////// + + if (write_it) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (!flash->chip->read(flash, newcontents, bios_start, bios_size)) { ++#else + if (!flash->chip->read(flash, newcontents, 0, size)) { ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + if (!memcmp(oldcontents, newcontents, size)) { + msg_cinfo("Good. It seems nothing was changed.\n"); + nonfatal_help_message(); +@@ -2014,14 +2154,22 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, + if (write_it) { + /* Work around chips which need some time to calm down. */ + programmer_delay(1000*1000); ++#if DELL_DENVERTON_SUPPORT == 1 ++ ret = verify_range(flash, newcontents + bios_start, bios_start, bios_size); ++#else + ret = verify_range(flash, newcontents, 0, size); ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + /* If we tried to write, and verification now fails, we + * might have an emergency situation. + */ + if (ret) + emergency_help_message(); + } else { ++#if DELL_DENVERTON_SUPPORT == 1 ++ ret = compare_range(newcontents + bios_start, oldcontents + bios_start, bios_start, bios_size); ++#else + ret = compare_range(newcontents, oldcontents, 0, size); ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + } + if (!ret) + msg_cinfo("VERIFIED.\n"); +diff --git a/ft2232_spi.c b/ft2232_spi.c +index 81be051..44354fd 100644 +--- a/ft2232_spi.c ++++ b/ft2232_spi.c +@@ -43,6 +43,7 @@ + #define FTDI_FT4232H_PID 0x6011 + #define FTDI_FT232H_PID 0x6014 + #define TIAO_TUMPA_PID 0x8a98 ++#define TIAO_TUMPA_LITE_PID 0x8a99 + #define AMONTEC_JTAGKEY_PID 0xCFF8 + + #define GOEPEL_VID 0x096C +@@ -62,6 +63,7 @@ const struct dev_entry devs_ft2232spi[] = { + {FTDI_VID, FTDI_FT4232H_PID, OK, "FTDI", "FT4232H"}, + {FTDI_VID, FTDI_FT232H_PID, OK, "FTDI", "FT232H"}, + {FTDI_VID, TIAO_TUMPA_PID, OK, "TIAO", "USB Multi-Protocol Adapter"}, ++ {FTDI_VID, TIAO_TUMPA_LITE_PID, OK, "TIAO", "USB Multi-Protocol Adapter Lite"}, + {FTDI_VID, AMONTEC_JTAGKEY_PID, OK, "Amontec", "JTAGkey"}, + {GOEPEL_VID, GOEPEL_PICOTAP_PID, OK, "GOEPEL", "PicoTAP"}, + {FIC_VID, OPENMOKO_DBGBOARD_PID, OK, "FIC", "OpenMoko Neo1973 Debug board (V2+)"}, +@@ -208,6 +210,10 @@ int ft2232_spi_init(void) + /* Interface A is SPI1, B is SPI2. */ + ft2232_type = TIAO_TUMPA_PID; + channel_count = 2; ++ } else if (!strcasecmp(arg, "tumpalite")) { ++ /* Only one channel is used on lite edition */ ++ ft2232_type = TIAO_TUMPA_LITE_PID; ++ channel_count = 1; + } else if (!strcasecmp(arg, "busblaster")) { + /* In its default configuration it is a jtagkey clone */ + ft2232_type = FTDI_FT2232H_PID; +diff --git a/gfxnvidia.c b/gfxnvidia.c +index d0a9feb..d3ee14e 100644 +--- a/gfxnvidia.c ++++ b/gfxnvidia.c +@@ -77,12 +77,6 @@ static const struct par_programmer par_programmer_gfxnvidia = { + .chip_writen = fallback_chip_writen, + }; + +-static int gfxnvidia_shutdown(void *data) +-{ +- physunmap(nvidia_bar, GFXNVIDIA_MEMMAP_SIZE); +- return 0; +-} +- + int gfxnvidia_init(void) + { + struct pci_dev *dev = NULL; +@@ -96,12 +90,14 @@ int gfxnvidia_init(void) + return 1; + + io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); ++ if (!io_base_addr) ++ return 1; ++ + io_base_addr += 0x300000; + msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr); + +- nvidia_bar = physmap("NVIDIA", io_base_addr, GFXNVIDIA_MEMMAP_SIZE); +- +- if (register_shutdown(gfxnvidia_shutdown, NULL)) ++ nvidia_bar = rphysmap("NVIDIA", io_base_addr, GFXNVIDIA_MEMMAP_SIZE); ++ if (nvidia_bar == ERROR_PTR) + return 1; + + /* Allow access to flash interface (will disable screen). */ +diff --git a/hwaccess.h b/hwaccess.h +index fd6eb12..83f349e 100644 +--- a/hwaccess.h ++++ b/hwaccess.h +@@ -37,7 +37,13 @@ + * or as builtin. + */ + #define index shadow_workaround_index ++ ++#if !defined (__NetBSD__) && !defined (__DragonFly__) + #include ++#else ++#include ++#endif ++ + #undef index + #endif + +diff --git a/ich_descriptors.c b/ich_descriptors.c +index 528717b..c007867 100644 +--- a/ich_descriptors.c ++++ b/ich_descriptors.c +@@ -45,6 +45,11 @@ + #define min(a, b) (a < b) ? a : b + #endif + ++#if DELL_DENVERTON_SUPPORT == 1 ++#define DELL_DENVERTON_MAX_DENSITY 7 ++extern int8_t is_dnv; // if 1 chipset is denverton serial, 0 otherwise ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity) + { + print(verbosity, "BES=0x%x, ", (reg_val & VSCC_BES) >> VSCC_BES_OFF); +@@ -100,6 +105,36 @@ void prettyprint_ich_descriptor_content(const struct ich_desc_content *cont) + + void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc) + { ++#if DELL_DENVERTON_SUPPORT == 1 ++ static const char * const dnv_freq_str[8] = { ++ "reserved", /* 000 */ ++ "reserved", /* 001 */ ++ "48 MHz", /* 010 */ ++ "reserved", /* 011 */ ++ "30MHz", /* 100 */ ++ "reserved", /* 101 */ ++ "17 MHz", /* 110 */ ++ "reserved", /* 111 */ ++ }; ++ static const char * const dnv_size_str[16] = { ++ "512 kB", /* 0000 */ ++ " 1 MB", /* 0001 */ ++ " 2 MB", /* 0010 */ ++ " 4 MB", /* 0011 */ ++ " 8 MB", /* 0100 */ ++ " 16 MB", /* 0101 */ ++ " 32 MB", /* 0110 */ ++ " 64 MB", /* 0111 */ ++ "reserved", /* 1000 */ ++ "reserved", /* 1001 */ ++ "reserved", /* 1010 */ ++ "reserved", /* 1011 */ ++ "reserved", /* 1100 */ ++ "reserved", /* 1101 */ ++ "reserved", /* 1110 */ ++ "reserved", /* 1111 */ ++ }; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + static const char * const freq_str[8] = { + "20 MHz", /* 000 */ + "33 MHz", /* 001 */ +@@ -121,6 +156,47 @@ void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc) + "reserved", /* 111 */ + }; + ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ msg_pdbg2("=== Component Section ===\n"); ++ msg_pdbg2("FLCOMP 0x%08x\n", desc->component.FLCOMP); ++ msg_pdbg2("FLILL 0x%08x\n", desc->component.FLILL ); ++ msg_pdbg2("\n"); ++ ++ msg_pdbg2("--- Details ---\n"); ++ msg_pdbg2("Component 1 density: %s\n", ++ dnv_size_str[desc->component.dnv_comp1_density]); ++ if (desc->content.NC) ++ msg_pdbg2("Component 2 density: %s\n", ++ dnv_size_str[desc->component.dnv_comp2_density]); ++ else ++ msg_pdbg2("Component 2 is not used.\n"); ++ msg_pdbg2("Read Clock Frequency: %s\n", ++ dnv_freq_str[desc->component.dnv_freq_read]); ++ msg_pdbg2("Read ID and Status Clock Freq.: %s\n", ++ dnv_freq_str[desc->component.dnv_freq_read_id]); ++ msg_pdbg2("Write and Erase Clock Freq.: %s\n", ++ dnv_freq_str[desc->component.dnv_freq_write]); ++ msg_pdbg2("Fast Read is %ssupported.\n", ++ desc->component.dnv_fastread ? "" : "not "); ++ if (desc->component.dnv_fastread) ++ msg_pdbg2("Fast Read Clock Frequency: %s\n", ++ dnv_freq_str[desc->component.dnv_freq_fastread]); ++ if (desc->component.FLILL == 0) ++ msg_pdbg2("No forbidden opcodes.\n"); ++ else { ++ msg_pdbg2("Invalid instruction 0: 0x%02x\n", ++ desc->component.invalid_instr0); ++ msg_pdbg2("Invalid instruction 1: 0x%02x\n", ++ desc->component.invalid_instr1); ++ msg_pdbg2("Invalid instruction 2: 0x%02x\n", ++ desc->component.invalid_instr2); ++ msg_pdbg2("Invalid instruction 3: 0x%02x\n", ++ desc->component.invalid_instr3); ++ } ++ } else { ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + msg_pdbg2("=== Component Section ===\n"); + msg_pdbg2("FLCOMP 0x%08x\n", desc->component.FLCOMP); + msg_pdbg2("FLILL 0x%08x\n", desc->component.FLILL ); +@@ -157,6 +233,9 @@ void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc) + msg_pdbg2("Invalid instruction 3: 0x%02x\n", + desc->component.invalid_instr3); + } ++#if DELL_DENVERTON_SUPPORT == 1 ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + msg_pdbg2("\n"); + } + +@@ -171,6 +250,12 @@ static void pprint_freg(const struct ich_desc_region *reg, uint32_t i) + } + uint32_t base = ICH_FREG_BASE(reg->FLREGs[i]); + uint32_t limit = ICH_FREG_LIMIT(reg->FLREGs[i]); ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ base = DELL_DENVERTON_FREG_BASE(reg->FLREGs[i]); ++ limit = DELL_DENVERTON_FREG_LIMIT(reg->FLREGs[i]); ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + msg_pdbg2("Region %d (%-6s) ", i, region_names[i]); + if (base > limit) + msg_pdbg2("is unused.\n"); +@@ -741,17 +826,36 @@ int getFCBA_component_density(const struct ich_descriptors *desc, uint8_t idx) + msg_perr("Only ICH SPI component index 0 or 1 are supported yet.\n"); + return 0; + } ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ if (size_enc > DELL_DENVERTON_MAX_DENSITY) { ++ msg_perr("Density of ICH SPI component with index %d is invalid. Encoded density is 0x%x.\n", idx, size_enc); ++ return 0; ++ } ++ } else { ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + if (size_enc > 5) { + msg_perr("Density of ICH SPI component with index %d is invalid. Encoded density is 0x%x.\n", + idx, size_enc); + return 0; + } ++#if DELL_DENVERTON_SUPPORT == 1 ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + return (1 << (19 + size_enc)); + } + + static uint32_t read_descriptor_reg(uint8_t section, uint16_t offset, void *spibar) + { + uint32_t control = 0; ++#if DELL_DENVERTON_SUPPORT == 1 ++ if (is_dnv) { ++ control |= (section << DENVERTON_FDOC_FDSS_OFF) & DENVERTON_FDOC_FDSS; ++ control |= (offset << DENVERTON_FDOC_FDSI_OFF) & DENVERTON_FDOC_FDSI; ++ mmio_le_writel(control, spibar + DENVERTON_REG_FDOC); ++ return mmio_le_readl(spibar + DENVERTON_REG_FDOD); ++ } ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + control |= (section << FDOC_FDSS_OFF) & FDOC_FDSS; + control |= (offset << FDOC_FDSI_OFF) & FDOC_FDSI; + mmio_le_writel(control, spibar + ICH9_REG_FDOC); +diff --git a/ich_descriptors.h b/ich_descriptors.h +index 3a44740..914a3cd 100644 +--- a/ich_descriptors.h ++++ b/ich_descriptors.h +@@ -64,6 +64,19 @@ + #define ICH_FREG_BASE(flreg) (((flreg) << 12) & 0x01fff000) + #define ICH_FREG_LIMIT(flreg) (((flreg) >> 4) & 0x01fff000) + ++#if DELL_DENVERTON_SUPPORT == 1 ++#define DENVERTON_REG_FDOC 0xB4 ++#define DENVERTON_FDOC_FDSI_OFF 2 ++#define DENVERTON_FDOC_FDSI (0x3ff << DENVERTON_FDOC_FDSI_OFF) ++#define DENVERTON_FDOC_FDSS_OFF 12 ++#define DENVERTON_FDOC_FDSS (0x3 << DENVERTON_FDOC_FDSS_OFF) ++ ++#define DENVERTON_REG_FDOD 0xB8 ++ ++#define DELL_DENVERTON_FREG_BASE(flreg) (((flreg) << 12) & 0x03fff000) ++#define DELL_DENVERTON_FREG_LIMIT(flreg) (((flreg) >> 4) & 0x03fff000) ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity); + + struct ich_desc_content { +@@ -78,6 +91,15 @@ struct ich_desc_content { + NR :3, /* Number Of Regions */ + :5; + }; ++#if DELL_DENVERTON_SUPPORT == 1 ++ struct { ++ int32_t dnv_FCBA :8, /* Flash Component Base Address */ ++ dnv_NC :2, /* Number Of Components */ ++ :6, ++ dnv_FRBA :8, /* Flash Region Base Address */ ++ :8; ++ }; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + }; + union { /* 0x08 */ + uint32_t FLMAP1; +@@ -113,6 +135,20 @@ struct ich_desc_component { + freq_read_id :3, + :2; + }; ++#if DELL_DENVERTON_SUPPORT == 1 ++ struct { ++ uint32_t dnv_comp1_density :4, ++ dnv_comp2_density :4, ++ :9, ++ dnv_freq_read :3, ++ dnv_fastread :1, ++ dnv_freq_fastread :3, ++ dnv_freq_write :3, ++ dnv_freq_read_id :3, ++ dnv_dualfastread :1, ++ :1; ++ }; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + }; + union { /* 0x04 */ + uint32_t FLILL; /* Flash Invalid Instructions Register */ +@@ -129,6 +165,15 @@ struct ich_desc_component { + uint32_t FPBA :13, /* Flash Partition Boundary Addr */ + :19; + }; ++#if DELL_DENVERTON_SUPPORT == 1 ++ uint32_t FLILL1; /* Flash Partition Boundary Register for dnv */ ++ struct { ++ uint32_t invalid_instr4 :8, ++ invalid_instr5 :8, ++ invalid_instr6 :8, ++ invalid_instr7 :8; ++ }; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + }; + }; + +@@ -167,6 +212,65 @@ struct ich_desc_region { + :3; + }; + }; ++#if DELL_DENVERTON_SUPPORT == 1 ++ struct { /* FLREG0 Flash Descriptor */ ++ uint32_t dnv_reg0_base :15, ++ :1, ++ dnv_reg0_limit :15, ++ :1; ++ }; ++ struct { /* FLREG1 BIOS */ ++ uint32_t dnv_reg1_base :15, ++ :1, ++ dnv_reg1_limit :15, ++ :1; ++ }; ++ struct { /* FLREG2 ME */ ++ uint32_t dnv_reg2_base :15, ++ :1, ++ dnv_reg2_limit :15, ++ :1; ++ }; ++ ++ /* FLREG3 - Reserved */ ++ ++ struct { /* FLREG4 Platform */ ++ uint32_t dnv_reg4_base :15, ++ :1, ++ dnv_reg4_limit :15, ++ :1; ++ }; ++ ++ /* FLREG5 - Reserved */ ++ /* FLREG6 - Reserved */ ++ /* FLREG7 - Reserved */ ++ /* FLREG8 - Reserved */ ++ /* FLREG9 - Reserved */ ++ ++ struct { /* FLREG10 IE */ ++ uint32_t dnv_reg10_base :15, ++ :1, ++ dnv_reg10_limit :15, ++ :1; ++ }; ++ struct { /* FLREG11 LAN CTRL0 */ ++ uint32_t dnv_reg11_base :15, ++ :1, ++ dnv_reg11_limit :15, ++ :1; ++ }; ++ struct { /* FLREG12 LAN CTRL1 */ ++ uint32_t dnv_reg12_base :15, ++ :1, ++ dnv_reg12_limit :15, ++ :1; ++ }; ++ ++ /* FLREG13 - Reserved */ ++ /* FLREG14 - Reserved */ ++ /* FLREG15 - Reserved */ ++ ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + }; + }; + +diff --git a/ichspi.c b/ichspi.c +index 6d1bd1a..742d424 100644 +--- a/ichspi.c ++++ b/ichspi.c +@@ -170,6 +170,52 @@ + #define ICH7_REG_OPTYPE 0x56 /* 16 Bits */ + #define ICH7_REG_OPMENU 0x58 /* 64 Bits */ + ++/* denverton controller register definition */ ++#if DELL_DENVERTON_SUPPORT == 1 ++#define DENVERTON_REG_BFPREG 0x00 /* 32 Bits SPI BIOS MMIO PRI */ ++#define DENVERTON_BFPREG_PRB_OFF 0 /* 0-14: BIOS Flash Promary Region Base */ ++#define DENVERTON_BFPREG_PRB (0x7FFF << DENVERTON_BFPREG_PRB_OFF) ++#define DENVERTON_BFPREG_PRL_OFF 16 /* 16-30: BIOS Flash Primary Region Limit */ ++#define DENVERTON_BFPREG_PRL (0xFFFF << DENVERTON_BFPREG_PRL_OFF) ++#define DENVERTON_REG_HSFS 0x04 /* 16 Bits Hardware Sequencing Flash Status */ ++#define DENVERTON_HSFS_FDONE_OFF 0 /* 0: Flash Cycle Done */ ++#define DENVERTON_HSFS_FDONE (0x1 << DENVERTON_HSFS_FDONE_OFF) ++#define DENVERTON_HSFS_FCERR_OFF 1 /* 1: Flash Cycle Error */ ++#define DENVERTON_HSFS_FCERR (0x1 << DENVERTON_HSFS_FCERR_OFF) ++#define DENVERTON_HSFS_FDOPSS_OFF 13 /* 13: Flash Descriptor Override Pin-Strap Status */ ++#define DENVERTON_HSFS_FDOPSS (0x1 << DENVERTON_HSFS_FDOPSS_OFF) ++#define DENVERTON_HSFS_FDV_OFF 14 /* 14: Flash Descriptor Valid */ ++#define DENVERTON_HSFS_FDV (0x1 << DENVERTON_HSFS_FDV_OFF) ++#define DENVERTON_HSFS_FLOCKDN_OFF 15 /* 15: Flash Configuration Lock-Down */ ++#define DENVERTON_HSFS_FLOCKDN (0x1 << DENVERTON_HSFS_FLOCKDN_OFF) ++#define DENVERTON_REG_HSFC 0x06 /* 16 Bits Hardware Sequencing Flash Control */ ++#define DENVERTON_HSFC_FGO_OFF 0 /* 0: Flash Cycle Go */ ++#define DENVERTON_HSFC_FGO (0x1 << DENVERTON_HSFC_FGO_OFF) ++#define DENVERTON_HSFC_FCYCLE_OFF 1 /* 1- 4: FLASH Cycle */ ++#define DENVERTON_HSFC_FCYCLE (0xF << DENVERTON_HSFC_FCYCLE_OFF) ++#define DENVERTON_HSFC_FDBC_OFF 8 /* 8-13: Flash Data Byte Count */ ++#define DENVERTON_HSFC_FDBC (0x3F << DENVERTON_HSFC_FDBC_OFF) ++#define DENVERTON_REG_FADDR 0x08 /* 32 Bits */ ++#define DENVERTON_REG_FDATA0 0x10 /* 32 Bits */ ++#define DENVERTON_REG_FRAP 0x50 /* 32 Bytes Flash Region Access Permissions */ ++#define DENVERTON_REG_FREG0 0x54 /* 32 Bytes Flash Region 0 */ ++#define DENVERTON_REG_PR0 0x84 /* 32 Bytes Protected Range 0 */ ++#define DENVERTON_PR_WP_OFF 31 /* 31: write protection enable */ ++#define DENVERTON_PR_RP_OFF 15 /* 15: read protection enable */ ++#define DENVERTON_REG_VSCC0 0xC4 /* 32 Bits Host Vendor Specific Component Capabilities for Component 0 */ ++#define DENVERTON_REG_VSCC1 0xC8 /* 32 Bits Host Vendor Specific Component Capabilities for Component 1 */ ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ ++#ifdef DELL_AVOTON_SUPPORT ++extern int8_t is_avoton; ++#endif /* DELL_AVOTON_SUPPORT */ ++ ++#if DELL_DENVERTON_SUPPORT == 1 ++extern uint32_t dnv_bios_region_start; ++extern uint32_t dnv_bios_region_size; ++extern uint32_t dnv_bios_region_limit; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + /* ICH SPI configuration lock-down. May be set during chipset enabling. */ + static int ichspi_lock = 0; + +@@ -466,6 +512,8 @@ static int generate_opcodes(OPCODES * op) + + switch (ich_generation) { + case CHIPSET_ICH7: ++ case CHIPSET_TUNNEL_CREEK: ++ case CHIPSET_CENTERTON: + preop = REGREAD16(ICH7_REG_PREOP); + optype = REGREAD16(ICH7_REG_OPTYPE); + opmenu[0] = REGREAD32(ICH7_REG_OPMENU); +@@ -529,7 +577,7 @@ static int program_opcodes(OPCODES *op, int enable_undo) + opmenu[0] |= ((uint32_t) op->opcode[a].opcode) << (a * 8); + } + +- /*Program Allowable Opcodes 4 - 7 */ ++ /* Program Allowable Opcodes 4 - 7 */ + opmenu[1] = 0; + for (a = 4; a < 8; a++) { + opmenu[1] |= ((uint32_t) op->opcode[a].opcode) << ((a - 4) * 8); +@@ -538,6 +586,8 @@ static int program_opcodes(OPCODES *op, int enable_undo) + msg_pdbg2("\n%s: preop=%04x optype=%04x opmenu=%08x%08x\n", __func__, preop, optype, opmenu[0], opmenu[1]); + switch (ich_generation) { + case CHIPSET_ICH7: ++ case CHIPSET_TUNNEL_CREEK: ++ case CHIPSET_CENTERTON: + /* Register undo only for enable_undo=1, i.e. first call. */ + if (enable_undo) { + rmmio_valw(ich_spibar + ICH7_REG_PREOP); +@@ -603,10 +653,19 @@ static void ich_set_bbar(uint32_t min_addr) + int bbar_off; + switch (ich_generation) { + case CHIPSET_ICH7: ++ case CHIPSET_TUNNEL_CREEK: ++ case CHIPSET_CENTERTON: + bbar_off = 0x50; + break; + case CHIPSET_ICH8: ++#ifdef DELL_AVOTON_SUPPORT ++ case CHIPSET_AVOTON: ++#endif ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("BBAR offset is unknown on ICH8!\n"); ++#else + msg_perr("BBAR offset is unknown on ICH8!\n"); ++#endif + return; + case CHIPSET_ICH9: + default: /* Future version might behave the same */ +@@ -817,11 +876,17 @@ static int ich7_run_opcode(OPCODE op, uint32_t offset, + /* FIXME: make sure we do not needlessly cause transaction errors. */ + temp16 = REGREAD16(ICH7_REG_SPIS); + if (temp16 & SPIS_FCERR) { ++#ifndef FORCE10_SPI_CHANGE + msg_perr("Transaction error!\n"); ++#endif + /* keep reserved bits */ + temp16 &= SPIS_RESERVED_MASK; + REGWRITE16(ICH7_REG_SPIS, temp16 | SPIS_FCERR); ++#ifndef FORCE10_SPI_CHANGE + return 1; ++#else ++ return 0; ++#endif + } + + if ((!write_cmd) && (datalength != 0)) +@@ -838,6 +903,22 @@ static int ich9_run_opcode(OPCODE op, uint32_t offset, + uint32_t temp32; + uint64_t opmenu; + int opcode_index; ++/* skip unused region for Rangeley */ ++ if (offset >= 0x10000 && offset <= 0x9fffff) { ++ uint32_t i = 0; ++ for (i = 0; i < datalength; i++) ++ *(data + i) = 0xff; ++ return 0; ++ } ++#ifdef DELL_AVOTON_SUPPORT ++ /* skip unused region for Rangeley */ ++ if (is_avoton && offset >= 0x10000 && offset <= 0x9fffff) { ++ uint32_t i = 0; ++ for (i = 0; i < datalength; i++) ++ *(data + i) = 0xff; ++ return 0; ++ } ++#endif /* AVOTON */ + + /* Is it a write command? */ + if ((op.spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS) +@@ -939,14 +1020,20 @@ static int ich9_run_opcode(OPCODE op, uint32_t offset, + /* FIXME make sure we do not needlessly cause transaction errors. */ + temp32 = REGREAD32(ICH9_REG_SSFS); + if (temp32 & SSFS_FCERR) { ++#ifndef FORCE10_SPI_CHANGE + msg_perr("Transaction error!\n"); ++#endif + prettyprint_ich9_reg_ssfs(temp32); + prettyprint_ich9_reg_ssfc(temp32); + /* keep reserved bits */ + temp32 &= SSFS_RESERVED_MASK | SSFC_RESERVED_MASK; + /* Clear the transaction error. */ + REGWRITE32(ICH9_REG_SSFS, temp32 | SSFS_FCERR); ++#ifndef FORCE10_SPI_CHANGE + return 1; ++#else ++ return 0; ++#endif + } + + if ((!write_cmd) && (datalength != 0)) +@@ -975,6 +1062,8 @@ static int run_opcode(const struct flashctx *flash, OPCODE op, uint32_t offset, + + switch (ich_generation) { + case CHIPSET_ICH7: ++ case CHIPSET_TUNNEL_CREEK: ++ case CHIPSET_CENTERTON: + return ich7_run_opcode(op, offset, datalength, data, maxlength); + case CHIPSET_ICH8: + default: /* Future version might behave the same */ +@@ -1418,6 +1507,191 @@ static int ich_spi_send_multicommand(struct flashctx *flash, + return ret; + } + ++#if DELL_DENVERTON_SUPPORT == 1 ++/* Sets FLA in FADDR to (addr & 0x01FFFFFF) without touching other bits. */ ++static void dnv_hwseq_set_addr(uint32_t addr) ++{ ++ uint32_t addr_old = REGREAD32(DENVERTON_REG_FADDR) & ~0x01FFFFFF; ++ REGWRITE32(DENVERTON_REG_FADDR, (addr & 0x01FFFFFF) | addr_old); ++} ++ ++/* Polls for Cycle Done Status, Flash Cycle Error or timeout in 8 us intervals. ++ Resets all error flags in HSFS. ++ Returns 0 if the cycle completes successfully without errors within ++ timeout us, 1 on errors. */ ++static int dnv_hwseq_wait_for_cycle_complete(uint32_t timeout, ++ uint32_t len) ++{ ++ uint16_t hsfs; ++ uint32_t addr; ++ ++ timeout /= 8; /* scale timeout duration to counter */ ++ while ((((hsfs = REGREAD16(DENVERTON_REG_HSFS)) & ++ (DENVERTON_HSFS_FDONE | DENVERTON_HSFS_FCERR)) == 0) && ++ --timeout) { ++ programmer_delay(8); ++ } ++ REGWRITE16(DENVERTON_REG_HSFS, REGREAD16(DENVERTON_REG_HSFS)); ++ if (!timeout) { ++ addr = REGREAD32(DENVERTON_REG_FADDR) & 0x01FFFFFF; ++ msg_perr("Timeout error between offset 0x%08x and " ++ "0x%08x (= 0x%08x + %d)!\n", ++ addr, addr + len - 1, addr, len - 1); ++ return 1; ++ } ++ ++ if (hsfs & DENVERTON_HSFS_FCERR) { ++ addr = REGREAD32(DENVERTON_REG_FADDR) & 0x01FFFFFF; ++ msg_perr("Transaction error between offset 0x%08x and " ++ "0x%08x (= 0x%08x + %d)!\n", ++ addr, addr + len - 1, addr, len - 1); ++ return 1; ++ } ++ return 0; ++} ++ ++int dnv_read_JEDEC_ID(uint8_t *buf) ++{ ++ uint16_t hsfc; ++ uint16_t timeout = 100 * 60; ++ uint8_t len = 3; ++ ++ /* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */ ++ REGWRITE16(DENVERTON_REG_HSFS, REGREAD16(DENVERTON_REG_HSFS)); ++ ++ dnv_hwseq_set_addr(0); ++ hsfc = REGREAD16(DENVERTON_REG_HSFC); ++ hsfc &= ~DENVERTON_HSFC_FCYCLE; /* clear operation */ ++ hsfc |= (0x6 << DENVERTON_HSFC_FCYCLE_OFF); /* set read JEDEC ID operation */ ++ hsfc &= ~DENVERTON_HSFC_FDBC; /* clear byte count */ ++ /* set byte count */ ++ hsfc |= (((len - 1) << DENVERTON_HSFC_FDBC_OFF) & DENVERTON_HSFC_FDBC); ++ hsfc |= DENVERTON_HSFC_FGO; /* start */ ++ REGWRITE16(DENVERTON_REG_HSFC, hsfc); ++ ++ if (dnv_hwseq_wait_for_cycle_complete(timeout, len)) ++ return 1; ++ ich_read_data(buf, len, DENVERTON_REG_FDATA0); ++ return 0; ++} ++ ++static int dnv_hwseq_probe(struct flashctx *flash) ++{ ++ uint8_t jedec_id[3]; ++ uint32_t tmp, manufacture_id, model_id; ++ struct block_eraser *eraser; ++ const struct flashchip *chip; ++ ++ msg_gdbg("Using HW Sequency to read JEDEC ID and find SPI flash.\n"); ++ if (dnv_read_JEDEC_ID(jedec_id)) { ++ msg_cerr("Can't get JEDEC ID through HW Sequency\n"); ++ return -1; ++ } ++ ++ manufacture_id = (uint32_t)jedec_id[0]; ++ model_id = (uint32_t)(jedec_id[1] << 8 | jedec_id[2]); ++ ++ for (chip = flashchips; chip && chip->name; chip++) { ++ if (chip->manufacture_id == manufacture_id && chip->model_id == model_id) { ++ break; ++ } ++ } ++ ++ flash->bios_size = (hwseq_data.size_comp0 + hwseq_data.size_comp1) / 1024; ++ ++ /* We only replace base information. The functions still use hw sequency, like read and write */ ++ flash->chip->vendor = chip->vendor; ++ flash->chip->name = chip->name; ++ flash->chip->manufacture_id = chip->manufacture_id; ++ flash->chip->model_id = chip->model_id; ++ flash->chip->total_size = chip->total_size; ++ flash->chip->page_size = chip->page_size; ++ ++ msg_cdbg("Hardware sequencing reports %d attached SPI flash chip", ++ (hwseq_data.size_comp1 != 0) ? 2 : 1); ++ if (hwseq_data.size_comp1 != 0) ++ msg_cdbg("s with a combined"); ++ else ++ msg_cdbg(" with a"); ++ msg_cdbg(" density of %d kB.\n", flash->chip->total_size); ++ ++ eraser = &(flash->chip->block_erasers[0]); ++ tmp = mmio_readl(ich_spibar + DENVERTON_REG_BFPREG); ++ dnv_bios_region_start = ((tmp & DENVERTON_BFPREG_PRB) >> DENVERTON_BFPREG_PRB_OFF) << 12; ++ dnv_bios_region_limit = ((tmp & DENVERTON_BFPREG_PRL) >> ++ DENVERTON_BFPREG_PRL_OFF) << 12 | ((1 << 12) -1); ++ dnv_bios_region_size = dnv_bios_region_limit - dnv_bios_region_start + 1; ++ eraser->eraseblocks[0].size = 4 * 1024; // 4K ++ eraser->eraseblocks[0].count = dnv_bios_region_size / ++ eraser->eraseblocks[0].size; ++ ++ flash->chip->tested = TEST_OK_PREW; ++ return 1; ++} ++ ++static int dnv_block_check(struct flashctx *flash, uint32_t addr, uint32_t len, SPI_SMI_CMD cmd) ++{ ++ char msg[3][4][32] = { ++ {"Read", "read", "", "Reading"}, ++ {"Erase", "erase", "Not erasing anthing.", "Erasing"}, ++ {"Write", "write", "", "Writing"}}; ++ ++ /* Although the hardware supports this (it would erase the whole block ++ * containing the address) we play safe here. */ ++ if (addr % DELL_DENVERTON_BLOCK_SIZE != 0) { ++ msg_cerr("%s address 0x%06x is not aligned to the %s " ++ "block boundary (any multiple of %d). %s\n", ++ msg[cmd][0], addr, msg[cmd][1], DELL_DENVERTON_BLOCK_SIZE, msg[cmd][2]); ++ return -1; ++ } ++ ++ if (addr + len > flash->chip->total_size * 1024) { ++ msg_perr("Request to %s some inaccessible memory address(es)" ++ " (addr=0x%x, len=%d). %s\n", msg[cmd][1], ++ addr, len, msg[cmd][2]); ++ return -1; ++ } ++ ++ msg_pdbg("%s %d bytes starting at 0x%06x.\n", msg[cmd][3], len, addr); ++ return 0; ++} ++ ++static int dnv_smi_spi(struct flashctx *flash, uint8_t *buf, uint32_t addr, ++ uint32_t len, SPI_SMI_CMD cmd) ++{ ++ int32_t ret = 0; ++ uint32_t block_index = 0; ++ ++ ret = dnv_block_check(flash, addr, len, cmd); ++ if (ret == 0) { ++ for (block_index = 0; block_index * DELL_DENVERTON_BLOCK_SIZE < len; block_index++ ) { ++ if ((ret = dnv_smi_doit(addr - dnv_bios_region_start, buf, cmd)) != 0) { ++ break; ++ } ++ buf += DELL_DENVERTON_BLOCK_SIZE; ++ addr += DELL_DENVERTON_BLOCK_SIZE; ++ } ++ } ++ return ret; ++} ++ ++static int dnv_smi_erase_spi(struct flashctx *flash, uint32_t addr, uint32_t len) ++{ ++ return dnv_smi_spi(flash, NULL, addr, len, SMI_ERASE_SPI); ++} ++ ++static int dnv_smi_read_spi(struct flashctx *flash, uint8_t *buf, uint32_t addr, uint32_t len) ++{ ++ return dnv_smi_spi(flash, buf, addr, len, SMI_READ_SPI); ++} ++ ++static int dnv_smi_write_spi(struct flashctx *flash, uint8_t *buf, uint32_t addr, uint32_t len) ++{ ++ return dnv_smi_spi(flash, buf, addr, len, SMI_WRITE_SPI); ++} ++ ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + #define ICH_BMWAG(x) ((x >> 24) & 0xff) + #define ICH_BMRAG(x) ((x >> 16) & 0xff) + #define ICH_BRWA(x) ((x >> 8) & 0xff) +@@ -1452,6 +1726,9 @@ static int ich9_handle_frap(uint32_t frap, int i) + msg_pdbg("FREG%i: %s region (0x%08x-0x%08x) is %s.\n", i, + region_names[i], base, (limit | 0x0fff), + access_names[rwperms]); ++#ifdef FORCE10_SPI_CHANGE ++ add_romentry(base, (limit | 0x0fff), region_names[i]); ++#endif + return 0; + } + +@@ -1514,6 +1791,90 @@ static void ich9_set_pr(int i, int read_prot, int write_prot) + msg_gspew("resulted in 0x%08x.\n", mmio_readl(addr)); + } + ++#if DELL_DENVERTON_SUPPORT == 1 ++/* returns 0 if region is unused or r/w */ ++static int dnv_handle_frap(uint32_t frap, int32_t i) ++{ ++ static const char *const access_names[4] = { ++ "locked", "read-only", "write-only", "read-write" ++ }; ++ static const char *const region_names[5] = { ++ "Flash Descriptor", "BIOS", "Management Engine", ++ "Gigabit Ethernet", "Platform Data" ++ }; ++ uint32_t base, limit; ++ int32_t rwperms = (((ICH_BRWA(frap) >> i) & 1) << 1) | ++ (((ICH_BRRA(frap) >> i) & 1) << 0); ++ int32_t offset = DENVERTON_REG_FREG0 + i * 4; ++ uint32_t freg = mmio_readl(ich_spibar + offset); ++ ++ base = ICH_FREG_BASE(freg); ++ limit = ICH_FREG_LIMIT(freg); ++ if (base > limit || (freg == 0 && i > 0)) { ++ /* this FREG is disabled */ ++ msg_pdbg2("0x%02X: 0x%08x FREG%i: %s region is unused.\n", ++ offset, freg, i, region_names[i]); ++ return 0; ++ } ++ msg_pdbg("0x%02X: 0x%08x ", offset, freg); ++ if (rwperms == 0x3) { ++ msg_pdbg("FREG%i: %s region (0x%08x-0x%08x) is %s.\n", i, ++ region_names[i], base, (limit | 0x0fff), ++ access_names[rwperms]); ++ return 0; ++ } ++ ++ msg_pwarn("FREG%i: Warning: %s region (0x%08x-0x%08x) is %s.\n", i, ++ region_names[i], base, (limit | 0x0fff), ++ access_names[rwperms]); ++ return 1; ++} ++ ++/* returns 0 if range is unused (i.e. r/w) */ ++static int dnv_handle_pr(int32_t i) ++{ ++ static const char *const access_names[3] = { ++ "locked", "read-only", "write-only" ++ }; ++ uint8_t off = DENVERTON_REG_PR0 + (i * 4); ++ uint32_t pr = mmio_readl(ich_spibar + off); ++ uint32_t rwperms = ICH_PR_PERMS(pr); ++ ++ if (rwperms == 0x3) { ++ msg_pdbg2("0x%02X: 0x%08x (PR%u is unused)\n", off, pr, i); ++ return 0; ++ } ++ ++ msg_pdbg("0x%02X: 0x%08x ", off, pr); ++ msg_pwarn("PR%u: Warning: 0x%08x-0x%08x is %s.\n", i, ICH_FREG_BASE(pr), ++ ICH_FREG_LIMIT(pr) | 0x0fff, access_names[rwperms]); ++ return 1; ++} ++ ++/* Set/Clear the read and write protection enable bits of PR register @i ++ * according to @read_prot and @write_prot. */ ++static void dnv_set_pr(int32_t i, int32_t read_prot, int32_t write_prot) ++{ ++ void *addr = ich_spibar + DENVERTON_REG_PR0 + (i * 4); ++ uint32_t old = mmio_readl(addr); ++ uint32_t new; ++ ++ msg_gspew("PR%u is 0x%08x", i, old); ++ new = old & ~((1 << DENVERTON_PR_RP_OFF) | (1 << DENVERTON_PR_WP_OFF)); ++ if (read_prot) ++ new |= (1 << DENVERTON_PR_RP_OFF); ++ if (write_prot) ++ new |= (1 << DENVERTON_PR_WP_OFF); ++ if (old == new) { ++ msg_gspew(" already.\n"); ++ return; ++ } ++ msg_gspew(", trying to set it to 0x%08x ", new); ++ rmmio_writel(new, addr); ++ msg_gspew("resulted in 0x%08x.\n", mmio_readl(addr)); ++} ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ + static const struct spi_programmer spi_programmer_ich7 = { + .type = SPI_CONTROLLER_ICH7, + .max_data_read = 64, +@@ -1545,12 +1906,21 @@ static const struct opaque_programmer opaque_programmer_ich_hwseq = { + .erase = ich_hwseq_block_erase, + }; + +-int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, +- enum ich_chipset ich_gen) ++#if DELL_DENVERTON_SUPPORT == 1 ++static const struct opaque_programmer opaque_master_dnv_hwseq = { ++ .max_data_read = 64, ++ .max_data_write = 64, ++ .probe = dnv_hwseq_probe, ++ .read = dnv_smi_read_spi, ++ .write = dnv_smi_write_spi, ++ .erase = dnv_smi_erase_spi, ++}; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ ++ ++int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_gen) + { + int i; +- uint8_t old, new; +- uint16_t spibar_offset, tmp2; ++ uint16_t tmp2; + uint32_t tmp; + char *arg; + int ich_spi_force = 0; +@@ -1564,42 +1934,18 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, + } ich_spi_mode = ich_auto; + + ich_generation = ich_gen; +- +- switch (ich_generation) { +- case CHIPSET_ICH_UNKNOWN: +- return ERROR_FATAL; +- case CHIPSET_ICH7: +- case CHIPSET_ICH8: +- spibar_offset = 0x3020; +- break; +- case CHIPSET_ICH9: +- default: /* Future version might behave the same */ +- spibar_offset = 0x3800; +- break; +- } +- +- /* SPIBAR is at RCRB+0x3020 for ICH[78] and RCRB+0x3800 for ICH9. */ +- msg_pdbg("SPIBAR = 0x%x + 0x%04x\n", base, spibar_offset); +- +- /* Assign Virtual Address */ +- ich_spibar = rcrb + spibar_offset; ++ ich_spibar = spibar; + + switch (ich_generation) { + case CHIPSET_ICH7: ++ case CHIPSET_TUNNEL_CREEK: ++ case CHIPSET_CENTERTON: + msg_pdbg("0x00: 0x%04x (SPIS)\n", + mmio_readw(ich_spibar + 0)); + msg_pdbg("0x02: 0x%04x (SPIC)\n", + mmio_readw(ich_spibar + 2)); + msg_pdbg("0x04: 0x%08x (SPIA)\n", + mmio_readl(ich_spibar + 4)); +- for (i = 0; i < 8; i++) { +- int offs; +- offs = 8 + (i * 8); +- msg_pdbg("0x%02x: 0x%08x (SPID%d)\n", offs, +- mmio_readl(ich_spibar + offs), i); +- msg_pdbg("0x%02x: 0x%08x (SPID%d+4)\n", offs + 4, +- mmio_readl(ich_spibar + offs + 4), i); +- } + ichspi_bbar = mmio_readl(ich_spibar + 0x50); + msg_pdbg("0x50: 0x%08x (BBAR)\n", + ichspi_bbar); +@@ -1625,7 +1971,123 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, + ich_set_bbar(0); + register_spi_programmer(&spi_programmer_ich7); + break; ++#if DELL_DENVERTON_SUPPORT == 1 ++ case CHIPSET_DENVERTON: ++ ++ tmp2 = mmio_readw(ich_spibar + DENVERTON_REG_HSFS); ++ msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2); ++ if (tmp2 & DENVERTON_HSFS_FLOCKDN) { ++ msg_pwarn("Warning: SPI Configuration Lockdown activated.\n"); ++ ichspi_lock = 1; ++ } ++ if (tmp2 & DENVERTON_HSFS_FDV) ++ desc_valid = 1; ++ if (!(tmp2 & DENVERTON_HSFS_FDOPSS) && desc_valid) ++ msg_pinfo("The Flash Descriptor Override Strap-Pin is set. " ++ "Restrictions implied by\n" ++ "the Master Section of the flash descriptor are " ++ "NOT in effect. Please note\n" ++ "that Protected Range (PR) restrictions still apply.\n"); ++ if (desc_valid) { ++ tmp2 = mmio_readw(ich_spibar + DENVERTON_REG_HSFC); ++ msg_pdbg("0x06: 0x%04x (HSFC)\n", tmp2); ++ } ++ ++ tmp = mmio_readl(ich_spibar + DENVERTON_REG_FADDR); ++ msg_pdbg2("0x08: 0x%08x (FADDR)\n", tmp); ++ ++ if (desc_valid) { ++ tmp = mmio_readl(ich_spibar + DENVERTON_REG_FRAP); ++ msg_pdbg("0x50: 0x%08x (FRAP)", tmp); ++ ++ /* Handle FREGx and FRAP registers */ ++ for (i=0; i < 5; i++) ++ ich_spi_rw_restricted |= dnv_handle_frap(tmp, i); ++ if (ich_spi_rw_restricted) ++ msg_pwarn("Not all flash regions are freely accessible " ++ "by flashrom. This is most likely\n" ++ "due to an active ME. " ++ "Please see http://flashrom.org/ME for details.\n"); ++ } ++ ++ /* Handle PR registers */ ++ for (i = 0; i < 5; i++) { ++ if (!ichspi_lock) ++ dnv_set_pr(i, 0, 0); ++ ich_spi_rw_restricted |= dnv_handle_pr(i); ++ } ++ ++ if (ich_spi_force) ++ msg_pinfo("Continuing with write support because " ++ "the user forced us to!\n"); ++ ++ if (desc_valid) { ++ tmp = mmio_readl(ich_spibar + DENVERTON_REG_VSCC0); ++ msg_pdbg("0xC4: 0x%08x (VSCC0)\n", tmp); ++ ++ tmp = mmio_readl(ich_spibar + DENVERTON_REG_VSCC1); ++ msg_pdbg("0xC8: 0x%08x (VSCC1)\n", tmp); ++ ++ if (read_ich_descriptors_via_fdo(ich_spibar, &desc) == ICH_RET_OK) ++ prettyprint_ich_descriptors(ich_gen, &desc); ++ ++ /* If the descriptor is valid and indicates multiple ++ * flash devices we need to use hwseq to be able to ++ * access the second flash device. ++ */ ++ if (ich_spi_mode == ich_auto && desc.content.NC != 0) { ++ msg_pinfo("Enabling hardware sequencing due to " ++ "multiple flash chips detected.\n"); ++ ich_spi_mode = ich_hwseq; ++ } ++ } ++ ++ if (ich_spi_mode == ich_auto && ichspi_lock) { ++ msg_pinfo("Enabling hardware sequencing because " ++ "some important opcode is locked.\n"); ++ ich_spi_mode = ich_hwseq; ++ } ++ ++ if (ich_spi_mode == ich_auto && ichspi_lock) { ++ msg_pinfo("Enabling hardware sequencing because " ++ "some important opcode is locked.\n"); ++ ich_spi_mode = ich_hwseq; ++ } ++ ++ if (ich_spi_mode == ich_hwseq) { ++ if (!desc_valid) { ++ msg_perr("Hardware sequencing was requested " ++ "but the flash descriptor is not valid. Aborting.\n"); ++ return ERROR_FATAL; ++ } ++ ++ uint32_t tmpi = getFCBA_component_density(&desc, 0); ++ if (tmpi < 0) { ++ msg_perr("Could not determine density of " ++ "flash component %d.\n", 0); ++ return ERROR_FATAL; ++ } ++ hwseq_data.size_comp0 = tmpi; ++ ++ tmpi = getFCBA_component_density(&desc, 1); ++ if (tmpi < 0) { ++ msg_perr("Could not determine density of flash component %d.\n", 1); ++ return ERROR_FATAL; ++ } ++ hwseq_data.size_comp1 = tmpi; ++ ++ register_opaque_programmer(&opaque_master_dnv_hwseq); ++ } else { ++ msg_perr("[%s:%d] Error!!!\n", __func__, __LINE__); ++ return -1; ++ } ++ ++ break; ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + case CHIPSET_ICH8: ++#ifdef DELL_AVOTON_SUPPORT ++ case CHIPSET_AVOTON: ++#endif + default: /* Future version might behave the same */ + arg = extract_programmer_param("ich_spi_mode"); + if (arg && !strcmp(arg, "hwseq")) { +@@ -1669,7 +2131,11 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, + msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2); + prettyprint_ich9_reg_hsfs(tmp2); + if (tmp2 & HSFS_FLOCKDN) { ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("Warning: SPI Configuration Lockdown activated.\n"); ++#else + msg_pwarn("Warning: SPI Configuration Lockdown activated.\n"); ++#endif + ichspi_lock = 1; + } + if (tmp2 & HSFS_FDV) +@@ -1690,6 +2156,7 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, + msg_pdbg2("0x08: 0x%08x (FADDR)\n", tmp); + + if (desc_valid) { ++ + tmp = mmio_readl(ich_spibar + ICH9_REG_FRAP); + msg_pdbg("0x50: 0x%08x (FRAP)\n", tmp); + msg_pdbg("BMWAG 0x%02x, ", ICH_BMWAG(tmp)); +@@ -1750,10 +2217,15 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, + msg_pdbg("VSCC: "); + prettyprint_ich_reg_vscc(tmp, MSG_DEBUG); + } else { ++#ifdef DELL_AVOTON_SUPPORT ++ if (ich_generation != CHIPSET_AVOTON) { ++#endif + ichspi_bbar = mmio_readl(ich_spibar + ICH9_REG_BBAR); + msg_pdbg("0xA0: 0x%08x (BBAR)\n", + ichspi_bbar); +- ++#ifdef DELL_AVOTON_SUPPORT ++ } ++#endif + if (desc_valid) { + tmp = mmio_readl(ich_spibar + ICH9_REG_LVSCC); + msg_pdbg("0xC4: 0x%08x (LVSCC)\n", tmp); +@@ -1811,21 +2283,6 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, + break; + } + +- old = pci_read_byte(dev, 0xdc); +- msg_pdbg("SPI Read Configuration: "); +- new = (old >> 2) & 0x3; +- switch (new) { +- case 0: +- case 1: +- case 2: +- msg_pdbg("prefetching %sabled, caching %sabled, ", +- (new & 0x2) ? "en" : "dis", +- (new & 0x1) ? "dis" : "en"); +- break; +- default: +- msg_pdbg("invalid prefetching/caching settings, "); +- break; +- } + return 0; + } + +@@ -1844,7 +2301,9 @@ int via_init_spi(struct pci_dev *dev, uint32_t mmio_base) + { + int i; + +- ich_spibar = physmap("VIA SPI MMIO registers", mmio_base, 0x70); ++ ich_spibar = rphysmap("VIA SPI MMIO registers", mmio_base, 0x70); ++ if (ich_spibar == ERROR_PTR) ++ return ERROR_FATAL; + /* Do we really need no write enable? Like the LPC one at D17F0 0x40 */ + + /* Not sure if it speaks all these bus protocols. */ +diff --git a/internal.c b/internal.c +index ab3c81f..30b184f 100644 +--- a/internal.c ++++ b/internal.c +@@ -331,9 +331,8 @@ int internal_init(void) + return ret; + + #if defined(__i386__) || defined(__x86_64__) +- /* Probe unconditionally for IT87* LPC->SPI translation and for +- * IT87* Parallel write enable. +- */ ++ /* 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(); + #endif + +diff --git a/it85spi.c b/it85spi.c +index 0b074eb..7efc680 100644 +--- a/it85spi.c ++++ b/it85spi.c +@@ -262,6 +262,9 @@ static int it85xx_spi_common_init(struct superio s) + * Major TODO here, and it will be a lot of work. + */ + base = (chipaddr)physmap("it85 communication", 0xFFFFF000, 0x1000); ++ if (base == (chipaddr)ERROR_PTR) ++ return 1; ++ + msg_pdbg("%s():%d base=0x%08x\n", __func__, __LINE__, + (unsigned int)base); + ce_high = (unsigned char *)(base + 0xE00); /* 0xFFFFFE00 */ +diff --git a/it87spi.c b/it87spi.c +index 8e4e0ad..be7f234 100644 +--- a/it87spi.c ++++ b/it87spi.c +@@ -27,6 +27,7 @@ + + #include + #include ++#include + #include "flash.h" + #include "chipdrivers.h" + #include "programmer.h" +@@ -36,7 +37,7 @@ + #define ITE_SUPERIO_PORT1 0x2e + #define ITE_SUPERIO_PORT2 0x4e + +-uint16_t it8716f_flashport = 0; ++static uint16_t it8716f_flashport = 0; + /* use fast 33MHz SPI (<>0) or slow 16MHz (0) */ + static int fast_spi = 1; + +@@ -124,10 +125,40 @@ static const struct spi_programmer spi_programmer_it87xx = { + static uint16_t it87spi_probe(uint16_t port) + { + uint8_t tmp = 0; +- char *portpos = NULL; + uint16_t flashport = 0; + + enter_conf_mode_ite(port); ++ ++ char *param = extract_programmer_param("dualbiosindex"); ++ if (param != NULL) { ++ sio_write(port, 0x07, 0x07); /* Select GPIO LDN */ ++ tmp = sio_read(port, 0xEF); ++ if (*param == '\0') { /* Print current setting only. */ ++ free(param); ++ } else { ++ char *dualbiosindex_suffix; ++ errno = 0; ++ long chip_index = strtol(param, &dualbiosindex_suffix, 0); ++ free(param); ++ if (errno != 0 || *dualbiosindex_suffix != '\0' || chip_index < 0 || chip_index > 1) { ++ msg_perr("DualBIOS: Invalid chip index requested - choose 0 or 1.\n"); ++ exit_conf_mode_ite(port); ++ return 1; ++ } ++ if (chip_index != (tmp & 1)) { ++ msg_pdbg("DualBIOS: Previous chip index: %d\n", tmp & 1); ++ sio_write(port, 0xEF, (tmp & 0xFE) | chip_index); ++ tmp = sio_read(port, 0xEF); ++ if ((tmp & 1) != chip_index) { ++ msg_perr("DualBIOS: Chip selection failed.\n"); ++ exit_conf_mode_ite(port); ++ return 1; ++ } ++ } ++ } ++ msg_pinfo("DualBIOS: Selected chip: %d\n", tmp & 1); ++ } ++ + /* NOLDN, reg 0x24, mask out lowest bit (suspend) */ + tmp = sio_read(port, 0x24) & 0xFE; + /* Check if LPC->SPI translation is active. */ +@@ -163,11 +194,11 @@ static uint16_t it87spi_probe(uint16_t port) + flashport |= sio_read(port, 0x65); + msg_pdbg("Serial flash port 0x%04x\n", flashport); + /* Non-default port requested? */ +- portpos = extract_programmer_param("it87spiport"); +- if (portpos) { ++ param = extract_programmer_param("it87spiport"); ++ if (param) { + char *endptr = NULL; + unsigned long forced_flashport; +- forced_flashport = strtoul(portpos, &endptr, 0); ++ forced_flashport = strtoul(param, &endptr, 0); + /* Port 0, port >0x1000, unaligned ports and garbage strings + * are rejected. + */ +@@ -180,7 +211,8 @@ static uint16_t it87spi_probe(uint16_t port) + msg_perr("Error: it87spiport specified, but no valid " + "port specified.\nPort must be a multiple of " + "0x8 and lie between 0x100 and 0xff8.\n"); +- free(portpos); ++ exit_conf_mode_ite(port); ++ free(param); + return 1; + } else { + flashport = (uint16_t)forced_flashport; +@@ -190,7 +222,7 @@ static uint16_t it87spi_probe(uint16_t port) + sio_write(port, 0x65, (flashport & 0xff)); + } + } +- free(portpos); ++ free(param); + exit_conf_mode_ite(port); + it8716f_flashport = flashport; + if (internal_buses_supported & BUS_SPI) +@@ -228,6 +260,7 @@ int init_superio_ite(void) + case 0x8716: + case 0x8718: + case 0x8720: ++ case 0x8728: + ret |= it87spi_probe(superios[i].port); + break; + default: +diff --git a/layout.c b/layout.c +index 1bd3152..d00c16c 100644 +--- a/layout.c ++++ b/layout.c +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2005-2008 coresystems GmbH + * (Written by Stefan Reinauer for coresystems GmbH) ++ * Copyright (C) 2011-2013 Stefan Tauner + * + * 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 +@@ -25,24 +26,23 @@ + #include "flash.h" + #include "programmer.h" + +-static int romimages = 0; +- + #define MAX_ROMLAYOUT 32 + + typedef struct { +- unsigned int start; +- unsigned int end; ++ chipoff_t start; ++ chipoff_t end; + unsigned int included; + char name[256]; +-} romlayout_t; ++} romentry_t; + +-/* include_args lists arguments specified at the command line with -i. They +- * must be processed at some point so that desired regions are marked as +- * "included" in the rom_entries list. +- */ ++/* rom_entries store the entries specified in a layout file and associated run-time data */ ++static romentry_t rom_entries[MAX_ROMLAYOUT]; ++static int num_rom_entries = 0; /* the number of successfully parsed rom_entries */ ++ ++/* include_args holds the arguments specified at the command line with -i. They must be processed at some point ++ * so that desired regions are marked as "included" in the rom_entries list. */ + static char *include_args[MAX_ROMLAYOUT]; +-static int num_include_args = 0; /* the number of valid entries. */ +-static romlayout_t rom_entries[MAX_ROMLAYOUT]; ++static int num_include_args = 0; /* the number of valid include_args. */ + + #ifndef __LIBPAYLOAD__ + int read_romlayout(char *name) +@@ -62,12 +62,13 @@ int read_romlayout(char *name) + while (!feof(romlayout)) { + char *tstr1, *tstr2; + +- if (romimages >= MAX_ROMLAYOUT) { ++ if (num_rom_entries >= MAX_ROMLAYOUT) { + msg_gerr("Maximum number of ROM images (%i) in layout " + "file reached.\n", MAX_ROMLAYOUT); ++ fclose(romlayout); + return 1; + } +- if (2 != fscanf(romlayout, "%s %s\n", tempstr, rom_entries[romimages].name)) ++ if (2 != fscanf(romlayout, "%s %s\n", tempstr, rom_entries[num_rom_entries].name)) + continue; + #if 0 + // fscanf does not like arbitrary comments like that :( later +@@ -82,13 +83,13 @@ int read_romlayout(char *name) + fclose(romlayout); + return 1; + } +- rom_entries[romimages].start = strtol(tstr1, (char **)NULL, 16); +- rom_entries[romimages].end = strtol(tstr2, (char **)NULL, 16); +- rom_entries[romimages].included = 0; +- romimages++; ++ rom_entries[num_rom_entries].start = strtol(tstr1, (char **)NULL, 16); ++ rom_entries[num_rom_entries].end = strtol(tstr2, (char **)NULL, 16); ++ rom_entries[num_rom_entries].included = 0; ++ num_rom_entries++; + } + +- for (i = 0; i < romimages; i++) { ++ for (i = 0; i < num_rom_entries; i++) { + msg_gdbg("romlayout %08x - %08x named %s\n", + rom_entries[i].start, + rom_entries[i].end, rom_entries[i].name); +@@ -100,6 +101,33 @@ int read_romlayout(char *name) + } + #endif + ++ ++#ifdef FORCE10_SPI_CHANGE ++int add_romentry(chipoff_t start, chipoff_t end, const char *name) ++{ ++ int len = 0; ++ ++ if (num_rom_entries >= MAX_ROMLAYOUT) { ++ msg_gerr("Maximum number of ROM images (%i) in layout " ++ "file reached.\n", MAX_ROMLAYOUT); ++ return 1; ++ } ++ rom_entries[num_rom_entries].start = start; ++ rom_entries[num_rom_entries].end = end; ++ rom_entries[num_rom_entries].included = 1; ++ len = strlen(name); ++ if (len > 256) { ++ msg_gdbg("terminating rom entry name to 256 as it exceeds max length\n"); ++ len = 255; ++ } ++ strncpy(rom_entries[num_rom_entries].name, name, len); ++ msg_gdbg("Adding rom entry: %08x - %08x named %s\n", rom_entries[num_rom_entries].start, ++ rom_entries[num_rom_entries].end, rom_entries[num_rom_entries].name); ++ num_rom_entries++; ++ return 0; ++} ++#endif ++ + /* returns the index of the entry (or a negative value if it is not found) */ + int find_include_arg(const char *const name) + { +@@ -139,11 +167,11 @@ static int find_romentry(char *name) + { + int i; + +- if (!romimages) ++ if (num_rom_entries == 0) + return -1; + + msg_gspew("Looking for region \"%s\"... ", name); +- for (i = 0; i < romimages; i++) { ++ for (i = 0; i < num_rom_entries; i++) { + if (!strcmp(rom_entries[i].name, name)) { + rom_entries[i].included = 1; + msg_gspew("found.\n"); +@@ -166,7 +194,7 @@ int process_include_args(void) + return 0; + + /* User has specified an area, but no layout file is loaded. */ +- if (!romimages) { ++ if (num_rom_entries == 0) { + msg_gerr("Region requested (with -i \"%s\"), " + "but no layout data is available.\n", + include_args[0]); +@@ -190,15 +218,30 @@ int process_include_args(void) + return 0; + } + +-romlayout_t *get_next_included_romentry(unsigned int start) ++void layout_cleanup(void) ++{ ++ int i; ++ for (i = 0; i < num_include_args; i++) { ++ free(include_args[i]); ++ include_args[i] = NULL; ++ } ++ num_include_args = 0; ++ ++ for (i = 0; i < num_rom_entries; i++) { ++ rom_entries[i].included = 0; ++ } ++ num_rom_entries = 0; ++} ++ ++romentry_t *get_next_included_romentry(unsigned int start) + { + int i; + unsigned int best_start = UINT_MAX; +- romlayout_t *best_entry = NULL; +- romlayout_t *cur; ++ romentry_t *best_entry = NULL; ++ romentry_t *cur; + + /* First come, first serve for overlapping regions. */ +- for (i = 0; i < romimages; i++) { ++ for (i = 0; i < num_rom_entries; i++) { + cur = &rom_entries[i]; + if (!cur->included) + continue; +@@ -217,18 +260,43 @@ romlayout_t *get_next_included_romentry(unsigned int start) + return best_entry; + } + +-int handle_romentries(const struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents) ++/* Validate and - if needed - normalize layout entries. */ ++int normalize_romentries(const struct flashctx *flash) ++{ ++ chipsize_t total_size = flash->chip->total_size * 1024; ++ int ret = 0; ++ ++ int i; ++ for (i = 0; i < num_rom_entries; i++) { ++ if (rom_entries[i].start >= total_size || rom_entries[i].end >= total_size) { ++ msg_gwarn("Warning: Address range of region \"%s\" exceeds the current chip's " ++ "address space.\n", rom_entries[i].name); ++ if (rom_entries[i].included) ++ ret = 1; ++ } ++ if (rom_entries[i].start > rom_entries[i].end) { ++ msg_gerr("Error: Size of the address range of region \"%s\" is not positive.\n", ++ rom_entries[i].name); ++ ret = 1; ++ } ++ } ++ ++ return ret; ++} ++ ++int build_new_image(const struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents) + { + unsigned int start = 0; +- romlayout_t *entry; ++ romentry_t *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. + */ ++#ifndef FORCE10_SPI_CHANGE + if (num_include_args == 0) + return 0; +- ++#endif + /* Non-included romentries are ignored. + * The union of all included romentries is used from the new image. + */ +@@ -239,7 +307,7 @@ int handle_romentries(const struct flashctx *flash, uint8_t *oldcontents, uint8_ + memcpy(newcontents + start, oldcontents + start, + size - start); + break; +- } ++ } + /* For non-included region, copy from old content. */ + if (entry->start > start) + memcpy(newcontents + start, oldcontents + start, +diff --git a/linux_spi.c b/linux_spi.c +index d12fceb..f0c6404 100644 +--- a/linux_spi.c ++++ b/linux_spi.c +@@ -60,7 +60,7 @@ static const struct spi_programmer spi_programmer_linux = { + int linux_spi_init(void) + { + char *p, *endp, *dev; +- uint32_t speed = 0; ++ uint32_t speed_hz = 0; + /* FIXME: make the following configurable by CLI options. */ + /* SPI mode 0 (beware this also includes: MSB first, CS active low and others */ + const uint8_t mode = SPI_MODE_0; +@@ -68,7 +68,7 @@ int linux_spi_init(void) + + p = extract_programmer_param("spispeed"); + if (p && strlen(p)) { +- speed = (uint32_t)strtoul(p, &endp, 10) * 1024; ++ speed_hz = (uint32_t)strtoul(p, &endp, 10) * 1000; + if (p == endp) { + msg_perr("%s: invalid clock: %s kHz\n", __func__, p); + free(p); +@@ -98,14 +98,14 @@ int linux_spi_init(void) + return 1; + /* We rely on the shutdown function for cleanup from here on. */ + +- if (speed > 0) { +- if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1) { ++ if (speed_hz > 0) { ++ if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz) == -1) { + msg_perr("%s: failed to set speed to %d Hz: %s\n", +- __func__, speed, strerror(errno)); ++ __func__, speed_hz, strerror(errno)); + return 1; + } + +- msg_pdbg("Using %d kHz clock\n", speed); ++ msg_pdbg("Using %d kHz clock\n", speed_hz/1000); + } + + if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) { +diff --git a/mcp6x_spi.c b/mcp6x_spi.c +index ac40557..20e9bd8 100644 +--- a/mcp6x_spi.c ++++ b/mcp6x_spi.c +@@ -135,25 +135,20 @@ int mcp6x_spi_init(int want_spi) + + /* Accessing a NULL pointer BAR is evil. Don't do it. */ + if (!mcp6x_spibaraddr && want_spi) { +- msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR " +- "is invalid.\n"); ++ msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n"); + return 1; + } else if (!mcp6x_spibaraddr && !want_spi) { + msg_pdbg("MCP SPI is not used.\n"); + return 0; + } else if (mcp6x_spibaraddr && !want_spi) { +- msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently" +- " doesn't have SPI enabled.\n"); ++ msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n"); + /* FIXME: Should we enable SPI anyway? */ + return 0; + } + /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */ +- mcp6x_spibar = physmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544); +- +-#if 0 +- /* FIXME: Run the physunmap in a shutdown function. */ +- physunmap(mcp6x_spibar, 0x544); +-#endif ++ mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544); ++ if (mcp6x_spibar == ERROR_PTR) ++ return 1; + + status = mmio_readw(mcp6x_spibar + 0x530); + msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n", +diff --git a/nic3com.c b/nic3com.c +index 8d67b54..27e28c7 100644 +--- a/nic3com.c ++++ b/nic3com.c +@@ -96,6 +96,8 @@ int nic3com_init(void) + return 1; + + io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); ++ if (!io_base_addr) ++ return 1; + + id = dev->device_id; + +diff --git a/nicintel.c b/nicintel.c +index 56678e7..98ba29f 100644 +--- a/nicintel.c ++++ b/nicintel.c +@@ -59,13 +59,6 @@ static const struct par_programmer par_programmer_nicintel = { + .chip_writen = fallback_chip_writen, + }; + +-static int nicintel_shutdown(void *data) +-{ +- physunmap(nicintel_control_bar, NICINTEL_CONTROL_MEMMAP_SIZE); +- physunmap(nicintel_bar, NICINTEL_MEMMAP_SIZE); +- return 0; +-} +- + int nicintel_init(void) + { + struct pci_dev *dev = NULL; +@@ -83,18 +76,19 @@ int nicintel_init(void) + return 1; + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); +- nicintel_bar = physmap("Intel NIC flash", addr, NICINTEL_MEMMAP_SIZE); ++ if (!addr) ++ return 1; ++ ++ nicintel_bar = rphysmap("Intel NIC flash", addr, NICINTEL_MEMMAP_SIZE); + if (nicintel_bar == ERROR_PTR) +- goto error_out_unmap; ++ return 1; + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); +- /* FIXME: This is not an aligned mapping. Use 4k? */ +- nicintel_control_bar = physmap("Intel NIC control/status reg", +- addr, NICINTEL_CONTROL_MEMMAP_SIZE); +- if (nicintel_control_bar == ERROR_PTR) +- goto error_out; ++ if (!addr) ++ return 1; + +- if (register_shutdown(nicintel_shutdown, NULL)) ++ nicintel_control_bar = rphysmap("Intel NIC control/status reg", addr, NICINTEL_CONTROL_MEMMAP_SIZE); ++ if (nicintel_control_bar == ERROR_PTR) + return 1; + + /* FIXME: This register is pretty undocumented in all publicly available +@@ -112,11 +106,6 @@ int nicintel_init(void) + register_par_programmer(&par_programmer_nicintel, BUS_PARALLEL); + + return 0; +- +-error_out_unmap: +- physunmap(nicintel_bar, NICINTEL_MEMMAP_SIZE); +-error_out: +- return 1; + } + + static void nicintel_chip_writeb(const struct flashctx *flash, uint8_t val, +diff --git a/nicintel_spi.c b/nicintel_spi.c +index 0045c09..1522c9b 100644 +--- a/nicintel_spi.c ++++ b/nicintel_spi.c +@@ -19,10 +19,16 @@ + */ + + /* +- * Datasheet: ++ * Datasheets: + * PCI/PCI-X Family of Gigabit Ethernet Controllers Software Developer's Manual + * 82540EP/EM, 82541xx, 82544GC/EI, 82545GM/EM, 82546GB/EB, and 82547xx +- * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf ++ * http://www.intel.com/content/www/us/en/ethernet-controllers/pci-pci-x-family-gbe-controllers-software-dev-manual.html ++ * ++ * PCIe GbE Controllers Open Source Software Developer's Manual ++ * http://www.intel.com/content/www/us/en/ethernet-controllers/pcie-gbe-controllers-open-source-manual.html ++ * ++ * Intel 82574 Gigabit Ethernet Controller Family Datasheet ++ * http://www.intel.com/content/www/us/en/ethernet-controllers/82574l-gbe-controller-datasheet.html + */ + + #include +@@ -72,6 +78,7 @@ const struct dev_entry nics_intel_spi[] = { + {PCI_VENDOR_ID_INTEL, 0x1076, OK, "Intel", "82541GI Gigabit Ethernet Controller"}, + {PCI_VENDOR_ID_INTEL, 0x107c, OK, "Intel", "82541PI Gigabit Ethernet Controller"}, + {PCI_VENDOR_ID_INTEL, 0x10b9, OK, "Intel", "82572EI Gigabit Ethernet Controller"}, ++ {PCI_VENDOR_ID_INTEL, 0x10d3, OK, "Intel", "82574L Gigabit Ethernet Controller"}, + + {0}, + }; +@@ -151,16 +158,12 @@ static int nicintel_spi_shutdown(void *data) + { + uint32_t tmp; + +- /* Disable writes manually. See the comment about EECD in +- * nicintel_spi_init() for details. +- */ ++ /* Disable writes manually. See the comment about EECD in nicintel_spi_init() for details. */ + tmp = pci_mmio_readl(nicintel_spibar + EECD); + tmp &= ~FLASH_WRITES_ENABLED; + tmp |= FLASH_WRITES_DISABLED; + pci_mmio_writel(tmp, nicintel_spibar + EECD); + +- physunmap(nicintel_spibar, MEMMAP_SIZE); +- + return 0; + } + +@@ -177,8 +180,13 @@ int nicintel_spi_init(void) + return 1; + + io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); +- nicintel_spibar = physmap("Intel Gigabit NIC w/ SPI flash", +- io_base_addr, MEMMAP_SIZE); ++ if (!io_base_addr) ++ return 1; ++ ++ nicintel_spibar = rphysmap("Intel Gigabit NIC w/ SPI flash", io_base_addr, MEMMAP_SIZE); ++ if (nicintel_spibar == ERROR_PTR) ++ return 1; ++ + /* Automatic restore of EECD on shutdown is not possible because EECD + * does not only contain FLASH_WRITES_DISABLED|FLASH_WRITES_ENABLED, + * but other bits with side effects as well. Those other bits must be +diff --git a/nicnatsemi.c b/nicnatsemi.c +index d62a73f..cb8da6d 100644 +--- a/nicnatsemi.c ++++ b/nicnatsemi.c +@@ -64,6 +64,8 @@ int nicnatsemi_init(void) + return 1; + + io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); ++ if (!io_base_addr) ++ return 1; + + /* The datasheet shows address lines MA0-MA16 in one place and MA0-MA15 + * in another. My NIC has MA16 connected to A16 on the boot ROM socket +diff --git a/nicrealtek.c b/nicrealtek.c +index fb8e9e1..02fbd39 100644 +--- a/nicrealtek.c ++++ b/nicrealtek.c +@@ -69,6 +69,8 @@ int nicrealtek_init(void) + return 1; + + io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); ++ if (!io_base_addr) ++ return 1; + + /* Beware, this ignores the vendor ID! */ + switch (dev->device_id) { +diff --git a/ogp_spi.c b/ogp_spi.c +index 0c09d6a..23431d1 100644 +--- a/ogp_spi.c ++++ b/ogp_spi.c +@@ -97,12 +97,6 @@ static const struct bitbang_spi_master bitbang_spi_master_ogp = { + .half_period = 0, + }; + +-static int ogp_spi_shutdown(void *data) +-{ +- physunmap(ogp_spibar, 4096); +- return 0; +-} +- + int ogp_spi_init(void) + { + struct pci_dev *dev = NULL; +@@ -126,8 +120,10 @@ int ogp_spi_init(void) + ogp_reg_sck = OGA1_XP10_CPROM_SCK; + } else { + msg_perr("Invalid or missing rom= parameter.\n"); ++ free(type); + return 1; + } ++ free(type); + + if (rget_io_perms()) + return 1; +@@ -137,9 +133,11 @@ int ogp_spi_init(void) + return 1; + + io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); +- ogp_spibar = physmap("OGP registers", io_base_addr, 4096); ++ if (!io_base_addr) ++ return 1; + +- if (register_shutdown(ogp_spi_shutdown, NULL)) ++ ogp_spibar = rphysmap("OGP registers", io_base_addr, 4096); ++ if (ogp_spibar == ERROR_PTR) + return 1; + + if (bitbang_spi_init(&bitbang_spi_master_ogp)) +diff --git a/pcidev.c b/pcidev.c +index c7e9d78..3a3f77c 100644 +--- a/pcidev.c ++++ b/pcidev.c +@@ -94,7 +94,7 @@ uintptr_t pcidev_readbar(struct pci_dev *dev, int bar) + + supported_cycles = pci_read_word(dev, PCI_COMMAND); + +- msg_pdbg("Requested BAR is "); ++ msg_pdbg("Requested BAR is of type "); + switch (bartype) { + case TYPE_MEMBAR: + msg_pdbg("MEM"); +diff --git a/physmap.c b/physmap.c +index 932fe75..150c44a 100644 +--- a/physmap.c ++++ b/physmap.c +@@ -21,11 +21,13 @@ + */ + + #include ++#include + #include + #include + #include + #include + #include "flash.h" ++#include "programmer.h" + #include "hwaccess.h" + + #if !defined(__DJGPP__) && !defined(__LIBPAYLOAD__) +@@ -88,7 +90,7 @@ static void *sys_physmap(uintptr_t phys_addr, size_t len) + #define sys_physmap_rw_uncached sys_physmap + #define sys_physmap_ro_cached sys_physmap + +-void physunmap(void *virt_addr, size_t len) ++void sys_physunmap_unaligned(void *virt_addr, size_t len) + { + __dpmi_meminfo mi; + +@@ -117,7 +119,7 @@ void *sys_physmap(uintptr_t phys_addr, size_t len) + #define sys_physmap_rw_uncached sys_physmap + #define sys_physmap_ro_cached sys_physmap + +-void physunmap(void *virt_addr, size_t len) ++void sys_physunmap_unaligned(void *virt_addr, size_t len) + { + } + #elif defined(__MACH__) && defined(__APPLE__) +@@ -138,7 +140,7 @@ static void *sys_physmap(uintptr_t phys_addr, size_t len) + #define sys_physmap_rw_uncached sys_physmap + #define sys_physmap_ro_cached sys_physmap + +-void physunmap(void *virt_addr, size_t len) ++void sys_physunmap_unaligned(void *virt_addr, size_t len) + { + unmap_physical(virt_addr, len); + } +@@ -164,12 +166,11 @@ static void *sys_physmap_rw_uncached(uintptr_t phys_addr, size_t len) + /* Open the memory device UNCACHED. Important for MMIO. */ + if (-1 == (fd_mem = open(MEM_DEV, O_RDWR | O_SYNC))) { + msg_perr("Critical error: open(" MEM_DEV "): %s\n", strerror(errno)); +- exit(2); ++ return ERROR_PTR; + } + } + +- virt_addr = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED, +- fd_mem, (off_t)phys_addr); ++ virt_addr = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd_mem, (off_t)phys_addr); + return MAP_FAILED == virt_addr ? ERROR_PTR : virt_addr; + } + +@@ -184,50 +185,77 @@ static void *sys_physmap_ro_cached(uintptr_t phys_addr, size_t len) + /* Open the memory device CACHED. */ + if (-1 == (fd_mem_cached = open(MEM_DEV, O_RDWR))) { + msg_perr("Critical error: open(" MEM_DEV "): %s\n", strerror(errno)); +- exit(2); ++ return ERROR_PTR; + } + } + +- virt_addr = mmap(NULL, len, PROT_READ, MAP_SHARED, +- fd_mem_cached, (off_t)phys_addr); ++ virt_addr = mmap(NULL, len, PROT_READ, MAP_SHARED, fd_mem_cached, (off_t)phys_addr); + return MAP_FAILED == virt_addr ? ERROR_PTR : virt_addr; + } + +-void physunmap(void *virt_addr, size_t len) ++void sys_physunmap_unaligned(void *virt_addr, size_t len) + { +- if (len == 0) { +- msg_pspew("Not unmapping zero size at %p\n", virt_addr); +- return; +- } +- + munmap(virt_addr, len); + } + #endif + +-#define PHYSMAP_NOFAIL 0 +-#define PHYSMAP_MAYFAIL 1 +-#define PHYSMAP_RW 0 +-#define PHYSMAP_RO 1 ++#define PHYSM_RW 0 ++#define PHYSM_RO 1 ++#define PHYSM_NOCLEANUP 0 ++#define PHYSM_CLEANUP 1 ++#define PHYSM_EXACT 0 ++#define PHYSM_ROUND 1 ++ ++/* Round start to nearest page boundary below and set len so that the resulting address range ends at the lowest ++ * possible page boundary where the original address range is still entirely contained. It returns the ++ * difference between the rounded start address and the original start address. */ ++static uintptr_t round_to_page_boundaries(uintptr_t *start, size_t *len) ++{ ++ uintptr_t page_size = getpagesize(); ++ uintptr_t page_mask = ~(page_size-1); ++ uintptr_t end = *start + *len; ++ uintptr_t old_start = *start; ++ msg_gspew("page_size=%" PRIxPTR "\n", page_size); ++ msg_gspew("pre-rounding: start=0x%0*" PRIxPTR ", len=0x%zx, end=0x%0*" PRIxPTR "\n", ++ PRIxPTR_WIDTH, *start, *len, PRIxPTR_WIDTH, end); ++ *start = *start & page_mask; ++ end = (end + page_size - 1) & page_mask; ++ *len = end - *start; ++ msg_gspew("post-rounding: start=0x%0*" PRIxPTR ", len=0x%zx, end=0x%0*" PRIxPTR "\n", ++ PRIxPTR_WIDTH, *start, *len, PRIxPTR_WIDTH, *start + *len); ++ return old_start - *start; ++} ++ ++struct undo_physmap_data { ++ void *virt_addr; ++ size_t len; ++}; ++ ++static int undo_physmap(void *data) ++{ ++ if (data == NULL) { ++ msg_perr("%s: tried to physunmap without valid data!\n", __func__); ++ return 1; ++ } ++ struct undo_physmap_data *d = data; ++ physunmap_unaligned(d->virt_addr, d->len); ++ free(data); ++ return 0; ++} + +-static void *physmap_common(const char *descr, uintptr_t phys_addr, +- size_t len, int mayfail, int readonly) ++static void *physmap_common(const char *descr, uintptr_t phys_addr, size_t len, bool readonly, bool autocleanup, ++ bool round) + { + void *virt_addr; ++ uintptr_t offset = 0; + + if (len == 0) { + msg_pspew("Not mapping %s, zero size at 0x%0*" PRIxPTR ".\n", descr, PRIxPTR_WIDTH, phys_addr); + return ERROR_PTR; + } + +- if ((getpagesize() - 1) & len) { +- msg_perr("Mapping %s at 0x%0*" PRIxPTR ", unaligned size 0x%zx.\n", +- descr, PRIxPTR_WIDTH, phys_addr, len); +- } +- +- if ((getpagesize() - 1) & phys_addr) { +- msg_perr("Mapping %s, 0x%zx bytes at unaligned 0x%0*" PRIxPTR ".\n", +- descr, len, PRIxPTR_WIDTH, phys_addr); +- } ++ if (round) ++ offset = round_to_page_boundaries(&phys_addr, &len); + + if (readonly) + virt_addr = sys_physmap_ro_cached(phys_addr, len); +@@ -253,23 +281,89 @@ static void *physmap_common(const char *descr, uintptr_t phys_addr, + "and reboot, or reboot into\n" + "single user mode.\n"); + #endif +- if (!mayfail) +- exit(3); ++ return ERROR_PTR; + } + +- return virt_addr; ++ if (autocleanup) { ++ struct undo_physmap_data *d = malloc(sizeof(struct undo_physmap_data)); ++ if (d == NULL) { ++ msg_perr("%s: Out of memory!\n", __func__); ++ physunmap_unaligned(virt_addr, len); ++ return ERROR_PTR; ++ } ++ ++ d->virt_addr = virt_addr; ++ d->len = len; ++ if (register_shutdown(undo_physmap, d) != 0) { ++ msg_perr("%s: Could not register shutdown function!\n", __func__); ++ physunmap_unaligned(virt_addr, len); ++ return ERROR_PTR; ++ } ++ } ++ ++ return virt_addr + offset; ++} ++ ++void physunmap_unaligned(void *virt_addr, size_t len) ++{ ++ /* No need to check for zero size, such mappings would have yielded ERROR_PTR. */ ++ if (virt_addr == ERROR_PTR) { ++#ifndef FORCE10_SPI_CHANGE ++ msg_perr("Trying to unmap a nonexisting mapping!\n" ++ "Please report a bug at flashrom@flashrom.org\n"); ++#else ++ msg_pdbg("Trying to unmap a nonexisting mapping!\n" ++ "Please report a bug at flashrom@flashrom.org\n"); ++#endif ++ return; ++ } ++ ++ sys_physunmap_unaligned(virt_addr, len); ++} ++ ++void physunmap(void *virt_addr, size_t len) ++{ ++ uintptr_t tmp; ++ ++ /* No need to check for zero size, such mappings would have yielded ERROR_PTR. */ ++ if (virt_addr == ERROR_PTR) { ++#ifndef FORCE10_SPI_CHANGE ++ msg_perr("Trying to unmap a nonexisting mapping!\n" ++ "Please report a bug at flashrom@flashrom.org\n"); ++#else ++ msg_pdbg("Trying to unmap a nonexisting mapping!\n" ++ "Please report a bug at flashrom@flashrom.org\n"); ++#endif ++ return; ++ } ++ tmp = (uintptr_t)virt_addr; ++ /* We assume that the virtual address of a page-aligned physical address is page-aligned as well. By ++ * extension, rounding a virtual unaligned address as returned by physmap should yield the same offset ++ * between rounded and original virtual address as between rounded and original physical address. ++ */ ++ round_to_page_boundaries(&tmp, &len); ++ virt_addr = (void *)tmp; ++ physunmap_unaligned(virt_addr, len); + } + + void *physmap(const char *descr, uintptr_t phys_addr, size_t len) + { +- return physmap_common(descr, phys_addr, len, PHYSMAP_NOFAIL, +- PHYSMAP_RW); ++ return physmap_common(descr, phys_addr, len, PHYSM_RW, PHYSM_NOCLEANUP, PHYSM_ROUND); ++} ++ ++void *rphysmap(const char *descr, uintptr_t phys_addr, size_t len) ++{ ++ return physmap_common(descr, phys_addr, len, PHYSM_RW, PHYSM_CLEANUP, PHYSM_ROUND); ++} ++ ++void *physmap_ro(const char *descr, uintptr_t phys_addr, size_t len) ++{ ++ return physmap_common(descr, phys_addr, len, PHYSM_RO, PHYSM_NOCLEANUP, PHYSM_ROUND); + } + +-void *physmap_try_ro(const char *descr, uintptr_t phys_addr, size_t len) ++void *physmap_ro_unaligned(const char *descr, uintptr_t phys_addr, size_t len) + { +- return physmap_common(descr, phys_addr, len, PHYSMAP_MAYFAIL, +- PHYSMAP_RO); ++ return physmap_common(descr, phys_addr, len, PHYSM_RO, PHYSM_NOCLEANUP, PHYSM_EXACT); + } + + /* MSR abstraction implementations for Linux, OpenBSD, FreeBSD/Dragonfly, OSX, libpayload +diff --git a/pony_spi.c b/pony_spi.c +index 101751f..2a3666f 100644 +--- a/pony_spi.c ++++ b/pony_spi.c +@@ -140,6 +140,7 @@ int pony_spi_init(void) + } else if (arg && !strlen(arg)) { + msg_perr("Error: Missing argument for programmer type.\n"); + free(arg); ++ return 1; + } else if (arg){ + msg_perr("Error: Invalid programmer type specified.\n"); + free(arg); +diff --git a/print.c b/print.c +index 6766eeb..6d2921b 100644 +--- a/print.c ++++ b/print.c +@@ -436,12 +436,13 @@ static void print_supported_boards_helper(const struct board_info *boards, + msg_ginfo("%s", b->name); + for (i = 0; i < maxboardlen - strlen(b->name); i++) + msg_ginfo(" "); +- if (b->working == OK) +- msg_ginfo("OK "); +- else if (b->working == NT) +- msg_ginfo("NT "); +- else +- msg_ginfo("BAD "); ++ ++ if (b->working == OK) ++ msg_ginfo("OK "); ++ else if (b->working == NT) ++ msg_ginfo("NT "); ++ else ++ msg_ginfo("BAD "); + + for (e = board_matches; e->vendor_name != NULL; e++) { + if (strcmp(e->vendor_name, b->vendor) +@@ -530,6 +531,7 @@ const struct board_info boards_known[] = { + B("abit", "AN-M2", OK, NULL, NULL), + B("abit", "AV8", OK, NULL, NULL), + B("abit", "AX8", OK, NULL, NULL), ++ B("abit", "BF6", OK, NULL, NULL), + B("abit", "BM6", OK, NULL, NULL), + B("abit", "Fatal1ty F-I90HD", OK, NULL, NULL), + B("abit", "IC7", OK, NULL, NULL), +@@ -695,6 +697,7 @@ const struct board_info boards_known[] = { + B("ASUS", "P5L-VM 1394", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5LVM_1394/", NULL), + B("ASUS", "P5LD2", NT, NULL, "Untested board enable."), + B("ASUS", "P5LD2-VM", NT, "http://www.asus.com/Motherboards/Intel_Socket_775/P5LD2VM/", "Untested board enable."), ++ B("ASUS", "P5LD2-VM DH", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5LD2VM_DH/", NULL), + B("ASUS", "P5LP-LE (Lithium-UL8E)", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00379616&tmp_task=prodinfoCategory&cc=us&dlc=en&lc=en&product=1159887", "This is an OEM board from HP."), + B("ASUS", "P5LP-LE (Epson OEM)", OK, NULL, "This is an OEM board from Epson (e.g. Endeavor MT7700)."), + B("ASUS", "P5LP-LE", NT, NULL, "This designation is used for OEM boards from HP, Epson and maybe others. The HP names vary and not all of them have been tested yet. Please report any success or failure, thanks."), +diff --git a/programmer.h b/programmer.h +index 4db8d58..d3cc557 100644 +--- a/programmer.h ++++ b/programmer.h +@@ -276,8 +276,11 @@ int processor_flash_enable(void); + + /* physmap.c */ + void *physmap(const char *descr, uintptr_t phys_addr, size_t len); +-void *physmap_try_ro(const char *descr, uintptr_t phys_addr, size_t len); ++void *rphysmap(const char *descr, uintptr_t phys_addr, size_t len); ++void *physmap_ro(const char *descr, uintptr_t phys_addr, size_t len); ++void *physmap_ro_unaligned(const char *descr, uintptr_t phys_addr, size_t len); + void physunmap(void *virt_addr, size_t len); ++void physunmap_unaligned(void *virt_addr, size_t len); + #if CONFIG_INTERNAL == 1 + int setup_cpu_msr(int cpu); + void cleanup_cpu_msr(void); +@@ -287,9 +290,11 @@ int cb_parse_table(const char **vendor, const char **model); + int cb_check_image(uint8_t *bios, int size); + + /* dmi.c */ ++#if defined(__i386__) || defined(__x86_64__) + extern int has_dmi_support; + void dmi_init(void); + int dmi_match(const char *pattern); ++#endif // defined(__i386__) || defined(__x86_64__) + + /* internal.c */ + struct superio { +@@ -550,10 +555,19 @@ int default_spi_write_256(struct flashctx *flash, uint8_t *buf, unsigned int sta + int default_spi_write_aai(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); + int register_spi_programmer(const struct spi_programmer *programmer); + +-/* The following enum is needed by ich_descriptor_tool and ich* code. */ ++/* The following enum is needed by ich_descriptor_tool and ich* code as well as in chipset_enable.c. */ + enum ich_chipset { + CHIPSET_ICH_UNKNOWN, +- CHIPSET_ICH7 = 7, ++#ifdef DELL_AVOTON_SUPPORT ++ CHIPSET_AVOTON, ++#endif ++ CHIPSET_ICH, ++ CHIPSET_ICH2345, ++ CHIPSET_ICH6, ++ CHIPSET_POULSBO, /* SCH U* */ ++ CHIPSET_TUNNEL_CREEK, /* Atom E6xx */ ++ CHIPSET_CENTERTON, /* Atom S1220 S1240 S1260 */ ++ CHIPSET_ICH7, + CHIPSET_ICH8, + CHIPSET_ICH9, + CHIPSET_ICH10, +@@ -563,13 +577,15 @@ enum ich_chipset { + CHIPSET_8_SERIES_LYNX_POINT, + CHIPSET_8_SERIES_LYNX_POINT_LP, + CHIPSET_8_SERIES_WELLSBURG, ++#if DELL_DENVERTON_SUPPORT == 1 ++ CHIPSET_DENVERTON, ++#endif /* #if DELL_DENVERTON_SUPPORT == 1 */ + }; + + /* ichspi.c */ + #if CONFIG_INTERNAL == 1 + extern uint32_t ichspi_bbar; +-int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, +- enum ich_chipset ich_generation); ++int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_generation); + int via_init_spi(struct pci_dev *dev, uint32_t mmio_base); + + /* amd_imc.c */ +@@ -659,7 +675,7 @@ typedef int fdtype; + + void sp_flush_incoming(void); + fdtype sp_openserport(char *dev, unsigned int baud); +-void __attribute__((noreturn)) sp_die(char *msg); ++int serialport_config(fdtype fd, unsigned int baud); + extern fdtype sp_fd; + /* expose serialport_shutdown as it's currently used by buspirate */ + int serialport_shutdown(void *data); +diff --git a/rayer_spi.c b/rayer_spi.c +index b312610..189341a 100644 +--- a/rayer_spi.c ++++ b/rayer_spi.c +@@ -17,11 +17,9 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +-/* Driver for the SPIPGM hardware by "RayeR" Martin Rehak. +- * See http://rayer.ic.cz/elektro/spipgm.htm for schematics and instructions. +- */ +- +-/* This driver uses non-portable direct I/O port accesses which won't work on ++/* Driver for various LPT adapters. ++ * ++ * This driver uses non-portable direct I/O port accesses which won't work on + * any non-x86 platform, and even on x86 there is a high chance there will be + * collisions with any loaded parallel port drivers. + * The big advantage of direct port I/O is OS independence and speed because +@@ -37,21 +35,87 @@ + #include "programmer.h" + #include "hwaccess.h" + +-enum rayer_type { +- TYPE_RAYER, +- TYPE_XILINX_DLC5, +-}; +- + /* We have two sets of pins, out and in. The numbers for both sets are + * independent and are bitshift values, not real pin numbers. + * Default settings are for the RayeR hardware. + */ +-/* Pins for master->slave direction */ +-static int rayer_cs_bit = 5; +-static int rayer_sck_bit = 6; +-static int rayer_mosi_bit = 7; +-/* Pins for slave->master direction */ +-static int rayer_miso_bit = 6; ++ ++struct rayer_programmer { ++ const char *type; ++ const enum test_state status; ++ const char *description; ++ const void *dev_data; ++}; ++ ++struct rayer_pinout { ++ uint8_t cs_bit; ++ uint8_t sck_bit; ++ uint8_t mosi_bit; ++ uint8_t miso_bit; ++ void (*preinit)(const void *); ++ int (*shutdown)(void *); ++}; ++ ++static const struct rayer_pinout rayer_spipgm = { ++ .cs_bit = 5, ++ .sck_bit = 6, ++ .mosi_bit = 7, ++ .miso_bit = 6, ++}; ++ ++static void dlc5_preinit(const void *); ++static int dlc5_shutdown(void *); ++ ++static const struct rayer_pinout xilinx_dlc5 = { ++ .cs_bit = 2, ++ .sck_bit = 1, ++ .mosi_bit = 0, ++ .miso_bit = 4, ++ .preinit = dlc5_preinit, ++ .shutdown = dlc5_shutdown, ++}; ++ ++static void byteblaster_preinit(const void *); ++static int byteblaster_shutdown(void *); ++ ++static const struct rayer_pinout altera_byteblastermv = { ++ .cs_bit = 1, ++ .sck_bit = 0, ++ .mosi_bit = 6, ++ .miso_bit = 7, ++ .preinit = byteblaster_preinit, ++ .shutdown = byteblaster_shutdown, ++}; ++ ++static void stk200_preinit(const void *); ++static int stk200_shutdown(void *); ++ ++static const struct rayer_pinout atmel_stk200 = { ++ .cs_bit = 7, ++ .sck_bit = 4, ++ .mosi_bit = 5, ++ .miso_bit = 6, ++ .preinit = stk200_preinit, ++ .shutdown = stk200_shutdown, ++}; ++ ++static const struct rayer_pinout wiggler_lpt = { ++ .cs_bit = 1, ++ .sck_bit = 2, ++ .mosi_bit = 3, ++ .miso_bit = 7, ++}; ++ ++static const struct rayer_programmer rayer_spi_types[] = { ++ {"rayer", NT, "RayeR SPIPGM", &rayer_spipgm}, ++ {"xilinx", NT, "Xilinx Parallel Cable III (DLC 5)", &xilinx_dlc5}, ++ {"byteblastermv", OK, "Altera ByteBlasterMV", &altera_byteblastermv}, ++ {"stk200", NT, "Atmel STK200/300 adapter", &atmel_stk200}, ++ {"wiggler", OK, "Wiggler LPT", &wiggler_lpt}, ++ {0}, ++}; ++ ++static const struct rayer_pinout *pinout = NULL; + + static uint16_t lpt_iobase; + +@@ -60,22 +124,22 @@ static uint8_t lpt_outbyte; + + static void rayer_bitbang_set_cs(int val) + { +- lpt_outbyte &= ~(1 << rayer_cs_bit); +- lpt_outbyte |= (val << rayer_cs_bit); ++ lpt_outbyte &= ~(1 << pinout->cs_bit); ++ lpt_outbyte |= (val << pinout->cs_bit); + OUTB(lpt_outbyte, lpt_iobase); + } + + static void rayer_bitbang_set_sck(int val) + { +- lpt_outbyte &= ~(1 << rayer_sck_bit); +- lpt_outbyte |= (val << rayer_sck_bit); ++ lpt_outbyte &= ~(1 << pinout->sck_bit); ++ lpt_outbyte |= (val << pinout->sck_bit); + OUTB(lpt_outbyte, lpt_iobase); + } + + static void rayer_bitbang_set_mosi(int val) + { +- lpt_outbyte &= ~(1 << rayer_mosi_bit); +- lpt_outbyte |= (val << rayer_mosi_bit); ++ lpt_outbyte &= ~(1 << pinout->mosi_bit); ++ lpt_outbyte |= (val << pinout->mosi_bit); + OUTB(lpt_outbyte, lpt_iobase); + } + +@@ -83,8 +147,8 @@ static int rayer_bitbang_get_miso(void) + { + uint8_t tmp; + +- tmp = INB(lpt_iobase + 1); +- tmp = (tmp >> rayer_miso_bit) & 0x1; ++ tmp = INB(lpt_iobase + 1) ^ 0x80; // bit.7 inverted ++ tmp = (tmp >> pinout->miso_bit) & 0x1; + return tmp; + } + +@@ -99,8 +163,8 @@ static const struct bitbang_spi_master bitbang_spi_master_rayer = { + + int rayer_spi_init(void) + { ++ const struct rayer_programmer *prog = rayer_spi_types; + char *arg = NULL; +- enum rayer_type rayer_type = TYPE_RAYER; + + /* Non-default port requested? */ + arg = extract_programmer_param("iobase"); +@@ -138,36 +202,20 @@ int rayer_spi_init(void) + + arg = extract_programmer_param("type"); + if (arg) { +- if (!strcasecmp(arg, "rayer")) { +- rayer_type = TYPE_RAYER; +- } else if (!strcasecmp(arg, "xilinx")) { +- rayer_type = TYPE_XILINX_DLC5; +- } else { ++ for (; prog->type != NULL; prog++) { ++ if (strcasecmp(arg, prog->type) == 0) { ++ break; ++ } ++ } ++ if (prog->type == NULL) { + msg_perr("Error: Invalid device type specified.\n"); + free(arg); + return 1; + } ++ free(arg); + } +- free(arg); +- switch (rayer_type) { +- case TYPE_RAYER: +- msg_pdbg("Using RayeR SPIPGM pinout.\n"); +- /* Bits for master->slave direction */ +- rayer_cs_bit = 5; +- rayer_sck_bit = 6; +- rayer_mosi_bit = 7; +- /* Bits for slave->master direction */ +- rayer_miso_bit = 6; +- break; +- case TYPE_XILINX_DLC5: +- msg_pdbg("Using Xilinx Parallel Cable III (DLC 5) pinout.\n"); +- /* Bits for master->slave direction */ +- rayer_cs_bit = 2; +- rayer_sck_bit = 1; +- rayer_mosi_bit = 0; +- /* Bits for slave->master direction */ +- rayer_miso_bit = 4; +- } ++ msg_pinfo("Using %s pinout.\n", prog->description); ++ pinout = (struct rayer_pinout *)prog->dev_data; + + if (rget_io_perms()) + return 1; +@@ -175,12 +223,60 @@ int rayer_spi_init(void) + /* Get the initial value before writing to any line. */ + lpt_outbyte = INB(lpt_iobase); + ++ if (pinout->shutdown) ++ register_shutdown(pinout->shutdown, (void*)pinout); ++ if (pinout->preinit) ++ pinout->preinit(pinout); ++ + if (bitbang_spi_init(&bitbang_spi_master_rayer)) + return 1; + + return 0; + } + ++static void byteblaster_preinit(const void *data){ ++ msg_pdbg("byteblaster_preinit\n"); ++ /* Assert #EN signal. */ ++ OUTB(2, lpt_iobase + 2 ); ++} ++ ++static int byteblaster_shutdown(void *data){ ++ msg_pdbg("byteblaster_shutdown\n"); ++ /* De-Assert #EN signal. */ ++ OUTB(0, lpt_iobase + 2 ); ++ return 0; ++} ++ ++static void stk200_preinit(const void *data) { ++ msg_pdbg("stk200_init\n"); ++ /* Assert #EN signals, set LED signal. */ ++ lpt_outbyte = (1 << 6) ; ++ OUTB(lpt_outbyte, lpt_iobase); ++} ++ ++static int stk200_shutdown(void *data) { ++ msg_pdbg("stk200_shutdown\n"); ++ /* Assert #EN signals, clear LED signal. */ ++ lpt_outbyte = (1 << 2) | (1 << 3); ++ OUTB(lpt_outbyte, lpt_iobase); ++ return 0; ++} ++ ++static void dlc5_preinit(const void *data) { ++ msg_pdbg("dlc5_preinit\n"); ++ /* Assert pin 6 to receive MISO. */ ++ lpt_outbyte |= (1<<4); ++ OUTB(lpt_outbyte, lpt_iobase); ++} ++ ++static int dlc5_shutdown(void *data) { ++ msg_pdbg("dlc5_shutdown\n"); ++ /* De-assert pin 6 to force MISO low. */ ++ lpt_outbyte &= ~(1<<4); ++ OUTB(lpt_outbyte, lpt_iobase); ++ return 0; ++} ++ + #else + #error PCI port I/O access is not supported on this architecture yet. + #endif +diff --git a/satamv.c b/satamv.c +index c3f27e7..e03508e 100644 +--- a/satamv.c ++++ b/satamv.c +@@ -57,12 +57,6 @@ static const struct par_programmer par_programmer_satamv = { + .chip_writen = fallback_chip_writen, + }; + +-static int satamv_shutdown(void *data) +-{ +- physunmap(mv_bar, 0x20000); +- return 0; +-} +- + /* + * Random notes: + * FCE# Flash Chip Enable +@@ -94,11 +88,11 @@ int satamv_init(void) + return 1; + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); +- mv_bar = physmap("Marvell 88SX7042 registers", addr, 0x20000); +- if (mv_bar == ERROR_PTR) ++ if (!addr) + return 1; + +- if (register_shutdown(satamv_shutdown, NULL)) ++ mv_bar = rphysmap("Marvell 88SX7042 registers", addr, 0x20000); ++ if (mv_bar == ERROR_PTR) + return 1; + + tmp = pci_mmio_readl(mv_bar + FLASH_PARAM); +@@ -144,12 +138,15 @@ int satamv_init(void) + pci_rmmio_writel(tmp, mv_bar + GPIO_PORT_CONTROL); + + /* Get I/O BAR location. */ +- tmp = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); ++ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); ++ if (!addr) ++ return 1; ++ + /* Truncate to reachable range. + * FIXME: Check if the I/O BAR is actually reachable. + * This is an arch specific check. + */ +- mv_iobar = tmp & 0xffff; ++ mv_iobar = addr & 0xffff; + msg_pspew("Activating I/O BAR at 0x%04x\n", mv_iobar); + + /* 512 kByte with two 8-bit latches, and +diff --git a/satasii.c b/satasii.c +index 72e35e5..83dc62c 100644 +--- a/satasii.c ++++ b/satasii.c +@@ -54,12 +54,6 @@ static const struct par_programmer par_programmer_satasii = { + .chip_writen = fallback_chip_writen, + }; + +-static int satasii_shutdown(void *data) +-{ +- physunmap(sii_bar, SATASII_MEMMAP_SIZE); +- return 0; +-} +- + static uint32_t satasii_wait_done(void) + { + uint32_t ctrl_reg; +@@ -91,21 +85,25 @@ int satasii_init(void) + + if ((id == 0x3132) || (id == 0x3124)) { + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); ++ if (!addr) ++ return 1; + reg_offset = 0x70; + } else { + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_5); ++ if (!addr) ++ return 1; + reg_offset = 0x50; + } + +- sii_bar = physmap("SATA SiI registers", addr, SATASII_MEMMAP_SIZE) + reg_offset; ++ sii_bar = rphysmap("SATA SiI registers", addr, SATASII_MEMMAP_SIZE); ++ if (sii_bar == ERROR_PTR) ++ return 1; ++ sii_bar += reg_offset; + + /* Check if ROM cycle are OK. */ + if ((id != 0x0680) && (!(pci_mmio_readl(sii_bar) & (1 << 26)))) + msg_pwarn("Warning: Flash seems unconnected.\n"); + +- if (register_shutdown(satasii_shutdown, NULL)) +- return 1; +- + register_par_programmer(&par_programmer_satasii, BUS_PARALLEL); + + return 0; +diff --git a/sb600spi.c b/sb600spi.c +index cb7c4ac..9523591 100644 +--- a/sb600spi.c ++++ b/sb600spi.c +@@ -57,7 +57,28 @@ static enum amd_chipset amd_gen = CHIPSET_AMD_UNKNOWN; + static void determine_generation(struct pci_dev *dev) + { + amd_gen = CHIPSET_AMD_UNKNOWN; +- if (dev->device_id == 0x780e) { ++ msg_pdbg2("Trying to determine the generation of the SPI interface... "); ++ if (dev->device_id == 0x438d) { ++ amd_gen = CHIPSET_SB6XX; ++ msg_pdbg("SB6xx detected.\n"); ++ } else if (dev->device_id == 0x439d) { ++ struct pci_dev *smbus_dev = pci_dev_find(0x1002, 0x4385); ++ if (smbus_dev == NULL) ++ return; ++ uint8_t rev = pci_read_byte(smbus_dev, PCI_REVISION_ID); ++ if (rev >= 0x39 && rev <= 0x3D) { ++ amd_gen = CHIPSET_SB7XX; ++ msg_pdbg("SB7xx/SP5100 detected.\n"); ++ } else if (rev >= 0x40 && rev <= 0x42) { ++ amd_gen = CHIPSET_SB89XX; ++ msg_pdbg("SB8xx/SB9xx/Hudson-1 detected.\n"); ++ } else { ++ msg_pwarn("SB device found but SMBus revision 0x%02x does not match known values.\n" ++ "Assuming SB8xx/SB9xx/Hudson-1. Please send a log to flashrom@flashrom.org\n", ++ rev); ++ amd_gen = CHIPSET_SB89XX; ++ } ++ } else if (dev->device_id == 0x780e) { + /* The PCI ID of the LPC bridge doesn't change between Hudson-2/3/4 and Yangtze (Kabini/Temash) + * although they use different SPI interfaces. */ + #ifdef USE_YANGTZE_HEURISTICS +@@ -94,7 +115,11 @@ static void determine_generation(struct pci_dev *dev) + "the output of lspci -nnvx, thanks!.\n", rev); + } + #endif +- } ++ } else ++ msg_pwarn("%s: Unknown LPC device %" PRIx16 ":%" PRIx16 ".\n" ++ "Please report this to flashrom@flashrom.org and include this log and\n" ++ "the output of lspci -nnvx, thanks!\n", ++ __func__, dev->vendor_id, dev->device_id); + } + + static void reset_internal_fifo_pointer(void) +@@ -247,10 +272,66 @@ static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt, + return 0; + } + ++struct spispeed { ++ const char *const name; ++ const uint8_t speed; ++}; ++ ++static const struct spispeed spispeeds[] = { ++ { "66 MHz", 0x00 }, ++ { "33 MHz", 0x01 }, ++ { "22 MHz", 0x02 }, ++ { "16.5 MHz", 0x03 }, ++}; ++ ++static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed) ++{ ++ bool success = false; ++ uint8_t speed = spispeed->speed; ++ ++ msg_pdbg("Setting SPI clock to %s (0x%x).\n", spispeed->name, speed); ++ if (amd_gen != CHIPSET_YANGTZE) { ++ rmmio_writeb((mmio_readb(sb600_spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), sb600_spibar + 0xd); ++ success = (speed == ((mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3)); ++ } ++ ++ if (!success) { ++ msg_perr("Setting SPI clock failed.\n"); ++ return 1; ++ } ++ return 0; ++} ++ ++static int handle_speed(struct pci_dev *dev) ++{ ++ uint32_t tmp; ++ int8_t spispeed_idx = 3; /* Default to 16.5 MHz */ ++ ++ /* See the chipset support matrix for SPI Base_Addr below for an explanation of the symbols used. ++ * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson234 yangtze ++ * 18 rsvd <- fastReadEnable ? <- ? SpiReadMode[0] ++ * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1] ++ */ ++ if (amd_gen != CHIPSET_YANGTZE) { ++ if (amd_gen >= CHIPSET_SB89XX && amd_gen <= CHIPSET_HUDSON234) { ++ bool fast_read = (mmio_readl(sb600_spibar + 0x00) >> 18) & 0x1; ++ msg_pdbg("Fast Reads are %sabled\n", fast_read ? "en" : "dis"); ++ if (fast_read) { ++ msg_pdbg("Disabling them temporarily.\n"); ++ rmmio_writel(mmio_readl(sb600_spibar + 0x00) & ~(0x1 << 18), ++ sb600_spibar + 0x00); ++ } ++ } ++ tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3; ++ msg_pdbg("NormSpeed is %s\n", spispeeds[tmp].name); ++ } ++ return set_speed(dev, &spispeeds[spispeed_idx]); ++} ++ + static int sb600_handle_imc(struct pci_dev *dev, bool amd_imc_force) + { + /* Handle IMC everywhere but sb600 which does not have one. */ +- if (dev->device_id == 0x438d) ++ if (amd_gen == CHIPSET_SB6XX) + return 0; + + /* TODO: we should not only look at IntegratedImcPresent (LPC Dev 20, Func 3, 40h) but also at +@@ -297,9 +378,6 @@ int sb600_probe_spi(struct pci_dev *dev) + uint32_t tmp; + uint8_t reg; + bool amd_imc_force = false; +- static const char *const speed_names[4] = { +- "66/reserved", "33", "22", "16.5" +- }; + + char *arg = extract_programmer_param("amd_imc_force"); + if (arg && !strcmp(arg, "yes")) { +@@ -326,14 +404,20 @@ int sb600_probe_spi(struct pci_dev *dev) + return 0; + + /* Physical memory has to be mapped at page (4k) boundaries. */ +- sb600_spibar = physmap("SB600 SPI registers", tmp & 0xfffff000, +- 0x1000); ++ sb600_spibar = rphysmap("SB600 SPI registers", tmp & 0xfffff000, 0x1000); ++ if (sb600_spibar == ERROR_PTR) ++ return ERROR_FATAL; ++ + /* The low bits of the SPI base address are used as offset into + * the mapped page. + */ + sb600_spibar += tmp & 0xfff; + + determine_generation(dev); ++ if (amd_gen == CHIPSET_AMD_UNKNOWN) { ++ msg_perr("Could not determine chipset generation."); ++ return ERROR_NONFATAL; ++ } + + if (amd_gen == CHIPSET_YANGTZE) { + msg_perr("SPI on Kabini/Temash and newer chipsets are not yet supported.\n" +@@ -341,34 +425,87 @@ int sb600_probe_spi(struct pci_dev *dev) + return ERROR_NONFATAL; + } + +- tmp = pci_read_long(dev, 0xa0); +- msg_pdbg("AltSpiCSEnable=%i, SpiRomEnable=%i, " +- "AbortEnable=%i\n", tmp & 0x1, (tmp & 0x2) >> 1, +- (tmp & 0x4) >> 2); +- tmp = (pci_read_byte(dev, 0xba) & 0x4) >> 2; +- msg_pdbg("PrefetchEnSPIFromIMC=%i, ", tmp); +- +- tmp = pci_read_byte(dev, 0xbb); +- /* FIXME: Set bit 3,6,7 if not already set. +- * Set bit 5, otherwise SPI accesses are pointless in LPC mode. +- * See doc 42413 AMD SB700/710/750 RPR. ++ /* How to read the following table and similar ones in this file: ++ * "?" means we have no datasheet for this chipset generation or it doesn't have any relevant info. ++ * "<-" means the bit/register meaning is identical to the next non-"?" chipset to the left. "<-" thus ++ * never refers to another "?". ++ * If a "?" chipset is between two chipsets with identical meaning, we assume the meaning didn't change ++ * twice in between, i.e. the meaning is unchanged for the "?" chipset. Usually we assume that ++ * succeeding hardware supports the same functionality as its predecessor unless proven different by ++ * tests or documentation, hence "?" will often be implemented equally to "<-". ++ * ++ * Chipset support matrix for SPI Base_Addr (LPC PCI reg 0xa0) ++ * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze ++ * 3 rsvd <- <- ? <- ? RouteTpm2Spi ++ * 2 rsvd AbortEnable rsvd ? <- ? <- ++ * 1 rsvd SpiRomEnable <- ? <- ? <- ++ * 0 rsvd AltSpiCSEnable rsvd ? <- ? <- + */ +- msg_pdbg("PrefetchEnSPIFromHost=%i, SpiOpEnInLpcMode=%i\n", +- tmp & 0x1, (tmp & 0x20) >> 5); +- tmp = mmio_readl(sb600_spibar); +- /* FIXME: If SpiAccessMacRomEn or SpiHostAccessRomEn are zero on +- * SB700 or later, reads and writes will be corrupted. Abort in this +- * case. Make sure to avoid this check on SB600. ++ if (amd_gen >= CHIPSET_SB7XX) { ++ tmp = pci_read_long(dev, 0xa0); ++ msg_pdbg("SpiRomEnable=%i", (tmp >> 1) & 0x1); ++ if (amd_gen == CHIPSET_SB7XX) ++ msg_pdbg(", AltSpiCSEnable=%i, AbortEnable=%i", tmp & 0x1, (tmp >> 2) & 0x1); ++ ++ tmp = pci_read_byte(dev, 0xba); ++ msg_pdbg(", PrefetchEnSPIFromIMC=%i", (tmp & 0x4) >> 2); ++ ++ tmp = pci_read_byte(dev, 0xbb); ++ /* FIXME: Set bit 3,6,7 if not already set. ++ * Set bit 5, otherwise SPI accesses are pointless in LPC mode. ++ * See doc 42413 AMD SB700/710/750 RPR. ++ */ ++ if (amd_gen == CHIPSET_SB7XX) ++ msg_pdbg(", SpiOpEnInLpcMode=%i", (tmp >> 5) & 0x1); ++ msg_pdbg(", PrefetchEnSPIFromHost=%i\n", tmp & 0x1); ++ } ++ ++ /* Chipset support matrix for SPI_Cntrl0 (spibar + 0x0) ++ * See the chipset support matrix for SPI Base_Addr above for an explanation of the symbols used. ++ * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze ++ * 17 rsvd <- <- ? <- ? <- ++ * 18 rsvd <- fastReadEnable<1> ? <- ? SpiReadMode[0]<1> ++ * 19 SpiArbEnable <- <- ? <- ? <- ++ * 20 (FifoPtrClr) <- <- ? <- ? <- ++ * 21 (FifoPtrInc) <- <- ? <- ? IllegalAccess ++ * 22 SpiAccessMacRomEn <- <- ? <- ? <- ++ * 23 SpiHostAccessRomEn <- <- ? <- ? <- ++ * 24:26 ArbWaitCount <- <- ? <- ? <- ++ * 27 SpiBridgeDisable <- <- ? <- ? rsvd ++ * 28 rsvd DropOneClkOnRd = SPIClkGate ? <- ? <- ++ * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]<1> ++ * 31 rsvd <- SpiBusy ? <- ? <- ++ * ++ * <1> see handle_speed + */ +- msg_pdbg("(0x%08" PRIx32 ") fastReadEnable=%u, SpiArbEnable=%i, SpiAccessMacRomEn=%i, " +- "SpiHostAccessRomEn=%i, ArbWaitCount=%i, " +- "SpiBridgeDisable=%i, DropOneClkOnRd=%i\n", +- tmp, (tmp >> 18) & 0x1, +- (tmp >> 19) & 0x1, (tmp >> 22) & 0x1, +- (tmp >> 23) & 0x1, (tmp >> 24) & 0x7, +- (tmp >> 27) & 0x1, (tmp >> 28) & 0x1); +- tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3; +- msg_pdbg("NormSpeed is %s MHz\n", speed_names[tmp]); ++ tmp = mmio_readl(sb600_spibar + 0x00); ++ msg_pdbg("(0x%08" PRIx32 ") SpiArbEnable=%i", tmp, (tmp >> 19) & 0x1); ++ ++ msg_pdbg(", SpiAccessMacRomEn=%i, SpiHostAccessRomEn=%i, ArbWaitCount=%i", ++ (tmp >> 22) & 0x1, (tmp >> 23) & 0x1, (tmp >> 24) & 0x7); ++ ++ if (amd_gen != CHIPSET_YANGTZE) ++ msg_pdbg(", SpiBridgeDisable=%i", (tmp >> 27) & 0x1); ++ ++ switch (amd_gen) { ++ case CHIPSET_SB7XX: ++ msg_pdbg(", DropOneClkOnRd/SpiClkGate=%i", (tmp >> 28) & 0x1); ++ case CHIPSET_SB89XX: ++ case CHIPSET_HUDSON234: ++ msg_pdbg(", SpiBusy=%i", (tmp >> 31) & 0x1); ++ default: break; ++ } ++ msg_pdbg("\n"); ++ ++ if (((tmp >> 22) & 0x1) == 0 || ((tmp >> 23) & 0x1) == 0) { ++ msg_perr("ERROR: State of SpiAccessMacRomEn or SpiHostAccessRomEn prohibits full access.\n"); ++ return ERROR_NONFATAL; ++ } ++ ++ if (amd_gen >= CHIPSET_SB89XX) { ++ tmp = mmio_readb(sb600_spibar + 0x1D); ++ msg_pdbg("Using SPI_CS%d\n", tmp & 0x3); ++ } + + /* Look for the SMBus device. */ + smbus_dev = pci_dev_find(0x1002, 0x4385); +@@ -409,6 +546,9 @@ int sb600_probe_spi(struct pci_dev *dev) + return 0; + } + ++ if (handle_speed(dev) != 0) ++ return ERROR_FATAL; ++ + if (sb600_handle_imc(dev, amd_imc_force) != 0) + return ERROR_FATAL; + +diff --git a/serial.c b/serial.c +index 1b394cd..126079a 100644 +--- a/serial.c ++++ b/serial.c +@@ -41,12 +41,6 @@ + + fdtype sp_fd = SER_INV_FD; + +-void __attribute__((noreturn)) sp_die(char *msg) +-{ +- perror(msg); +- exit(1); +-} +- + #ifdef _WIN32 + struct baudentry { + DWORD flag; +@@ -158,35 +152,18 @@ static void msg_perr_strerror(const char *msg) + #endif + } + +-fdtype sp_openserport(char *dev, unsigned int baud) ++int serialport_config(fdtype fd, unsigned int baud) + { +-#ifdef _WIN32 +- HANDLE fd; +- char *dev2 = dev; +- if ((strlen(dev) > 3) && +- (tolower((unsigned char)dev[0]) == 'c') && +- (tolower((unsigned char)dev[1]) == 'o') && +- (tolower((unsigned char)dev[2]) == 'm')) { +- dev2 = malloc(strlen(dev) + 5); +- if (!dev2) { +- msg_perr_strerror("Out of memory: "); +- return SER_INV_FD; +- } +- strcpy(dev2, "\\\\.\\"); +- strcpy(dev2 + 4, dev); +- } +- fd = CreateFile(dev2, GENERIC_READ | GENERIC_WRITE, 0, NULL, +- OPEN_EXISTING, 0, NULL); +- if (dev2 != dev) +- free(dev2); +- if (fd == INVALID_HANDLE_VALUE) { +- msg_perr_strerror("Cannot open serial port: "); +- return SER_INV_FD; ++ if (fd == SER_INV_FD) { ++ msg_perr("%s: File descriptor is invalid.\n", __func__); ++ return 1; + } ++ ++#ifdef _WIN32 + DCB dcb; + if (!GetCommState(fd, &dcb)) { + msg_perr_strerror("Could not fetch original serial port configuration: "); +- goto out_close; ++ return 1; + } + const struct baudentry *entry = round_baud(baud); + dcb.BaudRate = entry->flag; +@@ -195,35 +172,25 @@ fdtype sp_openserport(char *dev, unsigned int baud) + dcb.StopBits = ONESTOPBIT; + if (!SetCommState(fd, &dcb)) { + msg_perr_strerror("Could not change serial port configuration: "); +- goto out_close; ++ return 1; + } + if (!GetCommState(fd, &dcb)) { + msg_perr_strerror("Could not fetch new serial port configuration: "); +- goto out_close; ++ return 1; + } + msg_pdbg("Baud rate is %ld.\n", dcb.BaudRate); +- return fd; +-out_close: +- CloseHandle(sp_fd); +- return SER_INV_FD; + #else + struct termios wanted, observed; +- int fd; +- fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); +- if (fd < 0) { +- msg_perr_strerror("Cannot open serial port: "); +- return SER_INV_FD; +- } + fcntl(fd, F_SETFL, 0); + if (tcgetattr(fd, &observed) != 0) { + msg_perr_strerror("Could not fetch original serial port configuration: "); +- goto out_close; ++ return 1; + } + wanted = observed; + const struct baudentry *entry = round_baud(baud); + if (cfsetispeed(&wanted, entry->flag) != 0 || cfsetospeed(&wanted, entry->flag) != 0) { + msg_perr_strerror("Could not set serial baud rate: "); +- goto out_close; ++ return 1; + } + wanted.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); + wanted.c_cflag |= (CS8 | CLOCAL | CREAD); +@@ -232,11 +199,11 @@ out_close: + wanted.c_oflag &= ~OPOST; + if (tcsetattr(fd, TCSANOW, &wanted) != 0) { + msg_perr_strerror("Could not change serial port configuration: "); +- goto out_close; ++ return 1; + } + if (tcgetattr(fd, &observed) != 0) { + msg_perr_strerror("Could not fetch new serial port configuration: "); +- goto out_close; ++ return 1; + } + if (observed.c_cflag != wanted.c_cflag || + observed.c_lflag != wanted.c_lflag || +@@ -244,14 +211,54 @@ out_close: + observed.c_oflag != wanted.c_oflag || + cfgetispeed(&observed) != cfgetispeed(&wanted)) { + msg_perr("%s: Some requested options did not stick.\n", __func__); +- goto out_close; ++ return 1; + } +- msg_pdbg("Baud rate is %d.\n", entry->baud); +- return fd; ++ msg_pdbg("Baud rate is %d now.\n", entry->baud); ++#endif ++ return 0; ++} + +-out_close: +- close(sp_fd); +- return SER_INV_FD; ++fdtype sp_openserport(char *dev, unsigned int baud) ++{ ++ fdtype fd; ++#ifdef _WIN32 ++ char *dev2 = dev; ++ if ((strlen(dev) > 3) && ++ (tolower((unsigned char)dev[0]) == 'c') && ++ (tolower((unsigned char)dev[1]) == 'o') && ++ (tolower((unsigned char)dev[2]) == 'm')) { ++ dev2 = malloc(strlen(dev) + 5); ++ if (!dev2) { ++ msg_perr_strerror("Out of memory: "); ++ return SER_INV_FD; ++ } ++ strcpy(dev2, "\\\\.\\"); ++ strcpy(dev2 + 4, dev); ++ } ++ fd = CreateFile(dev2, GENERIC_READ | GENERIC_WRITE, 0, NULL, ++ OPEN_EXISTING, 0, NULL); ++ if (dev2 != dev) ++ free(dev2); ++ if (fd == INVALID_HANDLE_VALUE) { ++ msg_perr_strerror("Cannot open serial port: "); ++ return SER_INV_FD; ++ } ++ if (serialport_config(fd, baud) != 0) { ++ CloseHandle(fd); ++ return SER_INV_FD; ++ } ++ return fd; ++#else ++ fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); ++ if (fd < 0) { ++ msg_perr_strerror("Cannot open serial port: "); ++ return SER_INV_FD; ++ } ++ if (serialport_config(fd, baud) != 0) { ++ close(fd); ++ return SER_INV_FD; ++ } ++ return fd; + #endif + } + +@@ -350,7 +357,7 @@ int serialport_write(unsigned char *buf, unsigned int writecnt) + if (!tmp) { + msg_pdbg2("Empty write\n"); + empty_writes--; +- programmer_delay(500); ++ internal_delay(500); + if (empty_writes == 0) { + msg_perr("Serial port is unresponsive!\n"); + return 1; +diff --git a/serprog.c b/serprog.c +index 3476315..35c4f32 100644 +--- a/serprog.c ++++ b/serprog.c +@@ -100,6 +100,7 @@ static int sp_opensocket(char *ip, unsigned int port) + if (NULL == hostPtr) { + hostPtr = gethostbyaddr(ip, strlen(ip), AF_INET); + if (NULL == hostPtr) { ++ close(sock); + msg_perr("Error: cannot resolve %s\n", ip); + return -1; + } +@@ -114,7 +115,11 @@ static int sp_opensocket(char *ip, unsigned int port) + } + /* We are latency limited, and sometimes do write-write-read * + * (write-n) - so enable TCP_NODELAY. */ +- setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)); ++ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int))) { ++ close(sock); ++ msg_perr("Error: serprog cannot set socket options: %s\n", strerror(errno)); ++ return -1; ++ } + return sock; + } + #endif +@@ -237,43 +242,58 @@ static int sp_docommand(uint8_t command, uint32_t parmlen, + return 0; + } + +-static void sp_flush_stream(void) ++static int sp_flush_stream(void) + { + if (sp_streamed_transmit_ops) + do { + unsigned char c; + if (serialport_read(&c, 1) != 0) { +- sp_die("Error: cannot read from device (flushing stream)"); ++ msg_perr("Error: cannot read from device (flushing stream)"); ++ return 1; + } + if (c == S_NAK) { + msg_perr("Error: NAK to a stream buffer operation\n"); +- exit(1); ++ return 1; + } + if (c != S_ACK) { + msg_perr("Error: Invalid reply 0x%02X from device\n", c); +- exit(1); ++ return 1; + } + } while (--sp_streamed_transmit_ops); + sp_streamed_transmit_ops = 0; + sp_streamed_transmit_bytes = 0; ++ return 0; + } + +-static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t * parms) ++static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t *parms) + { + uint8_t *sp; + if (sp_automatic_cmdcheck(cmd)) + return 1; ++ + sp = malloc(1 + parmlen); +- if (!sp) sp_die("Error: cannot malloc command buffer"); ++ if (!sp) { ++ msg_perr("Error: cannot malloc command buffer\n"); ++ return 1; ++ } + sp[0] = cmd; + memcpy(&(sp[1]), parms, parmlen); +- if (sp_streamed_transmit_bytes >= (1 + parmlen + sp_device_serbuf_size)) +- sp_flush_stream(); +- if (serialport_write(sp, 1 + parmlen) != 0) +- sp_die("Error: cannot write command"); +- free(sp); ++ ++ if (sp_streamed_transmit_bytes >= (1 + parmlen + sp_device_serbuf_size)) { ++ if (sp_flush_stream() != 0) { ++ free(sp); ++ return 1; ++ } ++ } ++ if (serialport_write(sp, 1 + parmlen) != 0) { ++ msg_perr("Error: cannot write command\n"); ++ free(sp); ++ return 1; ++ } + sp_streamed_transmit_ops += 1; + sp_streamed_transmit_bytes += 1 + parmlen; ++ ++ free(sp); + return 0; + } + +@@ -656,16 +676,16 @@ int serprog_init(void) + return 0; + } + +-/* Move an in flashrom buffer existing write-n operation to * +- * the on-device operation buffer. */ +-static void sp_pass_writen(void) ++/* Move an in flashrom buffer existing write-n operation to the on-device operation buffer. */ ++static int sp_pass_writen(void) + { + unsigned char header[7]; +- msg_pspew(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n", +- sp_write_n_bytes, sp_write_n_addr); +- if (sp_streamed_transmit_bytes >= +- (7 + sp_write_n_bytes + sp_device_serbuf_size)) +- sp_flush_stream(); ++ msg_pspew(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n", sp_write_n_bytes, sp_write_n_addr); ++ if (sp_streamed_transmit_bytes >= (7 + sp_write_n_bytes + sp_device_serbuf_size)) { ++ if (sp_flush_stream() != 0) { ++ return 1; ++ } ++ } + /* In case it's just a single byte send it as a single write. */ + if (sp_write_n_bytes == 1) { + sp_write_n_bytes = 0; +@@ -673,9 +693,10 @@ static void sp_pass_writen(void) + header[1] = (sp_write_n_addr >> 8) & 0xFF; + header[2] = (sp_write_n_addr >> 16) & 0xFF; + header[3] = sp_write_n_buf[0]; +- sp_stream_buffer_op(S_CMD_O_WRITEB, 4, header); ++ if (sp_stream_buffer_op(S_CMD_O_WRITEB, 4, header) != 0) ++ return 1; + sp_opbuf_usage += 5; +- return; ++ return 0; + } + header[0] = S_CMD_O_WRITEN; + header[1] = (sp_write_n_bytes >> 0) & 0xFF; +@@ -684,39 +705,55 @@ static void sp_pass_writen(void) + header[4] = (sp_write_n_addr >> 0) & 0xFF; + header[5] = (sp_write_n_addr >> 8) & 0xFF; + header[6] = (sp_write_n_addr >> 16) & 0xFF; +- if (serialport_write(header, 7) != 0) +- sp_die("Error: cannot write write-n command\n"); +- if (serialport_write(sp_write_n_buf, sp_write_n_bytes) != 0) +- sp_die("Error: cannot write write-n data"); ++ if (serialport_write(header, 7) != 0) { ++ msg_perr(MSGHEADER "Error: cannot write write-n command\n"); ++ return 1; ++ } ++ if (serialport_write(sp_write_n_buf, sp_write_n_bytes) != 0) { ++ msg_perr(MSGHEADER "Error: cannot write write-n data"); ++ return 1; ++ } + sp_streamed_transmit_bytes += 7 + sp_write_n_bytes; + sp_streamed_transmit_ops += 1; + sp_opbuf_usage += 7 + sp_write_n_bytes; + sp_write_n_bytes = 0; + sp_prev_was_write = 0; ++ return 0; + } + +-static void sp_execute_opbuf_noflush(void) ++static int sp_execute_opbuf_noflush(void) + { +- if ((sp_max_write_n) && (sp_write_n_bytes)) +- sp_pass_writen(); +- sp_stream_buffer_op(S_CMD_O_EXEC, 0, NULL); +- msg_pspew(MSGHEADER "Executed operation buffer of %d bytes\n", +- sp_opbuf_usage); ++ if ((sp_max_write_n) && (sp_write_n_bytes)) { ++ if (sp_pass_writen() != 0) { ++ msg_perr("Error: could not transfer write buffer\n"); ++ return 1; ++ } ++ } ++ if (sp_stream_buffer_op(S_CMD_O_EXEC, 0, NULL) != 0) { ++ msg_perr("Error: could not execute command buffer\n"); ++ return 1; ++ } ++ msg_pspew(MSGHEADER "Executed operation buffer of %d bytes\n", sp_opbuf_usage); + sp_opbuf_usage = 0; + sp_prev_was_write = 0; +- return; ++ return 0; + } + +-static void sp_execute_opbuf(void) ++static int sp_execute_opbuf(void) + { +- sp_execute_opbuf_noflush(); +- sp_flush_stream(); ++ if (sp_execute_opbuf_noflush() != 0) ++ return 1; ++ if (sp_flush_stream() != 0) ++ return 1; ++ ++ return 0; + } + + static int serprog_shutdown(void *data) + { + if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes)) +- sp_execute_opbuf(); ++ if (sp_execute_opbuf() != 0) ++ msg_pwarn("Could not flush command buffer.\n"); + if (sp_check_commandavail(S_CMD_S_PIN_STATE)) { + uint8_t dis = 0; + if (sp_docommand(S_CMD_S_PIN_STATE, 1, &dis, 0, NULL) == 0) +@@ -731,14 +768,15 @@ static int serprog_shutdown(void *data) + return 0; + } + +-static void sp_check_opbuf_usage(int bytes_to_be_added) ++static int sp_check_opbuf_usage(int bytes_to_be_added) + { + if (sp_device_opbuf_size <= (sp_opbuf_usage + bytes_to_be_added)) { +- sp_execute_opbuf(); +- /* If this happens in the mid of an page load the page load * +- * will probably fail. */ +- msg_pdbg(MSGHEADER "Warning: executed operation buffer due to size reasons\n"); ++ /* If this happens in the middle of a page load the page load will probably fail. */ ++ msg_pwarn(MSGHEADER "Warning: executed operation buffer due to size reasons\n"); ++ if (sp_execute_opbuf() != 0) ++ return 1; + } ++ return 0; + } + + static void serprog_chip_writeb(const struct flashctx *flash, uint8_t val, +@@ -768,7 +806,7 @@ static void serprog_chip_writeb(const struct flashctx *flash, uint8_t val, + writeb_parm[1] = (addr >> 8) & 0xFF; + writeb_parm[2] = (addr >> 16) & 0xFF; + writeb_parm[3] = val; +- sp_stream_buffer_op(S_CMD_O_WRITEB, 4, writeb_parm); ++ sp_stream_buffer_op(S_CMD_O_WRITEB, 4, writeb_parm); // FIXME: return error + sp_opbuf_usage += 5; + } + } +@@ -785,16 +823,16 @@ static uint8_t serprog_chip_readb(const struct flashctx *flash, + buf[0] = ((addr >> 0) & 0xFF); + buf[1] = ((addr >> 8) & 0xFF); + buf[2] = ((addr >> 16) & 0xFF); +- sp_stream_buffer_op(S_CMD_R_BYTE, 3, buf); +- sp_flush_stream(); ++ sp_stream_buffer_op(S_CMD_R_BYTE, 3, buf); // FIXME: return error ++ sp_flush_stream(); // FIXME: return error + if (serialport_read(&c, 1) != 0) +- sp_die("readb byteread"); ++ msg_perr(MSGHEADER "readb byteread"); // FIXME: return error + msg_pspew("%s addr=0x%" PRIxPTR " returning 0x%02X\n", __func__, addr, c); + return c; + } + + /* Local version that really does the job, doesn't care of max_read_n. */ +-static void sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len) ++static int sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len) + { + unsigned char sbuf[6]; + msg_pspew("%s: addr=0x%" PRIxPTR " len=%zu\n", __func__, addr, len); +@@ -808,10 +846,13 @@ static void sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len) + sbuf[4] = ((len >> 8) & 0xFF); + sbuf[5] = ((len >> 16) & 0xFF); + sp_stream_buffer_op(S_CMD_R_NBYTES, 6, sbuf); +- sp_flush_stream(); +- if (serialport_read(buf, len) != 0) +- sp_die("Error: cannot read read-n data"); +- return; ++ if (sp_flush_stream() != 0) ++ return 1; ++ if (serialport_read(buf, len) != 0) { ++ msg_perr(MSGHEADER "Error: cannot read read-n data"); ++ return 1; ++ } ++ return 0; + } + + /* The externally called version that makes sure that max_read_n is obeyed. */ +@@ -821,12 +862,12 @@ static void serprog_chip_readn(const struct flashctx *flash, uint8_t * buf, + size_t lenm = len; + chipaddr addrm = addr; + while ((sp_max_read_n != 0) && (lenm > sp_max_read_n)) { +- sp_do_read_n(&(buf[addrm-addr]), addrm, sp_max_read_n); ++ sp_do_read_n(&(buf[addrm-addr]), addrm, sp_max_read_n); // FIXME: return error + addrm += sp_max_read_n; + lenm -= sp_max_read_n; + } + if (lenm) +- sp_do_read_n(&(buf[addrm-addr]), addrm, lenm); ++ sp_do_read_n(&(buf[addrm-addr]), addrm, lenm); // FIXME: return error + } + + void serprog_delay(int usecs) +@@ -858,11 +899,18 @@ static int serprog_spi_send_command(struct flashctx *flash, + unsigned char *parmbuf; + int ret; + msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt); +- if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes)) +- sp_execute_opbuf(); ++ if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes)) { ++ if (sp_execute_opbuf() != 0) { ++ msg_perr("Error: could not execute command buffer before sending SPI commands.\n"); ++ return 1; ++ } ++ } ++ + parmbuf = malloc(writecnt + 6); +- if (!parmbuf) +- sp_die("Error: cannot malloc SPI send param buffer"); ++ if (!parmbuf) { ++ msg_perr("Error: could not allocate SPI send param buffer.\n"); ++ return 1; ++ } + parmbuf[0] = (writecnt >> 0) & 0xFF; + parmbuf[1] = (writecnt >> 8) & 0xFF; + parmbuf[2] = (writecnt >> 16) & 0xFF; +@@ -879,8 +927,7 @@ static int serprog_spi_send_command(struct flashctx *flash, + /* FIXME: This function is optimized so that it does not split each transaction + * into chip page_size long blocks unnecessarily like spi_read_chunked. This has + * the advantage that it is much faster for most chips, but breaks those with +- * non-contiguous address space (like AT45DB161D). When spi_read_chunked is +- * fixed this method can be removed. */ ++ * non-continuous reads. When spi_read_chunked is fixed this method can be removed. */ + static int serprog_spi_read(struct flashctx *flash, uint8_t *buf, + unsigned int start, unsigned int len) + { +diff --git a/spi25.c b/spi25.c +index e001196..862a865 100644 +--- a/spi25.c ++++ b/spi25.c +@@ -679,7 +679,7 @@ int spi_block_erase_20(struct flashctx *flash, unsigned int addr, + + result = spi_send_multicommand(flash, cmds); + if (result) { +- msg_cerr("%s failed during command execution at address 0x%x\n", ++ msg_pdbg("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } +@@ -1128,13 +1128,9 @@ int default_spi_write_aai(struct flashctx *flash, uint8_t *buf, unsigned int sta + + + result = spi_send_multicommand(flash, cmds); +- if (result) { +- msg_cerr("%s failed during start command execution\n", +- __func__); +- /* FIXME: Should we send WRDI here as well to make sure the chip +- * is not in AAI mode? +- */ +- return result; ++ if (result != 0) { ++ msg_cerr("%s failed during start command execution: %d\n", __func__, result); ++ goto bailout; + } + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(10); +@@ -1146,16 +1142,21 @@ int default_spi_write_aai(struct flashctx *flash, uint8_t *buf, unsigned int sta + while (pos < start + len - 1) { + cmd[1] = buf[pos++ - start]; + cmd[2] = buf[pos++ - start]; +- spi_send_command(flash, JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE, 0, +- cmd, NULL); ++ result = spi_send_command(flash, JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE, 0, cmd, NULL); ++ if (result != 0) { ++ msg_cerr("%s failed during followup AAI command execution: %d\n", __func__, result); ++ goto bailout; ++ } + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(10); + } + +- /* Use WRDI to exit AAI mode. This needs to be done before issuing any +- * other non-AAI command. +- */ +- spi_write_disable(flash); ++ /* Use WRDI to exit AAI mode. This needs to be done before issuing any other non-AAI command. */ ++ result = spi_write_disable(flash); ++ if (result != 0) { ++ msg_cerr("%s failed to disable AAI mode.\n", __func__); ++ return SPI_GENERIC_ERROR; ++ } + + /* Write remaining byte (if any). */ + if (pos < start + len) { +@@ -1165,4 +1166,10 @@ int default_spi_write_aai(struct flashctx *flash, uint8_t *buf, unsigned int sta + } + + return 0; ++ ++bailout: ++ result = spi_write_disable(flash); ++ if (result != 0) ++ msg_cerr("%s failed to disable AAI mode.\n", __func__); ++ return SPI_GENERIC_ERROR; + } +diff --git a/spi25_statusreg.c b/spi25_statusreg.c +index 8fb7f2d..48fceb0 100644 +--- a/spi25_statusreg.c ++++ b/spi25_statusreg.c +@@ -270,7 +270,7 @@ static void spi_prettyprint_status_register_bp(uint8_t status, int bp) + } + + /* Unnamed bits. */ +-static void spi_prettyprint_status_register_bit(uint8_t status, int bit) ++void spi_prettyprint_status_register_bit(uint8_t status, int bit) + { + msg_cdbg("Chip status register: Bit %i is %sset\n", bit, (status & (1 << bit)) ? "" : "not "); + } +diff --git a/stm50.c b/stm50.c +new file mode 100644 +index 0000000..edcfdd2 +--- /dev/null ++++ b/stm50.c +@@ -0,0 +1,115 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2008 Claus Gindhart ++ * Copyright (C) 2009 Sean Nelson ++ * Copyright (C) 2013 Stefan Tauner ++ * ++ * 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 ++ */ ++ ++/* ++ * All ST M50 chips are locked on startup. Most of them have a uniform 64 kB block layout, but some have ++ * a non-uniform block/sector segmentation which has to be handled with more care. Some of the non-uniform ++ * chips support erasing of the 4 kB sectors with another command. ++ */ ++ ++#include "flash.h" ++#include "flashchips.h" ++#include "chipdrivers.h" ++ ++static int stm50_unlock_address(struct flashctx *flash, int offset) ++{ ++ chipaddr wrprotect = flash->virtual_registers + 2; ++ static const uint8_t unlock_sector = 0x00; ++ msg_cdbg("unlocking at 0x%x\n", offset); ++ chip_writeb(flash, unlock_sector, wrprotect + offset); ++ if (chip_readb(flash, wrprotect + offset) != unlock_sector) { ++ msg_cerr("Cannot unlock address 0x%x\n", offset); ++ return -1; ++ } ++ return 0; ++} ++ ++/* Chips known to use a non-uniform block and sector layout for locking (as well as for erasing): ++ * Name Size Address range of lock registers ++ * M50FLW080A 1MB FFB00002 - FFBFF002 ++ * M50FLW080B 1MB FFB00002 - FFBFF002 ++ * M50FW002 256k FFBC0002 - FFBFC002 ++ * M50LPW116 2MB FFA00002 - FFBFC002 ++ */ ++int unlock_stm50_nonuniform(struct flashctx *flash) ++{ ++ int i; ++ struct eraseblock *eraseblocks = flash->chip->block_erasers[0].eraseblocks; ++ unsigned int done = 0; ++ for (i = 0; i < NUM_ERASEREGIONS && eraseblocks[i].count != 0; i++) { ++ unsigned int block_size = eraseblocks[i].size; ++ unsigned int block_count = eraseblocks[i].count; ++ ++ int j; ++ for (j = 0; j < block_count; j++) { ++ if (stm50_unlock_address(flash, done)) { ++ msg_cerr("UNLOCK FAILED!\n"); ++ return -1; ++ } ++ done += block_count * block_size; ++ } ++ } ++ return 0; ++} ++ ++/* Unlocking for uniform 64 kB blocks starting at offset 2 of the feature registers. */ ++int unlock_stm50_uniform(struct flashctx *flash) ++{ ++ int i; ++ for (i = 0; i < flash->chip->total_size * 1024; i+= 64 * 1024) { ++ if (stm50_unlock_address(flash, i)) { ++ msg_cerr("UNLOCK FAILED!\n"); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int stm50_erase_sector(struct flashctx *flash, unsigned int addr) ++{ ++ chipaddr bios = flash->virtual_memory + addr; ++ ++ // clear status register ++ chip_writeb(flash, 0x50, bios); ++ // now start it ++ chip_writeb(flash, 0x32, bios); ++ chip_writeb(flash, 0xd0, bios); ++ programmer_delay(10); ++ ++ uint8_t status = wait_82802ab(flash); ++ print_status_82802ab(status); ++ ++ return status == 0x80; ++} ++ ++/* Some ST M50* chips do support erasing of sectors. This function will derive the erase function to use from ++ * the length of the of the block. For calls that apparently do not address a sector (but a block) we just call ++ * the block erase function instead. FIXME: This duplicates the behavior of the remaining erasers for blocks and ++ * might be fixed when flashrom supports multiple functions per eraser or erasers that do erase parts of the ++ * chip only. */ ++int erase_sector_stm50(struct flashctx *flash, unsigned int addr, unsigned int len) ++{ ++ if (len == 4096) ++ return stm50_erase_sector(flash, addr); ++ else ++ return erase_block_82802ab(flash, addr, len); ++} +diff --git a/udelay.c b/udelay.c +index e3cf3e3..9d3bfc2 100644 +--- a/udelay.c ++++ b/udelay.c +@@ -90,7 +90,11 @@ void myusec_calibrate_delay(void) + unsigned long timeusec, resolution; + int i, tries = 0; + ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("Calibrating delay loop... "); ++#else + msg_pinfo("Calibrating delay loop... "); ++#endif + resolution = measure_os_delay_resolution(); + if (resolution) { + msg_pdbg("OS timer resolution is %lu usecs, ", resolution); +@@ -151,7 +155,7 @@ recalibrate: + } + } + } else { +- msg_perr("delay loop is unreliable, trying to continue "); ++ msg_pdbg("delay loop is unreliable, trying to continue "); + } + + /* We're interested in the actual precision. */ +@@ -166,7 +170,11 @@ recalibrate: + timeusec = measure_delay(resolution * 4); + msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec); + ++#ifdef FORCE10_SPI_CHANGE ++ msg_pdbg("OK.\n"); ++#else + msg_pinfo("OK.\n"); ++#endif + } + + /* Not very precise sleep. */ +diff --git a/util/getrevision.sh b/util/getrevision.sh +new file mode 100755 +index 0000000..709e45f +--- /dev/null ++++ b/util/getrevision.sh +@@ -0,0 +1,311 @@ ++#!/bin/sh ++# ++# This file is part of the flashrom project. ++# ++# Copyright (C) 2005 coresystems GmbH ++# Copyright (C) 2009,2010 Carl-Daniel Hailfinger ++# Copyright (C) 2010 Chromium OS Authors ++# Copyright (C) 2013 Stefan Tauner ++# ++# 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 ++# ++ ++EXIT_SUCCESS=0 ++EXIT_FAILURE=1 ++ ++# Make sure we don't get translated output ++export LC_ALL=C ++# nor local times or dates ++export TZ=UTC0 ++ ++# Helper functions ++# First argument is the path to inspect (usually optional; w/o it the whole repository will be considered) ++svn_has_local_changes() { ++ svn status "$1" | egrep '^ *[ADMR] *' >/dev/null ++} ++ ++git_has_local_changes() { ++ git update-index -q --refresh >/dev/null ++ ! git diff-index --quiet HEAD -- "$1" ++} ++ ++git_last_commit() { ++ git log --pretty=format:"%h" -1 -- "$1" ++} ++ ++svn_is_file_tracked() { ++ svn info "$1" >/dev/null 2>&1 ++} ++ ++git_is_file_tracked() { ++ git ls-files --error-unmatch -- "$1" >/dev/null 2>&1 ++} ++ ++is_file_tracked() { ++ svn_is_file_tracked "$1" || git_is_file_tracked "$1" ++} ++ ++# Tries to find a remote source for the changes committed locally. ++# This includes the URL of the remote repository including the last commit and a suitable branch name. ++# Takes one optional argument: the path to inspect ++git_url() { ++ last_commit=$(git_last_commit "$1") ++ # get all remote branches containing the last commit (excluding origin/HEAD and git-svn branches/tags) ++ branches=$(git branch -r --contains $last_commit | sed '/\//!d;/.*->.*/d;s/[\t ]*//') ++ if [ -z "$branches" ] ; then ++ echo "No remote branch contains a suitable commit">&2 ++ return ++ fi ++ ++ # find "nearest" branch ++ local mindiff=9000 ++ local target= ++ for branch in $branches ; do ++ curdiff=$(git rev-list --count $last_commit..$branch) ++ if [ $curdiff -ge $mindiff ] ; then ++ continue ++ fi ++ mindiff=$curdiff ++ target=$branch ++ done ++ ++ echo "$(git ls-remote --exit-code --get-url ${target%/*}) ${target#*/}" ++} ++ ++# Returns a string indicating where others can get the current source code (excluding uncommitted changes) ++# Takes one optional argument: the path to inspect ++scm_url() { ++ local url= ++ ++ # for a primitive VCS like subversion finding the URL is easy: there is only one upstream host ++ if svn_is_file_tracked "$1" ; then ++ url="$(svn info "$1" 2>/dev/null | ++ grep URL: | ++ sed 's/.*URL:[[:blank:]]*//;s/:\/\/.*@/:\/\//' | ++ grep ^.)" ++ elif git_is_file_tracked "$1" ; then ++ url="$(git_url "$1")" ++ else ++ return ${EXIT_FAILURE} ++ fi ++ ++ echo "${url}" ++} ++ ++# Retrieve timestamp since last modification. If the sources are pristine, ++# then the timestamp will match that of the SCM's most recent modification ++# date. ++timestamp() { ++ local t ++ ++ # date syntaxes are manifold: ++ # gnu date [-d input]... [+FORMAT] ++ # netbsd date [-ajnu] [-d date] [-r seconds] [+format] [[[[[[CC]yy]mm]dd]HH]MM[.SS]] ++ # freebsd date [-jnu] [-d dst] [-r seconds] [-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format] [...] ++ # dragonflybsd date [-jnu] [-d dst] [-r seconds] [-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format] [...] ++ # openbsd date [-aju] [-d dst] [-r seconds] [+format] [[[[[[cc]yy]mm]dd]HH]MM[.SS]] [...] ++ if svn_is_file_tracked "$2" ; then ++ if svn_has_local_changes "$2"; then ++ t=$(date -u "$1") ++ else ++ # No local changes, get date of the last log record. Subversion provides that in ++ # ISO 8601 format when using the --xml switch. The sed call extracts that ignoring any ++ # fractional parts started by a comma or a dot. ++ local last_commit_date="$(svn info --xml "$2"| \ ++ sed -n -e 's/\([^,\.]*\)\([\.,].*\)*Z<\/date>/\1Z/p')" ++ ++ case $(uname) in ++ # Most BSD dates do not support parsing date values from user input with -d but all of ++ # them support parsing the syntax with [[[[[[cc]yy]mm]dd]HH]MM[.ss]]. We have to ++ # transform the ISO8601 date first though. ++ NetBSD|OpenBSD|DragonFly|FreeBSD) ++ last_commit_date="$(echo ${last_commit_date} | \ ++ sed -n -e 's/\(....\)-\(..\)-\(..\)T\(..\):\(..\):\(..\)Z/\1\2\3\4\5\.\6/p')" ++ t=$(date -u -j "${last_commit_date}" "$1" 2>/dev/null);; ++ *) ++ t=$(date -u -d "${last_commit_date}" "$1" 2>/dev/null);; ++ esac ++ fi ++ elif git_is_file_tracked "$2" ; then ++ # are there local changes? ++ if git_has_local_changes "$2" ; then ++ t=$(date -u "${1}") ++ else ++ # No local changes, get date of the last commit ++ case $(uname) in ++ # Most BSD dates do not support parsing date values from user input with -d but all of ++ # them support parsing epoch seconds with -r. Thanks to git we can easily use that: ++ NetBSD|OpenBSD|DragonFly|FreeBSD) ++ t=$(date -u -r "$(git log --pretty=format:%ct -1 -- $2)" "$1" 2>/dev/null);; ++ *) ++ t=$(date -d "$(git log --pretty=format:%cD -1 -- $2)" -u "$1" 2>/dev/null);; ++ esac ++ fi ++ else ++ t=$(date -u "$1") ++ fi ++ ++ if [ -z "$t" ]; then ++ echo "Warning: Could not determine timestamp." 2>/dev/null ++ fi ++ echo "${t}" ++} ++ ++# Retrieve local SCM revision info. This is useful if we're working in a different SCM than upstream and/or ++# have local changes. ++local_revision() { ++ local r= ++ ++ if svn_is_file_tracked "$1" ; then ++ r=$(svn_has_local_changes "$1" && echo "dirty") ++ elif git_is_file_tracked "$1" ; then ++ r=$(git_last_commit "$1") ++ ++ local svn_base=$(git log --grep git-svn-id -1 --format='%h') ++ if [ "$svn_base" != "" ] ; then ++ local diff_to_svn=$(git rev-list --count ${svn_base}..${r}) ++ if [ "$diff_to_svn" -gt 0 ] ; then ++ r="$r-$diff_to_svn" ++ fi ++ fi ++ ++ if git_has_local_changes "$1" ; then ++ r="$r-dirty" ++ fi ++ else ++ return ${EXIT_FAILURE} ++ fi ++ ++ echo "${r}" ++} ++ ++# Get the upstream flashrom revision stored in SVN metadata. ++upstream_revision() { ++ local r= ++ ++ if svn_is_file_tracked "$1" ; then ++ r=$(svn info "$1" 2>/dev/null | \ ++ grep "Last Changed Rev:" | \ ++ sed -e "s/^Last Changed Rev: *//" -e "s/\([0-9]*\).*/r\1/" | \ ++ grep "r[0-9]") ++ elif git_is_file_tracked "$1" ; then ++ # If this is a "native" git-svn clone we could use git svn log: ++ # git svn log --oneline -1 | sed 's/^r//;s/[[:blank:]].*//' or even git svn find-rev ++ # but it is easier to just grep for the git-svn-id unconditionally ++ r=$(git log --grep git-svn-id -1 -- "$1" | \ ++ grep git-svn-id | \ ++ sed 's/.*@/r/;s/[[:blank:]].*//') ++ fi ++ ++ if [ -z "$r" ]; then ++ r="unknown" # default to unknown ++ fi ++ echo "${r}" ++} ++ ++show_help() { ++ echo "Usage: ++ ${0} [path] ++ ++Commands ++ -h or --help ++ this message ++ -l or --local ++ local revision information including an indicator for uncommitted changes ++ -u or --upstream ++ upstream revision ++ -U or --url ++ URL associated with the latest commit ++ -d or --date ++ date of most recent modification ++ -t or --timestamp ++ timestamp of most recent modification ++" ++ return ++} ++ ++check_action() { ++ if [ -n "$action" ]; then ++ echo "Error: Multiple actions given.">&2 ++ exit ${EXIT_FAILURE} ++ fi ++} ++ ++main() { ++ local query_path= ++ local action= ++ ++ # The is the main loop ++ while [ $# -gt 0 ]; ++ do ++ case ${1} in ++ -h|--help) ++ action=show_help; ++ shift;; ++ -l|--local) ++ check_action $1 ++ action=local_revision ++ shift;; ++ -u|--upstream) ++ check_action $1 ++ action=upstream_revision ++ shift;; ++ -U|--url) ++ check_action $1 ++ action=scm_url ++ shift;; ++ -d|--date) ++ check_action $1 ++ action="timestamp +%Y-%m-%d" # refrain from suffixing 'Z' to indicate it's UTC ++ shift;; ++ -t|--timestamp) ++ check_action $1 ++ action="timestamp +%Y-%m-%dT%H:%M:%SZ" # There is only one valid time format! ISO 8601 ++ shift;; ++ -*) ++ show_help; ++ echo "Error: Invalid option: ${1}" ++ exit ${EXIT_FAILURE};; ++ *) ++ if [ -z "$query_path" ] ; then ++ if [ ! -e "$1" ] ; then ++ echo "Error: Path \"${1}\" does not exist.">&2 ++ exit ${EXIT_FAILURE} ++ fi ++ query_path=$1 ++ else ++ echo "Warning: Ignoring over-abundant paramter: \"${1}\"">&2 ++ fi ++ shift;; ++ esac; ++ done ++ ++ # default to current directory (usually equals the whole repository) ++ if [ -z "$query_path" ] ; then ++ query_path=. ++ fi ++ if ! is_file_tracked "$query_path" ; then ++ echo "Warning: Path \"${query_path}\" is not under version control.">&2 ++ fi ++ if [ -z "$action" ] ; then ++ show_help ++ echo "Error: No actions specified" ++ exit ${EXIT_FAILURE} ++ fi ++ ++ $action "$query_path" ++} ++ ++main $@ +diff --git a/util/ich_descriptors_tool/ich_descriptors_tool.c b/util/ich_descriptors_tool/ich_descriptors_tool.c +index c359913..dd35860 100644 +--- a/util/ich_descriptors_tool/ich_descriptors_tool.c ++++ b/util/ich_descriptors_tool/ich_descriptors_tool.c +@@ -77,12 +77,13 @@ static void dump_file(const char *prefix, const uint32_t *dump, unsigned int len + printf("Dumping %u bytes of the %s region from 0x%08x-0x%08x to %s... ", + file_len, region_names[i], base, limit, fn); + int fh = open(fn, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); +- free(fn); + if (fh < 0) { + fprintf(stderr, + "ERROR: couldn't open(%s): %s\n", fn, strerror(errno)); ++ free(fn); + exit(1); + } ++ free(fn); + + ret = write(fh, &dump[base >> 2], file_len); + if (ret != file_len) { +@@ -120,6 +121,7 @@ static void usage(char *argv[], char *error) + "\t- \"5\" or \"ibex\" for Intel's 5 series chipsets,\n" + "\t- \"6\" or \"cougar\" for Intel's 6 series chipsets,\n" + "\t- \"7\" or \"panther\" for Intel's 7 series chipsets.\n" ++"\t- \"avoton\",\n" + "If '-d' is specified some regions such as the BIOS image as seen by the CPU or\n" + "the GbE blob that is required to initialize the GbE are also dumped to files.\n", + argv[0], argv[0]); +@@ -197,6 +199,10 @@ int main(int argc, char *argv[]) + else if ((strcmp(csn, "7") == 0) || + (strcmp(csn, "panther") == 0)) + cs = CHIPSET_7_SERIES_PANTHER_POINT; ++#ifdef DELL_AVOTON_SUPPORT ++ else if (strcmp(csn, "avoton") == 0) ++ cs = CHIPSET_AVOTON; ++#endif + } + + ret = read_ich_descriptors_from_dump(buf, len, &desc); +diff --git a/util/z60_flashrom.rules b/util/z60_flashrom.rules +index 8456a04..948563c 100644 +--- a/util/z60_flashrom.rules ++++ b/util/z60_flashrom.rules +@@ -80,4 +80,8 @@ ATTRS{idVendor}=="15ba", ATTRS{idProduct}=="002a", MODE="664", GROUP="plugdev" + # http://www.diygadget.com/tiao-usb-multi-protocol-adapter-jtag-spi-i2c-serial.html + ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8a98", MODE="664", GROUP="plugdev" + ++# TIAO/DIYGADGET USB Multi-Protocol Adapter (TUMPA) Lite ++# http://www.tiaowiki.com/w/TIAO_USB_Multi_Protocol_Adapter_Lite_User's_Manual ++ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8a99", MODE="664", GROUP="plugdev" ++ + LABEL="flashrom_rules_end" +-- +2.7.4 + diff --git a/platform/broadcom/sonic-platform-modules-dell/tools/flashrom.sh b/platform/broadcom/sonic-platform-modules-dell/tools/flashrom.sh new file mode 100755 index 000000000000..c3af65fd6d84 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/tools/flashrom.sh @@ -0,0 +1,10 @@ +#!/bin/bash +export DELL_TOOLS_DIR="platform/broadcom/sonic-platform-modules-dell/tools" + +cd $DELL_TOOLS_DIR +rm -rf $DELL_TOOLS_DIR/flashrom +git clone https://github.com/flashrom/flashrom.git +cd flashrom +git checkout tags/0.9.7 +git apply ../0002-Flashrom-support-for-Intel-Rangeley-and-Denverton-CP.patch +make