龙芯2k1000-pmon(2)- envinit函数注释学习

这是第二篇了,感觉顺序还是很重要,那我稍微整理说明一下。

我的顺序是从main.c(pmon/common/main.c) 813行左右

void dbginit (char *adr) 函数开始的。

至于为什么,因为前面看了一截,又没有写笔记,所以就从这开始呗,后面再追加一下前面的部分吧。

第一篇讲的就是__init函数的执行部分。(还不完整)

第二篇,就是这篇,那就envinit吧,

一、这是环境变量初始化函数。这篇讲的是18行的envinit函数

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 终端实际打印的内容,后面分析代码的时候可以参考一下。

龙芯2k1000-pmon(2)- envinit函数注释学习_第1张图片

1.2 发现有两个文件有这个envinit函数,那到底是哪个文件生效了呢?

龙芯2k1000-pmon(2)- envinit函数注释学习_第2张图片

 1.3 很简单,看看哪个文件被编译为.o文件了,使用find的查找一下呗

龙芯2k1000-pmon(2)- envinit函数注释学习_第3张图片

 这就很明显了,env.c被编译了,而set.c并没有被使用。

那就找env.c了。

1.4 找到envinit函数

龙芯2k1000-pmon(2)- envinit函数注释学习_第4张图片

 这个函数中,有一个函数tgt_mapenv,参数是个函数指针

1.5 先看这个函数指针,这个函数,我已经做了一点注释。

首先明确一点,有两个全局变量:

stdenvtab 全局静态变量,并且做了初始化,理解为是一个标准环境变量

envvar  用于保存所有环境变量的数组,包括之前的标准环境变量,也会转化到这个数组中(后面有相关代码)

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

    if ((sp = getstdenv (name)) != 0) { //使用名字查询,是否在标准环境变量表中,不存在返回0
	if (sp-> chgfunc && !(*sp->chgfunc) (name, value)) //如果在标准表中,看这个是否又对应的函数,有则调用该函数
	    return 0;
	if (sp->values && _matchval (sp, value) < 0 && envinited) {//如果存在,则看原来的值是不是不存在
	    printf ("%s: bad %s value, try [%s]\n", value, name, sp->values);  //不存在则返回0
	    return 0;
	}
    }

    for (ep = envvar; ep < &envvar[NVAR]; ep++) {  //在环境变量数组中找
	if (!ep->name && !bp)
	  bp = ep;
	else if (ep->name && striequ (name, ep->name))  //有相同的就退出循环
	  break;
    }
    
    if (ep < &envvar[NVAR]) {   //有相同的,是break出的循环
	/* must have got a match, free old value */
	if (ep->value) {
	    free (ep->value); ep->value = 0;  //释放原来的空间
	}
    } else if (bp) { //没有相同的,存在有envvar数组中第一个空白的位置
	/* new entry */
	ep = bp;
	if (!(ep->name = malloc (strlen (name) + 1)))  //分配空间
	  return 0;
	strcpy (ep->name, name);  //数据拷贝到空间中
    } else {  //envvar数组中没有找到空白的位置,已经填满了
	return 0;
    }

    if (value) { //value存在
	if (!(ep->value = malloc (strlen (value) + 1))) {  //分配空间
	    free (ep->name); ep->name = 0;
	    return 0;
	}
	strcpy (ep->value, value);  
    }

    return 1;
}

代码我就不多解释了。

二、正式开始分析tgt_mapenv函数,又有两个同名函数,这里我们分析tgt_machdep.c(Targets/LS2K/ls2k/)中的

以下是代码,我写了部分注释。这里可以结合上述1.1的打印的结果看。

首先 in envinit 被打印出来了

nvram = ... 也打印出来了

主要是这个if语句,看结果打印呢,是执行了else语句的,那就来分析一下。

/*
 *  Read in environment from NV-ram and set.
 */
