[coreboot-gerrit] New patch to review for coreboot: util/iotools: Add iotools v1.5

Martin Roth (martinroth@google.com) gerrit at coreboot.org
Wed Oct 12 22:41:03 CEST 2016


Martin Roth (martinroth at google.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17010

-gerrit

commit 5b5d2e6a93462ac791c002f6d35ec10e5e9b870d
Author: Martin Roth <martinroth at google.com>
Date:   Wed Oct 12 12:05:44 2016 -0600

    util/iotools: Add iotools v1.5
    
    This is Google's iotools package.  It used to be hosted on
    code.google.com, but since that shut down, all that's available there
    is the archive packages.
    
    This is the final release that's hosted there.  Any changes which need
    to be made will come in follow-on patches.
    
    From the archived iotools site:
    The iotools package provides a set of simple command line tools which
    allow access to hardware device registers. Supported register interfaces
    include PCI, IO, memory mapped IO, SMBus, CPUID, and MSR. Also included
    are some utilities which allow for simple arithmetic, logical, and other
    operations.
    
    If you ever have to debug hardware, you could probably use these tools.
    
    Change-Id: Ia154f43a885c511adc82bc5bb2087bf43d2d7523
    Signed-off-by: Martin Roth <martinroth at google.com>
---
 util/iotools/COPYING         | 339 +++++++++++++++++++++++++++++++++++
 util/iotools/Makefile        |  65 +++++++
 util/iotools/README          |  22 +++
 util/iotools/TODO.txt        |   6 +
 util/iotools/cmos_rw.c       | 121 +++++++++++++
 util/iotools/commands.c      | 245 +++++++++++++++++++++++++
 util/iotools/commands.h      | 157 ++++++++++++++++
 util/iotools/io_rw.c         | 124 +++++++++++++
 util/iotools/iotools.c       |  82 +++++++++
 util/iotools/linux-i2c-dev.h | 363 +++++++++++++++++++++++++++++++++++++
 util/iotools/logic.c         | 161 +++++++++++++++++
 util/iotools/misc.c          | 275 ++++++++++++++++++++++++++++
 util/iotools/mmio_rw.c       | 325 ++++++++++++++++++++++++++++++++++
 util/iotools/msr.c           | 125 +++++++++++++
 util/iotools/pci_rw.c        | 297 +++++++++++++++++++++++++++++++
 util/iotools/platform.h      |  50 ++++++
 util/iotools/smbus_rw.c      | 413 +++++++++++++++++++++++++++++++++++++++++++
 17 files changed, 3170 insertions(+)

diff --git a/util/iotools/COPYING b/util/iotools/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/util/iotools/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/util/iotools/Makefile b/util/iotools/Makefile
new file mode 100644
index 0000000..9295d15
--- /dev/null
+++ b/util/iotools/Makefile
@@ -0,0 +1,65 @@
+# Copyright 2008 Google Inc.
+#
+# 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+VER_MAJOR = 1
+VER_MINOR = 5
+
+CC=$(CROSS_COMPILE)gcc
+
+# build options
+DEBUG ?= 1
+STATIC ?= 1
+
+ifneq ($(strip $(STATIC)),0)
+IOTOOLS_STATIC = -static
+endif
+
+ifneq ($(strip $(DEBUG)),0)
+IOTOOLS_DEBUG = -O0 -ggdb
+else
+IOTOOLS_DEBUG = -O2 -DNDEBUG
+endif
+
+CFLAGS = -Wall -Werror $(DEFS) $(ARCHFLAGS) $(EXTRA_CFLAGS) \
+         $(IOTOOLS_STATIC) $(IOTOOLS_DEBUG)
+DEFS = -D_GNU_SOURCE -DVER_MAJOR=$(VER_MAJOR) -DVER_MINOR=$(VER_MINOR)
+SBINDIR ?= /usr/local/sbin
+
+BINARY=iotools
+OBJS_TO_BUILD=$(filter-out iotools.o, $(patsubst %.c,%.o,$(wildcard *.c)))
+OBJS=$(OBJS_TO_BUILD)
+
+all: $(BINARY)
+
+$(BINARY): $(OBJS) iotools.o Makefile
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ iotools.o $(OBJS)
+
+install: $(BINARY)
+	cp -a $^ $(SBINDIR)
+	$(SBINDIR)/$(BINARY) --make-links
+
+RUSER ?= root
+RHOST ?=
+rinstall: $(BINARY)
+	@if [ -n "$(RHOST)" ]; then \
+		scp $^ $(RUSER)@$(RHOST):$(SBINDIR); \
+		ssh $(RUSER)@$(RHOST) $(SBINDIR)/$(BINARY) --make-links; \
+	else \
+		echo $@: no RHOST defined; \
+	fi
+
+clean:
+	$(RM) *.o $(BINARY)
diff --git a/util/iotools/README b/util/iotools/README
new file mode 100644
index 0000000..94a6464
--- /dev/null
+++ b/util/iotools/README
@@ -0,0 +1,22 @@
+Building
+
+iotools currently does not use an autoconf script or anything similar to build.
+iotools has only been tested to run and build on Linux. Additionally, it is
+assumed that gcc is being used to build. In order to build iotools, one just
+has to type 'make.'
+
+There are a few options that can be used during the build.
+
+DEBUG - If set to 1, debugging symbols will be generated in the binaries. The
+build currently turns this option on if the DEBUG variable has not been
+assigned.
+
+STATIC - If set to 1, the binary is linked statically. The build currently
+turns this option on if the STATIC variable has not been assigned.
+
+CROSS_COMPILE - This variable can be set to a path of another gcc. The build
+will use the compiler pointed to by CROSS_COMPILE.
+
+There is a 'rinstall' build target that will scp the iotools binary to a remote
+system defined by the RHOST variable. RUSER can be defined to login with RUSER
+to perform the copy. If RUSER is not defined, RUSER defaults to 'root.'
diff --git a/util/iotools/TODO.txt b/util/iotools/TODO.txt
new file mode 100644
index 0000000..f22050a
--- /dev/null
+++ b/util/iotools/TODO.txt
@@ -0,0 +1,6 @@
+Does smbus_readblock work?   Does it need a size argument?
+
+busy_loop should (optionally) provide some stats - running average,
+difference from last count, running average of differences
+
+Runon should be made safe for > 64 CPUs
diff --git a/util/iotools/cmos_rw.c b/util/iotools/cmos_rw.c
new file mode 100644
index 0000000..d03f80e
--- /dev/null
+++ b/util/iotools/cmos_rw.c
@@ -0,0 +1,121 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/*
+ * Quick CMOS access, requires linux nvram driver
+ * Tim Hockin <thockin at google.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include "commands.h"
+
+#define DEVICE		"/dev/nvram"
+#define NVRAM_OFFSET	14  /* From the kernel driver. bytes < 14 are RTC */
+
+static int
+cmos_rd(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long index;
+	uint8_t data;
+	int fd;
+
+	index = strtoul(argv[1], NULL, 0);
+	if (index < NVRAM_OFFSET) {
+		fprintf(stderr, "can't read bytes below %d\n", NVRAM_OFFSET);
+		return -1;
+	}
+	index -= NVRAM_OFFSET;
+
+	fd = open(DEVICE, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "open(\"%s\"): %s\n", DEVICE, strerror(errno));
+		return -1;
+	}
+
+	if (lseek(fd, index, SEEK_SET) < 0) {
+		fprintf(stderr, "lseek(%lu): %s\n", index, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	if (read(fd, &data, sizeof(data)) != sizeof(data)) {
+		fprintf(stderr, "read(): %s\n", strerror(errno));
+		close(fd);
+		return -1;
+	}
+	close(fd);
+
+	printf("0x%02x\n", data);
+
+	return 0;
+}
+
+static int
+cmos_wr(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long index;
+	unsigned long ldata;
+	uint8_t data;
+	int fd;
+
+	index = strtoul(argv[1], NULL, 0);
+	if (index < NVRAM_OFFSET) {
+		fprintf(stderr, "can't read bytes below %d\n", NVRAM_OFFSET);
+		return -1;
+	}
+	index -= NVRAM_OFFSET;
+	ldata = strtoul(argv[2], NULL, 0);
+	data = (typeof(data))ldata;
+
+	fd = open(DEVICE, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr, "open(\"%s\"): %s\n", DEVICE, strerror(errno));
+		return -1;
+	}
+
+	if (lseek(fd, index, SEEK_SET) < 0) {
+		fprintf(stderr, "lseek(%lu): %s\n", index, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	if (write(fd, &data, sizeof(data)) != sizeof(data)) {
+		fprintf(stderr, "write(): %s\n", strerror(errno));
+		close(fd);
+		return -1;
+	}
+	close(fd);
+
+	return 0;
+}
+
+MAKE_PREREQ_PARAMS_FIXED_ARGS(cmos_rd_params, 2, "<index>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(cmos_wr_params, 3, "<index> <data>", 0);
+
+static const struct cmd_info cmos_cmds[] = {
+	MAKE_CMD_WITH_PARAMS(cmos_read, cmos_rd, NULL, &cmos_rd_params),
+	MAKE_CMD_WITH_PARAMS(cmos_write, cmos_wr, NULL, &cmos_wr_params),
+};
+
+MAKE_CMD_GROUP(CMOS, "commands to access the CMOS registers", cmos_cmds);
+REGISTER_CMD_GROUP(CMOS);
diff --git a/util/iotools/commands.c b/util/iotools/commands.c
new file mode 100644
index 0000000..a28e6da
--- /dev/null
+++ b/util/iotools/commands.c
@@ -0,0 +1,245 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "commands.h"
+#include "platform.h"
+#ifdef ARCH_X86
+#include <sys/io.h>
+#endif /* #ifdef ARCH_X86 */
+
+/* Provide shared parameters the describe the size of the operation for 
+ * subcommand implementations. */
+
+#define MAKE_SIZE_PARAM(size_) \
+	const struct size_param size ##size_ = { \
+		.size = SIZE ##size_, \
+	}
+
+MAKE_SIZE_PARAM(8);
+MAKE_SIZE_PARAM(16);
+MAKE_SIZE_PARAM(32);
+MAKE_SIZE_PARAM(64);
+
+static struct cmd_group *group_head;
+
+static const struct cmd_info *
+locate_command(const char *cmd)
+{
+	int i;
+	const struct cmd_group *group;
+
+	for (group = group_head; group; group = group->next) {
+		for (i=0; i < group->num_commands; i++) {
+			if (!strcmp(cmd, group->commands[i].name))
+				return &group->commands[i];
+		}
+	}
+
+	/* Command not found. */
+	return NULL;
+}
+
+static int
+check_prereqs(int argc, const char *argv[], const struct prereq_params *params)
+{
+	/* If there are no prerequisites the check has succeeded. */
+	if (params == NULL) {
+		return 0;
+	}
+
+	if (argc < params->min_args || argc > params->max_args) {
+		fprintf(stderr, "usage: %s %s\n", argv[0], params->usage);
+		return -1;
+	}
+
+#ifdef ARCH_X86
+	/* If iopl_needed is non-zero attempt to change to iopl. */
+	if (params->iopl_needed != 0 && iopl(params->iopl_needed) < 0) {
+		fprintf(stderr, "can't set io privilege level\n");
+		return -1;
+	}
+#endif /* #ifdef ARCH_X86 */
+
+	return 0;
+}
+
+static int
+_run_command(int argc, const char *argv[], const struct cmd_info *cmd_info)
+{
+	if (check_prereqs(argc, argv, cmd_info->params) < 0) {
+		return -1;
+	}
+
+	return cmd_info->entry(argc, argv, cmd_info);
+}
+
+int
+run_command(int argc, const char *argv[])
+{
+	const struct cmd_info *cmd_info;
+	const char *cmd_name;
+
+	/* First check if the 1st parameter is a command that exists.
+	 * i.e. iotools io_read8 0x70 */
+	if (argc > 1) {
+		cmd_info = locate_command(argv[1]);
+		/* If command is found, execute it directly. */
+		if (cmd_info != NULL) {
+			return _run_command(--argc, ++argv, cmd_info);
+		}
+	}
+
+	/* Assume iotools was executed through a symlink, but strip off the any
+	 * leading path  to obtain the desired subcommand. */
+	cmd_name = strrchr(argv[0], '/');
+	cmd_name = (cmd_name == NULL) ? argv[0] : cmd_name+1;
+	cmd_info = locate_command(cmd_name);
+
+	if (cmd_info != NULL) {
+		return _run_command(argc, argv, cmd_info);
+	}
+
+	/* Subcommand was not found. Perform the fallback operation. */
+	return iotools_fallback(argc, argv);
+}
+
+static int
+locate_path_of_binary(char **path_to_bin, char **bin_name)
+{
+	char *lbin_name, *lpath_to_bin;
+	static char bin_fullpath[FILENAME_MAX];
+
+	/* Find the location of the currently executing binary. */
+	if (readlink("/proc/self/exe", bin_fullpath, FILENAME_MAX) == -1) {
+		fprintf(stderr, "Unable to locate currently running binary.\n");
+		return -1;
+	}
+
+	lbin_name = strrchr(bin_fullpath, '/');
+	*lbin_name++ = '\0';
+	lpath_to_bin = &bin_fullpath[0];
+
+	*bin_name = lbin_name;
+	*path_to_bin = lpath_to_bin;
+
+	return 0;
+}
+
+static const char *
+build_symlink_name(const char *path_to_bin, const struct cmd_info *cmd)
+{
+	static char link_name[FILENAME_MAX];
+
+	snprintf(link_name, FILENAME_MAX, "%s/%s", path_to_bin, cmd->name);
+
+	return link_name;
+}
+
+int
+make_command_links(void)
+{
+	char *bin_name, *path_to_bin;
+	int i;
+	const struct cmd_group *group;
+
+	if (locate_path_of_binary(&path_to_bin, &bin_name) < 0) {
+		return -1;
+	}
+
+	for (group = group_head; group; group = group->next) {
+		for (i=0; i < group->num_commands; i++) {
+			const char *link_name;
+
+			link_name = build_symlink_name(path_to_bin,
+			                               &group->commands[i]);
+			fprintf(stdout, "Creating link: %s -> %s\n",
+				link_name, bin_name);
+			if (symlink(bin_name, link_name) != 0) {
+				fprintf(stderr,
+				        "Unable to create link: %s -> %s\n",
+				        link_name, bin_name);
+			}
+		}
+	}
+
+	return 0;
+}
+
+int
+clean_command_links(void)
+{
+	char *bin_name, *path_to_bin;
+	int i;
+	const struct cmd_group *group;
+
+	if (locate_path_of_binary(&path_to_bin, &bin_name) < 0) {
+		return -1;
+	}
+
+	for (group = group_head; group; group = group->next) {
+		for (i=0; i < group->num_commands; i++) {
+			int r;
+			const char *link_name;
+
+			link_name = build_symlink_name(path_to_bin,
+			                               &group->commands[i]);
+
+			r = unlink(link_name);
+			if (r < 0 && errno != ENOENT) {
+				fprintf(stderr,
+				        "Unable to remove link: %s: %s\n",
+				        link_name, strerror(errno));
+			}
+		}
+	}
+
+	return 0;
+}
+
+int
+list_commands(void)
+{
+	int i;
+	const struct cmd_group *group;
+
+	for (group = group_head; group; group = group->next) {
+		fprintf(stdout, "%s%s%s\n", group->name,
+		        group->description ? ": " : "",
+		        group->description ? : "");
+		for (i=0; i < group->num_commands; i++) {
+			fprintf(stdout, "  %s\n", group->commands[i].name);
+		}
+	}
+
+	return 0;
+}
+
+int
+register_command_group(struct cmd_group *group)
+{
+	/* insert group at the beginning of the list. */
+	group->next = group_head;
+	group_head = group;
+
+	return 0;
+}
diff --git a/util/iotools/commands.h b/util/iotools/commands.h
new file mode 100644
index 0000000..05b4a0a
--- /dev/null
+++ b/util/iotools/commands.h
@@ -0,0 +1,157 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef _COMMANDS_H_
+#define _COMMANDS_H_
+
+#include <stdint.h>
+
+/* This is a shared data type to handle different type sizes for subcommands. */
+typedef union {
+	uint8_t u8;
+	uint16_t u16;
+	uint32_t u32;
+	uint64_t u64;
+} data_store;
+
+
+/* Provide shared parameters the describe the size of the operation for
+ * subcommand implementations. */
+enum IO_SIZE
+{
+	SIZE8 = 8,
+	SIZE16 = 16,
+	SIZE32 = 32,
+	SIZE64 = 64,
+};
+
+struct size_param {
+	int size;
+};
+
+#define EXTERN_SIZE_PARAM(_size) \
+	extern const struct size_param size ##_size
+
+EXTERN_SIZE_PARAM(8);
+EXTERN_SIZE_PARAM(16);
+EXTERN_SIZE_PARAM(32);
+EXTERN_SIZE_PARAM(64);
+
+/* The min and max args need to include the argv[0] parameter. For example,
+ * 'shl 1 3' would list the min and max arguments as 3. */
+struct prereq_params {
+	int min_args;      /* Miniumum number of arguments required. */
+	int max_args;      /* Maxiumum number of arguments required. */
+	const char *usage; /* Usage string - requires 1st param %s for arv[0] */
+	int iopl_needed;   /* non-zero if iopl needed. */
+};
+
+#define _PREREQ_PARAMS_VAR_ARGS(min_args_, max_args_, usage_, iopl_) \
+	{ \
+		.min_args = min_args_, \
+		.max_args = max_args_, \
+		.usage = usage_, \
+		.iopl_needed = iopl_, \
+	}
+
+#define MAKE_PREREQ_PARAMS_FIXED_ARGS(name_, nargs_, usage_, iopl_) \
+	static const struct prereq_params name_ = \
+		_PREREQ_PARAMS_VAR_ARGS(nargs_, nargs_, usage_, iopl_)
+
+#define MAKE_PREREQ_PARAMS_VAR_ARGS(name_, min_args_, max_args_, usage_, iopl_)\
+	static const struct prereq_params name_ = \
+		_PREREQ_PARAMS_VAR_ARGS(min_args_, max_args_, usage_, iopl_)
+
+struct cmd_group
+{
+	const char *name;
+	const char *description;
+	const struct cmd_info *commands;
+	const int num_commands;
+	/* Pointer to next group of commands. It is used internally to keep
+	 * track of command groups. Command group implemenations should not set
+	 * this field. */
+	struct cmd_group *next;
+};
+
+#define MAKE_CMD_GROUP(grp_name_, description_, cmd_array_) \
+	static struct cmd_group grp_name_## _group = { \
+		.name = #grp_name_, \
+		.description = description_, \
+		.commands = cmd_array_, \
+		.num_commands = arraysize(cmd_array_), \
+		.next = NULL, \
+	}
+
+#define REGISTER_CMD_GROUP(grp_name_) \
+	static void register_ ##grp_name_(void) __attribute__ ((constructor)); \
+	static void register_ ##grp_name_(void) \
+	{ \
+		register_command_group(&grp_name_## _group); \
+	}
+
+struct cmd_info
+{
+	const char *name;
+	int (*entry)(int argc, const char *argv[], const struct cmd_info *info);
+	const void *privdata;
+	const struct prereq_params *params;
+	const struct size_param *sizedata;
+};
+
+#define _MAKE_CMD(name_, entry_point_, priv_data_, params_, size_data_) \
+	{ \
+		.name = #name_, \
+		.entry = entry_point_, \
+		.privdata = priv_data_, \
+		.params = params_, \
+		.sizedata = size_data_, \
+	}
+
+#define MAKE_CMD(name_, entry_point_, priv_data_) \
+	_MAKE_CMD(name_, entry_point_, priv_data_, NULL, NULL)
+#define MAKE_CMD_WITH_SIZE(name_, entry_point_, size_data_) \
+	_MAKE_CMD(name_, entry_point_, NULL, NULL, size_data_)
+
+#define MAKE_CMD_WITH_PARAMS(name_, entry_point_, priv_data_, params_) \
+	_MAKE_CMD(name_, entry_point_, priv_data_, params_, NULL)
+#define MAKE_CMD_WITH_PARAMS_SIZE(name_, entry_point_, priv_data_, params_, size_data_) \
+	_MAKE_CMD(name_, entry_point_, priv_data_, params_, size_data_)
+
+
+int run_command(int argc, const char *argv[]);
+int make_command_links(void);
+int clean_command_links(void);
+int list_commands(void);
+int iotools_fallback(int argc, const char *argv[]);
+int register_command_group(struct cmd_group *group);
+
+#define arraysize(array_) \
+	(sizeof((array_))/sizeof((array_)[0]))
+
+/* Helper function to obtain size information embedded in private data of
+ * cmd_info structure. */
+static inline int
+get_command_size(const struct cmd_info *info)
+{
+	if (info->sizedata == NULL)
+		return -1;
+	return info->sizedata->size;
+}
+
+#endif /* _COMMANDS_H_ */
diff --git a/util/iotools/io_rw.c b/util/iotools/io_rw.c
new file mode 100644
index 0000000..52aaec1
--- /dev/null
+++ b/util/iotools/io_rw.c
@@ -0,0 +1,124 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "commands.h"
+#include "platform.h"
+
+#ifdef ARCH_X86
+#include <sys/io.h>
+
+static int
+io_read_x(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long iobase;
+	data_store data;
+	int ret;
+
+	ret = 0;
+	iobase = strtoul(argv[1], NULL, 0);
+
+	#define in8 inb
+	#define in16 inw
+	#define in32 inl
+	#define DO_READ(addr_, size_) \
+		data.u ##size_ = in ##size_(addr_); \
+		fprintf(stdout, "0x%0*x\n", \
+		        (int)sizeof(data.u ##size_)*2, data.u ##size_)
+
+	switch (get_command_size(info)) {
+	case SIZE8:
+		DO_READ(iobase, 8);
+		break;
+	case SIZE16:
+		DO_READ(iobase, 16);
+		break;
+	case SIZE32:
+		DO_READ(iobase, 32);
+		break;
+	default:
+		fprintf(stderr, "invalid io_read entry\n");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int
+io_write_x(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long iobase;
+	unsigned long ldata;
+	int ret;
+	data_store data;
+
+	ret = 0;
+	iobase = strtoul(argv[1], NULL, 0);
+	ldata = strtoul(argv[2], NULL, 0);
+
+	#define out8 outb
+	#define out16 outw
+	#define out32 outl
+	#define DO_WRITE(addr_, size_) \
+		data.u ##size_ = (typeof(data.u ##size_))ldata; \
+		out ##size_(data.u ##size_, addr_)
+
+	switch (get_command_size(info)) {
+	case SIZE8:
+		DO_WRITE(iobase, 8);
+		break;
+	case SIZE16:
+		DO_WRITE(iobase, 16);
+		break;
+	case SIZE32:
+		DO_WRITE(iobase, 32);
+		break;
+	default:
+		fprintf(stderr, "invalid io_write entry\n");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+MAKE_PREREQ_PARAMS_FIXED_ARGS(rd_params, 2, "<io_addr>", 3);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(wr_params, 3, "<io_addr> <data>", 3);
+
+#define MAKE_IO_READ_CMD(size_) \
+	MAKE_CMD_WITH_PARAMS_SIZE(io_read ##size_, &io_read_x, NULL, \
+	                          &rd_params, &size ##size_)
+#define MAKE_IO_WRITE_CMD(size_) \
+	MAKE_CMD_WITH_PARAMS_SIZE(io_write ##size_, &io_write_x, NULL, \
+	                          &wr_params, &size ##size_)
+#define MAKE_IO_RW_CMD_PAIR(size_) \
+	MAKE_IO_READ_CMD(size_), \
+	MAKE_IO_WRITE_CMD(size_)
+
+static const struct cmd_info io_cmds[] = {
+	MAKE_IO_RW_CMD_PAIR(8),
+	MAKE_IO_RW_CMD_PAIR(16),
+	MAKE_IO_RW_CMD_PAIR(32),
+};
+
+MAKE_CMD_GROUP(IO, "commands to access registers in the IO address space",
+               io_cmds);
+REGISTER_CMD_GROUP(IO);
+
+#endif /* #ifdef ARCH_X86 */
diff --git a/util/iotools/iotools.c b/util/iotools/iotools.c
new file mode 100644
index 0000000..c5df417
--- /dev/null
+++ b/util/iotools/iotools.c
@@ -0,0 +1,82 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "commands.h"
+
+int
+main(int argc, const char *argv[])
+{
+	int rc = run_command(argc, argv);
+	exit((rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void
+usage(const char *bin_name, FILE *fstream)
+{
+	fprintf(fstream, "usage: %s COMMAND\n", bin_name);
+	fprintf(fstream, "  COMMANDS:\n"
+			"    --make-links\n"
+			"    --clean-links\n"
+			"    --list-cmds\n"
+			"    -v --version\n");
+}
+
+static void
+version(const char *progname)
+{
+	printf("%s version %d.%d\n", progname, VER_MAJOR, VER_MINOR);
+}
+
+int
+iotools_fallback(int argc, const char *argv[])
+{
+	if (argc != 2) {
+		usage(argv[0], stderr);
+		return -1;
+	}
+
+	if (strcmp(argv[1], "--help") == 0) {
+		usage(argv[0], stdout);
+		return 0;
+	}
+
+	if (strcmp(argv[1], "--make-links") == 0) {
+		return make_command_links();
+	}
+
+	if (strcmp(argv[1], "--clean-links") == 0) {
+		return clean_command_links();
+	}
+
+	if (strcmp(argv[1], "--list-cmds") == 0) {
+		return list_commands();
+	}
+
+	if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) {
+		version(argv[0]);
+		return 0;
+	}
+
+	fprintf(stderr, "'%s' sub-command not supported by iotools\n", argv[1]);
+	usage(argv[0], stderr);
+	return -1;
+}
diff --git a/util/iotools/linux-i2c-dev.h b/util/iotools/linux-i2c-dev.h
new file mode 100644
index 0000000..5580a2f
--- /dev/null
+++ b/util/iotools/linux-i2c-dev.h
@@ -0,0 +1,363 @@
+/*
+    i2c-dev.h - i2c-bus driver, char device interface
+
+    Copyright (C) 1995-97 Simon G. Vogl
+    Copyright (C) 1998-99 Frodo Looijaard <frodol at dds.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#ifndef LIB_I2CDEV_H
+#define LIB_I2CDEV_H
+
+#include <linux/types.h>
+#include <sys/ioctl.h>
+
+#if defined(__STRICT_ANSI__) || !defined(__GNUC__)
+#define inline
+#endif
+
+/* -- i2c.h -- */
+
+
+/*
+ * I2C Message - used for pure i2c transaction, also from /dev interface
+ */
+struct i2c_msg {
+	__u16 addr;	/* slave address			*/
+	unsigned short flags;		
+#define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
+#define I2C_M_RD	0x01
+#define I2C_M_NOSTART	0x4000
+#define I2C_M_REV_DIR_ADDR	0x2000
+#define I2C_M_IGNORE_NAK	0x1000
+#define I2C_M_NO_RD_ACK		0x0800
+	short len;		/* msg length				*/
+	char *buf;		/* pointer to msg data			*/
+	int err;
+	short done;
+};
+
+/* To determine what functionality is present */
+
+#define I2C_FUNC_I2C			0x00000001
+#define I2C_FUNC_10BIT_ADDR		0x00000002
+#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
+#define I2C_FUNC_SMBUS_HWPEC_CALC	0x00000008 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_READ_WORD_DATA_PEC  0x00000800 /* SMBus 2.0 */ 
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC 0x00001000 /* SMBus 2.0 */ 
+#define I2C_FUNC_SMBUS_PROC_CALL_PEC	0x00002000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC 0x00004000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_QUICK		0x00010000 
+#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000 
+#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000 
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000 
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000 
+#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000 
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000 
+#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000 
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000 
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2	 0x10000000 /* I2C-like block xfer  */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC  0x40000000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC 0x80000000 /* SMBus 2.0 */
+
+#define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \
+                            I2C_FUNC_SMBUS_WRITE_BYTE
+#define I2C_FUNC_SMBUS_BYTE_DATA I2C_FUNC_SMBUS_READ_BYTE_DATA | \
+                                 I2C_FUNC_SMBUS_WRITE_BYTE_DATA
+#define I2C_FUNC_SMBUS_WORD_DATA I2C_FUNC_SMBUS_READ_WORD_DATA | \
+                                 I2C_FUNC_SMBUS_WRITE_WORD_DATA
+#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
+                                  I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
+#define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
+                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
+#define I2C_FUNC_SMBUS_I2C_BLOCK_2 I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 | \
+                                   I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2
+#define I2C_FUNC_SMBUS_BLOCK_DATA_PEC I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC | \
+                                      I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC
+#define I2C_FUNC_SMBUS_WORD_DATA_PEC  I2C_FUNC_SMBUS_READ_WORD_DATA_PEC | \
+                                      I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC
+
+#define I2C_FUNC_SMBUS_READ_BYTE_PEC		I2C_FUNC_SMBUS_READ_BYTE_DATA
+#define I2C_FUNC_SMBUS_WRITE_BYTE_PEC		I2C_FUNC_SMBUS_WRITE_BYTE_DATA
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA_PEC	I2C_FUNC_SMBUS_READ_WORD_DATA
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA_PEC	I2C_FUNC_SMBUS_WRITE_WORD_DATA
+#define I2C_FUNC_SMBUS_BYTE_PEC			I2C_FUNC_SMBUS_BYTE_DATA
+#define I2C_FUNC_SMBUS_BYTE_DATA_PEC		I2C_FUNC_SMBUS_WORD_DATA
+
+#define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \
+                            I2C_FUNC_SMBUS_BYTE | \
+                            I2C_FUNC_SMBUS_BYTE_DATA | \
+                            I2C_FUNC_SMBUS_WORD_DATA | \
+                            I2C_FUNC_SMBUS_PROC_CALL | \
+                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
+                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \
+                            I2C_FUNC_SMBUS_I2C_BLOCK
+
+/* 
+ * Data for SMBus Messages 
+ */
+#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */	
+#define I2C_SMBUS_I2C_BLOCK_MAX	32	/* Not specified but we use same structure */
+union i2c_smbus_data {
+	__u8 byte;
+	__u16 word;
+	__u8 block[I2C_SMBUS_BLOCK_MAX + 3]; /* block[0] is used for length */
+                          /* one more for read length in block process call */
+	                                            /* and one more for PEC */
+};
+
+/* smbus_access read or write markers */
+#define I2C_SMBUS_READ	1
+#define I2C_SMBUS_WRITE	0
+
+/* SMBus transaction types (size parameter in the above functions) 
+   Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
+#define I2C_SMBUS_QUICK		    0
+#define I2C_SMBUS_BYTE		    1
+#define I2C_SMBUS_BYTE_DATA	    2 
+#define I2C_SMBUS_WORD_DATA	    3
+#define I2C_SMBUS_PROC_CALL	    4
+#define I2C_SMBUS_BLOCK_DATA	    5
+#define I2C_SMBUS_I2C_BLOCK_DATA    6
+#define I2C_SMBUS_BLOCK_PROC_CALL   7		/* SMBus 2.0 */
+#define I2C_SMBUS_BLOCK_DATA_PEC    8		/* SMBus 2.0 */
+#define I2C_SMBUS_PROC_CALL_PEC     9		/* SMBus 2.0 */
+#define I2C_SMBUS_BLOCK_PROC_CALL_PEC  10	/* SMBus 2.0 */
+#define I2C_SMBUS_WORD_DATA_PEC	   11		/* SMBus 2.0 */
+
+
+/* ----- commands for the ioctl like i2c_command call:
+ * note that additional calls are defined in the algorithm and hw 
+ *	dependent layers - these can be listed here, or see the 
+ *	corresponding header files.
+ */
+				/* -> bit-adapter specific ioctls	*/
+#define I2C_RETRIES	0x0701	/* number of times a device address      */
+				/* should be polled when not            */
+                                /* acknowledging 			*/
+#define I2C_TIMEOUT	0x0702	/* set timeout - call with int 		*/
+
+
+/* this is for i2c-dev.c	*/
+#define I2C_SLAVE	0x0703	/* Change slave address			*/
+				/* Attn.: Slave address is 7 or 10 bits */
+#define I2C_SLAVE_FORCE	0x0706	/* Change slave address			*/
+				/* Attn.: Slave address is 7 or 10 bits */
+				/* This changes the address, even if it */
+				/* is already taken!			*/
+#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit	*/
+
+#define I2C_FUNCS	0x0705	/* Get the adapter functionality */
+#define I2C_RDWR	0x0707	/* Combined R/W transfer (one stop only)*/
+#define I2C_PEC		0x0708	/* != 0 for SMBus PEC                   */
+#if 0
+#define I2C_ACK_TEST	0x0710	/* See if a slave is at a specific address */
+#endif
+
+#define I2C_SMBUS	0x0720	/* SMBus-level access */
+
+/* -- i2c.h -- */
+
+
+/* Note: 10-bit addresses are NOT supported! */
+
+/* This is the structure as used in the I2C_SMBUS ioctl call */
+struct i2c_smbus_ioctl_data {
+	char read_write;
+	__u8 command;
+	int size;
+	union i2c_smbus_data *data;
+};
+
+/* This is the structure as used in the I2C_RDWR ioctl call */
+struct i2c_rdwr_ioctl_data {
+	struct i2c_msg *msgs;	/* pointers to i2c_msgs */
+	int nmsgs;		/* number of i2c_msgs */
+};
+
+
+static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, 
+                                     int size, union i2c_smbus_data *data)
+{
+	struct i2c_smbus_ioctl_data args;
+
+	args.read_write = read_write;
+	args.command = command;
+	args.size = size;
+	args.data = data;
+	return ioctl(file,I2C_SMBUS,&args);
+}
+
+
+static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
+{
+	return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
+}
+	
+static inline __s32 i2c_smbus_read_byte(int file)
+{
+	union i2c_smbus_data data;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
+		return -1;
+	else
+		return 0x0FF & data.byte;
+}
+
+static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
+{
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
+	                        I2C_SMBUS_BYTE,NULL);
+}
+
+static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
+{
+	union i2c_smbus_data data;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                     I2C_SMBUS_BYTE_DATA,&data))
+		return -1;
+	else
+		return 0x0FF & data.byte;
+}
+
+static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, 
+                                              __u8 value)
+{
+	union i2c_smbus_data data;
+	data.byte = value;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_BYTE_DATA, &data);
+}
+
+static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
+{
+	union i2c_smbus_data data;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                     I2C_SMBUS_WORD_DATA,&data))
+		return -1;
+	else
+		return 0x0FFFF & data.word;
+}
+
+static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, 
+                                              __u16 value)
+{
+	union i2c_smbus_data data;
+	data.word = value;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_WORD_DATA, &data);
+}
+
+static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
+{
+	union i2c_smbus_data data;
+	data.word = value;
+	if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                     I2C_SMBUS_PROC_CALL,&data))
+		return -1;
+	else
+		return 0x0FFFF & data.word;
+}
+
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, 
+                                              __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                     I2C_SMBUS_BLOCK_DATA,&data))
+		return -1;
+	else {
+		for (i = 1; i <= data.block[0]; i++)
+			values[i-1] = data.block[i];
+		return data.block[0];
+	}
+}
+
+static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, 
+                                               __u8 length, __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (length > 32)
+		length = 32;
+	for (i = 1; i <= length; i++)
+		data.block[i] = values[i-1];
+	data.block[0] = length;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_BLOCK_DATA, &data);
+}
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command,
+                                                  __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+	                      I2C_SMBUS_I2C_BLOCK_DATA,&data))
+		return -1;
+	else {
+		for (i = 1; i <= data.block[0]; i++)
+			values[i-1] = data.block[i];
+		return data.block[0];
+	}
+}
+
+static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
+                                               __u8 length, __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (length > 32)
+		length = 32;
+	for (i = 1; i <= length; i++)
+		data.block[i] = values[i-1];
+	data.block[0] = length;
+	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                        I2C_SMBUS_I2C_BLOCK_DATA, &data);
+}
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_block_process_call(int file, __u8 command,
+                                                 __u8 length, __u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (length > 32)
+		length = 32;
+	for (i = 1; i <= length; i++)
+		data.block[i] = values[i-1];
+	data.block[0] = length;
+	if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+	                     I2C_SMBUS_BLOCK_PROC_CALL,&data))
+		return -1;
+	else {
+		for (i = 1; i <= data.block[0]; i++)
+			values[i-1] = data.block[i];
+		return data.block[0];
+	}
+}
+
+
+#endif /* LIB_I2CDEV_H */
diff --git a/util/iotools/logic.c b/util/iotools/logic.c
new file mode 100644
index 0000000..752d2b1
--- /dev/null
+++ b/util/iotools/logic.c
@@ -0,0 +1,161 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "commands.h"
+
+typedef enum {
+	OR_OP,
+	AND_OP,
+	XOR_OP,
+	SHL_OP,
+	SHR_OP,
+	BTS_OP,
+	BTR_OP,
+} LOGIC_OP;
+
+struct logic_info {
+	int op;
+};
+
+static int
+logic_op(int argc, const char *argv[], const struct cmd_info *info)
+{
+	int rc;
+	unsigned long long result;
+	unsigned long long val;
+	struct logic_info *logic_info = (struct logic_info *)info->privdata;
+
+	/* Strip off command name and obtain source value to be operated on. */
+	argc--; argv++;
+	result = strtoull(argv[0], NULL, 0);
+	argc--; argv++;
+	rc = 0;
+	while (argc) {
+		/* Next value to use in operation. */
+		val = strtoull(argv[0], NULL, 0);
+		switch (logic_info->op) {
+		case OR_OP:
+			result |= val;
+			rc = (result == 0);
+			break;
+		case AND_OP:
+			result &= val;
+			rc = (result == 0);
+			break;
+		case XOR_OP:
+			result ^= val;
+			rc = (result == 0);
+			break;
+		case BTS_OP:
+			val = (1 << val);
+			rc = ((result & val) != 0);
+			result |= val;
+			break;
+		case BTR_OP:
+			val = (1 << val);
+			rc = ((result & val) != 0);
+			result &= ~val;
+			break;
+		default:
+			fprintf(stderr, "Invalid logic op\n");
+			return -1;
+		}
+		argc--; argv++;
+	}
+
+	printf("0x%llx\n", result);
+
+	return rc;
+}
+
+static int
+not(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long long result;
+
+	result = strtoull(argv[1], NULL, 0);
+	result = ~result;
+
+	printf("0x%llx\n", result);
+
+	return (result == 0);
+}
+
+static int
+shift(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long long val;
+	unsigned long shift;
+	struct logic_info *logic_info = (struct logic_info *)info->privdata;
+
+	val = strtoull(argv[1], NULL, 0);
+	shift = strtoul(argv[2], NULL, 0);
+
+	switch(logic_info->op) {
+	case SHL_OP:
+		val <<= shift;
+		break;
+	case SHR_OP:
+		val >>= shift;
+		break;
+	default:
+		fprintf(stderr, "Invalid shift operation\n");
+		return -1;
+	}
+	printf("0x%llx\n", val);
+
+	return 0;
+}
+
+MAKE_PREREQ_PARAMS_VAR_ARGS(logic_op_params, 2, INT_MAX,
+                            "<value> <value> ...", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(not_params, 2, "<value>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(shift_params, 3, "<value> <shift>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(bit_params, 3, "<value> <bit>", 0);
+
+#define MAKE_LOGIC_INFO(name_, type_) \
+	static struct logic_info name_## _info = { .op = type_ }
+
+MAKE_LOGIC_INFO(or, OR_OP);
+MAKE_LOGIC_INFO(and, AND_OP);
+MAKE_LOGIC_INFO(xor, XOR_OP);
+MAKE_LOGIC_INFO(shl, SHL_OP);
+MAKE_LOGIC_INFO(shr, SHR_OP);
+MAKE_LOGIC_INFO(btr, BTR_OP);
+MAKE_LOGIC_INFO(bts, BTS_OP);
+
+#define MAKE_LOGIC_CMD(name_, entry_, params_) \
+	MAKE_CMD_WITH_PARAMS(name_, entry_, &name_## _info, params_)
+
+static const struct cmd_info logic_cmds[] = {
+	MAKE_LOGIC_CMD(or, &logic_op, &logic_op_params),
+	MAKE_LOGIC_CMD(and, &logic_op, &logic_op_params),
+	MAKE_LOGIC_CMD(xor, &logic_op, &logic_op_params),
+	MAKE_LOGIC_CMD(shl, &shift, &shift_params),
+	MAKE_LOGIC_CMD(shr, &shift, &shift_params),
+	MAKE_CMD_WITH_PARAMS(not, &not, NULL, &not_params),
+	MAKE_LOGIC_CMD(btr, &logic_op, &bit_params),
+	MAKE_LOGIC_CMD(bts, &logic_op, &bit_params),
+};
+
+MAKE_CMD_GROUP(LOGIC, "commands to perform boolean algebra operations",
+               logic_cmds);
+REGISTER_CMD_GROUP(LOGIC);
diff --git a/util/iotools/misc.c b/util/iotools/misc.c
new file mode 100644
index 0000000..6bf22bb
--- /dev/null
+++ b/util/iotools/misc.c
@@ -0,0 +1,275 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#define _FILE_OFFSET_BITS 64
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sched.h>
+#include "commands.h"
+#include "platform.h"
+
+/*
+ * There is a chance that we don't have cpu_set_t available to us, like
+ * in the case where we are building against klibc.  In these cases,
+ * dummy up the glibc interfaces.
+ */
+#ifndef __CPU_SETSIZE
+#define __CPU_SETSIZE	1024
+#define __NCPUBITS	(8 * sizeof (__cpu_mask))
+typedef unsigned long int __cpu_mask;
+
+/* Data structure to describe CPU mask. */
+typedef struct {
+	__cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
+} cpu_set_t;
+
+static void CPU_ZERO(cpu_set_t *set)
+{
+	memset(set, 0, sizeof(set));
+}
+static void CPU_SET(int cpu, cpu_set_t *set)
+{
+	set->__bits[cpu / __NCPUBITS] |= 1 << (cpu % __NCPUBITS);
+}
+
+/*
+ * sched_setaffinity actually takes an "unsigned long *" for the mask,
+ * while glibc takes a "cpu_set_t *".  Wrap the call to deal with the
+ * inconsistency.
+ */
+static int local_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask)
+{
+	return sched_setaffinity(pid, cpusetsize, (unsigned long *)mask);
+}
+#else /* ifndef __CPU_SETSIZE */
+#define local_setaffinity sched_setaffinity
+#endif /* ifndef __CPU_SETSIZE */
+
+/* Helper function to set the affinity of the process to a given cpu. */
+static int
+set_cpu_affinity(int cpu)
+{
+	cpu_set_t cpuset;
+
+	/* run on the specified CPU */
+	CPU_ZERO(&cpuset);
+	CPU_SET(cpu, &cpuset);
+	if (local_setaffinity(getpid(), sizeof(cpuset), &cpuset) < 0) {
+		perror("sched_setaffinity()");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+busy_loop(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long long count = 0;
+	struct timeval t0;
+	struct timeval t1;
+	int reps;
+	int printed = 0;
+
+	if (argc == 2) {
+		reps = atoi(argv[1]);
+	} else {
+		reps = -1;
+	}
+
+	gettimeofday(&t0, NULL);
+	while (printed != reps) {
+		count++;
+		if ((count % 16384) == 0) {
+			unsigned int elapsed;
+			gettimeofday(&t1, NULL);
+			elapsed = (t1.tv_sec - t0.tv_sec)*1000000;
+			elapsed += t1.tv_usec - t0.tv_usec;
+			if (elapsed >= 1000000) {
+				printf("%llu\n", count);
+				gettimeofday(&t0, NULL);
+				count = 0;
+				if (reps != -1) {
+					printed++;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+#ifdef ARCH_X86
+
+#define rdtscll(val) do { \
+	uint32_t __a, __d; \
+	__asm__ __volatile__("rdtsc" : "=a" (__a), "=d" (__d)); \
+	(val) = ((uint64_t)__a) | (((uint64_t)__d)<<32); \
+} while(0)
+
+static int
+rdtsc(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long long tsc;
+
+	rdtscll(tsc);
+	printf("0x%016llx\n", tsc);
+
+	return 0;
+}
+
+static int
+cpuid_inline(int cpu, int function, int index, uint32_t *data)
+{
+	/* run on the specified CPU */
+	if (set_cpu_affinity(cpu) < 0) {
+		return -1;
+	}
+
+	asm volatile (
+#if defined(__i386__) && defined(__PIC__)
+	      /* We can't use %ebx on 32 bit builds with PIC.
+	       *
+	       * The need for this is, IMO, a bug in GCC.  We should not
+	       * need to know what registers it is using internally.  It
+	       * should be saving and restoring them itself.
+	       */
+	      "xchg %%ebx, %%esi;" /* save ebx in esi */
+	      "cpuid;"
+	      "xchg %%esi, %%ebx;" /* restore ebx, data moves to esi */
+	      : "=a" (data[0]), "=S" (data[1]), "=c" (data[2]), "=d" (data[3])
+	      : "0" (function), "2" (index)
+	      : "memory"
+#else
+	      "cpuid;"
+	      : "=a" (data[0]), "=b" (data[1]), "=c" (data[2]), "=d" (data[3])
+	      : "0" (function), "2" (index)
+	      : "memory"
+#endif
+	);
+
+	return 0;
+}
+
+static int
+cpuid(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long function;
+	unsigned long index;
+	int cpu;
+	uint32_t data[4];
+
+	cpu = strtol(argv[1], NULL, 0);
+	function = strtoul(argv[2], NULL, 0);
+	index = 0;
+	if (argc == 4) {
+		index = strtoul(argv[3], NULL, 0);
+	}
+
+	if (cpuid_inline(cpu, function, index, data) < 0) {
+		fprintf(stderr, "Could not obtain cpuid result.\n");
+		return -1;
+	}
+
+	printf("0x%08x 0x%08x 0x%08x 0x%08x\n",
+	       data[0], data[1], data[2], data[3]);
+
+	return 0;
+}
+
+#endif /* #ifdef ARCH_X86 */
+
+static int
+cpu_list(int argc, const char *argv[], const struct cmd_info *info)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+	int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+	int i;
+
+	for (i = 0; i < ncpus; i++) {
+		printf("%d\n", i);
+	}
+	return 0;
+#else /* ifdef _SC_NPROCESSORS_ONLN */
+	/* Some libraries do not define sysconf() properly. */
+	fprintf(stderr, "Operation not supported by library.\n");
+	return -1;
+#endif /* ifdef _SC_NPROCESSORS_ONLN */
+}
+
+/*
+ * runon()
+ *
+ * At least two parameters :
+ *
+ * argv[1]: cpu to run command on
+ * argv[2]: command to run
+ * argv[3]  and on are arguments to pass to exec()
+ *
+ */
+static int
+runon(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long cpu;
+
+	/* strip program command line */
+	argc--; argv++;
+
+	/* if we wanted to be clever we could parse it as cpu,cpu,cpu */
+	cpu = strtoul(argv[0], NULL, 0);
+	argc--; argv++;
+
+	/* run on the specified CPU */
+	if (set_cpu_affinity(cpu) < 0) {
+		return -1;
+	}
+
+	/* load the target */
+	execvp(argv[0], (void *)argv);
+
+	/* if we get here, we didn't exec... */
+	perror("exec");
+
+	return -1;
+}
+
+MAKE_PREREQ_PARAMS_VAR_ARGS(cpuid_params, 3, 4, "<cpu> <function> [index]", 0);
+MAKE_PREREQ_PARAMS_VAR_ARGS(runon_params, 3, INT_MAX, "<cpu> <cmd> [args]", 0);
+
+static const struct cmd_info misc_cmds[] = {
+#ifdef ARCH_X86
+	MAKE_CMD(rdtsc, rdtsc, NULL),
+	MAKE_CMD_WITH_PARAMS(cpuid, cpuid, NULL, &cpuid_params),
+#endif /* #ifdef ARCH_X86 */
+	MAKE_CMD(busy_loop, &busy_loop, NULL),
+	MAKE_CMD(cpu_list, cpu_list, NULL),
+	MAKE_CMD_WITH_PARAMS(runon, &runon, NULL, &runon_params),
+};
+
+MAKE_CMD_GROUP(MISC, NULL, misc_cmds);
+REGISTER_CMD_GROUP(MISC);
diff --git a/util/iotools/mmio_rw.c b/util/iotools/mmio_rw.c
new file mode 100644
index 0000000..0553549
--- /dev/null
+++ b/util/iotools/mmio_rw.c
@@ -0,0 +1,325 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/*
+ * Quick access to physical memory, requires /dev/mem
+ * Tim Hockin <thockin at google.com>
+ */
+#define _FILE_OFFSET_BITS 64
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include "commands.h"
+
+struct mmap_info {
+	int fd;
+	volatile void *mem;
+	int off;
+	size_t pgsize;
+	size_t length;
+	uint64_t addr;
+};
+
+struct mmap_file_flags {
+	int flags;
+};
+
+/* open /dev/mem and mmap the address specified in mmap_addr. return 1 on
+ * success, 0 on failure. */
+static int
+open_mapping(struct mmap_info *mmap_addr, int flags, unsigned bytes)
+{
+	int prot;
+
+	/* align addr */
+	mmap_addr->pgsize = getpagesize();
+	mmap_addr->off = mmap_addr->addr & (mmap_addr->pgsize - 1);
+	mmap_addr->addr &= ~ ((uint64_t)mmap_addr->pgsize - 1);
+
+	mmap_addr->fd = open("/dev/mem", flags);
+	if (mmap_addr->fd < 0) {
+		fprintf(stderr, "open(/dev/mem): %s\n", strerror(errno));
+		return -1;
+	}
+
+	prot = 0;
+	if ((flags&O_ACCMODE) == O_RDWR) {
+		prot = PROT_READ | PROT_WRITE;
+	} else if ((flags & O_ACCMODE) == O_RDONLY) {
+		prot = PROT_READ;
+	} else if  ((flags & O_ACCMODE) == O_WRONLY) {
+		prot = PROT_WRITE;
+	}
+
+	/* The length is comprised of the bytes requested including the partial
+	 * number of bytes that do not make up a full page. */
+	mmap_addr->length = bytes + mmap_addr->off;
+
+	mmap_addr->mem = mmap(NULL, mmap_addr->length, prot, MAP_SHARED,
+	           mmap_addr->fd, mmap_addr->addr);
+
+	if (!mmap_addr->mem || mmap_addr->mem == MAP_FAILED) {
+		fprintf(stderr, "mmap(/dev/mem): %s\n", strerror(errno));
+		close(mmap_addr->fd);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+close_mapping(struct mmap_info *mmap_addr)
+{
+	munmap((void *)mmap_addr->mem, mmap_addr->length);
+	close(mmap_addr->fd);
+}
+
+static int
+mmio_read_x(int argc, const char *argv[], const struct cmd_info *info)
+{
+	int ret;
+	data_store data;
+	struct mmap_info mmap_addr;
+	const struct mmap_file_flags *mmf;
+
+	mmf = info->privdata;
+
+	mmap_addr.addr = strtoull(argv[1], NULL, 0);
+
+	if (open_mapping(&mmap_addr, O_RDONLY | mmf->flags, sizeof(data)) < 0) {
+		return -1;
+	}
+
+	ret = 0;
+
+	#define DO_READ(mem_, off_, size_)\
+		data.u ##size_ = *(typeof(data.u ##size_) *)(mem_ + off_); \
+		fprintf(stdout, "0x%0*llx\n", (int)sizeof(data.u ##size_)*2, \
+		        (unsigned long long)data.u ##size_)
+
+	switch (get_command_size(info)) {
+	case SIZE8:
+		DO_READ(mmap_addr.mem, mmap_addr.off, 8);
+		break;
+	case SIZE16:
+		DO_READ(mmap_addr.mem, mmap_addr.off, 16);
+		break;
+	case SIZE32:
+		DO_READ(mmap_addr.mem, mmap_addr.off, 32);
+		break;
+	case SIZE64:
+		if (sizeof(void *) != sizeof(uint64_t)) {
+			fprintf(stderr, "warning: 64 bit operations might "
+			        "not be atomic on 32 bit builds\n");
+		}
+		DO_READ(mmap_addr.mem, mmap_addr.off, 64);
+		break;
+	default:
+		fprintf(stderr, "invalid mmio_read parameter\n");
+		ret = -1;
+	}
+
+	close_mapping(&mmap_addr);
+
+	return ret;
+}
+
+static int
+mmio_write_x(int argc, const char *argv[], const struct cmd_info *info)
+{
+	int ret;
+	unsigned long ldata;
+	data_store data;
+	struct mmap_info mmap_addr;
+
+	mmap_addr.addr = strtoull(argv[1], NULL, 0);
+	ldata = strtoul(argv[2], NULL, 0);
+
+	if (open_mapping(&mmap_addr, O_RDWR, sizeof(data)) < 0) {
+		return -1;
+	}
+
+	ret = 0;
+
+	#define DO_WRITE(mem_, off_, size_)\
+		data.u ##size_ = (typeof(data.u ##size_))ldata;\
+		*(typeof(data.u ##size_) *)(mem_+off_) = data.u ##size_
+
+	switch (get_command_size(info)) {
+	case SIZE8:
+		DO_WRITE(mmap_addr.mem, mmap_addr.off, 8);
+		break;
+	case SIZE16:
+		DO_WRITE(mmap_addr.mem, mmap_addr.off, 16);
+		break;
+	case SIZE32:
+		DO_WRITE(mmap_addr.mem, mmap_addr.off, 32);
+		break;
+	case SIZE64:
+		if (sizeof(void *) != sizeof(uint64_t)) {
+			fprintf(stderr, "warning: 64 bit operations might "
+			        "not be atomic on 32 bit builds\n");
+		}
+		DO_WRITE(mmap_addr.mem, mmap_addr.off, 64);
+		break;
+	default:
+		fprintf(stderr, "invalid mmio_write parameter\n");
+		ret = -1;
+	}
+
+	close_mapping(&mmap_addr);
+
+	return ret;
+}
+
+static int
+mmio_dump(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long bytes_to_dump;
+	unsigned long bytes_left;
+	struct mmap_info mmap_addr;
+	uint32_t *addr;
+	uint64_t desired_addr;
+	int fields_on_line;
+	int write_binary;
+
+	desired_addr = strtoull(argv[1], NULL, 0);
+	bytes_to_dump = strtoul(argv[2], NULL, 0);
+
+	write_binary = 0;
+	if (argc == 4) {
+		if (!strcmp(argv[3], "-b")) {
+			write_binary = 1;
+		} else {
+			return -1;
+		}
+	}
+
+	mmap_addr.addr = desired_addr;
+	if (open_mapping(&mmap_addr, O_RDONLY, bytes_to_dump) < 0) {
+		return -1;
+	}
+
+	if (write_binary) {
+		void *buffer_to_write = (void *)mmap_addr.mem + mmap_addr.off;
+		int ret = fwrite(buffer_to_write, bytes_to_dump, 1, stdout);
+		close_mapping(&mmap_addr);
+		return ret == 1 ? 0 : -1;
+	}
+
+	addr = (void *)mmap_addr.mem + mmap_addr.off;
+	bytes_left = bytes_to_dump;
+
+	fields_on_line = 0;
+	while (bytes_left) {
+		int bytes_printed = sizeof(*addr);
+		/* Print out the current address. */
+		if (!fields_on_line) {
+			fprintf(stdout, "0x%016llx:",
+			        (unsigned long long)desired_addr);
+		}
+
+		/* Print out the leftover bytes. */
+		if (bytes_left < sizeof(*addr)) {
+			unsigned char *ptr = (unsigned char *)addr;
+			fprintf(stdout, " 0x%02x", *ptr);
+			/* Adjust the working pointer and the bytes_printed */
+			addr = (typeof(addr))++ptr;
+			bytes_printed = sizeof(*ptr);
+		} else {
+			fprintf(stdout, " 0x%08x", *addr);
+			addr++;
+		}
+
+		/* Keep track of statistics. */
+		bytes_left -= bytes_printed;
+		desired_addr += bytes_printed;
+
+		/* Default to printing out 4 sets of 32-bit values. */
+		fields_on_line = (fields_on_line + 1) % 4;
+
+		/* Handle the new line once we are field 0 again. */
+		if (!fields_on_line) {
+			fprintf(stdout, "\n");
+		}
+	}
+
+	/* Print newline if we stopped printing in the middle of a line. */
+	if (fields_on_line) {
+		fprintf(stdout, "\n");
+	}
+
+	close_mapping(&mmap_addr);
+
+	return 0;
+}
+
+static struct mmap_file_flags cacheable_access = {};
+static struct mmap_file_flags uncacheable_access = { O_SYNC };
+
+MAKE_PREREQ_PARAMS_FIXED_ARGS(rd_params, 2, "<addr>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(wr_params, 3, "<addr> <value>", 0);
+MAKE_PREREQ_PARAMS_VAR_ARGS(dump_params, 3, 4, "<addr> <num_bytes> [-b]", 0);
+
+#define MAKE_MMIO_READ_CMD(prefix_, size_, access_) \
+	MAKE_CMD_WITH_PARAMS_SIZE(prefix_ ## _read ##size_, &mmio_read_x, \
+	                          &access_, &rd_params, &size ##size_)
+#define MAKE_MMIO_WRITE_CMD(prefix_, size_, access_) \
+	MAKE_CMD_WITH_PARAMS_SIZE(prefix_ ## _write ##size_, &mmio_write_x, \
+	                          &access_, &wr_params, &size ##size_)
+#define MAKE_MMIO_RW_CMD_PAIR(prefix_, size_, access_) \
+	MAKE_MMIO_READ_CMD(prefix_, size_, access_), \
+	MAKE_MMIO_WRITE_CMD(prefix_, size_, access_)
+
+#define MAKE_UC_MMIO_RW_CMD_PAIR(size_) \
+	MAKE_MMIO_RW_CMD_PAIR(mmio, size_, uncacheable_access)
+#define MAKE_WB_MMIO_RW_CMD_PAIR(size_) \
+	MAKE_MMIO_RW_CMD_PAIR(mem, size_, cacheable_access)
+
+static const struct cmd_info mmio_cmds[] = {
+	MAKE_UC_MMIO_RW_CMD_PAIR(8),
+	MAKE_UC_MMIO_RW_CMD_PAIR(16),
+	MAKE_UC_MMIO_RW_CMD_PAIR(32),
+	MAKE_UC_MMIO_RW_CMD_PAIR(64),
+	MAKE_CMD_WITH_PARAMS(mmio_dump, &mmio_dump, &uncacheable_access,
+	                     &dump_params)
+};
+
+MAKE_CMD_GROUP(MMIO,
+               "commands to access uncacheable memory mapped address spaces",
+               mmio_cmds);
+REGISTER_CMD_GROUP(MMIO);
+
+static const struct cmd_info cacheable_mmio_cmds[] = {
+	MAKE_WB_MMIO_RW_CMD_PAIR(8),
+	MAKE_WB_MMIO_RW_CMD_PAIR(16),
+	MAKE_WB_MMIO_RW_CMD_PAIR(32),
+	MAKE_WB_MMIO_RW_CMD_PAIR(64),
+	MAKE_CMD_WITH_PARAMS(mem_dump, &mmio_dump, &cacheable_access,
+	                     &dump_params)
+};
+
+MAKE_CMD_GROUP(MEM,
+               "commands to access cacheable memory mapped address spaces",
+               cacheable_mmio_cmds);
+REGISTER_CMD_GROUP(MEM);
diff --git a/util/iotools/msr.c b/util/iotools/msr.c
new file mode 100644
index 0000000..87e7258
--- /dev/null
+++ b/util/iotools/msr.c
@@ -0,0 +1,125 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/*
+ * Quick MSR access, requires linux msr driver
+ * Tim Hockin <thockin at google.com>
+ */
+#define _FILE_OFFSET_BITS 64
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include "commands.h"
+#include "platform.h"
+
+#ifdef ARCH_X86
+
+static int
+open_and_seek(int cpu, unsigned long msr, int mode, int *fd)
+{
+	char dev[512];
+
+	snprintf(dev, sizeof(dev), "/dev/cpu/%d/msr", cpu);
+	*fd = open(dev, mode);
+	if (*fd < 0) {
+		fprintf(stderr, "open(\"%s\"): %s\n", dev, strerror(errno));
+		return -1;
+	}
+
+	if (lseek(*fd, msr, SEEK_SET) == (off_t)-1) {
+		fprintf(stderr, "lseek(%lu): %s\n", msr, strerror(errno));
+		close(*fd);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+rd_msr(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long msr;
+	int cpu;
+	uint64_t data;
+	int fd;
+
+	cpu = strtol(argv[1], NULL, 0);
+	msr = strtoul(argv[2], NULL, 0);
+
+	if (open_and_seek(cpu, msr, O_RDONLY, &fd) < 0) {
+		return -1;
+	}
+
+	if (read(fd, &data, sizeof(data)) != sizeof(data)) {
+		fprintf(stderr, "read(): %s\n", strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+
+	printf("0x%016" PRIx64 "\n", data);
+
+	return 0;
+}
+
+static int
+wr_msr(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned long msr;
+	int cpu;
+	uint64_t data;
+	int fd;
+	int ret = 0;
+
+	cpu = strtol(argv[1], NULL, 0);
+	msr = strtoul(argv[2], NULL, 0);
+	data = strtoull(argv[3], NULL, 0);
+
+	if (open_and_seek(cpu, msr, O_WRONLY, &fd) < 0) {
+		return -1;
+	}
+
+	if (write(fd, &data, sizeof(data)) != sizeof(data)) {
+		fprintf(stderr, "write(): %s\n", strerror(errno));
+		ret = -1;
+	}
+
+	close(fd);
+
+	return ret;
+}
+
+MAKE_PREREQ_PARAMS_FIXED_ARGS(rd_params, 3, "<cpu> <msr>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(wr_params, 4, "<cpu> <msr> <data>", 0);
+
+static const struct cmd_info msr_cmds[] = {
+	MAKE_CMD_WITH_PARAMS(rdmsr, &rd_msr, NULL, &rd_params),
+	MAKE_CMD_WITH_PARAMS(wrmsr, &wr_msr, NULL, &wr_params),
+};
+
+MAKE_CMD_GROUP(MSR, "commands to access CPU model specific registers",
+               msr_cmds);
+REGISTER_CMD_GROUP(MSR);
+
+#endif /* #ifdef ARCH_X86 */
diff --git a/util/iotools/pci_rw.c b/util/iotools/pci_rw.c
new file mode 100644
index 0000000..26c0242
--- /dev/null
+++ b/util/iotools/pci_rw.c
@@ -0,0 +1,297 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/*
+ * Quick PCI access, requires linux /proc/bus/pci
+ * Tim Hockin <thockin at google.com>
+ */
+#define _FILE_OFFSET_BITS 64
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include "commands.h"
+#include "platform.h"
+
+#define PROCFS_BASE_DIR	"/proc/bus/pci"
+#define SYSFS_BASE_DIR	"/sys/bus/pci/devices"
+
+static int
+open_device(int segment, int bus, int device, int function, int mode)
+{
+	static char filename[FILENAME_MAX];
+	int fd;
+
+	/* Try sysfs first, but fall back on the proc filesystem. */
+	snprintf(filename, sizeof(filename), "%s/%04x:%02x:%02x.%x/config",
+		 SYSFS_BASE_DIR, segment, bus, device, function);
+	fd = open(filename, mode);
+
+	/* If sysfs failed, try the proc filesystem. */
+	if (fd < 0) {
+		if (segment == 0) {
+			snprintf(filename, sizeof(filename), "%s/%02x/%02x.%x",
+				 PROCFS_BASE_DIR, bus, device, function);
+		} else {
+			snprintf(filename, sizeof(filename),
+			         "%s/%04x:%02x/%02x.%x", PROCFS_BASE_DIR,
+			         segment, bus, device, function);
+		}
+		fd = open(filename, mode);
+	}
+
+	if (fd < 0) {
+		fprintf(stderr,
+		        "Unable to open file to access PCI device "
+		        "'%04x:%02x:%02x.%x': %s\n",
+		        segment, bus, device, function, strerror(errno));
+	}
+
+	return fd;
+}
+
+static int
+pci_read_x(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned int seg;
+	unsigned int bus;
+	unsigned int dev;
+	unsigned int func;
+	unsigned int reg;
+	data_store data;
+	int fd;
+	int arg;
+
+	arg = 1;
+	if (argc > 5) {
+		seg = strtol(argv[arg++], NULL, 0);
+	} else {
+		seg = 0;
+	}
+	bus = strtol(argv[arg++], NULL, 0);
+	dev = strtoul(argv[arg++], NULL, 0);
+	func = strtoul(argv[arg++], NULL, 0);
+	reg = strtoul(argv[arg++], NULL, 0);
+	
+
+	fd = open_device(seg, bus, dev, func, O_RDONLY);
+	if (fd < 0) {
+		return -1;
+	}
+
+	if (lseek(fd, reg, SEEK_SET) < 0) {
+		fprintf(stderr, "lseek(%u): %s\n", reg, strerror(errno));
+		return -1;
+	}
+
+
+	#define DO_READ(size_) \
+		if (read(fd, &data.u ##size_, sizeof(data.u ##size_)) != \
+		    sizeof(data.u ##size_)) { \
+		    fprintf(stderr, "read(): %s\n", strerror(errno)); \
+		    return -1; \
+		} \
+		data.u ##size_ = le_to_host_##size_(data.u ##size_); \
+		fprintf(stdout, "0x%0*x\n", (int)sizeof(data.u ##size_)*2, \
+		       data.u ##size_)
+
+	switch(get_command_size(info)) {
+	case SIZE8:
+		DO_READ(8);
+		break;
+	case SIZE16:
+		DO_READ(16);
+		break;
+	case SIZE32:
+		DO_READ(32);
+		break;
+	default:
+		fprintf(stderr, "invalid pci_read entry\n");
+		return -1;
+	}
+
+	close(fd);
+
+	return 0;
+}
+
+static int
+pci_write_x(int argc, const char *argv[], const struct cmd_info *info)
+{
+	unsigned int seg;
+	unsigned int bus;
+	unsigned int dev;
+	unsigned int func;
+	unsigned int reg;
+	unsigned long ldata;
+	data_store data;
+	int fd;
+	int arg;
+
+	arg = 1;
+	if (argc > 6) {
+		seg = strtol(argv[arg++], NULL, 0);
+	} else {
+		seg = 0;
+	}
+	bus = strtol(argv[arg++], NULL, 0);
+	dev = strtoul(argv[arg++], NULL, 0);
+	func = strtoul(argv[arg++], NULL, 0);
+	reg = strtoul(argv[arg++], NULL, 0);
+	ldata = strtoul(argv[arg++], NULL, 0);
+
+	if ((fd = open_device(seg, bus, dev, func, O_WRONLY)) < 0 ) {
+		return -1;
+	}
+
+	if (lseek(fd, reg, SEEK_SET) < 0) {
+		fprintf(stderr, "lseek(%u): %s\n", reg, strerror(errno));
+		return -1;
+	}
+
+#define DO_WRITE(size_) \
+	do { \
+	data.u ##size_ = host_to_le_##size_((typeof(data.u ##size_))ldata); \
+	if (write(fd, &data.u ##size_, sizeof(data.u ##size_)) != \
+	    sizeof(data.u ##size_)) { \
+		fprintf(stderr, "write(): %s\n", strerror(errno)); \
+		return -1;\
+	} \
+	} while (0)
+
+	switch(get_command_size(info)) {
+	case SIZE8:
+		DO_WRITE(8);
+		break;
+	case SIZE16:
+		DO_WRITE(16);
+		break;
+	case SIZE32:
+		DO_WRITE(32);
+		break;
+	default:
+		fprintf(stderr, "invalid pci_read entry\n");
+		return -1;
+	}
+
+	close(fd);
+
+	return 0;
+}
+
+static int
+pci_list_sysfs(void)
+{
+	DIR *dir = opendir(SYSFS_BASE_DIR);
+	struct dirent *de;
+
+	if (!dir) {
+		fprintf(stderr, "opendir(%s): %s\n",
+		        SYSFS_BASE_DIR, strerror(errno));
+		return -1;
+	}
+	while ((de = readdir(dir))) {
+		int seg, bus, dev, fun;
+		int r = sscanf(de->d_name, "%04x:%02x:%02x.%x",
+		               &seg, &bus, &dev, &fun);
+		if (r == 4) {
+			printf("%d %d %d\n", bus, dev, fun);
+		}
+	}
+	closedir(dir);
+	return 0;
+}
+
+static int
+pci_list_procfs(void)
+{
+	DIR *dir = opendir(PROCFS_BASE_DIR);
+	struct dirent *de;
+
+	if (!dir) {
+		fprintf(stderr, "opendir(%s): %s\n",
+		        PROCFS_BASE_DIR, strerror(errno));
+		return -1;
+	}
+	while ((de = readdir(dir))) {
+		int bus;
+		int r = sscanf(de->d_name, "%02x", &bus);
+		if (r == 1 && de->d_type == DT_DIR) {
+			char buf[1024];
+			snprintf(buf, sizeof(buf), "%s/%s",
+			         PROCFS_BASE_DIR, de->d_name);
+			DIR *subdir = opendir(buf);
+			struct dirent *subde;
+
+			if (!subdir) {
+				fprintf(stderr, "opendir(%s): %s\n",
+				        buf, strerror(errno));
+				return -1;
+			}
+			while ((subde = readdir(subdir))) {
+				int dev, fun;
+				r = sscanf(subde->d_name, "%02x.%x",
+				           &dev, &fun);
+				if (r == 2) {
+					printf("%d %d %d\n", bus, dev, fun);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+pci_list(int argc, const char *argv[], const struct cmd_info *info)
+{
+	int ret = pci_list_sysfs();
+	if (ret < 0) {
+		ret = pci_list_procfs();
+	}
+	return ret;
+}
+
+MAKE_PREREQ_PARAMS_VAR_ARGS(rd_params, 5, 6,
+                            "[segment] <bus> <dev> <func> <reg>", 0);
+MAKE_PREREQ_PARAMS_VAR_ARGS(wr_params, 6, 7,
+                            "[segment] <bus> <dev> <func> <reg> <data>", 0);
+
+#define MAKE_PCI_READ_CMD(size_) \
+	MAKE_CMD_WITH_PARAMS_SIZE(pci_read ##size_, &pci_read_x, NULL, \
+	                         &rd_params, &size ##size_)
+#define MAKE_PCI_WRITE_CMD(size_) \
+	MAKE_CMD_WITH_PARAMS_SIZE(pci_write ##size_, &pci_write_x, NULL, \
+	                          &wr_params, &size ##size_)
+#define MAKE_PCI_RW_CMD_PAIR(size_) \
+	MAKE_PCI_READ_CMD(size_), \
+	MAKE_PCI_WRITE_CMD(size_)
+
+static const struct cmd_info pci_cmds[] = {
+	MAKE_PCI_RW_CMD_PAIR(8),
+	MAKE_PCI_RW_CMD_PAIR(16),
+	MAKE_PCI_RW_CMD_PAIR(32),
+	MAKE_CMD(pci_list, pci_list, NULL),
+};
+
+MAKE_CMD_GROUP(PCI, "commands to access PCI registers", pci_cmds);
+REGISTER_CMD_GROUP(PCI);
diff --git a/util/iotools/platform.h b/util/iotools/platform.h
new file mode 100644
index 0000000..76985d2
--- /dev/null
+++ b/util/iotools/platform.h
@@ -0,0 +1,50 @@
+/*
+ Copyright 2012 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef _PLATFORM_H_
+#define _PLATFORM_H_
+
+/*
+ * Platform-specific support.
+ */
+
+#include <byteswap.h>
+
+#if defined(__i386__) || defined(__x86_64__)
+# define ARCH_X86 1
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define IS_BIG_ENDIAN 1
+# define le_to_host_8(x) (x)
+# define le_to_host_16(x) bswap_16(x)
+# define le_to_host_32(x) bswap_32(x)
+# define host_to_le_8(x) (x)
+# define host_to_le_16(x) bswap_16(x)
+# define host_to_le_32(x) bswap_32(x)
+#else
+# define IS_LITTLE_ENDIAN 1
+# define le_to_host_8(x) (x)
+# define le_to_host_16(x) (x)
+# define le_to_host_32(x) (x)
+# define host_to_le_8(x) (x)
+# define host_to_le_16(x) (x)
+# define host_to_le_32(x) (x)
+#endif
+
+#endif /* _PLATFORM_H_ */
diff --git a/util/iotools/smbus_rw.c b/util/iotools/smbus_rw.c
new file mode 100644
index 0000000..8b0f5f9
--- /dev/null
+++ b/util/iotools/smbus_rw.c
@@ -0,0 +1,413 @@
+/*
+ Copyright 2008 Google Inc.
+
+ 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "commands.h"
+#include "linux-i2c-dev.h"
+
+enum SMBUS_SIZE
+{
+	/* The following 3 transactions issue a register address preceding the
+	 * transaction. In SMBus lingo, this is "command code." */
+	SMBUS_SIZE_8  = SIZE8,
+	SMBUS_SIZE_16 = SIZE16,
+	SMBUS_SIZE_BLOCK,
+	/* This transaction does not issue a register address. */
+	SMBUS_SIZE_BYTE,
+	/* Quick transactions. */
+	SMBUS_QUICK,
+};
+
+typedef union {
+	data_store fixed;
+	uint8_t   array[32];
+} SMBUS_DTYPE;
+
+struct smbus_op_params {
+	int fd;
+	uint8_t reg;
+	uint8_t i2c_bus;
+	uint8_t address;
+	int len;
+	SMBUS_DTYPE data;
+};
+
+struct smbus_op {
+	int size;
+	int (*perform_op)(struct smbus_op_params *params, const struct smbus_op *op);
+};
+
+/* setup a file descriptor for i2c slave access */
+static int
+open_i2c_slave(unsigned char i2c_bus, unsigned char slave_address)
+{
+	char devfile[15];
+	int fd;
+
+	sprintf(devfile, "/dev/i2c-%d", i2c_bus);
+	fd = open(devfile, O_RDWR);
+	if (fd < 0) {
+		printf("Couldn't open i2c device file: %s\n", strerror(errno));
+		return -1;
+	}
+
+	/* Double cast the last argument for compat with klibc. */
+	if (ioctl(fd, I2C_SLAVE, (void *)(intptr_t)slave_address) < 0) {
+		printf("Could not attach to i2c bus %d slave address %d: %s\n",
+		       i2c_bus, slave_address, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int
+parse_uint8(const char *arg, uint8_t *ret)
+{
+	unsigned long ldata;
+	char *end;
+
+	ldata = strtoul(arg, &end, 0);
+	if (ldata == LONG_MAX || *end != '\0') {
+		return -1;
+	}
+	*ret = (uint8_t)ldata;
+	return 0;
+}
+
+/* smbus_prologue is responsible for doing the common bits for both smbus read
+ * and write. It will parse the comand line arguments and open the appropriate
+ * i2c device. It returns 1 on success, 0 on failure. */
+static int
+smbus_prologue(const char *argv[], struct smbus_op_params *params,
+               const struct smbus_op *op)
+{
+	if (parse_uint8(argv[1], &params->i2c_bus)) {
+		fprintf(stderr, "invalid adapter value\n");
+		return -1;
+	}
+	if (parse_uint8(argv[2], &params->address)) {
+		fprintf(stderr, "invalid address value\n");
+		return -1;
+	}
+
+	/* Only obtain the register if size designates that it is not a byte
+	 * or quick operation. */
+	if (op->size != SMBUS_SIZE_BYTE && op->size != SMBUS_QUICK) {
+		if (parse_uint8(argv[3], &params->reg)) {
+			fprintf(stderr, "invalid register value\n");
+			return -1;
+		}
+	}
+
+	params->fd = open_i2c_slave(params->i2c_bus, params->address);
+	if (params->fd < 0) {
+		fprintf(stderr, "can't get slave\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+smbus_read(int argc, const char *argv[], const struct cmd_info *info)
+{
+	int ret;
+	struct smbus_op_params params;
+	const struct smbus_op *op =
+		(const struct smbus_op *)info->privdata;
+
+	if (smbus_prologue(argv, &params, op) < 0) {
+		return -1;
+	}
+
+	ret = op->perform_op(&params, op);
+
+	close(params.fd);
+
+	return ret;
+}
+
+static int
+smbus_read_op(struct smbus_op_params *params, const struct smbus_op *op)
+{
+	int result;
+
+	switch (op->size) {
+	case SMBUS_SIZE_8:
+		result = i2c_smbus_read_byte_data(params->fd, params->reg);
+		params->data.fixed.u8 = result;
+		break;
+	case SMBUS_SIZE_16:
+		result = i2c_smbus_read_word_data(params->fd, params->reg);
+		params->data.fixed.u16 = result;
+		break;
+	case SMBUS_SIZE_BLOCK:
+		result = i2c_smbus_read_block_data(params->fd,
+		             params->reg, params->data.array) - 1;
+		break;
+	case SMBUS_SIZE_BYTE:
+		result = i2c_smbus_read_byte(params->fd);
+		params->data.fixed.u8 = result;
+		break;
+	default:
+		fprintf(stderr, "Illegal SMBus size for read operation.\n");
+		return -1;
+	}
+
+	/* if result contains the number of bytes read; make sure it is >= 1
+	 * otherwise make sure result >= 0 */
+	if (result < 0) {
+		if (op->size != SMBUS_SIZE_BYTE) {
+			fprintf(stderr, "can't read register 0x%02X, %s\n",
+			        params->reg, strerror(errno));
+		} else {
+			fprintf(stderr, "can't read from device 0x%02X, %s\n",
+			        params->address, strerror(errno));
+		}
+		return -1;
+	}
+
+	/* print out the data read. */
+	switch (op->size) {
+	case SMBUS_SIZE_BYTE:
+	case SMBUS_SIZE_8:
+		printf("0x%02X\n", params->data.fixed.u8);
+		break;
+	case SMBUS_SIZE_16:
+		printf("0x%04X\n", params->data.fixed.u16);
+		break;
+	case SMBUS_SIZE_BLOCK:
+		{
+		int i;
+		for (i=0; i <= result; i++)
+			printf("%02X", params->data.array[i]);
+		printf("\n");
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int
+parse_io_width(const char *arg, struct smbus_op_params *params,
+               const struct smbus_op *op)
+{
+	unsigned long ldata;
+	char *end;
+
+	switch (op->size) {
+	case SMBUS_QUICK:
+		ldata = strtoul(arg, &end, 0);
+		if (ldata == LONG_MAX || *end != '\0') {
+			return -1;
+		} else if (ldata != 0 && ldata != 1) {
+			return -1;
+		}
+		params->data.fixed.u8 = ldata;
+		break;
+	case SMBUS_SIZE_BYTE:
+	case SMBUS_SIZE_8:
+		ldata = strtoul(arg, &end, 0);
+		if (ldata == LONG_MAX || *end != '\0') {
+			return -1;
+		}
+		params->data.fixed.u8 = ldata;
+		break;
+	case SMBUS_SIZE_16:
+		ldata = strtoul(arg, &end, 0);
+		if (ldata == LONG_MAX || *end != '\0') {
+			return -1;
+		}
+		params->data.fixed.u16 = ldata;
+		break;
+	case SMBUS_SIZE_BLOCK:
+		{
+		int len;
+		int i;
+		char *err;
+		char str_nibble[3];
+
+		len = strlen(arg);
+		if ( (len <= 0) || (len > 64) || (len % 2 != 0) ) {
+			return -1;
+		}
+
+		/* null terminate string. */
+		str_nibble[2] = '\0';
+		for (i = len - 2; i >= 0 ; i -= 2) {
+			str_nibble[0] = arg[i];
+			str_nibble[1] = arg[i+1];
+			params->data.array[i/2] = strtol(str_nibble, &err, 16);
+			if (err[0] != '\0') {
+				return -1;
+			}
+		}
+		params->len = len / 2;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+smbus_write(int argc, const char *argv[], const struct cmd_info *info)
+{
+	int ret;
+	struct smbus_op_params params;
+	const struct smbus_op *op =
+		(const struct smbus_op *)info->privdata;
+	/* All SMBus write operations use argv[4] except for the send_byte
+	 * and quick operations which uses 3. */
+	int arg_num = 4;
+
+	if (smbus_prologue(argv, &params, op) < 0) {
+		return -1;
+	}
+
+	if (op->size == SMBUS_SIZE_BYTE || op->size == SMBUS_QUICK) {
+		arg_num = 3;
+	}
+
+	if (parse_io_width(argv[arg_num], &params, op) < 0 ) {
+		fprintf(stderr, "invalid value to write\n");
+		return -1;
+	}
+
+	ret = op->perform_op(&params, op);
+
+	close(params.fd);
+
+	return ret;
+}
+
+static int
+smbus_write_op(struct smbus_op_params *params, const struct smbus_op *op)
+{
+	int result;
+
+	/* FIXME: Why is this needed if the open_i2c_slave performs the ioctl
+	 * with I2C_SLAVE? */
+	/* Double cast the last argument for compat with klibc. */
+	if (ioctl(params->fd, I2C_SLAVE_FORCE,
+	          (void *)(intptr_t)params->address) < 0) {
+		fprintf(stderr, "can't set address 0x%02X, %s\n",
+		        params->address, strerror(errno));
+		return -1;
+	}
+
+	switch (op->size) {
+	case SMBUS_SIZE_8:
+		result = i2c_smbus_write_byte_data(params->fd, params->reg,
+		             params->data.fixed.u8);
+		break;
+	case SMBUS_SIZE_16:
+		result = i2c_smbus_write_word_data(params->fd, params->reg,
+		             params->data.fixed.u16);
+		break;
+	case SMBUS_SIZE_BLOCK:
+		result = i2c_smbus_write_block_data(params->fd, params->reg,
+		             params->len, params->data.array);
+		break;
+	case SMBUS_SIZE_BYTE:
+		result = i2c_smbus_write_byte(params->fd,
+		                              params->data.fixed.u8);
+		break;
+	case SMBUS_QUICK:
+		result = i2c_smbus_write_quick(params->fd,
+		                              params->data.fixed.u8);
+		break;
+	default:
+		fprintf(stderr, "Illegal SMBus size for write operation.\n");
+		return -1;
+	}
+
+	if (result < 0) {
+		if (op->size != SMBUS_SIZE_BYTE && op->size != SMBUS_QUICK) {
+			fprintf(stderr, "can't write register 0x%02X, %s\n",
+			        params->reg, strerror(errno));
+		} else {
+			fprintf(stderr, "can't write to device 0x%02X, %s\n",
+			        params->address, strerror(errno));
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+MAKE_PREREQ_PARAMS_FIXED_ARGS(smbus_read_params, 4,
+	"<adapter> <address> <register>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(smbus_write_params, 5,
+	"<adapter> <address> <register> <value>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(smbus_receive_byte_params, 3,
+	"<adapter> <address>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(smbus_send_byte_params, 4,
+	"<adapter> <address> <value>", 0);
+MAKE_PREREQ_PARAMS_FIXED_ARGS(smbus_quick_params, 4,
+	"<adapter> <address> <0|1>", 0);
+
+#define MAKE_SMBUS_OP(name_, size_, fn_) \
+	static const struct smbus_op name_ = { \
+		.size = size_, \
+		.perform_op = fn_, \
+	}
+#define MAKE_SMBUS_RW_OP(name_, size_, rd_fn_, wr_fn_) \
+	MAKE_SMBUS_OP(name_## _r, SMBUS_SIZE_ ##size_, rd_fn_); \
+	MAKE_SMBUS_OP(name_## _w, SMBUS_SIZE_ ##size_, wr_fn_)
+
+MAKE_SMBUS_RW_OP(smbus_op_8, 8, smbus_read_op, smbus_write_op);
+MAKE_SMBUS_RW_OP(smbus_op_16, 16, smbus_read_op, smbus_write_op);
+MAKE_SMBUS_RW_OP(smbus_op_block, BLOCK, smbus_read_op, smbus_write_op);
+MAKE_SMBUS_RW_OP(smbus_op_byte, BYTE, smbus_read_op, smbus_write_op);
+MAKE_SMBUS_OP(smbus_op_quick, SMBUS_QUICK, smbus_write_op);
+
+#define MAKE_SMBUS_RW_CMDS(size_) \
+	MAKE_CMD_WITH_PARAMS(smbus_read ##size_, smbus_read, \
+	                     &smbus_op_ ##size_## _r, &smbus_read_params), \
+	MAKE_CMD_WITH_PARAMS(smbus_write ##size_, smbus_write, \
+	                     &smbus_op_ ##size_## _w, &smbus_write_params)
+
+static const struct cmd_info smbus_cmds[] = {
+	MAKE_SMBUS_RW_CMDS(8),
+	MAKE_SMBUS_RW_CMDS(16),
+	MAKE_SMBUS_RW_CMDS(block),
+	MAKE_CMD_WITH_PARAMS(smbus_receive_byte, smbus_read,
+	                     &smbus_op_byte_r, &smbus_receive_byte_params),
+	MAKE_CMD_WITH_PARAMS(smbus_send_byte, smbus_write,
+	                     &smbus_op_byte_w, &smbus_send_byte_params),
+	MAKE_CMD_WITH_PARAMS(smbus_quick, smbus_write,
+	                     &smbus_op_quick, &smbus_quick_params),
+};
+
+MAKE_CMD_GROUP(SMBus, "commands to access the system management bus",
+               smbus_cmds);
+REGISTER_CMD_GROUP(SMBus);



More information about the coreboot-gerrit mailing list