summaryrefslogtreecommitdiffstats
path: root/vpddecode.c
diff options
context:
space:
mode:
Diffstat (limited to 'vpddecode.c')
-rw-r--r--vpddecode.c201
1 files changed, 201 insertions, 0 deletions
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;
+}