void tgt_mapenv(int (*func) __P((char *, char *)))  //参数名注意一下,后面多次用到,就是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    //从打印的结果看,这个宏定义被定义了
	nvram = (char *)(tgt_flashmap())->fl_map_base;
	printf("nvram=%08x\n", (unsigned int)nvram);   //这句被打印了
	if (fl_devident(nvram, NULL) == 0 ||  //返回值不是0
	    cksum(nvram + NVRAM_OFFS, NVRAM_SIZE, 0) != 0) { //返回值为0,if条件不成立
#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;   //执行这个语句
		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 按照打印的情况,代码执行了哪里还是比较容易识别,if是不成立的,但是要看一下执行的结果如何。

|| 或操作,第一个条件不成立,才会执行第二个表达式。

龙芯2k1000-pmon(2)- envinit函数注释学习_第5张图片

 m参数得到的值是NULL,所以直接返回一个全局变量的地址,那结果肯定不等于0

2.2 所以if的第一个条件是不成立的,那第二个条件需要被执行

龙芯2k1000-pmon(2)- envinit函数注释学习_第6张图片

2.3 第二个条件,cksum 这里我就比较纠结了,看代码执行的情况,返回的结果应该是0.

 看代码,第三个参数set得到的是0.

第一个参数,看打印的结果是bfcff000

第二个参数,是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);
}

代码中两个if语句不执行,因为set=0.(参数传入时是0)

while语句要执行,sum是加法操作,这里可以看成是高8位和第8位各自相加.

得到的结果直接返回return(sum) 

2.4 这个结果怎么会等于0呢?(看打印的情况,if条件确实是不成立呀)

除非,这片区域全部为0,但是后面的代码又在开始分析这一段,而且还解析出来环境变量。

那,这一段在保存的时候,应该就是使用了校验和的方法保存的。

看这地址是在flash中,啥时候保存了这样一段数据,还计算了校验和呢?

真是把我迷惑了。

搜索这个环境变量呗,FR,搜索到的其他结果好像都没关,那就是这个makefile

没想到居然又翻到了makefile

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

目标是dtb,这个确实是编译了这个目标。

看第三句

-f 存在,如果存在gzrom.bin 然后cp 然后执行python pmonenv.py 

居然还有python文件。。。。。。。

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

跟打印的结果一对比,果然是这几个呀!!!!

2.5 看一眼python文件吧。(半吊子水准,请见谅)

