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