PMON的构建系统
构建 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
定义了几个重要的符号,比如TARGET
和START
,然后包含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.bin
被gzip
压缩成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.o
和zloader.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");
}