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