summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS98
-rw-r--r--LICENSE339
-rw-r--r--Makefile148
-rw-r--r--NEWS193
-rw-r--r--README115
-rw-r--r--biosdecode.c723
-rw-r--r--config.h34
-rw-r--r--dmidecode.c6177
-rw-r--r--dmidecode.h53
-rw-r--r--dmioem.c1410
-rw-r--r--dmioem.h25
-rw-r--r--dmiopt.c390
-rw-r--r--dmiopt.h52
-rw-r--r--dmioutput.c137
-rw-r--r--dmioutput.h34
-rw-r--r--man/biosdecode.892
-rw-r--r--man/dmidecode.8331
-rw-r--r--man/ownership.841
-rw-r--r--man/vpddecode.881
-rw-r--r--ownership.c214
-rw-r--r--types.h59
-rw-r--r--util.c279
-rw-r--r--util.h30
-rw-r--r--version.h1
-rw-r--r--vpddecode.c201
-rw-r--r--vpdopt.c157
-rw-r--r--vpdopt.h45
27 files changed, 11459 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..7065f72
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,98 @@
+DEVELOPER AND MAINTAINER
+Jean Delvare <jdelvare@suse.de>
+
+ORIGINAL AUTHORS
+Alan Cox <alan@redhat.com>
+Jean Delvare <jdelvare@suse.de>
+
+CODE CONTRIBUTORS (IN CHRONOLOGICAL ORDER)
+Matt Domsch <Matt_Domsch@dell.com>
+Arjan van de Ven <arjanv@redhat.com>
+Mark D. Studebaker <mds@paradyne.com>
+Larry Lile <llile@dreamworks.com>
+Dave Johnson <ddj@cascv.brown.edu>
+Petter Reinholdtsen <pere@hungry.com>
+Roberto Nibali <ratz@tac.ch>
+John Cagle <jcagle@kernel.org>
+Jens Elkner <elkner@linofee.org>
+Jarod Wilson <jarod@redhat.com>
+Anton Arapov <anton@redhat.com>
+Roy Franz <roy.franz@linaro.org>
+Tyler Bell <tyler.bell@hp.com>
+Xie XiuQi <xiexiuqi@huawei.com>
+Petr Oros <poros@redhat.com>
+Prabhakar Pujeri <prabhakar.pujeri@dell.com>
+Erwan Velu <e.velu@criteo.com>
+Jerry Hoemann <jerry.hoemann@hpe.com>
+
+MANY THANKS TO (IN CHRONOLOGICAL ORDER)
+Werner Heuser
+Alexandre Duret-Lutz
+Xavier Roche
+Pamela Huntley
+Gael Stephan
+Sebastian Henschel
+Richard Sharpe
+David Wilson
+Glen Foster
+Chad Smith
+Joshua Goldenhar
+Luc Van de Velde
+Mario Lang
+Hugues Lepesant
+Sergey Leonovich
+Mike Cooper
+Marc Rieffel
+Jeff Moyer
+Josef Moellers
+Zing Zing Shishak
+Rafael Avila de Espindola
+Roger Koot
+Martin Pool
+Doug Brenner
+Alex Williamson
+Durval Menezes
+Raphael Raimbault
+Raul Nunez de Arenas Coronado
+Francois Revol
+Dominik Klein
+Erwan Velu
+Don Howard
+Frans Pop
+Tomek Mateja
+Myke Olson
+Torsten Seemann
+Garry Belka
+Klaus Muth
+Antoine Fuselier
+Matthew Garrett
+Landry Breuil
+Luke Suchocki
+Attila Nagy
+Alex Iribarren
+Sebastien Douche
+William Lallemand
+Olivier Guerrier
+Pascal Terjan
+Stuart Hayes
+Sofian Brabez
+Vincent Pelletier
+Andreas Gruenbacher
+Lin Li
+Thomas Hiller
+Paul Flo Williams
+Olof Johansson
+Alexandre Lissy
+Michal Svec
+Vojtech Pavlik
+Murlin Wenzel
+Harald Mueller-Ney
+Lars Mueller
+Thomas Mingarelli
+Andrey Matveyev
+Stefan Tauner
+Naga Chumbalkar
+Jens Rosenboom
+Lianbo Jiang
+Tianjia Zhang
+Ivan Tkachenko
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -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/Makefile b/Makefile
new file mode 100644
index 0000000..7aa729d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,148 @@
+#
+# DMI Decode
+# BIOS Decode
+# VPD Decode
+#
+# Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
+# Copyright (C) 2002-2020 Jean Delvare <jdelvare@suse.de>
+#
+# 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.
+#
+
+CC ?= gcc
+# Base CFLAGS can be overridden by environment
+CFLAGS ?= -O2
+# When debugging, disable -O2 and enable -g
+#CFLAGS ?= -g
+
+CFLAGS += -W -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wmissing-prototypes -Winline -Wundef
+
+# Let lseek and mmap support 64-bit wide offsets
+CFLAGS += -D_FILE_OFFSET_BITS=64
+
+#CFLAGS += -DBIGENDIAN
+#CFLAGS += -DALIGNMENT_WORKAROUND
+
+# Pass linker flags here (can be set from environment too)
+LDFLAGS ?=
+
+DESTDIR =
+prefix = /usr/local
+sbindir = $(prefix)/sbin
+mandir = $(prefix)/share/man
+man8dir = $(mandir)/man8
+docdir = $(prefix)/share/doc/dmidecode
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+INSTALL_PROGRAM := $(INSTALL) -m 755
+RM := rm -f
+
+# BSD make provides $MACHINE, but GNU make doesn't
+MACHINE ?= $(shell uname -m 2>/dev/null)
+
+# These programs are only useful on x86
+PROGRAMS-i386 := biosdecode ownership vpddecode
+PROGRAMS-i486 := $(PROGRAMS-i386)
+PROGRAMS-i586 := $(PROGRAMS-i386)
+PROGRAMS-i686 := $(PROGRAMS-i386)
+PROGRAMS-x86_64 := biosdecode ownership vpddecode
+PROGRAMS-amd64 := $(PROGRAMS-x86_64)
+
+PROGRAMS := dmidecode $(PROGRAMS-$(MACHINE))
+
+all : $(PROGRAMS)
+
+#
+# Programs
+#
+
+dmidecode : dmidecode.o dmiopt.o dmioem.o dmioutput.o util.o
+ $(CC) $(LDFLAGS) dmidecode.o dmiopt.o dmioem.o dmioutput.o util.o -o $@
+
+biosdecode : biosdecode.o util.o
+ $(CC) $(LDFLAGS) biosdecode.o util.o -o $@
+
+ownership : ownership.o util.o
+ $(CC) $(LDFLAGS) ownership.o util.o -o $@
+
+vpddecode : vpddecode.o vpdopt.o util.o
+ $(CC) $(LDFLAGS) vpddecode.o vpdopt.o util.o -o $@
+
+#
+# Objects
+#
+
+dmidecode.o : dmidecode.c version.h types.h util.h config.h dmidecode.h \
+ dmiopt.h dmioem.h dmioutput.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+dmiopt.o : dmiopt.c config.h types.h util.h dmidecode.h dmiopt.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+dmioem.o : dmioem.c types.h dmidecode.h dmioem.h dmioutput.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+dmioutput.o : dmioutput.c types.h dmioutput.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+biosdecode.o : biosdecode.c version.h types.h util.h config.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+ownership.o : ownership.c version.h types.h util.h config.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+vpddecode.o : vpddecode.c version.h types.h util.h config.h vpdopt.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+vpdopt.o : vpdopt.c config.h util.h vpdopt.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+util.o : util.c types.h util.h config.h
+ $(CC) $(CFLAGS) -c $< -o $@
+
+#
+# Commands
+#
+
+strip : $(PROGRAMS)
+ strip $(PROGRAMS)
+
+install : install-bin install-man install-doc
+
+uninstall : uninstall-bin uninstall-man uninstall-doc
+
+install-bin : $(PROGRAMS)
+ $(INSTALL_DIR) $(DESTDIR)$(sbindir)
+ for program in $(PROGRAMS) ; do \
+ $(INSTALL_PROGRAM) $$program $(DESTDIR)$(sbindir) ; done
+
+uninstall-bin :
+ for program in $(PROGRAMS) ; do \
+ $(RM) $(DESTDIR)$(sbindir)/$$program ; done
+
+install-man :
+ $(INSTALL_DIR) $(DESTDIR)$(man8dir)
+ for program in $(PROGRAMS) ; do \
+ $(INSTALL_DATA) man/$$program.8 $(DESTDIR)$(man8dir) ; done
+
+uninstall-man :
+ for program in $(PROGRAMS) ; do \
+ $(RM) $(DESTDIR)$(man8dir)/$$program.8 ; done
+
+install-doc :
+ $(INSTALL_DIR) $(DESTDIR)$(docdir)
+ $(INSTALL_DATA) README $(DESTDIR)$(docdir)
+ $(INSTALL_DATA) NEWS $(DESTDIR)$(docdir)
+ $(INSTALL_DATA) AUTHORS $(DESTDIR)$(docdir)
+
+uninstall-doc :
+ $(RM) -r $(DESTDIR)$(docdir)
+
+clean :
+ $(RM) *.o $(PROGRAMS) core
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..504e3fb
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,193 @@
+Version 3.5 (Tue Mar 14 2023)
+ - Decode HPE OEM records 216, 224, 230, 238 and 242.
+ - Fortify entry point length checks.
+ - Add a --no-quirks option.
+ - Drop the CPUID exception list.
+ - Do not let --dump-bin overwrite an existing file.
+ - Ensure /dev/mem is a character device file.
+ - Bug fixes:
+ Fix segmentation fault in HPE OEM record 240
+ - Minor improvements:
+ Typo fixes
+ Write the whole dump file at once
+ Fix a build warning when USE_MMAP isn't set
+
+Version 3.4 (Mon Jun 27 2022)
+ - Support for SMBIOS 3.4.0. This includes new memory device types, new
+ processor upgrades, new slot types and characteristics, decoding of memory
+ module extended speed, new system slot types, new processor characteristics
+ and new format of Processor ID.
+ - Support for SMBIOS 3.5.0. This includes new processor upgrades, BIOS
+ characteristics, new slot characteristics, new on-board device types, new
+ pointing device interface types, and a new record type (type 45 -
+ Firmware Inventory Information).
+ - Decode HPE OEM records 194, 199, 203, 236, 237, 238 and 240.
+ - Bug fixes:
+ Fix OEM vendor name matching
+ Fix ASCII filtering of strings
+ Fix crash with option -u
+ - Minor improvements:
+ Skip details of uninstalled memory modules
+ Don't display the raw CPU ID in quiet mode
+ Improve the formatting of the manual pages
+
+Version 3.3 (Wed Oct 14 2020)
+ - [BUILD] Allow overriding build settings from the environment.
+ - [COMPATIBILITY] Document how the UUID fields are interpreted.
+ - [PORTABILITY] Don't use memcpy on /dev/mem on arm64.
+ - [PORTABILITY] Only scan /dev/mem for entry point on x86.
+ - Support for SMBIOS 3.3.0. This includes new processor names, new port
+ connector types, and new memory device form factors, types and
+ technologies.
+ - Add bios-revision, firmware-revision and system-sku-number to -s option.
+ - Use the most appropriate unit for cache size.
+ - Decode system slot base bus width and peers.
+ - Important bug fixes:
+ Fix Redfish Hostname print length
+ Fix formatting of TPM table output
+ Fix System Slot Information for PCIe SSD
+ Don't choke on invalid processor voltage
+ - Use the most appropriate unit for cache size.
+
+Version 3.2 (Wed Sep 14 2018)
+ - [COMPATIBILITY] The UUID is now displayed using lowercase letters, per
+ RFC 4122 (#53569). You must ensure that any code parsing it is
+ case-insensitive.
+ - Support for SMBIOS 3.2.0. This includes new processor names, new socket
+ and port connector types, new system slot state and property, and support
+ for non-volatile memory (NVDIMM).
+ - Support for Redfish management controllers.
+ - A new command line option to query a specific structure by its handle.
+ - A new command line option to query the system family string.
+ - Support for 3 ThinkPad-specific structures (patch #9642).
+ - Support for HPE's new company name.
+ - Support UEFI on FreeBSD.
+ - Important bug fixes:
+ Fix firmware version of TPM device
+ Fix the HPE UEFI feature flag check
+ - (biosdecode) A new command line option to fully decode PIR information
+ (support request #109339).
+
+Version 3.1 (Tue May 23 2017)
+ - Support for SMBIOS 3.1.0 and 3.1.1. This includes new chassis types, new
+ processor family names, new processor family upgrade names, and new slot
+ types, as well as support of larger BIOS ROM sizes and cache sizes, and a
+ new structure type (43, TPM Device.)
+ - A new command line option to query OEM strings.
+ - All error messages are now printed on stderr (#47274, #48158.)
+ - Several bug fixes related to 64-bit entry points (#50037 and more.)
+ - Important bug fixes:
+ #46176 (Unexpected end of file error)
+ #46066 (Crash with SIGBUS)
+ - Various minor fixes, improvements and cleanups.
+
+Version 3.0 (Thu Sep 03 2015)
+ - Support for SMBIOS 3.0. This includes new chassis types, new
+ processor family names, new processor family upgrade names, new slot
+ types, and new memory device types.
+ - Support for the new 64-bit entry point (_SM3_) defined in SMBIOS 3.0.
+ - Support for the new kernel interface (as of Linux v4.2) as an
+ alternative to relying /dev/mem to access the entry point and DMI
+ table.
+ - Decoding of Acer-specific DMI type 170.
+ - Decoding of HP-specific DMI types 212, 219 and 233.
+ - Various minor fixes and output format cleanups.
+
+Version 2.12 (Wed Apr 17 2013)
+ - Support of the SMBIOS 2.8.0 specification.
+
+Version 2.11 (Wed Jan 19 2011)
+ - Support of the SMBIOS 2.7.0 specification:
+ - UEFI support
+ - Virtual machine flags in BIOS characteristics
+ - Limited support for the Management Controller Host Interface
+ - Various fixes that address stability.
+
+Version 2.10 (Sun Nov 23 2008)
+ - Support for Solaris (x86 only, of course).
+ - Possibility to dump the SMBIOS/DMI table to a small binary file
+ (option --dump-bin).
+ - Possibility to read the SMBIOS/DMI table from such binary files
+ (option --from-dump).
+ - Support for SMBIOS 2.6. This includes new chassis types, new
+ processor family names, new processor family upgrade names, bus
+ address for system slots, and a new entry type for on-board devices,
+ amongst many other minor changes.
+ - Support for DMI entry type 31 (Boot integrity services).
+ - Many processor family names taken from the CIM Schema document.
+ - (vpddecode) No longer ask users to report broken records.
+ - (vpddecode) Fix --quiet option.
+
+Version 2.9 (Mon Feb 26 2007)
+ - Support of the SMBIOS 2.5 specification. It adds many enumerated
+ values for recent hardware, as well as CPU core and thread count
+ reporting.
+ - Decoding of 3 HP-specific entries. More vendor-specific entries can
+ be supported later if vendors contribute code or documentation.
+ - Run-time detection of EFI, so that a single binary can support
+ Intel-based Macintosh machines and regular x86 machines.
+ - Better IA-64 support.
+ - Fixes to the decoding of individual fields, including the CPU
+ signature of recent CPU models.
+ - (biosdecode) Support of the FJKEYINF entry point type (for Fujitsu laptops).
+ - (vpddecode) The product name look-up table was dropped. It was unreliable
+ and a burden to maintain.
+ - biosdecode, ownership and vpddecode are no longer built on IA-64.
+
+Version 2.8 (Sat Feb 04 2006)
+ - Option --string has four additional keywords available:
+ system-uuid, chassis-type, processor-family and processor-frequency.
+ These needed additional work because, technically speaking, they are
+ not DMI strings.
+ - IPMI interface type SSIF was added. This is a new interface type
+ defined by IPMI 2.0.
+ - (vpddecode) New --string option, much similar in spirit to
+ dmidecode's. Available keywords are bios-build-id, box-serial-number,
+ motherboard-serial-number, machine-type-model and bios-release-date.
+ - (vpddecode) 9 product names were added to the lookup table.
+ - A few bug fixes, cleanups and minor improvements all around the place.
+
+Version 2.7 (Thu Aug 04 2005)
+ - New command line interface. For example, it is now possible to limit
+ the output of dmidecode to a given DMI type, or to extract a single
+ string from the DMI table. The documentation has been updated
+ accordingly.
+ - The default output of dmidecode was slightly modified to be more
+ easily readable by humans. This might break tools parsing its output.
+ Such tools may benefit from the new command line interface, although
+ this interface shouldn't be considered stable until version 2.8.
+ - (vpddecode) New command line interface.
+ - (vpddecode) 6 product names were added.
+
+Version 2.6 (Mon Feb 28 2005)
+ - Fixes a 2 GB memory limit regression.
+ - Basic command-line handling.
+ - BeOS and Cygwin support.
+
+Version 2.5 (Thu Nov 11 2004)
+ - Code cleanups.
+ - Compatibility fixes.
+ - Documentation updates.
+
+Version 2.4 (Fri Mar 19 2004)
+ - Manual pages added.
+ - (vpddecode) Many improvements.
+ - A few fixes and minor improvements.
+
+Version 2.3 (Sun Oct 19 2003)
+ - Support of x86_64 systems.
+ - Support of systems with 2 GB and more memory.
+ - Loads of bug fixes and corrections.
+ - New tool "vpddecode" added.
+
+Version 2.2 (Fri Aug 08 2003)
+ - Support of IA-64 systems.
+ - Support of IBM and Fujitsu-Siemens laptops.
+ - Many minor bug fixes.
+ - New tool "ownership" added.
+
+Version 2.1 (Tue Jun 10 2003)
+ - Support of the SMBIOS 2.3.4 specification.
+ - Better support of IPMI.
+ - Minor bugs fixed.
+ - Documentation added.
diff --git a/README b/README
new file mode 100644
index 0000000..c87e52c
--- /dev/null
+++ b/README
@@ -0,0 +1,115 @@
+** INTRODUCTION **
+
+Dmidecode reports information about your system's hardware as described in
+your system BIOS according to the SMBIOS/DMI standard. This information
+typically includes system manufacturer, model name, serial number, BIOS
+version, asset tag as well as a lot of other details of varying level of
+interest and reliability depending on the manufacturer. This will often
+include usage status for the CPU sockets, expansion slots (e.g. AGP, PCI,
+ISA) and memory module slots, and the list of I/O ports (e.g. serial,
+parallel, USB).
+
+DMI data can be used to enable or disable specific portions of kernel code
+depending on the specific hardware. Thus, one use of dmidecode is for kernel
+developers to detect system "signatures" and add them to the kernel source
+code when needed.
+
+Beware that DMI data have proven to be too unreliable to be blindly trusted.
+Dmidecode does not scan your hardware, it only reports what the BIOS told it
+to.
+
+
+** INSTALLATION **
+
+The home web page for dmidecode is hosted on Savannah:
+ http://www.nongnu.org/dmidecode/
+You will find the latest version (including CVS) there, as well as fresh news
+and other interesting material, such as a list of related projects and
+articles.
+
+This program was first written for Linux, and has since been reported to work
+on FreeBSD, NetBSD, OpenBSD, BeOS and Solaris as well.
+
+There's no configure script, so simply run "make" to build dmidecode, and
+"make install" to install it. You also can use "make uninstall" to remove
+all the files you installed. By default, files are installed in /usr/local
+but you can change this behavior by editing the Makefile file and setting
+prefix to wherever you want. You may change the C compiler and the
+compilation flags as well.
+
+Optionally, you can run "make strip" prior to "make install" if you want
+smaller binaries. However, be aware that this will prevent any further
+attempt to debug the programs.
+
+Two parameters can be set in the Makefile file to make dmidecode work on
+non-i386 systems. They should be used if your system uses the big endian
+byte ordering (Motorola) or doesn't support unaligned memory accesses,
+respectively. For example, compiling for a SPARC processor would require
+both (but I am not aware of SPARC-based systems implementing SMBIOS).
+Compiling for an IA64 processor requires the memory alignment workaround,
+and it is enabled automatically.
+
+
+** DOCUMENTATION **
+
+Each tool has a manual page, found in the "man" subdirectory. Manual pages
+are installed by "make install". See these manual pages for command line
+interface details and tool specific information.
+
+For an history of the changes made to dmidecode, see the NEWS file.
+
+If you need help, your best chances are to visit the web page (see the
+INSTALLATION section above) or to get in touch with the developers directly.
+Have a look at the AUTHORS file and contact one of the maintainers.
+
+If you want to help with the development of dmidecode, please consider
+joining the dmidecode-devel discussion list:
+ http://lists.nongnu.org/mailman/listinfo/dmidecode-devel
+
+
+** COMMON PROBLEMS **
+
+IA-64
+
+Non-Linux systems are not yet supported.
+
+MMAP
+
+Note that mmap() is now used by default wherever possible, since this seems
+to solve a number of problems. This default behavior can be changed in
+config.h. Just to make sure this is clear, mmap() is not used for performance
+reasons but to increase the number of systems on which dmidecode can be
+successfully run.
+
+CYGWIN
+
+Dmidecode used to work under Cygwin. However the /dev/mem interface was
+removed at some point in time so it no longer works.
+
+
+** MISCELLANEOUS TOOLS **
+
+Three other tools come along with dmidecode: biosdecode, ownership and
+vpddecode. These tools are only useful on systems with a BIOS, so they
+are not built on IA-64 by default.
+
+BIOSDECODE
+
+This one prints all BIOS related information it can find in /dev/mem.
+It used to be part of dmidecode itself, but as dmidecode was growing,
+we felt that the non-DMI part had to be moved to a separate tool.
+
+OWNERSHIP
+
+This tool was written on a request by Luc Van de Velde for use with Novell
+tools in his company. It retrieves the "ownership tag" that can be set on
+most Compaq computers. Since it uses the same mechanisms dmidecode and
+biosdecode use, and could be of some use for other people as well, we
+decided to make it part of the project.
+
+VPDDECODE
+
+This tool prints the contents of the "vital product data" structure as
+found in most IBM and Lenovo computers. It used to have a lookup table
+for the machine name, but it was unreliable and hard to maintain so it
+was ultimately dropped. It has a command line interface.
diff --git a/biosdecode.c b/biosdecode.c
new file mode 100644
index 0000000..99a27fe
--- /dev/null
+++ b/biosdecode.c
@@ -0,0 +1,723 @@
+/*
+ * BIOS Decode
+ *
+ * Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
+ * Copyright (C) 2002-2017 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open unpatent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * References:
+ * - DMTF "System Management BIOS (SMBIOS) Reference Specification"
+ * Version 3.0.0
+ * http://www.dmtf.org/standards/smbios
+ * - Intel "Preboot Execution Environment (PXE) Specification"
+ * Version 2.1
+ * http://www.intel.com/labs/manage/wfm/wfmspecs.htm
+ * - ACPI "Advanced Configuration and Power Interface Specification"
+ * Revision 2.0
+ * http://www.acpi.info/spec20.htm
+ * - Phoenix "BIOS32 Service Directory"
+ * Revision 0.4
+ * http://www.phoenix.com/en/support/white+papers-specs/
+ * - Microsoft "Plug and Play BIOS Specification"
+ * Version 1.0A
+ * http://www.microsoft.com/hwdev/tech/PnP/
+ * - Microsoft "PCI IRQ Routing Table Specification"
+ * Version 1.0
+ * http://www.microsoft.com/hwdev/archive/BUSBIOS/pciirq.asp
+ * - Compaq "Technical Reference Guide for Compaq Deskpro 4000 and 6000"
+ * First Edition
+ * http://h18000.www1.hp.com/support/techpubs/technical_reference_guides/113a1097.html
+ * - IBM "Using the BIOS Build ID to identify Thinkpad systems"
+ * Revision 2005-09-19
+ * http://www-307.ibm.com/pc/support/site.wss/MIGR-45120.html
+ * - Fujitsu application panel technical details
+ * As of July 23rd, 2004
+ * http://apanel.sourceforge.net/tech.php
+ * - Intel Multiprocessor Specification
+ * Version 1.4
+ * http://www.intel.com/design/archives/processors/pro/docs/242016.htm
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "version.h"
+#include "config.h"
+#include "types.h"
+#include "util.h"
+
+/* Options are global */
+struct opt
+{
+ const char *devmem;
+ unsigned int flags;
+ unsigned char pir;
+};
+static struct opt opt;
+
+#define FLAG_VERSION (1 << 0)
+#define FLAG_HELP (1 << 1)
+
+#define PIR_SHORT 0
+#define PIR_FULL 1
+
+struct bios_entry {
+ const char *anchor;
+ size_t anchor_len; /* computed */
+ off_t low_address;
+ off_t high_address;
+ size_t (*length)(const u8 *);
+ int (*decode)(const u8*, size_t);
+};
+
+
+/*
+ * SMBIOS
+ */
+
+static size_t smbios3_length(const u8 *p)
+{
+ return p[0x06];
+}
+
+static int smbios3_decode(const u8 *p, size_t len)
+{
+ if (len < 0x18 || !checksum(p, p[0x06]))
+ return 0;
+
+ printf("SMBIOS %u.%u.%u present.\n",
+ p[0x07], p[0x08], p[0x09]);
+ printf("\tStructure Table Maximum Length: %u bytes\n",
+ DWORD(p + 0x0C));
+ printf("\tStructure Table 64-bit Address: 0x%08X%08X\n",
+ QWORD(p + 0x10).h, QWORD(p + 0x10).l);
+
+ return 1;
+}
+
+static size_t smbios_length(const u8 *p)
+{
+ return p[0x05] == 0x1E ? 0x1F : p[0x05];
+}
+
+static int smbios_decode(const u8 *p, size_t len)
+{
+ if (len < 0x1F || !checksum(p, p[0x05])
+ || memcmp("_DMI_", p + 0x10, 5) != 0
+ || !checksum(p + 0x10, 0x0F))
+ return 0;
+
+ printf("SMBIOS %u.%u present.\n",
+ p[0x06], p[0x07]);
+ printf("\tStructure Table Length: %u bytes\n",
+ WORD(p + 0x16));
+ printf("\tStructure Table Address: 0x%08X\n",
+ DWORD(p + 0x18));
+ printf("\tNumber Of Structures: %u\n",
+ WORD(p + 0x1C));
+ printf("\tMaximum Structure Size: %u bytes\n",
+ WORD(p + 0x08));
+
+ return 1;
+}
+
+static size_t dmi_length(const u8 *p)
+{
+ (void) p;
+
+ return 0x0F;
+}
+
+static int dmi_decode(const u8 *p, size_t len)
+{
+ if (len < 0x0F || !checksum(p, len))
+ return 0;
+
+ printf("Legacy DMI %u.%u present.\n",
+ p[0x0E]>>4, p[0x0E] & 0x0F);
+ printf("\tStructure Table Length: %u bytes\n",
+ WORD(p + 0x06));
+ printf("\tStructure Table Address: 0x%08X\n",
+ DWORD(p + 0x08));
+ printf("\tNumber Of Structures: %u\n",
+ WORD(p + 0x0C));
+
+ return 1;
+}
+
+/*
+ * SYSID
+ */
+
+static size_t sysid_length(const u8 *p)
+{
+ return WORD(p + 0x08);
+}
+
+static int sysid_decode(const u8 *p, size_t len)
+{
+ if (len < 0x11 || !checksum(p, WORD(p + 0x08)))
+ return 0;
+
+ printf("SYSID present.\n");
+ printf("\tRevision: %u\n",
+ p[0x10]);
+ printf("\tStructure Table Address: 0x%08X\n",
+ DWORD(p + 0x0A));
+ printf("\tNumber Of Structures: %u\n",
+ WORD(p + 0x0E));
+
+ return 1;
+}
+
+/*
+ * PnP
+ */
+
+static size_t pnp_length(const u8 *p)
+{
+ return p[0x05];
+}
+
+static const char *pnp_event_notification(u8 code)
+{
+ static const char *notification[] = {
+ "Not Supported", /* 0x0 */
+ "Polling",
+ "Asynchronous",
+ "Unknown" /* 0x3 */
+ };
+
+ return notification[code];
+}
+
+static int pnp_decode(const u8 *p, size_t len)
+{
+ if (len < 0x21 || !checksum(p, p[0x05]))
+ return 0;
+
+ printf("PNP BIOS %u.%u present.\n",
+ p[0x04] >> 4, p[0x04] & 0x0F);
+ printf("\tEvent Notification: %s\n",
+ pnp_event_notification(WORD(p + 0x06) & 0x03));
+ if ((WORD(p + 0x06) & 0x03) == 0x01)
+ printf("\tEvent Notification Flag Address: 0x%08X\n",
+ DWORD(p + 0x09));
+ printf("\tReal Mode 16-bit Code Address: %04X:%04X\n",
+ WORD(p + 0x0F), WORD(p + 0x0D));
+ printf("\tReal Mode 16-bit Data Address: %04X:0000\n",
+ WORD(p + 0x1B));
+ printf("\t16-bit Protected Mode Code Address: 0x%08X\n",
+ DWORD(p + 0x13) + WORD(p + 0x11));
+ printf("\t16-bit Protected Mode Data Address: 0x%08X\n",
+ DWORD(p + 0x1D));
+ if (DWORD(p + 0x17) != 0)
+ printf("\tOEM Device Identifier: %c%c%c%02X%02X\n",
+ 0x40 + ((p[0x17] >> 2) & 0x1F),
+ 0x40 + ((p[0x17] & 0x03) << 3) + ((p[0x18] >> 5) & 0x07),
+ 0x40 + (p[0x18] & 0x1F), p[0x19], p[0x20]);
+
+ return 1;
+}
+
+/*
+ * ACPI
+ */
+
+static size_t acpi_length(const u8 *p)
+{
+ return p[15] == 2 ? 36 : 20;
+}
+
+static const char *acpi_revision(u8 code)
+{
+ switch (code)
+ {
+ case 0:
+ return " 1.0";
+ case 2:
+ return " 2.0";
+ default:
+ return "";
+ }
+}
+
+static int acpi_decode(const u8 *p, size_t len)
+{
+ if (len < 20 || !checksum(p, 20))
+ return 0;
+
+ printf("ACPI%s present.\n",
+ acpi_revision(p[15]));
+ printf("\tOEM Identifier: %c%c%c%c%c%c\n",
+ p[9], p[10], p[11], p[12], p[13], p[14]);
+ printf("\tRSD Table 32-bit Address: 0x%08X\n",
+ DWORD(p + 16));
+
+ if (len < 36)
+ return 1;
+
+ if (DWORD(p + 20) > len || !checksum(p, DWORD(p + 20)))
+ return 0;
+
+ if (DWORD(p + 20) < 32) return 1;
+
+ printf("\tXSD Table 64-bit Address: 0x%08X%08X\n",
+ QWORD(p + 24).h, QWORD(p + 24).l);
+
+ return 1;
+}
+
+/*
+ * Sony
+ */
+
+static size_t sony_length(const u8 *p)
+{
+ return p[0x05];
+}
+
+static int sony_decode(const u8 *p, size_t len)
+{
+ if (!checksum(p, len))
+ return 0;
+
+ printf("Sony system detected.\n");
+
+ return 1;
+}
+
+/*
+ * BIOS32
+ */
+
+static size_t bios32_length(const u8 *p)
+{
+ return p[0x09] << 4;
+}
+
+static int bios32_decode(const u8 *p, size_t len)
+{
+ if (len < 0x0A || !checksum(p, p[0x09] << 4))
+ return 0;
+
+ printf("BIOS32 Service Directory present.\n");
+ printf("\tRevision: %u\n",
+ p[0x08]);
+ printf("\tCalling Interface Address: 0x%08X\n",
+ DWORD(p + 0x04));
+
+ return 1;
+}
+
+/*
+ * PIR
+ */
+
+static void pir_irqs(u16 code)
+{
+ if (code == 0)
+ printf(" None");
+ else
+ {
+ u8 i;
+
+ for (i = 0; i < 16; i++)
+ if (code & (1 << i))
+ printf(" %u", i);
+ }
+}
+
+static void pir_slot_number(u8 code)
+{
+ if (code == 0)
+ printf(" on-board");
+ else
+ printf(" slot %u", code);
+}
+
+static size_t pir_length(const u8 *p)
+{
+ return WORD(p + 6);
+}
+
+static void pir_link_bitmap(char letter, const u8 *p)
+{
+ if (p[0] == 0) /* Not connected */
+ return;
+
+ printf("\t\tINT%c#: Link 0x%02x, IRQ Bitmap", letter, p[0]);
+ pir_irqs(WORD(p + 1));
+ printf("\n");
+}
+
+static int pir_decode(const u8 *p, size_t len)
+{
+ int i, n;
+
+ if (len < 32 || !checksum(p, WORD(p + 6)))
+ return 0;
+
+ printf("PCI Interrupt Routing %u.%u present.\n",
+ p[5], p[4]);
+ printf("\tRouter Device: %02x:%02x.%1x\n",
+ p[8], p[9]>>3, p[9] & 0x07);
+ printf("\tExclusive IRQs:");
+ pir_irqs(WORD(p + 10));
+ printf("\n");
+ if (DWORD(p + 12) != 0)
+ printf("\tCompatible Router: %04x:%04x\n",
+ WORD(p + 12), WORD(p + 14));
+ if (DWORD(p + 16) != 0)
+ printf("\tMiniport Data: 0x%08X\n",
+ DWORD(p + 16));
+
+ n = (len - 32) / 16;
+ for (i = 1, p += 32; i <= n; i++, p += 16)
+ {
+ printf("\tDevice: %02x:%02x,", p[0], p[1] >> 3);
+ pir_slot_number(p[14]);
+ printf("\n");
+ if (opt.pir == PIR_FULL)
+ {
+ pir_link_bitmap('A', p + 2);
+ pir_link_bitmap('B', p + 5);
+ pir_link_bitmap('C', p + 8);
+ pir_link_bitmap('D', p + 11);
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Compaq-specific entries
+ */
+
+static size_t compaq_length(const u8 *p)
+{
+ return p[4] * 10 + 5;
+}
+
+static int compaq_decode(const u8 *p, size_t len)
+{
+ unsigned int i;
+ (void) len;
+
+ printf("Compaq-specific entries present.\n");
+
+ /* integrity checking (lack of checksum) */
+ for (i = 0; i < p[4]; i++)
+ {
+ /*
+ * We do not check for truncated entries, because the length
+ * was computed from the number of records in compaq_length
+ * right above, so it can't be wrong.
+ */
+ if (p[5 + i * 10] != '$'
+ || !(p[6 + i * 10] >= 'A' && p[6 + i * 10] <= 'Z')
+ || !(p[7 + i * 10] >= 'A' && p[7 + i * 10] <= 'Z')
+ || !(p[8 + i * 10] >= 'A' && p[8 + i * 10] <= 'Z'))
+ {
+ printf("\t Abnormal entry! Please report. [%02X %02X "
+ "%02X %02X]\n", p[5 + i * 10], p[6 + i * 10],
+ p[7 + i * 10], p[8 + i * 10]);
+ return 0;
+ }
+ }
+
+ for (i = 0; i < p[4]; i++)
+ {
+ printf("\tEntry %u: %c%c%c%c at 0x%08X (%u bytes)\n",
+ i + 1, p[5 + i * 10], p[6 + i * 10], p[7 + i * 10],
+ p[8 + i * 10], DWORD(p + 9 + i * 10),
+ WORD(p + 13 + i * 10));
+ }
+
+ return 1;
+}
+
+/*
+ * VPD (vital product data, IBM-specific)
+ */
+
+static void vpd_print_entry(const char *name, const u8 *p, size_t len)
+{
+ size_t i;
+
+ printf("\t%s: ", name);
+ for (i = 0; i < len; i++)
+ if (p[i] >= 32 && p[i] < 127)
+ printf("%c", p[i]);
+ printf("\n");
+}
+
+static size_t vpd_length(const u8 *p)
+{
+ return p[5];
+}
+
+static int vpd_decode(const u8 *p, size_t len)
+{
+ if (len < 0x30)
+ return 0;
+
+ /* XSeries have longer records. */
+ if (!(len >= 0x45 && checksum(p, len))
+ /* Some Netvista seem to work with this. */
+ && !checksum(p, 0x30)
+ /* The Thinkpad checksum does *not* include the first 13 bytes. */
+ && !checksum(p + 0x0D, 0x30 - 0x0D))
+ return 0;
+
+ printf("VPD present.\n");
+
+ vpd_print_entry("BIOS Build ID", p + 0x0D, 9);
+ vpd_print_entry("Box Serial Number", p + 0x16, 7);
+ vpd_print_entry("Motherboard Serial Number", p + 0x1D, 11);
+ vpd_print_entry("Machine Type/Model", p + 0x28, 7);
+
+ if (len < 0x45)
+ return 1;
+
+ vpd_print_entry("BIOS Release Date", p + 0x30, 8);
+
+ return 1;
+}
+
+/*
+ * Fujitsu application panel
+ */
+
+static size_t fjkeyinf_length(const u8 *p)
+{
+ (void) p;
+ /*
+ * We don't know at this point, it's somewhere between 12 and 32.
+ * So we return the max, it shouldn't hurt.
+ */
+ return 32;
+}
+
+static int fjkeyinf_decode(const u8 *p, size_t len)
+{
+ int i;
+ (void) len;
+
+ printf("Fujitsu application panel present.\n");
+
+ for (i = 0; i < 6; i++)
+ {
+ if (*(p + 8 + i * 4) == 0)
+ return 1;
+ printf("\tDevice %d: type %u, chip %u", i + 1,
+ *(p + 8 + i * 4), *(p + 8 + i * 4 + 2));
+ if (*(p + 8 + i * 4 + 1)) /* Access method */
+ printf(", SMBus address 0x%x",
+ *(p + 8 + i * 4 + 3) >> 1);
+ printf("\n");
+ }
+
+ return 1;
+}
+
+/*
+ * Intel Multiprocessor
+ */
+
+static size_t mp_length(const u8 *p)
+{
+ return 16 * p[8];
+}
+
+static int mp_decode(const u8 *p, size_t len)
+{
+ if (!checksum(p, len))
+ return 0;
+
+ printf("Intel Multiprocessor present.\n");
+ printf("\tSpecification Revision: %s\n",
+ p[9] == 0x01 ? "1.1" : p[9] == 0x04 ? "1.4" : "Invalid");
+ if (p[11])
+ printf("\tDefault Configuration: #%d\n", p[11]);
+ else
+ printf("\tConfiguration Table Address: 0x%08X\n",
+ DWORD(p + 4));
+ printf("\tMode: %s\n", p[12] & (1 << 7) ?
+ "IMCR and PIC" : "Virtual Wire");
+
+ return 1;
+}
+
+/*
+ * Main
+ */
+
+static struct bios_entry bios_entries[] = {
+ { "_SM3_", 0, 0xF0000, 0xFFFFF, smbios3_length, smbios3_decode },
+ { "_SM_", 0, 0xF0000, 0xFFFFF, smbios_length, smbios_decode },
+ { "_DMI_", 0, 0xF0000, 0xFFFFF, dmi_length, dmi_decode },
+ { "_SYSID_", 0, 0xE0000, 0xFFFFF, sysid_length, sysid_decode },
+ { "$PnP", 0, 0xF0000, 0xFFFFF, pnp_length, pnp_decode },
+ { "RSD PTR ", 0, 0xE0000, 0xFFFFF, acpi_length, acpi_decode },
+ { "$SNY", 0, 0xE0000, 0xFFFFF, sony_length, sony_decode },
+ { "_32_", 0, 0xE0000, 0xFFFFF, bios32_length, bios32_decode },
+ { "$PIR", 0, 0xF0000, 0xFFFFF, pir_length, pir_decode },
+ { "32OS", 0, 0xE0000, 0xFFFFF, compaq_length, compaq_decode },
+ { "\252\125VPD", 0, 0xF0000, 0xFFFFF, vpd_length, vpd_decode },
+ { "FJKEYINF", 0, 0xF0000, 0xFFFFF, fjkeyinf_length, fjkeyinf_decode },
+ { "_MP_", 0, 0xE0000, 0xFFFFF, mp_length, mp_decode },
+ { NULL, 0, 0, 0, NULL, NULL }
+};
+
+/* Believe it or not, this is significantly faster than memcmp */
+static int anchor_match(const struct bios_entry *entry, const char *p)
+{
+ size_t i;
+
+ for (i = 0; i < entry->anchor_len; i++)
+ if (entry->anchor[i] != p[i])
+ return 0;
+
+ return 1;
+}
+
+/* Return -1 on error, 0 on success */
+static int parse_command_line(int argc, char * const argv[])
+{
+ int option;
+ const char *optstring = "d:hV";
+ struct option longopts[] = {
+ { "dev-mem", required_argument, NULL, 'd' },
+ { "pir", required_argument, NULL, 'P' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((option = getopt_long(argc, argv, optstring, longopts, NULL)) != -1)
+ switch (option)
+ {
+ case 'd':
+ opt.devmem = optarg;
+ break;
+ case 'P':
+ if (strcmp(optarg, "full") == 0)
+ opt.pir = PIR_FULL;
+ break;
+ case 'h':
+ opt.flags |= FLAG_HELP;
+ break;
+ case 'V':
+ opt.flags |= FLAG_VERSION;
+ break;
+ case '?':
+ return -1;
+ }
+
+ return 0;
+}
+
+static void print_help(void)
+{
+ static const char *help =
+ "Usage: biosdecode [OPTIONS]\n"
+ "Options are:\n"
+ " -d, --dev-mem FILE Read memory from device FILE (default: " DEFAULT_MEM_DEV ")\n"
+ " --pir full Decode the details of the PCI IRQ routing table\n"
+ " -h, --help Display this help text and exit\n"
+ " -V, --version Display the version and exit\n";
+
+ printf("%s", help);
+}
+
+int main(int argc, char * const argv[])
+{
+ u8 *buf;
+ off_t fp;
+ int i;
+
+ if (sizeof(u8) != 1 || sizeof(u16) != 2 || sizeof(u32) != 4)
+ {
+ fprintf(stderr, "%s: compiler incompatibility\n", argv[0]);
+ exit(255);
+ }
+
+ /* Set default option values */
+ opt.devmem = DEFAULT_MEM_DEV;
+ opt.flags = 0;
+
+ if (parse_command_line(argc, argv) < 0)
+ exit(2);
+
+ if (opt.flags & FLAG_HELP)
+ {
+ print_help();
+ return 0;
+ }
+
+ if (opt.flags & FLAG_VERSION)
+ {
+ printf("%s\n", VERSION);
+ return 0;
+ }
+
+ printf("# biosdecode %s\n", VERSION);
+
+ if ((buf = mem_chunk(0xE0000, 0x20000, opt.devmem)) == NULL)
+ exit(1);
+
+ /* Compute anchor lengths once and for all */
+ for (i = 0; bios_entries[i].anchor != NULL; i++)
+ bios_entries[i].anchor_len = strlen(bios_entries[i].anchor);
+
+ for (fp = 0xE0000; fp <= 0xFFFF0; fp += 16)
+ {
+ u8 *p = buf + fp - 0xE0000;
+
+ for (i = 0; bios_entries[i].anchor != NULL; i++)
+ {
+ if (anchor_match(&bios_entries[i], (char *)p)
+ && fp >= bios_entries[i].low_address
+ && fp < bios_entries[i].high_address)
+ {
+ off_t len = bios_entries[i].length(p);
+
+ if (fp + len - 1 <= bios_entries[i].high_address)
+ {
+ if (bios_entries[i].decode(p, len))
+ {
+ fp += (((len - 1) >> 4) << 4);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ free(buf);
+
+ return 0;
+}
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..4237355
--- /dev/null
+++ b/config.h
@@ -0,0 +1,34 @@
+/*
+ * Configuration
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/* Default memory device file */
+#if defined(__BEOS__) || defined(__HAIKU__)
+#define DEFAULT_MEM_DEV "/dev/misc/mem"
+#else
+#ifdef __sun
+#define DEFAULT_MEM_DEV "/dev/xsvc"
+#else
+#define DEFAULT_MEM_DEV "/dev/mem"
+#endif
+#endif
+
+/* Use mmap or not */
+#ifndef __BEOS__
+#define USE_MMAP
+#endif
+
+/* Use memory alignment workaround or not */
+#ifdef __ia64__
+#define ALIGNMENT_WORKAROUND
+#endif
+
+/* Avoid unaligned memcpy on /dev/mem */
+#ifdef __aarch64__
+#define USE_SLOW_MEMCPY
+#endif
+
+#endif
diff --git a/dmidecode.c b/dmidecode.c
new file mode 100644
index 0000000..54f59c1
--- /dev/null
+++ b/dmidecode.c
@@ -0,0 +1,6177 @@
+/*
+ * DMI Decode
+ *
+ * Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
+ * Copyright (C) 2002-2020 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open unpatent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * Unless specified otherwise, all references are aimed at the "System
+ * Management BIOS Reference Specification, Version 3.2.0" document,
+ * available from http://www.dmtf.org/standards/smbios.
+ *
+ * Note to contributors:
+ * Please reference every value you add or modify, especially if the
+ * information does not come from the above mentioned specification.
+ *
+ * Additional references:
+ * - Intel AP-485 revision 36
+ * "Intel Processor Identification and the CPUID Instruction"
+ * http://www.intel.com/support/processors/sb/cs-009861.htm
+ * - DMTF Common Information Model
+ * CIM Schema version 2.19.1
+ * http://www.dmtf.org/standards/cim/
+ * - IPMI 2.0 revision 1.0
+ * "Intelligent Platform Management Interface Specification"
+ * http://developer.intel.com/design/servers/ipmi/spec.htm
+ * - AMD publication #25481 revision 2.28
+ * "CPUID Specification"
+ * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
+ * - BIOS Integrity Services Application Programming Interface version 1.0
+ * http://www.intel.com/design/archives/wfm/downloads/bisspec.htm
+ * - DMTF DSP0239 version 1.1.0
+ * "Management Component Transport Protocol (MCTP) IDs and Codes"
+ * http://www.dmtf.org/standards/pmci
+ * - "TPM Main, Part 2 TPM Structures"
+ * Specification version 1.2, level 2, revision 116
+ * https://trustedcomputinggroup.org/tpm-main-specification/
+ * - "PC Client Platform TPM Profile (PTP) Specification"
+ * Family "2.0", Level 00, Revision 00.43, January 26, 2015
+ * https://trustedcomputinggroup.org/pc-client-platform-tpm-profile-ptp-specification/
+ * - "RedFish Host Interface Specification" (DMTF DSP0270)
+ * https://www.dmtf.org/sites/default/files/DSP0270_1.0.1.pdf
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#ifdef __FreeBSD__
+#include <errno.h>
+#include <kenv.h>
+#endif
+
+#include "version.h"
+#include "config.h"
+#include "types.h"
+#include "util.h"
+#include "dmidecode.h"
+#include "dmiopt.h"
+#include "dmioem.h"
+#include "dmioutput.h"
+
+#define out_of_spec "<OUT OF SPEC>"
+static const char *bad_index = "<BAD INDEX>";
+
+enum cpuid_type cpuid_type = cpuid_none;
+
+#define SUPPORTED_SMBIOS_VER 0x030500
+
+#define FLAG_NO_FILE_OFFSET (1 << 0)
+#define FLAG_STOP_AT_EOT (1 << 1)
+
+#define SYS_FIRMWARE_DIR "/sys/firmware/dmi/tables"
+#define SYS_ENTRY_FILE SYS_FIRMWARE_DIR "/smbios_entry_point"
+#define SYS_TABLE_FILE SYS_FIRMWARE_DIR "/DMI"
+
+/*
+ * Type-independant Stuff
+ */
+
+/* Returns 1 if the buffer contains only printable ASCII characters */
+int is_printable(const u8 *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (data[i] < 32 || data[i] >= 127)
+ return 0;
+
+ return 1;
+}
+
+/* Replace non-ASCII characters with dots */
+static void ascii_filter(char *bp, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ if (bp[i] < 32 || bp[i] >= 127)
+ bp[i] = '.';
+}
+
+static char *_dmi_string(const struct dmi_header *dm, u8 s, int filter)
+{
+ char *bp = (char *)dm->data;
+
+ bp += dm->length;
+ while (s > 1 && *bp)
+ {
+ bp += strlen(bp);
+ bp++;
+ s--;
+ }
+
+ if (!*bp)
+ return NULL;
+
+ if (filter)
+ ascii_filter(bp, strlen(bp));
+
+ return bp;
+}
+
+const char *dmi_string(const struct dmi_header *dm, u8 s)
+{
+ char *bp;
+
+ if (s == 0)
+ return "Not Specified";
+
+ bp = _dmi_string(dm, s, 1);
+ if (bp == NULL)
+ return bad_index;
+
+ return bp;
+}
+
+static const char *dmi_smbios_structure_type(u8 code)
+{
+ static const char *type[] = {
+ "BIOS", /* 0 */
+ "System",
+ "Base Board",
+ "Chassis",
+ "Processor",
+ "Memory Controller",
+ "Memory Module",
+ "Cache",
+ "Port Connector",
+ "System Slots",
+ "On Board Devices",
+ "OEM Strings",
+ "System Configuration Options",
+ "BIOS Language",
+ "Group Associations",
+ "System Event Log",
+ "Physical Memory Array",
+ "Memory Device",
+ "32-bit Memory Error",
+ "Memory Array Mapped Address",
+ "Memory Device Mapped Address",
+ "Built-in Pointing Device",
+ "Portable Battery",
+ "System Reset",
+ "Hardware Security",
+ "System Power Controls",
+ "Voltage Probe",
+ "Cooling Device",
+ "Temperature Probe",
+ "Electrical Current Probe",
+ "Out-of-band Remote Access",
+ "Boot Integrity Services",
+ "System Boot",
+ "64-bit Memory Error",
+ "Management Device",
+ "Management Device Component",
+ "Management Device Threshold Data",
+ "Memory Channel",
+ "IPMI Device",
+ "Power Supply",
+ "Additional Information",
+ "Onboard Device",
+ "Management Controller Host Interface",
+ "TPM Device",
+ "Processor",
+ "Firmware",
+ "String Property" /* 46 */
+ };
+
+ if (code >= 128)
+ return "OEM-specific";
+ if (code <= 46)
+ return type[code];
+ return out_of_spec;
+}
+
+static int dmi_bcd_range(u8 value, u8 low, u8 high)
+{
+ if (value > 0x99 || (value & 0x0F) > 0x09)
+ return 0;
+ if (value < low || value > high)
+ return 0;
+ return 1;
+}
+
+static void dmi_dump(const struct dmi_header *h)
+{
+ static char raw_data[48];
+ int row, i;
+ unsigned int off;
+ char *s;
+
+ pr_list_start("Header and Data", NULL);
+ for (row = 0; row < ((h->length - 1) >> 4) + 1; row++)
+ {
+ off = 0;
+ for (i = 0; i < 16 && i < h->length - (row << 4); i++)
+ off += sprintf(raw_data + off, i ? " %02X" : "%02X",
+ (h->data)[(row << 4) + i]);
+ pr_list_item(raw_data);
+ }
+ pr_list_end();
+
+ if ((h->data)[h->length] || (h->data)[h->length + 1])
+ {
+ pr_list_start("Strings", NULL);
+ i = 1;
+ while ((s = _dmi_string(h, i++, !(opt.flags & FLAG_DUMP))))
+ {
+ if (opt.flags & FLAG_DUMP)
+ {
+ int j, l = strlen(s) + 1;
+
+ for (row = 0; row < ((l - 1) >> 4) + 1; row++)
+ {
+ off = 0;
+ for (j = 0; j < 16 && j < l - (row << 4); j++)
+ off += sprintf(raw_data + off,
+ j ? " %02X" : "%02X",
+ (unsigned char)s[(row << 4) + j]);
+ pr_list_item(raw_data);
+ }
+ /* String isn't filtered yet so do it now */
+ ascii_filter(s, l - 1);
+ }
+ pr_list_item("%s", s);
+ }
+ pr_list_end();
+ }
+}
+
+/* shift is 0 if the value is in bytes, 1 if it is in kilobytes */
+void dmi_print_memory_size(const char *attr, u64 code, int shift)
+{
+ unsigned long capacity;
+ u16 split[7];
+ static const char *unit[8] = {
+ "bytes", "kB", "MB", "GB", "TB", "PB", "EB", "ZB"
+ };
+ int i;
+
+ /*
+ * We split the overall size in powers of thousand: EB, PB, TB, GB,
+ * MB, kB and B. In practice, it is expected that only one or two
+ * (consecutive) of these will be non-zero.
+ */
+ split[0] = code.l & 0x3FFUL;
+ split[1] = (code.l >> 10) & 0x3FFUL;
+ split[2] = (code.l >> 20) & 0x3FFUL;
+ split[3] = ((code.h << 2) & 0x3FCUL) | (code.l >> 30);
+ split[4] = (code.h >> 8) & 0x3FFUL;
+ split[5] = (code.h >> 18) & 0x3FFUL;
+ split[6] = code.h >> 28;
+
+ /*
+ * Now we find the highest unit with a non-zero value. If the following
+ * is also non-zero, we use that as our base. If the following is zero,
+ * we simply display the highest unit.
+ */
+ for (i = 6; i > 0; i--)
+ {
+ if (split[i])
+ break;
+ }
+ if (i > 0 && split[i - 1])
+ {
+ i--;
+ capacity = split[i] + (split[i + 1] << 10);
+ }
+ else
+ capacity = split[i];
+
+ pr_attr(attr, "%lu %s", capacity, unit[i + shift]);
+}
+
+/*
+ * 7.1 BIOS Information (Type 0)
+ */
+
+static void dmi_bios_runtime_size(u32 code)
+{
+ const char *format;
+
+ if (code & 0x000003FF)
+ {
+ format = "%u bytes";
+ }
+ else
+ {
+ format = "%u kB";
+ code >>= 10;
+ }
+
+ pr_attr("Runtime Size", format, code);
+}
+
+static void dmi_bios_rom_size(u8 code1, u16 code2)
+{
+ static const char *unit[4] = {
+ "MB", "GB", out_of_spec, out_of_spec
+ };
+
+ if (code1 != 0xFF)
+ {
+ u64 s = { .l = (code1 + 1) << 6 };
+ dmi_print_memory_size("ROM Size", s, 1);
+ }
+ else
+ pr_attr("ROM Size", "%u %s", code2 & 0x3FFF, unit[code2 >> 14]);
+}
+
+static void dmi_bios_characteristics(u64 code)
+{
+ /* 7.1.1 */
+ static const char *characteristics[] = {
+ "BIOS characteristics not supported", /* 3 */
+ "ISA is supported",
+ "MCA is supported",
+ "EISA is supported",
+ "PCI is supported",
+ "PC Card (PCMCIA) is supported",
+ "PNP is supported",
+ "APM is supported",
+ "BIOS is upgradeable",
+ "BIOS shadowing is allowed",
+ "VLB is supported",
+ "ESCD support is available",
+ "Boot from CD is supported",
+ "Selectable boot is supported",
+ "BIOS ROM is socketed",
+ "Boot from PC Card (PCMCIA) is supported",
+ "EDD is supported",
+ "Japanese floppy for NEC 9800 1.2 MB is supported (int 13h)",
+ "Japanese floppy for Toshiba 1.2 MB is supported (int 13h)",
+ "5.25\"/360 kB floppy services are supported (int 13h)",
+ "5.25\"/1.2 MB floppy services are supported (int 13h)",
+ "3.5\"/720 kB floppy services are supported (int 13h)",
+ "3.5\"/2.88 MB floppy services are supported (int 13h)",
+ "Print screen service is supported (int 5h)",
+ "8042 keyboard services are supported (int 9h)",
+ "Serial services are supported (int 14h)",
+ "Printer services are supported (int 17h)",
+ "CGA/mono video services are supported (int 10h)",
+ "NEC PC-98" /* 31 */
+ };
+ int i;
+
+ /*
+ * This isn't very clear what this bit is supposed to mean
+ */
+ if (code.l & (1 << 3))
+ {
+ pr_list_item("%s", characteristics[0]);
+ return;
+ }
+
+ for (i = 4; i <= 31; i++)
+ if (code.l & (1 << i))
+ pr_list_item("%s", characteristics[i - 3]);
+}
+
+static void dmi_bios_characteristics_x1(u8 code)
+{
+ /* 7.1.2.1 */
+ static const char *characteristics[] = {
+ "ACPI is supported", /* 0 */
+ "USB legacy is supported",
+ "AGP is supported",
+ "I2O boot is supported",
+ "LS-120 boot is supported",
+ "ATAPI Zip drive boot is supported",
+ "IEEE 1394 boot is supported",
+ "Smart battery is supported" /* 7 */
+ };
+ int i;
+
+ for (i = 0; i <= 7; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", characteristics[i]);
+}
+
+static void dmi_bios_characteristics_x2(u8 code)
+{
+ /* 37.1.2.2 */
+ static const char *characteristics[] = {
+ "BIOS boot specification is supported", /* 0 */
+ "Function key-initiated network boot is supported",
+ "Targeted content distribution is supported",
+ "UEFI is supported",
+ "System is a virtual machine",
+ "Manufacturing mode is supported",
+ "Manufacturing mode is enabled" /* 6 */
+ };
+ int i;
+
+ for (i = 0; i <= 6; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", characteristics[i]);
+}
+
+/*
+ * 7.2 System Information (Type 1)
+ */
+
+static void dmi_system_uuid(void (*print_cb)(const char *name, const char *format, ...),
+ const char *attr, const u8 *p, u16 ver)
+{
+ int only0xFF = 1, only0x00 = 1;
+ int i;
+
+ for (i = 0; i < 16 && (only0x00 || only0xFF); i++)
+ {
+ if (p[i] != 0x00) only0x00 = 0;
+ if (p[i] != 0xFF) only0xFF = 0;
+ }
+
+ if (only0xFF)
+ {
+ if (print_cb)
+ print_cb(attr, "Not Present");
+ else
+ printf("Not Present\n");
+ return;
+ }
+ if (only0x00)
+ {
+ if (print_cb)
+ print_cb(attr, "Not Settable");
+ else
+ printf("Not Settable\n");
+ return;
+ }
+
+ /*
+ * As of version 2.6 of the SMBIOS specification, the first 3
+ * fields of the UUID are supposed to be encoded on little-endian.
+ * The specification says that this is the defacto standard,
+ * however I've seen systems following RFC 4122 instead and use
+ * network byte order, so I am reluctant to apply the byte-swapping
+ * for older versions.
+ */
+ if (ver >= 0x0206)
+ {
+ if (print_cb)
+ print_cb(attr,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ else
+ printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ }
+ else
+ {
+ if (print_cb)
+ print_cb(attr,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ else
+ printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ }
+}
+
+static const char *dmi_system_wake_up_type(u8 code)
+{
+ /* 7.2.2 */
+ static const char *type[] = {
+ "Reserved", /* 0x00 */
+ "Other",
+ "Unknown",
+ "APM Timer",
+ "Modem Ring",
+ "LAN Remote",
+ "Power Switch",
+ "PCI PME#",
+ "AC Power Restored" /* 0x08 */
+ };
+
+ if (code <= 0x08)
+ return type[code];
+ return out_of_spec;
+}
+
+/*
+ * 7.3 Base Board Information (Type 2)
+ */
+
+static void dmi_base_board_features(u8 code)
+{
+ /* 7.3.1 */
+ static const char *features[] = {
+ "Board is a hosting board", /* 0 */
+ "Board requires at least one daughter board",
+ "Board is removable",
+ "Board is replaceable",
+ "Board is hot swappable" /* 4 */
+ };
+
+ if ((code & 0x1F) == 0)
+ pr_list_start("Features", "%s", "None");
+ else
+ {
+ int i;
+
+ pr_list_start("Features", NULL);
+ for (i = 0; i <= 4; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", features[i]);
+ }
+ pr_list_end();
+}
+
+static const char *dmi_base_board_type(u8 code)
+{
+ /* 7.3.2 */
+ static const char *type[] = {
+ "Unknown", /* 0x01 */
+ "Other",
+ "Server Blade",
+ "Connectivity Switch",
+ "System Management Module",
+ "Processor Module",
+ "I/O Module",
+ "Memory Module",
+ "Daughter Board",
+ "Motherboard",
+ "Processor+Memory Module",
+ "Processor+I/O Module",
+ "Interconnect Board" /* 0x0D */
+ };
+
+ if (code >= 0x01 && code <= 0x0D)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_base_board_handles(u8 count, const u8 *p)
+{
+ int i;
+
+ pr_list_start("Contained Object Handles", "%u", count);
+ for (i = 0; i < count; i++)
+ pr_list_item("0x%04X", WORD(p + sizeof(u16) * i));
+ pr_list_end();
+}
+
+/*
+ * 7.4 Chassis Information (Type 3)
+ */
+
+static const char *dmi_chassis_type(u8 code)
+{
+ /* 7.4.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Desktop",
+ "Low Profile Desktop",
+ "Pizza Box",
+ "Mini Tower",
+ "Tower",
+ "Portable",
+ "Laptop",
+ "Notebook",
+ "Hand Held",
+ "Docking Station",
+ "All In One",
+ "Sub Notebook",
+ "Space-saving",
+ "Lunch Box",
+ "Main Server Chassis", /* CIM_Chassis.ChassisPackageType says "Main System Chassis" */
+ "Expansion Chassis",
+ "Sub Chassis",
+ "Bus Expansion Chassis",
+ "Peripheral Chassis",
+ "RAID Chassis",
+ "Rack Mount Chassis",
+ "Sealed-case PC",
+ "Multi-system",
+ "CompactPCI",
+ "AdvancedTCA",
+ "Blade",
+ "Blade Enclosing",
+ "Tablet",
+ "Convertible",
+ "Detachable",
+ "IoT Gateway",
+ "Embedded PC",
+ "Mini PC",
+ "Stick PC" /* 0x24 */
+ };
+
+ code &= 0x7F; /* bits 6:0 are chassis type, 7th bit is the lock bit */
+
+ if (code >= 0x01 && code <= 0x24)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_chassis_lock(u8 code)
+{
+ static const char *lock[] = {
+ "Not Present", /* 0x00 */
+ "Present" /* 0x01 */
+ };
+
+ return lock[code];
+}
+
+static const char *dmi_chassis_state(u8 code)
+{
+ /* 7.4.2 */
+ static const char *state[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Safe",
+ "Warning",
+ "Critical",
+ "Non-recoverable" /* 0x06 */
+ };
+
+ if (code >= 0x01 && code <= 0x06)
+ return state[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_chassis_security_status(u8 code)
+{
+ /* 7.4.3 */
+ static const char *status[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "None",
+ "External Interface Locked Out",
+ "External Interface Enabled" /* 0x05 */
+ };
+
+ if (code >= 0x01 && code <= 0x05)
+ return status[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_chassis_height(u8 code)
+{
+ if (code == 0x00)
+ pr_attr("Height", "Unspecified");
+ else
+ pr_attr("Height", "%u U", code);
+}
+
+static void dmi_chassis_power_cords(u8 code)
+{
+ if (code == 0x00)
+ pr_attr("Number Of Power Cords", "Unspecified");
+ else
+ pr_attr("Number Of Power Cords", "%u", code);
+}
+
+static void dmi_chassis_elements(u8 count, u8 len, const u8 *p)
+{
+ int i;
+
+ pr_list_start("Contained Elements", "%u", count);
+ for (i = 0; i < count; i++)
+ {
+ if (len >= 0x03)
+ {
+ const char *type;
+
+ type = (p[i * len] & 0x80) ?
+ dmi_smbios_structure_type(p[i * len] & 0x7F) :
+ dmi_base_board_type(p[i * len] & 0x7F);
+
+ if (p[1 + i * len] == p[2 + i * len])
+ pr_list_item("%s (%u)", type, p[1 + i * len]);
+ else
+ pr_list_item("%s (%u-%u)", type, p[1 + i * len],
+ p[2 + i * len]);
+ }
+ }
+ pr_list_end();
+}
+
+/*
+ * 7.5 Processor Information (Type 4)
+ */
+
+static const char *dmi_processor_type(u8 code)
+{
+ /* 7.5.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Central Processor",
+ "Math Processor",
+ "DSP Processor",
+ "Video Processor" /* 0x06 */
+ };
+
+ if (code >= 0x01 && code <= 0x06)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_processor_family(const struct dmi_header *h, u16 ver)
+{
+ const u8 *data = h->data;
+ unsigned int i, low, high;
+ u16 code;
+
+ /* 7.5.2 */
+ static struct {
+ int value;
+ const char *name;
+ } family2[] = {
+ { 0x01, "Other" },
+ { 0x02, "Unknown" },
+ { 0x03, "8086" },
+ { 0x04, "80286" },
+ { 0x05, "80386" },
+ { 0x06, "80486" },
+ { 0x07, "8087" },
+ { 0x08, "80287" },
+ { 0x09, "80387" },
+ { 0x0A, "80487" },
+ { 0x0B, "Pentium" },
+ { 0x0C, "Pentium Pro" },
+ { 0x0D, "Pentium II" },
+ { 0x0E, "Pentium MMX" },
+ { 0x0F, "Celeron" },
+ { 0x10, "Pentium II Xeon" },
+ { 0x11, "Pentium III" },
+ { 0x12, "M1" },
+ { 0x13, "M2" },
+ { 0x14, "Celeron M" },
+ { 0x15, "Pentium 4 HT" },
+
+ { 0x18, "Duron" },
+ { 0x19, "K5" },
+ { 0x1A, "K6" },
+ { 0x1B, "K6-2" },
+ { 0x1C, "K6-3" },
+ { 0x1D, "Athlon" },
+ { 0x1E, "AMD29000" },
+ { 0x1F, "K6-2+" },
+ { 0x20, "Power PC" },
+ { 0x21, "Power PC 601" },
+ { 0x22, "Power PC 603" },
+ { 0x23, "Power PC 603+" },
+ { 0x24, "Power PC 604" },
+ { 0x25, "Power PC 620" },
+ { 0x26, "Power PC x704" },
+ { 0x27, "Power PC 750" },
+ { 0x28, "Core Duo" },
+ { 0x29, "Core Duo Mobile" },
+ { 0x2A, "Core Solo Mobile" },
+ { 0x2B, "Atom" },
+ { 0x2C, "Core M" },
+ { 0x2D, "Core m3" },
+ { 0x2E, "Core m5" },
+ { 0x2F, "Core m7" },
+ { 0x30, "Alpha" },
+ { 0x31, "Alpha 21064" },
+ { 0x32, "Alpha 21066" },
+ { 0x33, "Alpha 21164" },
+ { 0x34, "Alpha 21164PC" },
+ { 0x35, "Alpha 21164a" },
+ { 0x36, "Alpha 21264" },
+ { 0x37, "Alpha 21364" },
+ { 0x38, "Turion II Ultra Dual-Core Mobile M" },
+ { 0x39, "Turion II Dual-Core Mobile M" },
+ { 0x3A, "Athlon II Dual-Core M" },
+ { 0x3B, "Opteron 6100" },
+ { 0x3C, "Opteron 4100" },
+ { 0x3D, "Opteron 6200" },
+ { 0x3E, "Opteron 4200" },
+ { 0x3F, "FX" },
+ { 0x40, "MIPS" },
+ { 0x41, "MIPS R4000" },
+ { 0x42, "MIPS R4200" },
+ { 0x43, "MIPS R4400" },
+ { 0x44, "MIPS R4600" },
+ { 0x45, "MIPS R10000" },
+ { 0x46, "C-Series" },
+ { 0x47, "E-Series" },
+ { 0x48, "A-Series" },
+ { 0x49, "G-Series" },
+ { 0x4A, "Z-Series" },
+ { 0x4B, "R-Series" },
+ { 0x4C, "Opteron 4300" },
+ { 0x4D, "Opteron 6300" },
+ { 0x4E, "Opteron 3300" },
+ { 0x4F, "FirePro" },
+ { 0x50, "SPARC" },
+ { 0x51, "SuperSPARC" },
+ { 0x52, "MicroSPARC II" },
+ { 0x53, "MicroSPARC IIep" },
+ { 0x54, "UltraSPARC" },
+ { 0x55, "UltraSPARC II" },
+ { 0x56, "UltraSPARC IIi" },
+ { 0x57, "UltraSPARC III" },
+ { 0x58, "UltraSPARC IIIi" },
+
+ { 0x60, "68040" },
+ { 0x61, "68xxx" },
+ { 0x62, "68000" },
+ { 0x63, "68010" },
+ { 0x64, "68020" },
+ { 0x65, "68030" },
+ { 0x66, "Athlon X4" },
+ { 0x67, "Opteron X1000" },
+ { 0x68, "Opteron X2000" },
+ { 0x69, "Opteron A-Series" },
+ { 0x6A, "Opteron X3000" },
+ { 0x6B, "Zen" },
+
+ { 0x70, "Hobbit" },
+
+ { 0x78, "Crusoe TM5000" },
+ { 0x79, "Crusoe TM3000" },
+ { 0x7A, "Efficeon TM8000" },
+
+ { 0x80, "Weitek" },
+
+ { 0x82, "Itanium" },
+ { 0x83, "Athlon 64" },
+ { 0x84, "Opteron" },
+ { 0x85, "Sempron" },
+ { 0x86, "Turion 64" },
+ { 0x87, "Dual-Core Opteron" },
+ { 0x88, "Athlon 64 X2" },
+ { 0x89, "Turion 64 X2" },
+ { 0x8A, "Quad-Core Opteron" },
+ { 0x8B, "Third-Generation Opteron" },
+ { 0x8C, "Phenom FX" },
+ { 0x8D, "Phenom X4" },
+ { 0x8E, "Phenom X2" },
+ { 0x8F, "Athlon X2" },
+ { 0x90, "PA-RISC" },
+ { 0x91, "PA-RISC 8500" },
+ { 0x92, "PA-RISC 8000" },
+ { 0x93, "PA-RISC 7300LC" },
+ { 0x94, "PA-RISC 7200" },
+ { 0x95, "PA-RISC 7100LC" },
+ { 0x96, "PA-RISC 7100" },
+
+ { 0xA0, "V30" },
+ { 0xA1, "Quad-Core Xeon 3200" },
+ { 0xA2, "Dual-Core Xeon 3000" },
+ { 0xA3, "Quad-Core Xeon 5300" },
+ { 0xA4, "Dual-Core Xeon 5100" },
+ { 0xA5, "Dual-Core Xeon 5000" },
+ { 0xA6, "Dual-Core Xeon LV" },
+ { 0xA7, "Dual-Core Xeon ULV" },
+ { 0xA8, "Dual-Core Xeon 7100" },
+ { 0xA9, "Quad-Core Xeon 5400" },
+ { 0xAA, "Quad-Core Xeon" },
+ { 0xAB, "Dual-Core Xeon 5200" },
+ { 0xAC, "Dual-Core Xeon 7200" },
+ { 0xAD, "Quad-Core Xeon 7300" },
+ { 0xAE, "Quad-Core Xeon 7400" },
+ { 0xAF, "Multi-Core Xeon 7400" },
+ { 0xB0, "Pentium III Xeon" },
+ { 0xB1, "Pentium III Speedstep" },
+ { 0xB2, "Pentium 4" },
+ { 0xB3, "Xeon" },
+ { 0xB4, "AS400" },
+ { 0xB5, "Xeon MP" },
+ { 0xB6, "Athlon XP" },
+ { 0xB7, "Athlon MP" },
+ { 0xB8, "Itanium 2" },
+ { 0xB9, "Pentium M" },
+ { 0xBA, "Celeron D" },
+ { 0xBB, "Pentium D" },
+ { 0xBC, "Pentium EE" },
+ { 0xBD, "Core Solo" },
+ /* 0xBE handled as a special case */
+ { 0xBF, "Core 2 Duo" },
+ { 0xC0, "Core 2 Solo" },
+ { 0xC1, "Core 2 Extreme" },
+ { 0xC2, "Core 2 Quad" },
+ { 0xC3, "Core 2 Extreme Mobile" },
+ { 0xC4, "Core 2 Duo Mobile" },
+ { 0xC5, "Core 2 Solo Mobile" },
+ { 0xC6, "Core i7" },
+ { 0xC7, "Dual-Core Celeron" },
+ { 0xC8, "IBM390" },
+ { 0xC9, "G4" },
+ { 0xCA, "G5" },
+ { 0xCB, "ESA/390 G6" },
+ { 0xCC, "z/Architecture" },
+ { 0xCD, "Core i5" },
+ { 0xCE, "Core i3" },
+ { 0xCF, "Core i9" },
+
+ { 0xD2, "C7-M" },
+ { 0xD3, "C7-D" },
+ { 0xD4, "C7" },
+ { 0xD5, "Eden" },
+ { 0xD6, "Multi-Core Xeon" },
+ { 0xD7, "Dual-Core Xeon 3xxx" },
+ { 0xD8, "Quad-Core Xeon 3xxx" },
+ { 0xD9, "Nano" },
+ { 0xDA, "Dual-Core Xeon 5xxx" },
+ { 0xDB, "Quad-Core Xeon 5xxx" },
+
+ { 0xDD, "Dual-Core Xeon 7xxx" },
+ { 0xDE, "Quad-Core Xeon 7xxx" },
+ { 0xDF, "Multi-Core Xeon 7xxx" },
+ { 0xE0, "Multi-Core Xeon 3400" },
+
+ { 0xE4, "Opteron 3000" },
+ { 0xE5, "Sempron II" },
+ { 0xE6, "Embedded Opteron Quad-Core" },
+ { 0xE7, "Phenom Triple-Core" },
+ { 0xE8, "Turion Ultra Dual-Core Mobile" },
+ { 0xE9, "Turion Dual-Core Mobile" },
+ { 0xEA, "Athlon Dual-Core" },
+ { 0xEB, "Sempron SI" },
+ { 0xEC, "Phenom II" },
+ { 0xED, "Athlon II" },
+ { 0xEE, "Six-Core Opteron" },
+ { 0xEF, "Sempron M" },
+
+ { 0xFA, "i860" },
+ { 0xFB, "i960" },
+
+ { 0x100, "ARMv7" },
+ { 0x101, "ARMv8" },
+ { 0x104, "SH-3" },
+ { 0x105, "SH-4" },
+ { 0x118, "ARM" },
+ { 0x119, "StrongARM" },
+ { 0x12C, "6x86" },
+ { 0x12D, "MediaGX" },
+ { 0x12E, "MII" },
+ { 0x140, "WinChip" },
+ { 0x15E, "DSP" },
+ { 0x1F4, "Video Processor" },
+
+ { 0x200, "RV32" },
+ { 0x201, "RV64" },
+ { 0x202, "RV128" },
+ };
+ /*
+ * Note to developers: when adding entries to this list, check if
+ * function dmi_processor_id below needs updating too.
+ */
+
+ /* Special case for ambiguous value 0x30 (SMBIOS 2.0 only) */
+ if (ver == 0x0200 && data[0x06] == 0x30 && h->length >= 0x08)
+ {
+ const char *manufacturer = dmi_string(h, data[0x07]);
+
+ if (strstr(manufacturer, "Intel") != NULL
+ || strncasecmp(manufacturer, "Intel", 5) == 0)
+ return "Pentium Pro";
+ }
+
+ code = (data[0x06] == 0xFE && h->length >= 0x2A) ?
+ WORD(data + 0x28) : data[0x06];
+
+ /* Special case for ambiguous value 0xBE */
+ if (code == 0xBE)
+ {
+ if (h->length >= 0x08)
+ {
+ const char *manufacturer = dmi_string(h, data[0x07]);
+
+ /* Best bet based on manufacturer string */
+ if (strstr(manufacturer, "Intel") != NULL
+ || strncasecmp(manufacturer, "Intel", 5) == 0)
+ return "Core 2";
+ if (strstr(manufacturer, "AMD") != NULL
+ || strncasecmp(manufacturer, "AMD", 3) == 0)
+ return "K7";
+ }
+
+ return "Core 2 or K7";
+ }
+
+ /* Perform a binary search */
+ low = 0;
+ high = ARRAY_SIZE(family2) - 1;
+
+ while (1)
+ {
+ i = (low + high) / 2;
+ if (family2[i].value == code)
+ return family2[i].name;
+ if (low == high) /* Not found */
+ return out_of_spec;
+
+ if (code < family2[i].value)
+ high = i;
+ else
+ low = i + 1;
+ }
+}
+
+static enum cpuid_type dmi_get_cpuid_type(const struct dmi_header *h)
+{
+ const u8 *data = h->data;
+ const u8 *p = data + 0x08;
+ u16 type;
+
+ type = (data[0x06] == 0xFE && h->length >= 0x2A) ?
+ WORD(data + 0x28) : data[0x06];
+
+ if (type == 0x05) /* 80386 */
+ {
+ return cpuid_80386;
+ }
+ else if (type == 0x06) /* 80486 */
+ {
+ u16 dx = WORD(p);
+ /*
+ * Not all 80486 CPU support the CPUID instruction, we have to find
+ * whether the one we have here does or not. Note that this trick
+ * works only because we know that 80486 must be little-endian.
+ */
+ if ((dx & 0x0F00) == 0x0400
+ && ((dx & 0x00F0) == 0x0040 || (dx & 0x00F0) >= 0x0070)
+ && ((dx & 0x000F) >= 0x0003))
+ return cpuid_x86_intel;
+ else
+ return cpuid_80486;
+ }
+ else if ((type >= 0x100 && type <= 0x101) /* ARM */
+ || (type >= 0x118 && type <= 0x119)) /* ARM */
+ {
+ /*
+ * The field's format depends on the processor's support of
+ * the SMCCC_ARCH_SOC_ID architectural call. Software can determine
+ * the support for SoC ID by examining the Processor Characteristics field
+ * for "Arm64 SoC ID" bit.
+ */
+ if (h->length >= 0x28
+ && (WORD(data + 0x26) & (1 << 9)))
+ return cpuid_arm_soc_id;
+ else
+ return cpuid_arm_legacy;
+ }
+ else if ((type >= 0x0B && type <= 0x15) /* Intel, Cyrix */
+ || (type >= 0x28 && type <= 0x2F) /* Intel */
+ || (type >= 0xA1 && type <= 0xB3) /* Intel */
+ || type == 0xB5 /* Intel */
+ || (type >= 0xB9 && type <= 0xC7) /* Intel */
+ || (type >= 0xCD && type <= 0xCF) /* Intel */
+ || (type >= 0xD2 && type <= 0xDB) /* VIA, Intel */
+ || (type >= 0xDD && type <= 0xE0)) /* Intel */
+ return cpuid_x86_intel;
+ else if ((type >= 0x18 && type <= 0x1D) /* AMD */
+ || type == 0x1F /* AMD */
+ || (type >= 0x38 && type <= 0x3F) /* AMD */
+ || (type >= 0x46 && type <= 0x4F) /* AMD */
+ || (type >= 0x66 && type <= 0x6B) /* AMD */
+ || (type >= 0x83 && type <= 0x8F) /* AMD */
+ || (type >= 0xB6 && type <= 0xB7) /* AMD */
+ || (type >= 0xE4 && type <= 0xEF)) /* AMD */
+ return cpuid_x86_amd;
+
+ /* neither X86 nor ARM */
+ return cpuid_none;
+}
+
+void dmi_print_cpuid(void (*print_cb)(const char *name, const char *format, ...),
+ const char *label, enum cpuid_type sig, const u8 *p)
+{
+ u32 eax, midr, jep106, soc_revision;
+ u16 dx;
+
+ switch (sig)
+ {
+ case cpuid_80386:
+ dx = WORD(p);
+ /*
+ * 80386 have a different signature.
+ */
+ print_cb(label,
+ "Type %u, Family %u, Major Stepping %u, Minor Stepping %u",
+ dx >> 12, (dx >> 8) & 0xF,
+ (dx >> 4) & 0xF, dx & 0xF);
+ return;
+
+ case cpuid_80486:
+ dx = WORD(p);
+ print_cb(label,
+ "Type %u, Family %u, Model %u, Stepping %u",
+ (dx >> 12) & 0x3, (dx >> 8) & 0xF,
+ (dx >> 4) & 0xF, dx & 0xF);
+ return;
+
+ case cpuid_arm_legacy: /* ARM before SOC ID */
+ midr = DWORD(p);
+ /*
+ * The format of this field was not defined for ARM processors
+ * before version 3.1.0 of the SMBIOS specification, so we
+ * silently skip it if it reads all zeroes.
+ */
+ if (midr == 0)
+ return;
+ print_cb(label,
+ "Implementor 0x%02x, Variant 0x%x, Architecture %u, Part 0x%03x, Revision %u",
+ midr >> 24, (midr >> 20) & 0xF,
+ (midr >> 16) & 0xF, (midr >> 4) & 0xFFF, midr & 0xF);
+ return;
+
+ case cpuid_arm_soc_id: /* ARM with SOC ID */
+ /*
+ * If Soc ID is supported, the first DWORD is the JEP-106 code;
+ * the second DWORD is the SoC revision value.
+ */
+ jep106 = DWORD(p);
+ soc_revision = DWORD(p + 4);
+ /*
+ * According to SMC Calling Convention (SMCCC) v1.3 specification
+ * (https://developer.arm.com/documentation/den0028/d/), the format
+ * of the values returned by the SMCCC_ARCH_SOC_ID call is as follows:
+ *
+ * JEP-106 code for the SiP (SoC_ID_type == 0)
+ * Bit[31] must be zero
+ * Bits[30:24] JEP-106 bank index for the SiP
+ * Bits[23:16] JEP-106 identification code with parity bit for the SiP
+ * Bits[15:0] Implementation defined SoC ID
+ *
+ * SoC revision (SoC_ID_type == 1)
+ * Bit[31] must be zero
+ * Bits[30:0] SoC revision
+ */
+ pr_attr("Signature",
+ "JEP-106 Bank 0x%02x Manufacturer 0x%02x, SoC ID 0x%04x, SoC Revision 0x%08x",
+ (jep106 >> 24) & 0x7F, (jep106 >> 16) & 0x7F, jep106 & 0xFFFF, soc_revision);
+ return;
+
+ case cpuid_x86_intel: /* Intel */
+ eax = DWORD(p);
+ /*
+ * Extra flags are now returned in the ECX register when
+ * one calls the CPUID instruction. Their meaning is
+ * explained in table 3-5, but DMI doesn't support this
+ * yet.
+ */
+ print_cb(label,
+ "Type %u, Family %u, Model %u, Stepping %u",
+ (eax >> 12) & 0x3,
+ ((eax >> 20) & 0xFF) + ((eax >> 8) & 0x0F),
+ ((eax >> 12) & 0xF0) + ((eax >> 4) & 0x0F),
+ eax & 0xF);
+ break;
+
+ case cpuid_x86_amd: /* AMD, publication #25481 revision 2.28 */
+ eax = DWORD(p);
+ print_cb(label, "Family %u, Model %u, Stepping %u",
+ ((eax >> 8) & 0xF) + (((eax >> 8) & 0xF) == 0xF ? (eax >> 20) & 0xFF : 0),
+ ((eax >> 4) & 0xF) | (((eax >> 8) & 0xF) == 0xF ? (eax >> 12) & 0xF0 : 0),
+ eax & 0xF);
+ break;
+ default:
+ return;
+ }
+}
+
+static void dmi_processor_id(const struct dmi_header *h)
+{
+ /* Intel AP-485 revision 36, table 2-4 */
+ static const char *flags[32] = {
+ "FPU (Floating-point unit on-chip)", /* 0 */
+ "VME (Virtual mode extension)",
+ "DE (Debugging extension)",
+ "PSE (Page size extension)",
+ "TSC (Time stamp counter)",
+ "MSR (Model specific registers)",
+ "PAE (Physical address extension)",
+ "MCE (Machine check exception)",
+ "CX8 (CMPXCHG8 instruction supported)",
+ "APIC (On-chip APIC hardware supported)",
+ NULL, /* 10 */
+ "SEP (Fast system call)",
+ "MTRR (Memory type range registers)",
+ "PGE (Page global enable)",
+ "MCA (Machine check architecture)",
+ "CMOV (Conditional move instruction supported)",
+ "PAT (Page attribute table)",
+ "PSE-36 (36-bit page size extension)",
+ "PSN (Processor serial number present and enabled)",
+ "CLFSH (CLFLUSH instruction supported)",
+ NULL, /* 20 */
+ "DS (Debug store)",
+ "ACPI (ACPI supported)",
+ "MMX (MMX technology supported)",
+ "FXSR (FXSAVE and FXSTOR instructions supported)",
+ "SSE (Streaming SIMD extensions)",
+ "SSE2 (Streaming SIMD extensions 2)",
+ "SS (Self-snoop)",
+ "HTT (Multi-threading)",
+ "TM (Thermal monitor supported)",
+ NULL, /* 30 */
+ "PBE (Pending break enabled)" /* 31 */
+ };
+ const u8 *data = h->data;
+ const u8 *p = data + 0x08;
+ enum cpuid_type sig = dmi_get_cpuid_type(h);
+ u32 edx;
+
+ /*
+ * This might help learn about new processors supporting the
+ * CPUID instruction or another form of identification.
+ */
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("ID", "%02X %02X %02X %02X %02X %02X %02X %02X",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+
+ dmi_print_cpuid(pr_attr, "Signature", sig, p);
+
+ if (sig != cpuid_x86_intel && sig != cpuid_x86_amd)
+ return;
+
+ edx = DWORD(p + 4);
+ if ((edx & 0xBFEFFBFF) == 0)
+ pr_list_start("Flags", "None");
+ else
+ {
+ int i;
+
+ pr_list_start("Flags", NULL);
+ for (i = 0; i <= 31; i++)
+ if (flags[i] != NULL && edx & (1 << i))
+ pr_list_item("%s", flags[i]);
+ }
+ pr_list_end();
+}
+
+static void dmi_processor_voltage(const char *attr, u8 code)
+{
+ /* 7.5.4 */
+ static const char *voltage[] = {
+ "5.0 V", /* 0 */
+ "3.3 V",
+ "2.9 V" /* 2 */
+ };
+ int i;
+
+ if (code & 0x80)
+ pr_attr(attr, "%.1f V", (float)(code & 0x7f) / 10);
+ else if ((code & 0x07) == 0x00)
+ pr_attr(attr, "Unknown");
+ else
+ {
+ char voltage_str[18];
+ int off = 0;
+
+ for (i = 0; i <= 2; i++)
+ {
+ if (code & (1 << i))
+ {
+ /* Insert space if not the first value */
+ off += sprintf(voltage_str + off,
+ off ? " %s" :"%s",
+ voltage[i]);
+ }
+ }
+ if (off)
+ pr_attr(attr, voltage_str);
+ }
+}
+
+static void dmi_processor_frequency(const char *attr, const u8 *p)
+{
+ u16 code = WORD(p);
+
+ if (code)
+ {
+ if (attr)
+ pr_attr(attr, "%u MHz", code);
+ else
+ printf("%u MHz\n", code);
+ }
+ else
+ {
+ if (attr)
+ pr_attr(attr, "Unknown");
+ else
+ printf("Unknown\n");
+ }
+}
+
+/* code is assumed to be a 3-bit value */
+static const char *dmi_processor_status(u8 code)
+{
+ static const char *status[] = {
+ "Unknown", /* 0x00 */
+ "Enabled",
+ "Disabled By User",
+ "Disabled By BIOS",
+ "Idle", /* 0x04 */
+ out_of_spec,
+ out_of_spec,
+ "Other" /* 0x07 */
+ };
+
+ return status[code];
+}
+
+static const char *dmi_processor_upgrade(u8 code)
+{
+ /* 7.5.5 */
+ static const char *upgrade[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Daughter Board",
+ "ZIF Socket",
+ "Replaceable Piggy Back",
+ "None",
+ "LIF Socket",
+ "Slot 1",
+ "Slot 2",
+ "370-pin Socket",
+ "Slot A",
+ "Slot M",
+ "Socket 423",
+ "Socket A (Socket 462)",
+ "Socket 478",
+ "Socket 754",
+ "Socket 940",
+ "Socket 939",
+ "Socket mPGA604",
+ "Socket LGA771",
+ "Socket LGA775",
+ "Socket S1",
+ "Socket AM2",
+ "Socket F (1207)",
+ "Socket LGA1366",
+ "Socket G34",
+ "Socket AM3",
+ "Socket C32",
+ "Socket LGA1156",
+ "Socket LGA1567",
+ "Socket PGA988A",
+ "Socket BGA1288",
+ "Socket rPGA988B",
+ "Socket BGA1023",
+ "Socket BGA1224",
+ "Socket BGA1155",
+ "Socket LGA1356",
+ "Socket LGA2011",
+ "Socket FS1",
+ "Socket FS2",
+ "Socket FM1",
+ "Socket FM2",
+ "Socket LGA2011-3",
+ "Socket LGA1356-3",
+ "Socket LGA1150",
+ "Socket BGA1168",
+ "Socket BGA1234",
+ "Socket BGA1364",
+ "Socket AM4",
+ "Socket LGA1151",
+ "Socket BGA1356",
+ "Socket BGA1440",
+ "Socket BGA1515",
+ "Socket LGA3647-1",
+ "Socket SP3",
+ "Socket SP3r2",
+ "Socket LGA2066",
+ "Socket BGA1392",
+ "Socket BGA1510",
+ "Socket BGA1528",
+ "Socket LGA4189",
+ "Socket LGA1200",
+ "Socket LGA4677" /* 0x3F */
+ };
+
+ if (code >= 0x01 && code <= 0x3F)
+ return upgrade[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_processor_cache(const char *attr, u16 code, const char *level,
+ u16 ver)
+{
+ if (code == 0xFFFF)
+ {
+ if (ver >= 0x0203)
+ pr_attr(attr, "Not Provided");
+ else
+ pr_attr(attr, "No %s Cache", level);
+ }
+ else
+ pr_attr(attr, "0x%04X", code);
+}
+
+static void dmi_processor_characteristics(const char *attr, u16 code)
+{
+ /* 7.5.9 */
+ static const char *characteristics[] = {
+ "64-bit capable", /* 2 */
+ "Multi-Core",
+ "Hardware Thread",
+ "Execute Protection",
+ "Enhanced Virtualization",
+ "Power/Performance Control",
+ "128-bit Capable",
+ "Arm64 SoC ID" /* 9 */
+ };
+
+ if ((code & 0x00FC) == 0)
+ pr_attr(attr, "None");
+ else
+ {
+ int i;
+
+ pr_list_start(attr, NULL);
+ for (i = 2; i <= 9; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", characteristics[i - 2]);
+ pr_list_end();
+ }
+}
+
+/*
+ * 7.6 Memory Controller Information (Type 5)
+ */
+
+static const char *dmi_memory_controller_ed_method(u8 code)
+{
+ /* 7.6.1 */
+ static const char *method[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "None",
+ "8-bit Parity",
+ "32-bit ECC",
+ "64-bit ECC",
+ "128-bit ECC",
+ "CRC" /* 0x08 */
+ };
+
+ if (code >= 0x01 && code <= 0x08)
+ return method[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_memory_controller_ec_capabilities(const char *attr, u8 code)
+{
+ /* 7.6.2 */
+ static const char *capabilities[] = {
+ "Other", /* 0 */
+ "Unknown",
+ "None",
+ "Single-bit Error Correcting",
+ "Double-bit Error Correcting",
+ "Error Scrubbing" /* 5 */
+ };
+
+ if ((code & 0x3F) == 0)
+ pr_attr(attr, "None");
+ else
+ {
+ int i;
+
+ pr_list_start(attr, NULL);
+ for (i = 0; i <= 5; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", capabilities[i]);
+ pr_list_end();
+ }
+}
+
+static const char *dmi_memory_controller_interleave(u8 code)
+{
+ /* 7.6.3 */
+ static const char *interleave[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "One-way Interleave",
+ "Two-way Interleave",
+ "Four-way Interleave",
+ "Eight-way Interleave",
+ "Sixteen-way Interleave" /* 0x07 */
+ };
+
+ if (code >= 0x01 && code <= 0x07)
+ return interleave[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_memory_controller_speeds(const char *attr, u16 code)
+{
+ /* 7.6.4 */
+ const char *speeds[] = {
+ "Other", /* 0 */
+ "Unknown",
+ "70 ns",
+ "60 ns",
+ "50 ns" /* 4 */
+ };
+
+ if ((code & 0x001F) == 0)
+ pr_attr(attr, "None");
+ else
+ {
+ int i;
+
+ pr_list_start(attr, NULL);
+ for (i = 0; i <= 4; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", speeds[i]);
+ pr_list_end();
+ }
+}
+
+static void dmi_memory_controller_slots(u8 count, const u8 *p)
+{
+ int i;
+
+ pr_list_start("Associated Memory Slots", "%u", count);
+ for (i = 0; i < count; i++)
+ pr_list_item("0x%04X", WORD(p + sizeof(u16) * i));
+ pr_list_end();
+}
+
+/*
+ * 7.7 Memory Module Information (Type 6)
+ */
+
+static void dmi_memory_module_types(const char *attr, u16 code, int flat)
+{
+ /* 7.7.1 */
+ static const char *types[] = {
+ "Other", /* 0 */
+ "Unknown",
+ "Standard",
+ "FPM",
+ "EDO",
+ "Parity",
+ "ECC",
+ "SIMM",
+ "DIMM",
+ "Burst EDO",
+ "SDRAM" /* 10 */
+ };
+
+ if ((code & 0x07FF) == 0)
+ pr_attr(attr, "None");
+ else if (flat)
+ {
+ char type_str[68];
+ int i, off = 0;
+
+ for (i = 0; i <= 10; i++)
+ {
+ if (code & (1 << i))
+ {
+ /* Insert space if not the first value */
+ off += sprintf(type_str + off,
+ off ? " %s" :"%s",
+ types[i]);
+ }
+ }
+ if (off)
+ pr_attr(attr, type_str);
+ }
+ else
+ {
+ int i;
+
+ pr_list_start(attr, NULL);
+ for (i = 0; i <= 10; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", types[i]);
+ pr_list_end();
+ }
+}
+
+static void dmi_memory_module_connections(u8 code)
+{
+ if (code == 0xFF)
+ pr_attr("Bank Connections", "None");
+ else if ((code & 0xF0) == 0xF0)
+ pr_attr("Bank Connections", "%u", code & 0x0F);
+ else if ((code & 0x0F) == 0x0F)
+ pr_attr("Bank Connections", "%u", code >> 4);
+ else
+ pr_attr("Bank Connections", "%u %u", code >> 4, code & 0x0F);
+}
+
+static void dmi_memory_module_speed(const char *attr, u8 code)
+{
+ if (code == 0)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%u ns", code);
+}
+
+static void dmi_memory_module_size(const char *attr, u8 code)
+{
+ const char *connection;
+
+ /* 7.7.2 */
+ if (code & 0x80)
+ connection = " (Double-bank Connection)";
+ else
+ connection = " (Single-bank Connection)";
+
+ switch (code & 0x7F)
+ {
+ case 0x7D:
+ pr_attr(attr, "Not Determinable%s", connection);
+ break;
+ case 0x7E:
+ pr_attr(attr, "Disabled%s", connection);
+ break;
+ case 0x7F:
+ pr_attr(attr, "Not Installed");
+ return;
+ default:
+ pr_attr(attr, "%u MB%s", 1 << (code & 0x7F),
+ connection);
+ }
+}
+
+static void dmi_memory_module_error(u8 code)
+{
+ static const char *status[] = {
+ "OK", /* 0x00 */
+ "Uncorrectable Errors",
+ "Correctable Errors",
+ "Correctable and Uncorrectable Errors" /* 0x03 */
+ };
+
+ if (code & (1 << 2))
+ pr_attr("Error Status", "See Event Log");
+ else
+ pr_attr("Error Status", "%s", status[code & 0x03]);
+}
+
+/*
+ * 7.8 Cache Information (Type 7)
+ */
+
+static const char *dmi_cache_mode(u8 code)
+{
+ static const char *mode[] = {
+ "Write Through", /* 0x00 */
+ "Write Back",
+ "Varies With Memory Address",
+ "Unknown" /* 0x03 */
+ };
+
+ return mode[code];
+}
+
+/* code is assumed to be a 2-bit value */
+static const char *dmi_cache_location(u8 code)
+{
+ static const char *location[4] = {
+ "Internal", /* 0x00 */
+ "External",
+ out_of_spec, /* 0x02 */
+ "Unknown" /* 0x03 */
+ };
+
+ return location[code];
+}
+
+static void dmi_cache_size_2(const char *attr, u32 code)
+{
+ u64 size;
+
+ if (code & 0x80000000)
+ {
+ code &= 0x7FFFFFFFLU;
+ size.l = code << 6;
+ size.h = code >> 26;
+ }
+ else
+ {
+ size.l = code;
+ size.h = 0;
+ }
+
+ /* Use a more convenient unit for large cache size */
+ dmi_print_memory_size(attr, size, 1);
+}
+
+static void dmi_cache_size(const char *attr, u16 code)
+{
+ dmi_cache_size_2(attr,
+ (((u32)code & 0x8000LU) << 16) | (code & 0x7FFFLU));
+}
+
+static void dmi_cache_types(const char *attr, u16 code, int flat)
+{
+ /* 7.8.2 */
+ static const char *types[] = {
+ "Other", /* 0 */
+ "Unknown",
+ "Non-burst",
+ "Burst",
+ "Pipeline Burst",
+ "Synchronous",
+ "Asynchronous" /* 6 */
+ };
+
+ if ((code & 0x007F) == 0)
+ pr_attr(attr, "None");
+ else if (flat)
+ {
+ char type_str[70];
+ int i, off = 0;
+
+ for (i = 0; i <= 6; i++)
+ {
+ if (code & (1 << i))
+ {
+ /* Insert space if not the first value */
+ off += sprintf(type_str + off,
+ off ? " %s" :"%s",
+ types[i]);
+ }
+ }
+ if (off)
+ pr_attr(attr, type_str);
+ }
+ else
+ {
+ int i;
+
+ pr_list_start(attr, NULL);
+ for (i = 0; i <= 6; i++)
+ if (code & (1 << i))
+ pr_list_item("%s", types[i]);
+ pr_list_end();
+ }
+}
+
+static const char *dmi_cache_ec_type(u8 code)
+{
+ /* 7.8.3 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "None",
+ "Parity",
+ "Single-bit ECC",
+ "Multi-bit ECC" /* 0x06 */
+ };
+
+ if (code >= 0x01 && code <= 0x06)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_cache_type(u8 code)
+{
+ /* 7.8.4 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Instruction",
+ "Data",
+ "Unified" /* 0x05 */
+ };
+
+ if (code >= 0x01 && code <= 0x05)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_cache_associativity(u8 code)
+{
+ /* 7.8.5 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Direct Mapped",
+ "2-way Set-associative",
+ "4-way Set-associative",
+ "Fully Associative",
+ "8-way Set-associative",
+ "16-way Set-associative",
+ "12-way Set-associative",
+ "24-way Set-associative",
+ "32-way Set-associative",
+ "48-way Set-associative",
+ "64-way Set-associative",
+ "20-way Set-associative" /* 0x0E */
+ };
+
+ if (code >= 0x01 && code <= 0x0E)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+/*
+ * 7.9 Port Connector Information (Type 8)
+ */
+
+static const char *dmi_port_connector_type(u8 code)
+{
+ /* 7.9.2 */
+ static const char *type[] = {
+ "None", /* 0x00 */
+ "Centronics",
+ "Mini Centronics",
+ "Proprietary",
+ "DB-25 male",
+ "DB-25 female",
+ "DB-15 male",
+ "DB-15 female",
+ "DB-9 male",
+ "DB-9 female",
+ "RJ-11",
+ "RJ-45",
+ "50 Pin MiniSCSI",
+ "Mini DIN",
+ "Micro DIN",
+ "PS/2",
+ "Infrared",
+ "HP-HIL",
+ "Access Bus (USB)",
+ "SSA SCSI",
+ "Circular DIN-8 male",
+ "Circular DIN-8 female",
+ "On Board IDE",
+ "On Board Floppy",
+ "9 Pin Dual Inline (pin 10 cut)",
+ "25 Pin Dual Inline (pin 26 cut)",
+ "50 Pin Dual Inline",
+ "68 Pin Dual Inline",
+ "On Board Sound Input From CD-ROM",
+ "Mini Centronics Type-14",
+ "Mini Centronics Type-26",
+ "Mini Jack (headphones)",
+ "BNC",
+ "IEEE 1394",
+ "SAS/SATA Plug Receptacle",
+ "USB Type-C Receptacle" /* 0x23 */
+ };
+ static const char *type_0xA0[] = {
+ "PC-98", /* 0xA0 */
+ "PC-98 Hireso",
+ "PC-H98",
+ "PC-98 Note",
+ "PC-98 Full" /* 0xA4 */
+ };
+
+ if (code <= 0x23)
+ return type[code];
+ if (code >= 0xA0 && code <= 0xA4)
+ return type_0xA0[code - 0xA0];
+ if (code == 0xFF)
+ return "Other";
+ return out_of_spec;
+}
+
+static const char *dmi_port_type(u8 code)
+{
+ /* 7.9.3 */
+ static const char *type[] = {
+ "None", /* 0x00 */
+ "Parallel Port XT/AT Compatible",
+ "Parallel Port PS/2",
+ "Parallel Port ECP",
+ "Parallel Port EPP",
+ "Parallel Port ECP/EPP",
+ "Serial Port XT/AT Compatible",
+ "Serial Port 16450 Compatible",
+ "Serial Port 16550 Compatible",
+ "Serial Port 16550A Compatible",
+ "SCSI Port",
+ "MIDI Port",
+ "Joystick Port",
+ "Keyboard Port",
+ "Mouse Port",
+ "SSA SCSI",
+ "USB",
+ "Firewire (IEEE P1394)",
+ "PCMCIA Type I",
+ "PCMCIA Type II",
+ "PCMCIA Type III",
+ "Cardbus",
+ "Access Bus Port",
+ "SCSI II",
+ "SCSI Wide",
+ "PC-98",
+ "PC-98 Hireso",
+ "PC-H98",
+ "Video Port",
+ "Audio Port",
+ "Modem Port",
+ "Network Port",
+ "SATA",
+ "SAS" /* 0x21 */
+ };
+ static const char *type_0xA0[] = {
+ "8251 Compatible", /* 0xA0 */
+ "8251 FIFO Compatible" /* 0xA1 */
+ };
+
+ if (code <= 0x21)
+ return type[code];
+ if (code >= 0xA0 && code <= 0xA1)
+ return type_0xA0[code - 0xA0];
+ if (code == 0xFF)
+ return "Other";
+ return out_of_spec;
+}
+
+/*
+ * 7.10 System Slots (Type 9)
+ */
+
+static const char *dmi_slot_type(u8 code)
+{
+ /* 7.10.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "ISA",
+ "MCA",
+ "EISA",
+ "PCI",
+ "PC Card (PCMCIA)",
+ "VLB",
+ "Proprietary",
+ "Processor Card",
+ "Proprietary Memory Card",
+ "I/O Riser Card",
+ "NuBus",
+ "PCI-66",
+ "AGP",
+ "AGP 2x",
+ "AGP 4x",
+ "PCI-X",
+ "AGP 8x",
+ "M.2 Socket 1-DP",
+ "M.2 Socket 1-SD",
+ "M.2 Socket 2",
+ "M.2 Socket 3",
+ "MXM Type I",
+ "MXM Type II",
+ "MXM Type III",
+ "MXM Type III-HE",
+ "MXM Type IV",
+ "MXM 3.0 Type A",
+ "MXM 3.0 Type B",
+ "PCI Express 2 SFF-8639 (U.2)",
+ "PCI Express 3 SFF-8639 (U.2)",
+ "PCI Express Mini 52-pin with bottom-side keep-outs",
+ "PCI Express Mini 52-pin without bottom-side keep-outs",
+ "PCI Express Mini 76-pin",
+ "PCI Express 4 SFF-8639 (U.2)",
+ "PCI Express 5 SFF-8639 (U.2)",
+ "OCP NIC 3.0 Small Form Factor (SFF)",
+ "OCP NIC 3.0 Large Form Factor (LFF)",
+ "OCP NIC Prior to 3.0" /* 0x28 */
+ };
+ static const char *type_0x30[] = {
+ "CXL FLexbus 1.0" /* 0x30 */
+ };
+ static const char *type_0xA0[] = {
+ "PC-98/C20", /* 0xA0 */
+ "PC-98/C24",
+ "PC-98/E",
+ "PC-98/Local Bus",
+ "PC-98/Card",
+ "PCI Express",
+ "PCI Express x1",
+ "PCI Express x2",
+ "PCI Express x4",
+ "PCI Express x8",
+ "PCI Express x16",
+ "PCI Express 2",
+ "PCI Express 2 x1",
+ "PCI Express 2 x2",
+ "PCI Express 2 x4",
+ "PCI Express 2 x8",
+ "PCI Express 2 x16",
+ "PCI Express 3",
+ "PCI Express 3 x1",
+ "PCI Express 3 x2",
+ "PCI Express 3 x4",
+ "PCI Express 3 x8",
+ "PCI Express 3 x16",
+ out_of_spec, /* 0xB7 */
+ "PCI Express 4",
+ "PCI Express 4 x1",
+ "PCI Express 4 x2",
+ "PCI Express 4 x4",
+ "PCI Express 4 x8",
+ "PCI Express 4 x16",
+ "PCI Express 5",
+ "PCI Express 5 x1",
+ "PCI Express 5 x2",
+ "PCI Express 5 x4",
+ "PCI Express 5 x8",
+ "PCI Express 5 x16",
+ "PCI Express 6+",
+ "EDSFF E1",
+ "EDSFF E3" /* 0xC6 */
+ };
+ /*
+ * Note to developers: when adding entries to these lists, check if
+ * function dmi_slot_id below needs updating too.
+ */
+
+ if (code >= 0x01 && code <= 0x28)
+ return type[code - 0x01];
+ if (code == 0x30)
+ return type_0x30[code - 0x30];
+ if (code >= 0xA0 && code <= 0xC6)
+ return type_0xA0[code - 0xA0];
+ return out_of_spec;
+}
+
+/* If hide_unknown is set, return NULL instead of "Other" or "Unknown" */
+static const char *dmi_slot_bus_width(u8 code, int hide_unknown)
+{
+ /* 7.10.2 */
+ static const char *width[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "8-bit",
+ "16-bit",
+ "32-bit",
+ "64-bit",
+ "128-bit",
+ "x1",
+ "x2",
+ "x4",
+ "x8",
+ "x12",
+ "x16",
+ "x32" /* 0x0E */
+ };
+
+ if (code >= 0x01 && code <= 0x0E)
+ {
+ if (code <= 0x02 && hide_unknown)
+ return NULL;
+ return width[code - 0x01];
+ }
+ return out_of_spec;
+}
+
+static void dmi_slot_type_with_width(u8 type, u8 width)
+{
+ const char *type_str, *width_str;
+
+ type_str = dmi_slot_type(type);
+ width_str = dmi_slot_bus_width(width, 1);
+
+ if (width_str)
+ pr_attr("Type", "%s %s", width_str, type_str);
+ else
+ pr_attr("Type", "%s", type_str);
+}
+
+static const char *dmi_slot_current_usage(u8 code)
+{
+ /* 7.10.3 */
+ static const char *usage[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Available",
+ "In Use",
+ "Unavailable" /* 0x05 */
+ };
+
+ if (code >= 0x01 && code <= 0x05)
+ return usage[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_slot_length(u8 code)
+{
+ /* 7.10.4 */
+ static const char *length[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Short",
+ "Long",
+ "2.5\" drive form factor",
+ "3.5\" drive form factor" /* 0x06 */
+ };
+
+ if (code >= 0x01 && code <= 0x06)
+ return length[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_slot_id(u8 code1, u8 code2, u8 type)
+{
+ /* 7.10.5 */
+ switch (type)
+ {
+ case 0x04: /* MCA */
+ pr_attr("ID", "%u", code1);
+ break;
+ case 0x05: /* EISA */
+ pr_attr("ID", "%u", code1);
+ break;
+ case 0x06: /* PCI */
+ case 0x0E: /* PCI */
+ case 0x0F: /* AGP */
+ case 0x10: /* AGP */
+ case 0x11: /* AGP */
+ case 0x12: /* PCI-X */
+ case 0x13: /* AGP */
+ case 0x1F: /* PCI Express 2 */
+ case 0x20: /* PCI Express 3 */
+ case 0x21: /* PCI Express Mini */
+ case 0x22: /* PCI Express Mini */
+ case 0x23: /* PCI Express Mini */
+ case 0xA5: /* PCI Express */
+ case 0xA6: /* PCI Express */
+ case 0xA7: /* PCI Express */
+ case 0xA8: /* PCI Express */
+ case 0xA9: /* PCI Express */
+ case 0xAA: /* PCI Express */
+ case 0xAB: /* PCI Express 2 */
+ case 0xAC: /* PCI Express 2 */
+ case 0xAD: /* PCI Express 2 */
+ case 0xAE: /* PCI Express 2 */
+ case 0xAF: /* PCI Express 2 */
+ case 0xB0: /* PCI Express 2 */
+ case 0xB1: /* PCI Express 3 */
+ case 0xB2: /* PCI Express 3 */
+ case 0xB3: /* PCI Express 3 */
+ case 0xB4: /* PCI Express 3 */
+ case 0xB5: /* PCI Express 3 */
+ case 0xB6: /* PCI Express 3 */
+ case 0xB8: /* PCI Express 4 */
+ case 0xB9: /* PCI Express 4 */
+ case 0xBA: /* PCI Express 4 */
+ case 0xBB: /* PCI Express 4 */
+ case 0xBC: /* PCI Express 4 */
+ case 0xBD: /* PCI Express 4 */
+ case 0xBE: /* PCI Express 5 */
+ case 0xBF: /* PCI Express 5 */
+ case 0xC0: /* PCI Express 5 */
+ case 0xC1: /* PCI Express 5 */
+ case 0xC2: /* PCI Express 5 */
+ case 0xC3: /* PCI Express 5 */
+ case 0xC4: /* PCI Express 6+ */
+ pr_attr("ID", "%u", code1);
+ break;
+ case 0x07: /* PCMCIA */
+ pr_attr("ID", "Adapter %u, Socket %u", code1, code2);
+ break;
+ }
+}
+
+static void dmi_slot_characteristics(const char *attr, u8 code1, u8 code2)
+{
+ /* 7.10.6 */
+ static const char *characteristics1[] = {
+ "5.0 V is provided", /* 1 */
+ "3.3 V is provided",
+ "Opening is shared",
+ "PC Card-16 is supported",
+ "Cardbus is supported",
+ "Zoom Video is supported",
+ "Modem ring resume is supported" /* 7 */
+ };
+ /* 7.10.7 */
+ static const char *characteristics2[] = {
+ "PME signal is supported", /* 0 */
+ "Hot-plug devices are supported",
+ "SMBus signal is supported",
+ "PCIe slot bifurcation is supported",
+ "Async/surprise removal is supported",
+ "Flexbus slot, CXL 1.0 capable",
+ "Flexbus slot, CXL 2.0 capable" /* 6 */
+ };
+
+ if (code1 & (1 << 0))
+ pr_attr(attr, "Unknown");
+ else if ((code1 & 0xFE) == 0 && (code2 & 0x07) == 0)
+ pr_attr(attr, "None");
+ else
+ {
+ int i;
+
+ pr_list_start(attr, NULL);
+ for (i = 1; i <= 7; i++)
+ if (code1 & (1 << i))
+ pr_list_item("%s", characteristics1[i - 1]);
+ for (i = 0; i <= 6; i++)
+ if (code2 & (1 << i))
+ pr_list_item("%s", characteristics2[i]);
+ pr_list_end();
+ }
+}
+
+static void dmi_slot_segment_bus_func(u16 code1, u8 code2, u8 code3)
+{
+ /* 7.10.8 */
+ if (!(code1 == 0xFFFF && code2 == 0xFF && code3 == 0xFF))
+ pr_attr("Bus Address", "%04x:%02x:%02x.%x",
+ code1, code2, code3 >> 3, code3 & 0x7);
+}
+
+static void dmi_slot_peers(u8 n, const u8 *data)
+{
+ char attr[16];
+ int i;
+
+ for (i = 1; i <= n; i++, data += 5)
+ {
+ sprintf(attr, "Peer Device %hhu", (u8)i);
+ pr_attr(attr, "%04x:%02x:%02x.%x (Width %u)",
+ WORD(data), data[2], data[3] >> 3, data[3] & 0x07,
+ data[4]);
+ }
+}
+
+static void dmi_slot_information(u8 type, u8 code)
+{
+ switch (type)
+ {
+ case 0x1F: /* PCI Express 2 */
+ case 0x20: /* PCI Express 3 */
+ case 0x21: /* PCI Express Mini */
+ case 0x22: /* PCI Express Mini */
+ case 0x23: /* PCI Express Mini */
+ case 0xA5: /* PCI Express */
+ case 0xA6: /* PCI Express */
+ case 0xA7: /* PCI Express */
+ case 0xA8: /* PCI Express */
+ case 0xA9: /* PCI Express */
+ case 0xAA: /* PCI Express */
+ case 0xAB: /* PCI Express 2 */
+ case 0xAC: /* PCI Express 2 */
+ case 0xAD: /* PCI Express 2 */
+ case 0xAE: /* PCI Express 2 */
+ case 0xAF: /* PCI Express 2 */
+ case 0xB0: /* PCI Express 2 */
+ case 0xB1: /* PCI Express 3 */
+ case 0xB2: /* PCI Express 3 */
+ case 0xB3: /* PCI Express 3 */
+ case 0xB4: /* PCI Express 3 */
+ case 0xB5: /* PCI Express 3 */
+ case 0xB6: /* PCI Express 3 */
+ case 0xB8: /* PCI Express 4 */
+ case 0xB9: /* PCI Express 4 */
+ case 0xBA: /* PCI Express 4 */
+ case 0xBB: /* PCI Express 4 */
+ case 0xBC: /* PCI Express 4 */
+ case 0xBD: /* PCI Express 4 */
+ case 0xBE: /* PCI Express 5 */
+ case 0xBF: /* PCI Express 5 */
+ case 0xC0: /* PCI Express 5 */
+ case 0xC1: /* PCI Express 5 */
+ case 0xC2: /* PCI Express 5 */
+ case 0xC3: /* PCI Express 5 */
+ case 0xC4: /* PCI Express 6+ */
+ if (code)
+ pr_attr("PCI Express Generation", "%u", code);
+ break;
+ }
+}
+
+static void dmi_slot_physical_width(u8 code)
+{
+ if (code)
+ pr_attr("Slot Physical Width", "%s",
+ dmi_slot_bus_width(code, 0));
+}
+
+static void dmi_slot_pitch(u16 code)
+{
+ if (code)
+ pr_attr("Pitch", "%u.%02u mm", code / 100, code % 100);
+}
+
+static const char *dmi_slot_height(u8 code)
+{
+ /* 7.10.3 */
+ static const char *height[] = {
+ "Not applicable", /* 0x00 */
+ "Other",
+ "Unknown",
+ "Full height",
+ "Low-profile" /* 0x04 */
+ };
+
+ if (code <= 0x04)
+ return height[code];
+ return out_of_spec;
+}
+
+/*
+ * 7.11 On Board Devices Information (Type 10)
+ */
+
+static const char *dmi_on_board_devices_type(u8 code)
+{
+ /* 7.11.1 and 7.42.2 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Video",
+ "SCSI Controller",
+ "Ethernet",
+ "Token Ring",
+ "Sound",
+ "PATA Controller",
+ "SATA Controller",
+ "SAS Controller",
+ "Wireless LAN",
+ "Bluetooth",
+ "WWAN",
+ "eMMC",
+ "NVMe Controller",
+ "UFS Controller" /* 0x10 */
+ };
+
+ if (code >= 0x01 && code <= 0x10)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_on_board_devices(const struct dmi_header *h)
+{
+ u8 *p = h->data + 4;
+ u8 count = (h->length - 0x04) / 2;
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (count == 1)
+ pr_handle_name("On Board Device Information");
+ else
+ pr_handle_name("On Board Device %d Information",
+ i + 1);
+ pr_attr("Type", "%s",
+ dmi_on_board_devices_type(p[2 * i] & 0x7F));
+ pr_attr("Status", "%s",
+ p[2 * i] & 0x80 ? "Enabled" : "Disabled");
+ pr_attr("Description", "%s", dmi_string(h, p[2 * i + 1]));
+ }
+}
+
+/*
+ * 7.12 OEM Strings (Type 11)
+ */
+
+static void dmi_oem_strings(const struct dmi_header *h)
+{
+ char attr[11];
+ u8 *p = h->data + 4;
+ u8 count = p[0x00];
+ int i;
+
+ for (i = 1; i <= count; i++)
+ {
+ sprintf(attr, "String %hhu", (u8)i);
+ pr_attr(attr, "%s",dmi_string(h, i));
+ }
+}
+
+/*
+ * 7.13 System Configuration Options (Type 12)
+ */
+
+static void dmi_system_configuration_options(const struct dmi_header *h)
+{
+ char attr[11];
+ u8 *p = h->data + 4;
+ u8 count = p[0x00];
+ int i;
+
+ for (i = 1; i <= count; i++)
+ {
+ sprintf(attr, "Option %hhu", (u8)i);
+ pr_attr(attr, "%s",dmi_string(h, i));
+ }
+}
+
+/*
+ * 7.14 BIOS Language Information (Type 13)
+ */
+
+static void dmi_bios_languages(const struct dmi_header *h)
+{
+ u8 *p = h->data + 4;
+ u8 count = p[0x00];
+ int i;
+
+ for (i = 1; i <= count; i++)
+ pr_list_item("%s", dmi_string(h, i));
+}
+
+static const char *dmi_bios_language_format(u8 code)
+{
+ if (code & 0x01)
+ return "Abbreviated";
+ else
+ return "Long";
+}
+
+/*
+ * 7.15 Group Associations (Type 14)
+ */
+
+static void dmi_group_associations_items(u8 count, const u8 *p)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ pr_list_item("0x%04X (%s)",
+ WORD(p + 3 * i + 1),
+ dmi_smbios_structure_type(p[3 * i]));
+ }
+}
+
+/*
+ * 7.16 System Event Log (Type 15)
+ */
+
+static const char *dmi_event_log_method(u8 code)
+{
+ static const char *method[] = {
+ "Indexed I/O, one 8-bit index port, one 8-bit data port", /* 0x00 */
+ "Indexed I/O, two 8-bit index ports, one 8-bit data port",
+ "Indexed I/O, one 16-bit index port, one 8-bit data port",
+ "Memory-mapped physical 32-bit address",
+ "General-purpose non-volatile data functions" /* 0x04 */
+ };
+
+ if (code <= 0x04)
+ return method[code];
+ if (code >= 0x80)
+ return "OEM-specific";
+ return out_of_spec;
+}
+
+static void dmi_event_log_status(u8 code)
+{
+ static const char *valid[] = {
+ "Invalid", /* 0 */
+ "Valid" /* 1 */
+ };
+ static const char *full[] = {
+ "Not Full", /* 0 */
+ "Full" /* 1 */
+ };
+
+ pr_attr("Status", "%s, %s",
+ valid[(code >> 0) & 1], full[(code >> 1) & 1]);
+}
+
+static void dmi_event_log_address(u8 method, const u8 *p)
+{
+ /* 7.16.3 */
+ switch (method)
+ {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ pr_attr("Access Address", "Index 0x%04X, Data 0x%04X",
+ WORD(p), WORD(p + 2));
+ break;
+ case 0x03:
+ pr_attr("Access Address", "0x%08X", DWORD(p));
+ break;
+ case 0x04:
+ pr_attr("Access Address", "0x%04X", WORD(p));
+ break;
+ default:
+ pr_attr("Access Address", "Unknown");
+ }
+}
+
+static const char *dmi_event_log_header_type(u8 code)
+{
+ static const char *type[] = {
+ "No Header", /* 0x00 */
+ "Type 1" /* 0x01 */
+ };
+
+ if (code <= 0x01)
+ return type[code];
+ if (code >= 0x80)
+ return "OEM-specific";
+ return out_of_spec;
+}
+
+static const char *dmi_event_log_descriptor_type(u8 code)
+{
+ /* 7.16.6.1 */
+ static const char *type[] = {
+ NULL, /* 0x00 */
+ "Single-bit ECC memory error",
+ "Multi-bit ECC memory error",
+ "Parity memory error",
+ "Bus timeout",
+ "I/O channel block",
+ "Software NMI",
+ "POST memory resize",
+ "POST error",
+ "PCI parity error",
+ "PCI system error",
+ "CPU failure",
+ "EISA failsafe timer timeout",
+ "Correctable memory log disabled",
+ "Logging disabled",
+ NULL, /* 0x0F */
+ "System limit exceeded",
+ "Asynchronous hardware timer expired",
+ "System configuration information",
+ "Hard disk information",
+ "System reconfigured",
+ "Uncorrectable CPU-complex error",
+ "Log area reset/cleared",
+ "System boot" /* 0x17 */
+ };
+
+ if (code <= 0x17 && type[code] != NULL)
+ return type[code];
+ if (code >= 0x80 && code <= 0xFE)
+ return "OEM-specific";
+ if (code == 0xFF)
+ return "End of log";
+ return out_of_spec;
+}
+
+static const char *dmi_event_log_descriptor_format(u8 code)
+{
+ /* 7.16.6.2 */
+ static const char *format[] = {
+ "None", /* 0x00 */
+ "Handle",
+ "Multiple-event",
+ "Multiple-event handle",
+ "POST results bitmap",
+ "System management",
+ "Multiple-event system management" /* 0x06 */
+ };
+
+ if (code <= 0x06)
+ return format[code];
+ if (code >= 0x80)
+ return "OEM-specific";
+ return out_of_spec;
+}
+
+static void dmi_event_log_descriptors(u8 count, u8 len, const u8 *p)
+{
+ /* 7.16.1 */
+ char attr[16];
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (len >= 0x02)
+ {
+ sprintf(attr, "Descriptor %d", i + 1);
+ pr_attr(attr, "%s",
+ dmi_event_log_descriptor_type(p[i * len]));
+ sprintf(attr, "Data Format %d", i + 1);
+ pr_attr(attr, "%s",
+ dmi_event_log_descriptor_format(p[i * len + 1]));
+ }
+ }
+}
+
+/*
+ * 7.17 Physical Memory Array (Type 16)
+ */
+
+static const char *dmi_memory_array_location(u8 code)
+{
+ /* 7.17.1 */
+ static const char *location[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "System Board Or Motherboard",
+ "ISA Add-on Card",
+ "EISA Add-on Card",
+ "PCI Add-on Card",
+ "MCA Add-on Card",
+ "PCMCIA Add-on Card",
+ "Proprietary Add-on Card",
+ "NuBus" /* 0x0A */
+ };
+ static const char *location_0xA0[] = {
+ "PC-98/C20 Add-on Card", /* 0xA0 */
+ "PC-98/C24 Add-on Card",
+ "PC-98/E Add-on Card",
+ "PC-98/Local Bus Add-on Card",
+ "CXL Flexbus 1.0" /* 0xA4 */
+ };
+
+ if (code >= 0x01 && code <= 0x0A)
+ return location[code - 0x01];
+ if (code >= 0xA0 && code <= 0xA4)
+ return location_0xA0[code - 0xA0];
+ return out_of_spec;
+}
+
+static const char *dmi_memory_array_use(u8 code)
+{
+ /* 7.17.2 */
+ static const char *use[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "System Memory",
+ "Video Memory",
+ "Flash Memory",
+ "Non-volatile RAM",
+ "Cache Memory" /* 0x07 */
+ };
+
+ if (code >= 0x01 && code <= 0x07)
+ return use[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_memory_array_ec_type(u8 code)
+{
+ /* 7.17.3 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "None",
+ "Parity",
+ "Single-bit ECC",
+ "Multi-bit ECC",
+ "CRC" /* 0x07 */
+ };
+
+ if (code >= 0x01 && code <= 0x07)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_memory_array_error_handle(u16 code)
+{
+ if (code == 0xFFFE)
+ pr_attr("Error Information Handle", "Not Provided");
+ else if (code == 0xFFFF)
+ pr_attr("Error Information Handle", "No Error");
+ else
+ pr_attr("Error Information Handle", "0x%04X", code);
+}
+
+/*
+ * 7.18 Memory Device (Type 17)
+ */
+
+static void dmi_memory_device_width(const char *attr, u16 code)
+{
+ /*
+ * If no memory module is present, width may be 0
+ */
+ if (code == 0xFFFF || (code == 0 && !(opt.flags & FLAG_NO_QUIRKS)))
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%u bits", code);
+}
+
+static void dmi_memory_device_size(u16 code)
+{
+ if (code == 0)
+ pr_attr("Size", "No Module Installed");
+ else if (code == 0xFFFF)
+ pr_attr("Size", "Unknown");
+ else
+ {
+ u64 s = { .l = code & 0x7FFF };
+ if (!(code & 0x8000))
+ s.l <<= 10;
+ dmi_print_memory_size("Size", s, 1);
+ }
+}
+
+static void dmi_memory_device_extended_size(u32 code)
+{
+ code &= 0x7FFFFFFFUL;
+
+ /*
+ * Use the greatest unit for which the exact value can be displayed
+ * as an integer without rounding
+ */
+ if (code & 0x3FFUL)
+ pr_attr("Size", "%lu MB", (unsigned long)code);
+ else if (code & 0xFFC00UL)
+ pr_attr("Size", "%lu GB", (unsigned long)code >> 10);
+ else
+ pr_attr("Size", "%lu TB", (unsigned long)code >> 20);
+}
+
+static void dmi_memory_voltage_value(const char *attr, u16 code)
+{
+ if (code == 0)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, code % 100 ? "%g V" : "%.1f V",
+ (float)code / 1000);
+}
+
+static const char *dmi_memory_device_form_factor(u8 code)
+{
+ /* 7.18.1 */
+ static const char *form_factor[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "SIMM",
+ "SIP",
+ "Chip",
+ "DIP",
+ "ZIP",
+ "Proprietary Card",
+ "DIMM",
+ "TSOP",
+ "Row Of Chips",
+ "RIMM",
+ "SODIMM",
+ "SRIMM",
+ "FB-DIMM",
+ "Die" /* 0x10 */
+ };
+
+ if (code >= 0x01 && code <= 0x10)
+ return form_factor[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_memory_device_set(u8 code)
+{
+ if (code == 0)
+ pr_attr("Set", "None");
+ else if (code == 0xFF)
+ pr_attr("Set", "Unknown");
+ else
+ pr_attr("Set", "%u", code);
+}
+
+static const char *dmi_memory_device_type(u8 code)
+{
+ /* 7.18.2 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "DRAM",
+ "EDRAM",
+ "VRAM",
+ "SRAM",
+ "RAM",
+ "ROM",
+ "Flash",
+ "EEPROM",
+ "FEPROM",
+ "EPROM",
+ "CDRAM",
+ "3DRAM",
+ "SDRAM",
+ "SGRAM",
+ "RDRAM",
+ "DDR",
+ "DDR2",
+ "DDR2 FB-DIMM",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "DDR3",
+ "FBD2",
+ "DDR4",
+ "LPDDR",
+ "LPDDR2",
+ "LPDDR3",
+ "LPDDR4",
+ "Logical non-volatile device",
+ "HBM",
+ "HBM2",
+ "DDR5",
+ "LPDDR5" /* 0x23 */
+ };
+
+ if (code >= 0x01 && code <= 0x23)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_memory_device_type_detail(u16 code)
+{
+ /* 7.18.3 */
+ static const char *detail[] = {
+ "Other", /* 1 */
+ "Unknown",
+ "Fast-paged",
+ "Static Column",
+ "Pseudo-static",
+ "RAMBus",
+ "Synchronous",
+ "CMOS",
+ "EDO",
+ "Window DRAM",
+ "Cache DRAM",
+ "Non-Volatile",
+ "Registered (Buffered)",
+ "Unbuffered (Unregistered)",
+ "LRDIMM" /* 15 */
+ };
+ char list[172]; /* Update length if you touch the array above */
+
+ if ((code & 0xFFFE) == 0)
+ pr_attr("Type Detail", "None");
+ else
+ {
+ int i, off = 0;
+
+ list[0] = '\0';
+ for (i = 1; i <= 15; i++)
+ if (code & (1 << i))
+ off += sprintf(list + off, off ? " %s" : "%s",
+ detail[i - 1]);
+ pr_attr("Type Detail", list);
+ }
+}
+
+static void dmi_memory_device_speed(const char *attr, u16 code1, u32 code2)
+{
+ if (code1 == 0xFFFF)
+ {
+ if (code2 == 0)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%lu MT/s", code2);
+ }
+ else
+ {
+ if (code1 == 0)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%u MT/s", code1);
+ }
+}
+
+static void dmi_memory_technology(u8 code)
+{
+ /* 7.18.6 */
+ static const char * const technology[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "DRAM",
+ "NVDIMM-N",
+ "NVDIMM-F",
+ "NVDIMM-P",
+ "Intel Optane DC persistent memory" /* 0x07 */
+ };
+ if (code >= 0x01 && code <= 0x07)
+ pr_attr("Memory Technology", "%s", technology[code - 0x01]);
+ else
+ pr_attr("Memory Technology", "%s", out_of_spec);
+}
+
+static void dmi_memory_operating_mode_capability(u16 code)
+{
+ /* 7.18.7 */
+ static const char * const mode[] = {
+ "Other", /* 1 */
+ "Unknown",
+ "Volatile memory",
+ "Byte-accessible persistent memory",
+ "Block-accessible persistent memory" /* 5 */
+ };
+ char list[99]; /* Update length if you touch the array above */
+
+ if ((code & 0xFFFE) == 0)
+ pr_attr("Memory Operating Mode Capability", "None");
+ else {
+ int i, off = 0;
+
+ list[0] = '\0';
+ for (i = 1; i <= 5; i++)
+ if (code & (1 << i))
+ off += sprintf(list + off, off ? " %s" : "%s",
+ mode[i - 1]);
+ pr_attr("Memory Operating Mode Capability", list);
+ }
+}
+
+static void dmi_memory_manufacturer_id(const char *attr, u16 code)
+{
+ /* 7.18.8 */
+ /* 7.18.10 */
+ /* LSB is 7-bit Odd Parity number of continuation codes */
+ if (code == 0)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "Bank %d, Hex 0x%02X",
+ (code & 0x7F) + 1, code >> 8);
+}
+
+static void dmi_memory_product_id(const char *attr, u16 code)
+{
+ /* 7.18.9 */
+ /* 7.18.11 */
+ if (code == 0)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "0x%04X", code);
+}
+
+static void dmi_memory_size(const char *attr, u64 code)
+{
+ /* 7.18.12 */
+ /* 7.18.13 */
+ if (code.h == 0xFFFFFFFF && code.l == 0xFFFFFFFF)
+ pr_attr(attr, "Unknown");
+ else if (code.h == 0x0 && code.l == 0x0)
+ pr_attr(attr, "None");
+ else
+ dmi_print_memory_size(attr, code, 0);
+}
+
+/*
+ * 7.19 32-bit Memory Error Information (Type 18)
+ */
+
+static const char *dmi_memory_error_type(u8 code)
+{
+ /* 7.19.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "OK",
+ "Bad Read",
+ "Parity Error",
+ "Single-bit Error",
+ "Double-bit Error",
+ "Multi-bit Error",
+ "Nibble Error",
+ "Checksum Error",
+ "CRC Error",
+ "Corrected Single-bit Error",
+ "Corrected Error",
+ "Uncorrectable Error" /* 0x0E */
+ };
+
+ if (code >= 0x01 && code <= 0x0E)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_memory_error_granularity(u8 code)
+{
+ /* 7.19.2 */
+ static const char *granularity[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Device Level",
+ "Memory Partition Level" /* 0x04 */
+ };
+
+ if (code >= 0x01 && code <= 0x04)
+ return granularity[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_memory_error_operation(u8 code)
+{
+ /* 7.19.3 */
+ static const char *operation[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Read",
+ "Write",
+ "Partial Write" /* 0x05 */
+ };
+
+ if (code >= 0x01 && code <= 0x05)
+ return operation[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_memory_error_syndrome(u32 code)
+{
+ if (code == 0x00000000)
+ pr_attr("Vendor Syndrome", "Unknown");
+ else
+ pr_attr("Vendor Syndrome", "0x%08X", code);
+}
+
+static void dmi_32bit_memory_error_address(const char *attr, u32 code)
+{
+ if (code == 0x80000000)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "0x%08X", code);
+}
+
+/*
+ * 7.20 Memory Array Mapped Address (Type 19)
+ */
+
+static void dmi_mapped_address_size(u32 code)
+{
+ if (code == 0)
+ pr_attr("Range Size", "Invalid");
+ else
+ {
+ u64 size;
+
+ size.h = 0;
+ size.l = code;
+ dmi_print_memory_size("Range Size", size, 1);
+ }
+}
+
+static void dmi_mapped_address_extended_size(u64 start, u64 end)
+{
+ if (start.h == end.h && start.l == end.l)
+ pr_attr("Range Size", "Invalid");
+ else
+ dmi_print_memory_size("Range Size", u64_range(start, end), 0);
+}
+
+/*
+ * 7.21 Memory Device Mapped Address (Type 20)
+ */
+
+static void dmi_mapped_address_row_position(u8 code)
+{
+ if (code == 0)
+ pr_attr("Partition Row Position", "%s", out_of_spec);
+ else if (code == 0xFF)
+ pr_attr("Partition Row Position", "Unknown");
+ else
+ pr_attr("Partition Row Position", "%u", code);
+}
+
+static void dmi_mapped_address_interleave_position(u8 code)
+{
+ if (code != 0)
+ {
+ if (code == 0xFF)
+ pr_attr("Interleave Position", "Unknown");
+ else
+ pr_attr("Interleave Position", "%u", code);
+ }
+}
+
+static void dmi_mapped_address_interleaved_data_depth(u8 code)
+{
+ if (code != 0)
+ {
+ if (code == 0xFF)
+ pr_attr("Interleaved Data Depth", "Unknown");
+ else
+ pr_attr("Interleaved Data Depth", "%u", code);
+ }
+}
+
+/*
+ * 7.22 Built-in Pointing Device (Type 21)
+ */
+
+static const char *dmi_pointing_device_type(u8 code)
+{
+ /* 7.22.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Mouse",
+ "Track Ball",
+ "Track Point",
+ "Glide Point",
+ "Touch Pad",
+ "Touch Screen",
+ "Optical Sensor" /* 0x09 */
+ };
+
+ if (code >= 0x01 && code <= 0x09)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_pointing_device_interface(u8 code)
+{
+ /* 7.22.2 */
+ static const char *interface[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Serial",
+ "PS/2",
+ "Infrared",
+ "HIP-HIL",
+ "Bus Mouse",
+ "ADB (Apple Desktop Bus)" /* 0x08 */
+ };
+ static const char *interface_0xA0[] = {
+ "Bus Mouse DB-9", /* 0xA0 */
+ "Bus Mouse Micro DIN",
+ "USB",
+ "I2C",
+ "SPI" /* 0xA4 */
+ };
+
+ if (code >= 0x01 && code <= 0x08)
+ return interface[code - 0x01];
+ if (code >= 0xA0 && code <= 0xA4)
+ return interface_0xA0[code - 0xA0];
+ return out_of_spec;
+}
+
+/*
+ * 7.23 Portable Battery (Type 22)
+ */
+
+static const char *dmi_battery_chemistry(u8 code)
+{
+ /* 7.23.1 */
+ static const char *chemistry[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Lead Acid",
+ "Nickel Cadmium",
+ "Nickel Metal Hydride",
+ "Lithium Ion",
+ "Zinc Air",
+ "Lithium Polymer" /* 0x08 */
+ };
+
+ if (code >= 0x01 && code <= 0x08)
+ return chemistry[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_battery_capacity(u16 code, u8 multiplier)
+{
+ if (code == 0)
+ pr_attr("Design Capacity", "Unknown");
+ else
+ pr_attr("Design Capacity", "%u mWh", code * multiplier);
+}
+
+static void dmi_battery_voltage(u16 code)
+{
+ if (code == 0)
+ pr_attr("Design Voltage", "Unknown");
+ else
+ pr_attr("Design Voltage", "%u mV", code);
+}
+
+static void dmi_battery_maximum_error(u8 code)
+{
+ if (code == 0xFF)
+ pr_attr("Maximum Error", "Unknown");
+ else
+ pr_attr("Maximum Error", "%u%%", code);
+}
+
+/*
+ * 7.24 System Reset (Type 23)
+ */
+
+/* code is assumed to be a 2-bit value */
+static const char *dmi_system_reset_boot_option(u8 code)
+{
+ static const char *option[] = {
+ out_of_spec, /* 0x0 */
+ "Operating System", /* 0x1 */
+ "System Utilities",
+ "Do Not Reboot" /* 0x3 */
+ };
+
+ return option[code];
+}
+
+static void dmi_system_reset_count(const char *attr, u16 code)
+{
+ if (code == 0xFFFF)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%u", code);
+}
+
+static void dmi_system_reset_timer(const char *attr, u16 code)
+{
+ if (code == 0xFFFF)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%u min", code);
+}
+
+/*
+ * 7.25 Hardware Security (Type 24)
+ */
+
+static const char *dmi_hardware_security_status(u8 code)
+{
+ static const char *status[] = {
+ "Disabled", /* 0x00 */
+ "Enabled",
+ "Not Implemented",
+ "Unknown" /* 0x03 */
+ };
+
+ return status[code];
+}
+
+/*
+ * 7.26 System Power Controls (Type 25)
+ */
+
+static void dmi_power_controls_power_on(const u8 *p)
+{
+ char time[15];
+ int off = 0;
+
+ /* 7.26.1 */
+ if (dmi_bcd_range(p[0], 0x01, 0x12))
+ off += sprintf(time + off, "%02X", p[0]);
+ else
+ off += sprintf(time + off, "*");
+ if (dmi_bcd_range(p[1], 0x01, 0x31))
+ off += sprintf(time + off, "-%02X", p[1]);
+ else
+ off += sprintf(time + off, "-*");
+ if (dmi_bcd_range(p[2], 0x00, 0x23))
+ off += sprintf(time + off, " %02X", p[2]);
+ else
+ off += sprintf(time + off, " *");
+ if (dmi_bcd_range(p[3], 0x00, 0x59))
+ off += sprintf(time + off, ":%02X", p[3]);
+ else
+ off += sprintf(time + off, ":*");
+ if (dmi_bcd_range(p[4], 0x00, 0x59))
+ off += sprintf(time + off, ":%02X", p[4]);
+ else
+ off += sprintf(time + off, ":*");
+
+ pr_attr("Next Scheduled Power-on", time);
+}
+
+/*
+ * 7.27 Voltage Probe (Type 26)
+ */
+
+static const char *dmi_voltage_probe_location(u8 code)
+{
+ /* 7.27.1 */
+ static const char *location[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Processor",
+ "Disk",
+ "Peripheral Bay",
+ "System Management Module",
+ "Motherboard",
+ "Memory Module",
+ "Processor Module",
+ "Power Unit",
+ "Add-in Card" /* 0x0B */
+ };
+
+ if (code >= 0x01 && code <= 0x0B)
+ return location[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_probe_status(u8 code)
+{
+ /* 7.27.1 */
+ static const char *status[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "OK",
+ "Non-critical",
+ "Critical",
+ "Non-recoverable" /* 0x06 */
+ };
+
+ if (code >= 0x01 && code <= 0x06)
+ return status[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_voltage_probe_value(const char *attr, u16 code)
+{
+ if (code == 0x8000)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%.3f V", (float)(i16)code / 1000);
+}
+
+static void dmi_voltage_probe_resolution(u16 code)
+{
+ if (code == 0x8000)
+ pr_attr("Resolution", "Unknown");
+ else
+ pr_attr("Resolution", "%.1f mV", (float)code / 10);
+}
+
+static void dmi_probe_accuracy(u16 code)
+{
+ if (code == 0x8000)
+ pr_attr("Accuracy", "Unknown");
+ else
+ pr_attr("Accuracy", "%.2f%%", (float)code / 100);
+}
+
+/*
+ * 7.28 Cooling Device (Type 27)
+ */
+
+static const char *dmi_cooling_device_type(u8 code)
+{
+ /* 7.28.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Fan",
+ "Centrifugal Blower",
+ "Chip Fan",
+ "Cabinet Fan",
+ "Power Supply Fan",
+ "Heat Pipe",
+ "Integrated Refrigeration" /* 0x09 */
+ };
+ static const char *type_0x10[] = {
+ "Active Cooling", /* 0x10 */
+ "Passive Cooling" /* 0x11 */
+ };
+
+ if (code >= 0x01 && code <= 0x09)
+ return type[code - 0x01];
+ if (code >= 0x10 && code <= 0x11)
+ return type_0x10[code - 0x10];
+ return out_of_spec;
+}
+
+static void dmi_cooling_device_speed(u16 code)
+{
+ if (code == 0x8000)
+ pr_attr("Nominal Speed", "Unknown Or Non-rotating");
+ else
+ pr_attr("Nominal Speed", "%u rpm", code);
+}
+
+/*
+ * 7.29 Temperature Probe (Type 28)
+ */
+
+static const char *dmi_temperature_probe_location(u8 code)
+{
+ /* 7.29.1 */
+ static const char *location[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Processor",
+ "Disk",
+ "Peripheral Bay",
+ "System Management Module",
+ "Motherboard",
+ "Memory Module",
+ "Processor Module",
+ "Power Unit",
+ "Add-in Card",
+ "Front Panel Board",
+ "Back Panel Board",
+ "Power System Board",
+ "Drive Back Plane" /* 0x0F */
+ };
+
+ if (code >= 0x01 && code <= 0x0F)
+ return location[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_temperature_probe_value(const char *attr, u16 code)
+{
+ if (code == 0x8000)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%.1f deg C", (float)(i16)code / 10);
+}
+
+static void dmi_temperature_probe_resolution(u16 code)
+{
+ if (code == 0x8000)
+ pr_attr("Resolution", "Unknown");
+ else
+ pr_attr("Resolution", "%.3f deg C", (float)code / 1000);
+}
+
+/*
+ * 7.30 Electrical Current Probe (Type 29)
+ */
+
+static void dmi_current_probe_value(const char *attr, u16 code)
+{
+ if (code == 0x8000)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "%.3f A", (float)(i16)code / 1000);
+}
+
+static void dmi_current_probe_resolution(u16 code)
+{
+ if (code == 0x8000)
+ pr_attr("Resolution", "Unknown");
+ else
+ pr_attr("Resolution", "%.1f mA", (float)code / 10);
+}
+
+/*
+ * 7.33 System Boot Information (Type 32)
+ */
+
+static const char *dmi_system_boot_status(u8 code)
+{
+ static const char *status[] = {
+ "No errors detected", /* 0 */
+ "No bootable media",
+ "Operating system failed to load",
+ "Firmware-detected hardware failure",
+ "Operating system-detected hardware failure",
+ "User-requested boot",
+ "System security violation",
+ "Previously-requested image",
+ "System watchdog timer expired" /* 8 */
+ };
+
+ if (code <= 8)
+ return status[code];
+ if (code >= 128 && code <= 191)
+ return "OEM-specific";
+ if (code >= 192)
+ return "Product-specific";
+ return out_of_spec;
+}
+
+/*
+ * 7.34 64-bit Memory Error Information (Type 33)
+ */
+
+static void dmi_64bit_memory_error_address(const char *attr, u64 code)
+{
+ if (code.h == 0x80000000 && code.l == 0x00000000)
+ pr_attr(attr, "Unknown");
+ else
+ pr_attr(attr, "0x%08X%08X", code.h, code.l);
+}
+
+/*
+ * 7.35 Management Device (Type 34)
+ */
+
+/*
+ * Several boards have a bug where some type 34 structures have their
+ * length incorrectly set to 0x10 instead of 0x0B. This causes the
+ * first 5 characters of the device name to be trimmed. It's easy to
+ * check and fix, so do it, but warn.
+ */
+static void dmi_fixup_type_34(struct dmi_header *h, int display)
+{
+ u8 *p = h->data;
+
+ /* Make sure the hidden data is ASCII only */
+ if (h->length == 0x10
+ && is_printable(p + 0x0B, 0x10 - 0x0B))
+ {
+ if (!(opt.flags & FLAG_QUIET) && display)
+ fprintf(stderr,
+ "Invalid entry length (%u). Fixed up to %u.\n",
+ 0x10, 0x0B);
+ h->length = 0x0B;
+ }
+}
+
+static const char *dmi_management_device_type(u8 code)
+{
+ /* 7.35.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "LM75",
+ "LM78",
+ "LM79",
+ "LM80",
+ "LM81",
+ "ADM9240",
+ "DS1780",
+ "MAX1617",
+ "GL518SM",
+ "W83781D",
+ "HT82H791" /* 0x0D */
+ };
+
+ if (code >= 0x01 && code <= 0x0D)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_management_device_address_type(u8 code)
+{
+ /* 7.35.2 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "I/O Port",
+ "Memory",
+ "SMBus" /* 0x05 */
+ };
+
+ if (code >= 0x01 && code <= 0x05)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+/*
+ * 7.38 Memory Channel (Type 37)
+ */
+
+static const char *dmi_memory_channel_type(u8 code)
+{
+ /* 7.38.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "RamBus",
+ "SyncLink" /* 0x04 */
+ };
+
+ if (code >= 0x01 && code <= 0x04)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_memory_channel_devices(u8 count, const u8 *p)
+{
+ char attr[18];
+ int i;
+
+ for (i = 1; i <= count; i++)
+ {
+ sprintf(attr, "Device %hhu Load", (u8)i);
+ pr_attr(attr, "%u", p[3 * i]);
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ sprintf(attr, "Device %hhu Handle", (u8)i);
+ pr_attr(attr, "0x%04X", WORD(p + 3 * i + 1));
+ }
+ }
+}
+
+/*
+ * 7.39 IPMI Device Information (Type 38)
+ */
+
+static const char *dmi_ipmi_interface_type(u8 code)
+{
+ /* 7.39.1 and IPMI 2.0, appendix C1, table C1-2 */
+ static const char *type[] = {
+ "Unknown", /* 0x00 */
+ "KCS (Keyboard Control Style)",
+ "SMIC (Server Management Interface Chip)",
+ "BT (Block Transfer)",
+ "SSIF (SMBus System Interface)" /* 0x04 */
+ };
+
+ if (code <= 0x04)
+ return type[code];
+ return out_of_spec;
+}
+
+static void dmi_ipmi_base_address(u8 type, const u8 *p, u8 lsb)
+{
+ if (type == 0x04) /* SSIF */
+ {
+ pr_attr("Base Address", "0x%02X (SMBus)", (*p) >> 1);
+ }
+ else
+ {
+ u64 address = QWORD(p);
+ pr_attr("Base Address", "0x%08X%08X (%s)",
+ address.h, (address.l & ~1) | lsb,
+ address.l & 1 ? "I/O" : "Memory-mapped");
+ }
+}
+
+/* code is assumed to be a 2-bit value */
+static const char *dmi_ipmi_register_spacing(u8 code)
+{
+ /* IPMI 2.0, appendix C1, table C1-1 */
+ static const char *spacing[] = {
+ "Successive Byte Boundaries", /* 0x00 */
+ "32-bit Boundaries",
+ "16-byte Boundaries", /* 0x02 */
+ out_of_spec /* 0x03 */
+ };
+
+ return spacing[code];
+}
+
+/*
+ * 7.40 System Power Supply (Type 39)
+ */
+
+static void dmi_power_supply_power(u16 code)
+{
+ if (code == 0x8000)
+ pr_attr("Max Power Capacity", "Unknown");
+ else
+ pr_attr("Max Power Capacity", "%u W", (unsigned int)code);
+}
+
+static const char *dmi_power_supply_type(u8 code)
+{
+ /* 7.40.1 */
+ static const char *type[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Linear",
+ "Switching",
+ "Battery",
+ "UPS",
+ "Converter",
+ "Regulator" /* 0x08 */
+ };
+
+ if (code >= 0x01 && code <= 0x08)
+ return type[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_power_supply_status(u8 code)
+{
+ /* 7.40.1 */
+ static const char *status[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "OK",
+ "Non-critical",
+ "Critical" /* 0x05 */
+ };
+
+ if (code >= 0x01 && code <= 0x05)
+ return status[code - 0x01];
+ return out_of_spec;
+}
+
+static const char *dmi_power_supply_range_switching(u8 code)
+{
+ /* 7.40.1 */
+ static const char *switching[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Manual",
+ "Auto-switch",
+ "Wide Range",
+ "N/A" /* 0x06 */
+ };
+
+ if (code >= 0x01 && code <= 0x06)
+ return switching[code - 0x01];
+ return out_of_spec;
+}
+
+/*
+ * 7.41 Additional Information (Type 40)
+ *
+ * Proper support of this entry type would require redesigning a large part of
+ * the code, so I am waiting to see actual implementations of it to decide
+ * whether it's worth the effort.
+ */
+
+static void dmi_additional_info(const struct dmi_header *h)
+{
+ u8 *p = h->data + 4;
+ u8 count = *p++;
+ u8 length;
+ int i, offset = 5;
+
+ for (i = 0; i < count; i++)
+ {
+ pr_handle_name("Additional Information %d", i + 1);
+
+ /* Check for short entries */
+ if (h->length < offset + 1) break;
+ length = p[0x00];
+ if (length < 0x05 || h->length < offset + length) break;
+
+ pr_attr("Referenced Handle", "0x%04x",
+ WORD(p + 0x01));
+ pr_attr("Referenced Offset", "0x%02x",
+ p[0x03]);
+ pr_attr("String", "%s",
+ dmi_string(h, p[0x04]));
+
+ switch (length - 0x05)
+ {
+ case 1:
+ pr_attr("Value", "0x%02x", p[0x05]);
+ break;
+ case 2:
+ pr_attr("Value", "0x%04x", WORD(p + 0x05));
+ break;
+ case 4:
+ pr_attr("Value", "0x%08x", DWORD(p + 0x05));
+ break;
+ default:
+ pr_attr("Value", "Unexpected size");
+ break;
+ }
+
+ p += length;
+ offset += length;
+ }
+}
+
+/*
+ * 7.43 Management Controller Host Interface (Type 42)
+ */
+
+static const char *dmi_management_controller_host_type(u8 code)
+{
+ /* DMTF DSP0239 (MCTP) version 1.1.0 */
+ static const char *type[] = {
+ "KCS: Keyboard Controller Style", /* 0x02 */
+ "8250 UART Register Compatible",
+ "16450 UART Register Compatible",
+ "16550/16550A UART Register Compatible",
+ "16650/16650A UART Register Compatible",
+ "16750/16750A UART Register Compatible",
+ "16850/16850A UART Register Compatible" /* 0x08 */
+ };
+
+ if (code >= 0x02 && code <= 0x08)
+ return type[code - 0x02];
+ if (code <= 0x3F)
+ return "MCTP";
+ if (code == 0x40)
+ return "Network";
+ if (code == 0xF0)
+ return "OEM";
+ return out_of_spec;
+}
+
+/*
+ * 7.43.2: Protocol Record Types
+ */
+static const char *dmi_protocol_record_type(u8 type)
+{
+ const char *protocol[] = {
+ "Reserved", /* 0x0 */
+ "Reserved",
+ "IPMI",
+ "MCTP",
+ "Redfish over IP", /* 0x4 */
+ };
+
+ if (type <= 0x4)
+ return protocol[type];
+ if (type == 0xF0)
+ return "OEM";
+ return out_of_spec;
+}
+
+/*
+ * DSP0270: 8.6: Protocol IP Assignment types
+ */
+static const char *dmi_protocol_assignment_type(u8 type)
+{
+ const char *assignment[] = {
+ "Unknown", /* 0x0 */
+ "Static",
+ "DHCP",
+ "AutoConf",
+ "Host Selected", /* 0x4 */
+ };
+
+ if (type <= 0x4)
+ return assignment[type];
+ return out_of_spec;
+}
+
+/*
+ * DSP0270: 8.6: Protocol IP Address type
+ */
+static const char *dmi_address_type(u8 type)
+{
+ const char *addressformat[] = {
+ "Unknown", /* 0x0 */
+ "IPv4",
+ "IPv6", /* 0x2 */
+ };
+
+ if (type <= 0x2)
+ return addressformat[type];
+ return out_of_spec;
+}
+
+/*
+ * DSP0270: 8.6 Protocol Address decode
+ */
+static const char *dmi_address_decode(u8 *data, char *storage, u8 addrtype)
+{
+ if (addrtype == 0x1) /* IPv4 */
+ return inet_ntop(AF_INET, data, storage, 64);
+ if (addrtype == 0x2) /* IPv6 */
+ return inet_ntop(AF_INET6, data, storage, 64);
+ return out_of_spec;
+}
+
+/*
+ * DSP0270: 8.5: Parse the protocol record format
+ */
+static void dmi_parse_protocol_record(u8 *rec)
+{
+ u8 rid;
+ u8 rlen;
+ u8 *rdata;
+ char buf[64];
+ u8 assign_val;
+ u8 addrtype;
+ u8 hlen;
+ const char *addrstr;
+ const char *hname;
+ char attr[38];
+
+ /* DSP0270: 8.5: Protocol Identifier */
+ rid = rec[0x0];
+ /* DSP0270: 8.5: Protocol Record Length */
+ rlen = rec[0x1];
+ /* DSP0270: 8.5: Protocol Record Data */
+ rdata = &rec[0x2];
+
+ pr_attr("Protocol ID", "%02x (%s)", rid,
+ dmi_protocol_record_type(rid));
+
+ /*
+ * Don't decode anything other than Redfish for now
+ * Note 0x4 is Redfish over IP in 7.43.2
+ * and DSP0270: 8.5
+ */
+ if (rid != 0x4)
+ return;
+
+ /*
+ * Ensure that the protocol record is of sufficient length
+ * For RedFish that means rlen must be at least 91 bytes
+ * other protcols will need different length checks
+ */
+ if (rlen < 91)
+ return;
+
+ /*
+ * DSP0270: 8.6: Redfish Over IP Service UUID
+ * Note: ver is hardcoded to 0x311 here just for
+ * convenience. It could get passed from the SMBIOS
+ * header, but that's a lot of passing of pointers just
+ * to get that info, and the only thing it is used for is
+ * to determine the endianness of the field. Since we only
+ * do this parsing on versions of SMBIOS after 3.1.1, and the
+ * endianness of the field is always little after version 2.6.0
+ * we can just pick a sufficiently recent version here.
+ */
+ dmi_system_uuid(pr_subattr, "Service UUID", &rdata[0], 0x311);
+
+ /*
+ * DSP0270: 8.6: Redfish Over IP Host IP Assignment Type
+ * Note, using decimal indices here, as the DSP0270
+ * uses decimal, so as to make it more comparable
+ */
+ assign_val = rdata[16];
+ pr_subattr("Host IP Assignment Type", "%s",
+ dmi_protocol_assignment_type(assign_val));
+
+ /* DSP0270: 8.6: Redfish Over IP Host Address format */
+ addrtype = rdata[17];
+ addrstr = dmi_address_type(addrtype);
+ pr_subattr("Host IP Address Format", "%s",
+ addrstr);
+
+ /* DSP0270: 8.6 IP Assignment types */
+ /* We only use the Host IP Address and Mask if the assignment type is static */
+ if (assign_val == 0x1 || assign_val == 0x3)
+ {
+ /* DSP0270: 8.6: the Host IPv[4|6] Address */
+ sprintf(attr, "%s Address", addrstr);
+ pr_subattr(attr, "%s",
+ dmi_address_decode(&rdata[18], buf, addrtype));
+
+ /* DSP0270: 8.6: Prints the Host IPv[4|6] Mask */
+ sprintf(attr, "%s Mask", addrstr);
+ pr_subattr(attr, "%s",
+ dmi_address_decode(&rdata[34], buf, addrtype));
+ }
+
+ /* DSP0270: 8.6: Get the Redfish Service IP Discovery Type */
+ assign_val = rdata[50];
+ /* Redfish Service IP Discovery type mirrors Host IP Assignment type */
+ pr_subattr("Redfish Service IP Discovery Type", "%s",
+ dmi_protocol_assignment_type(assign_val));
+
+ /* DSP0270: 8.6: Get the Redfish Service IP Address Format */
+ addrtype = rdata[51];
+ addrstr = dmi_address_type(addrtype);
+ pr_subattr("Redfish Service IP Address Format", "%s",
+ addrstr);
+
+ if (assign_val == 0x1 || assign_val == 0x3)
+ {
+ u16 port;
+ u32 vlan;
+
+ /* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Address */
+ sprintf(attr, "%s Redfish Service Address", addrstr);
+ pr_subattr(attr, "%s",
+ dmi_address_decode(&rdata[52], buf,
+ addrtype));
+
+ /* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Mask */
+ sprintf(attr, "%s Redfish Service Mask", addrstr);
+ pr_subattr(attr, "%s",
+ dmi_address_decode(&rdata[68], buf,
+ addrtype));
+
+ /* DSP0270: 8.6: Redfish vlan and port info */
+ port = WORD(&rdata[84]);
+ vlan = DWORD(&rdata[86]);
+ pr_subattr("Redfish Service Port", "%hu", port);
+ pr_subattr("Redfish Service Vlan", "%u", vlan);
+ }
+
+ /* DSP0270: 8.6: Redfish host length and name */
+ hlen = rdata[90];
+
+ /*
+ * DSP0270: 8.6: The length of the host string + 91 (the minimum
+ * size of a protocol record) cannot exceed the record length
+ * (rec[0x1])
+ */
+ hname = (const char *)&rdata[91];
+ if (hlen + 91 > rlen)
+ {
+ hname = out_of_spec;
+ hlen = strlen(out_of_spec);
+ }
+ pr_subattr("Redfish Service Hostname", "%.*s", hlen, hname);
+}
+
+/*
+ * DSP0270: 8.3: Device type ennumeration
+ */
+static const char *dmi_parse_device_type(u8 type)
+{
+ const char *devname[] = {
+ "USB", /* 0x2 */
+ "PCI/PCIe", /* 0x3 */
+ };
+
+ if (type >= 0x2 && type <= 0x3)
+ return devname[type - 0x2];
+ if (type >= 0x80)
+ return "OEM";
+ return out_of_spec;
+}
+
+static void dmi_parse_controller_structure(const struct dmi_header *h)
+{
+ int i;
+ u8 *data = h->data;
+ /* Host interface type */
+ u8 type;
+ /* Host Interface specific data length */
+ u8 len;
+ u8 count;
+ u32 total_read;
+
+ /*
+ * Minimum length of this struct is 0xB bytes
+ */
+ if (h->length < 0xB)
+ return;
+
+ /*
+ * Also need to ensure that the interface specific data length
+ * plus the size of the structure to that point don't exceed
+ * the defined length of the structure, or we will overrun its
+ * bounds
+ */
+ len = data[0x5];
+ total_read = len + 0x6;
+
+ if (total_read > h->length)
+ return;
+
+ type = data[0x4];
+ pr_attr("Host Interface Type", "%s",
+ dmi_management_controller_host_type(type));
+
+ /*
+ * The following decodes are code for Network interface host types only
+ * As defined in DSP0270
+ */
+ if (type != 0x40)
+ return;
+
+ if (len != 0)
+ {
+ /* DSP0270: 8.3 Table 2: Device Type */
+ type = data[0x6];
+
+ pr_attr("Device Type", "%s",
+ dmi_parse_device_type(type));
+ if (type == 0x2 && len >= 5)
+ {
+ /* USB Device Type - need at least 6 bytes */
+ u8 *usbdata = &data[0x7];
+ /* USB Device Descriptor: idVendor */
+ pr_attr("idVendor", "0x%04x",
+ WORD(&usbdata[0x0]));
+ /* USB Device Descriptor: idProduct */
+ pr_attr("idProduct", "0x%04x",
+ WORD(&usbdata[0x2]));
+ /*
+ * USB Serial number is here, but its useless, don't
+ * bother decoding it
+ */
+ }
+ else if (type == 0x3 && len >= 9)
+ {
+ /* PCI Device Type - Need at least 8 bytes */
+ u8 *pcidata = &data[0x7];
+ /* PCI Device Descriptor: VendorID */
+ pr_attr("VendorID", "0x%04x",
+ WORD(&pcidata[0x0]));
+ /* PCI Device Descriptor: DeviceID */
+ pr_attr("DeviceID", "0x%04x",
+ WORD(&pcidata[0x2]));
+ /* PCI Device Descriptor: PCI SubvendorID */
+ pr_attr("SubVendorID", "0x%04x",
+ WORD(&pcidata[0x4]));
+ /* PCI Device Descriptor: PCI SubdeviceID */
+ pr_attr("SubDeviceID", "0x%04x",
+ WORD(&pcidata[0x6]));
+ }
+ else if (type == 0x4 && len >= 5)
+ {
+ /* OEM Device Type - Need at least 4 bytes */
+ u8 *oemdata = &data[0x7];
+ /* OEM Device Descriptor: IANA */
+ pr_attr("Vendor ID", "0x%02x:0x%02x:0x%02x:0x%02x",
+ oemdata[0x0], oemdata[0x1],
+ oemdata[0x2], oemdata[0x3]);
+ }
+ /* Don't mess with unknown types for now */
+ }
+
+ /*
+ * DSP0270: 8.2 and 8.5: Protocol record count and protocol records
+ * Move to the Protocol Count.
+ */
+ data = &data[total_read];
+
+ /*
+ * We've validated up to 0x6 + len bytes, but we need to validate
+ * the next byte below, the count value.
+ */
+ total_read++;
+ if (total_read > h->length)
+ {
+ fprintf(stderr,
+ "Total read length %d exceeds total structure length %d (handle 0x%04hx)\n",
+ total_read, h->length, h->handle);
+ return;
+ }
+
+ /* Get the protocol records count */
+ count = data[0x0];
+ if (count)
+ {
+ u8 *rec = &data[0x1];
+ for (i = 0; i < count; i++)
+ {
+ /*
+ * Need to ensure that this record doesn't overrun
+ * the total length of the type 42 struct. Note the +2
+ * is added for the two leading bytes of a protocol
+ * record representing the type and length bytes.
+ */
+ total_read += rec[1] + 2;
+ if (total_read > h->length)
+ {
+ fprintf(stderr,
+ "Total read length %d exceeds total structure length %d (handle 0x%04hx, record %d)\n",
+ total_read, h->length, h->handle, i + 1);
+ return;
+ }
+
+ dmi_parse_protocol_record(rec);
+
+ /*
+ * DSP0270: 8.6
+ * Each record is rec[1] bytes long, starting at the
+ * data byte immediately following the length field.
+ * That means we need to add the byte for the rec id,
+ * the byte for the length field, and the value of the
+ * length field itself.
+ */
+ rec += rec[1] + 2;
+ }
+ }
+}
+
+/*
+ * 7.44 TPM Device (Type 43)
+ */
+
+static void dmi_tpm_vendor_id(const u8 *p)
+{
+ char vendor_id[5];
+ int i;
+
+ /* ASCII filtering */
+ for (i = 0; i < 4 && p[i] != 0; i++)
+ {
+ if (p[i] < 32 || p[i] >= 127)
+ vendor_id[i] = '.';
+ else
+ vendor_id[i] = p[i];
+ }
+
+ /* Terminate the string */
+ vendor_id[i] = '\0';
+
+ pr_attr("Vendor ID", "%s", vendor_id);
+}
+
+static void dmi_tpm_characteristics(u64 code)
+{
+ /* 7.1.1 */
+ static const char *characteristics[] = {
+ "TPM Device characteristics not supported", /* 2 */
+ "Family configurable via firmware update",
+ "Family configurable via platform software support",
+ "Family configurable via OEM proprietary mechanism" /* 5 */
+ };
+ int i;
+
+ /*
+ * This isn't very clear what this bit is supposed to mean
+ */
+ if (code.l & (1 << 2))
+ {
+ pr_list_item("%s", characteristics[0]);
+ return;
+ }
+
+ for (i = 3; i <= 5; i++)
+ if (code.l & (1 << i))
+ pr_list_item("%s", characteristics[i - 2]);
+}
+
+/*
+ * 7.46 Firmware Inventory Information (Type 45)
+ */
+
+static void dmi_firmware_characteristics(u16 code)
+{
+ /* 7.46.3 */
+ static const char *characteristics[] = {
+ "Updatable", /* 0 */
+ "Write-Protect" /* 1 */
+ };
+ int i;
+
+ for (i = 0; i <= 1; i++)
+ pr_list_item("%s: %s", characteristics[i],
+ (code & (1 << i)) ? "Yes" : "No");
+}
+
+static const char *dmi_firmware_state(u8 code)
+{
+ /* 7.46.4 */
+ static const char *state[] = {
+ "Other", /* 0x01 */
+ "Unknown",
+ "Disabled",
+ "Enabled",
+ "Absent",
+ "Stand-by Offline",
+ "Stand-by Spare",
+ "Unavailable Offline" /* 0x08 */
+ };
+
+ if (code >= 0x01 && code <= 0x08)
+ return state[code - 0x01];
+ return out_of_spec;
+}
+
+static void dmi_firmware_components(u8 count, const u8 *p)
+{
+ int i;
+
+ pr_list_start("Associated Components", "%u", count);
+ for (i = 0; i < count; i++)
+ pr_list_item("0x%04X", WORD(p + sizeof(u16) * i));
+ pr_list_end();
+}
+
+/*
+ * Main
+ */
+
+static void dmi_decode(const struct dmi_header *h, u16 ver)
+{
+ const u8 *data = h->data;
+
+ /*
+ * Note: DMI types 37 and 42 are untested
+ */
+ switch (h->type)
+ {
+ case 0: /* 7.1 BIOS Information */
+ pr_handle_name("BIOS Information");
+ if (h->length < 0x12) break;
+ pr_attr("Vendor", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Version", "%s",
+ dmi_string(h, data[0x05]));
+ pr_attr("Release Date", "%s",
+ dmi_string(h, data[0x08]));
+ /*
+ * On IA-64 and UEFI-based systems, the BIOS base
+ * address will read 0 because there is no BIOS. Skip
+ * the base address and the runtime size in this case.
+ */
+ if (WORD(data + 0x06) != 0)
+ {
+ pr_attr("Address", "0x%04X0",
+ WORD(data + 0x06));
+ dmi_bios_runtime_size((0x10000 - WORD(data + 0x06)) << 4);
+ }
+ dmi_bios_rom_size(data[0x09], h->length < 0x1A ? 16 : WORD(data + 0x18));
+ pr_list_start("Characteristics", NULL);
+ dmi_bios_characteristics(QWORD(data + 0x0A));
+ pr_list_end();
+ if (h->length < 0x13) break;
+ dmi_bios_characteristics_x1(data[0x12]);
+ if (h->length < 0x14) break;
+ dmi_bios_characteristics_x2(data[0x13]);
+ if (h->length < 0x18) break;
+ if (data[0x14] != 0xFF && data[0x15] != 0xFF)
+ pr_attr("BIOS Revision", "%u.%u",
+ data[0x14], data[0x15]);
+ if (data[0x16] != 0xFF && data[0x17] != 0xFF)
+ pr_attr("Firmware Revision", "%u.%u",
+ data[0x16], data[0x17]);
+ break;
+
+ case 1: /* 7.2 System Information */
+ pr_handle_name("System Information");
+ if (h->length < 0x08) break;
+ pr_attr("Manufacturer", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Product Name", "%s",
+ dmi_string(h, data[0x05]));
+ pr_attr("Version", "%s",
+ dmi_string(h, data[0x06]));
+ pr_attr("Serial Number", "%s",
+ dmi_string(h, data[0x07]));
+ if (h->length < 0x19) break;
+ dmi_system_uuid(pr_attr, "UUID", data + 0x08, ver);
+ pr_attr("Wake-up Type", "%s",
+ dmi_system_wake_up_type(data[0x18]));
+ if (h->length < 0x1B) break;
+ pr_attr("SKU Number", "%s",
+ dmi_string(h, data[0x19]));
+ pr_attr("Family", "%s",
+ dmi_string(h, data[0x1A]));
+ break;
+
+ case 2: /* 7.3 Base Board Information */
+ pr_handle_name("Base Board Information");
+ if (h->length < 0x08) break;
+ pr_attr("Manufacturer", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Product Name", "%s",
+ dmi_string(h, data[0x05]));
+ pr_attr("Version", "%s",
+ dmi_string(h, data[0x06]));
+ pr_attr("Serial Number", "%s",
+ dmi_string(h, data[0x07]));
+ if (h->length < 0x09) break;
+ pr_attr("Asset Tag", "%s",
+ dmi_string(h, data[0x08]));
+ if (h->length < 0x0A) break;
+ dmi_base_board_features(data[0x09]);
+ if (h->length < 0x0E) break;
+ pr_attr("Location In Chassis", "%s",
+ dmi_string(h, data[0x0A]));
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Chassis Handle", "0x%04X",
+ WORD(data + 0x0B));
+ pr_attr("Type", "%s",
+ dmi_base_board_type(data[0x0D]));
+ if (h->length < 0x0F) break;
+ if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
+ if (!(opt.flags & FLAG_QUIET))
+ dmi_base_board_handles(data[0x0E], data + 0x0F);
+ break;
+
+ case 3: /* 7.4 Chassis Information */
+ pr_handle_name("Chassis Information");
+ if (h->length < 0x09) break;
+ pr_attr("Manufacturer", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Type", "%s",
+ dmi_chassis_type(data[0x05]));
+ pr_attr("Lock", "%s",
+ dmi_chassis_lock(data[0x05] >> 7));
+ pr_attr("Version", "%s",
+ dmi_string(h, data[0x06]));
+ pr_attr("Serial Number", "%s",
+ dmi_string(h, data[0x07]));
+ pr_attr("Asset Tag", "%s",
+ dmi_string(h, data[0x08]));
+ if (h->length < 0x0D) break;
+ pr_attr("Boot-up State", "%s",
+ dmi_chassis_state(data[0x09]));
+ pr_attr("Power Supply State", "%s",
+ dmi_chassis_state(data[0x0A]));
+ pr_attr("Thermal State", "%s",
+ dmi_chassis_state(data[0x0B]));
+ pr_attr("Security Status", "%s",
+ dmi_chassis_security_status(data[0x0C]));
+ if (h->length < 0x11) break;
+ pr_attr("OEM Information", "0x%08X",
+ DWORD(data + 0x0D));
+ if (h->length < 0x13) break;
+ dmi_chassis_height(data[0x11]);
+ dmi_chassis_power_cords(data[0x12]);
+ if (h->length < 0x15) break;
+ if (h->length < 0x15 + data[0x13] * data[0x14]) break;
+ dmi_chassis_elements(data[0x13], data[0x14], data + 0x15);
+ if (h->length < 0x16 + data[0x13] * data[0x14]) break;
+ pr_attr("SKU Number", "%s",
+ dmi_string(h, data[0x15 + data[0x13] * data[0x14]]));
+ break;
+
+ case 4: /* 7.5 Processor Information */
+ pr_handle_name("Processor Information");
+ if (h->length < 0x1A) break;
+ pr_attr("Socket Designation", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Type", "%s",
+ dmi_processor_type(data[0x05]));
+ pr_attr("Family", "%s",
+ dmi_processor_family(h, ver));
+ pr_attr("Manufacturer", "%s",
+ dmi_string(h, data[0x07]));
+ dmi_processor_id(h);
+ pr_attr("Version", "%s",
+ dmi_string(h, data[0x10]));
+ dmi_processor_voltage("Voltage", data[0x11]);
+ dmi_processor_frequency("External Clock", data + 0x12);
+ dmi_processor_frequency("Max Speed", data + 0x14);
+ dmi_processor_frequency("Current Speed", data + 0x16);
+ if (data[0x18] & (1 << 6))
+ pr_attr("Status", "Populated, %s",
+ dmi_processor_status(data[0x18] & 0x07));
+ else
+ pr_attr("Status", "Unpopulated");
+ pr_attr("Upgrade", "%s",
+ dmi_processor_upgrade(data[0x19]));
+ if (h->length < 0x20) break;
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ dmi_processor_cache("L1 Cache Handle",
+ WORD(data + 0x1A), "L1", ver);
+ dmi_processor_cache("L2 Cache Handle",
+ WORD(data + 0x1C), "L2", ver);
+ dmi_processor_cache("L3 Cache Handle",
+ WORD(data + 0x1E), "L3", ver);
+ }
+ if (h->length < 0x23) break;
+ pr_attr("Serial Number", "%s",
+ dmi_string(h, data[0x20]));
+ pr_attr("Asset Tag", "%s",
+ dmi_string(h, data[0x21]));
+ pr_attr("Part Number", "%s",
+ dmi_string(h, data[0x22]));
+ if (h->length < 0x28) break;
+ if (data[0x23] != 0)
+ pr_attr("Core Count", "%u",
+ h->length >= 0x2C && data[0x23] == 0xFF ?
+ WORD(data + 0x2A) : data[0x23]);
+ if (data[0x24] != 0)
+ pr_attr("Core Enabled", "%u",
+ h->length >= 0x2E && data[0x24] == 0xFF ?
+ WORD(data + 0x2C) : data[0x24]);
+ if (data[0x25] != 0)
+ pr_attr("Thread Count", "%u",
+ h->length >= 0x30 && data[0x25] == 0xFF ?
+ WORD(data + 0x2E) : data[0x25]);
+ dmi_processor_characteristics("Characteristics",
+ WORD(data + 0x26));
+ break;
+
+ case 5: /* 7.6 Memory Controller Information */
+ pr_handle_name("Memory Controller Information");
+ if (h->length < 0x0F) break;
+ pr_attr("Error Detecting Method", "%s",
+ dmi_memory_controller_ed_method(data[0x04]));
+ dmi_memory_controller_ec_capabilities("Error Correcting Capabilities",
+ data[0x05]);
+ pr_attr("Supported Interleave", "%s",
+ dmi_memory_controller_interleave(data[0x06]));
+ pr_attr("Current Interleave", "%s",
+ dmi_memory_controller_interleave(data[0x07]));
+ pr_attr("Maximum Memory Module Size", "%u MB",
+ 1 << data[0x08]);
+ pr_attr("Maximum Total Memory Size", "%u MB",
+ data[0x0E] * (1 << data[0x08]));
+ dmi_memory_controller_speeds("Supported Speeds",
+ WORD(data + 0x09));
+ dmi_memory_module_types("Supported Memory Types",
+ WORD(data + 0x0B), 0);
+ dmi_processor_voltage("Memory Module Voltage", data[0x0D]);
+ if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
+ dmi_memory_controller_slots(data[0x0E], data + 0x0F);
+ if (h->length < 0x10 + data[0x0E] * sizeof(u16)) break;
+ dmi_memory_controller_ec_capabilities("Enabled Error Correcting Capabilities",
+ data[0x0F + data[0x0E] * sizeof(u16)]);
+ break;
+
+ case 6: /* 7.7 Memory Module Information */
+ pr_handle_name("Memory Module Information");
+ if (h->length < 0x0C) break;
+ pr_attr("Socket Designation", "%s",
+ dmi_string(h, data[0x04]));
+ dmi_memory_module_connections(data[0x05]);
+ dmi_memory_module_speed("Current Speed", data[0x06]);
+ dmi_memory_module_types("Type", WORD(data + 0x07), 1);
+ dmi_memory_module_size("Installed Size", data[0x09]);
+ dmi_memory_module_size("Enabled Size", data[0x0A]);
+ dmi_memory_module_error(data[0x0B]);
+ break;
+
+ case 7: /* 7.8 Cache Information */
+ pr_handle_name("Cache Information");
+ if (h->length < 0x0F) break;
+ pr_attr("Socket Designation", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Configuration", "%s, %s, Level %u",
+ WORD(data + 0x05) & 0x0080 ? "Enabled" : "Disabled",
+ WORD(data + 0x05) & 0x0008 ? "Socketed" : "Not Socketed",
+ (WORD(data + 0x05) & 0x0007) + 1);
+ pr_attr("Operational Mode", "%s",
+ dmi_cache_mode((WORD(data + 0x05) >> 8) & 0x0003));
+ pr_attr("Location", "%s",
+ dmi_cache_location((WORD(data + 0x05) >> 5) & 0x0003));
+ if (h->length >= 0x1B)
+ dmi_cache_size_2("Installed Size", DWORD(data + 0x17));
+ else
+ dmi_cache_size("Installed Size", WORD(data + 0x09));
+ if (h->length >= 0x17)
+ dmi_cache_size_2("Maximum Size", DWORD(data + 0x13));
+ else
+ dmi_cache_size("Maximum Size", WORD(data + 0x07));
+ dmi_cache_types("Supported SRAM Types", WORD(data + 0x0B), 0);
+ dmi_cache_types("Installed SRAM Type", WORD(data + 0x0D), 1);
+ if (h->length < 0x13) break;
+ dmi_memory_module_speed("Speed", data[0x0F]);
+ pr_attr("Error Correction Type", "%s",
+ dmi_cache_ec_type(data[0x10]));
+ pr_attr("System Type", "%s",
+ dmi_cache_type(data[0x11]));
+ pr_attr("Associativity", "%s",
+ dmi_cache_associativity(data[0x12]));
+ break;
+
+ case 8: /* 7.9 Port Connector Information */
+ pr_handle_name("Port Connector Information");
+ if (h->length < 0x09) break;
+ pr_attr("Internal Reference Designator", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Internal Connector Type", "%s",
+ dmi_port_connector_type(data[0x05]));
+ pr_attr("External Reference Designator", "%s",
+ dmi_string(h, data[0x06]));
+ pr_attr("External Connector Type", "%s",
+ dmi_port_connector_type(data[0x07]));
+ pr_attr("Port Type", "%s",
+ dmi_port_type(data[0x08]));
+ break;
+
+ case 9: /* 7.10 System Slots */
+ pr_handle_name("System Slot Information");
+ if (h->length < 0x0C) break;
+ pr_attr("Designation", "%s",
+ dmi_string(h, data[0x04]));
+ dmi_slot_type_with_width(data[0x05], data[0x06]);
+ pr_attr("Current Usage", "%s",
+ dmi_slot_current_usage(data[0x07]));
+ pr_attr("Length", "%s",
+ dmi_slot_length(data[0x08]));
+ dmi_slot_id(data[0x09], data[0x0A], data[0x05]);
+ if (h->length < 0x0D)
+ dmi_slot_characteristics("Characteristics", data[0x0B], 0x00);
+ else
+ dmi_slot_characteristics("Characteristics", data[0x0B], data[0x0C]);
+ if (h->length < 0x11) break;
+ dmi_slot_segment_bus_func(WORD(data + 0x0D), data[0x0F], data[0x10]);
+ if (h->length < 0x13) break;
+ pr_attr("Data Bus Width", "%u", data[0x11]);
+ pr_attr("Peer Devices", "%u", data[0x12]);
+ if (h->length < 0x13 + data[0x12] * 5) break;
+ dmi_slot_peers(data[0x12], data + 0x13);
+ if (h->length < 0x17 + data[0x12] * 5) break;
+ dmi_slot_information(data[0x05], data[0x13 + data[0x12] * 5]);
+ dmi_slot_physical_width(data[0x14 + data[0x12] * 5]);
+ dmi_slot_pitch(WORD(data + 0x15 + data[0x12] * 5));
+ if (h->length < 0x18 + data[0x12] * 5) break;
+ pr_attr("Height", "%s",
+ dmi_slot_height(data[0x17 + data[0x12] * 5]));
+ break;
+
+ case 10: /* 7.11 On Board Devices Information */
+ dmi_on_board_devices(h);
+ break;
+
+ case 11: /* 7.12 OEM Strings */
+ pr_handle_name("OEM Strings");
+ if (h->length < 0x05) break;
+ dmi_oem_strings(h);
+ break;
+
+ case 12: /* 7.13 System Configuration Options */
+ pr_handle_name("System Configuration Options");
+ if (h->length < 0x05) break;
+ dmi_system_configuration_options(h);
+ break;
+
+ case 13: /* 7.14 BIOS Language Information */
+ pr_handle_name("BIOS Language Information");
+ if (h->length < 0x16) break;
+ if (ver >= 0x0201)
+ {
+ pr_attr("Language Description Format", "%s",
+ dmi_bios_language_format(data[0x05]));
+ }
+ pr_list_start("Installable Languages", "%u", data[0x04]);
+ dmi_bios_languages(h);
+ pr_list_end();
+ pr_attr("Currently Installed Language", "%s",
+ dmi_string(h, data[0x15]));
+ break;
+
+ case 14: /* 7.15 Group Associations */
+ pr_handle_name("Group Associations");
+ if (h->length < 0x05) break;
+ pr_attr("Name", "%s",
+ dmi_string(h, data[0x04]));
+ pr_list_start("Items", "%u",
+ (h->length - 0x05) / 3);
+ dmi_group_associations_items((h->length - 0x05) / 3, data + 0x05);
+ pr_list_end();
+ break;
+
+ case 15: /* 7.16 System Event Log */
+ pr_handle_name("System Event Log");
+ if (h->length < 0x14) break;
+ pr_attr("Area Length", "%u bytes",
+ WORD(data + 0x04));
+ pr_attr("Header Start Offset", "0x%04X",
+ WORD(data + 0x06));
+ if (WORD(data + 0x08) - WORD(data + 0x06))
+ pr_attr("Header Length", "%u byte%s",
+ WORD(data + 0x08) - WORD(data + 0x06),
+ WORD(data + 0x08) - WORD(data + 0x06) > 1 ? "s" : "");
+ pr_attr("Data Start Offset", "0x%04X",
+ WORD(data + 0x08));
+ pr_attr("Access Method", "%s",
+ dmi_event_log_method(data[0x0A]));
+ dmi_event_log_address(data[0x0A], data + 0x10);
+ dmi_event_log_status(data[0x0B]);
+ pr_attr("Change Token", "0x%08X",
+ DWORD(data + 0x0C));
+ if (h->length < 0x17) break;
+ pr_attr("Header Format", "%s",
+ dmi_event_log_header_type(data[0x14]));
+ pr_attr("Supported Log Type Descriptors", "%u",
+ data[0x15]);
+ if (h->length < 0x17 + data[0x15] * data[0x16]) break;
+ dmi_event_log_descriptors(data[0x15], data[0x16], data + 0x17);
+ break;
+
+ case 16: /* 7.17 Physical Memory Array */
+ pr_handle_name("Physical Memory Array");
+ if (h->length < 0x0F) break;
+ pr_attr("Location", "%s",
+ dmi_memory_array_location(data[0x04]));
+ pr_attr("Use", "%s",
+ dmi_memory_array_use(data[0x05]));
+ pr_attr("Error Correction Type", "%s",
+ dmi_memory_array_ec_type(data[0x06]));
+ if (DWORD(data + 0x07) == 0x80000000)
+ {
+ if (h->length < 0x17)
+ pr_attr("Maximum Capacity", "Unknown");
+ else
+ dmi_print_memory_size("Maximum Capacity",
+ QWORD(data + 0x0F), 0);
+ }
+ else
+ {
+ u64 capacity;
+
+ capacity.h = 0;
+ capacity.l = DWORD(data + 0x07);
+ dmi_print_memory_size("Maximum Capacity",
+ capacity, 1);
+ }
+ if (!(opt.flags & FLAG_QUIET))
+ dmi_memory_array_error_handle(WORD(data + 0x0B));
+ pr_attr("Number Of Devices", "%u",
+ WORD(data + 0x0D));
+ break;
+
+ case 17: /* 7.18 Memory Device */
+ pr_handle_name("Memory Device");
+ if (h->length < 0x15) break;
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ pr_attr("Array Handle", "0x%04X",
+ WORD(data + 0x04));
+ dmi_memory_array_error_handle(WORD(data + 0x06));
+ }
+ dmi_memory_device_width("Total Width", WORD(data + 0x08));
+ dmi_memory_device_width("Data Width", WORD(data + 0x0A));
+ if (h->length >= 0x20 && WORD(data + 0x0C) == 0x7FFF)
+ dmi_memory_device_extended_size(DWORD(data + 0x1C));
+ else
+ dmi_memory_device_size(WORD(data + 0x0C));
+ pr_attr("Form Factor", "%s",
+ dmi_memory_device_form_factor(data[0x0E]));
+ dmi_memory_device_set(data[0x0F]);
+ pr_attr("Locator", "%s",
+ dmi_string(h, data[0x10]));
+ pr_attr("Bank Locator", "%s",
+ dmi_string(h, data[0x11]));
+ pr_attr("Type", "%s",
+ dmi_memory_device_type(data[0x12]));
+ dmi_memory_device_type_detail(WORD(data + 0x13));
+ if (h->length < 0x17) break;
+ /* If no module is present, the remaining fields are irrelevant */
+ if (WORD(data + 0x0C) == 0 && !(opt.flags & FLAG_NO_QUIRKS))
+ break;
+ dmi_memory_device_speed("Speed", WORD(data + 0x15),
+ h->length >= 0x5C ?
+ DWORD(data + 0x54) : 0);
+ if (h->length < 0x1B) break;
+ pr_attr("Manufacturer", "%s",
+ dmi_string(h, data[0x17]));
+ pr_attr("Serial Number", "%s",
+ dmi_string(h, data[0x18]));
+ pr_attr("Asset Tag", "%s",
+ dmi_string(h, data[0x19]));
+ pr_attr("Part Number", "%s",
+ dmi_string(h, data[0x1A]));
+ if (h->length < 0x1C) break;
+ if ((data[0x1B] & 0x0F) == 0)
+ pr_attr("Rank", "Unknown");
+ else
+ pr_attr("Rank", "%u", data[0x1B] & 0x0F);
+ if (h->length < 0x22) break;
+ dmi_memory_device_speed("Configured Memory Speed",
+ WORD(data + 0x20),
+ h->length >= 0x5C ?
+ DWORD(data + 0x58) : 0);
+ if (h->length < 0x28) break;
+ dmi_memory_voltage_value("Minimum Voltage",
+ WORD(data + 0x22));
+ dmi_memory_voltage_value("Maximum Voltage",
+ WORD(data + 0x24));
+ dmi_memory_voltage_value("Configured Voltage",
+ WORD(data + 0x26));
+ if (h->length < 0x34) break;
+ dmi_memory_technology(data[0x28]);
+ dmi_memory_operating_mode_capability(WORD(data + 0x29));
+ pr_attr("Firmware Version", "%s",
+ dmi_string(h, data[0x2B]));
+ dmi_memory_manufacturer_id("Module Manufacturer ID",
+ WORD(data + 0x2C));
+ dmi_memory_product_id("Module Product ID",
+ WORD(data + 0x2E));
+ dmi_memory_manufacturer_id("Memory Subsystem Controller Manufacturer ID",
+ WORD(data + 0x30));
+ dmi_memory_product_id("Memory Subsystem Controller Product ID",
+ WORD(data + 0x32));
+ if (h->length < 0x3C) break;
+ dmi_memory_size("Non-Volatile Size", QWORD(data + 0x34));
+ if (h->length < 0x44) break;
+ dmi_memory_size("Volatile Size", QWORD(data + 0x3C));
+ if (h->length < 0x4C) break;
+ dmi_memory_size("Cache Size", QWORD(data + 0x44));
+ if (h->length < 0x54) break;
+ dmi_memory_size("Logical Size", QWORD(data + 0x4C));
+ break;
+
+ case 18: /* 7.19 32-bit Memory Error Information */
+ pr_handle_name("32-bit Memory Error Information");
+ if (h->length < 0x17) break;
+ pr_attr("Type", "%s",
+ dmi_memory_error_type(data[0x04]));
+ pr_attr("Granularity", "%s",
+ dmi_memory_error_granularity(data[0x05]));
+ pr_attr("Operation", "%s",
+ dmi_memory_error_operation(data[0x06]));
+ dmi_memory_error_syndrome(DWORD(data + 0x07));
+ dmi_32bit_memory_error_address("Memory Array Address",
+ DWORD(data + 0x0B));
+ dmi_32bit_memory_error_address("Device Address",
+ DWORD(data + 0x0F));
+ dmi_32bit_memory_error_address("Resolution",
+ DWORD(data + 0x13));
+ break;
+
+ case 19: /* 7.20 Memory Array Mapped Address */
+ pr_handle_name("Memory Array Mapped Address");
+ if (h->length < 0x0F) break;
+ if (h->length >= 0x1F && DWORD(data + 0x04) == 0xFFFFFFFF)
+ {
+ u64 start, end;
+
+ start = QWORD(data + 0x0F);
+ end = QWORD(data + 0x17);
+
+ pr_attr("Starting Address", "0x%08X%08Xk",
+ start.h, start.l);
+ pr_attr("Ending Address", "0x%08X%08Xk",
+ end.h, end.l);
+ dmi_mapped_address_extended_size(start, end);
+ }
+ else
+ {
+ pr_attr("Starting Address", "0x%08X%03X",
+ DWORD(data + 0x04) >> 2,
+ (DWORD(data + 0x04) & 0x3) << 10);
+ pr_attr("Ending Address", "0x%08X%03X",
+ DWORD(data + 0x08) >> 2,
+ ((DWORD(data + 0x08) & 0x3) << 10) + 0x3FF);
+ dmi_mapped_address_size(DWORD(data + 0x08) - DWORD(data + 0x04) + 1);
+ }
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Physical Array Handle", "0x%04X",
+ WORD(data + 0x0C));
+ pr_attr("Partition Width", "%u",
+ data[0x0E]);
+ break;
+
+ case 20: /* 7.21 Memory Device Mapped Address */
+ pr_handle_name("Memory Device Mapped Address");
+ if (h->length < 0x13) break;
+ if (h->length >= 0x23 && DWORD(data + 0x04) == 0xFFFFFFFF)
+ {
+ u64 start, end;
+
+ start = QWORD(data + 0x13);
+ end = QWORD(data + 0x1B);
+
+ pr_attr("Starting Address", "0x%08X%08Xk",
+ start.h, start.l);
+ pr_attr("Ending Address", "0x%08X%08Xk",
+ end.h, end.l);
+ dmi_mapped_address_extended_size(start, end);
+ }
+ else
+ {
+ pr_attr("Starting Address", "0x%08X%03X",
+ DWORD(data + 0x04) >> 2,
+ (DWORD(data + 0x04) & 0x3) << 10);
+ pr_attr("Ending Address", "0x%08X%03X",
+ DWORD(data + 0x08) >> 2,
+ ((DWORD(data + 0x08) & 0x3) << 10) + 0x3FF);
+ dmi_mapped_address_size(DWORD(data + 0x08) - DWORD(data + 0x04) + 1);
+ }
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ pr_attr("Physical Device Handle", "0x%04X",
+ WORD(data + 0x0C));
+ pr_attr("Memory Array Mapped Address Handle", "0x%04X",
+ WORD(data + 0x0E));
+ }
+ dmi_mapped_address_row_position(data[0x10]);
+ dmi_mapped_address_interleave_position(data[0x11]);
+ dmi_mapped_address_interleaved_data_depth(data[0x12]);
+ break;
+
+ case 21: /* 7.22 Built-in Pointing Device */
+ pr_handle_name("Built-in Pointing Device");
+ if (h->length < 0x07) break;
+ pr_attr("Type", "%s",
+ dmi_pointing_device_type(data[0x04]));
+ pr_attr("Interface", "%s",
+ dmi_pointing_device_interface(data[0x05]));
+ pr_attr("Buttons", "%u",
+ data[0x06]);
+ break;
+
+ case 22: /* 7.23 Portable Battery */
+ pr_handle_name("Portable Battery");
+ if (h->length < 0x10) break;
+ pr_attr("Location", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Manufacturer", "%s",
+ dmi_string(h, data[0x05]));
+ if (data[0x06] || h->length < 0x1A)
+ pr_attr("Manufacture Date", "%s",
+ dmi_string(h, data[0x06]));
+ if (data[0x07] || h->length < 0x1A)
+ pr_attr("Serial Number", "%s",
+ dmi_string(h, data[0x07]));
+ pr_attr("Name", "%s",
+ dmi_string(h, data[0x08]));
+ if (data[0x09] != 0x02 || h->length < 0x1A)
+ pr_attr("Chemistry", "%s",
+ dmi_battery_chemistry(data[0x09]));
+ if (h->length < 0x16)
+ dmi_battery_capacity(WORD(data + 0x0A), 1);
+ else
+ dmi_battery_capacity(WORD(data + 0x0A), data[0x15]);
+ dmi_battery_voltage(WORD(data + 0x0C));
+ pr_attr("SBDS Version", "%s",
+ dmi_string(h, data[0x0E]));
+ dmi_battery_maximum_error(data[0x0F]);
+ if (h->length < 0x1A) break;
+ if (data[0x07] == 0)
+ pr_attr("SBDS Serial Number", "%04X",
+ WORD(data + 0x10));
+ if (data[0x06] == 0)
+ pr_attr("SBDS Manufacture Date", "%u-%02u-%02u",
+ 1980 + (WORD(data + 0x12) >> 9),
+ (WORD(data + 0x12) >> 5) & 0x0F,
+ WORD(data + 0x12) & 0x1F);
+ if (data[0x09] == 0x02)
+ pr_attr("SBDS Chemistry", "%s",
+ dmi_string(h, data[0x14]));
+ pr_attr("OEM-specific Information", "0x%08X",
+ DWORD(data + 0x16));
+ break;
+
+ case 23: /* 7.24 System Reset */
+ pr_handle_name("System Reset");
+ if (h->length < 0x0D) break;
+ pr_attr("Status", "%s",
+ data[0x04] & (1 << 0) ? "Enabled" : "Disabled");
+ pr_attr("Watchdog Timer", "%s",
+ data[0x04] & (1 << 5) ? "Present" : "Not Present");
+ if (!(data[0x04] & (1 << 5)))
+ break;
+ pr_attr("Boot Option", "%s",
+ dmi_system_reset_boot_option((data[0x04] >> 1) & 0x3));
+ pr_attr("Boot Option On Limit", "%s",
+ dmi_system_reset_boot_option((data[0x04] >> 3) & 0x3));
+ dmi_system_reset_count("Reset Count", WORD(data + 0x05));
+ dmi_system_reset_count("Reset Limit", WORD(data + 0x07));
+ dmi_system_reset_timer("Timer Interval", WORD(data + 0x09));
+ dmi_system_reset_timer("Timeout", WORD(data + 0x0B));
+ break;
+
+ case 24: /* 7.25 Hardware Security */
+ pr_handle_name("Hardware Security");
+ if (h->length < 0x05) break;
+ pr_attr("Power-On Password Status", "%s",
+ dmi_hardware_security_status(data[0x04] >> 6));
+ pr_attr("Keyboard Password Status", "%s",
+ dmi_hardware_security_status((data[0x04] >> 4) & 0x3));
+ pr_attr("Administrator Password Status", "%s",
+ dmi_hardware_security_status((data[0x04] >> 2) & 0x3));
+ pr_attr("Front Panel Reset Status", "%s",
+ dmi_hardware_security_status(data[0x04] & 0x3));
+ break;
+
+ case 25: /* 7.26 System Power Controls */
+ pr_handle_name("System Power Controls");
+ if (h->length < 0x09) break;
+ dmi_power_controls_power_on(data + 0x04);
+ break;
+
+ case 26: /* 7.27 Voltage Probe */
+ pr_handle_name("Voltage Probe");
+ if (h->length < 0x14) break;
+ pr_attr("Description", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Location", "%s",
+ dmi_voltage_probe_location(data[0x05] & 0x1f));
+ pr_attr("Status", "%s",
+ dmi_probe_status(data[0x05] >> 5));
+ dmi_voltage_probe_value("Maximum Value", WORD(data + 0x06));
+ dmi_voltage_probe_value("Minimum Value", WORD(data + 0x08));
+ dmi_voltage_probe_resolution(WORD(data + 0x0A));
+ dmi_voltage_probe_value("Tolerance", WORD(data + 0x0C));
+ dmi_probe_accuracy(WORD(data + 0x0E));
+ pr_attr("OEM-specific Information", "0x%08X",
+ DWORD(data + 0x10));
+ if (h->length < 0x16) break;
+ dmi_voltage_probe_value("Nominal Value", WORD(data + 0x14));
+ break;
+
+ case 27: /* 7.28 Cooling Device */
+ pr_handle_name("Cooling Device");
+ if (h->length < 0x0C) break;
+ if (!(opt.flags & FLAG_QUIET) && WORD(data + 0x04) != 0xFFFF)
+ pr_attr("Temperature Probe Handle", "0x%04X",
+ WORD(data + 0x04));
+ pr_attr("Type", "%s",
+ dmi_cooling_device_type(data[0x06] & 0x1f));
+ pr_attr("Status", "%s",
+ dmi_probe_status(data[0x06] >> 5));
+ if (data[0x07] != 0x00)
+ pr_attr("Cooling Unit Group", "%u",
+ data[0x07]);
+ pr_attr("OEM-specific Information", "0x%08X",
+ DWORD(data + 0x08));
+ if (h->length < 0x0E) break;
+ dmi_cooling_device_speed(WORD(data + 0x0C));
+ if (h->length < 0x0F) break;
+ pr_attr("Description", "%s", dmi_string(h, data[0x0E]));
+ break;
+
+ case 28: /* 7.29 Temperature Probe */
+ pr_handle_name("Temperature Probe");
+ if (h->length < 0x14) break;
+ pr_attr("Description", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Location", "%s",
+ dmi_temperature_probe_location(data[0x05] & 0x1F));
+ pr_attr("Status", "%s",
+ dmi_probe_status(data[0x05] >> 5));
+ dmi_temperature_probe_value("Maximum Value",
+ WORD(data + 0x06));
+ dmi_temperature_probe_value("Minimum Value",
+ WORD(data + 0x08));
+ dmi_temperature_probe_resolution(WORD(data + 0x0A));
+ dmi_temperature_probe_value("Tolerance",
+ WORD(data + 0x0C));
+ dmi_probe_accuracy(WORD(data + 0x0E));
+ pr_attr("OEM-specific Information", "0x%08X",
+ DWORD(data + 0x10));
+ if (h->length < 0x16) break;
+ dmi_temperature_probe_value("Nominal Value",
+ WORD(data + 0x14));
+ break;
+
+ case 29: /* 7.30 Electrical Current Probe */
+ pr_handle_name("Electrical Current Probe");
+ if (h->length < 0x14) break;
+ pr_attr("Description", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Location", "%s",
+ dmi_voltage_probe_location(data[5] & 0x1F));
+ pr_attr("Status", "%s",
+ dmi_probe_status(data[0x05] >> 5));
+ dmi_current_probe_value("Maximum Value",
+ WORD(data + 0x06));
+ dmi_current_probe_value("Minimum Value",
+ WORD(data + 0x08));
+ dmi_current_probe_resolution(WORD(data + 0x0A));
+ dmi_current_probe_value("Tolerance",
+ WORD(data + 0x0C));
+ dmi_probe_accuracy(WORD(data + 0x0E));
+ pr_attr("OEM-specific Information", "0x%08X",
+ DWORD(data + 0x10));
+ if (h->length < 0x16) break;
+ dmi_current_probe_value("Nominal Value",
+ WORD(data + 0x14));
+ break;
+
+ case 30: /* 7.31 Out-of-band Remote Access */
+ pr_handle_name("Out-of-band Remote Access");
+ if (h->length < 0x06) break;
+ pr_attr("Manufacturer Name", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Inbound Connection", "%s",
+ data[0x05] & (1 << 0) ? "Enabled" : "Disabled");
+ pr_attr("Outbound Connection", "%s",
+ data[0x05] & (1 << 1) ? "Enabled" : "Disabled");
+ break;
+
+ case 31: /* 7.32 Boot Integrity Services Entry Point */
+ pr_handle_name("Boot Integrity Services Entry Point");
+ if (h->length < 0x1C) break;
+ pr_attr("Checksum", "%s",
+ checksum(data, h->length) ? "OK" : "Invalid");
+ pr_attr("16-bit Entry Point Address", "%04X:%04X",
+ DWORD(data + 0x08) >> 16,
+ DWORD(data + 0x08) & 0xFFFF);
+ pr_attr("32-bit Entry Point Address", "0x%08X",
+ DWORD(data + 0x0C));
+ break;
+
+ case 32: /* 7.33 System Boot Information */
+ pr_handle_name("System Boot Information");
+ if (h->length < 0x0B) break;
+ pr_attr("Status", "%s",
+ dmi_system_boot_status(data[0x0A]));
+ break;
+
+ case 33: /* 7.34 64-bit Memory Error Information */
+ pr_handle_name("64-bit Memory Error Information");
+ if (h->length < 0x1F) break;
+ pr_attr("Type", "%s",
+ dmi_memory_error_type(data[0x04]));
+ pr_attr("Granularity", "%s",
+ dmi_memory_error_granularity(data[0x05]));
+ pr_attr("Operation", "%s",
+ dmi_memory_error_operation(data[0x06]));
+ dmi_memory_error_syndrome(DWORD(data + 0x07));
+ dmi_64bit_memory_error_address("Memory Array Address",
+ QWORD(data + 0x0B));
+ dmi_64bit_memory_error_address("Device Address",
+ QWORD(data + 0x13));
+ dmi_32bit_memory_error_address("Resolution",
+ DWORD(data + 0x1B));
+ break;
+
+ case 34: /* 7.35 Management Device */
+ pr_handle_name("Management Device");
+ if (h->length < 0x0B) break;
+ pr_attr("Description", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Type", "%s",
+ dmi_management_device_type(data[0x05]));
+ pr_attr("Address", "0x%08X",
+ DWORD(data + 0x06));
+ pr_attr("Address Type", "%s",
+ dmi_management_device_address_type(data[0x0A]));
+ break;
+
+ case 35: /* 7.36 Management Device Component */
+ pr_handle_name("Management Device Component");
+ if (h->length < 0x0B) break;
+ pr_attr("Description", "%s",
+ dmi_string(h, data[0x04]));
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ pr_attr("Management Device Handle", "0x%04X",
+ WORD(data + 0x05));
+ pr_attr("Component Handle", "0x%04X",
+ WORD(data + 0x07));
+ if (WORD(data + 0x09) != 0xFFFF)
+ pr_attr("Threshold Handle", "0x%04X",
+ WORD(data + 0x09));
+ }
+ break;
+
+ case 36: /* 7.37 Management Device Threshold Data */
+ pr_handle_name("Management Device Threshold Data");
+ if (h->length < 0x10) break;
+ if (WORD(data + 0x04) != 0x8000)
+ pr_attr("Lower Non-critical Threshold", "%d",
+ (i16)WORD(data + 0x04));
+ if (WORD(data + 0x06) != 0x8000)
+ pr_attr("Upper Non-critical Threshold", "%d",
+ (i16)WORD(data + 0x06));
+ if (WORD(data + 0x08) != 0x8000)
+ pr_attr("Lower Critical Threshold", "%d",
+ (i16)WORD(data + 0x08));
+ if (WORD(data + 0x0A) != 0x8000)
+ pr_attr("Upper Critical Threshold", "%d",
+ (i16)WORD(data + 0x0A));
+ if (WORD(data + 0x0C) != 0x8000)
+ pr_attr("Lower Non-recoverable Threshold", "%d",
+ (i16)WORD(data + 0x0C));
+ if (WORD(data + 0x0E) != 0x8000)
+ pr_attr("Upper Non-recoverable Threshold", "%d",
+ (i16)WORD(data + 0x0E));
+ break;
+
+ case 37: /* 7.38 Memory Channel */
+ pr_handle_name("Memory Channel");
+ if (h->length < 0x07) break;
+ pr_attr("Type", "%s",
+ dmi_memory_channel_type(data[0x04]));
+ pr_attr("Maximal Load", "%u",
+ data[0x05]);
+ pr_attr("Devices", "%u",
+ data[0x06]);
+ if (h->length < 0x07 + 3 * data[0x06]) break;
+ dmi_memory_channel_devices(data[0x06], data + 0x07);
+ break;
+
+ case 38: /* 7.39 IPMI Device Information */
+ /*
+ * We use the word "Version" instead of "Revision", conforming to
+ * the IPMI specification.
+ */
+ pr_handle_name("IPMI Device Information");
+ if (h->length < 0x10) break;
+ pr_attr("Interface Type", "%s",
+ dmi_ipmi_interface_type(data[0x04]));
+ pr_attr("Specification Version", "%u.%u",
+ data[0x05] >> 4, data[0x05] & 0x0F);
+ pr_attr("I2C Slave Address", "0x%02x",
+ data[0x06] >> 1);
+ if (data[0x07] != 0xFF)
+ pr_attr("NV Storage Device Address", "%u",
+ data[0x07]);
+ else
+ pr_attr("NV Storage Device", "Not Present");
+ dmi_ipmi_base_address(data[0x04], data + 0x08,
+ h->length < 0x11 ? 0 : (data[0x10] >> 4) & 1);
+ if (h->length < 0x12) break;
+ if (data[0x04] != 0x04)
+ {
+ pr_attr("Register Spacing", "%s",
+ dmi_ipmi_register_spacing(data[0x10] >> 6));
+ if (data[0x10] & (1 << 3))
+ {
+ pr_attr("Interrupt Polarity", "%s",
+ data[0x10] & (1 << 1) ? "Active High" : "Active Low");
+ pr_attr("Interrupt Trigger Mode", "%s",
+ data[0x10] & (1 << 0) ? "Level" : "Edge");
+ }
+ }
+ if (data[0x11] != 0x00)
+ {
+ pr_attr("Interrupt Number", "%u",
+ data[0x11]);
+ }
+ break;
+
+ case 39: /* 7.40 System Power Supply */
+ pr_handle_name("System Power Supply");
+ if (h->length < 0x10) break;
+ if (data[0x04] != 0x00)
+ pr_attr("Power Unit Group", "%u",
+ data[0x04]);
+ pr_attr("Location", "%s",
+ dmi_string(h, data[0x05]));
+ pr_attr("Name", "%s",
+ dmi_string(h, data[0x06]));
+ pr_attr("Manufacturer", "%s",
+ dmi_string(h, data[0x07]));
+ pr_attr("Serial Number", "%s",
+ dmi_string(h, data[0x08]));
+ pr_attr("Asset Tag", "%s",
+ dmi_string(h, data[0x09]));
+ pr_attr("Model Part Number", "%s",
+ dmi_string(h, data[0x0A]));
+ pr_attr("Revision", "%s",
+ dmi_string(h, data[0x0B]));
+ dmi_power_supply_power(WORD(data + 0x0C));
+ if (WORD(data + 0x0E) & (1 << 1))
+ pr_attr("Status", "Present, %s",
+ dmi_power_supply_status((WORD(data + 0x0E) >> 7) & 0x07));
+ else
+ pr_attr("Status", "Not Present");
+ pr_attr("Type", "%s",
+ dmi_power_supply_type((WORD(data + 0x0E) >> 10) & 0x0F));
+ pr_attr("Input Voltage Range Switching", "%s",
+ dmi_power_supply_range_switching((WORD(data + 0x0E) >> 3) & 0x0F));
+ pr_attr("Plugged", "%s",
+ WORD(data + 0x0E) & (1 << 2) ? "No" : "Yes");
+ pr_attr("Hot Replaceable", "%s",
+ WORD(data + 0x0E) & (1 << 0) ? "Yes" : "No");
+ if (h->length < 0x16) break;
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ if (WORD(data + 0x10) != 0xFFFF)
+ pr_attr("Input Voltage Probe Handle", "0x%04X",
+ WORD(data + 0x10));
+ if (WORD(data + 0x12) != 0xFFFF)
+ pr_attr("Cooling Device Handle", "0x%04X",
+ WORD(data + 0x12));
+ if (WORD(data + 0x14) != 0xFFFF)
+ pr_attr("Input Current Probe Handle", "0x%04X",
+ WORD(data + 0x14));
+ }
+ break;
+
+ case 40: /* 7.41 Additional Information */
+ if (h->length < 0x0B) break;
+ if (opt.flags & FLAG_QUIET)
+ return;
+ dmi_additional_info(h);
+ break;
+
+ case 41: /* 7.42 Onboard Device Extended Information */
+ pr_handle_name("Onboard Device");
+ if (h->length < 0x0B) break;
+ pr_attr("Reference Designation", "%s", dmi_string(h, data[0x04]));
+ pr_attr("Type", "%s",
+ dmi_on_board_devices_type(data[0x05] & 0x7F));
+ pr_attr("Status", "%s",
+ data[0x05] & 0x80 ? "Enabled" : "Disabled");
+ pr_attr("Type Instance", "%u", data[0x06]);
+ dmi_slot_segment_bus_func(WORD(data + 0x07), data[0x09], data[0x0A]);
+ break;
+
+ case 42: /* 7.43 Management Controller Host Interface */
+ pr_handle_name("Management Controller Host Interface");
+ if (ver < 0x0302)
+ {
+ if (h->length < 0x05) break;
+ pr_attr("Interface Type", "%s",
+ dmi_management_controller_host_type(data[0x04]));
+ /*
+ * There you have a type-dependent, variable-length
+ * part in the middle of the structure, with no
+ * length specifier, so no easy way to decode the
+ * common, final part of the structure. What a pity.
+ */
+ if (h->length < 0x09) break;
+ if (data[0x04] == 0xF0) /* OEM */
+ {
+ pr_attr("Vendor ID", "0x%02X%02X%02X%02X",
+ data[0x05], data[0x06], data[0x07],
+ data[0x08]);
+ }
+ }
+ else
+ dmi_parse_controller_structure(h);
+ break;
+
+ case 43: /* 7.44 TPM Device */
+ pr_handle_name("TPM Device");
+ if (h->length < 0x1B) break;
+ dmi_tpm_vendor_id(data + 0x04);
+ pr_attr("Specification Version", "%d.%d", data[0x08], data[0x09]);
+ switch (data[0x08])
+ {
+ case 0x01:
+ /*
+ * We skip the first 2 bytes, which are
+ * redundant with the above, and uncoded
+ * in a silly way.
+ */
+ pr_attr("Firmware Revision", "%u.%u",
+ data[0x0C], data[0x0D]);
+ break;
+ case 0x02:
+ pr_attr("Firmware Revision", "%u.%u",
+ DWORD(data + 0x0A) >> 16,
+ DWORD(data + 0x0A) & 0xFFFF);
+ /*
+ * We skip the next 4 bytes, as their
+ * format is not standardized and their
+ * usefulness seems limited anyway.
+ */
+ break;
+ }
+ pr_attr("Description", "%s", dmi_string(h, data[0x12]));
+ pr_list_start("Characteristics", NULL);
+ dmi_tpm_characteristics(QWORD(data + 0x13));
+ pr_list_end();
+ if (h->length < 0x1F) break;
+ pr_attr("OEM-specific Information", "0x%08X",
+ DWORD(data + 0x1B));
+ break;
+
+ case 45: /* 7.46 Firmware Inventory Information */
+ pr_handle_name("Firmware Inventory Information");
+ if (h->length < 0x18) break;
+ pr_attr("Firmware Component Name", "%s",
+ dmi_string(h, data[0x04]));
+ pr_attr("Firmware Version", "%s",
+ dmi_string(h, data[0x05]));
+ pr_attr("Firmware ID", "%s", dmi_string(h, data[0x07]));
+ pr_attr("Release Date", "%s", dmi_string(h, data[0x09]));
+ pr_attr("Manufacturer", "%s", dmi_string(h, data[0x0A]));
+ pr_attr("Lowest Supported Firmware Version", "%s",
+ dmi_string(h, data[0x0B]));
+ dmi_memory_size("Image Size", QWORD(data + 0x0C));
+ pr_list_start("Characteristics", NULL);
+ dmi_firmware_characteristics(WORD(data + 0x14));
+ pr_list_end();
+ pr_attr("State", "%s", dmi_firmware_state(data[0x16]));
+ if (h->length < 0x18 + data[0x17] * 2) break;
+ if (!(opt.flags & FLAG_QUIET))
+ dmi_firmware_components(data[0x17], data + 0x18);
+ break;
+
+ case 126:
+ pr_handle_name("Inactive");
+ break;
+
+ case 127:
+ pr_handle_name("End Of Table");
+ break;
+
+ default:
+ if (dmi_decode_oem(h))
+ break;
+ if (opt.flags & FLAG_QUIET)
+ return;
+ pr_handle_name("%s Type",
+ h->type >= 128 ? "OEM-specific" : "Unknown");
+ dmi_dump(h);
+ }
+ pr_sep();
+}
+
+static void to_dmi_header(struct dmi_header *h, u8 *data)
+{
+ h->type = data[0];
+ h->length = data[1];
+ h->handle = WORD(data + 2);
+ h->data = data;
+}
+
+static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver)
+{
+ int key;
+ u8 offset = opt.string->offset;
+
+ if (opt.string->type == 11) /* OEM strings */
+ {
+ if (h->length < 5 || offset > data[4])
+ {
+ fprintf(stderr, "No OEM string number %u\n", offset);
+ return;
+ }
+
+ if (offset)
+ printf("%s\n", dmi_string(h, offset));
+ else
+ printf("%u\n", data[4]); /* count */
+ return;
+ }
+
+ if (offset >= h->length)
+ return;
+
+ key = (opt.string->type << 8) | offset;
+ switch (key)
+ {
+ case 0x015: /* -s bios-revision */
+ if (data[offset - 1] != 0xFF && data[offset] != 0xFF)
+ printf("%u.%u\n", data[offset - 1], data[offset]);
+ break;
+ case 0x017: /* -s firmware-revision */
+ if (data[offset - 1] != 0xFF && data[offset] != 0xFF)
+ printf("%u.%u\n", data[offset - 1], data[offset]);
+ break;
+ case 0x108:
+ dmi_system_uuid(NULL, NULL, data + offset, ver);
+ break;
+ case 0x305:
+ printf("%s\n", dmi_chassis_type(data[offset]));
+ break;
+ case 0x406:
+ printf("%s\n", dmi_processor_family(h, ver));
+ break;
+ case 0x416:
+ dmi_processor_frequency(NULL, data + offset);
+ break;
+ default:
+ printf("%s\n", dmi_string(h, data[offset]));
+ }
+}
+
+static int dmi_table_dump(const u8 *ep, u32 ep_len, const u8 *table,
+ u32 table_len)
+{
+ int fd;
+ FILE *f;
+
+ fd = open(opt.dumpfile, O_WRONLY|O_CREAT|O_EXCL, 0666);
+ if (fd == -1)
+ {
+ fprintf(stderr, "%s: ", opt.dumpfile);
+ perror("open");
+ return -1;
+ }
+
+ f = fdopen(fd, "wb");
+ if (!f)
+ {
+ fprintf(stderr, "%s: ", opt.dumpfile);
+ perror("fdopen");
+ return -1;
+ }
+
+ if (!(opt.flags & FLAG_QUIET))
+ pr_comment("Writing %d bytes to %s.", ep_len, opt.dumpfile);
+ if (fwrite(ep, ep_len, 1, f) != 1)
+ {
+ fprintf(stderr, "%s: ", opt.dumpfile);
+ perror("fwrite");
+ goto err_close;
+ }
+
+ if (fseek(f, 32, SEEK_SET) != 0)
+ {
+ fprintf(stderr, "%s: ", opt.dumpfile);
+ perror("fseek");
+ goto err_close;
+ }
+
+ if (!(opt.flags & FLAG_QUIET))
+ pr_comment("Writing %d bytes to %s.", table_len, opt.dumpfile);
+ if (fwrite(table, table_len, 1, f) != 1)
+ {
+ fprintf(stderr, "%s: ", opt.dumpfile);
+ perror("fwrite");
+ goto err_close;
+ }
+
+ if (fclose(f))
+ {
+ fprintf(stderr, "%s: ", opt.dumpfile);
+ perror("fclose");
+ return -1;
+ }
+
+ return 0;
+
+err_close:
+ fclose(f);
+ return -1;
+}
+
+static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
+{
+ u8 *data;
+ int i = 0;
+
+ /* First pass: Save specific values needed to decode OEM types */
+ data = buf;
+ while ((i < num || !num)
+ && data + 4 <= buf + len) /* 4 is the length of an SMBIOS structure header */
+ {
+ u8 *next;
+ struct dmi_header h;
+
+ to_dmi_header(&h, data);
+
+ /*
+ * If a short entry is found (less than 4 bytes), not only it
+ * is invalid, but we cannot reliably locate the next entry.
+ * Also stop at end-of-table marker if so instructed.
+ */
+ if (h.length < 4 ||
+ (h.type == 127 &&
+ (opt.flags & (FLAG_QUIET | FLAG_STOP_AT_EOT))))
+ break;
+ i++;
+
+ /* Look for the next handle */
+ next = data + h.length;
+ while ((unsigned long)(next - buf + 1) < len
+ && (next[0] != 0 || next[1] != 0))
+ next++;
+ next += 2;
+
+ /* Make sure the whole structure fits in the table */
+ if ((unsigned long)(next - buf) > len)
+ break;
+
+ /* Assign vendor for vendor-specific decodes later */
+ if (h.type == 1 && h.length >= 6)
+ dmi_set_vendor(_dmi_string(&h, data[0x04], 0),
+ _dmi_string(&h, data[0x05], 0));
+
+ /* Remember CPUID type for HPE type 199 */
+ if (h.type == 4 && h.length >= 0x1A && cpuid_type == cpuid_none)
+ cpuid_type = dmi_get_cpuid_type(&h);
+ data = next;
+ }
+
+ /* Second pass: Actually decode the data */
+ i = 0;
+ data = buf;
+ while ((i < num || !num)
+ && data + 4 <= buf + len) /* 4 is the length of an SMBIOS structure header */
+ {
+ u8 *next;
+ struct dmi_header h;
+ int display;
+
+ to_dmi_header(&h, data);
+ display = ((opt.type == NULL || opt.type[h.type])
+ && (opt.handle == ~0U || opt.handle == h.handle)
+ && !((opt.flags & FLAG_QUIET) && (h.type == 126 || h.type == 127))
+ && !opt.string);
+
+ /*
+ * If a short entry is found (less than 4 bytes), not only it
+ * is invalid, but we cannot reliably locate the next entry.
+ * Better stop at this point, and let the user know his/her
+ * table is broken.
+ */
+ if (h.length < 4)
+ {
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ fprintf(stderr,
+ "Invalid entry length (%u). DMI table "
+ "is broken! Stop.\n\n",
+ (unsigned int)h.length);
+ opt.flags |= FLAG_QUIET;
+ }
+ break;
+ }
+ i++;
+
+ /* In quiet mode, stop decoding at end of table marker */
+ if ((opt.flags & FLAG_QUIET) && h.type == 127)
+ break;
+
+ if (display
+ && (!(opt.flags & FLAG_QUIET) || (opt.flags & FLAG_DUMP)))
+ pr_handle(&h);
+
+ /* Look for the next handle */
+ next = data + h.length;
+ while ((unsigned long)(next - buf + 1) < len
+ && (next[0] != 0 || next[1] != 0))
+ next++;
+ next += 2;
+
+ /* Make sure the whole structure fits in the table */
+ if ((unsigned long)(next - buf) > len)
+ {
+ if (display && !(opt.flags & FLAG_QUIET))
+ pr_struct_err("<TRUNCATED>");
+ pr_sep();
+ data = next;
+ break;
+ }
+
+ /* Fixup a common mistake */
+ if (h.type == 34 && !(opt.flags & FLAG_NO_QUIRKS))
+ dmi_fixup_type_34(&h, display);
+
+ if (display)
+ {
+ if (opt.flags & FLAG_DUMP)
+ {
+ dmi_dump(&h);
+ pr_sep();
+ }
+ else
+ dmi_decode(&h, ver);
+ }
+ else if (opt.string != NULL
+ && opt.string->type == h.type)
+ dmi_table_string(&h, data, ver);
+
+ data = next;
+
+ /* SMBIOS v3 requires stopping at this marker */
+ if (h.type == 127 && (flags & FLAG_STOP_AT_EOT))
+ break;
+ }
+
+ /*
+ * SMBIOS v3 64-bit entry points do not announce a structures count,
+ * and only indicate a maximum size for the table.
+ */
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ if (num && i != num)
+ fprintf(stderr, "Wrong DMI structures count: %d announced, "
+ "only %d decoded.\n", num, i);
+ if ((unsigned long)(data - buf) > len
+ || (num && (unsigned long)(data - buf) < len))
+ fprintf(stderr, "Wrong DMI structures length: %u bytes "
+ "announced, structures occupy %lu bytes.\n",
+ len, (unsigned long)(data - buf));
+ }
+}
+
+/* Allocates a buffer for the table, must be freed by the caller */
+static u8 *dmi_table_get(off_t base, u32 *len, u16 num, u32 ver,
+ const char *devmem, u32 flags)
+{
+ u8 *buf;
+
+ if (ver > SUPPORTED_SMBIOS_VER && !(opt.flags & FLAG_QUIET))
+ {
+ pr_comment("SMBIOS implementations newer than version %u.%u.%u are not",
+ SUPPORTED_SMBIOS_VER >> 16,
+ (SUPPORTED_SMBIOS_VER >> 8) & 0xFF,
+ SUPPORTED_SMBIOS_VER & 0xFF);
+ pr_comment("fully supported by this version of dmidecode.");
+ }
+
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ if (opt.type == NULL)
+ {
+ if (num)
+ pr_info("%u structures occupying %u bytes.",
+ num, *len);
+ if (!(opt.flags & FLAG_FROM_DUMP))
+ pr_info("Table at 0x%08llX.",
+ (unsigned long long)base);
+ }
+ pr_sep();
+ }
+
+ if ((flags & FLAG_NO_FILE_OFFSET) || (opt.flags & FLAG_FROM_DUMP))
+ {
+ /*
+ * When reading from sysfs or from a dump file, the file may be
+ * shorter than announced. For SMBIOS v3 this is expcted, as we
+ * only know the maximum table size, not the actual table size.
+ * For older implementations (and for SMBIOS v3 too), this
+ * would be the result of the kernel truncating the table on
+ * parse error.
+ */
+ size_t size = *len;
+ buf = read_file(flags & FLAG_NO_FILE_OFFSET ? 0 : base,
+ &size, devmem);
+ if (!(opt.flags & FLAG_QUIET) && num && size != (size_t)*len)
+ {
+ fprintf(stderr, "Wrong DMI structures length: %u bytes "
+ "announced, only %lu bytes available.\n",
+ *len, (unsigned long)size);
+ }
+ *len = size;
+ }
+ else
+ buf = mem_chunk(base, *len, devmem);
+
+ if (buf == NULL)
+ {
+ fprintf(stderr, "Failed to read table, sorry.\n");
+#ifndef USE_MMAP
+ if (!(flags & FLAG_NO_FILE_OFFSET))
+ fprintf(stderr,
+ "Try compiling dmidecode with -DUSE_MMAP.\n");
+#endif
+ }
+
+ return buf;
+}
+
+
+/*
+ * Build a crafted entry point with table address hard-coded to 32,
+ * as this is where we will put it in the output file. We adjust the
+ * DMI checksum appropriately. The SMBIOS checksum needs no adjustment.
+ */
+static void overwrite_dmi_address(u8 *buf)
+{
+ buf[0x05] += buf[0x08] + buf[0x09] + buf[0x0A] + buf[0x0B] - 32;
+ buf[0x08] = 32;
+ buf[0x09] = 0;
+ buf[0x0A] = 0;
+ buf[0x0B] = 0;
+}
+
+/* Same thing for SMBIOS3 entry points */
+static void overwrite_smbios3_address(u8 *buf)
+{
+ buf[0x05] += buf[0x10] + buf[0x11] + buf[0x12] + buf[0x13]
+ + buf[0x14] + buf[0x15] + buf[0x16] + buf[0x17] - 32;
+ buf[0x10] = 32;
+ buf[0x11] = 0;
+ buf[0x12] = 0;
+ buf[0x13] = 0;
+ buf[0x14] = 0;
+ buf[0x15] = 0;
+ buf[0x16] = 0;
+ buf[0x17] = 0;
+}
+
+static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
+{
+ u32 ver, len;
+ u64 offset;
+ u8 *table;
+
+ /* Don't let checksum run beyond the buffer */
+ if (buf[0x06] > 0x20)
+ {
+ fprintf(stderr,
+ "Entry point length too large (%u bytes, expected %u).\n",
+ (unsigned int)buf[0x06], 0x18U);
+ return 0;
+ }
+
+ if (buf[0x06] < 0x18
+ || !checksum(buf, buf[0x06]))
+ return 0;
+
+ ver = (buf[0x07] << 16) + (buf[0x08] << 8) + buf[0x09];
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("SMBIOS %u.%u.%u present.",
+ buf[0x07], buf[0x08], buf[0x09]);
+
+ offset = QWORD(buf + 0x10);
+ if (!(flags & FLAG_NO_FILE_OFFSET) && offset.h && sizeof(off_t) < 8)
+ {
+ fprintf(stderr, "64-bit addresses not supported, sorry.\n");
+ return 0;
+ }
+
+ /* Maximum length, may get trimmed */
+ len = DWORD(buf + 0x0C);
+ table = dmi_table_get(((off_t)offset.h << 32) | offset.l, &len, 0, ver,
+ devmem, flags | FLAG_STOP_AT_EOT);
+ if (table == NULL)
+ return 1;
+
+ if (opt.flags & FLAG_DUMP_BIN)
+ {
+ u8 crafted[32];
+
+ memcpy(crafted, buf, 32);
+ overwrite_smbios3_address(crafted);
+
+ dmi_table_dump(crafted, crafted[0x06], table, len);
+ }
+ else
+ {
+ dmi_table_decode(table, len, 0, ver >> 8,
+ flags | FLAG_STOP_AT_EOT);
+ }
+
+ free(table);
+
+ return 1;
+}
+
+static void dmi_fixup_version(u16 *ver)
+{
+ /* Some BIOS report weird SMBIOS version, fix that up */
+ switch (*ver)
+ {
+ case 0x021F:
+ case 0x0221:
+ if (!(opt.flags & FLAG_QUIET))
+ fprintf(stderr,
+ "SMBIOS version fixup (2.%d -> 2.%d).\n",
+ *ver & 0xFF, 3);
+ *ver = 0x0203;
+ break;
+ case 0x0233:
+ if (!(opt.flags & FLAG_QUIET))
+ fprintf(stderr,
+ "SMBIOS version fixup (2.%d -> 2.%d).\n",
+ 51, 6);
+ *ver = 0x0206;
+ break;
+ }
+}
+
+static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
+{
+ u16 ver, num;
+ u32 len;
+ u8 *table;
+
+ /* Don't let checksum run beyond the buffer */
+ if (buf[0x05] > 0x20)
+ {
+ fprintf(stderr,
+ "Entry point length too large (%u bytes, expected %u).\n",
+ (unsigned int)buf[0x05], 0x1FU);
+ return 0;
+ }
+
+ /*
+ * The size of this structure is 0x1F bytes, but we also accept value
+ * 0x1E due to a mistake in SMBIOS specification version 2.1.
+ */
+ if (buf[0x05] < 0x1E
+ || !checksum(buf, buf[0x05])
+ || memcmp(buf + 0x10, "_DMI_", 5) != 0
+ || !checksum(buf + 0x10, 0x0F))
+ return 0;
+
+ ver = (buf[0x06] << 8) + buf[0x07];
+ if (!(opt.flags & FLAG_NO_QUIRKS))
+ dmi_fixup_version(&ver);
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("SMBIOS %u.%u present.",
+ ver >> 8, ver & 0xFF);
+
+ /* Maximum length, may get trimmed */
+ len = WORD(buf + 0x16);
+ num = WORD(buf + 0x1C);
+ table = dmi_table_get(DWORD(buf + 0x18), &len, num, ver << 8,
+ devmem, flags);
+ if (table == NULL)
+ return 1;
+
+ if (opt.flags & FLAG_DUMP_BIN)
+ {
+ u8 crafted[32];
+
+ memcpy(crafted, buf, 32);
+ overwrite_dmi_address(crafted + 0x10);
+
+ dmi_table_dump(crafted, crafted[0x05], table, len);
+ }
+ else
+ {
+ dmi_table_decode(table, len, num, ver, flags);
+ }
+
+ free(table);
+
+ return 1;
+}
+
+static int legacy_decode(u8 *buf, const char *devmem, u32 flags)
+{
+ u16 ver, num;
+ u32 len;
+ u8 *table;
+
+ if (!checksum(buf, 0x0F))
+ return 0;
+
+ ver = ((buf[0x0E] & 0xF0) << 4) + (buf[0x0E] & 0x0F);
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("Legacy DMI %u.%u present.",
+ buf[0x0E] >> 4, buf[0x0E] & 0x0F);
+
+ /* Maximum length, may get trimmed */
+ len = WORD(buf + 0x06);
+ num = WORD(buf + 0x0C);
+ table = dmi_table_get(DWORD(buf + 0x08), &len, num, ver << 8,
+ devmem, flags);
+ if (table == NULL)
+ return 1;
+
+ if (opt.flags & FLAG_DUMP_BIN)
+ {
+ u8 crafted[16];
+
+ memcpy(crafted, buf, 16);
+ overwrite_dmi_address(crafted);
+
+ dmi_table_dump(crafted, 0x0F, table, len);
+ }
+ else
+ {
+ dmi_table_decode(table, len, num, ver, flags);
+ }
+
+ free(table);
+
+ return 1;
+}
+
+/*
+ * Probe for EFI interface
+ */
+#define EFI_NOT_FOUND (-1)
+#define EFI_NO_SMBIOS (-2)
+static int address_from_efi(off_t *address)
+{
+#if defined(__linux__)
+ FILE *efi_systab;
+ const char *filename;
+ char linebuf[64];
+#elif defined(__FreeBSD__)
+ char addrstr[KENV_MVALLEN + 1];
+#endif
+ const char *eptype;
+ int ret;
+
+ *address = 0; /* Prevent compiler warning */
+
+#if defined(__linux__)
+ /*
+ * Linux up to 2.6.6: /proc/efi/systab
+ * Linux 2.6.7 and up: /sys/firmware/efi/systab
+ */
+ if ((efi_systab = fopen(filename = "/sys/firmware/efi/systab", "r")) == NULL
+ && (efi_systab = fopen(filename = "/proc/efi/systab", "r")) == NULL)
+ {
+ /* No EFI interface, fallback to memory scan */
+ return EFI_NOT_FOUND;
+ }
+ ret = EFI_NO_SMBIOS;
+ while ((fgets(linebuf, sizeof(linebuf) - 1, efi_systab)) != NULL)
+ {
+ char *addrp = strchr(linebuf, '=');
+ *(addrp++) = '\0';
+ if (strcmp(linebuf, "SMBIOS3") == 0
+ || strcmp(linebuf, "SMBIOS") == 0)
+ {
+ *address = strtoull(addrp, NULL, 0);
+ eptype = linebuf;
+ ret = 0;
+ break;
+ }
+ }
+ if (fclose(efi_systab) != 0)
+ perror(filename);
+
+ if (ret == EFI_NO_SMBIOS)
+ fprintf(stderr, "%s: SMBIOS entry point missing\n", filename);
+#elif defined(__FreeBSD__)
+ /*
+ * On FreeBSD, SMBIOS anchor base address in UEFI mode is exposed
+ * via kernel environment:
+ * https://svnweb.freebsd.org/base?view=revision&revision=307326
+ */
+ ret = kenv(KENV_GET, "hint.smbios.0.mem", addrstr, sizeof(addrstr));
+ if (ret == -1)
+ {
+ if (errno != ENOENT)
+ perror("kenv");
+ return EFI_NOT_FOUND;
+ }
+
+ *address = strtoull(addrstr, NULL, 0);
+ eptype = "SMBIOS";
+ ret = 0;
+#else
+ ret = EFI_NOT_FOUND;
+#endif
+
+ if (ret == 0 && !(opt.flags & FLAG_QUIET))
+ pr_comment("%s entry point at 0x%08llx",
+ eptype, (unsigned long long)*address);
+
+ return ret;
+}
+
+int main(int argc, char * const argv[])
+{
+ int ret = 0; /* Returned value */
+ int found = 0;
+ off_t fp;
+ size_t size;
+ int efi;
+ u8 *buf = NULL;
+
+ /*
+ * We don't want stdout and stderr to be mixed up if both are
+ * redirected to the same file.
+ */
+ setlinebuf(stdout);
+ setlinebuf(stderr);
+
+ if (sizeof(u8) != 1 || sizeof(u16) != 2 || sizeof(u32) != 4 || '\0' != 0)
+ {
+ fprintf(stderr, "%s: compiler incompatibility\n", argv[0]);
+ exit(255);
+ }
+
+ /* Set default option values */
+ opt.devmem = DEFAULT_MEM_DEV;
+ opt.flags = 0;
+ opt.handle = ~0U;
+
+ if (parse_command_line(argc, argv)<0)
+ {
+ ret = 2;
+ goto exit_free;
+ }
+
+ if (opt.flags & FLAG_HELP)
+ {
+ print_help();
+ goto exit_free;
+ }
+
+ if (opt.flags & FLAG_VERSION)
+ {
+ printf("%s\n", VERSION);
+ goto exit_free;
+ }
+
+ if (!(opt.flags & FLAG_QUIET))
+ pr_comment("dmidecode %s", VERSION);
+
+ /* Read from dump if so instructed */
+ if (opt.flags & FLAG_FROM_DUMP)
+ {
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("Reading SMBIOS/DMI data from file %s.",
+ opt.dumpfile);
+ if ((buf = mem_chunk(0, 0x20, opt.dumpfile)) == NULL)
+ {
+ ret = 1;
+ goto exit_free;
+ }
+
+ if (memcmp(buf, "_SM3_", 5) == 0)
+ {
+ if (smbios3_decode(buf, opt.dumpfile, 0))
+ found++;
+ }
+ else if (memcmp(buf, "_SM_", 4) == 0)
+ {
+ if (smbios_decode(buf, opt.dumpfile, 0))
+ found++;
+ }
+ else if (memcmp(buf, "_DMI_", 5) == 0)
+ {
+ if (legacy_decode(buf, opt.dumpfile, 0))
+ found++;
+ }
+ goto done;
+ }
+
+ /*
+ * First try reading from sysfs tables. The entry point file could
+ * contain one of several types of entry points, so read enough for
+ * the largest one, then determine what type it contains.
+ */
+ size = 0x20;
+ if (!(opt.flags & FLAG_NO_SYSFS)
+ && (buf = read_file(0, &size, SYS_ENTRY_FILE)) != NULL)
+ {
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("Getting SMBIOS data from sysfs.");
+ if (size >= 24 && memcmp(buf, "_SM3_", 5) == 0)
+ {
+ if (smbios3_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET))
+ found++;
+ }
+ else if (size >= 31 && memcmp(buf, "_SM_", 4) == 0)
+ {
+ if (smbios_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET))
+ found++;
+ }
+ else if (size >= 15 && memcmp(buf, "_DMI_", 5) == 0)
+ {
+ if (legacy_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET))
+ found++;
+ }
+
+ if (found)
+ goto done;
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("Failed to get SMBIOS data from sysfs.");
+ }
+
+ /* Next try EFI (ia64, Intel-based Mac, arm64) */
+ efi = address_from_efi(&fp);
+ switch (efi)
+ {
+ case EFI_NOT_FOUND:
+ goto memory_scan;
+ case EFI_NO_SMBIOS:
+ ret = 1;
+ goto exit_free;
+ }
+
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("Found SMBIOS entry point in EFI, reading table from %s.",
+ opt.devmem);
+ if ((buf = mem_chunk(fp, 0x20, opt.devmem)) == NULL)
+ {
+ ret = 1;
+ goto exit_free;
+ }
+
+ if (memcmp(buf, "_SM3_", 5) == 0)
+ {
+ if (smbios3_decode(buf, opt.devmem, 0))
+ found++;
+ }
+ else if (memcmp(buf, "_SM_", 4) == 0)
+ {
+ if (smbios_decode(buf, opt.devmem, 0))
+ found++;
+ }
+ goto done;
+
+memory_scan:
+#if defined __i386__ || defined __x86_64__
+ if (!(opt.flags & FLAG_QUIET))
+ pr_info("Scanning %s for entry point.", opt.devmem);
+ /* Fallback to memory scan (x86, x86_64) */
+ if ((buf = mem_chunk(0xF0000, 0x10000, opt.devmem)) == NULL)
+ {
+ ret = 1;
+ goto exit_free;
+ }
+
+ /* Look for a 64-bit entry point first */
+ for (fp = 0; fp <= 0xFFE0; fp += 16)
+ {
+ if (memcmp(buf + fp, "_SM3_", 5) == 0)
+ {
+ if (smbios3_decode(buf + fp, opt.devmem, 0))
+ {
+ found++;
+ goto done;
+ }
+ }
+ }
+
+ /* If none found, look for a 32-bit entry point */
+ for (fp = 0; fp <= 0xFFF0; fp += 16)
+ {
+ if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0)
+ {
+ if (smbios_decode(buf + fp, opt.devmem, 0))
+ {
+ found++;
+ goto done;
+ }
+ }
+ else if (memcmp(buf + fp, "_DMI_", 5) == 0)
+ {
+ if (legacy_decode(buf + fp, opt.devmem, 0))
+ {
+ found++;
+ goto done;
+ }
+ }
+ }
+#endif
+
+done:
+ if (!found && !(opt.flags & FLAG_QUIET))
+ pr_comment("No SMBIOS nor DMI entry point found, sorry.");
+
+ free(buf);
+exit_free:
+ free(opt.type);
+
+ return ret;
+}
diff --git a/dmidecode.h b/dmidecode.h
new file mode 100644
index 0000000..318cdc6
--- /dev/null
+++ b/dmidecode.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2005-2020 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DMIDECODE_H
+#define DMIDECODE_H
+
+#include "types.h"
+
+struct dmi_header
+{
+ u8 type;
+ u8 length;
+ u16 handle;
+ u8 *data;
+};
+
+enum cpuid_type
+{
+ cpuid_none,
+ cpuid_80386,
+ cpuid_80486,
+ cpuid_arm_legacy,
+ cpuid_arm_soc_id,
+ cpuid_x86_intel,
+ cpuid_x86_amd,
+};
+
+extern enum cpuid_type cpuid_type;
+
+int is_printable(const u8 *data, int len);
+const char *dmi_string(const struct dmi_header *dm, u8 s);
+void dmi_print_memory_size(const char *addr, u64 code, int shift);
+void dmi_print_cpuid(void (*print_cb)(const char *name, const char *format, ...),
+ const char *label, enum cpuid_type sig, const u8 *p);
+
+#endif
diff --git a/dmioem.c b/dmioem.c
new file mode 100644
index 0000000..dc4b857
--- /dev/null
+++ b/dmioem.c
@@ -0,0 +1,1410 @@
+/*
+ * Decoding of OEM-specific entries
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2007-2020 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "types.h"
+#include "util.h"
+#include "dmidecode.h"
+#include "dmioem.h"
+#include "dmiopt.h"
+#include "dmioutput.h"
+
+/*
+ * Globals for vendor-specific decodes
+ */
+
+enum DMI_VENDORS
+{
+ VENDOR_UNKNOWN,
+ VENDOR_ACER,
+ VENDOR_HP,
+ VENDOR_HPE,
+ VENDOR_IBM,
+ VENDOR_LENOVO,
+};
+
+static enum DMI_VENDORS dmi_vendor = VENDOR_UNKNOWN;
+static const char *dmi_product = NULL;
+
+/*
+ * Remember the system vendor for later use. We only actually store the
+ * value if we know how to decode at least one specific entry type for
+ * that vendor.
+ */
+void dmi_set_vendor(const char *v, const char *p)
+{
+ const struct { const char *str; enum DMI_VENDORS id; } vendor[] = {
+ { "Acer", VENDOR_ACER },
+ { "HP", VENDOR_HP },
+ { "Hewlett-Packard", VENDOR_HP },
+ { "HPE", VENDOR_HPE },
+ { "Hewlett Packard Enterprise", VENDOR_HPE },
+ { "IBM", VENDOR_IBM },
+ { "LENOVO", VENDOR_LENOVO },
+ };
+ unsigned int i;
+ size_t len;
+
+ /*
+ * Often DMI strings have trailing spaces. Ignore these
+ * when checking for known vendor names.
+ */
+ len = v ? strlen(v) : 0;
+ while (len && v[len - 1] == ' ')
+ len--;
+
+ for (i = 0; i < ARRAY_SIZE(vendor); i++)
+ {
+ if (strlen(vendor[i].str) == len &&
+ strncmp(v, vendor[i].str, len) == 0)
+ {
+ dmi_vendor = vendor[i].id;
+ break;
+ }
+ }
+
+ dmi_product = p;
+}
+
+/*
+ * Acer-specific data structures are decoded here.
+ */
+
+static int dmi_decode_acer(const struct dmi_header *h)
+{
+ u8 *data = h->data;
+ u16 cap;
+
+ switch (h->type)
+ {
+ case 170:
+ /*
+ * Vendor Specific: Acer Hotkey Function
+ *
+ * Source: acer-wmi kernel driver
+ *
+ * Probably applies to some laptop models of other
+ * brands, including Fujitsu-Siemens, Medion, Lenovo,
+ * and eMachines.
+ */
+ pr_handle_name("Acer Hotkey Function");
+ if (h->length < 0x0F) break;
+ cap = WORD(data + 0x04);
+ pr_attr("Function bitmap for Communication Button", "0x%04hx", cap);
+ pr_subattr("WiFi", "%s", cap & 0x0001 ? "Yes" : "No");
+ pr_subattr("3G", "%s", cap & 0x0040 ? "Yes" : "No");
+ pr_subattr("WiMAX", "%s", cap & 0x0080 ? "Yes" : "No");
+ pr_subattr("Bluetooth", "%s", cap & 0x0800 ? "Yes" : "No");
+ pr_attr("Function bitmap for Application Button", "0x%04hx", WORD(data + 0x06));
+ pr_attr("Function bitmap for Media Button", "0x%04hx", WORD(data + 0x08));
+ pr_attr("Function bitmap for Display Button", "0x%04hx", WORD(data + 0x0A));
+ pr_attr("Function bitmap for Others Button", "0x%04hx", WORD(data + 0x0C));
+ pr_attr("Communication Function Key Number", "%d", data[0x0E]);
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * HPE-specific data structures are decoded here.
+ *
+ * Code contributed by John Cagle and Tyler Bell.
+ */
+
+static void dmi_print_hp_net_iface_rec(u8 id, u8 bus, u8 dev, const u8 *mac)
+{
+ /* Some systems do not provide an id. nic_ctr provides an artificial
+ * id, and assumes the records will be provided "in order". Also,
+ * using 0xFF marker is not future proof. 256 NICs is a lot, but
+ * 640K ought to be enough for anybody(said no one, ever).
+ * */
+ static u8 nic_ctr;
+ char attr[8];
+
+ if (id == 0xFF)
+ id = ++nic_ctr;
+
+ sprintf(attr, "NIC %hhu", id);
+ if (dev == 0x00 && bus == 0x00)
+ pr_attr(attr, "Disabled");
+ else if (dev == 0xFF && bus == 0xFF)
+ pr_attr(attr, "Not Installed");
+ else
+ {
+ pr_attr(attr, "PCI device %02x:%02x.%x, "
+ "MAC address %02X:%02X:%02X:%02X:%02X:%02X",
+ bus, dev >> 3, dev & 7,
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ }
+}
+
+typedef enum { G6 = 6, G7, G8, G9, G10, G10P } dmi_hpegen_t;
+
+static int dmi_hpegen(const char *s)
+{
+ struct { const char *name; dmi_hpegen_t gen; } table[] = {
+ { "Gen10 Plus", G10P },
+ { "Gen10", G10 },
+ { "Gen9", G9 },
+ { "Gen8", G8 },
+ { "G7", G7 },
+ { "G6", G6 },
+ };
+ unsigned int i;
+
+ if (!strstr(s, "ProLiant") && !strstr(s, "Apollo") &&
+ !strstr(s, "Synergy") && !strstr(s, "Edgeline"))
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(table); i++) {
+ if (strstr(s, table[i].name))
+ return(table[i].gen);
+ }
+
+ return (dmi_vendor == VENDOR_HPE) ? G10P : G6;
+}
+
+static void dmi_hp_240_attr(u64 defined, u64 set)
+{
+ static const char *attributes[] = {
+ "Updatable",
+ "Reset Required",
+ "Authentication Required",
+ "In Use",
+ "UEFI Image",
+ };
+ unsigned int i;
+
+ pr_list_start("Attributes Defined/Set", NULL);
+ for (i = 0; i < ARRAY_SIZE(attributes); i++)
+ {
+ if (!(defined.l & (1UL << i)))
+ continue;
+ pr_list_item("%s: %s", attributes[i], set.l & (1UL << i) ? "Yes" : "No");
+ }
+ pr_list_end();
+}
+
+static void dmi_hp_203_assoc_hndl(const char *fname, u16 num)
+{
+ if (opt.flags & FLAG_QUIET)
+ return;
+
+ if (num == 0xFFFE)
+ pr_attr(fname, "N/A");
+ else
+ pr_attr(fname, "0x%04X", num);
+}
+
+static void dmi_hp_203_pciinfo(const char *fname, u16 num)
+{
+ if (num == 0xFFFF)
+ pr_attr(fname, "Device Not Present");
+ else
+ pr_attr(fname, "0x%04x", num);
+}
+
+static void dmi_hp_203_bayenc(const char *fname, u8 num)
+{
+ switch (num)
+ {
+ case 0x00:
+ pr_attr(fname, "Unknown");
+ break;
+ case 0xff:
+ pr_attr(fname, "Do Not Display");
+ break;
+ default:
+ pr_attr(fname, "%d", num);
+ }
+}
+
+static void dmi_hp_203_devtyp(const char *fname, unsigned int code)
+{
+ const char *str = "Reserved";
+ static const char *type[] = {
+ "Unknown", /* 0x00 */
+ "Reserved",
+ "Reserved",
+ "Flexible LOM",
+ "Embedded LOM",
+ "NIC in a Slot",
+ "Storage Controller",
+ "Smart Array Storage Controller",
+ "USB Hard Disk",
+ "Other PCI Device",
+ "RAM Disk",
+ "Firmware Volume",
+ "UEFI Shell",
+ "Generic UEFI USB Boot Entry",
+ "Dynamic Smart Array Controller",
+ "File",
+ "NVME Hard Drive",
+ "NVDIMM" /* 0x11 */
+ };
+
+ if (code < ARRAY_SIZE(type))
+ str = type[code];
+
+ pr_attr(fname, "%s", str);
+}
+
+static void dmi_hp_203_devloc(const char *fname, unsigned int code)
+{
+ const char *str = "Reserved";
+ static const char *location[] = {
+ "Unknown", /* 0x00 */
+ "Embedded",
+ "iLO Virtual Media",
+ "Front USB Port",
+ "Rear USB Port",
+ "Internal USB",
+ "Internal SD Card",
+ "Internal Virtual USB (Embedded NAND)",
+ "Embedded SATA Port",
+ "Embedded Smart Array",
+ "PCI Slot",
+ "RAM Memory",
+ "USB",
+ "Dynamic Smart Array Controller",
+ "URL",
+ "NVMe Drive Bay" /* 0x0F */
+ };
+
+ if (code < ARRAY_SIZE(location))
+ str = location[code];
+
+ pr_attr(fname, "%s", str);
+}
+
+static void dmi_hp_216_fw_type(u16 code)
+{
+ const char *str = "Reserved";
+ static const char * const type[] = {
+ "Reserved", /* 0x00 */
+ "System ROM",
+ "Redundant System ROM",
+ "System ROM Bootblock",
+ "Power Management Controller Firmware",
+ "Power Management Controller Firmware Bootloader",
+ "SL Chassis Firmware",
+ "SL Chassis Firmware Bootloader",
+ "Hardware PAL/CPLD",
+ "SPS Firmware (ME Firmware)",
+ "SL Chassis PAL/CPLD",
+ "Compatibility Support Module (CSM)",
+ "APML",
+ "Smart Storage Battery (Megacell) Firmware",
+ "Trusted Module (TPM or TCM) Firmware Version",
+ "NVMe Backplane Firmware",
+ "Intelligent Provisioning",
+ "SPI Descriptor Version",
+ "Innovation Engine Firmware (IE Firmware)",
+ "UMB Backplane Firmware",
+ "Reserved", /* 0x14 */
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved", /* 0x1F */
+ "EL Chassis Abstraction Revision",
+ "EL Chassis Firmware Revision",
+ "EL Chassis PAL/CPLD",
+ "EL Cartride Abstraction Revision",
+ "Reserved", /* 0x24 */
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved", /* 0x2F */
+ "Embedded Video Controller",
+ "PCIe Riser Programmable Logic Device",
+ "PCIe cards that contain a CPLD",
+ "Intel NVMe VROC",
+ "Intel SATA VROC",
+ "Intel SPS Firmware",
+ "Secondary System Programmable Logic Device",
+ "CPU MEZZ Programmable Logic Device", /* 0x37 */
+ "Intel Artic Sound -M Accelerator Models Firmware",
+ "Ampere System Control Processor (SCP – PMPro+SMPro)",
+ "Intel CFR information", /* 0x3A */
+ };
+
+ if (code < ARRAY_SIZE(type))
+ str = type[code];
+
+ pr_attr("Firmware Type", "%s", str);
+}
+
+static void dmi_hp_216_version(u8 format, u8 *data)
+{
+ const char * const name = "Version Data";
+ const char * const reserved = "Reserved";
+ int gen;
+
+ gen = dmi_hpegen(dmi_product);
+
+ switch (format) {
+ case 0:
+ pr_attr(name, "No Version Data");
+ break;
+ case 1:
+ pr_attr(name, "%c.%d.%d", data[0] & (1 << 7) ? 'B' : 'R',
+ data[0] & 0x7, data[1] & 0x7);
+ break;
+ case 2:
+ pr_attr(name, "%d.%d", data[0] >> 4, data[0] & 0x0f);
+ break;
+ case 4:
+ pr_attr(name, "%d.%d.%d", data[0] >> 4, data[0] & 0x0f, data[1] & 0x7f);
+ break;
+ case 5:
+ if (gen == G9) {
+ pr_attr(name, "%d.%d.%d", data[0] >> 4, data[0] & 0x0f, data[1] & 0x7f);
+ } else if (gen == G10 || gen == G10P) {
+ pr_attr(name, "%d.%d.%d.%d", data[1] & 0x0f, data[3] & 0x0f,
+ data[5] & 0x0f, data[6] & 0x0f);
+ } else {
+ pr_attr(name, "%s", reserved);
+ }
+ break;
+ case 6:
+ pr_attr(name, "%d.%d", data[1], data[0]);
+ break;
+ case 7:
+ pr_attr(name, "v%d.%.2d (%.2d/%.2d/%d)", data[0], data[1],
+ data[2], data[3], WORD(data + 4));
+ break;
+ case 8:
+ pr_attr(name, "%d.%d", WORD(data + 4), WORD(data));
+ break;
+ case 9:
+ pr_attr(name, "%d.%d.%d", data[0], data[1], WORD(data + 2));
+ break;
+ case 10:
+ pr_attr(name, "%d.%d.%d Build %d", data[0], data[1], data[2], data[3]);
+ break;
+ case 11:
+ pr_attr(name, "%d.%d %d", WORD(data + 2), WORD(data), DWORD(data + 4));
+ break;
+ case 12:
+ pr_attr(name, "%d.%d.%d.%d", WORD(data), WORD(data + 2),
+ WORD(data + 4), WORD(data + 6));
+ break;
+ case 13:
+ pr_attr(name, "%d", data[0]);
+ break;
+ case 14:
+ pr_attr(name, "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
+ break;
+ case 15:
+ pr_attr(name, "%d.%d.%d.%d (%.2d/%.2d/%d)",
+ WORD(data), WORD(data + 2), WORD(data + 4), WORD(data + 6),
+ data[8], data[9], WORD(data + 10));
+ break;
+ case 16:
+ pr_attr(name, "%c%c%c%c.%d%d",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+ break;
+ case 17:
+ pr_attr(name, "%08X", DWORD(data));
+ break;
+ case 18:
+ pr_attr(name, "%d.%2d", data[0], data[1]);
+ break;
+ case 3: /* fall through */
+ default:
+ pr_attr(name, "%s", reserved);
+ }
+}
+
+static int dmi_hp_224_status(u8 code)
+{
+ static const char * const present[] = {
+ "Not Present", /* 0x00 */
+ "Present/Enabled",
+ "Present/Disabled",
+ "Reserved" /* 0x03 */
+ };
+
+ pr_attr("Status", "%s", present[code & 0x03]);
+ if ((code & 0x03) == 0x00)
+ return 0;
+ pr_attr("Option ROM Measuring", "%s", (code & (1 << 2)) ? "Yes" : "No");
+ pr_attr("Hidden", "%s", (code & (1 << 3)) ? "Yes" : "No");
+ return 1;
+}
+
+static void dmi_hp_224_ex_status(u8 status, u8 code)
+{
+ const char *str = "Reserved";
+ static const char * const disable_reason[] = {
+ "Not Specified", /* 0x00 */
+ "User Disabled",
+ "Error Condition",
+ "Reserved" /* 0x03 */
+ };
+ static const char * const error_condition[] = {
+ "Not Specified", /* 0x00 */
+ "Self-Test", /* 0x01 */
+ };
+ if ((status & 0x03) == 0x02)
+ pr_attr("Disable Reason", "%s", disable_reason[code & 0x03]);
+ if ((code & 0x03) == 0x02) {
+ u8 error = (code >> 2) & 0x0f;
+ if (error < ARRAY_SIZE(error_condition))
+ str = error_condition[error];
+ pr_attr("Error Condition", "%s", str);
+ }
+}
+
+static void dmi_hp_224_module_type(u8 code)
+{
+ const char *str = "Reserved";
+ static const char * const type[] = {
+ "Not Specified", /* 0x00 */
+ "TPM 1.2",
+ "TPM 2.0",
+ "Intel PTT fTPM" /* 0x03 */
+ };
+ if ((code & 0x0f) < ARRAY_SIZE(type))
+ str = type[code & 0x0f];
+ pr_attr("Type", "%s", str);
+ pr_attr("Standard Algorithm Supported", "%s", (code & (1 << 4)) ? "Yes" : "No");
+ pr_attr("Chinese Algorithm Supported", "%s", (code & (1 << 5)) ? "Yes" : "No");
+}
+
+static void dmi_hp_224_module_attr(u8 code)
+{
+ static const char * const phys_attr[] = {
+ "Not Specified", /* 0x00 */
+ "Pluggable and Optional",
+ "Pluggable but Standard",
+ "Soldered Down on System Board" /* 0x03 */
+ };
+ static const char * const fips_attr[] = {
+ "Not Specified", /* 0x00 */
+ "Not FIPS Certified",
+ "FIPS Certified",
+ "Reserved" /* 0x03 */
+ };
+ pr_attr("Trusted Module Attributes", "%s", phys_attr[code & 0x3]);
+ pr_attr("FIPS Certification", "%s", fips_attr[((code >> 2) & 0x03)]);
+}
+
+static void dmi_hp_224_chipid(u16 code)
+{
+ const char *str = "Reserved";
+ static const char * const chipid[] = {
+ "None", /* 0x00 */
+ "STMicroGen10 TPM",
+ "Intel firmware TPM (PTT)",
+ "Nationz TPM",
+ "STMicroGen10 Plus TPM",
+ "STMicroGen11 TPM", /* 0x05 */
+ };
+ if ((code & 0xff) < ARRAY_SIZE(chipid))
+ str = chipid[code & 0xff];
+ pr_attr("Chip Identifier", "%s", str);
+}
+
+static void dmi_hp_230_method_bus_seg_addr(u8 code, u8 bus_seg, u8 addr)
+{
+ const char *str = "Reserved";
+ static const char * const method[] = {
+ "Not Available", /* 0x00 */
+ "IPMI I2C",
+ "iLO",
+ "Chassis Manager", /* 0x03 */
+ };
+ if (code < ARRAY_SIZE(method))
+ str = method[code];
+ pr_attr("Access Method", "%s", str);
+ if (code == 0 || code >= ARRAY_SIZE(method))
+ return;
+ if (bus_seg != 0xFF)
+ {
+ if (code == 2)
+ pr_attr("I2C Segment Number", "%d", bus_seg);
+ else
+ pr_attr("I2C Bus Number", "%d", bus_seg);
+ }
+ if (addr != 0xFF)
+ pr_attr("I2C Address", "0x%02x", addr >> 1);
+}
+
+static void dmi_hp_238_loc(const char *fname, unsigned int code)
+{
+ const char *str = "Reserved";
+ static const char *location[] = {
+ "Internal", /* 0x00 */
+ "Front of Server",
+ "Rear of Server",
+ "Embedded internal SD Card",
+ "iLO USB",
+ "USB Hub for NAND Controller",
+ "Reserved",
+ "Debug Port", /* 0x07 */
+ };
+
+ if (code < ARRAY_SIZE(location))
+ str = location[code];
+
+ pr_attr(fname, "%s", str);
+}
+
+static void dmi_hp_238_flags(const char *fname, unsigned int code)
+{
+ const char *str = "Reserved";
+ static const char *flags[] = {
+ "Not Shared", /* 0x00 */
+ "Shared with physical switch",
+ "Shared with automatic control", /* 0x02 */
+ };
+
+ if (code < ARRAY_SIZE(flags))
+ str = flags[code];
+
+ pr_attr(fname, "%s", str);
+}
+
+static void dmi_hp_238_speed(const char *fname, unsigned int code)
+{
+ const char *str = "Reserved";
+ static const char *speed[] = {
+ "Reserved", /* 0x00 */
+ "USB 1.1 Full Speed",
+ "USB 2.0 High Speed",
+ "USB 3.0 Super Speed" /* 0x03 */
+ };
+
+ if (code < ARRAY_SIZE(speed))
+ str = speed[code];
+
+ pr_attr(fname, "%s", str);
+}
+
+static void dmi_hp_242_hdd_type(u8 code)
+{
+ const char *str = "Reserved";
+ static const char * const type[] = {
+ "Undetermined", /* 0x00 */
+ "NVMe SSD",
+ "SATA",
+ "SAS",
+ "SATA SSD",
+ "NVMe Manged by VROC/VMD", /* 0x05 */
+ };
+ if (code < ARRAY_SIZE(type))
+ str = type[code];
+
+ pr_attr("Hard Drive Type", "%s", str);
+}
+
+static void dmi_hp_242_form_factor(u8 code)
+{
+ const char *str = "Reserved";
+ static const char * const form[] = {
+ "Reserved", /* 0x00 */
+ "Reserved",
+ "3.5\" form factor",
+ "2.5\" form factor",
+ "1.8\" form factor",
+ "Less than 1.8\" form factor",
+ "mSATA",
+ "M.2",
+ "MicroSSD",
+ "CFast", /* 0x09 */
+ };
+ if (code < ARRAY_SIZE(form))
+ str = form[code];
+
+ pr_attr("Form Factor", "%s", str);
+}
+
+static void dmi_hp_242_speed(const char *attr, u16 speed)
+{
+ if (speed)
+ pr_attr(attr, "%hu Gbit/s", speed);
+ else
+ pr_attr(attr, "%s", "Unknown");
+}
+
+static int dmi_decode_hp(const struct dmi_header *h)
+{
+ u8 *data = h->data;
+ int nic, ptr;
+ u32 feat;
+ const char *company = (dmi_vendor == VENDOR_HP) ? "HP" : "HPE";
+ int gen;
+
+ gen = dmi_hpegen(dmi_product);
+ if (gen < 0)
+ return 0;
+
+ switch (h->type)
+ {
+ case 194:
+ /*
+ * Vendor Specific: Super IO Enable/Disable Features
+ *
+ * Offset | Name | Width | Description
+ * -------------------------------------
+ * 0x00 | Type | BYTE | 0xC2, Super IO Enable/Disable Indicator
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Dev Status | BYTE | Device Status
+ */
+ pr_handle_name("%s ProLiant Super IO Enable/Disable Indicator", company);
+ if (h->length < 0x05) break;
+ feat = data[0x04];
+ pr_attr("Serial Port A", "%s", feat & (1 << 0) ? "Enabled" : "Disabled");
+ pr_attr("Serial Port B", "%s", feat & (1 << 1) ? "Enabled" : "Disabled");
+ pr_attr("Parallel Port", "%s", feat & (1 << 2) ? "Enabled" : "Disabled");
+ pr_attr("Floppy Disk Port", "%s", feat & (1 << 3) ? "Enabled" : "Disabled");
+ pr_attr("Virtual Serial Port", "%s", feat & (1 << 4) ? "Enabled" : "Disabled");
+ break;
+
+ case 199:
+ /*
+ * Vendor Specific: CPU Microcode Patch
+ *
+ * Offset | Name | Width | Description
+ * -------------------------------------
+ * 0x00 | Type | BYTE | 0xC7, CPU Microcode Patch
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Patch Info | Varies| { <DWORD: ID, DWORD Date, DWORD CPUID> ...}
+ */
+ if (gen < G9) return 0;
+ pr_handle_name("%s ProLiant CPU Microcode Patch Support Info", company);
+
+ for (ptr = 0x4; ptr + 12 <= h->length; ptr += 12) {
+ u32 cpuid = DWORD(data + ptr + 2 * 4);
+ u32 date;
+
+ /* AMD omits BaseFamily. Reconstruction valid on family >= 15. */
+ if (cpuid_type == cpuid_x86_amd)
+ cpuid = ((cpuid & 0xfff00) << 8) | 0x0f00 | (cpuid & 0xff);
+
+ dmi_print_cpuid(pr_attr, "CPU ID", cpuid_type, (u8 *) &cpuid);
+
+ date = DWORD(data + ptr + 4);
+ pr_subattr("Date", "%04x-%02x-%02x",
+ date & 0xffff, (date >> 24) & 0xff, (date >> 16) & 0xff);
+ pr_subattr("Patch", "0x%X", DWORD(data + ptr));
+ }
+ break;
+
+ case 203:
+ /*
+ * Vendor Specific: HP Device Correlation Record
+ *
+ * Offset | Name | Width | Description
+ * -------------------------------------
+ * 0x00 | Type | BYTE | 0xCB, Correlation Record
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Assoc Device | WORD | Handle of Associated Type 9 or Type 41 Record
+ * 0x06 | Assoc SMBus | WORD | Handle of Associated Type 228 SMBus Segment Record
+ * 0x08 | PCI Vendor ID| WORD | PCI Vendor ID of device 0xFFFF -> not present
+ * 0x0A | PCI Device ID| WORD | PCI Device ID of device 0xFFFF -> not present
+ * 0x0C | PCI SubVendor| WORD | PCI Sub Vendor ID of device 0xFFFF -> not present
+ * 0x0E | PCI SubDevice| WORD | PCI Sub Device ID of device 0xFFFF -> not present
+ * 0x10 | Class Code | BYTE | PCI Class Code of Endpoint. 0xFF if device not present.
+ * 0x11 | Class SubCode| BYTE | PCI Sub Class Code of Endpoint. 0xFF if device not present.
+ * 0x12 | Parent Handle| WORD |
+ * 0x14 | Flags | WORD |
+ * 0x16 | Device Type | BYTE | UEFI only
+ * 0x17 | Device Loc | BYTE | Device Location
+ * 0x18 | Dev Instance | BYTE | Device Instance
+ * 0x19 | Sub Instance | BYTE | NIC Port # or NVMe Drive Bay
+ * 0x1A | Bay | BYTE |
+ * 0x1B | Enclosure | BYTE |
+ * 0x1C | UEFI Dev Path| STRING| String number for UEFI Device Path
+ * 0x1D | Struct Name | STRING| String number for UEFI Device Structured Name
+ * 0x1E | Device Name | STRING| String number for UEFI Device Name
+ * 0x1F | UEFI Location| STRING| String number for UEFI Location
+ * 0x20 | Assoc Handle | WORD | Type 9 Handle. Defined if Flags[0] == 1.
+ * 0x22 | Part Number | STRING| PCI Device Part Number
+ * 0x23 | Serial Number| STRING| PCI Device Serial Number
+ * 0x24 | Seg Number | WORD | Segment Group number. 0 -> Single group topology
+ * 0x26 | Bus Number | BYTE | PCI Device Bus Number
+ * 0x27 | Func Number | BTYE | PCI Device and Function Number
+ */
+ if (gen < G9) return 0;
+ pr_handle_name("%s Device Correlation Record", company);
+ if (h->length < 0x1F) break;
+ dmi_hp_203_assoc_hndl("Associated Device Record", WORD(data + 0x04));
+ dmi_hp_203_assoc_hndl("Associated SMBus Record", WORD(data + 0x06));
+ if (WORD(data + 0x08) == 0xffff && WORD(data + 0x0A) == 0xffff &&
+ WORD(data + 0x0C) == 0xffff && WORD(data + 0x0E) == 0xffff &&
+ data[0x10] == 0xFF && data[0x11] == 0xFF)
+ {
+ pr_attr("PCI Device Info", "Device Not Present");
+ }
+ else
+ {
+ dmi_hp_203_pciinfo("PCI Vendor ID", WORD(data + 0x08));
+ dmi_hp_203_pciinfo("PCI Device ID", WORD(data + 0x0A));
+ dmi_hp_203_pciinfo("PCI Sub Vendor ID", WORD(data + 0x0C));
+ dmi_hp_203_pciinfo("PCI Sub Device ID", WORD(data + 0x0E));
+ dmi_hp_203_pciinfo("PCI Class Code", (char)data[0x10]);
+ dmi_hp_203_pciinfo("PCI Sub Class Code", (char)data[0x11]);
+ }
+ dmi_hp_203_assoc_hndl("Parent Handle", WORD(data + 0x12));
+ pr_attr("Flags", "0x%04X", WORD(data + 0x14));
+ dmi_hp_203_devtyp("Device Type", data[0x16]);
+ dmi_hp_203_devloc("Device Location", data[0x17]);
+ pr_attr("Device Instance", "%d", data[0x18]);
+ pr_attr("Device Sub-Instance", "%d", data[0x19]);
+ dmi_hp_203_bayenc("Bay", data[0x1A]);
+ dmi_hp_203_bayenc("Enclosure", data[0x1B]);
+ pr_attr("Device Path", "%s", dmi_string(h, data[0x1C]));
+ pr_attr("Structured Name", "%s", dmi_string(h, data[0x1D]));
+ pr_attr("Device Name", "%s", dmi_string(h, data[0x1E]));
+ if (h->length < 0x22) break;
+ pr_attr("UEFI Location", "%s", dmi_string(h, data[0x1F]));
+ if (!(opt.flags & FLAG_QUIET))
+ {
+ if (WORD(data + 0x14) & 1)
+ pr_attr("Associated Real/Phys Handle", "0x%04X",
+ WORD(data + 0x20));
+ else
+ pr_attr("Associated Real/Phys Handle", "N/A");
+ }
+ if (h->length < 0x24) break;
+ pr_attr("PCI Part Number", "%s", dmi_string(h, data[0x22]));
+ pr_attr("Serial Number", "%s", dmi_string(h, data[0x23]));
+ if (h->length < 0x28) break;
+ pr_attr("Segment Group Number", "0x%04x", WORD(data + 0x24));
+ pr_attr("PCI Device", "%02x:%02x.%x",
+ data[0x26], data[0x27] >> 3, data[0x27] & 7);
+ break;
+
+ case 204:
+ /*
+ * Vendor Specific: HPE ProLiant System/Rack Locator
+ */
+ pr_handle_name("%s ProLiant System/Rack Locator", company);
+ if (h->length < 0x0B) break;
+ pr_attr("Rack Name", "%s", dmi_string(h, data[0x04]));
+ pr_attr("Enclosure Name", "%s", dmi_string(h, data[0x05]));
+ pr_attr("Enclosure Model", "%s", dmi_string(h, data[0x06]));
+ pr_attr("Enclosure Serial", "%s", dmi_string(h, data[0x0A]));
+ pr_attr("Enclosure Bays", "%d", data[0x08]);
+ pr_attr("Server Bay", "%s", dmi_string(h, data[0x07]));
+ pr_attr("Bays Filled", "%d", data[0x09]);
+ break;
+
+ case 209:
+ case 221:
+ /*
+ * Vendor Specific: HPE ProLiant NIC MAC Information
+ *
+ * This prints the BIOS NIC number,
+ * PCI bus/device/function, and MAC address
+ *
+ * Type 209:
+ * Offset | Name | Width | Description
+ * -------------------------------------
+ * 0x00 | Type | BYTE | 0xD1, MAC Info
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Dev No | BYTE | PCI Device/Function No
+ * 0x05 | Bus No | BYTE | PCI Bus
+ * 0x06 | MAC | 6B | MAC addr
+ * 0x0C | NIC #2 | 8B | Repeat 0x04-0x0B
+ *
+ * Type 221: is deprecated in the latest docs
+ */
+ pr_handle_name("%s %s", company, h->type == 221 ?
+ "BIOS iSCSI NIC PCI and MAC Information" :
+ "BIOS PXE NIC PCI and MAC Information");
+ nic = 1;
+ ptr = 4;
+ while (h->length >= ptr + 8)
+ {
+ dmi_print_hp_net_iface_rec(nic,
+ data[ptr + 0x01],
+ data[ptr],
+ &data[ptr + 0x02]);
+ nic++;
+ ptr += 8;
+ }
+ break;
+
+ case 212:
+ /*
+ * Vendor Specific: HPE 64-bit CRU Information
+ *
+ * Source: hpwdt kernel driver
+ */
+ pr_handle_name("%s 64-bit CRU Information", company);
+ if (h->length < 0x18) break;
+ if (is_printable(data + 0x04, 4))
+ pr_attr("Signature", "0x%08x (%c%c%c%c)",
+ DWORD(data + 0x04),
+ data[0x04], data[0x05],
+ data[0x06], data[0x07]);
+ else
+ pr_attr("Signature", "0x%08x", DWORD(data + 0x04));
+ if (DWORD(data + 0x04) == 0x55524324)
+ {
+ u64 paddr = QWORD(data + 0x08);
+ paddr.l += DWORD(data + 0x14);
+ if (paddr.l < DWORD(data + 0x14))
+ paddr.h++;
+ pr_attr("Physical Address", "0x%08x%08x",
+ paddr.h, paddr.l);
+ pr_attr("Length", "0x%08x", DWORD(data + 0x10));
+ }
+ break;
+
+ case 216:
+ /*
+ * Vendor Specific: Version Indicator Record
+ *
+ * This record is used to allow determining Firmware and CPLD revisions for
+ * components in the system. The goal of this record is to provide a
+ * flexible method to communicate to software and firmware the revisions
+ * of these components. This record replaces much of the functionality of
+ * Record Type 193. OEM SMBIOS Record Type 193 was not scaling well with
+ * the large number of potential CPLD devices, power management controllers,
+ * etc. This record is flexible such that each instance of Type 216
+ * defines one firmware component. This record also includes the string
+ * name for which software should refer to the component. The record
+ * includes both data bytes to indicate the revision and a string value. A
+ * firmware component can implement either or both. If both are supported,
+ * it allows easy display of the revision, but prevents the need for
+ * software/firmware to parse strings when doing comparisons on revisions.
+ * As there is one Type 216 Record per firmware component, the Handle for
+ * the Record can be used to tie firmware components with other OEM SMBIOS
+ * Records in the future if needed (similar to how SMBIOS Type 17 is tied
+ * to other Record Types related to DIMMs)
+ *
+ * Offset | Name | Width | Description
+ * ------------------------------------------
+ * 0x00 | Type | BYTE | 0xD8, Version Indicator Record
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | FW Type | WORD | Type of Firmware
+ * 0x06 | FW Name | STRING | Name of Firmware
+ * 0x07 | FW Version | STRING | Firmware Version
+ * 0x08 | Data Format| BYTE | Format of the Version Data
+ * 0x09 |Version Data|12 BYTES| Version Data in Format from field 0x08
+ * 0x15 | Unique ID | WORD | Unique ID for Firmware flash
+ */
+ if (gen < G8) return 0;
+ pr_handle_name("%s Version Indicator", company);
+ if (h->length < 23) break;
+ dmi_hp_216_fw_type(WORD(data + 0x04));
+ pr_attr("Firmware Name String", "%s", dmi_string(h, data[0x06]));
+ pr_attr("Firmware Version String", "%s", dmi_string(h, data[0x07]));
+ dmi_hp_216_version(data[0x08], data + 0x09);
+ if (WORD(data + 0x15))
+ pr_attr("Unique ID", "0x%04x", WORD(data + 0x15));
+ break;
+
+ case 219:
+ /*
+ * Vendor Specific: HPE ProLiant Information
+ *
+ * Source: hpwdt kernel driver
+ */
+ pr_handle_name("%s ProLiant Information", company);
+ if (h->length < 0x08) break;
+ pr_attr("Power Features", "0x%08x", DWORD(data + 0x04));
+ if (h->length < 0x0C) break;
+ pr_attr("Omega Features", "0x%08x", DWORD(data + 0x08));
+ if (h->length < 0x14) break;
+ feat = DWORD(data + 0x10);
+ pr_attr("Misc. Features", "0x%08x", feat);
+ pr_subattr("iCRU", "%s", feat & 0x0001 ? "Yes" : "No");
+ pr_subattr("UEFI", "%s", feat & 0x1400 ? "Yes" : "No");
+ break;
+
+ case 224:
+ /*
+ * Vendor Specific: Trusted Module (TPM or TCM) Status
+ *
+ * Offset | Name | Width | Description
+ * -------------------------------------
+ * 0x00 | Type | BYTE | 0xE0, Trusted Module (TPM or TCM) Status
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Status | BYTE | Status Flag Byte
+ * 0x05 | Ex Stat| BYTE | TPM Extended Status
+ * 0x06 | Type | BYTE | Trusted Module Type
+ * 0x07 | Attrib | BYTE | Trusted Module Attributes
+ * 0x08 | Handle | WORD | Handle to map to Type 216
+ * 0x0A | Chip ID| WORD | Chip Identifier Values
+ */
+ pr_handle_name("%s Trusted Module (TPM or TCM) Status", company);
+ if (h->length < 0x05) break;
+ if (!dmi_hp_224_status(data[0x04]))
+ break;
+ if (h->length < 0x0a) break;
+ dmi_hp_224_ex_status(data[0x04], data[0x05]);
+ dmi_hp_224_module_type(data[0x06]);
+ dmi_hp_224_module_attr(data[0x07]);
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Associated Handle", "0x%04X", WORD(data + 0x8));
+ if (h->length < 0x0c) break;
+ dmi_hp_224_chipid(WORD(data + 0x0a));
+ break;
+
+ case 230:
+ /*
+ * Vendor Specific: Power Supply Information OEM SMBIOS Record
+ *
+ * This record is used to communicate additional Power Supply Information
+ * beyond the Industry Standard System Power Supply (Type 39) Record.
+ *
+ * Offset| Name | Width | Description
+ * -----------------------------------------
+ * 0x00 | Type | BYTE | 0xE6, Power Supply Information Indicator
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Assoc Handle| WORD | Associated Handle (Type 39)
+ * 0x06 | Manufacturer| STRING| Actual third party manufacturer
+ * 0x07 | Revision | STRING| Power Supply Revision Level
+ * 0x08 | FRU Access | BYTE | Power Supply FRU Access Method
+ * 0x09 | I2C Bus Num | BYTE | I2C Bus #. Value based upon context
+ * 0x0A | I2C Address | BYTE | I2C Address
+ */
+ pr_handle_name("%s Power Supply Information", company);
+ if (h->length < 0x0B) break;
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Associated Handle", "0x%04X", WORD(data + 0x4));
+ pr_attr("Manufacturer", "%s", dmi_string(h, data[0x06]));
+ pr_attr("Revision", "%s", dmi_string(h, data[0x07]));
+ dmi_hp_230_method_bus_seg_addr(data[0x08], data[0x09], data[0x0A]);
+ break;
+
+ case 233:
+ /*
+ * Vendor Specific: HPE ProLiant NIC MAC Information
+ *
+ * This prints the BIOS NIC number,
+ * PCI bus/device/function, and MAC address
+ *
+ * Offset | Name | Width | Description
+ * -------------------------------------
+ * 0x00 | Type | BYTE | 0xE9, NIC structure
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Grp No | WORD | 0 for single segment
+ * 0x06 | Bus No | BYTE | PCI Bus
+ * 0x07 | Dev No | BYTE | PCI Device/Function No
+ * 0x08 | MAC | 32B | MAC addr padded w/ 0s
+ * 0x28 | Port No| BYTE | Each NIC maps to a Port
+ */
+ pr_handle_name("%s BIOS PXE NIC PCI and MAC Information",
+ company);
+ if (h->length < 0x0E) break;
+ /* If the record isn't long enough, we don't have an ID
+ * use 0xFF to use the internal counter.
+ * */
+ nic = h->length > 0x28 ? data[0x28] : 0xFF;
+ dmi_print_hp_net_iface_rec(nic, data[0x06], data[0x07],
+ &data[0x08]);
+ break;
+
+ case 236:
+ /*
+ * Vendor Specific: HPE ProLiant HDD Backplane
+ *
+ * Offset | Name | Width | Description
+ * ---------------------------------------
+ * 0x00 | Type | BYTE | 0xEC, HDD Backplane
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | I2C Address| BYTE | Backplane FRU I2C Address
+ * 0x05 | Box Number | WORD | Backplane Box Number
+ * 0x07 | NVRAM ID | WORD | Backplane NVRAM ID
+ * 0x09 | WWID | QWORD | SAS Expander WWID
+ * 0x11 | Total Bays | BYTE | Total SAS Bays
+ * 0x12 | A0 Bays | BYTE | (deprecated) Number of SAS drive bays behind port 0xA0
+ * 0x13 | A2 Bays | BYTE | (deprecated) Number of SAS drive bays behind port 0xA2
+ * 0x14 | Name | STRING| (deprecated) Backplane Name
+ */
+ pr_handle_name("%s HDD Backplane FRU Information", company);
+ if (h->length < 0x08) break;
+ pr_attr("FRU I2C Address", "0x%X raw(0x%X)", data[0x4] >> 1, data[0x4]);
+ pr_attr("Box Number", "%d", WORD(data + 0x5));
+ pr_attr("NVRAM ID", "0x%X", WORD(data + 0x7));
+ if (h->length < 0x11) break;
+ pr_attr("SAS Expander WWID", "0x%X", QWORD(data + 0x9));
+ if (h->length < 0x12) break;
+ pr_attr("Total SAS Bays", "%d", data[0x11]);
+ if (h->length < 0x15) break;
+ if (gen < G10P) {
+ pr_attr("A0 Bay Count", "%d", data[0x12]);
+ pr_attr("A2 Bay Count", "%d", data[0x13]);
+ pr_attr("Backplane Name", "%s", dmi_string(h, data[0x14]));
+ }
+ break;
+
+ case 237:
+ /*
+ * Vendor Specific: HPE DIMM Vendor Part Number Information
+ *
+ * Offset | Name | Width | Description
+ * ---------------------------------------
+ * 0x00 | Type | BYTE | 0xED, DIMM Vendor Part Number information record
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Hand Assoc | WORD | Handle to map to Type 17
+ * 0x06 | Manufacture|STRING | DIMM Manufacturer
+ * 0x07 | Part Number|STRING | DIMM Manufacturer's Part Number
+ * 0x08 | Serial Num |STRING | DIMM Vendor Serial Number
+ * 0x09 | Spare Part |STRING | DIMM Spare Part Number
+ */
+ if (gen < G9) return 0;
+ pr_handle_name("%s DIMM Vendor Information", company);
+ if (h->length < 0x08) break;
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Associated Handle", "0x%04X", WORD(data + 0x4));
+ pr_attr("DIMM Manufacturer", "%s", dmi_string(h, data[0x06]));
+ pr_attr("DIMM Manufacturer Part Number", "%s", dmi_string(h, data[0x07]));
+ if (h->length < 0x09) break;
+ pr_attr("DIMM Vendor Serial Number", "%s", dmi_string(h, data[0x08]));
+ if (h->length < 0x0A) break;
+ pr_attr("DIMM Spare Part Number", "%s", dmi_string(h, data[0x09]));
+ break;
+
+ case 238:
+ /*
+ * Vendor Specific: HPE USB Port Connector Correlation Record
+ *
+ * Offset | Name | Width | Description
+ * ---------------------------------------
+ * 0x00 | Type | BYTE | 0xEE, HP Device Correlation Record
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Hand Assoc | WORD | Handle to map to Type 8
+ * 0x06 | Parent Bus | BYTE | PCI Bus number of USB controller of this port
+ * 0x07 | Par Dev/Fun| BYTE | PCI Dev/Fun of USB Controller of this port
+ * 0x08 | Location | BYTE | Enumerated value of location of USB port
+ * 0x09 | Flags | WORD | USB Shared Management Port
+ * 0x0B | Port Inst | BYTE | Instance number for this type of USB port
+ * 0x0C | Parent Hub | BYTE | Instance number of internal Hub
+ * 0x0D | Port Speed | BYTE | Enumerated value of speed configured by BIOS
+ * 0x0E | Device Path| STRING| UEFI Device Path of USB endpoint
+ */
+ if (gen < G9) return 0;
+ pr_handle_name("%s Proliant USB Port Connector Correlation Record", company);
+ if (h->length < 0x0F) break;
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Associated Handle", "0x%04X", WORD(data + 0x4));
+ pr_attr("PCI Device", "%02x:%02x.%x", data[0x6],
+ data[0x7] >> 3, data[0x7] & 0x7);
+ dmi_hp_238_loc("Location", data[0x8]);
+ dmi_hp_238_flags("Management Port", WORD(data + 0x9));
+ pr_attr("Port Instance", "%d", data[0xB]);
+ if (data[0xC] != 0xFE)
+ pr_attr("Parent Hub Port Instance", "%d", data[0xC]);
+ else
+ pr_attr("Parent Hub Port Instance", "N/A");
+ dmi_hp_238_speed("Port Speed Capability", data[0xD]);
+ pr_attr("Device Path", "%s", dmi_string(h, data[0xE]));
+ break;
+
+ case 240:
+ /*
+ * Vendor Specific: HPE Proliant Inventory Record
+ *
+ * Reports firmware version information for devices that report their
+ * firmware using their UEFI drivers. Additionally provides association
+ * with other SMBIOS records, such as Type 203 (which in turn is
+ * associated with Types 9, 41, and 228).
+ *
+ * Offset | Name | Width | Description
+ * ---------------------------------------
+ * 0x00 | Type | BYTE | 0xF0, HP Firmware Inventory Record
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Hndl Assoc | WORD | Handle to map to Type 203
+ * 0x06 | Pkg Vers | DWORD | FW Vers Release of All FW in Device
+ * 0x0A | Ver String | STRING| FW Version String
+ * 0x0B | Image Size | QWORD | FW image size (bytes)
+ * 0x13 | Attributes | QWORD | Bitfield: Is attribute defined?
+ * 0x1B | Attr Set | QWORD | BitField: If defined, is attribute set?
+ * 0x23 | Version | DWORD | Lowest supported version.
+ */
+ pr_handle_name("%s Proliant Inventory Record", company);
+ if (h->length < 0x27) break;
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Associated Handle", "0x%04X", WORD(data + 0x4));
+ pr_attr("Package Version", "0x%08X", DWORD(data + 0x6));
+ pr_attr("Version String", "%s", dmi_string(h, data[0x0A]));
+
+ if (DWORD(data + 0x0B))
+ dmi_print_memory_size("Image Size", QWORD(data + 0xB), 0);
+ else
+ pr_attr("Image Size", "Not Available");
+
+ dmi_hp_240_attr(QWORD(data + 0x13), QWORD(data + 0x1B));
+
+ if (DWORD(data + 0x23))
+ pr_attr("Lowest Supported Version", "0x%08X", DWORD(data + 0x23));
+ else
+ pr_attr("Lowest Supported Version", "Not Available");
+ break;
+
+ case 242:
+ /*
+ * Vendor Specific: HPE Hard Drive Inventory Record
+ *
+ * This record provides a mechanism for software to gather information for
+ * NVMe and SATA drives that are directly attached to the system. This
+ * record does not contain drive information for drives attached to a HBA
+ * (i.e. a SmartArray controller). This record will only contain information
+ * for a hard drive detected by the BIOS during POST and does not
+ * comprehend a hot plug event after the system has booted.
+ *
+ * Offset | Name | Width | Description
+ * ---------------------------------------
+ * 0x00 | Type | BYTE | 0xF2, HPE Hard Drive Inventory Record
+ * 0x01 | Length | BYTE | Length of structure
+ * 0x02 | Handle | WORD | Unique handle
+ * 0x04 | Hndl Assoc | WORD | Handle to map to Type 203
+ * 0x06 | HDD Type | BYTE | Hard drive type
+ * 0x07 | HDD Uniq ID| QWORD | SATA-> WWID. NVMe -> IEEE Ext Uniq ID.
+ * 0x0F | Capacity | DWORD | Drive Capacity in Mbytes
+ * 0x13 | Hours | 16BYTE| Number of poweron hours
+ * 0x23 | Reserved | BYTE | Reserved
+ * 0x24 | Power | BTYE | Wattage
+ * 0x25 | Form Factor| BYTE | HDD Form Factor
+ * 0x26 | Health | BYTE | Hard Drive Health Status
+ * 0x27 | Serial Num | STRING| NVMe/SATA Serial Number
+ * 0x28 | Model Num | STRING| NVMe/SATA Model Number
+ * 0x29 | FW Rev | STRING| Firmware revision
+ * 0x2A | Location | STRING| Drive location
+ * 0x2B | Crypt Stat | BYTE | Drive encryption status from BIOS
+ * 0x2C | Capacity | QWORD | Hard Drive capacity in bytes
+ * 0x34 | Block Size | DWORD | Logical Block Size in bytes
+ * 0x38 | Rot Speed | WORD | Nominal Rotational Speed (RPM)
+ * 0x3A | Neg Speed | WORD | Current negotiated bus speed
+ * 0x3C | Cap Speed | WORD | Fastest Capable Bus Speed of drive
+ */
+ if (gen < G10) return 0;
+ pr_handle_name("%s ProLiant Hard Drive Inventory Record", company);
+ if (h->length < 0x2C) break;
+ if (!(opt.flags & FLAG_QUIET))
+ pr_attr("Associated Handle", "0x%04X", WORD(data + 0x4));
+ dmi_hp_242_hdd_type(data[0x06]);
+ pr_attr("ID", "%llx", QWORD(data + 0x07));
+ if (h->length < 0x3E)
+ pr_attr("Capacity", "%u MB", DWORD(data + 0x0F));
+ else
+ dmi_print_memory_size("Capacity", QWORD(data + 0x2C), 0);
+ /* NB: Poweron low QWORD good for 2,104,351,365,926,255 years */
+ pr_attr("Poweron", "%ld hours", QWORD(data + 0x13));
+ if (data[0x24])
+ pr_attr("Power Wattage", "%hhu W", data[0x24]);
+ else
+ pr_attr("Power Wattage", "%s", "Unknown");
+ dmi_hp_242_form_factor(data[0x25]);
+ feat = data[0x26];
+ pr_attr("Health Status", "%s", (feat == 0x00) ? "OK" :
+ (feat == 0x01) ? "Warning" :
+ (feat == 0x02) ? "Critical" :
+ (feat == 0xFF) ? "Unknown" : "Reserved");
+ pr_attr("Serial Number", dmi_string(h, data[0x27]));
+ pr_attr("Model Number", dmi_string(h, data[0x28]));
+ pr_attr("Firmware Revision", dmi_string(h, data[0x29]));
+ pr_attr("Location", dmi_string(h, data[0x2A]));
+ feat = data[0x2B];
+ pr_attr("Encryption Status", "%s", (feat == 0) ? "Not Encrypted" :
+ (feat == 1) ? "Encrypted" :
+ (feat == 2) ? "Unknown" :
+ (feat == 3) ? "Not Supported" : "Reserved");
+ if (h->length < 0x3E) break;
+ pr_attr("Block Size", "%u bytes", DWORD(data + 0x34));
+ /* Rotational Speed: 0 -> Not Reported, 1 -> N/A (SSD) */
+ if (data[0x38] > 1)
+ pr_attr("Rotational Speed", "%hhu RPM", data[0x38]);
+ dmi_hp_242_speed("Negotiated Speed", WORD(data + 0x3A));
+ dmi_hp_242_speed("Capable Speed", WORD(data + 0x3C));
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
+{
+ u8 *data = h->data;
+
+ switch (h->type)
+ {
+ case 131:
+ /*
+ * Vendor Specific: ThinkVantage Technologies feature bits
+ *
+ * Source: Compal hel81 Service Manual Software Specification,
+ * documented under "System Management BIOS(SM BIOS)
+ * version 2.4 or greater"
+ *
+ * Offset | Name | Width | Description
+ * ----------------------------------------------
+ * 0x00 | Type | BYTE | 0x83
+ * 0x01 | Length | BYTE | 0x16
+ * 0x02 | Handle | WORD | Varies
+ * 0x04 | Version | BYTE | 0x01
+ * 0x05 | TVT Structure | BYTEx16 | Each of the 128 bits represents a TVT feature:
+ * | | | - bit 127 means diagnostics (PC Doctor) is available
+ * | | | (http://www.pc-doctor.com/company/pr-articles/45-lenovo-introduces-thinkvantage-toolbox)
+ * | | | - the rest (126-0) are reserved/unknown
+ *
+ * It must also be followed by a string containing
+ * "TVT-Enablement". There exist other type 131 records
+ * with different length and a different string, for
+ * other purposes.
+ */
+
+ if (h->length != 0x16
+ || strcmp(dmi_string(h, 1), "TVT-Enablement") != 0)
+ return 0;
+
+ pr_handle_name("ThinkVantage Technologies");
+ pr_attr("Version", "%u", data[0x04]);
+ pr_attr("Diagnostics", "%s",
+ data[0x14] & 0x80 ? "Available" : "No");
+ break;
+
+ case 135:
+ /*
+ * Vendor Specific: Device Presence Detection bits
+ *
+ * Source: Compal hel81 Service Manual Software Specification,
+ * documented as "SMBIOS Type 135: Bulk for Lenovo
+ * Mobile PC Unique OEM Data" under appendix D.
+ *
+ * Offset | Name | Width | Description
+ * ---------------------------------------------------
+ * 0x00 | Type | BYTE | 0x87
+ * 0x01 | Length | BYTE | 0x0A
+ * 0x02 | Handle | WORD | Varies
+ * 0x04 | Signature | WORD | 0x5054 (ASCII for "TP")
+ * 0x06 | OEM struct offset | BYTE | 0x07
+ * 0x07 | OEM struct number | BYTE | 0x03, for this structure
+ * 0x08 | OEM struct revision | BYTE | 0x01, for this format
+ * 0x09 | Device presence bits | BYTE | Each of the 8 bits indicates device presence:
+ * | | | - bit 0 indicates the presence of a fingerprint reader
+ * | | | - the rest (7-1) are reserved/unknown
+ *
+ * Other OEM struct number+rev combinations have been
+ * seen in the wild but we don't know how to decode
+ * them.
+ */
+
+ if (h->length < 0x0A || data[0x04] != 'T' || data[0x05] != 'P')
+ return 0;
+
+ /* Bail out if not the expected format */
+ if (data[0x06] != 0x07 || data[0x07] != 0x03 || data[0x08] != 0x01)
+ return 0;
+
+ pr_handle_name("ThinkPad Device Presence Detection");
+ pr_attr("Fingerprint Reader", "%s",
+ data[0x09] & 0x01 ? "Present" : "No");
+ break;
+
+ case 140:
+ /*
+ * Vendor Specific: ThinkPad Embedded Controller Program
+ *
+ * Source: some guesswork, and publicly available information;
+ * Lenovo's BIOS update READMEs often contain the ECP IDs
+ * which match the first string in this type.
+ *
+ * Offset | Name | Width | Description
+ * ----------------------------------------------------
+ * 0x00 | Type | BYTE | 0x8C
+ * 0x01 | Length | BYTE |
+ * 0x02 | Handle | WORD | Varies
+ * 0x04 | Signature | BYTEx6 | ASCII for "LENOVO"
+ * 0x0A | OEM struct offset | BYTE | 0x0B
+ * 0x0B | OEM struct number | BYTE | 0x07, for this structure
+ * 0x0C | OEM struct revision | BYTE | 0x01, for this format
+ * 0x0D | ECP version ID | STRING |
+ * 0x0E | ECP release date | STRING |
+ */
+
+ if (h->length < 0x0F || memcmp(data + 4, "LENOVO", 6) != 0)
+ return 0;
+
+ /* Bail out if not the expected format */
+ if (data[0x0A] != 0x0B || data[0x0B] != 0x07 || data[0x0C] != 0x01)
+ return 0;
+
+ pr_handle_name("ThinkPad Embedded Controller Program");
+ pr_attr("Version ID", "%s", dmi_string(h, 1));
+ pr_attr("Release Date", "%s", dmi_string(h, 2));
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Dispatch vendor-specific entries decoding
+ * Return 1 if decoding was successful, 0 otherwise
+ */
+int dmi_decode_oem(const struct dmi_header *h)
+{
+ switch (dmi_vendor)
+ {
+ case VENDOR_HP:
+ case VENDOR_HPE:
+ return dmi_decode_hp(h);
+ case VENDOR_ACER:
+ return dmi_decode_acer(h);
+ case VENDOR_IBM:
+ case VENDOR_LENOVO:
+ return dmi_decode_ibm_lenovo(h);
+ default:
+ return 0;
+ }
+}
diff --git a/dmioem.h b/dmioem.h
new file mode 100644
index 0000000..b79b769
--- /dev/null
+++ b/dmioem.h
@@ -0,0 +1,25 @@
+/*
+ * Decoding of OEM-specific entries
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2007-2008 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+struct dmi_header;
+
+void dmi_set_vendor(const char *s, const char *p);
+int dmi_decode_oem(const struct dmi_header *h);
diff --git a/dmiopt.c b/dmiopt.c
new file mode 100644
index 0000000..fa84f22
--- /dev/null
+++ b/dmiopt.c
@@ -0,0 +1,390 @@
+/*
+ * Command line handling of dmidecode
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2005-2008 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "config.h"
+#include "types.h"
+#include "util.h"
+#include "dmidecode.h"
+#include "dmiopt.h"
+
+
+/* Options are global */
+struct opt opt;
+
+
+/*
+ * Handling of option --type
+ */
+
+struct type_keyword
+{
+ const char *keyword;
+ const u8 *type;
+};
+
+static const u8 opt_type_bios[] = { 0, 13, 255 };
+static const u8 opt_type_system[] = { 1, 12, 15, 23, 32, 255 };
+static const u8 opt_type_baseboard[] = { 2, 10, 41, 255 };
+static const u8 opt_type_chassis[] = { 3, 255 };
+static const u8 opt_type_processor[] = { 4, 255 };
+static const u8 opt_type_memory[] = { 5, 6, 16, 17, 255 };
+static const u8 opt_type_cache[] = { 7, 255 };
+static const u8 opt_type_connector[] = { 8, 255 };
+static const u8 opt_type_slot[] = { 9, 255 };
+
+static const struct type_keyword opt_type_keyword[] = {
+ { "bios", opt_type_bios },
+ { "system", opt_type_system },
+ { "baseboard", opt_type_baseboard },
+ { "chassis", opt_type_chassis },
+ { "processor", opt_type_processor },
+ { "memory", opt_type_memory },
+ { "cache", opt_type_cache },
+ { "connector", opt_type_connector },
+ { "slot", opt_type_slot },
+};
+
+static void print_opt_type_list(void)
+{
+ unsigned int i;
+
+ fprintf(stderr, "Valid type keywords are:\n");
+ for (i = 0; i < ARRAY_SIZE(opt_type_keyword); i++)
+ {
+ fprintf(stderr, " %s\n", opt_type_keyword[i].keyword);
+ }
+}
+
+static u8 *parse_opt_type(u8 *p, const char *arg)
+{
+ unsigned int i;
+
+ /* Allocate memory on first call only */
+ if (p == NULL)
+ {
+ p = (u8 *)calloc(256, sizeof(u8));
+ if (p == NULL)
+ {
+ perror("calloc");
+ return NULL;
+ }
+ }
+
+ /* First try as a keyword */
+ for (i = 0; i < ARRAY_SIZE(opt_type_keyword); i++)
+ {
+ if (!strcasecmp(arg, opt_type_keyword[i].keyword))
+ {
+ int j = 0;
+ while (opt_type_keyword[i].type[j] != 255)
+ p[opt_type_keyword[i].type[j++]] = 1;
+ goto found;
+ }
+ }
+
+ /* Else try as a number */
+ while (*arg != '\0')
+ {
+ unsigned long val;
+ char *next;
+
+ val = strtoul(arg, &next, 0);
+ if (next == arg || (*next != '\0' && *next != ',' && *next != ' '))
+ {
+ fprintf(stderr, "Invalid type keyword: %s\n", arg);
+ print_opt_type_list();
+ goto exit_free;
+ }
+ if (val > 0xff)
+ {
+ fprintf(stderr, "Invalid type number: %lu\n", val);
+ goto exit_free;
+ }
+
+ p[val] = 1;
+ arg = next;
+ while (*arg == ',' || *arg == ' ')
+ arg++;
+ }
+
+found:
+ return p;
+
+exit_free:
+ free(p);
+ return NULL;
+}
+
+
+/*
+ * Handling of option --string
+ */
+
+/* This lookup table could admittedly be reworked for improved performance.
+ Due to the low count of items in there at the moment, it did not seem
+ worth the additional code complexity though. */
+static const struct string_keyword opt_string_keyword[] = {
+ { "bios-vendor", 0, 0x04 },
+ { "bios-version", 0, 0x05 },
+ { "bios-release-date", 0, 0x08 },
+ { "bios-revision", 0, 0x15 }, /* 0x14 and 0x15 */
+ { "firmware-revision", 0, 0x17 }, /* 0x16 and 0x17 */
+ { "system-manufacturer", 1, 0x04 },
+ { "system-product-name", 1, 0x05 },
+ { "system-version", 1, 0x06 },
+ { "system-serial-number", 1, 0x07 },
+ { "system-uuid", 1, 0x08 }, /* dmi_system_uuid() */
+ { "system-sku-number", 1, 0x19 },
+ { "system-family", 1, 0x1a },
+ { "baseboard-manufacturer", 2, 0x04 },
+ { "baseboard-product-name", 2, 0x05 },
+ { "baseboard-version", 2, 0x06 },
+ { "baseboard-serial-number", 2, 0x07 },
+ { "baseboard-asset-tag", 2, 0x08 },
+ { "chassis-manufacturer", 3, 0x04 },
+ { "chassis-type", 3, 0x05 }, /* dmi_chassis_type() */
+ { "chassis-version", 3, 0x06 },
+ { "chassis-serial-number", 3, 0x07 },
+ { "chassis-asset-tag", 3, 0x08 },
+ { "processor-family", 4, 0x06 }, /* dmi_processor_family() */
+ { "processor-manufacturer", 4, 0x07 },
+ { "processor-version", 4, 0x10 },
+ { "processor-frequency", 4, 0x16 }, /* dmi_processor_frequency() */
+};
+
+/* This is a template, 3rd field is set at runtime. */
+static struct string_keyword opt_oem_string_keyword =
+ { NULL, 11, 0x00 };
+
+static void print_opt_string_list(void)
+{
+ unsigned int i;
+
+ fprintf(stderr, "Valid string keywords are:\n");
+ for (i = 0; i < ARRAY_SIZE(opt_string_keyword); i++)
+ {
+ fprintf(stderr, " %s\n", opt_string_keyword[i].keyword);
+ }
+}
+
+static int parse_opt_string(const char *arg)
+{
+ unsigned int i;
+
+ if (opt.string)
+ {
+ fprintf(stderr, "Only one string can be specified\n");
+ return -1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(opt_string_keyword); i++)
+ {
+ if (!strcasecmp(arg, opt_string_keyword[i].keyword))
+ {
+ opt.string = &opt_string_keyword[i];
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "Invalid string keyword: %s\n", arg);
+ print_opt_string_list();
+ return -1;
+}
+
+static int parse_opt_oem_string(const char *arg)
+{
+ unsigned long val;
+ char *next;
+
+ if (opt.string)
+ {
+ fprintf(stderr, "Only one string can be specified\n");
+ return -1;
+ }
+
+ /* Return the number of OEM strings */
+ if (strcmp(arg, "count") == 0)
+ goto done;
+
+ val = strtoul(arg, &next, 10);
+ if (next == arg || *next != '\0' || val == 0x00 || val > 0xff)
+ {
+ fprintf(stderr, "Invalid OEM string number: %s\n", arg);
+ return -1;
+ }
+
+ opt_oem_string_keyword.offset = val;
+done:
+ opt.string = &opt_oem_string_keyword;
+ return 0;
+}
+
+static u32 parse_opt_handle(const char *arg)
+{
+ u32 val;
+ char *next;
+
+ val = strtoul(arg, &next, 0);
+ if (next == arg || *next != '\0' || val > 0xffff)
+ {
+ fprintf(stderr, "Invalid handle number: %s\n", arg);
+ return ~0;
+ }
+ return val;
+}
+
+/*
+ * Command line options handling
+ */
+
+/* Return -1 on error, 0 on success */
+int parse_command_line(int argc, char * const argv[])
+{
+ int option;
+ const char *optstring = "d:hqs:t:uH:V";
+ struct option longopts[] = {
+ { "dev-mem", required_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "no-quirks", no_argument, NULL, 'Q' },
+ { "string", required_argument, NULL, 's' },
+ { "type", required_argument, NULL, 't' },
+ { "dump", no_argument, NULL, 'u' },
+ { "dump-bin", required_argument, NULL, 'B' },
+ { "from-dump", required_argument, NULL, 'F' },
+ { "handle", required_argument, NULL, 'H' },
+ { "oem-string", required_argument, NULL, 'O' },
+ { "no-sysfs", no_argument, NULL, 'S' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((option = getopt_long(argc, argv, optstring, longopts, NULL)) != -1)
+ switch (option)
+ {
+ case 'B':
+ opt.flags |= FLAG_DUMP_BIN;
+ opt.dumpfile = optarg;
+ break;
+ case 'F':
+ opt.flags |= FLAG_FROM_DUMP;
+ opt.dumpfile = optarg;
+ break;
+ case 'd':
+ opt.devmem = optarg;
+ break;
+ case 'h':
+ opt.flags |= FLAG_HELP;
+ break;
+ case 'q':
+ opt.flags |= FLAG_QUIET;
+ break;
+ case 'Q':
+ opt.flags |= FLAG_NO_QUIRKS;
+ break;
+ case 's':
+ if (parse_opt_string(optarg) < 0)
+ return -1;
+ opt.flags |= FLAG_QUIET;
+ break;
+ case 'O':
+ if (parse_opt_oem_string(optarg) < 0)
+ return -1;
+ opt.flags |= FLAG_QUIET;
+ break;
+ case 't':
+ opt.type = parse_opt_type(opt.type, optarg);
+ if (opt.type == NULL)
+ return -1;
+ break;
+ case 'H':
+ opt.handle = parse_opt_handle(optarg);
+ if (opt.handle == ~0U)
+ return -1;
+ break;
+ case 'u':
+ opt.flags |= FLAG_DUMP;
+ break;
+ case 'S':
+ opt.flags |= FLAG_NO_SYSFS;
+ break;
+ case 'V':
+ opt.flags |= FLAG_VERSION;
+ break;
+ case '?':
+ switch (optopt)
+ {
+ case 's':
+ fprintf(stderr, "String keyword expected\n");
+ print_opt_string_list();
+ break;
+ case 't':
+ fprintf(stderr, "Type number or keyword expected\n");
+ print_opt_type_list();
+ break;
+ }
+ return -1;
+ }
+
+ /* Check for mutually exclusive output format options */
+ if ((opt.string != NULL) + (opt.type != NULL)
+ + !!(opt.flags & FLAG_DUMP_BIN) + (opt.handle != ~0U) > 1)
+ {
+ fprintf(stderr, "Options --string, --type, --handle and --dump-bin are mutually exclusive\n");
+ return -1;
+ }
+
+ if ((opt.flags & FLAG_FROM_DUMP) && (opt.flags & FLAG_DUMP_BIN))
+ {
+ fprintf(stderr, "Options --from-dump and --dump-bin are mutually exclusive\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void print_help(void)
+{
+ static const char *help =
+ "Usage: dmidecode [OPTIONS]\n"
+ "Options are:\n"
+ " -d, --dev-mem FILE Read memory from device FILE (default: " DEFAULT_MEM_DEV ")\n"
+ " -h, --help Display this help text and exit\n"
+ " -q, --quiet Less verbose output\n"
+ " --no-quirks Decode everything without quirks\n"
+ " -s, --string KEYWORD Only display the value of the given DMI string\n"
+ " -t, --type TYPE Only display the entries of given type\n"
+ " -H, --handle HANDLE Only display the entry of given handle\n"
+ " -u, --dump Do not decode the entries\n"
+ " --dump-bin FILE Dump the DMI data to a binary file\n"
+ " --from-dump FILE Read the DMI data from a binary file\n"
+ " --no-sysfs Do not attempt to read DMI data from sysfs files\n"
+ " --oem-string N Only display the value of the given OEM string\n"
+ " -V, --version Display the version and exit\n";
+
+ printf("%s", help);
+}
diff --git a/dmiopt.h b/dmiopt.h
new file mode 100644
index 0000000..62ffcbb
--- /dev/null
+++ b/dmiopt.h
@@ -0,0 +1,52 @@
+/*
+ * Command line handling of dmidecode
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2005-2008 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "types.h"
+
+struct string_keyword
+{
+ const char *keyword;
+ u8 type;
+ u8 offset;
+};
+
+struct opt
+{
+ const char *devmem;
+ unsigned int flags;
+ u8 *type;
+ const struct string_keyword *string;
+ char *dumpfile;
+ u32 handle;
+};
+extern struct opt opt;
+
+#define FLAG_VERSION (1 << 0)
+#define FLAG_HELP (1 << 1)
+#define FLAG_DUMP (1 << 2)
+#define FLAG_QUIET (1 << 3)
+#define FLAG_DUMP_BIN (1 << 4)
+#define FLAG_FROM_DUMP (1 << 5)
+#define FLAG_NO_SYSFS (1 << 6)
+#define FLAG_NO_QUIRKS (1 << 7)
+
+int parse_command_line(int argc, char * const argv[]);
+void print_help(void);
diff --git a/dmioutput.c b/dmioutput.c
new file mode 100644
index 0000000..42f8d32
--- /dev/null
+++ b/dmioutput.c
@@ -0,0 +1,137 @@
+/*
+ * Generic output functions
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2020 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "dmioutput.h"
+
+void pr_comment(const char *format, ...)
+{
+ va_list args;
+
+ printf("# ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+void pr_info(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+void pr_handle(const struct dmi_header *h)
+{
+ printf("Handle 0x%04X, DMI type %d, %d bytes\n",
+ h->handle, h->type, h->length);
+}
+
+void pr_handle_name(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+void pr_attr(const char *name, const char *format, ...)
+{
+ va_list args;
+
+ printf("\t%s: ", name);
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+void pr_subattr(const char *name, const char *format, ...)
+{
+ va_list args;
+
+ printf("\t\t%s: ", name);
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+void pr_list_start(const char *name, const char *format, ...)
+{
+ va_list args;
+
+ printf("\t%s:", name);
+
+ /* format is optional, skip value if not provided */
+ if (format)
+ {
+ printf(" ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ }
+ printf("\n");
+
+}
+
+void pr_list_item(const char *format, ...)
+{
+ va_list args;
+
+ printf("\t\t");
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+void pr_list_end(void)
+{
+ /* a no-op for text output */
+}
+
+void pr_sep(void)
+{
+ printf("\n");
+}
+
+void pr_struct_err(const char *format, ...)
+{
+ va_list args;
+
+ printf("\t");
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
diff --git a/dmioutput.h b/dmioutput.h
new file mode 100644
index 0000000..a492ec0
--- /dev/null
+++ b/dmioutput.h
@@ -0,0 +1,34 @@
+/*
+ * Generic output functions
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2020 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmidecode.h"
+
+void pr_comment(const char *format, ...);
+void pr_info(const char *format, ...);
+void pr_handle(const struct dmi_header *h);
+void pr_handle_name(const char *format, ...);
+void pr_attr(const char *name, const char *format, ...);
+void pr_subattr(const char *name, const char *format, ...);
+void pr_list_start(const char *name, const char *format, ...);
+void pr_list_item(const char *format, ...);
+void pr_list_end(void);
+void pr_sep(void);
+void pr_struct_err(const char *format, ...);
diff --git a/man/biosdecode.8 b/man/biosdecode.8
new file mode 100644
index 0000000..b0996d7
--- /dev/null
+++ b/man/biosdecode.8
@@ -0,0 +1,92 @@
+.TH BIOSDECODE 8 "February 2007" "dmidecode"
+.\"
+.SH NAME
+biosdecode \- \s-1BIOS\s0 information decoder
+.\"
+.SH SYNOPSIS
+.B biosdecode
+.RI [ OPTIONS ]
+.\"
+.SH DESCRIPTION
+.B biosdecode
+parses the \s-1BIOS\s0 memory and prints information about all structures (or
+entry points) it knows of. Currently known entry point types are:
+.IP \(bu "\w'\(bu'u+1n"
+\s-1SMBIOS\s0 (System Management \s-1BIOS\s0)
+.br
+Use
+.B dmidecode
+for a more detailed output.
+.IP \(bu
+\s-1DMI\s0 (Desktop Management Interface, a legacy version of \s-1SMBIOS\s0)
+.br
+Use
+.B dmidecode
+for a more detailed output.
+.IP \(bu
+\s-1SYSID\s0
+.IP \(bu
+\s-1PNP\s0 (Plug and Play)
+.IP \(bu
+\s-1ACPI\s0 (Advanced Configuration and Power Interface)
+.IP \(bu
+\s-1BIOS32\s0 (\s-1BIOS32\s0 Service Directory)
+.IP \(bu
+\s-1PIR\s0 (\s-1PCI\s0 \s-1IRQ\s0 Routing)
+.IP \(bu
+\s-132OS\s0 (\s-1BIOS32\s0 Extension, Compaq-specific)
+.br
+See
+.B ownership
+for a Compaq ownership tag retrieval tool.
+.IP \(bu
+\s-1SNY\s0 (Sony-specific, not decoded)
+.IP \(bu
+\s-1VPD\s0 (Vital Product Data, IBM-specific)
+.br
+Use
+.B vpddecode
+for a more detailed output.
+.IP \(bu
+\s-1FJKEYINF\s0 (Application Panel, Fujitsu-specific)
+
+.PP
+.B biosdecode
+started its life as a part of
+.B dmidecode
+but as more entry point types were added, it was moved to a different
+program.
+.\"
+.SH OPTIONS
+.TP
+.BR "-d" ", " "--dev-mem \fIFILE\fP"
+Read memory from device \fIFILE\fP (default: \fI/dev/mem\fP)
+.TP
+.BR " " " " "--pir \fBfull\fP"
+Decode the details of the PCI IRQ routing table.
+Only \fBfull\fP mode is supported.
+.TP
+.BR "-h" ", " "--help"
+Display usage information and exit
+.TP
+.BR "-V" ", " "--version"
+Display the version and exit
+.\"
+.SH FILES
+.I /dev/mem
+.\"
+.SH BUGS
+Most of the time,
+.B biosdecode
+prints too much information (you don't really care about addresses)
+or not enough (because it doesn't follow pointers and has no lookup
+tables).
+.\"
+.SH AUTHORS
+Alan Cox, Jean Delvare
+.\"
+.SH "SEE ALSO"
+.BR dmidecode (8),
+.BR mem (4),
+.BR ownership (8),
+.BR vpddecode (8)
diff --git a/man/dmidecode.8 b/man/dmidecode.8
new file mode 100644
index 0000000..83affc2
--- /dev/null
+++ b/man/dmidecode.8
@@ -0,0 +1,331 @@
+.TH DMIDECODE 8 "February 2023" "dmidecode"
+.\"
+.SH NAME
+dmidecode \- \s-1DMI\s0 table decoder
+.\"
+.SH SYNOPSIS
+.B dmidecode
+.RI [ OPTIONS ]
+.\"
+.SH DESCRIPTION
+.B dmidecode
+is a tool for dumping a computer's \s-1DMI\s0 (some say \s-1SMBIOS\s0) table
+contents in a human-readable format. This table contains a description of the
+system's hardware components, as well as other useful pieces of information
+such as serial numbers and \s-1BIOS\s0 revision. Thanks to this table, you can
+retrieve this information without having to probe for the actual hardware.
+While this is a good point in terms of report speed and safeness, this also
+makes the presented information possibly unreliable.
+
+The \s-1DMI\s0 table doesn't only describe what the system is currently made
+of, it also can report the possible evolutions (such as the fastest supported
+\s-1CPU\s0 or the maximal amount of memory supported).
+
+\s-1SMBIOS\s0 stands for System Management \s-1BIOS\s0, while \s-1DMI\s0
+stands for Desktop Management Interface. Both standards are tightly related
+and developed by the \s-1DMTF\s0 (Desktop Management Task Force).
+
+As you run it,
+.B dmidecode
+will try to locate the \s-1DMI\s0 table. It will first try to read the DMI table
+from sysfs, and next try reading directly from memory if sysfs access failed.
+If
+.B dmidecode
+succeeds in locating a valid DMI table, it will then parse this table
+and display a list of records like this one:
+
+Handle 0x0002, DMI type 2, 8 bytes.
+Base Board Information
+ Manufacturer: Intel
+ Product Name: C440GX+
+ Version: 727281-001
+ Serial Number: INCY92700942
+
+Each record has:
+.IP \(bu "\w'\(bu'u+1n"
+A handle. This is a unique identifier, which allows records to
+reference each other. For example, processor records usually reference
+cache memory records using their handles.
+.IP \(bu
+A type. The \s-1SMBIOS\s0 specification defines different types of elements
+a computer can be made of. In this example, the type is 2, which
+means that the record contains "Base Board Information".
+.IP \(bu
+A size. Each record has a 4-byte header (2 for the handle, 1 for the type,
+1 for the size), the rest is used by the record data. This value doesn't
+take text strings into account (these are placed at the end of the record),
+so the actual length of the record may be (and is often) greater than the
+displayed value.
+.IP \(bu
+Decoded values. The information presented of course depends on the type
+of record. Here, we learn about the board's manufacturer, model, version
+and serial number.
+.\"
+.SH OPTIONS
+.TP
+.BR "-d" ", " "--dev-mem \fIFILE\fP"
+Read memory from device \fIFILE\fP (default: \fI/dev/mem\fP)
+.TP
+.BR "-q" ", " "--quiet"
+Be less verbose. Unknown, inactive and \s-1OEM\s0-specific entries are not
+displayed. Meta-data and handle references are hidden.
+.TP
+.BR " " " " "--no-quirks"
+Decode everything exactly as it is in the table, without trying to fix up
+common mistakes or hide irrelevant fields.
+This mode is primarily aimed at firmware developers.
+.TP
+.BR "-s" ", " "--string \fIKEYWORD\fP"
+Only display the value of the \s-1DMI\s0 string identified by \fIKEYWORD\fP.
+It must be a keyword from the following list:
+.nh
+.BR bios\-vendor ,
+.BR bios\-version ,
+.BR bios\-release\-date ,
+.BR bios\-revision ,
+.BR firmware\-revision ,
+.BR system\-manufacturer ,
+.BR system\-product\-name ,
+.BR system\-version ,
+.BR system\-serial\-number ,
+.BR system\-uuid ,
+.BR system\-sku\-number ,
+.BR system\-family ,
+.BR baseboard\-manufacturer ,
+.BR baseboard\-product\-name ,
+.BR baseboard\-version ,
+.BR baseboard\-serial\-number ,
+.BR baseboard\-asset\-tag ,
+.BR chassis\-manufacturer ,
+.BR chassis\-type ,
+.BR chassis\-version ,
+.BR chassis\-serial\-number ,
+.BR chassis\-asset\-tag ,
+.BR processor\-family ,
+.BR processor\-manufacturer ,
+.BR processor\-version ,
+.BR processor\-frequency .
+.hy
+Each keyword corresponds to a given \s-1DMI\s0 type and a given offset
+within this entry type.
+Not all strings may be meaningful or even defined on all systems. Some
+keywords may return more than one result on some systems (e.g.
+.nh
+.B processor\-version
+.hy
+on a multi-processor system).
+If \fIKEYWORD\fP is not provided or not valid, a list of all valid
+keywords is printed and
+.B dmidecode
+exits with an error.
+This option cannot be used more than once.
+
+Note: on Linux, most of these strings can alternatively be read directly
+from
+.BR sysfs ,
+typically from files under
+.IR /sys/devices/virtual/dmi/id .
+Most of these files are even readable by regular users.
+.TP
+.BR "-t" ", " "--type \fITYPE\fP"
+Only display the entries of type \fITYPE\fP. It can be either a
+\s-1DMI\s0 type number, or a comma-separated list of type numbers, or a
+keyword from the following list:
+.nh
+.BR bios ,
+.BR system ,
+.BR baseboard ,
+.BR chassis ,
+.BR processor ,
+.BR memory ,
+.BR cache ,
+.BR connector ,
+.BR slot .
+.hy
+Refer to the DMI TYPES section below for details.
+If this option is used more than once, the set of displayed entries will be
+the union of all the given types.
+If \fITYPE\fP is not provided or not valid, a list of all valid keywords
+is printed and
+.B dmidecode
+exits with an error.
+.TP
+.BR "-H" ", " "--handle \fIHANDLE\fP"
+Only display the entry whose handle matches \fIHANDLE\fP.
+\fIHANDLE\fP is a 16-bit integer.
+.TP
+.BR "-u" ", " "--dump"
+Do not decode the entries, dump their contents as hexadecimal instead.
+Note that this is still a text output, no binary data will be thrown upon
+you. The strings attached to each entry are displayed as both
+hexadecimal and \s-1ASCII\s0. This option is mainly useful for debugging.
+.TP
+.BR " " " " "--dump-bin \fIFILE\fP"
+Do not decode the entries, instead dump the DMI data to a file in binary
+form. The generated file is suitable to pass to \fB--from-dump\fP
+later.
+\fIFILE\fP must not exist.
+.TP
+.BR " " " " "--from-dump \fIFILE\fP"
+Read the DMI data from a binary file previously generated using
+\fB--dump-bin\fP.
+.TP
+.BR " " " " "--no-sysfs"
+Do not attempt to read DMI data from sysfs files. This is mainly useful for
+debugging.
+.TP
+.BR " " " " "--oem-string \fIN\fP"
+Only display the value of the \s-1OEM\s0 string number \fIN\fP. The first
+\s-1OEM\s0 string has number \fB1\fP. With special value \fBcount\fP, return the
+number of OEM strings instead.
+.TP
+.BR "-h" ", " "--help"
+Display usage information and exit
+.TP
+.BR "-V" ", " "--version"
+Display the version and exit
+.P
+Options
+.BR --string ,
+.BR --type,
+.BR --dump-bin " and " --oem-string
+determine the output format and are mutually exclusive.
+.P
+Please note in case of
+.B dmidecode
+is run on a system with BIOS that boasts new SMBIOS specification, which
+is not supported by the tool yet, it will print out relevant message in
+addition to requested data on the very top of the output. Thus informs the
+output data is not reliable.
+.\"
+.SH "DMI TYPES"
+The \s-1SMBIOS\s0 specification defines the following \s-1DMI\s0 types:
+.TS
+r l
+__
+r l.
+Type Information
+0 BIOS
+1 System
+2 Baseboard
+3 Chassis
+4 Processor
+5 Memory Controller
+6 Memory Module
+7 Cache
+8 Port Connector
+9 System Slots
+10 On Board Devices
+11 OEM Strings
+12 System Configuration Options
+13 BIOS Language
+14 Group Associations
+15 System Event Log
+16 Physical Memory Array
+17 Memory Device
+18 32-bit Memory Error
+19 Memory Array Mapped Address
+20 Memory Device Mapped Address
+21 Built-in Pointing Device
+22 Portable Battery
+23 System Reset
+24 Hardware Security
+25 System Power Controls
+26 Voltage Probe
+27 Cooling Device
+28 Temperature Probe
+29 Electrical Current Probe
+30 Out-of-band Remote Access
+31 Boot Integrity Services
+32 System Boot
+33 64-bit Memory Error
+34 Management Device
+35 Management Device Component
+36 Management Device Threshold Data
+37 Memory Channel
+38 IPMI Device
+39 Power Supply
+40 Additional Information
+41 Onboard Devices Extended Information
+42 Management Controller Host Interface
+.TE
+
+Additionally, type 126 is used for disabled entries and type 127 is an
+end-of-table marker. Types 128 to 255 are for \s-1OEM\s0-specific data.
+.B dmidecode
+will display these entries by default, but it can only decode them
+when the vendors have contributed documentation or code for them.
+
+Keywords can be used instead of type numbers with \fB--type\fP.
+Each keyword is equivalent to a list of type numbers:
+
+.TS
+l l
+__
+l l.
+Keyword Types
+bios 0, 13
+system 1, 12, 15, 23, 32
+baseboard 2, 10, 41
+chassis 3
+processor 4
+memory 5, 6, 16, 17
+cache 7
+connector 8
+slot 9
+.TE
+
+Keywords are matched case-insensitively. The following command lines are equivalent:
+.IP \(bu "\w'\(bu'u+1n"
+dmidecode --type 0 --type 13
+.IP \(bu
+dmidecode --type 0,13
+.IP \(bu
+dmidecode --type bios
+.IP \(bu
+dmidecode --type BIOS
+.\"
+.SH BINARY DUMP FILE FORMAT
+The binary dump files generated by \fB--dump-bin\fP and read using \fB--from-dump\fP
+are formatted as follows:
+.IP \(bu "\w'\(bu'u+1n"
+The SMBIOS or DMI entry point is located at offset 0x00.
+It is crafted to hard-code the table address at offset 0x20.
+.IP \(bu "\w'\(bu'u+1n"
+The DMI table is located at offset 0x20.
+.\"
+.SH UUID FORMAT
+There is some ambiguity about how to interpret the UUID fields prior to SMBIOS
+specification version 2.6. There was no mention of byte swapping, and RFC 4122
+says that no byte swapping should be applied by default. However, SMBIOS
+specification version 2.6 (and later) explicitly states that the first 3 fields
+of the UUID should be read as little-endian numbers (byte-swapped).
+Furthermore, it implies that the same was already true for older versions of
+the specification, even though it was not mentioned. In practice, many hardware
+vendors were not byte-swapping the UUID. So, in order to preserve
+compatibility, it was decided to interpret the UUID fields according to RFC
+4122 (no byte swapping) when the SMBIOS version is older than 2.6, and to
+interpret the first 3 fields as little-endian (byte-swapped) when the SMBIOS
+version is 2.6 or later. The Linux kernel follows the same logic.
+.\"
+.SH FILES
+.I /dev/mem
+.br
+.I /sys/firmware/dmi/tables/smbios_entry_point
+(Linux only)
+.br
+.I /sys/firmware/dmi/tables/DMI
+(Linux only)
+.\"
+.SH BUGS
+More often than not, information contained in the \s-1DMI\s0 tables is inaccurate,
+incomplete or simply wrong.
+.\"
+.SH AUTHORS
+Alan Cox, Jean Delvare
+.\"
+.SH "SEE ALSO"
+.BR biosdecode (8),
+.BR mem (4),
+.BR ownership (8),
+.BR vpddecode (8)
diff --git a/man/ownership.8 b/man/ownership.8
new file mode 100644
index 0000000..2742300
--- /dev/null
+++ b/man/ownership.8
@@ -0,0 +1,41 @@
+.TH OWNERSHIP 8 "February 2005" "dmidecode"
+.\"
+.SH NAME
+ownership \- Compaq ownership tag retriever
+.\"
+.SH SYNOPSIS
+.B ownership
+.RI [ OPTIONS ]
+.\"
+.SH DESCRIPTION
+.B ownership
+retrieves and prints the "ownership tag" that can be set on Compaq
+computers. Contrary to all other programs of the
+.B dmidecode
+package,
+.B ownership
+doesn't print any version information, nor labels, but only the raw
+ownership tag. This should help its integration in scripts.
+.\"
+.SH OPTIONS
+.TP
+.BR "-d" ", " "--dev-mem \fIFILE\fP"
+Read memory from device \fIFILE\fP (default: \fI/dev/mem\fP)
+.TP
+.BR "-h" ", " "--help"
+Display usage information and exit
+.TP
+.BR "-V" ", " "--version"
+Display the version and exit
+.\"
+.SH FILES
+.I /dev/mem
+.\"
+.SH AUTHOR
+Jean Delvare
+.\"
+.SH "SEE ALSO"
+.BR biosdecode (8),
+.BR dmidecode (8),
+.BR mem (4),
+.BR vpddecode (8)
diff --git a/man/vpddecode.8 b/man/vpddecode.8
new file mode 100644
index 0000000..44901ad
--- /dev/null
+++ b/man/vpddecode.8
@@ -0,0 +1,81 @@
+.TH VPDDECODE 8 "February 2007" "dmidecode"
+.\"
+.SH NAME
+vpddecode \- \s-1VPD\s0 structure decoder
+.\"
+.SH SYNOPSIS
+.B vpddecode
+.RI [ OPTIONS ]
+.\"
+.SH DESCRIPTION
+.B vpddecode
+prints the "vital product data" information that can be found in almost
+all IBM and Lenovo computers. Available items are:
+.IP \(bu "\w'\(bu'u+1n"
+\s-1BIOS\s0 Build \s-1ID\s0
+.IP \(bu
+Box Serial Number
+.IP \(bu
+Motherboard Serial Number
+.IP \(bu
+Machine Type/Model
+.PP
+Some systems have these additional items:
+.IP \(bu "\w'\(bu'u+1n"
+BIOS Release Date
+.IP \(bu
+Default Flash Image File Name
+.PP
+Note that these additional items are not documented by IBM, so this is
+guess work, and as such should not be blindly trusted. Feedback about
+the accuracy of these labels is welcome.
+.\"
+.SH OPTIONS
+.TP
+.BR "-d" ", " "--dev-mem \fIFILE\fP"
+Read memory from device \fIFILE\fP (default: \fI/dev/mem\fP)
+.TP
+.BR "-s" ", " "--string \fIKEYWORD\fP"
+Only display the value of the \s-1VPD\s0 string identified by \fIKEYWORD\fP.
+It must be a keyword from the following list:
+.nh
+.BR bios-build-id ,
+.BR box-serial-number ,
+.BR motherboard-serial-number ,
+.BR machine-type-model ,
+.BR bios-release-date .
+.hy
+Each keyword corresponds to an offset and a length within the \s-1VPD\s0
+record.
+Not all strings may be defined on all \s-1VPD\s0-enabled systems.
+If \fIKEYWORD\fP is not provided or not valid, a list of all valid
+keywords is printed and
+.B vpddecode
+exits with an error.
+This option cannot be used more than once.
+Mutually exclusive with \fB--dump\fP.
+.TP
+.BR "-u" ", " "--dump"
+Do not decode the VPD records, dump their contents as hexadecimal instead.
+Note that this is still a text output, no binary data will be thrown upon
+you. ASCII equivalent is displayed when possible. This option is mainly
+useful for debugging.
+Mutually exclusive with \fB--string\fP.
+.TP
+.BR "-h" ", " "--help"
+Display usage information and exit
+.TP
+.BR "-V" ", " "--version"
+Display the version and exit
+.\"
+.SH FILES
+.I /dev/mem
+.\"
+.SH AUTHOR
+Jean Delvare
+.\"
+.SH "SEE ALSO"
+.BR biosdecode (8),
+.BR dmidecode (8),
+.BR mem (4),
+.BR ownership (8)
diff --git a/ownership.c b/ownership.c
new file mode 100644
index 0000000..de44bf4
--- /dev/null
+++ b/ownership.c
@@ -0,0 +1,214 @@
+/*
+ * Compaq Ownership Tag
+ *
+ * Copyright (C) 2003-2005 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open unpatent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * References:
+ * - Compaq "Technical Reference Guide for Compaq Deskpro 4000 and 6000"
+ * First Edition
+ * http://h18000.www1.hp.com/support/techpubs/technical_reference_guides/113a1097.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "version.h"
+#include "config.h"
+#include "types.h"
+#include "util.h"
+
+/* Options are global */
+struct opt
+{
+ const char *devmem;
+ unsigned int flags;
+};
+static struct opt opt;
+
+#define FLAG_VERSION (1 << 0)
+#define FLAG_HELP (1 << 1)
+
+static void ownership(u32 base, const char *pname, const char *devmem)
+{
+ u8 *buf;
+ int i;
+
+ /* read the ownership tag */
+ if ((buf = mem_chunk(base, 0x51, devmem)) == NULL)
+ {
+ perror(pname);
+ return;
+ }
+
+ /* chop the trailing garbage */
+ i = 0x4f;
+ while (i >= 0 && (buf[i] == 0x20 || buf[i] == 0x00))
+ i--;
+ buf[i + 1] = '\0';
+
+ /* filter and print */
+ if (i >= 0)
+ {
+ for (; i >= 0; i--)
+ {
+ if (buf[i] < 32 || (buf[i] >= 127 && buf[i] < 160))
+ buf[i] = '?';
+ }
+ printf("%s\n", (char *)buf);
+ }
+
+ free(buf);
+}
+
+static u32 decode(const u8 *p)
+{
+ int i;
+
+ /* integrity checking (lack of checksum) */
+ for (i = 0; i < p[4]; i++)
+ {
+ if (p[5 + i * 10] != '$'
+ || !(p[6 + i * 10] >= 'A' && p[6 + i * 10] <= 'Z')
+ || !(p[7 + i * 10] >= 'A' && p[7 + i * 10] <= 'Z')
+ || !(p[8 + i * 10] >= 'A' && p[8 + i * 10] <= 'Z'))
+ {
+ printf("\t Abnormal Entry! Please report. [%02x %02x %02x %02x]\n",
+ p[5 + i * 10], p[6 + i * 10],
+ p[7 + i * 10], p[8 + i * 10]);
+ return 0;
+ }
+ }
+
+ /* search for the right entry */
+ for (i = 0; i < p[4]; i++)
+ if (memcmp(p + 5 + i * 10, "$ERB", 4) == 0)
+ return DWORD(p + 9 + i * 10);
+
+ return 0;
+}
+
+/* Return -1 on error, 0 on success */
+static int parse_command_line(int argc, char * const argv[])
+{
+ int option;
+ const char *optstring = "d:hV";
+ struct option longopts[] = {
+ { "dev-mem", required_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((option = getopt_long(argc, argv, optstring, longopts, NULL)) != -1)
+ switch (option)
+ {
+ case 'd':
+ opt.devmem = optarg;
+ break;
+ case 'h':
+ opt.flags |= FLAG_HELP;
+ break;
+ case 'V':
+ opt.flags |= FLAG_VERSION;
+ break;
+ case '?':
+ return -1;
+ }
+
+ return 0;
+}
+
+static void print_help(void)
+{
+ static const char *help =
+ "Usage: ownership [OPTIONS]\n"
+ "Options are:\n"
+ " -d, --dev-mem FILE Read memory from device FILE (default: " DEFAULT_MEM_DEV ")\n"
+ " -h, --help Display this help text and exit\n"
+ " -V, --version Display the version and exit\n";
+
+ printf("%s", help);
+}
+
+int main(int argc, char * const argv[])
+{
+ u8 *buf;
+ off_t fp;
+ int ok = 0;
+
+ if (sizeof(u8) != 1 || sizeof(u32) != 4)
+ {
+ fprintf(stderr, "%s: compiler incompatibility\n", argv[0]);
+ exit(255);
+ }
+
+ /* Set default option values */
+ opt.devmem = DEFAULT_MEM_DEV;
+ opt.flags = 0;
+
+ if (parse_command_line(argc, argv)<0)
+ exit(2);
+
+ if (opt.flags & FLAG_HELP)
+ {
+ print_help();
+ return 0;
+ }
+
+ if (opt.flags & FLAG_VERSION)
+ {
+ printf("%s\n", VERSION);
+ return 0;
+ }
+
+ if ((buf = mem_chunk(0xE0000, 0x20000, opt.devmem)) == NULL)
+ exit(1);
+
+ for (fp = 0; !ok && fp <= 0x1FFF0; fp += 16)
+ {
+ u8 *p = buf + fp;
+
+ if (memcmp((char *)p, "32OS", 4) == 0)
+ {
+ off_t len = p[4] * 10 + 5;
+
+ if (fp + len - 1 <= 0x1FFFF)
+ {
+ u32 base;
+
+ if ((base = decode(p)))
+ {
+ ok = 1;
+ ownership(base, argv[0], opt.devmem);
+ }
+ }
+ }
+ }
+
+ free(buf);
+
+ return 0;
+}
diff --git a/types.h b/types.h
new file mode 100644
index 0000000..51c32d7
--- /dev/null
+++ b/types.h
@@ -0,0 +1,59 @@
+#ifndef TYPES_H
+#define TYPES_H
+
+#include "config.h"
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef signed short i16;
+typedef unsigned int u32;
+
+/*
+ * You may use the following defines to adjust the type definitions
+ * depending on the architecture:
+ * - Define BIGENDIAN on big-endian systems.
+ * - Define ALIGNMENT_WORKAROUND if your system doesn't support
+ * non-aligned memory access. In this case, we use a slower, but safer,
+ * memory access method. This should be done automatically in config.h
+ * for architectures which need it.
+ */
+
+#ifdef BIGENDIAN
+typedef struct {
+ u32 h;
+ u32 l;
+} u64;
+#else
+typedef struct {
+ u32 l;
+ u32 h;
+} u64;
+#endif
+
+#if defined(ALIGNMENT_WORKAROUND) || defined(BIGENDIAN)
+static inline u64 U64(u32 low, u32 high)
+{
+ u64 self;
+
+ self.l = low;
+ self.h = high;
+
+ return self;
+}
+#endif
+
+/*
+ * Per SMBIOS v2.8.0 and later, all structures assume a little-endian
+ * ordering convention.
+ */
+#if defined(ALIGNMENT_WORKAROUND) || defined(BIGENDIAN)
+#define WORD(x) (u16)((x)[0] + ((x)[1] << 8))
+#define DWORD(x) (u32)((x)[0] + ((x)[1] << 8) + ((x)[2] << 16) + ((x)[3] << 24))
+#define QWORD(x) (U64(DWORD(x), DWORD(x + 4)))
+#else /* ALIGNMENT_WORKAROUND || BIGENDIAN */
+#define WORD(x) (u16)(*(const u16 *)(x))
+#define DWORD(x) (u32)(*(const u32 *)(x))
+#define QWORD(x) (*(const u64 *)(x))
+#endif /* ALIGNMENT_WORKAROUND || BIGENDIAN */
+
+#endif
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..2770b1d
--- /dev/null
+++ b/util.c
@@ -0,0 +1,279 @@
+/*
+ * Common "util" functions
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2002-2018 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open unpatent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "config.h"
+
+#ifdef USE_MMAP
+#include <sys/mman.h>
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif /* !MAP_FAILED */
+#endif /* USE MMAP */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "types.h"
+#include "util.h"
+
+static int myread(int fd, u8 *buf, size_t count, const char *prefix)
+{
+ ssize_t r = 1;
+ size_t r2 = 0;
+
+ while (r2 != count && r != 0)
+ {
+ r = read(fd, buf + r2, count - r2);
+ if (r == -1)
+ {
+ if (errno != EINTR)
+ {
+ perror(prefix);
+ return -1;
+ }
+ }
+ else
+ r2 += r;
+ }
+
+ if (r2 != count)
+ {
+ fprintf(stderr, "%s: Unexpected end of file\n", prefix);
+ return -1;
+ }
+
+ return 0;
+}
+
+int checksum(const u8 *buf, size_t len)
+{
+ u8 sum = 0;
+ size_t a;
+
+ for (a = 0; a < len; a++)
+ sum += buf[a];
+ return (sum == 0);
+}
+
+/*
+ * Reads all of file from given offset, up to max_len bytes.
+ * A buffer of at most max_len bytes is allocated by this function, and
+ * needs to be freed by the caller.
+ * This provides a similar usage model to mem_chunk()
+ *
+ * Returns a pointer to the allocated buffer, or NULL on error, and
+ * sets max_len to the length actually read.
+ */
+void *read_file(off_t base, size_t *max_len, const char *filename)
+{
+ struct stat statbuf;
+ int fd;
+ u8 *p;
+
+ /*
+ * Don't print error message on missing file, as we will try to read
+ * files that may or may not be present.
+ */
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ {
+ if (errno != ENOENT)
+ perror(filename);
+ return NULL;
+ }
+
+ /*
+ * Check file size, don't allocate more than can be read.
+ */
+ if (fstat(fd, &statbuf) == 0)
+ {
+ if (base >= statbuf.st_size)
+ {
+ fprintf(stderr, "%s: Can't read data beyond EOF\n",
+ filename);
+ p = NULL;
+ goto out;
+ }
+ if (*max_len > (size_t)statbuf.st_size - base)
+ *max_len = statbuf.st_size - base;
+ }
+
+ if ((p = malloc(*max_len)) == NULL)
+ {
+ perror("malloc");
+ goto out;
+ }
+
+ if (lseek(fd, base, SEEK_SET) == -1)
+ {
+ fprintf(stderr, "%s: ", filename);
+ perror("lseek");
+ goto err_free;
+ }
+
+ if (myread(fd, p, *max_len, filename) == 0)
+ goto out;
+
+err_free:
+ free(p);
+ p = NULL;
+
+out:
+ if (close(fd) == -1)
+ perror(filename);
+
+ return p;
+}
+
+#ifdef USE_MMAP
+static void safe_memcpy(void *dest, const void *src, size_t n)
+{
+#ifdef USE_SLOW_MEMCPY
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ *((u8 *)dest + i) = *((const u8 *)src + i);
+#else
+ memcpy(dest, src, n);
+#endif /* USE_SLOW_MEMCPY */
+}
+#endif /* USE_MMAP */
+
+/*
+ * Copy a physical memory chunk into a memory buffer.
+ * This function allocates memory.
+ */
+void *mem_chunk(off_t base, size_t len, const char *devmem)
+{
+ struct stat statbuf;
+ void *p = NULL;
+ int fd;
+#ifdef USE_MMAP
+ off_t mmoffset;
+ void *mmp;
+#endif
+
+ /*
+ * Safety check: if running as root, devmem is expected to be a
+ * character device file.
+ */
+ if ((fd = open(devmem, O_RDONLY)) == -1
+ || fstat(fd, &statbuf) == -1
+ || (geteuid() == 0 && !S_ISCHR(statbuf.st_mode)))
+ {
+ fprintf(stderr, "Can't read memory from %s\n", devmem);
+ if (fd == -1)
+ return NULL;
+ goto out;
+ }
+
+ if ((p = malloc(len)) == NULL)
+ {
+ perror("malloc");
+ goto out;
+ }
+
+#ifdef USE_MMAP
+ /*
+ * mmap() will fail with SIGBUS if trying to map beyond the end of
+ * the file.
+ */
+ if (S_ISREG(statbuf.st_mode) && base + (off_t)len > statbuf.st_size)
+ {
+ fprintf(stderr, "mmap: Can't map beyond end of file %s\n",
+ devmem);
+ goto err_free;
+ }
+
+#ifdef _SC_PAGESIZE
+ mmoffset = base % sysconf(_SC_PAGESIZE);
+#else
+ mmoffset = base % getpagesize();
+#endif /* _SC_PAGESIZE */
+ /*
+ * Please note that we don't use mmap() for performance reasons here,
+ * but to workaround problems many people encountered when trying
+ * to read from /dev/mem using regular read() calls.
+ */
+ mmp = mmap(NULL, mmoffset + len, PROT_READ, MAP_SHARED, fd, base - mmoffset);
+ if (mmp == MAP_FAILED)
+ goto try_read;
+
+ safe_memcpy(p, (u8 *)mmp + mmoffset, len);
+
+ if (munmap(mmp, mmoffset + len) == -1)
+ {
+ fprintf(stderr, "%s: ", devmem);
+ perror("munmap");
+ }
+
+ goto out;
+
+try_read:
+#endif /* USE_MMAP */
+ if (lseek(fd, base, SEEK_SET) == -1)
+ {
+ fprintf(stderr, "%s: ", devmem);
+ perror("lseek");
+ goto err_free;
+ }
+
+ if (myread(fd, p, len, devmem) == 0)
+ goto out;
+
+err_free:
+ free(p);
+ p = NULL;
+
+out:
+ if (close(fd) == -1)
+ perror(devmem);
+
+ return p;
+}
+
+/* Returns end - start + 1, assuming start < end */
+u64 u64_range(u64 start, u64 end)
+{
+ u64 res;
+
+ res.h = end.h - start.h;
+ res.l = end.l - start.l;
+
+ if (end.l < start.l)
+ res.h--;
+ if (++res.l == 0)
+ res.h++;
+
+ return res;
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..ef24eb9
--- /dev/null
+++ b/util.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2003-2017 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+
+#include "types.h"
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+int checksum(const u8 *buf, size_t len);
+void *read_file(off_t base, size_t *len, const char *filename);
+void *mem_chunk(off_t base, size_t len, const char *devmem);
+u64 u64_range(u64 start, u64 end);
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..179ab30
--- /dev/null
+++ b/version.h
@@ -0,0 +1 @@
+#define VERSION "3.5"
diff --git a/vpddecode.c b/vpddecode.c
new file mode 100644
index 0000000..360524c
--- /dev/null
+++ b/vpddecode.c
@@ -0,0 +1,201 @@
+/*
+ * IBM Vital Product Data decoder
+ *
+ * Copyright (C) 2003-2007 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open unpatent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * References:
+ * - IBM "Using the BIOS Build ID to identify Thinkpad systems"
+ * Revision 2006-01-31
+ * http://www-307.ibm.com/pc/support/site.wss/MIGR-45120.html
+ *
+ * Notes:
+ * - Main part of the code is taken directly from biosdecode, with an
+ * additional command line interface and a few experimental features.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "version.h"
+#include "config.h"
+#include "types.h"
+#include "util.h"
+#include "vpdopt.h"
+
+static void print_entry(const char *name, const u8 *p, size_t len)
+{
+ size_t i;
+
+ if (name != NULL)
+ printf("%s: ", name);
+ for (i = 0; i < len; i++)
+ {
+ /* ASCII filtering */
+ if (p[i] >= 32 && p[i] < 127)
+ printf("%c", p[i]);
+ else if (p[i] != 0)
+ printf(".");
+ }
+ printf("\n");
+}
+
+static void dump(const u8 *p, u8 len)
+{
+ int done, i, min;
+
+ for (done = 0; done < len; done += 16)
+ {
+ printf("%02X:", done);
+ min = (len - done < 16) ? len - done : 16;
+
+ /* As hexadecimal first */
+ for (i = 0; i < min; i++)
+ printf(" %02X", p[done + i]);
+ for (; i < 16; i++) /* Complete line if needed */
+ printf(" ");
+ printf(" ");
+
+ /* And now as text, with ASCII filtering */
+ for (i = 0; i < min; i++)
+ printf("%c", (p[done + i] >= 32 && p[done + i] < 127) ?
+ p[done + i] : '.');
+ printf("\n");
+ }
+}
+
+static int decode(const u8 *p)
+{
+ if (p[5] < 0x30)
+ return 0;
+
+ /* XSeries have longer records, exact length seems to vary. */
+ if (!(p[5] >= 0x45 && checksum(p, p[5]))
+ /* Some Netvista seem to work with this. */
+ && !(checksum(p, 0x30))
+ /* The Thinkpad/Thinkcentre checksum does *not* include the first
+ 13 bytes. */
+ && !(checksum(p + 0x0D, 0x30 - 0x0D)))
+ {
+ /* A few systems have a bad checksum (xSeries 325, 330, 335
+ and 345 with early BIOS) but the record is otherwise
+ valid. */
+ if (!(opt.flags & FLAG_QUIET))
+ printf("# Bad checksum!\n");
+ }
+
+ if (opt.string != NULL)
+ {
+ if (opt.string->offset + opt.string->len < p[5])
+ print_entry(NULL, p + opt.string->offset,
+ opt.string->len);
+ return 1;
+ }
+
+ print_entry("BIOS Build ID", p + 0x0D, 9);
+ print_entry("Box Serial Number", p + 0x16, 7);
+ print_entry("Motherboard Serial Number", p + 0x1D, 11);
+ print_entry("Machine Type/Model", p + 0x28, 7);
+
+ if (p[5] < 0x44)
+ return 1;
+
+ print_entry("BIOS Release Date", p + 0x30, 8);
+ print_entry("Default Flash Image File Name", p + 0x38, 12);
+
+ if (p[5] >= 0x46 && p[0x44] != 0x00)
+ {
+ printf("%s: %u\n", "BIOS Revision", p[0x44]);
+ }
+
+ return 1;
+}
+
+int main(int argc, char * const argv[])
+{
+ u8 *buf;
+ int found = 0;
+ unsigned int fp;
+
+ if (sizeof(u8) != 1)
+ {
+ fprintf(stderr, "%s: compiler incompatibility\n", argv[0]);
+ exit(255);
+ }
+
+ /* Set default option values */
+ opt.devmem = DEFAULT_MEM_DEV;
+ opt.flags = 0;
+
+ if (parse_command_line(argc, argv)<0)
+ exit(2);
+
+ if (opt.flags & FLAG_HELP)
+ {
+ print_help();
+ return 0;
+ }
+
+ if (opt.flags & FLAG_VERSION)
+ {
+ printf("%s\n", VERSION);
+ return 0;
+ }
+
+ if (!(opt.flags & FLAG_QUIET))
+ printf("# vpddecode %s\n", VERSION);
+
+ if ((buf = mem_chunk(0xF0000, 0x10000, opt.devmem)) == NULL)
+ exit(1);
+
+ for (fp = 0; fp <= 0xFFF0; fp += 4)
+ {
+ u8 *p = buf + fp;
+
+ if (memcmp((char *)p, "\252\125VPD", 5) == 0
+ && fp + p[5] - 1 <= 0xFFFF)
+ {
+ if (fp % 16 && !(opt.flags & FLAG_QUIET))
+ printf("# Unaligned address (%#x)\n",
+ 0xf0000 + fp);
+ if (opt.flags & FLAG_DUMP)
+ {
+ dump(p, p[5]);
+ found++;
+ }
+ else
+ {
+ if (decode(p))
+ found++;
+ }
+ }
+ }
+
+ free(buf);
+
+ if (!found && !(opt.flags & FLAG_QUIET))
+ printf("# No VPD structure found, sorry.\n");
+
+ return 0;
+}
diff --git a/vpdopt.c b/vpdopt.c
new file mode 100644
index 0000000..8f411cc
--- /dev/null
+++ b/vpdopt.c
@@ -0,0 +1,157 @@
+/*
+ * Command line handling of vpddecode
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2005-2007 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "config.h"
+#include "util.h"
+#include "vpdopt.h"
+
+
+/* Options are global */
+struct opt opt;
+
+
+/*
+ * Handling of option --string
+ */
+
+/* This lookup table could admittedly be reworked for improved performance.
+ Due to the low count of items in there at the moment, it did not seem
+ worth the additional code complexity though. */
+static const struct string_keyword opt_string_keyword[] = {
+ { "bios-build-id", 0x0D, 9 },
+ { "box-serial-number", 0x16, 7 },
+ { "motherboard-serial-number", 0x1D, 11 },
+ { "machine-type-model", 0x28, 7 },
+ { "bios-release-date", 0x30, 8 },
+};
+
+static void print_opt_string_list(void)
+{
+ unsigned int i;
+
+ fprintf(stderr, "Valid string keywords are:\n");
+ for (i = 0; i < ARRAY_SIZE(opt_string_keyword); i++)
+ {
+ fprintf(stderr, " %s\n", opt_string_keyword[i].keyword);
+ }
+}
+
+static int parse_opt_string(const char *arg)
+{
+ unsigned int i;
+
+ if (opt.string)
+ {
+ fprintf(stderr, "Only one string can be specified\n");
+ return -1;
+ }
+
+ for (i = 0; i<ARRAY_SIZE(opt_string_keyword); i++)
+ {
+ if (!strcasecmp(arg, opt_string_keyword[i].keyword))
+ {
+ opt.string = &opt_string_keyword[i];
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "Invalid string keyword: %s\n", arg);
+ print_opt_string_list();
+ return -1;
+}
+
+
+/*
+ * Command line options handling
+ */
+
+/* Return -1 on error, 0 on success */
+int parse_command_line(int argc, char * const argv[])
+{
+ int option;
+ const char *optstring = "d:hs:uV";
+ struct option longopts[] = {
+ { "dev-mem", required_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "string", required_argument, NULL, 's' },
+ { "dump", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((option = getopt_long(argc, argv, optstring, longopts, NULL)) != -1)
+ switch (option)
+ {
+ case 'd':
+ opt.devmem = optarg;
+ break;
+ case 'h':
+ opt.flags |= FLAG_HELP;
+ break;
+ case 's':
+ if (parse_opt_string(optarg) < 0)
+ return -1;
+ opt.flags |= FLAG_QUIET;
+ break;
+ case 'u':
+ opt.flags |= FLAG_DUMP;
+ break;
+ case 'V':
+ opt.flags |= FLAG_VERSION;
+ break;
+ case '?':
+ switch (optopt)
+ {
+ case 's':
+ fprintf(stderr, "String keyword expected\n");
+ print_opt_string_list();
+ break;
+ }
+ return -1;
+ }
+
+ if ((opt.flags & FLAG_DUMP) && opt.string != NULL)
+ {
+ fprintf(stderr, "Options --string and --dump are mutually exclusive\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void print_help(void)
+{
+ static const char *help =
+ "Usage: vpddecode [OPTIONS]\n"
+ "Options are:\n"
+ " -d, --dev-mem FILE Read memory from device FILE (default: " DEFAULT_MEM_DEV ")\n"
+ " -h, --help Display this help text and exit\n"
+ " -s, --string KEYWORD Only display the value of the given VPD string\n"
+ " -u, --dump Do not decode the VPD records\n"
+ " -V, --version Display the version and exit\n";
+
+ printf("%s", help);
+}
diff --git a/vpdopt.h b/vpdopt.h
new file mode 100644
index 0000000..beefcbd
--- /dev/null
+++ b/vpdopt.h
@@ -0,0 +1,45 @@
+/*
+ * Command line handling of vpddecode
+ * This file is part of the dmidecode project.
+ *
+ * Copyright (C) 2005-2006 Jean Delvare <jdelvare@suse.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+
+struct string_keyword
+{
+ const char *keyword;
+ off_t offset;
+ size_t len;
+};
+
+struct opt
+{
+ const char *devmem;
+ unsigned int flags;
+ const struct string_keyword *string;
+};
+extern struct opt opt;
+
+#define FLAG_VERSION (1 << 0)
+#define FLAG_HELP (1 << 1)
+#define FLAG_DUMP (1 << 2)
+#define FLAG_QUIET (1 << 3)
+
+int parse_command_line(int argc, char * const argv[]);
+void print_help(void);