构建 PMON

编译PMON之前先要编译tools/pmconfig:

apt-get install bison
apt-get install flex
apt-get install xtuils-dev    # for makedepend
cd tools/pmoncfg
make
cp pmoncfg /usr/local/bin

编译 PMON

cd zloader.ls2k/
make cfg all tgt=rom CROSS_COMPILE=mipsel-linux-

最后生成 gzrom.bin,将gzrom.bin文件烧写进flash芯片。

构建过程分析

追踪Makefile

zloader.ls2k/Makefile只有一行:

include $(shell ./getname)

getname分析当前路径名,发现其中的ls2k,从而得出要包含的是Makefile.ls2k

Makefile.ls2k内容为:

TARGET=LS2K
TARGETEL=ls2k
export START=start.o
MEMSIZE=128
ZLOADER_OPTIONS=-mips3

include Makefile.inc

定义了几个重要的符号,比如TARGETSTART,然后包含Makefile.inc,这才是要用的Makefile

编译生成pmon.bin

最重要的构建目标是cfg

cfg:
	# DO NOT DELETE
	perl -i -ne 'print;exit if(/^# DO NOT DELETE/);' ../lib/libc/Makefile 
	perl -i -ne 'print;exit if(/^# DO NOT DELETE/);' ../lib/libm/Makefile 
	perl -i -ne 'print;exit if(/^# DO NOT DELETE/);' ../lib/libz/Makefile 
	mkdir -p ../Targets/${TARGET}/compile
	cd ../Targets/${TARGET}/conf/;pmoncfg ${TARGETEL}
	make -C ../Targets/${TARGET}/compile/${TARGETEL}/ depend clean

构建cfg时,创建了 Target/LS2K/compile/ls2k目录,在此目录下执行pmoncfg时,生成了真正的Makefile, 而它又包含了源码顶层目录的Makefile.inc, 然后执行make命令,生成了真正的目标文件pmon

OBJS= ...

CFILES= ...

all: pmon

pmon: ${SYSTEM_DEP} newvers
        ${SYSTEM_LD_HEAD}
        ${SYSTEM_LD}
        ${SYSTEM_LD_TAIL}

${SYSTEM_LD_TAIL}是在最顶层的Makefile.inc中定义的,其中有:

SYSTEM_LD_TAIL= @${SIZE} $@; chmod 755 $@ ; \
                ${OBJCOPY} -O binary $@ $@.bin

此时,pmon被转换位pmon.bin

打包压缩过程分析

回到 zloader.ls2k/Makefile.inc,继续分析压缩打包过程。

pmon.bin.c:  ../Targets/${TARGET}/compile/${TARGETEL}/pmon.bin
	gzip $< -c > pmon.bin.gz
	./bin2c pmon.bin.gz pmon.bin.c biosdata

pmon.bingzip压缩成pmon.bin.gz,再由bin2c转化为pmon.bin.c中的char biosdata[]数组。

zloader.o: zloader.c inflate.c  malloc.c  memop.c  pmon.bin.c initmips.c
	$(CC) -c zloader.c ${ZLOADER_OPTIONS} -DMEMSIZE=${MEMSIZE}

zloader.c中,有一条语句 #include "pmon.bin.c",因此biosdata最终和解压函数run_unzip(最终被编译成zloader.o目标文件。

