summaryrefslogtreecommitdiffstats
path: root/src/bldprogs/biossums.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bldprogs/biossums.c')
-rw-r--r--src/bldprogs/biossums.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/bldprogs/biossums.c b/src/bldprogs/biossums.c
new file mode 100644
index 00000000..871c55ae
--- /dev/null
+++ b/src/bldprogs/biossums.c
@@ -0,0 +1,255 @@
+/* $Id: biossums.c $ */
+/** @file
+ * Tool for modifying a BIOS image to write the BIOS checksum.
+ */
+
+/*
+ * Copyright (C) 2006-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * 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, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#ifndef RT_OS_WINDOWS
+# include <unistd.h> /* unlink */
+#endif
+
+typedef unsigned char uint8_t;
+
+static uint8_t abBios[64*1024];
+static FILE *g_pIn = NULL;
+static FILE *g_pOut = NULL;
+static const char *g_pszOutFile = NULL;
+static const char *g_argv0;
+
+/**
+ * Find where the filename starts in the given path.
+ */
+static const char *name(const char *pszPath)
+{
+ const char *psz = strrchr(pszPath, '/');
+#if defined(_MSC_VER) || defined(__OS2__)
+ const char *psz2 = strrchr(pszPath, '\\');
+ if (!psz2)
+ psz2 = strrchr(pszPath, ':');
+ if (psz2 && (!psz || psz2 > psz))
+ psz = psz2;
+#endif
+ return psz ? psz + 1 : pszPath;
+}
+
+/**
+ * Report an error.
+ */
+static int fatal(const char *pszFormat, ...)
+{
+ va_list va;
+
+ fprintf(stderr, "%s: ", name(g_argv0));
+
+ va_start(va, pszFormat);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+
+ /* clean up */
+ if (g_pIn)
+ fclose(g_pIn);
+ if (g_pOut)
+ fclose(g_pOut);
+ if (g_pszOutFile)
+ unlink(g_pszOutFile);
+
+ return 1;
+}
+
+/**
+ * Calculate the checksum.
+ */
+static uint8_t calculateChecksum(uint8_t *pb, size_t cb, size_t iChecksum)
+{
+ uint8_t u8Sum = 0;
+ size_t i;
+
+ for (i = 0; i < cb; i++)
+ if (i != iChecksum)
+ u8Sum += pb[i];
+
+ return -u8Sum;
+}
+
+/**
+ * Find a header in the binary.
+ *
+ * @param pb Where to search for the signature
+ * @param cb Size of the search area
+ * @param pbHeader Pointer to the start of the signature
+ * @returns 0 if signature was not found, 1 if found or
+ * 2 if more than one signature was found */
+static int searchHeader(uint8_t *pb, size_t cb, const char *pszHeader, uint8_t **pbHeader)
+{
+ int fFound = 0;
+ unsigned int i;
+ size_t cbSignature = strlen(pszHeader);
+
+ for (i = 0; i < cb; i += 16)
+ if (!memcmp(pb + i, pszHeader, cbSignature))
+ {
+ if (fFound++)
+ return 2;
+ *pbHeader = pb + i;
+ }
+
+ return fFound;
+}
+
+int main(int argc, char **argv)
+{
+ FILE *pIn, *pOut;
+ size_t cbIn, cbOut;
+ int fAdapterBios = 0;
+
+ g_argv0 = argv[0];
+
+ if (argc != 3)
+ return fatal("Input file name and output file name required.\n");
+
+ pIn = g_pIn = fopen(argv[1], "rb");
+ if (!pIn)
+ return fatal("Error opening '%s' for reading (%s).\n", argv[1], strerror(errno));
+
+ pOut = g_pOut = fopen(argv[2], "wb");
+ if (!pOut)
+ return fatal("Error opening '%s' for writing (%s).\n", argv[2], strerror(errno));
+ g_pszOutFile = argv[2];
+
+ /* safety precaution (aka. complete paranoia :-) */
+ memset(abBios, 0, sizeof(abBios));
+
+ cbIn = fread(abBios, 1, sizeof(abBios), pIn);
+ if (ferror(pIn))
+ return fatal("Error reading from '%s' (%s).\n", argv[1], strerror(errno));
+ g_pIn = NULL;
+ fclose(pIn);
+
+ fAdapterBios = abBios[0] == 0x55 && abBios[1] == 0xaa;
+
+ /* align size to page size */
+ if ((cbIn % 4096) != 0)
+ cbIn = (cbIn + 4095) & ~4095;
+
+ if (!fAdapterBios && cbIn != 64*1024)
+ return fatal("Size of system BIOS is not 64KB!\n");
+
+ if (fAdapterBios)
+ {
+ /* adapter BIOS */
+
+ /* set the length indicator */
+ abBios[2] = (uint8_t)(cbIn / 512);
+ }
+ else
+ {
+ /* system BIOS */
+ size_t cbChecksum;
+ uint8_t u8Checksum;
+ uint8_t *pbHeader;
+
+ /* Set the BIOS32 header checksum. */
+ switch (searchHeader(abBios, cbIn, "_32_", &pbHeader))
+ {
+ case 0:
+ return fatal("No BIOS32 header not found!\n");
+ case 2:
+ return fatal("More than one BIOS32 header found!\n");
+ case 1:
+ cbChecksum = (size_t)pbHeader[9] * 16;
+ u8Checksum = calculateChecksum(pbHeader, cbChecksum, 10);
+ pbHeader[10] = u8Checksum;
+ break;
+ }
+
+ /* Set the PIR header checksum according to PCI IRQ Routing table
+ * specification version 1.0, Microsoft Corporation, 1996 */
+ switch (searchHeader(abBios, cbIn, "$PIR", &pbHeader))
+ {
+ case 0:
+ return fatal("No PCI IRQ routing table found!\n");
+ case 2:
+ return fatal("More than one PCI IRQ routing table found!\n");
+ case 1:
+ cbChecksum = (size_t)pbHeader[6] + (size_t)pbHeader[7] * 256;
+ u8Checksum = calculateChecksum(pbHeader, cbChecksum, 31);
+ pbHeader[31] = u8Checksum;
+ break;
+ }
+
+ /* Set the SMBIOS header checksum according to System Management BIOS
+ * Reference Specification Version 2.5, DSP0134. */
+ switch (searchHeader(abBios, cbIn, "_SM_", &pbHeader))
+ {
+ case 0:
+ return fatal("No SMBIOS header found!\n");
+ case 2:
+ return fatal("More than one SMBIOS header found!\n");
+ case 1:
+ /* at first fix the DMI header starting at SMBIOS header offset 16 */
+ u8Checksum = calculateChecksum(pbHeader+16, 15, 5);
+ pbHeader[21] = u8Checksum;
+
+ /* now fix the checksum of the whole SMBIOS header */
+ cbChecksum = (size_t)pbHeader[5];
+ u8Checksum = calculateChecksum(pbHeader, cbChecksum, 4);
+ pbHeader[4] = u8Checksum;
+ break;
+ }
+
+ /* If there is a VPD table, adjust its checksum. */
+ switch (searchHeader(abBios, cbIn, "\xAA\x55VPD", &pbHeader))
+ {
+ case 0:
+ break; /* VPD is optional */
+ case 2:
+ return fatal("More than one VPD header found!\n");
+ case 1:
+ cbChecksum = (size_t)pbHeader[5];
+ if (cbChecksum < 0x30)
+ return fatal("VPD size too small!\n");
+ u8Checksum = calculateChecksum(pbHeader, cbChecksum, cbChecksum - 1);
+ pbHeader[cbChecksum - 1] = u8Checksum;
+ break;
+ }
+ }
+
+ /* set the BIOS checksum */
+ abBios[cbIn-1] = calculateChecksum(abBios, cbIn, cbIn - 1);
+
+ cbOut = fwrite(abBios, 1, cbIn, pOut);
+ if (ferror(pOut))
+ return fatal("Error writing to '%s' (%s).\n", g_pszOutFile, strerror(errno));
+ g_pOut = NULL;
+ if (fclose(pOut))
+ return fatal("Error closing '%s' (%s).\n", g_pszOutFile, strerror(errno));
+
+ return 0;
+}
+