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.