diff options
-rw-r--r-- | AUTHORS | 98 | ||||
-rw-r--r-- | LICENSE | 339 | ||||
-rw-r--r-- | Makefile | 148 | ||||
-rw-r--r-- | NEWS | 179 | ||||
-rw-r--r-- | README | 115 | ||||
-rw-r--r-- | biosdecode.c | 723 | ||||
-rw-r--r-- | config.h | 34 | ||||
-rw-r--r-- | dmidecode.c | 6102 | ||||
-rw-r--r-- | dmidecode.h | 53 | ||||
-rw-r--r-- | dmioem.c | 916 | ||||
-rw-r--r-- | dmioem.h | 25 | ||||
-rw-r--r-- | dmiopt.c | 385 | ||||
-rw-r--r-- | dmiopt.h | 51 | ||||
-rw-r--r-- | dmioutput.c | 137 | ||||
-rw-r--r-- | dmioutput.h | 34 | ||||
-rw-r--r-- | man/biosdecode.8 | 92 | ||||
-rw-r--r-- | man/dmidecode.8 | 325 | ||||
-rw-r--r-- | man/ownership.8 | 41 | ||||
-rw-r--r-- | man/vpddecode.8 | 81 | ||||
-rw-r--r-- | ownership.c | 214 | ||||
-rw-r--r-- | types.h | 59 | ||||
-rw-r--r-- | util.c | 316 | ||||
-rw-r--r-- | util.h | 31 | ||||
-rw-r--r-- | version.h | 1 | ||||
-rw-r--r-- | vpddecode.c | 201 | ||||
-rw-r--r-- | vpdopt.c | 157 | ||||
-rw-r--r-- | vpdopt.h | 45 |
27 files changed, 10902 insertions, 0 deletions
@@ -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 @@ -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 @@ -0,0 +1,179 @@ +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 ans 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. @@ -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..9aeff91 --- /dev/null +++ b/dmidecode.c @@ -0,0 +1,6102 @@ +/* + * 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 <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; + else if (type == 0x01 || type == 0x02) + { + const char *version = dmi_string(h, data[0x10]); + /* + * Some X86-class CPU have family "Other" or "Unknown". In this case, + * we use the version string to determine if they are known to + * support the CPUID instruction. + */ + if (strncmp(version, "Pentium III MMX", 15) == 0 + || strncmp(version, "Intel(R) Core(TM)2", 18) == 0 + || strncmp(version, "Intel(R) Pentium(R)", 19) == 0 + || strcmp(version, "Genuine Intel(R) CPU U1400") == 0) + return cpuid_x86_intel; + else if (strncmp(version, "AMD Athlon(TM)", 14) == 0 + || strncmp(version, "AMD Opteron(tm)", 15) == 0 + || strncmp(version, "Dual-Core AMD Opteron(tm)", 25) == 0) + 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) + 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) + 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[key - 1] != 0xFF && data[key] != 0xFF) + printf("%u.%u\n", data[key - 1], data[key]); + break; + case 0x017: /* -s firmware-revision */ + if (data[key - 1] != 0xFF && data[key] != 0xFF) + printf("%u.%u\n", data[key - 1], data[key]); + 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 void dmi_table_dump(const u8 *buf, u32 len) +{ + if (!(opt.flags & FLAG_QUIET)) + pr_comment("Writing %d bytes to %s.", len, opt.dumpfile); + write_dump(32, len, buf, opt.dumpfile, 0); +} + +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) + 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)); + } +} + +static void dmi_table(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; + } + + if (opt.flags & FLAG_DUMP_BIN) + dmi_table_dump(buf, len); + else + dmi_table_decode(buf, len, num, ver >> 8, flags); + + free(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; + u64 offset; + + /* 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 (!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; + } + + dmi_table(((off_t)offset.h << 32) | offset.l, + DWORD(buf + 0x0C), 0, ver, devmem, flags | FLAG_STOP_AT_EOT); + + if (opt.flags & FLAG_DUMP_BIN) + { + u8 crafted[32]; + + memcpy(crafted, buf, 32); + overwrite_smbios3_address(crafted); + + if (!(opt.flags & FLAG_QUIET)) + pr_comment("Writing %d bytes to %s.", crafted[0x06], + opt.dumpfile); + write_dump(0, crafted[0x06], crafted, opt.dumpfile, 1); + } + + return 1; +} + +static int smbios_decode(u8 *buf, const char *devmem, u32 flags) +{ + u16 ver; + + /* 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; + } + + if (!checksum(buf, buf[0x05]) + || memcmp(buf + 0x10, "_DMI_", 5) != 0 + || !checksum(buf + 0x10, 0x0F)) + return 0; + + ver = (buf[0x06] << 8) + buf[0x07]; + /* 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; + } + if (!(opt.flags & FLAG_QUIET)) + pr_info("SMBIOS %u.%u present.", + ver >> 8, ver & 0xFF); + + dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C), + ver << 8, devmem, flags); + + if (opt.flags & FLAG_DUMP_BIN) + { + u8 crafted[32]; + + memcpy(crafted, buf, 32); + overwrite_dmi_address(crafted + 0x10); + + if (!(opt.flags & FLAG_QUIET)) + pr_comment("Writing %d bytes to %s.", crafted[0x05], + opt.dumpfile); + write_dump(0, crafted[0x05], crafted, opt.dumpfile, 1); + } + + return 1; +} + +static int legacy_decode(u8 *buf, const char *devmem, u32 flags) +{ + if (!checksum(buf, 0x0F)) + return 0; + + if (!(opt.flags & FLAG_QUIET)) + pr_info("Legacy DMI %u.%u present.", + buf[0x0E] >> 4, buf[0x0E] & 0x0F); + + dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C), + ((buf[0x0E] & 0xF0) << 12) + ((buf[0x0E] & 0x0F) << 8), + devmem, flags); + + if (opt.flags & FLAG_DUMP_BIN) + { + u8 crafted[16]; + + memcpy(crafted, buf, 16); + overwrite_dmi_address(crafted); + + if (!(opt.flags & FLAG_QUIET)) + pr_comment("Writing %d bytes to %s.", 0x0F, + opt.dumpfile); + write_dump(0, 0x0F, crafted, opt.dumpfile, 1); + } + + 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..c26fff9 --- /dev/null +++ b/dmioem.c @@ -0,0 +1,916 @@ +/* + * 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_attr("Attributes Defined/Set", NULL); + for (i = 0; i < ARRAY_SIZE(attributes); i++) + { + if (!(defined.l & (1UL << i))) + continue; + pr_subattr(attributes[i], "%s", set.l & (1UL << i) ? "Yes" : "No"); + } +} + +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 Virutal 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_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", + "HP NAND Controller (USX 2065 or other)", + "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 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 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 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; + + 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..d08288f --- /dev/null +++ b/dmiopt.c @@ -0,0 +1,385 @@ +/* + * 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' }, + { "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 '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" + " -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..2374637 --- /dev/null +++ b/dmiopt.h @@ -0,0 +1,51 @@ +/* + * 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) + +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..ed066b3 --- /dev/null +++ b/man/dmidecode.8 @@ -0,0 +1,325 @@ +.TH DMIDECODE 8 "January 2019" "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 "-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. +.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; +} @@ -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 @@ -0,0 +1,316 @@ +/* + * 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; +} + +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 +} + +/* + * 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) +{ + void *p; + int fd; +#ifdef USE_MMAP + struct stat statbuf; + off_t mmoffset; + void *mmp; +#endif + + if ((fd = open(devmem, O_RDONLY)) == -1) + { + perror(devmem); + return NULL; + } + + if ((p = malloc(len)) == NULL) + { + perror("malloc"); + goto out; + } + +#ifdef USE_MMAP + if (fstat(fd, &statbuf) == -1) + { + fprintf(stderr, "%s: ", devmem); + perror("stat"); + goto err_free; + } + + /* + * 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; +} + +int write_dump(size_t base, size_t len, const void *data, const char *dumpfile, int add) +{ + FILE *f; + + f = fopen(dumpfile, add ? "r+b" : "wb"); + if (!f) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fopen"); + return -1; + } + + if (fseek(f, base, SEEK_SET) != 0) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fseek"); + goto err_close; + } + + if (fwrite(data, len, 1, f) != 1) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fwrite"); + goto err_close; + } + + if (fclose(f)) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fclose"); + return -1; + } + + return 0; + +err_close: + fclose(f); + return -1; +} + +/* 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; +} @@ -0,0 +1,31 @@ +/* + * 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); +int write_dump(size_t base, size_t len, const void *data, const char *dumpfile, int add); +u64 u64_range(u64 start, u64 end); diff --git a/version.h b/version.h new file mode 100644 index 0000000..3f8431b --- /dev/null +++ b/version.h @@ -0,0 +1 @@ +#define VERSION "3.4" 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); |