git-svn-id: svn://svn.coreboot.org/coreboot/trunk@784 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
		
			
				
	
	
		
			444 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			444 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <sys/io.h>
 | |
| #include <string.h>
 | |
| #include <ctype.h>
 | |
| #include "../../src/include/pc80/mc146818rtc.h"
 | |
| #include "../../src/include/boot/linuxbios_tables.h"
 | |
| 
 | |
| #define CMOS_IMAGE_BUFFER_SIZE 128
 | |
| #define INPUT_LINE_MAX 256
 | |
| #define MAX_VALUE_BYTE_LENGTH 64
 | |
| 
 | |
| 
 | |
| static unsigned char cmos_table[4096];
 | |
| 
 | |
| /* This array is used to isolate bits that are to be changed in a byte */
 | |
| static unsigned char clip[9]={0,1,3,7,0x0f,0x1f,0x3f,0x7f,0xff};
 | |
| 
 | |
| 
 | |
| /* This routine loops through the entried and tests if any of the fields overlap
 | |
| 	input entry_start = the memory pointer to the start of the entries.
 | |
| 	      entry_end = the byte past the entries.
 | |
| 	output  none
 | |
| 		if there is an overlap, the routine exits, other wise it returns.
 | |
| */
 | |
| void test_for_entry_overlaps(int entry_start,int entry_end)
 | |
