Godson 2k1000 PMON envinit function annotation learning

This is the second article. I feel that the order is still very important. Let me sort it out and explain it a little.

My order is from main C (PMON / common / main. C) about 813 lines

The void dbginit (char *adr) function starts.

As for why, because I read a section in front and didn't write notes, so let's start here and add the previous part later.

The first one is about__ The execution part of the init function. (incomplete)

The second one is this one. Let's envinit,

1, This is the environment variable initialization function. This article is about the 18 line envinit function

extern void get_ec_version(void);
/*
 *  PMON2000 entrypoint. Called after initial setup.
 */
void
dbginit (char *adr)
{
	unsigned long long	memsize;
	char	*s;

/*	splhigh();*/

	memsize = memorysize;

	__init();	/* Do all constructor initialisation */

	SBD_DISPLAY ("ENVI", CHKPNT_ENVI);
	envinit ();

#if defined(SMP)
	/* Turn on caches unless opted out */
	if (!getenv("nocache"))
		md_cacheon();
#endif

	SBD_DISPLAY ("SBDD", CHKPNT_SBDD);
	tgt_devinit();
#if defined(LS3A7A_STR) || defined(LS3A2H_STR) || defined(LS2K_STR)
	check_str();
#endif
#ifdef INET
	SBD_DISPLAY ("NETI", CHKPNT_NETI);
	init_net (1);
#endif

#if NCMD_HIST > 0
	SBD_DISPLAY ("HSTI", CHKPNT_HSTI);
	histinit ();
#endif

#if NMOD_SYMBOLS > 0
	SBD_DISPLAY ("SYMI", CHKPNT_SYMI);
	syminit ();
#endif

#ifdef DEMO
	SBD_DISPLAY ("DEMO", CHKPNT_DEMO);
	demoinit ();
#endif

	SBD_DISPLAY ("SBDE", CHKPNT_SBDE);
	initial_sr |= tgt_enable (tgt_getmachtype ());

#ifdef SR_FR
	Status = initial_sr & ~SR_FR; /* don't confuse naive clients */
#endif
	/* Set up initial console terminal state */
	ioctl(STDIN, TCGETA, &consterm);

#ifdef HAVE_LOGO
	tgt_logo();
#else
	printf ("\n * PMON2000 Professional *");
#endif
	printf ("\nConfiguration [%s,%s", TARGETNAME,
			BYTE_ORDER == BIG_ENDIAN ? "EB" : "EL");
#ifdef INET
	printf (",NET");
#endif
#if NSD > 0
	printf (",SCSI");
#endif
#if NWD > 0
	printf (",IDE");
#endif
	printf ("]\nVersion: %s.\n", vers);
	printf ("Supported loaders [%s]\n", getExecString());
	printf ("Supported filesystems [%s]\n", getFSString());
	printf ("This software may be redistributed under the BSD copyright.\n");

		
	print_cpu_info();
	print_mem_freq();

	printf ("Memory size %lld MB .\n", memorysize_total);

	tgt_memprint();
#if defined(SMP)
	tgt_smpstartup();
#endif

	printf ("\n");
	loongson_smbios_init();	
	md_clreg(NULL);
	md_setpc(NULL, (int32_t) CLIENTPC);
	md_setsp(NULL, tgt_clienttos ());
	DevicesInit();
}

1.1 the content actually printed by the terminal can be referred to later when analyzing the code.

1.2 it is found that two files have this envinit function. Which file is effective?

1.3 it's easy to see which file is compiled into o file, use find to find it

That's obvious, env C is compiled, and set C is not used.

Then find env C.

1.4 find the envinit function

In this function, there is a function tgt_mapenv, the parameter is a function pointer

1.5 first look at this function pointer and this function. I have made some comments.

To be clear, there are two global variables:

stdenvtab is a global static variable and initialized. It is understood as a standard environment variable

envvar is an array used to store all environment variables, including the previous standard environment variables, which will also be converted into this array (relevant code will follow)

