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