c95d82c450db3e471aa3f36840935e63e97a991dvboxsync/* $Id$ */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync/** @file
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * Tool for modifying a BIOS image to write the BIOS checksum.
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync/*
c58f1213e628a545081c70e26c6b67a841cff880vboxsync * Copyright (C) 2006-2010 Oracle Corporation
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync *
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * available from http://www.virtualbox.org. This file is free software;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * General Public License (GPL) as published by the Free Software
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync#include <stdlib.h>
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync#include <stdio.h>
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync#include <string.h>
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync#include <stdarg.h>
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync#include <errno.h>
c6b071a92d5edc09f56392988e3c628ede9c8e9evboxsync#ifndef RT_OS_WINDOWS
c6b071a92d5edc09f56392988e3c628ede9c8e9evboxsync# include <unistd.h> /* unlink */
c6b071a92d5edc09f56392988e3c628ede9c8e9evboxsync#endif
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsynctypedef unsigned char uint8_t;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsyncstatic uint8_t abBios[64*1024];
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsyncstatic FILE *g_pIn = NULL;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsyncstatic FILE *g_pOut = NULL;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsyncstatic const char *g_pszOutFile = NULL;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsyncstatic const char *g_argv0;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync/**
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync * Find where the filename starts in the given path.
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync */
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsyncstatic const char *name(const char *pszPath)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync{
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync const char *psz = strrchr(pszPath, '/');
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync#if defined(_MSC_VER) || defined(__OS2__)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync const char *psz2 = strrchr(pszPath, '\\');
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync if (!psz2)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync psz2 = strrchr(pszPath, ':');
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync if (psz2 && (!psz || psz2 > psz))
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync psz = psz2;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync#endif
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return psz ? psz + 1 : pszPath;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync}
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync/**
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync * Report an error.
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync */
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsyncstatic int fatal(const char *pszFormat, ...)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync{
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync va_list va;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync fprintf(stderr, "%s: ", name(g_argv0));
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync va_start(va, pszFormat);
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync vfprintf(stderr, pszFormat, va);
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync va_end(va);
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync /* clean up */
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync if (g_pIn)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync fclose(g_pIn);
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync if (g_pOut)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync fclose(g_pOut);
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync if (g_pszOutFile)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync unlink(g_pszOutFile);
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return 1;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync}
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync/**
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * Calculate the checksum.
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync */
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsyncstatic uint8_t calculateChecksum(uint8_t *pb, size_t cb, size_t iChecksum)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync{
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync uint8_t u8Sum = 0;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync size_t i;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync for (i = 0; i < cb; i++)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (i != iChecksum)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync u8Sum += pb[i];
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync return -u8Sum;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync}
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync/**
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync * Find a header in the binary.
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync *
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * @param pb Where to search for the signature
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * @param cb Size of the search area
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * @param pbHeader Pointer to the start of the signature
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * @returns 0 if signature was not found, 1 if found or
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * 2 if more than one signature was found */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsyncstatic int searchHeader(uint8_t *pb, size_t cb, const char *pszHeader, uint8_t **pbHeader)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync{
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync int fFound = 0;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync unsigned int i;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync size_t cbSignature = strlen(pszHeader);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync for (i = 0; i < cb; i += 16)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (!memcmp(pb + i, pszHeader, cbSignature))
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync {
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (fFound++)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync return 2;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync *pbHeader = pb + i;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync }
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync return fFound;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync}
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsyncint main(int argc, char **argv)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync{
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync FILE *pIn, *pOut;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync size_t cbIn, cbOut;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync int fAdapterBios = 0;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync g_argv0 = argv[0];
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (argc != 3)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("Input file name and output file name required.\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync pIn = g_pIn = fopen(argv[1], "rb");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (!pIn)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("Error opening '%s' for reading (%s).\n", argv[1], strerror(errno));
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync pOut = g_pOut = fopen(argv[2], "wb");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (!pOut)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("Error opening '%s' for writing (%s).\n", argv[2], strerror(errno));
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync g_pszOutFile = argv[2];
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync /* safety precaution (aka. complete paranoia :-) */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync memset(abBios, 0, sizeof(abBios));
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync cbIn = fread(abBios, 1, sizeof(abBios), pIn);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (ferror(pIn))
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("Error reading from '%s' (%s).\n", argv[1], strerror(errno));
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync g_pIn = NULL;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync fclose(pIn);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync fAdapterBios = abBios[0] == 0x55 && abBios[1] == 0xaa;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* align size to page size */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if ((cbIn % 4096) != 0)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync cbIn = (cbIn + 4095) & ~4095;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (!fAdapterBios && cbIn != 64*1024)
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("Size of system BIOS is not 64KB!\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (fAdapterBios)
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync {
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* adapter BIOS */
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* set the length indicator */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync abBios[2] = (uint8_t)(cbIn / 512);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync }
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync else
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync {
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* system BIOS */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync size_t cbChecksum;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync uint8_t u8Checksum;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync uint8_t *pbHeader;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* Set the BIOS32 header checksum. */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync switch (searchHeader(abBios, cbIn, "_32_", &pbHeader))
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync {
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 0:
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("No BIOS32 header not found!\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 2:
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("More than one BIOS32 header found!\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 1:
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync cbChecksum = (size_t)pbHeader[9] * 16;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync u8Checksum = calculateChecksum(pbHeader, cbChecksum, 10);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync pbHeader[10] = u8Checksum;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync break;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync }
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* Set the PIR header checksum according to PCI IRQ Routing table
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * specification version 1.0, Microsoft Corporation, 1996 */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync switch (searchHeader(abBios, cbIn, "$PIR", &pbHeader))
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync {
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 0:
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("No PCI IRQ routing table found!\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 2:
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("More than one PCI IRQ routing table found!\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 1:
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync cbChecksum = (size_t)pbHeader[6] + (size_t)pbHeader[7] * 256;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync u8Checksum = calculateChecksum(pbHeader, cbChecksum, 31);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync pbHeader[31] = u8Checksum;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync break;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync }
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* Set the SMBIOS header checksum according to System Management BIOS
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync * Reference Specification Version 2.5, DSP0134. */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync switch (searchHeader(abBios, cbIn, "_SM_", &pbHeader))
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync {
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 0:
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("No SMBIOS header found!\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 2:
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("More than one SMBIOS header found!\n");
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync case 1:
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* at first fix the DMI header starting at SMBIOS header offset 16 */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync u8Checksum = calculateChecksum(pbHeader+16, 15, 5);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync pbHeader[21] = u8Checksum;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* now fix the checksum of the whole SMBIOS header */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync cbChecksum = (size_t)pbHeader[5];
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync u8Checksum = calculateChecksum(pbHeader, cbChecksum, 4);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync pbHeader[4] = u8Checksum;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync break;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync }
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync }
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync /* set the BIOS checksum */
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync abBios[cbIn-1] = calculateChecksum(abBios, cbIn, cbIn - 1);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync cbOut = fwrite(abBios, 1, cbIn, pOut);
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync if (ferror(pOut))
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("Error writing to '%s' (%s).\n", g_pszOutFile, strerror(errno));
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync g_pOut = NULL;
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync if (fclose(pOut))
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync return fatal("Error closing '%s' (%s).\n", g_pszOutFile, strerror(errno));
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync return 0;
c95d82c450db3e471aa3f36840935e63e97a991dvboxsync}
fc2e0a5b6dfa679b43885b8c5829f583b3b76909vboxsync