static int
_setenv (name, value)
    char *name, *value;
{
    struct envpair *ep;
    struct envpair *bp = 0;
    const struct stdenv *sp;

    if ((sp = getstdenv (name)) != 0) { //Use the name to query whether it is in the standard environment variable table. If it does not exist, return 0
	if (sp-> chgfunc && !(*sp->chgfunc) (name, value)) //If there is a corresponding function in the standard table, call the function
	    return 0;
	if (sp->values && _matchval (sp, value) < 0 && envinited) {//If it exists, check whether the original value does not exist
	    printf ("%s: bad %s value, try [%s]\n", value, name, sp->values);  //Returns 0 if it does not exist
	    return 0;
	}
    }

    for (ep = envvar; ep < &envvar[NVAR]; ep++) {  //Found in environment variable array
	if (!ep->name && !bp)
	  bp = ep;
	else if (ep->name && striequ (name, ep->name))  //If there is the same, exit the loop
	  break;
    }
    
    if (ep < &envvar[NVAR]) {   //The same is the cycle of break
	/* must have got a match, free old value */
	if (ep->value) {
	    free (ep->value); ep->value = 0;  //Free up the original space
	}
    } else if (bp) { //There is no same. There is the first blank position in the envvar array
	/* new entry */
	ep = bp;
	if (!(ep->name = malloc (strlen (name) + 1)))  //Allocate space
	  return 0;
	strcpy (ep->name, name);  //Copy data to space
    } else {  //There is no blank space in the envvar array, which has been filled
	return 0;
    }

    if (value) { //value exists
	if (!(ep->value = malloc (strlen (value) + 1))) {  //Allocate space
	    free (ep->name); ep->name = 0;
	    return 0;
	}
	strcpy (ep->value, value);  
    }

    return 1;
}

I won't explain more about the code.

2, Officially start analyzing tgt_mapenv function, there are two functions with the same name. Here we analyze tgt_machdep.c (Targets/LS2K/ls2k /)

I wrote the following comments. This can be seen in combination with the printing results of 1.1 above.

First, in envinit is printed out

nvram = ... Also printed out

It's mainly the if statement. Look at the result printing. The else statement is executed. Let's analyze it.

/*
 *  Read in environment from NV-ram and set.
 */
