/* biossums.c --- written by Eike W. for the Bochs BIOS */ /* adapted for the LGPL'd VGABIOS by vruppert */ /* This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdlib.h> #include <stdio.h> #include <string.h> typedef unsigned char byte; void check( int value, char* message ); #define MAX_BIOS_DATA 0x10000 long chksum_bios_get_offset( byte* data, long offset ); byte chksum_bios_calc_value( byte* data, long offset ); byte chksum_bios_get_value( byte* data, long offset ); void chksum_bios_set_value( byte* data, long offset, byte value ); #define PMID_LEN 20 #define PMID_CHKSUM 19 long chksum_pmid_get_offset( byte* data, long offset ); byte chksum_pmid_calc_value( byte* data, long offset ); byte chksum_pmid_get_value( byte* data, long offset ); void chksum_pmid_set_value( byte* data, long offset, byte value ); #define PCIR_LEN 24 long chksum_pcir_get_offset( byte* data, long offset ); byte bios_data[MAX_BIOS_DATA]; long bios_len; int main(int argc, char* argv[]) { FILE* stream; long offset, tmp_offset, pcir_offset; byte bios_len_byte, cur_val = 0, new_val = 0; int hits, modified; if (argc != 2) { printf( "Error. Need a file-name as an argument.\n" ); exit( EXIT_FAILURE ); } if ((stream = fopen(argv[1], "rb")) == NULL) { printf("Error opening %s for reading.\n", argv[1]); exit(EXIT_FAILURE); } memset(bios_data, 0, MAX_BIOS_DATA); bios_len = fread(bios_data, 1, MAX_BIOS_DATA, stream); if (bios_len > MAX_BIOS_DATA) { printf("Error reading max. 65536 Bytes from %s.\n", argv[1]); fclose(stream); exit(EXIT_FAILURE); } fclose(stream); modified = 0; if (bios_len < 0x8000) { bios_len = 0x8000; modified = 1; } else if ((bios_len & 0x1FF) != 0) { bios_len = (bios_len + 0x200) & ~0x1FF; modified = 1; } bios_len_byte = (byte)(bios_len / 512); if (bios_len_byte != bios_data[2]) { if (modified == 0) { bios_len += 0x200; } bios_data[2] = (byte)(bios_len / 512); modified = 1; } hits = 0; offset = 0L; while( (tmp_offset = chksum_pmid_get_offset( bios_data, offset )) != -1L ) { offset = tmp_offset; cur_val = chksum_pmid_get_value( bios_data, offset ); new_val = chksum_pmid_calc_value( bios_data, offset ); printf( "\nPMID entry at: 0x%4lX\n", offset ); printf( "Current checksum: 0x%02X\n", cur_val ); printf( "Calculated checksum: 0x%02X ", new_val ); hits++; } if ((hits == 1) && (cur_val != new_val)) { printf("Setting checksum."); chksum_pmid_set_value( bios_data, offset, new_val ); if (modified == 0) { bios_len += 0x200; bios_data[2]++; } modified = 1; } if (hits >= 2) { printf( "Multiple PMID entries! No checksum set." ); } if (hits) { printf("\n"); } offset = 0L; pcir_offset = chksum_pcir_get_offset( bios_data, offset ); if (pcir_offset != -1L) { if (bios_data[pcir_offset + 16] != bios_data[2]) { bios_data[pcir_offset + 16] = bios_data[2]; if (modified == 0) { bios_len += 0x200; bios_data[2]++; bios_data[pcir_offset + 16]++; } modified = 1; } } offset = 0L; do { offset = chksum_bios_get_offset(bios_data, offset); cur_val = chksum_bios_get_value(bios_data, offset); new_val = chksum_bios_calc_value(bios_data, offset); if ((cur_val != new_val) && (modified == 0)) { bios_len += 0x200; bios_data[2]++; if (pcir_offset != -1L) { bios_data[pcir_offset + 16]++; } modified = 1; } else { printf("\nBios checksum at: 0x%4lX\n", offset); printf("Current checksum: 0x%02X\n", cur_val); printf("Calculated checksum: 0x%02X ", new_val); if (cur_val != new_val) { printf("Setting checksum."); chksum_bios_set_value(bios_data, offset, new_val); cur_val = new_val; modified = 1; } printf( "\n" ); } } while (cur_val != new_val); if (modified == 1) { if ((stream = fopen( argv[1], "wb")) == NULL) { printf("Error opening %s for writing.\n", argv[1]); exit(EXIT_FAILURE); } if (fwrite(bios_data, 1, bios_len, stream) < bios_len) { printf("Error writing %d KBytes to %s.\n", bios_len / 1024, argv[1]); fclose(stream); exit(EXIT_FAILURE); } fclose(stream); } return (EXIT_SUCCESS); } void check( int okay, char* message ) { if( !okay ) { printf( "\n\nError. %s.\n", message ); exit( EXIT_FAILURE ); } } long chksum_bios_get_offset( byte* data, long offset ) { return (bios_len - 1); } byte chksum_bios_calc_value( byte* data, long offset ) { int i; byte sum; sum = 0; for( i = 0; i < offset; i++ ) { sum = sum + *( data + i ); } sum = -sum; /* iso ensures -s + s == 0 on unsigned types */ return( sum ); } byte chksum_bios_get_value( byte* data, long offset ) { return( *( data + offset ) ); } void chksum_bios_set_value( byte* data, long offset, byte value ) { *( data + offset ) = value; } byte chksum_pmid_calc_value( byte* data, long offset ) { int i; int len; byte sum; len = PMID_LEN; check((offset + len) <= (bios_len - 1), "PMID entry length out of bounds" ); sum = 0; for( i = 0; i < len; i++ ) { if( i != PMID_CHKSUM ) { sum = sum + *( data + offset + i ); } } sum = -sum; return( sum ); } long chksum_pmid_get_offset( byte* data, long offset ) { long result = -1L; while ((offset + PMID_LEN) < (bios_len - 1)) { offset = offset + 1; if( *( data + offset + 0 ) == 'P' && \ *( data + offset + 1 ) == 'M' && \ *( data + offset + 2 ) == 'I' && \ *( data + offset + 3 ) == 'D' ) { result = offset; break; } } return( result ); } byte chksum_pmid_get_value( byte* data, long offset ) { check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" ); return( *( data + offset + PMID_CHKSUM ) ); } void chksum_pmid_set_value( byte* data, long offset, byte value ) { check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" ); *( data + offset + PMID_CHKSUM ) = value; } long chksum_pcir_get_offset( byte* data, long offset ) { long result = -1L; while ((offset + PCIR_LEN) < (bios_len - 1)) { offset = offset + 1; if( *( data + offset + 0 ) == 'P' && \ *( data + offset + 1 ) == 'C' && \ *( data + offset + 2 ) == 'I' && \ *( data + offset + 3 ) == 'R' ) { result = offset; break; } } return( result ); }