${START}:
	rm -f ../Targets/${TARGET}/compile/${TARGETEL}/${START}
	gcc  -DSTARTADDR=${ROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S  > ../Targets/${TARGET}/conf/ld.script
	make -C ../Targets/${TARGET}/compile/${TARGETEL}/
	cp ../Targets/${TARGET}/compile/${TARGETEL}/${START} .

注意,${START}就是start.o,其源文件为Targets/LS2K/ls2k/start.S

ejtag_rom ejtag_rom1 ejtag_ram rom: clean ${START} zloader.o 
	gcc  -DSTARTADDR=${GZROMSTARTADDR} -D_ROM1 -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S  > ld.script
	${LD} -T ld.script -e start -o gzrom ${START} zloader.o 
	${CROSS_COMPILE}objcopy -O binary gzrom gzrom.bin

终于,start.ozloader.o被连接到一起,生成了gzrom,进而用objcopy转换为gzrom.bin

地址问题

zloader/Makefile.inc中,定义了PMON在内存中的位置:

GZROMSTARTADDR?=0xffffffff8f900000
ROMSTARTADDR?=0xffffffff8f010000

连接生成目标文件pmon时,起始地址用的是8f010000。连接生成目标文件gzrom时,起始地址用的是8f900000。 这两个目标文件入口点都是start.o,就是说start.o被连接了两次,一次地址是8f010000,另一次是8f900000

根据See Mips Run,系统启动时,入口地址是0xBFC0 0000,相应的物理地址是1FC0 0000。 在cache初始化之后,0x9FC0 0000也会被映射到同一个位置。gzrom.bin被写入这个位置。 就是说,start.o开始执行的时候,并不知道自己在0xBFC0 0000,而是认为自己在8f900000

解压缩过程—真假initmips()

start.o执行完之后,将跳到initmips()函数中,调用run_unzip()biosdata[]解压缩到0x8f01 0000

注意这个initmips()并不是真正的做系统初始化工作的initmips(), 真正的initmips()是在Targets/LS2K/ls2k/tgt_machdep.c中定义的,现在它还只是一堆被压缩的二进制数据,存在biosdata[]数组中。

假的initmips()是在zloader/initmips.c文件中定义的,而这个文件不是手工编写的,是用一个perl脚本genrom自动生成的。

Makefile.inc中有:

ifeq ("${tgt}","rom")
gencode=./genrom
endif

...

initmips.c:  ../Targets/${TARGET}/compile/${TARGETEL}/pmon
	${gencode} $< > initmips.c

可见,initmips.c文件不是人工编写的,是用genrom自动生成的。

genrom脚本打开目标文件pmon,找到真正的initmips()入口地址,并在生成initmips.c时,插入一段汇编代码, 跳转到真正的initmips()去执行初始化工作。

下面的代码片段来自genrom:

#!/usr/bin/perl 
my ($myedata,$myend,$initmips,$mystart);
open(F,qq(objdump -x $ARGV[0]|));
while(<F>)
{
chomp;
if(/([0-9a-f]+).+_edata/){
   $myedata=qq(0x$1);
 }

if(/([0-9a-f]+).+_end$/){
   $myend=qq(0x$1);
 }
if(/([0-9a-f]+).+initmips$/){
   $myinitmips=qq(0x$1);
 }
if(/([0-9a-f]+).+\s_start$/){
   $mystart=qq(0x$1);
 }
}

void initmips(unsigned int msize,int dmsize,int dctrl)
{
    long *edata=(void *)$myedata;
    long *end=(void *)$myend;
    int *p;
	int debug=(msize==0);
//	CPU_TLBClear();
    tgt_puts("Uncompressing Bios");
    if(!debug||dctrl&1)enable_cache();
	while(1)
	{
    if(run_unzip(biosdata,$mystart)>=0)break;
	}
    tgt_puts("OK,Booting Bios\\r\\n");
    for(p=edata;p<=end;p++)
    {
        *p=0;
    }
	memset($mystart-0x1000,0,0x1000);//$mystart-0x1000 for frame(registers),memset for pretty
#ifdef NOCACHE2
	flush_cache();
#else
	flush_cache2();
#endif
    realinitmips(debug?dmsize:msize);
}

inline __attribute__((always_inline)) void realinitmips(unsigned int msize)
{
	     asm ("li  \$29,$mystart-0x4000;\\n" \\
"		       li \$2,$myinitmips;\\n" \\
"			   move \$4,\%0;\\n" \\
"			   jalr \$2;\\n" \\
"			   nop;\\n" \\
"			  1: b 1b;nop;" \\
          :
          : "r" (msize)
          : "\$29", "\$2","\$4");

}