void tgt_mapenv(int (*func) __P((char *, char *)))  //Note that the parameter name, which is used many times later, is setenv
{
	char *ep;
	char env[512];
	char *nvram;
	int i;

	/*
	 *  Check integrity of the NVRAM env area. If not in order
	 *  initialize it to empty.
	 */
	printf("in envinit\n");
#ifdef NVRAM_IN_FLASH / / from the print result, the macro definition is defined
	nvram = (char *)(tgt_flashmap())->fl_map_base;
	printf("nvram=%08x\n", (unsigned int)nvram);   //This sentence has been printed
	if (fl_devident(nvram, NULL) == 0 ||  //The return value is not 0
	    cksum(nvram + NVRAM_OFFS, NVRAM_SIZE, 0) != 0) { //The return value is 0, and the if condition does not hold
#else
	nvram = (char *)malloc(512);
	nvram_get(nvram);
	if (cksum(nvram, NVRAM_SIZE, 0) != 0) {
#endif
		printf("NVRAM is invalid!\n");
		nvram_invalid = 1;
	} else {
#ifdef NVRAM_IN_FLASH
		nvram += NVRAM_OFFS;   //Execute this statement
		ep = nvram + 2;;

		while (*ep != 0) {
			char *val = 0, *p = env;
			i = 0;
			while ((*p++ = *ep++) && (ep <= nvram + NVRAM_SIZE - 1)
			       && i++ < 255) {
				if ((*(p - 1) == '=') && (val == NULL)) {
					*(p - 1) = '\0';
					val = p;
				}
			}
			if (ep <= nvram + NVRAM_SIZE - 1 && i < 255) {
				(*func) (env, val);
				printf("2022-03-03 : env = %s val = %s ep = %x i = %d\n", env, val,ep,i);
			} else {
				nvram_invalid = 2;
				printf("2022-03-03 : nvram_invalid ep = %x i = %d\n", ep,i);
				break;
			}
		}
#endif
	}

	printf("NVRAM@%x\n", (u_int32_t) nvram);
#ifdef NVRAM_IN_FLASH
	printf("ACTIVECOM_OFFS = %d, = 0x%x\n", ACTIVECOM_OFFS, ACTIVECOM_OFFS);
	printf("MASTER_BRIDGE_OFFS = %d, = 0x%x\n", MASTER_BRIDGE_OFFS,
	       MASTER_BRIDGE_OFFS);
	printf("before :activecom = %d. em_enable = %d\n", activecom,
	       em_enable);
//      printf("nuram[MASTER_BRIDGE_OFFS] = %d.\n" nvram[MASTER_BRIDGE_OFFS]);
	if (!nvram_invalid)
		bcopy(&nvram[ACTIVECOM_OFFS], &activecom, 1);
	else
		activecom = 3 /*1 */ ;
	sprintf(env, "0x%02x", activecom);
	(*func) ("activecom", env);	/*tangyt */

	if (!nvram_invalid)
		bcopy(&nvram[MASTER_BRIDGE_OFFS], &em_enable, 1);
	else
		em_enable = 3 /*1 */ ;
	sprintf(env, "0x%02x", em_enable);
	(*func) ("em_enable", env);	/*tangyt */

	printf("activecom = %d.   em_enable = %d.\n", activecom, em_enable);
#endif
	/*
	 *  Ethernet address for Galileo ethernet is stored in the last
	 *  six bytes of nvram storage. Set environment to it.
	 */

	bcopy(&nvram[ETHER_OFFS], hwethadr, 6);
	sprintf(env, "%02x:%02x:%02x:%02x:%02x:%02x", hwethadr[0], hwethadr[1],
		hwethadr[2], hwethadr[3], hwethadr[4], hwethadr[5]);
	(*func) ("ethaddr", env);

#ifndef NVRAM_IN_FLASH
	free(nvram);
#endif

#ifdef no_thank_you
	(*func) ("vxWorks", env);
#endif

	sprintf(env, "%d", memorysize / (1024 * 1024));
	(*func) ("memsize", env);

	sprintf(env, "%d", memorysize_high / (1024 * 1024));
	(*func) ("highmemsize", env);

	sprintf(env, "%d", md_pipefreq);
	(*func) ("cpuclock", env);

	sprintf(env, "%d", md_cpufreq);
	(*func) ("busclock", env);

	(*func) ("systype", SYSTYPE);

}

2.1 according to the printing situation, it is easy to identify where the code is executed. if is not tenable, but it depends on the execution result.

||Or operation. The second expression will be executed only if the first condition is not true.

The value obtained by the m parameter is NULL, so the address of a global variable is directly returned, and the result must not be equal to 0

2.2 therefore, the first condition of if is not tenable, and the second condition needs to be executed

2.3 the second condition, cksum, is more tangled here. Depending on the code execution, the returned result should be 0.5

Look at the code. The third parameter set gets 0

The first parameter is bfcff000

The second parameter is 492

/*
 *  Calculate checksum. If 'set' checksum is calculated and set.
 */
static int cksum(void *p, size_t s, int set)
{
	u_int16_t sum = 0;
	u_int8_t *sp = p;
	int sz = s / 2;

	if (set) {
		*sp = 0;	/* Clear checksum */
		*(sp + 1) = 0;	/* Clear checksum */
	}
	while (sz--) {
		sum += (*sp++) << 8;
		sum += *sp++;
	}
	if (set) {
		sum = -sum;
		*(u_int8_t *) p = sum >> 8;
		*((u_int8_t *) p + 1) = sum;
	}
	return (sum);
}

Two if statements in the code are not executed because set=0 (0 when the parameter is passed in)

The while statement is to be executed. sum is an addition operation, which can be regarded as the addition of the upper 8 bits and the 8th bit respectively

The obtained result is returned directly to return (sum)

2.4 how can this result be equal to 0? (according to the printing situation, the if condition is really not tenable)

Unless, this area is all 0, but the following code starts to analyze this section again, and also resolves the environment variables.

Well, when saving this paragraph, it should be saved by using the checksum method.

Look, the address is in flash. When did you save such a piece of data and calculate the checksum?

It really confused me.

Searching for this environment variable, FR, seems to have nothing to do with other search results, that is, this makefile

Unexpectedly, I turned to makefile again

dtb:
	make -C ../Targets/${TARGET}/compile/${TARGETEL}/ DTB_O=`pwd`/${TARGET}.dtb.i DTB_I=`pwd`/../Targets/${TARGET}/conf/${TARGET}.dts dtb
	./dtc -I dts -O dtb -o ${TARGET}.dtb  ${TARGET}.dtb.i
	[ -f gzrom.bin ] && cp gzrom.bin gzrom-dtb.bin && python ../tools/pmonenv.py -f gzrom-dtb.bin -d ${TARGET}.dtb -w  al=\(usb0,0\)/boot/vmlinuz al1=\(wd0,0\)/bspls2k.elf append="'console=ttyS0,115200 console=tty initcall_debug=1 loglevel=20'" FR=1

The target is dtb, which is indeed compiled.

Look at the third sentence

-f exists, if it exists gzrom Bin then cp, and then execute Python pmonenv py 

There are python files.......

python ../tools/pmonenv.py -f gzrom-dtb.bin -d ${TARGET}.dtb -w  al=\(usb0,0\)/boot/vmlinuz al1=\(wd0,0\)/bspls2k.elf append="'console=ttyS0,115200 console=tty initcall_debug=1 loglevel=20'" FR=1

Compared with the printed results, these are indeed the ones!!!!

2.5 take a look at python files. (half standard, please forgive me)

Source code, add a few lines of print information later, and don't guess. (can't execute after adding Chinese!!!!!)

