build_opt_tbl:
[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 128
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) != 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, LB_CKS_RANGE_START, line);
465                         exit(1);
466                 }
467                 if ((cs->range_end/8) != 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), LB_CKS_RANGE_END, line);
470                         exit(1);
471                 }
472                 if ((cs->location/8) != 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                 strncpy(tmpfilename, dirname(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)) fwrite("\n\t",1,2,fp);
514                         sprintf(buf,"0x%02x,",cmos_table[i]);
515                         fwrite(buf,1,5,fp);
516                 }
517                 /* write the end */
518                 sprintf(buf,"0x%02x\n",cmos_table[i]);
519                 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                 if (rename(tmpfilename, option)) {
529                         fprintf(stderr, "Error - Could not write %s: ", option);
530                         perror(NULL);
531                         unlink(tmpfilename);
532                         exit(1);
533                 }
534         }
535
536         /* See if we also want to output a C header file */
537         if (header) {
538                 struct cmos_option_table *hdr;
539                 struct lb_record *ptr, *end;
540
541                 strncpy(tmpfilename, dirname(option), TMPFILE_LEN);
542                 strncat(tmpfilename, TMPFILE_TEMPLATE, TMPFILE_LEN);
543                 tmpfile = mkstemp(tmpfilename);
544                 if(tmpfile == -1) {
545                         perror("Error - Could not create temporary file");
546                         exit(1);
547                 }
548
549                 fp = fdopen(tmpfile, "w");
550                 if (!fp) {
551                         perror("Error - Could not open temporary file");
552                         unlink(tmpfilename);
553                         exit(1);
554                 }
555
556                 /* Get the cmos table header */
557                 hdr = (struct cmos_option_table *)cmos_table;
558                 /* Walk through the entry records */
559                 ptr = (struct lb_record *)(cmos_table + hdr->header_length);
560                 end = (struct lb_record *)(cmos_table + hdr->size);
561                 for(;ptr < end; ptr = (struct lb_record *)(((char *)ptr) + ptr->size)) {
562                         if (ptr->tag != LB_TAG_OPTION) {
563                                 continue;
564                         }
565                         ce = (struct cmos_entries *)ptr;
566                         if (ce->config == 'r') {
567                                 continue;
568                         }
569                         if (!is_ident((char *)ce->name)) {
570                                 fprintf(stderr, "Invalid identifier: %s\n",
571                                         ce->name);
572                                 fclose(fp);
573                                 unlink(tmpfilename);
574                                 exit(1);
575                         }
576                         fprintf(fp, "#define CMOS_VSTART_%s %d\n",
577                                 ce->name, ce->bit);
578                         fprintf(fp, "#define CMOS_VLEN_%s %d\n",
579                                 ce->name, ce->length);
580                 }
581                 fclose(fp);
582
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