Make the kconfig-style build work in mingw:
[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                 /* And since we are not ready to be fully general purpose yet.. */
476                 if ((cs->range_start/8) != CONFIG_LB_CKS_RANGE_START) {
477                         fprintf(stderr, "Error - Range start((value in file #%d), which is  byte #%d)"
478                                 "does not match the value of the config variable LB_CKS_RANGE_START)(%d) in line\n%s\n", 
479                                 cs->range_start, cs->range_start/8, CONFIG_LB_CKS_RANGE_START, line);
480                         exit(1);
481                 }
482                 if ((cs->range_end/8) != CONFIG_LB_CKS_RANGE_END) {
483                         fprintf(stderr, "Error - Range end ((value in file #%d), which is  byte #%d)"
484                                         "does not match the value of the config variable LB_CKS_RANGE_END (%d) in line\n%s\n", 
485                                         cs->range_end, cs->range_end/8, 
486                                         CONFIG_LB_CKS_RANGE_END, line);
487                         exit(1);
488                 }
489                 if ((cs->location/8) != CONFIG_LB_CKS_LOC) {
490                         fprintf(stderr, "Error - Location ((value in file #%d), which is  byte #%d) does not match LB_CKS_LOC (%d) in line\n%s\n", 
491                                 cs->location, cs->location/8, CONFIG_LB_CKS_LOC, line);
492                         exit(1);
493                 }
494
495                 cs->tag = LB_TAG_OPTION_CHECKSUM;
496                 cs->size = sizeof(*cs);
497                 cs->type = CHECKSUM_PCBIOS;
498                 cptr = (char *)cs;
499                 cptr += cs->size;
500                 cs = (struct cmos_checksum *)cptr;
501
502         }
503         ct->size += (cptr - (char *)(cmos_table + ct->size));
504         fclose(fp);
505
506         /* See if we want to output a C source file */
507         if(option) {
508                 int err=0;
509                 strncpy(tmpfilename, dirname(strdup(option)), TMPFILE_LEN);
510                 strncat(tmpfilename, TMPFILE_TEMPLATE, TMPFILE_LEN);
511                 tmpfile = mkstemp(tmpfilename);
512                 if(tmpfile == -1) {
513                         perror("Error - Could not create temporary file");
514                         exit(1);
515                 }
516
517                 if((fp=fdopen(tmpfile,"w"))==NULL){
518                         perror("Error - Could not open temporary file");
519                         unlink(tmpfilename);
520                         exit(1);
521                 }
522
523                 /* write the header */
524                 if(!fwrite("unsigned char option_table[] = {",1,32,fp)) {
525                         perror("Error - Could not write image file");
526                         fclose(fp);
527                         unlink(tmpfilename);
528                         exit(1);
529                 }
530                 /* write the array values */
531                 for(i=0;i<(ct->size-1);i++) {
532                         if(!(i%10) && !err) err=!fwrite("\n\t",1,2,fp);
533                         sprintf(buf,"0x%02x,",cmos_table[i]);
534                         if(!err) err=!fwrite(buf,1,5,fp);
535                 }
536                 /* write the end */
537                 sprintf(buf,"0x%02x\n",cmos_table[i]);
538                 if(!err) err=!fwrite(buf,1,4,fp);
539                 if(!fwrite("};\n",1,3,fp)) {
540                         perror("Error - Could not write image file");
541                         fclose(fp);
542                         unlink(tmpfilename);
543                         exit(1);
544                 }
545
546                 fclose(fp);
547                 UNLINK_IF_NECESSARY(option);
548                 if (rename(tmpfilename, option)) {
549                         fprintf(stderr, "Error - Could not write %s: ", option);
550                         perror(NULL);
551                         unlink(tmpfilename);
552                         exit(1);
553                 }
554         }
555
556         /* See if we also want to output a C header file */
557         if (header) {
558                 struct cmos_option_table *hdr;
559                 struct lb_record *ptr, *end;
560
561                 strncpy(tmpfilename, dirname(strdup(option)), TMPFILE_LEN);
562                 strncat(tmpfilename, TMPFILE_TEMPLATE, TMPFILE_LEN);
563                 tmpfile = mkstemp(tmpfilename);
564                 if(tmpfile == -1) {
565                         perror("Error - Could not create temporary file");
566                         exit(1);
567                 }
568
569                 fp = fdopen(tmpfile, "w");
570                 if (!fp) {
571                         perror("Error - Could not open temporary file");
572                         unlink(tmpfilename);
573                         exit(1);
574                 }
575
576                 /* Get the cmos table header */
577                 hdr = (struct cmos_option_table *)cmos_table;
578                 /* Walk through the entry records */
579                 ptr = (struct lb_record *)(cmos_table + hdr->header_length);
580                 end = (struct lb_record *)(cmos_table + hdr->size);
581                 for(;ptr < end; ptr = (struct lb_record *)(((char *)ptr) + ptr->size)) {
582                         if (ptr->tag != LB_TAG_OPTION) {
583                                 continue;
584                         }
585                         ce = (struct cmos_entries *)ptr;
586
587                         if (!is_ident((char *)ce->name)) {
588                                 fprintf(stderr, "Invalid identifier: %s\n",
589                                         ce->name);
590                                 fclose(fp);
591                                 unlink(tmpfilename);
592                                 exit(1);
593                         }
594                         fprintf(fp, "#define CMOS_VSTART_%s %d\n",
595                                 ce->name, ce->bit);
596                         fprintf(fp, "#define CMOS_VLEN_%s %d\n",
597                                 ce->name, ce->length);
598                 }
599                 fclose(fp);
600
601                 UNLINK_IF_NECESSARY(header);
602                 if (rename(tmpfilename, header)) {
603                         fprintf(stderr, "Error - Could not write %s: ", header);
604                         perror(NULL);
605                         unlink(tmpfilename);
606                         exit(1);
607                 }
608         }
609         return(0);
610 }
611
612