This patch drops the coreboot CMOS checksum ranges from Kconfig because
[coreboot.git] / util / options / build_opt_tbl.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2003 Eric Biederman (ebiederm@xmission.com)
5  * Copyright (C) 2007-2010 coresystems GmbH
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <libgen.h>
28 #include "../../src/include/pc80/mc146818rtc.h"
29 #include "../../src/include/boot/coreboot_tables.h"
30
31 #define CMOS_IMAGE_BUFFER_SIZE 256
32 #define INPUT_LINE_MAX 256
33 #define MAX_VALUE_BYTE_LENGTH 64
34
35 #define TMPFILE_LEN 256
36 #define TMPFILE_TEMPLATE "/build_opt_tbl_XXXXXX"
37
38 static unsigned char cmos_table[4096];
39
40 /* This array is used to isolate bits that are to be changed in a byte */
41 static unsigned char clip[9]={0,1,3,7,0x0f,0x1f,0x3f,0x7f,0xff};
42
43 #ifdef WIN32
44 #include <fcntl.h>
45 char *mkstemp(char* name)
46 {
47         static char val='0';
48         char *c=name;
49         while (*c!='X') c++;
50         *c=val++;
51         return open(name,O_CREAT | O_RDWR);
52 }
53 #define UNLINK_IF_NECESSARY(x) unlink(x)
54 #else
55 #define UNLINK_IF_NECESSARY(x)
56 #endif
57
58 /**
59  * This routine loops through the entried and tests if any of the fields
60  * overlap.
61  * If there is an overlap, the routine exits, otherwise it returns.
62  *
63  * @param entry_start memory pointer to the start of the entries.
64  * @param entry_end   memory pointer to the byte past the entries.
65  */
66 static void test_for_entry_overlaps(void *entry_start, void *entry_end)
67 {
68         int ptr;
69         char *cptr;
70         int buffer_bit_size;
71         int offset;
72         int byte;
73         int byte_length;
74         struct cmos_entries *ce;
75         unsigned char test[CMOS_IMAGE_BUFFER_SIZE];
76         unsigned char set;
77
78         /* calculate the size of the cmos buffer in bits */
79         buffer_bit_size=(CMOS_IMAGE_BUFFER_SIZE*8);
80         /* clear the temporary test buffer */
81         for(ptr=0; ptr < CMOS_IMAGE_BUFFER_SIZE; ptr++)
82                 test[ptr]=0;
83
84         /* loop through each entry in the table testing for errors */
85         for(cptr = entry_start; cptr < (char *)entry_end; cptr += ce->size) {
86                 ce=(struct cmos_entries *)cptr;
87                 /* test if entry goes past the end of the buffer */
88                 if((int)(ce->bit+ce->length) > buffer_bit_size) {
89                         printf("Error - Entry %s start bit + length must be less than %d\n",
90                                 ce->name,buffer_bit_size);
91                         exit(1);
92                 }
93                 byte=ce->bit/8;
94                 offset=ce->bit%8;
95                 byte_length=ce->length/8;
96                 if(byte_length) {       /* entry is 8 bits long or more */
97                         if(offset) { /* if 8 bits or more long, it must be byte aligned */
98                                 printf("Error - Entry %s length over 8 must be byte aligned\n",
99                                         ce->name);
100                                 exit(1);
101                         }
102                         /* test if entries 8 or more in length are even bytes */ 
103                         if(ce->length%8){
104                                 printf("Error - Entry %s length over 8 must be a multiple of 8\n",
105                                         ce->name);
106                                 exit(1);
107                         }
108                         /* test if any of the bits have been previously used */
109                         for(;byte_length;byte_length--,byte++) {
110                                 if(test[byte]) {
111                                         printf("Error - Entry %s uses same bits previously used\n",
112                                                 ce->name);
113                                         exit(1);
114                                 }
115                                 test[byte]=clip[8]; /* set the bits defined in test */
116                         }
117                 } else {
118                         /* test if bits overlap byte boundaries */
119                         if((int)ce->length > (8-offset)) {
120                                 printf("Error - Entry %s length overlaps a byte boundry\n",
121                                         ce->name);
122                                 exit(1);
123                         }
124                         /* test for bits previously used */
125                         set=(clip[ce->length]<<offset);
126                         if(test[byte]&set) {
127                                 printf("Error - Entry %s uses same bits previously used\n",
128                                                 ce->name);
129                                 exit(1);
130                         }
131                         test[byte]|=set;  /* set the bits defined in test */
132                 }
133         }
134         return;
135 }
136
137 /* This routine displays the usage options */
138 static void display_usage(char *name)
139 {
140         printf("Usage: %s [--config filename]\n", name);
141         printf("                       [--option filename]\n");
142         printf("                       [--header filename]\n\n");
143         printf("--config = Build the definitions table from the given file.\n");
144         printf("--option = Output a C source file with the definitions.\n");
145         printf("--header = Ouput a C header file with the definitions.\n");
146         exit(1);
147 }
148
149 static void skip_spaces(char *line, char **ptr)
150 {
151         if (!isspace(**ptr)) {
152                 printf("Error missing whitespace in line\n%s\n", line);
153                 exit(1);
154         }
155         while(isspace(**ptr)) {
156                 (*ptr)++;
157         }
158         return;
159 }
160
161 static unsigned long get_number(char *line, char **ptr, int base)
162 {
163         unsigned long value;
164         char *ptr2;
165         value = strtoul(*ptr, &ptr2, base);
166         if (ptr2 == *ptr) {
167                 printf("Error missing digits at: \n%s\n in line:\n%s\n", 
168                         *ptr, line);
169                 exit(1);
170         }
171         *ptr = ptr2;
172         return value;
173 }
174
175 static int is_ident_digit(int c)
176 {
177         int result;
178         switch(c) {
179         case '0':       case '1':       case '2':       case '3':
180         case '4':       case '5':       case '6':       case '7':
181         case '8':       case '9':
182                 result = 1;
183                 break;
184         default:
185                 result = 0;
186                 break;
187         }
188         return result;
189 }
190
191 static int is_ident_nondigit(int c)
192 {
193         int result;
194         switch(c) {
195         case 'A':       case 'B':       case 'C':       case 'D':
196         case 'E':       case 'F':       case 'G':       case 'H':
197         case 'I':       case 'J':       case 'K':       case 'L':
198         case 'M':       case 'N':       case 'O':       case 'P':
199         case 'Q':       case 'R':       case 'S':       case 'T':
200         case 'U':       case 'V':       case 'W':       case 'X':
201         case 'Y':       case 'Z':
202         case 'a':       case 'b':       case 'c':       case 'd':
203         case 'e':       case 'f':       case 'g':       case 'h':
204         case 'i':       case 'j':       case 'k':       case 'l':
205         case 'm':       case 'n':       case 'o':       case 'p':
206         case 'q':       case 'r':       case 's':       case 't':
207         case 'u':       case 'v':       case 'w':       case 'x':
208         case 'y':       case 'z':
209         case '_':
210                 result = 1;
211                 break;
212         default:
213                 result = 0;
214                 break;
215         }
216         return result;
217 }
218
219 static int is_ident(char *str)
220 {
221         int result;
222         int ch;
223         ch = *str;
224         result = 0;
225         if (is_ident_nondigit(ch)) {
226                 do {
227                         str++;
228                         ch = *str;
229                 } while(ch && (is_ident_nondigit(ch) || (is_ident_digit(ch))));
230                 result = (ch == '\0');
231         }
232         return result;
233 }
234
235 /**
236  * This routine builds the cmos definition table from the cmos layout file
237  *
238  * The input comes from the configuration file which contains two parts
239  * entries and enumerations.  Each section is started with the key words
240  * entries and enumerations.  Records then follow in their respective 
241  * formats.
242  *
243  * The output of this program is the cmos definitions table.  It is stored
244  * in the cmos_table array. If this module is called, and the global 
245  * table_file has been implimented by the user, the table is also written
246  * to the specified file.
247  *
248  * This program exits with a return code of 1 on error.  It returns 0 on 
249  * successful completion
250  */
251 int main(int argc, char **argv)
252 {
253         int i;
254         char *config=0;
255         char *option=0;
256         char *header=0;
257         FILE *fp;
258         int tempfile;
259         char tempfilename[TMPFILE_LEN];
260         struct cmos_option_table *ct;
261         struct cmos_entries *ce;
262         struct cmos_enums *c_enums, *c_enums_start;
263         struct cmos_checksum *cs, *new_cs;
264         char line[INPUT_LINE_MAX];
265         unsigned char uc;
266         int entry_mode=0;
267         int enum_mode=0;
268         int checksum_mode=0;
269         int cnt;
270         char *cptr;
271         void *entry_start, *entry_end;
272         int entries_length;
273         int enum_length;
274         int len;
275         char buf[16];
276
277         for(i=1;i<argc;i++) {
278                 if(argv[i][0]!='-') {
279                         display_usage(argv[0]);
280                 }
281                 switch(argv[i][1]) {
282                         case '-':       /* data is requested from a file */
283                                 switch(argv[i][2]) {
284                                         case 'c':  /* use a configuration file */
285                                                 if(strcmp(&argv[i][2],"config")) {
286                                                         display_usage(argv[0]);
287                                                 }
288                                                 config=argv[++i];
289                                                 break;
290                                         case 'o':  /* use a cmos definitions table file */
291                                                 if(strcmp(&argv[i][2],"option")) {
292                                                         display_usage(argv[0]);
293                                                 }
294                                                 option=argv[++i];
295                                                 break;
296                                         case 'h': /* Output a header file */
297                                                 if (strcmp(&argv[i][2], "header") != 0) {
298                                                         display_usage(argv[0]);
299                                                 }
300                                                 header=argv[++i];
301                                                 break;
302                                         default:
303                                                 display_usage(argv[0]);
304                                                 break;
305                                 }
306                                 break;
307
308                         default:
309                                 display_usage(argv[0]);
310                                 break;
311                 }
312         }
313
314
315         /* Has the user specified a configuration file */
316         if(config) {    /* if yes, open it */
317                 if((fp=fopen(config,"r"))==NULL){
318                         fprintf(stderr, "Error - Can not open config file %s\n",config);
319                         exit(1);  /* exit if it can not be opened */
320                 }
321         }
322         else {  /* no configuration file specified, so try the default */
323                 if((fp=fopen("cmos.layout","r"))==NULL){
324                         fprintf(stderr, "Error - Can not open cmos.layout\n");
325                         exit(1);  /* end of no configuration file is found */
326                 }
327         }
328         /* type cast a pointer, so we can us the structure */
329         ct=(struct cmos_option_table*)cmos_table;
330         /* start the table with the type signature */
331         ct->tag = LB_TAG_CMOS_OPTION_TABLE;
332         /* put in the header length */
333         ct->header_length=sizeof(*ct);
334
335         /* Get the entry records */
336         ce=(struct cmos_entries*)(cmos_table+(ct->header_length));
337         cptr = (char*)ce;
338         for(;;){  /* this section loops through the entry records */
339                 if(fgets(line,INPUT_LINE_MAX,fp)==NULL) 
340                         break; /* end if no more input */
341                 // FIXME mode should be a single enum.
342                 if(!entry_mode) {  /* skip input until the entries key word */
343                         if (strstr(line,"entries") != 0) {
344                                 entry_mode=1;
345                                 enum_mode=0;
346                                 checksum_mode=0;
347                                 continue;
348                         }
349                 } else {  /* Test if we are done with entries and starting enumerations */
350                         if (strstr(line,"enumerations") != 0){
351                                 entry_mode=0;
352                                 enum_mode=1;
353                                 checksum_mode=0;
354                                 break;
355                         }
356                         if (strstr(line, "checksums") != 0) {
357                                 entry_mode=0;
358                                 enum_mode=0;
359                                 checksum_mode=1;
360                                 break;
361                         }
362                 }
363
364                 /* skip commented and blank lines */
365                 if(line[0]=='#') continue;
366                 if(line[strspn(line," ")]=='\n') continue;
367                 /* scan in the input data */
368                 sscanf(line,"%d %d %c %d %s",
369                         &ce->bit,&ce->length,&uc,&ce->config_id,&ce->name[0]);
370                 ce->config=(int)uc;
371                 /* check bit and length ranges */
372                 if(ce->bit>(CMOS_IMAGE_BUFFER_SIZE*8)) {
373                         fprintf(stderr, "Error - bit is to big in line \n%s\n",line);
374                         exit(1);
375                 }
376                 if((ce->length>(MAX_VALUE_BYTE_LENGTH*8))&&(uc!='r')) {
377                         fprintf(stderr, "Error - Length is to long in line \n%s\n",line);
378                         exit(1);
379                 }
380                 if (!is_ident((char *)ce->name)) {
381                         fprintf(stderr, 
382                                 "Error - Name %s is an invalid identifier in line\n %s\n", 
383                                 ce->name, line);
384                         exit(1);
385                 }
386                 /* put in the record type */
387                 ce->tag=LB_TAG_OPTION;
388                 /* calculate and save the record length */
389                 len=strlen((char *)ce->name)+1;
390                 /* make the record int aligned */
391                 if(len%4)
392                         len+=(4-(len%4));
393                 ce->size=sizeof(struct cmos_entries)-32+len;
394                 cptr = (char*)ce;
395                 cptr += ce->size;  /* increment to the next table position */
396                 ce = (struct cmos_entries*) cptr;
397         }
398
399         /* put the length of the entries into the header section */
400         entries_length = (cptr - (char *)&cmos_table) - ct->header_length;
401
402         /* compute the start of the enumerations section */
403         entry_start = ((char*)&cmos_table) + ct->header_length;
404         entry_end   = ((char *)entry_start) + entries_length;
405         c_enums_start = c_enums = (struct cmos_enums*)entry_end;
406         /* test for overlaps in the entry records */
407         test_for_entry_overlaps(entry_start, entry_end);
408
409         for(;enum_mode;){ /* loop to build the enumerations section */
410                 long ptr;
411                 if(fgets(line,INPUT_LINE_MAX,fp)==NULL) 
412                         break; /* go till end of input */
413
414                 if (strstr(line, "checksums") != 0) {
415                         enum_mode=0;
416                         checksum_mode=1;
417                         break;
418                 }
419
420                 /* skip commented and blank lines */
421                 if(line[0]=='#') continue;
422                 if(line[strspn(line," ")]=='\n') continue;
423
424                 /* scan in the data */
425                 for(ptr=0;(line[ptr]==' ')||(line[ptr]=='\t');ptr++);
426                 c_enums->config_id=strtol(&line[ptr],(char**)NULL,10);
427                 for(;(line[ptr]!=' ')&&(line[ptr]!='\t');ptr++);
428                 for(;(line[ptr]==' ')||(line[ptr]=='\t');ptr++);
429                 c_enums->value=strtol(&line[ptr],(char**)NULL,10);
430                 for(;(line[ptr]!=' ')&&(line[ptr]!='\t');ptr++);
431                 for(;(line[ptr]==' ')||(line[ptr]=='\t');ptr++);
432                 for(cnt=0;(line[ptr]!='\n')&&(cnt<31);ptr++,cnt++)
433                         c_enums->text[cnt]=line[ptr];
434                 c_enums->text[cnt]=0;
435         
436                 /* make the record int aligned */
437                 cnt++;
438                 if(cnt%4)
439                         cnt+=4-(cnt%4);
440                 /* store the record length */
441                 c_enums->size=((char *)&c_enums->text[cnt]) - (char *)c_enums;
442                 /* store the record type */
443                 c_enums->tag=LB_TAG_OPTION_ENUM;
444                 /* increment to the next record */
445                 c_enums=(struct cmos_enums*)&c_enums->text[cnt];
446         }
447         /* save the enumerations length */
448         enum_length= (char *)c_enums - (char *)c_enums_start;
449         ct->size=ct->header_length+enum_length+entries_length;
450
451         /* Get the checksum records */
452         new_cs = (struct cmos_checksum *)(cmos_table+(ct->size));
453         for(;checksum_mode;) { /* This section finds the checksums */
454                 char *ptr;
455                 if(fgets(line, INPUT_LINE_MAX,fp)==NULL)
456                         break; /* end if no more input */
457
458                 /* skip commented and blank lines */
459                 if (line[0]=='#') continue;
460                 if (line[strspn(line, " ")]=='\n') continue;
461                 if (memcmp(line, "checksum", 8) != 0) continue;
462
463                 /* We actually found a new cmos checksum entry */
464                 cs = new_cs;
465
466                 /* get the information */
467                 ptr = line + 8;
468                 skip_spaces(line, &ptr);
469                 cs->range_start = get_number(line, &ptr, 10);
470
471                 skip_spaces(line, &ptr);
472                 cs->range_end = get_number(line, &ptr, 10);
473
474                 skip_spaces(line, &ptr);
475                 cs->location = get_number(line, &ptr, 10);
476                 
477                 /* Make certain there are spaces until the end of the line */
478                 skip_spaces(line, &ptr);
479
480                 if ((cs->range_start%8) != 0) {
481                         fprintf(stderr, "Error - range start is not byte aligned in line\n%s\n", line);
482                         exit(1);
483                 }
484                 if (cs->range_start >= (CMOS_IMAGE_BUFFER_SIZE*8)) {
485                         fprintf(stderr, "Error - range start is to big in line\n%s\n", line);
486                         exit(1);
487                 }
488                 if ((cs->range_end%8) != 7) {
489                         fprintf(stderr, "Error - range end is not byte aligned in line\n%s\n", line);
490                         exit(1);
491                 }
492                 if ((cs->range_end) >= (CMOS_IMAGE_BUFFER_SIZE*8)) {
493                         fprintf(stderr, "Error - range end is to long in line\n%s\n", line);
494                         exit(1);
495                 }
496                 if ((cs->location%8) != 0) {
497                         fprintf(stderr, "Error - location is not byte aligned in line\n%s\n", line);
498                         exit(1);
499                 }
500                 if ((cs->location >= (CMOS_IMAGE_BUFFER_SIZE*8)) ||
501                         ((cs->location + 16) > (CMOS_IMAGE_BUFFER_SIZE*8))) 
502                 {
503                         fprintf(stderr, "Error - location is to big in line\n%s\n", line);
504                         exit(1);
505                 }
506
507                 cs->tag = LB_TAG_OPTION_CHECKSUM;
508                 cs->size = sizeof(*cs);
509                 cs->type = CHECKSUM_PCBIOS;
510
511                 cptr = (char *)cs;
512                 cptr += cs->size;
513                 new_cs = (struct cmos_checksum *)cptr;
514         }
515         ct->size += (cptr - (char *)(cmos_table + ct->size));
516         fclose(fp);
517
518         /* See if we want to output a C source file */
519         if(option) {
520                 int err=0;
521                 strncpy(tempfilename, dirname(strdup(option)), TMPFILE_LEN);
522                 strncat(tempfilename, TMPFILE_TEMPLATE, TMPFILE_LEN);
523                 tempfile = mkstemp(tempfilename);
524                 if(tempfile == -1) {
525                         perror("Error - Could not create temporary file");
526                         exit(1);
527                 }
528
529                 if((fp=fdopen(tempfile,"w"))==NULL){
530                         perror("Error - Could not open temporary file");
531                         unlink(tempfilename);
532                         exit(1);
533                 }
534
535                 /* write the header */
536                 if(!fwrite("unsigned char option_table[] = {",1,32,fp)) {
537                         perror("Error - Could not write image file");
538                         fclose(fp);
539                         unlink(tempfilename);
540                         exit(1);
541                 }
542                 /* write the array values */
543                 for(i=0; i<(int)(ct->size-1); i++) {
544                         if(!(i%10) && !err) err=!fwrite("\n\t",1,2,fp);
545                         sprintf(buf,"0x%02x,",cmos_table[i]);
546                         if(!err) err=!fwrite(buf,1,5,fp);
547                 }
548                 /* write the end */
549                 sprintf(buf,"0x%02x\n",cmos_table[i]);
550                 if(!err) err=!fwrite(buf,1,4,fp);
551                 if(!fwrite("};\n",1,3,fp)) {
552                         perror("Error - Could not write image file");
553                         fclose(fp);
554                         unlink(tempfilename);
555                         exit(1);
556                 }
557
558                 fclose(fp);
559                 UNLINK_IF_NECESSARY(option);
560                 if (rename(tempfilename, option)) {
561                         fprintf(stderr, "Error - Could not write %s: ", option);
562                         perror(NULL);
563                         unlink(tempfilename);
564                         exit(1);
565                 }
566         }
567
568         /* See if we also want to output a C header file */
569         if (header) {
570                 struct cmos_option_table *hdr;
571                 struct lb_record *ptr, *end;
572
573                 strncpy(tempfilename, dirname(strdup(option)), TMPFILE_LEN);
574                 strncat(tempfilename, TMPFILE_TEMPLATE, TMPFILE_LEN);
575                 tempfile = mkstemp(tempfilename);
576                 if(tempfile == -1) {
577                         perror("Error - Could not create temporary file");
578                         exit(1);
579                 }
580
581                 fp = fdopen(tempfile, "w");
582                 if (!fp) {
583                         perror("Error - Could not open temporary file");
584                         unlink(tempfilename);
585                         exit(1);
586                 }
587
588                 /* Get the cmos table header */
589                 hdr = (struct cmos_option_table *)cmos_table;
590                 /* Walk through the entry records */
591                 ptr = (struct lb_record *)(cmos_table + hdr->header_length);
592                 end = (struct lb_record *)(cmos_table + hdr->size);
593                 for(;ptr < end; ptr = (struct lb_record *)(((char *)ptr) + ptr->size)) {
594                         if (ptr->tag != LB_TAG_OPTION) {
595                                 continue;
596                         }
597                         ce = (struct cmos_entries *)ptr;
598
599                         if (!is_ident((char *)ce->name)) {
600                                 fprintf(stderr, "Invalid identifier: %s\n",
601                                         ce->name);
602                                 fclose(fp);
603                                 unlink(tempfilename);
604                                 exit(1);
605                         }
606                         fprintf(fp, "#define CMOS_VSTART_%s %d\n",
607                                 ce->name, ce->bit);
608                         fprintf(fp, "#define CMOS_VLEN_%s %d\n",
609                                 ce->name, ce->length);
610                 }
611                 fprintf(fp, "\n#define LB_CKS_RANGE_START %d\n", cs->range_start / 8);
612                 fprintf(fp, "#define LB_CKS_RANGE_END %d\n", cs->range_end / 8);
613                 fprintf(fp, "#define LB_CKS_LOC %d\n", cs->location / 8);
614                 fclose(fp);
615
616                 UNLINK_IF_NECESSARY(header);
617                 if (rename(tempfilename, header)) {
618                         fprintf(stderr, "Error - Could not write %s: ", header);
619                         perror(NULL);
620                         unlink(tempfilename);
621                         exit(1);
622                 }
623         }
624
625         return 0;
626 }
627
628