这是第二篇了,感觉顺序还是很重要,那我稍微整理说明一下。
我的顺序是从main.c(pmon/common/main.c) 813行左右
void dbginit (char *adr) 函数开始的。
至于为什么,因为前面看了一截,又没有写笔记,所以就从这开始呗,后面再追加一下前面的部分吧。
第一篇讲的就是__init函数的执行部分。(还不完整)
第二篇,就是这篇,那就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();
}
这就很明显了,env.c被编译了,而set.c并没有被使用。
那就找env.c了。
这个函数中,有一个函数tgt_mapenv,参数是个函数指针
首先明确一点,有两个全局变量:
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;
}
代码我就不多解释了。
以下是代码,我写了部分注释。这里可以结合上述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);
}
|| 或操作,第一个条件不成立,才会执行第二个表达式。
m参数得到的值是NULL,所以直接返回一个全局变量的地址,那结果肯定不等于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)
除非,这片区域全部为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
跟打印的结果一对比,果然是这几个呀!!!!
源代码,一会增加几行打印信息,就不去猜了。(增加中文后居然执行不了!!!!)
"""
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)
#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数组的头两个字节中。
结合下面的打印结果,一起看。
这一段也是设置相关环境变量的值,注意func就是_setenv函数
这段代码就是解析这些默认值了。。。。。
应该来说每一项都很眼熟了吧。