1/* biossums.c  --- written by Eike W. for the Bochs BIOS */
2/* adapted for the LGPL'd VGABIOS by vruppert */
3
4/*  This library is free software; you can redistribute it and/or
5 *  modify it under the terms of the GNU Lesser General Public
6 *  License as published by the Free Software Foundation; either
7 *  version 2 of the License, or (at your option) any later version.
8 *
9 *  This library is distributed in the hope that it will be useful,
10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 *  Lesser General Public License for more details.
13 *
14 *  You should have received a copy of the GNU Lesser General Public
15 *  License along with this library; if not, write to the Free Software
16 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
17 */
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21
22typedef unsigned char byte;
23
24void check( int value, char* message );
25
26#define MAX_BIOS_DATA 0x10000
27
28long chksum_bios_get_offset( byte* data, long offset );
29byte chksum_bios_calc_value( byte* data, long offset );
30byte chksum_bios_get_value(  byte* data, long offset );
31void chksum_bios_set_value(  byte* data, long offset, byte value );
32
33#define PMID_LEN        20
34#define PMID_CHKSUM     19
35
36long chksum_pmid_get_offset( byte* data, long offset );
37byte chksum_pmid_calc_value( byte* data, long offset );
38byte chksum_pmid_get_value(  byte* data, long offset );
39void chksum_pmid_set_value(  byte* data, long offset, byte value );
40
41#define PCIR_LEN        24
42
43long chksum_pcir_get_offset( byte* data, long offset );
44
45
46byte bios_data[MAX_BIOS_DATA];
47long bios_len;
48
49
50int main(int argc, char* argv[])
51{
52  FILE* stream;
53  long  offset, tmp_offset, pcir_offset;
54  byte  bios_len_byte, cur_val = 0, new_val = 0;
55  int   hits, modified;
56
57  if (argc != 2) {
58    printf( "Error. Need a file-name as an argument.\n" );
59    exit( EXIT_FAILURE );
60  }
61
62  if ((stream = fopen(argv[1], "rb")) == NULL) {
63    printf("Error opening %s for reading.\n", argv[1]);
64    exit(EXIT_FAILURE);
65  }
66  memset(bios_data, 0, MAX_BIOS_DATA);
67  bios_len = fread(bios_data, 1, MAX_BIOS_DATA, stream);
68  if (bios_len > MAX_BIOS_DATA) {
69    printf("Error reading max. 65536 Bytes from %s.\n", argv[1]);
70    fclose(stream);
71    exit(EXIT_FAILURE);
72  }
73  fclose(stream);
74  modified = 0;
75  if (bios_len < 0x8000) {
76    bios_len = 0x8000;
77    modified = 1;
78  } else if ((bios_len & 0x1FF) != 0) {
79    bios_len = (bios_len + 0x200) & ~0x1FF;
80    modified = 1;
81  }
82  bios_len_byte = (byte)(bios_len / 512);
83  if (bios_len_byte != bios_data[2]) {
84    if (modified == 0) {
85      bios_len += 0x200;
86    }
87    bios_data[2] = (byte)(bios_len / 512);
88    modified = 1;
89  }
90
91  hits   = 0;
92  offset = 0L;
93  while( (tmp_offset = chksum_pmid_get_offset( bios_data, offset )) != -1L ) {
94    offset  = tmp_offset;
95    cur_val = chksum_pmid_get_value(  bios_data, offset );
96    new_val = chksum_pmid_calc_value( bios_data, offset );
97    printf( "\nPMID entry at: 0x%4lX\n", offset  );
98    printf( "Current checksum:     0x%02X\n",   cur_val );
99    printf( "Calculated checksum:  0x%02X  ",   new_val );
100    hits++;
101  }
102  if ((hits == 1) && (cur_val != new_val)) {
103    printf("Setting checksum.");
104    chksum_pmid_set_value( bios_data, offset, new_val );
105    if (modified == 0) {
106      bios_len += 0x200;
107      bios_data[2]++;
108    }
109    modified = 1;
110  }
111  if (hits >= 2) {
112    printf( "Multiple PMID entries! No checksum set." );
113  }
114  if (hits) {
115    printf("\n");
116  }
117
118  offset = 0L;
119  pcir_offset = chksum_pcir_get_offset( bios_data, offset );
120  if (pcir_offset != -1L) {
121    if (bios_data[pcir_offset + 16] != bios_data[2]) {
122      bios_data[pcir_offset + 16] = bios_data[2];
123      if (modified == 0) {
124        bios_len += 0x200;
125        bios_data[2]++;
126        bios_data[pcir_offset + 16]++;
127      }
128      modified = 1;
129    }
130  }
131
132  offset  = 0L;
133  do {
134    offset  = chksum_bios_get_offset(bios_data, offset);
135    cur_val = chksum_bios_get_value(bios_data, offset);
136    new_val = chksum_bios_calc_value(bios_data, offset);
137    if ((cur_val != new_val) && (modified == 0)) {
138      bios_len += 0x200;
139      bios_data[2]++;
140      if (pcir_offset != -1L) {
141        bios_data[pcir_offset + 16]++;
142      }
143      modified = 1;
144    } else {
145      printf("\nBios checksum at:   0x%4lX\n", offset);
146      printf("Current checksum:     0x%02X\n", cur_val);
147      printf("Calculated checksum:  0x%02X  ", new_val);
148      if (cur_val != new_val) {
149        printf("Setting checksum.");
150        chksum_bios_set_value(bios_data, offset, new_val);
151        cur_val = new_val;
152        modified = 1;
153      }
154      printf( "\n" );
155    }
156  } while (cur_val != new_val);
157
158  if (modified == 1) {
159    if ((stream = fopen( argv[1], "wb")) == NULL) {
160      printf("Error opening %s for writing.\n", argv[1]);
161      exit(EXIT_FAILURE);
162    }
163    if (fwrite(bios_data, 1, bios_len, stream) < bios_len) {
164      printf("Error writing %d KBytes to %s.\n", bios_len / 1024, argv[1]);
165      fclose(stream);
166      exit(EXIT_FAILURE);
167    }
168    fclose(stream);
169  }
170
171  return (EXIT_SUCCESS);
172}
173
174
175void check( int okay, char* message ) {
176
177  if( !okay ) {
178    printf( "\n\nError. %s.\n", message );
179    exit( EXIT_FAILURE );
180  }
181}
182
183
184long chksum_bios_get_offset( byte* data, long offset ) {
185
186  return (bios_len - 1);
187}
188
189
190byte chksum_bios_calc_value( byte* data, long offset ) {
191
192  int   i;
193  byte  sum;
194
195  sum = 0;
196  for( i = 0; i < offset; i++ ) {
197    sum = sum + *( data + i );
198  }
199  sum = -sum;          /* iso ensures -s + s == 0 on unsigned types */
200  return( sum );
201}
202
203
204byte chksum_bios_get_value( byte* data, long offset ) {
205
206  return( *( data + offset ) );
207}
208
209
210void chksum_bios_set_value( byte* data, long offset, byte value ) {
211
212  *( data + offset ) = value;
213}
214
215
216byte chksum_pmid_calc_value( byte* data, long offset ) {
217
218  int           i;
219  int           len;
220  byte sum;
221
222  len = PMID_LEN;
223  check((offset + len) <= (bios_len - 1), "PMID entry length out of bounds" );
224  sum = 0;
225  for( i = 0; i < len; i++ ) {
226    if( i != PMID_CHKSUM ) {
227      sum = sum + *( data + offset + i );
228    }
229  }
230  sum = -sum;
231  return( sum );
232}
233
234
235long chksum_pmid_get_offset( byte* data, long offset ) {
236
237  long result = -1L;
238
239  while ((offset + PMID_LEN) < (bios_len - 1)) {
240    offset = offset + 1;
241    if( *( data + offset + 0 ) == 'P' && \
242        *( data + offset + 1 ) == 'M' && \
243        *( data + offset + 2 ) == 'I' && \
244        *( data + offset + 3 ) == 'D' ) {
245      result = offset;
246      break;
247    }
248  }
249  return( result );
250}
251
252
253byte chksum_pmid_get_value( byte* data, long offset ) {
254
255  check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" );
256  return(  *( data + offset + PMID_CHKSUM ) );
257}
258
259
260void chksum_pmid_set_value( byte* data, long offset, byte value ) {
261
262  check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" );
263  *( data + offset + PMID_CHKSUM ) = value;
264}
265
266
267long chksum_pcir_get_offset( byte* data, long offset ) {
268
269  long result = -1L;
270
271  while ((offset + PCIR_LEN) < (bios_len - 1)) {
272    offset = offset + 1;
273    if( *( data + offset + 0 ) == 'P' && \
274        *( data + offset + 1 ) == 'C' && \
275        *( data + offset + 2 ) == 'I' && \
276        *( data + offset + 3 ) == 'R' ) {
277      result = offset;
278      break;
279    }
280  }
281  return( result );
282}
283