源代码,一会增加几行打印信息,就不去猜了。(增加中文后居然执行不了!!!!

"""
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)  # 设置文件偏移地址 从文件开始位置偏移foff
    a=f.read(fsz);  # 读取文件的内容,长度fsz
    a=a.ljust(fsz,b'\x00')  # a的内容左对齐,长度为fsz,不够的地方使用"\0"填充
    f.close()  # 关闭文件
    d={}
    b = struct.unpack('!'+str(len(a)//2)+'H',a) # 按照参数1的格式解包为结构体成员 !表示网络字节序,H表示short类型2字节,len(a)/2,表示总字节数除2
    if(sum(b)&0xffff):  # 校验和不为0 出错
     print('checksum error, rebuild env')
     t = argv
    else:
     e = a[2:].find(b'\x00\x00')  # 找到第二个字节开始,出现两个‘\0’的地方
     t = a[2:2+e].split(b'\x00')+list(map(lambda x:x.encode('utf8'),argv))  # 转utf-8编码
    for i in t:   # 对每一项进行操作
       a=i.split(b'=',1) # 找到等号分开 
       if len(a) > 1:    # 多余一项就是设置值
         d[a[0]] = a[1]   # 内容写到字典中,
       elif a[0] in d:   # 只有一项就是要删除这个值
         del d[a[0]]
    return d

def writeenv(fname,foff,fsz,d):
    
    a=b'\x00\x00'  # a 赋值两个‘\0’
    for i in d.keys():   # i 是key
     a += i+b'='+d[i]+b'\x00' # key = ... '\0'
    a=a.ljust(fsz,b'\x00')   # 不满足fsz长度的话补0
    
    b = struct.pack('!H',(-sum(struct.unpack('!'+str(len(a)//2)+'H',a)))&0xffff)  # 打包,并计算校验和
    a=b+a[2:]
    
    f=open(fname,'rb+')
    f.seek(foff,0)
    f.write(a)  # 把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):
    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  # 偏移地址,默认0x000ff000
    fsz = int(opt['-s'],0) if '-s' in opt else 500  # 数据块的大小,默认500字节
    fname = opt['-f'] if '-f' in opt else 'gzrom.bin'   # 提取文件名
    
    d=readenv(fname,foff,fsz,argv)
    print(d) # 打印字典d
    if '-w' in opt:  # 如果有“-w” 选项
     writeenv(fname,foff,fsz,d)  # 把d写入文件
     if '-b' in opt: writehexenv(fname, opt['-b'])
     if '-d' in opt: writedtb(fname, opt['-d'], foff)
           

加上打印语句试试吧。因为不能有中文注释,那就把注释删掉了。标了几个数字号码。

"""
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 结合编译时打印出来的数据,python代码的分析:

龙芯2k1000-pmon(2)- envinit函数注释学习_第7张图片

 #1 #2 可以看出来,gzrom-dtb.bin是由cp gzrom命令得来的,所以开始是没有数据的。

读到的a是空,长度也是0.

#3 #4 正因为如此,a经过ljust之后,全部填充的是0,b打印出来都是0,长度是250(500/2)

#5 e 结果为0,因为全是0,所以偏移为0,a[2:],是空出来两个字节,用于存放负校验和的值(这个值跟校验和相加等于0,在后面可以看到)

# 6 t是传入的参数,除了选项之外的字符串。这里进行转换为utf-8的编码了。用列表存起来了

# 7  对列表中的值再逐个分离,有=号和没=号的,长度大于1,表示=号两边有值,否则就是只有一个值,两边有值就加入到字典中,,否则的话就是删除对应的那个字典项。

看到这里是个循环,每项都处理了。之前第一项是空的,直接就跳过了。

# 此时,返回了d,这个字典里面保存了参数解析出来的选项,但是没有写到文件中去。

有-w选项 就会调用writeenv函数

#8 这里我们看到了字典的内容

 #9 再次处理为=号的方式,加入到文件中去

注意是追加的方式,看到字符串越来越长了。 注意前面两个字节是0,每一个项的末尾有一个0

# 10 这个地方打印出来的是校验和的负值(算出校验和加个负号),两个字节

 注意 * 号也是一个字节

# 11 看到把校验和组合到a数组的头两个字节中。

 

2.7 但是这里还有一个小问题,前面我们看到代码里面是492个字节,而这个python文件处理的是500个字节,是不是有点问题,问题确实是有问题,但是因为后面的字节都是0,加不加,无所谓啦,反正校验和会正确。

2. 8 这段代码就不用过多解析了

龙芯2k1000-pmon(2)- envinit函数注释学习_第8张图片

 结合下面的打印结果,一起看。

 

2.9、这个部分好像没啥意义,看最后的环境变量

龙芯2k1000-pmon(2)- envinit函数注释学习_第9张图片

 这一段也是设置相关环境变量的值,注意func就是_setenv函数龙芯2k1000-pmon(2)- envinit函数注释学习_第10张图片

 

三、标准环境变量的加入

龙芯2k1000-pmon(2)- envinit函数注释学习_第11张图片

 龙芯2k1000-pmon(2)- envinit函数注释学习_第12张图片

 这段代码就是解析这些默认值了。。。。。

四、最后,看看PMON的环境变量

龙芯2k1000-pmon(2)- envinit函数注释学习_第13张图片

 应该来说每一项都很眼熟了吧。

你可能感兴趣的:(龙芯3A3000-PMON,学习)