| {
 | |
| 	int ptr;
 | |
| 	int buffer_bit_size;
 | |
| 	int offset;
 | |
| 	int byte;
 | |
| 	int byte_length;
 | |
|         struct cmos_entries *ce;
 | |
| 	unsigned char test[CMOS_IMAGE_BUFFER_SIZE];
 | |
| 	unsigned char set;
 | |
| 
 | |
| 	/* calculate the size of the cmos buffer in bits */
 | |
| 	buffer_bit_size=(CMOS_IMAGE_BUFFER_SIZE*8);
 | |
| 	/* clear the temporary test buffer */
 | |
| 	for(ptr=0;ptr<CMOS_IMAGE_BUFFER_SIZE;ptr++)
 | |
| 		test[ptr]=0;
 | |
| 	/* loop through each entry in the table testing for errors */
 | |
| 	for(ptr=entry_start;ptr<entry_end;ptr+=ce->size) {
 | |
| 		ce=(struct cmos_entries *)ptr;
 | |
| 		/* test if entry goes past the end of the buffer */
 | |
| 		if((ce->bit+ce->length)>buffer_bit_size) {
 | |
| 			printf("Error - Entry %s start bit + length must be less than %d\n",
 | |
| 				ce->name,buffer_bit_size);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		byte=ce->bit/8;
 | |
| 		offset=ce->bit%8;
 | |
| 		byte_length=ce->length/8;
 | |
| 		if(byte_length) {	/* entry is 8 bits long or more */
 | |
| 			if(offset) { /* if 8 bits or more long, it must be byte aligned */
 | |
| 				printf("Error - Entry %s length over 8 must be byte aligned\n",
 | |
| 					ce->name);
 | |
| 				exit(1);
 | |
| 			}
 | |
| 			/* test if entries 8 or more in length are even bytes */ 
 | |
| 			if(ce->length%8){
 | |
|                                 printf("Error - Entry %s length over 8 must be a multiple of 8\n",
 | |
|                                         ce->name);
 | |
|                                 exit(1);
 | |
|                         }
 | |
| 			/* test if any of the bits have been previously used */
 | |
| 			for(;byte_length;byte_length--,byte++) {
 | |
| 				if(test[byte]) {
 | |
|                                     printf("Error - Entry %s uses same bits previously used\n",
 | |
| 						ce->name);
 | |
|                                     exit(1);
 | |
| 	                        }
 | |
| 				test[byte]=clip[8]; /* set the bits defined in test */
 | |
| 			}
 | |
| 		} else {
 | |
| 			/* test if bits overlap byte boundaries */
 | |
| 			if(ce->length>(8-offset)) {
 | |
|                                 printf("Error - Entry %s length overlaps a byte boundry\n",                                        ce->name);
 | |
|                                 exit(1);
 | |
|                         }
 | |
| 			/* test for bits previously used */
 | |
| 			set=(clip[ce->length]<<offset);
 | |
| 			if(test[byte]&set) {
 | |
|                         	printf("Error - Entry %s uses same bits previously used\n",
 | |
|                                                 ce->name);
 | |
|                                 exit(1);
 | |
|                         }
 | |
|                         test[byte]|=set;  /* set the bits defined in test */
 | |
| 		}
 | |
| 	}
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| /* This routine displays the usage options */
 | |
| void display_usage(void)
 | |
| {
 | |
|         printf("Usage build_opt_table [-b] [--option filename]\n");
 | |
|         printf("                [--config filename]\n");
 | |
|         printf("b = build option_table.c\n");
 | |
|         printf("--option = name of option table output file\n");
 | |
|         printf("--config = build the definitions table from the given file\n");
 | |
|         exit(1);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void skip_spaces(char *line, char **ptr)
 | |
| {
 | |
| 	if (!isspace(**ptr)) {
 | |
| 		printf("Error missing whitespace in line\n%s\n", line);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	while(isspace(**ptr)) {
 | |
| 		(*ptr)++;
 | |
| 	}
 | |
| 	return;
 | |
| }
 | |
| static unsigned long get_number(char *line, char **ptr, int base)
 | |
| {
 | |
| 	unsigned long value;
 | |
| 	char *ptr2;
 | |
| 	value = strtoul(*ptr, &ptr2, base);
 | |
| 	if (ptr2 == *ptr) {
 | |
| 		printf("Error missing digits at: \n%s\n in line:\n%s\n", 
 | |
| 			*ptr, line);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	*ptr = ptr2;
 | |
| 	return value;
 | |
| }
 | |
| 
 | |
| /* This routine builds the cmos definition table from the cmos layout file
 | |
| 	input The input comes from the configuration file which contains two parts
 | |
| 		entries and enumerations. Each section is started with the key words
 | |
| 		entries and enumerations.  Records then follow in their respective 
 | |
| 		formats.
 | |
| 	output The output of this program is the cmos definitions table.  It is stored
 | |
| 		in the cmos_table array. If this module is called, and the global 
 | |
| 		table_file has been implimented by the user, the table is also written
 | |
| 		to the specified file.
 | |
| 		This program exits on and error.  It returns a 1 on successful 
 | |
| 		completion
 | |
| */
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
| 	int i;
 | |
| 	char *config=0;
 | |
| 	char *option=0;
 | |
| 	FILE *fp;
 | |
| 	struct cmos_option_table *ct;
 | |
| 	struct cmos_entries *ce;
 | |
| 	struct cmos_enums *c_enums, *c_enums_start;
 | |
| 	struct cmos_checksum *cs;
 | |
| 	unsigned char line[INPUT_LINE_MAX];
 | |
| 	unsigned char uc;
 | |
| 	int entry_mode=0;
 | |
| 	int enum_mode=0;
 | |
| 	int checksum_mode=0;
 | |
| 	int ptr,cnt;
 | |
| 	char *cptr;
 | |
| 	int offset,entry_start;
 | |
| 	int entries_length;
 | |
| 	int enum_length;
 | |
| 	int len;
 | |
| 	unsigned char buf[16];
 | |
| 
 | |
|         for(i=1;i<argc;i++) {
 | |
|                 if(argv[i][0]!='-') {
 | |
|                         display_usage();
 | |
|                 }
 | |
|                 switch(argv[i][1]) {
 | |
|                         case 'b':       /* build the table */
 | |
|                                 break;
 | |
|                         case '-':       /* data is requested from a file */
 | |
|                                 switch(argv[i][2]) {
 | |
|                                         case 'c':  /* use a configuration file */
 | |
|                                                 if(strcmp(&argv[i][2],"config")) {
 | |
|                                                         display_usage();
 | |
|                                                 }
 | |
|                                                 config=argv[++i];
 | |
|                                                 break;
 | |
|                                         case 'o':  /* use a cmos definitions table file */
 | |
|                                                 if(strcmp(&argv[i][2],"option")) {
 | |
|                                                         display_usage();
 | |
|                                                 }
 | |
|                                                 option=argv[++i];
 | |
|                                                 break;
 | |
|                                         default:
 | |
|                                                 display_usage();
 | |
|                                                 break;
 | |
|                                 }
 | |
|                                 break;
 | |
|                         default:
 | |
|                                 display_usage();
 | |
|                                 break;
 | |
|                 }
 | |
|         }
 | |
| 
 | |
| 
 | |
| 	/* Has the user specified a configuration file */
 | |
| 	if(config) {	/* if yes, open it */
 | |
| 		if((fp=fopen(config,"r"))==NULL){
 | |
| 			printf("Error - Can not open config file %s\n",config);
 | |
| 			exit(1);  /* exit if it can not be opened */
 | |
| 		}
 | |
| 	}
 | |
| 	else {  /* no configuration file specified, so try the default */
 | |
| 		if((fp=fopen("cmos.layout","r"))==NULL){
 | |
| 			printf("Error - Can not open cmos.layout\n");
 | |
| 			exit(1);  /* end of no configuration file is found */
 | |
| 		}
 | |
| 	}
 | |
| 	/* type cast a pointer, so we can us the structure */
 | |
| 	ct=(struct cmos_option_table*)cmos_table;
 | |
| 	/* start the table with the type signature */
 | |
| 	ct->tag = LB_TAG_CMOS_OPTION_TABLE;
 | |
| 	/* put in the header length */
 | |
| 	ct->header_length=sizeof(*ct);
 | |
| 
 | |
| 	/* Get the entry records */
 | |
| 	ce=(struct cmos_entries*)(cmos_table+(ct->header_length));
 | |
| 	cptr = (char*)ce;
 | |
| 	for(;;){  /* this section loops through the entry records */
 | |
| 		if(fgets(line,INPUT_LINE_MAX,fp)==NULL) 
 | |
| 			break; /* end if no more input */
 | |
| 		if(!entry_mode) {  /* skip input until the entries key word */
 | |
| 			if (strstr(line,"entries") != 0) {
 | |
| 				entry_mode=1;
 | |
| 				continue;
 | |
| 			}
 | |
| 		}
 | |
| 		else{  /* Test if we are done with entries and starting enumerations */
 | |
| 			if (strstr(line,"enumerations") != 0){
 | |
| 				entry_mode=0;
 | |
| 				enum_mode=1;
 | |
| 				break;
 | |
| 			}
 | |
| 			if (strstr(line, "checksums") != 0) {
 | |
| 				enum_mode=0;
 | |
| 				checksum_mode=1;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* skip commented and blank lines */
 | |
| 		if(line[0]=='#') continue;
 | |
| 		if(line[strspn(line," ")]=='\n') continue;
 | |
| 		/* scan in the input data */
 | |
| 		sscanf(line,"%d %d %c %d %s",
 | |
| 			&ce->bit,&ce->length,&uc,&ce->config_id,&ce->name[0]);
 | |
| 		ce->config=(int)uc;
 | |
| 		/* check bit and length ranges */
 | |
| 		if(ce->bit>(CMOS_IMAGE_BUFFER_SIZE*8)) {
 | |
|                         printf("Error - bit is to big in line \n%s\n",line);
 | |
|                         exit(1);
 | |
|                 }
 | |
| 		if((ce->length>(MAX_VALUE_BYTE_LENGTH*8))&&(uc!='r')) {
 | |
| 			printf("Error - Length is to long in line \n%s\n",line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		/* put in the record type */
 | |
| 		ce->tag=LB_TAG_OPTION;
 | |
| 		/* calculate and save the record length */
 | |
| 		len=strlen(ce->name)+1;
 | |
| 		/* make the record int aligned */
 | |
| 		if(len%4)
 | |
| 			len+=(4-(len%4));
 | |
| 		ce->size=sizeof(struct cmos_entries)-32+len;
 | |
| 		cptr = (char*)ce;
 | |
| 		cptr+=ce->size;  /* increment to the next table position */
 | |
| 		ce = (struct cmos_entries*) cptr;
 | |
| 	}
 | |
| 
 | |
| 	/* put the length of the entries into the header section */
 | |
| 	entries_length=(int)cptr;
 | |
| 	entries_length-=(int)(cmos_table+ct->header_length);
 | |
| 
 | |
| 	/* compute the start of the enumerations section */
 | |
| 	entry_start=(int)cmos_table;
 | |
| 	entry_start+=ct->header_length;
 | |
| 	offset=entry_start+entries_length;
 | |
| 	c_enums_start=c_enums=(struct cmos_enums*)offset;
 | |
|   	/* test for overlaps in the entry records */
 | |
| 	test_for_entry_overlaps(entry_start,offset);
 | |
| 
 | |
| 	for(;enum_mode;){ /* loop to build the enumerations section */
 | |
| 		if(fgets(line,INPUT_LINE_MAX,fp)==NULL) 
 | |
| 			break; /* go till end of input */
 | |
| 
 | |
| 		if (strstr(line, "checksums") != 0) {
 | |
| 			enum_mode=0;
 | |
| 			checksum_mode=1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		/* skip commented and blank lines */
 | |
| 		if(line[0]=='#') continue;
 | |
| 		if(line[strspn(line," ")]=='\n') continue;
 | |
| 
 | |
| 		/* scan in the data */
 | |
| 		for(ptr=0;(line[ptr]==' ')||(line[ptr]=='\t');ptr++);
 | |
| 		c_enums->config_id=strtol(&line[ptr],(char**)NULL,10);
 | |
| 		for(;(line[ptr]!=' ')&&(line[ptr]!='\t');ptr++);
 | |
| 		for(;(line[ptr]==' ')||(line[ptr]=='\t');ptr++);
 | |
| 		c_enums->value=strtol(&line[ptr],(char**)NULL,10);
 | |
| 		for(;(line[ptr]!=' ')&&(line[ptr]!='\t');ptr++);
 | |
| 		for(;(line[ptr]==' ')||(line[ptr]=='\t');ptr++);
 | |
| 		for(cnt=0;(line[ptr]!='\n')&&(cnt<31);ptr++,cnt++)
 | |
| 			c_enums->text[cnt]=line[ptr];
 | |
| 		c_enums->text[cnt]=0;
 | |
| 	
 | |
| 		/* make the record int aligned */
 | |
| 		cnt++;
 | |
| 		if(cnt%4)
 | |
| 			cnt+=4-(cnt%4);
 | |
| 		/* store the record length */
 | |
| 		c_enums->size=((int)&c_enums->text[cnt])-(int)c_enums;
 | |
| 		/* store the record type */
 | |
| 		c_enums->tag=LB_TAG_OPTION_ENUM;
 | |
| 		/* increment to the next record */
 | |
| 		c_enums=(struct cmos_enums*)&c_enums->text[cnt];
 | |
| 	}
 | |
| 	/* save the enumerations length */
 | |
| 	enum_length=(int)c_enums-(int)c_enums_start;
 | |
| 	ct->size=ct->header_length+enum_length+entries_length;
 | |
| 
 | |
| 	/* Get the checksum records */
 | |
| 	cs=(struct cmos_checksum *)(cmos_table+(ct->size));
 | |
| 	cptr = (char*)cs;
 | |
| 	for(;checksum_mode;) { /* This section finds the checksums */
 | |
| 		char *ptr;
 | |
| 		if(fgets(line, INPUT_LINE_MAX,fp)==NULL)
 | |
| 			break; /* end if no more input */
 | |
| 
 | |
| 		/* skip commented and blank lines */
 | |
| 		if (line[0]=='#') continue;
 | |
| 		if (line[strspn(line, " ")]=='\n') continue;
 | |
| 		if (memcmp(line, "checksum", 8) != 0) continue;
 | |
| 
 | |
| 		/* get the information */
 | |
| 		ptr = line + 8;
 | |
| 		skip_spaces(line, &ptr);
 | |
| 		cs->range_start = get_number(line, &ptr, 10);
 | |
| 
 | |
| 		skip_spaces(line, &ptr);
 | |
| 		cs->range_end = get_number(line, &ptr, 10);
 | |
| 
 | |
| 		skip_spaces(line, &ptr);
 | |
| 		cs->location = get_number(line, &ptr, 10);
 | |
| 		
 | |
| 		/* Make certain there are spaces until the end of the line */
 | |
| 		skip_spaces(line, &ptr);
 | |
| 
 | |
| 		if ((cs->range_start%8) != 0) {
 | |
| 			printf("Error - range start is not byte aligned in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if (cs->range_start >= (CMOS_IMAGE_BUFFER_SIZE*8)) {
 | |
| 			printf("Error - range start is to big in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if ((cs->range_end%8) != 7) {
 | |
| 			printf("Error - range end is not byte aligned in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if ((cs->range_end) >= (CMOS_IMAGE_BUFFER_SIZE*8)) {
 | |
| 			printf("Error - range end is to long in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if ((cs->location%8) != 0) {
 | |
| 			printf("Error - location is not byte aligned in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if ((cs->location >= (CMOS_IMAGE_BUFFER_SIZE*8)) ||
 | |
| 			((cs->location + 16) > (CMOS_IMAGE_BUFFER_SIZE*8))) 
 | |
| 		{
 | |
| 			printf("Error - location is to big in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		/* And since we are not ready to be fully general purpose yet.. */
 | |
| 		if ((cs->range_start/8) != LB_CKS_RANGE_START) {
 | |
| 			printf("Error - Range start(%d) does not match define(%d) in line\n%s\n", 
 | |
| 				cs->range_start/8, LB_CKS_RANGE_START, line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if ((cs->range_end/8) != LB_CKS_RANGE_END) {
 | |
| 			printf("Error - Range end does not match define in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if ((cs->location/8) != LB_CKS_LOC) {
 | |
| 			printf("Error - Location does not match define in line\n%s\n", line);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 
 | |
| 		cs->tag = LB_TAG_OPTION_CHECKSUM;
 | |
| 		cs->size = sizeof(*cs);
 | |
| 		cs->type = CHECKSUM_PCBIOS;
 | |
| 		cptr = (char *)cs;
 | |
| 		cptr += cs->size;
 | |
| 		cs = (struct cmos_checksum *)cptr;
 | |
| 
 | |
| 	}
 | |
| 	ct->size += (cptr - (char *)(cmos_table + ct->size));
 | |
| 	fclose(fp);
 | |
| 
 | |
| 	/* test if an alternate file is to be created */
 | |
| 	if(option) {
 | |
| 		if((fp=fopen(option,"w"))==NULL){
 | |
|                         printf("Error - Can not open %s\n",option);
 | |
|                         exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	else {  /* no, so use the default option_table.c */
 | |
|                 if((fp=fopen("option_table.c","w"))==NULL){
 | |
|                         printf("Error - Can not open option_table.c\n");
 | |
|                         exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	/* write the header */
 | |
|         if(!fwrite("unsigned char option_table[] = {",1,32,fp)) {
 | |
|                 printf("Error - Could not write image file\n");
 | |
|                 fclose(fp);
 | |
|                 exit(1);
 | |
|         }
 | |
| 	/* write the array values */
 | |
| 	for(i=0;i<(ct->size-1);i++) {
 | |
| 		if(!(i%10)) fwrite("\n\t",1,2,fp);
 | |
| 		sprintf(buf,"0x%02x,",cmos_table[i]);
 | |
| 		fwrite(buf,1,5,fp);
 | |
| 	}
 | |
| 	/* write the end */
 | |
| 	sprintf(buf,"0x%02x",cmos_table[i]);
 | |
| 	fwrite(buf,1,4,fp);
 | |
|         if(!fwrite("};\n",1,3,fp)) {
 | |
|                 printf("Error - Could not write image file\n");
 | |
|                 fclose(fp);
 | |
|                 exit(1);
 | |
|         }
 | |
| 
 | |
|         fclose(fp);
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| 
 |