"""
python pmonenv.py -f gzrom.bin -o 0x70000 -s 512 al=/dev/mtd0 append="'root=/dev/mtdblock0'"
python ../tools/pmonenv.py -f gzrom-dtb.bin -d ls2k.dtb -w  al=/dev/ram@p0x110000000 al1=/dev/ram@p0x110000000 append="'console=ttyS0,115200 initcall_debug=1 loglevel=20 nosmp'" FR=1
"""
import struct
import sys
import getopt
def readenv(fname,foff,fsz,argv):
    f=open(fname,'rb+') # Open the file in binary mode
    f.seek(foff,0)  # Set the file offset address to offset foff from the file start position
    a=f.read(fsz);  # Read the contents of the file, length fsz
    a=a.ljust(fsz,b'\x00')  # The contents of a are aligned to the left, and the length is fsz. If not enough, use "\ 0" to fill in
    f.close()  # Close file
    d={}
    b = struct.unpack('!'+str(len(a)//2)+'H',a) # unpack as structure member according to the format of parameter 1! Indicates the network byte order, H indicates 2 bytes of short type, len(a)/2 indicates the total number of bytes divided by 2
    if(sum(b)&0xffff):  # Error when checksum is not 0
     print('checksum error, rebuild env')
     t = argv
    else:
     e = a[2:].find(b'\x00\x00')  # Find the beginning of the second byte, where two '\ 0' appear
     t = a[2:2+e].split(b'\x00')+list(map(lambda x:x.encode('utf8'),argv))  # Transcoding to utf-8
    for i in t:   # Operate on each item
       a=i.split(b'=',1) # Find the equal sign to separate 
       if len(a) > 1:    # The extra item is the setting value
         d[a[0]] = a[1]   # The content is written in the dictionary,
       elif a[0] in d:   # Only one thing is to delete this value
         del d[a[0]]
    return d

def writeenv(fname,foff,fsz,d):
    
    a=b'\x00\x00'  # a assigns two '\ 0'
    for i in d.keys():   # i is key
     a += i+b'='+d[i]+b'\x00' # key = ... '\0'
    a=a.ljust(fsz,b'\x00')   # If the length of fsz is not satisfied, 0 is added
    
    b = struct.pack('!H',(-sum(struct.unpack('!'+str(len(a)//2) + 'H', a)) &0xffff) # package and calculate the checksum
    a=b+a[2:]
    
    f=open(fname,'rb+')
    f.seek(foff,0)
    f.write(a)  # Write the contents of a to the file
    f.close()

def writehexenv(fname,hexbin):
    f=open(fname,'rb+')
    f.seek(foff+fsz, 0)
    f.write('\xff'*256)
    for b in hexbin.split(','):
      i,v = b.split(':')
      f.seek(foff+int(i,0),0)
      f.write(v.decode('hex'))
    f.close()

def writedtb(fname,dtb,foff):
    f=open(dtb,'rb')
    a=f.read();
    f.close()
    a=a.ljust(0x4000-8,'\x00')
    b = struct.pack('I',(-sum(struct.unpack(''+str(len(a)//4)+'I',a)))&0xffffffff)
    a=b+a+b
    
    f=open(fname,'rb+')
    f.seek(foff-0x4000,0)
    f.write(a)
    f.close()
    
if __name__ == '__main__':
    opt,argv=getopt.getopt(sys.argv[1:],'b:o:s:f:wd:')
    opt=dict(opt)
    foff = int(opt['-o'],0) if '-o' in opt  else 0x000ff000  # Offset address, default 0x000ff000
    fsz = int(opt['-s'],0) if '-s' in opt else 500  # Default data block size, 500 bytes
    fname = opt['-f'] if '-f' in opt else 'gzrom.bin'   # Extract file name
    
    d=readenv(fname,foff,fsz,argv)
    print(d) # Print dictionary d
    if '-w' in opt:  # If there is "- w" option
     writeenv(fname,foff,fsz,d)  # Write d to file
     if '-b' in opt: writehexenv(fname, opt['-b'])
     if '-d' in opt: writedtb(fname, opt['-d'], foff)
           

Try printing sentences. Because there can be no Chinese notes, delete the notes. Marked with several numbers.

"""
python pmonenv.py -f gzrom.bin -o 0x70000 -s 512 al=/dev/mtd0 append="'root=/dev/mtdblock0'"
python ../tools/pmonenv.py -f gzrom-dtb.bin -d ls2k.dtb -w  al=/dev/ram@p0x110000000 al1=/dev/ram@p0x110000000 append="'console=ttyS0,115200 initcall_debug=1 loglevel=20 nosmp'" FR=1
"""
import struct
import sys
import getopt
def readenv(fname,foff,fsz,argv):
	f=open(fname,'rb+')  
	f.seek(foff,0) 
	a=f.read(fsz) 
	print("1 a read = ",a)   # 1
	print("a.len = ",len(a))   # 2 
	a=a.ljust(fsz,b'\x00')    
	f.close()   
	d={}
	b = struct.unpack('!'+str(len(a)//2)+'H',a)  
	print("3 b = ",b)  # 3
	print("b.len = ",len(b))   # 4
	if(sum(b)&0xffff):  
		print('checksum error, rebuild env')
		t = argv
	else:
		e = a[2:].find(b'\x00\x00')  
		print("4 e = ",e)    # 5
		t = a[2:2+e].split(b'\x00')+list(map(lambda x:x.encode('utf8'),argv))   
		print("5 t = ",t)   # 6
	print("\n for start*******************")
	for i in t:
		a=i.split(b'=',1)  
		print("6 a = ",a)   # 7
		if len(a) > 1:     
			d[a[0]] = a[1]   
		elif a[0] in d:   
			del d[a[0]]
	print("for end*******************\n")
	return d

def writeenv(fname,foff,fsz,d):
	print("\nwriteenv start*******************")
	print("7 d = ",d)   # 8
	a=b'\x00\x00'   
	for i in d.keys():   
		a += i+b'='+d[i]+b'\x00' 
		print("8 a = ",a)   # 9
	a=a.ljust(fsz,b'\x00')   
    
	b = struct.pack('!H',(-sum(struct.unpack('!'+str(len(a)//2)+'H',a)))&0xffff)
	print("9 b = ",b)   # 10
	a=b+a[2:]
	print("10 a = ",a)   # 11
	f=open(fname,'rb+')
	f.seek(foff,0)
	f.write(a)  
	f.close()

def writehexenv(fname,hexbin):
	f=open(fname,'rb+')
	f.seek(foff+fsz, 0)
	f.write('\xff'*256)
	for b in hexbin.split(','):
		i,v = b.split(':')
		f.seek(foff+int(i,0),0)
		f.write(v.decode('hex'))
	f.close()

def writedtb(fname,dtb,foff):
	print("\n writedtb start----------------------")
	f=open(dtb,'rb')
	a=f.read();
	print("a.len= ",len(a))  # 12
	f.close()
	a=a.ljust(0x4000-8,'\x00')
	# print("a = ",a)
	b = struct.pack('I',(-sum(struct.unpack(''+str(len(a)//4)+'I',a)))&0xffffffff)
	a=b+a+b
	print("a.len= ",len(a))  # 13
	# print("a = ",a)

	f=open(fname,'rb+')
	f.seek(foff-0x4000,0)
	f.write(a)
	f.close()
    
if __name__ == '__main__':
	opt,argv=getopt.getopt(sys.argv[1:],'b:o:s:f:wd:')
	opt=dict(opt)
	foff = int(opt['-o'],0) if '-o' in opt  else 0x000ff000  
	fsz = int(opt['-s'],0) if '-s' in opt else 500  
	fname = opt['-f'] if '-f' in opt else 'gzrom.bin' 
    
	d=readenv(fname,foff,fsz,argv)
	print(d) 
	if '-w' in opt:  
		writeenv(fname,foff,fsz,d)   
		if '-b' in opt: writehexenv(fname, opt['-b'])
		if '-d' in opt: writedtb(fname, opt['-d'], foff)
           


2.6 analysis of python code in combination with the data printed during compilation:

#1 #2 as you can see, gzrom DTB Bin is obtained from the cp gzrom command, so there is no data at first.

The read a is empty, and the length is 0

#3 #4 precisely because of this, a is filled with 0 after ljust, b is printed with 0, and the length is 250 (500 / 2)

#5. The result of E is 0. Because it is all 0, the offset is 0. a[2:] is two bytes left blank for storing the value of negative checksum (the value added with checksum equals 0, which can be seen later)

#6 t is the passed in parameter, except for the string of options. Here, the code is converted to utf-8. Saved it with a list

#7. Separate the values in the list one by one. If there is a = sign and no = sign, the length is greater than 1, indicating that there are values on both sides of the = sign. Otherwise, there is only one value, and the values on both sides are added to the dictionary. Otherwise, the corresponding dictionary item is deleted.

See here is a loop, each item has been processed. The first item was empty before, so I skipped it directly.

#At this time, d is returned. The dictionary stores the options parsed by the parameters, but it is not written to the file.

With the - w option, the writeenv function is called

#Here we see the contents of the dictionary

#9 once again, add it to the file in the way of = sign

Note that it is the way of appending. You can see that the string is getting longer and longer. Note that the first two bytes are 0, and there is a 0 at the end of each item

#10 what is printed out here is the negative value of the checksum (calculate the checksum and add a negative sign), two bytes

Note that the * sign is also a byte

#11 see that the checksum is combined into the first two bytes of the a array.

 

2.7 but there is another small problem here. We saw that there are 492 bytes in the code, while the python file processes 500 bytes. Is there a problem? The problem is indeed a problem, but because the following bytes are 0, it doesn't matter whether they are added or not. The inverse checksum will be correct.

2.8 this code does not need to be parsed too much

Combined with the print results below, let's see together.

 

2.9. This part seems meaningless. Look at the final environmental variables

This paragraph is also used to set the values of relevant environment variables. Note that func is_ setenv function

 

3, Addition of standard environment variables

 

This code parses these default values.....

4, Finally, look at the environment variables of PMON

It should be said that every item looks familiar.

Added by Dark_Archon on Fri, 04 Mar 2022 17:49:58 +0200