Model Me, Model World
Blayground
光头程序的链接脚本
准备工作
这次又换了工作环境,切换到一台小熨斗八迷你内存电脑,上次的那个 OpenBSD 虚拟机是别想跑起来了。但是看到了得使用 XModem,那么光用 PuTTY 解决不了问题了。于是乎尝试各种奇怪的软件,ExtraPuTTY,SecureCRT,HyperTerminal Private Edition,XShell。不是得付费就是 xmodem 坏掉的。换上TeraTerm终于消停了。
简单测试
拿到树莓派就在想这玩意儿没有闪存来放引导,每次怎么能直接从外置卡中启动呢?唯一说得通的也就是在那块博通芯片内部有个预编程的程序,每次上电先执行那个程序,那个程序呢,居然还能够访问 FAT32 文件系统,然后把固定文件名的文件装载到内存里执行。然后课程作业里有一个推荐的源码仓库,点进去看了下,发现是个很详细的教程,真是好。
根据源码仓库中教程中的指示,设法取得bootcode.bin与start.elf。同时得到bootloader05生成的kernel.img。把这三个文件放到一张空的安全数字存储卡的根下,插回板子,插上串口,电源周期一下,这时候串口会打印一些奇怪而有序的数字,看来是正常启动了。然后用 XModem 上传一个blinker.bin,成功后串口又打印了一串数字,这时候看板子上的绿灯开始一跳一跳了。
上面说明了这是一个可行的方案,接下来试一下自己的工具链是否靠谱。把 Makefile 中的$ARMGNU 改成合适的值。之后试一下生成文件能重现上面的结果,那么表示现在这工具链至少是可以的。
修改引导程序代码
首先先把这个程序改成用普通终端就能正常使用的东西。
看一下bootloader05的代码,可以发现里面有不少状态。状态0通过判端输入的字符是否是SOH来进入 Xmodem 接收模式,那么现在加入对别的字符的判断来进入其他的模式。比如'v'就是验证,大'P'是戳内存,小'p'是窥内存等等。
之后一个个实现’p' 'P' 'v' 'l' 'g'功能就好,用户直接从串口输入,同时串口给出反馈信息。注意一下换行空格之类的就行。crc32 函数直接从网上搜来的。
最后看了下其实没有修改很多行,并且输出一个奇怪的字串占用了大约四分之一的代码修改量,其实应该判断下地址有没有词对齐等等。diff附在文末。
现在这个引导程序就直接能在串口上跑了,比如用户输入'p00000000'就能看 0x00000000 地址的内容。如果输入'P0000000000000000'就能把 0x00000000 写入地址 0x00000000。如果输入'l'并且之后用 Xmodem 发送一个文件的话,那个文件就会从$ARMBASE开始被写入。如果输入'v000000d4'就能计算从$ARMBASE地址开始大小为 0xd4 数据块的 crc32 值,并打印到串口上。之后用户可以用 crc32 程序计算自己本地所对应文件的 crc32 值是不是在板上那块内存中计算出来的值。
加壳
其实这样就够了,但是呢由于课上提到一个叫做xmodem-uploader.py的文件,似乎好像也得搞这么一个文件交作业。不过,找了半天也不知道被藏在了什么地方,实在没辙百度搜一下拼一枪了。一搜吓一跳,看了几篇文章决定还是用心智正常点的方式写这脚本吧。
首先调教一下 python 使之能够import serial以及import xmodem。之后就正常写一个伪命令行,检测输入的命令是什么,然后采取相应的动作。之前在板子上跑的代码没有错误检查(除了一个地址销毁机制),开 Xmodem 传文件也是手动的,比较 crc32 也得靠肉眼。那么现在这个脚本的目的就是过滤不正确的输入,以及自动化上传文件,还有文件自动校验。
生成光头程序小技能
这次写代码其实没什么好说的,却占了大多数篇幅,其实真正有意思的是那个 Makefile。花点时间好好研究一下。
顺便看到了clang llvm的做法,记下来以备忘,文末附上 Makefile。工作如下:
clang作为前端,吐出*.bc文件。猜一猜大概就是字节码吧,毕竟后端叫做底层虚拟机嘛。这一步指定指令字长。
llvm-link把多个字节码文件链接到一个文件中去。opt优化字节码。llc生成汇编代码,只有在这一步才与指令体系架构有关。- 用
binutils将汇编代码汇编,同时根据链接脚本,将所有代码段胶水在一起,生成特定格式的可执行文件,或者变成内存十六进制光头文件。
换成gcc的流程也就不言自明了。
附 1:lds
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}附 2:Makefile
ARMGNU ?= arm-linux-eabi
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding
gcc : kernel.img blinker.bin
all : gcc clang
clean :
rm -f *.o
rm -f *.bin
rm -f *.hex
rm -f *.elf
rm -f *.list
rm -f *.img
rm -f *.bc
rm -f *.clang.s
vectors.o : vectors.s
$(ARMGNU)-as vectors.s -o vectors.o
bootloader05.o : bootloader05.c
$(ARMGNU)-gcc $(COPS) -c bootloader05.c -o bootloader05.o
periph.o : periph.c
$(ARMGNU)-gcc $(COPS) -c periph.c -o periph.o
kernel.img : loader vectors.o periph.o bootloader05.o
$(ARMGNU)-ld vectors.o periph.o bootloader05.o -T loader -o bootloader05.elf
$(ARMGNU)-objdump -D bootloader05.elf > bootloader05.list
$(ARMGNU)-objcopy bootloader05.elf -O ihex bootloader05.hex
$(ARMGNU)-objcopy bootloader05.elf -O binary kernel.img
start.o : start.s
$(ARMGNU)-as start.s -o start.o
blinker.o : blinker.c
$(ARMGNU)-gcc $(COPS) -c blinker.c -o blinker.o
blinker.bin : memmap start.o blinker.o
$(ARMGNU)-ld start.o blinker.o -T memmap -o blinker.elf
$(ARMGNU)-objdump -D blinker.elf > blinker.list
$(ARMGNU)-objcopy blinker.elf -O ihex blinker.hex
$(ARMGNU)-objcopy blinker.elf -O binary blinker.bin
LOPS = -Wall -m32 -emit-llvm
LLCOPS = $(LLCOPS1)
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding
OOPS = -std-compile-opts
LLCOPS0 = -march=arm
LLCOPS1 = -march=arm -mcpu=arm1176jzf-s
clang : bootloader05.bin
bootloader05.bc : bootloader05.c
clang $(LOPS) -c bootloader05.c -o bootloader05.bc
periph.bc : periph.c
clang $(LOPS) -c periph.c -o periph.bc
bootloader05.clang.elf : loader vectors.o bootloader05.bc periph.bc
llvm-link periph.bc bootloader05.bc -o bootloader05.nopt.bc
opt $(OOPS) bootloader05.nopt.bc -o bootloader05.opt.bc
llc $(LLCOPS) bootloader05.opt.bc -o bootloader05.clang.s
$(ARMGNU)-as bootloader05.clang.s -o bootloader05.clang.o
$(ARMGNU)-ld -o bootloader05.clang.elf -T loader vectors.o bootloader05.clang.o
$(ARMGNU)-objdump -D bootloader05.clang.elf > bootloader05.clang.list
bootloader05.bin : bootloader05.clang.elf
$(ARMGNU)-objcopy bootloader05.clang.elf bootloader05.clang.bin -O binary附 3:伪命令行小脚本
显示/隐藏
# -*- coding: utf-8 -*-
import serial
import zlib
import os
from xmodem import XMODEM
if __name__ == "__main__":
serial_port = serial.Serial(
port='COM6',
baudrate=115200,
bytesize=8,
parity=serial.PARITY_NONE,
stopbits=1,
xonxoff=0,
rtscts=0,
timeout=None
)
#```
#### 传“函数指针”给xmodem实例
#```python
def putc(data, timeout=1):
serial_port.write(data)
return 1
def getc(data, timeout=1):
return serial_port.read()
modem = XMODEM(getc, putc)
#```
#### 伪命令行逻辑
#```python
while True:
cmd = raw_input('>> ').split(' ')
serial_port.flushInput()
#```
#### 三个简单的功能
#```python
if cmd[0] == 'go':
putc('g')
serial_port.flush()
elif cmd[0] == 'peek':
if len(cmd) <= 1 or cmd[1][0] != '0' or cmd[1][1] != 'x' or len(cmd[1]) != 10:
print "Usage: peek 0x????????"
continue
serial_port.write('p' + cmd[1][2:] + '\n')
serial_port.flush()
serial_port.readline()
print(serial_port.readline().strip())
elif cmd[0] == 'poke':
if len(cmd) < 3 or cmd[1][0] != '0' or cmd[1][1] != 'x' or len(cmd[1]) != 10 or \
cmd[2][0] != '0' or cmd[2][1] != 'x' or len(cmd[2]) != 10:
print "Usage: poke 0x???????? 0x????????"
continue
serial_port.write('P' + cmd[1][2:] + cmd[2][2:] + '\n')
serial_port.flush()
serial_port.readline()
print(serial_port.readline().strip())
#```
#### 载入功能调用xmodme库,验证功能使用crc32
#```python
elif cmd[0] == 'load':
if len(cmd) < 2:
print "Usage: load path/to/file/to/be/loaded"
stream = file(cmd[1], 'rb')
if modem.send(stream):
print("Send to board done")
else:
print("Send failed, power cycle the board and try again")
elif cmd[0] == 'verify':
if len(cmd) < 2:
print "Usage: verify path/to/file/to/be/verified"
continue
prev = 0
v_file = open(cmd[1], "rb")
v_size = "%08X" % os.path.getsize(cmd[1])
for eachLine in v_file:
prev = zlib.crc32(eachLine, prev)
v_crc32 = "%08X" % (prev & 0xFFFFFFFF)
v_file.close()
serial_port.write('v' + v_size + '\n')
r_crc32 = (serial_port.readline().split()[1])
if r_crc32 == v_crc32:
print "ok crc32:%s" % v_crc32
else:
print "bad: local: %s, remote: %s" % \
(v_crc32 , r_crc32)
serial_port.flush()
#```
#### 清洁工作
#```python
elif cmd[0] == 'quit':
break
else:
print "Available commands: peek poke load verify quit"
serial_port.close()附 4:bootloader05.c
显示/隐藏
diff --git a/bootloader05/bootloader05.c b/bootloader05/bootloader05.c
index a742f0d..d0842c6 100644
--- a/bootloader05/bootloader05.c
+++ b/bootloader05/bootloader05.c
@@ -31,6 +31,77 @@ extern unsigned int timer_tick ( void );
extern void timer_init ( void );
extern unsigned int timer_tick ( void );
+
+unsigned int r32bit() {
+ unsigned int temp;
+ unsigned int rchar;
+ int i;
+ temp = 0;
+ for (i = 0; i < 8; i++) {
+ rchar = uart_recv();
+ uart_send(rchar);
+ if (rchar <= '9' && rchar >= '0') {
+ rchar = rchar - '0';
+ }
+ else if (rchar <= 'f' && rchar >= 'a'){
+ rchar = rchar - 'a' + 10;
+ }
+ else if (rchar <= 'F' && rchar >= 'A'){
+ rchar = rchar - 'A' + 10;
+ }
+ else {
+ uart_send('w');
+ uart_send('r');
+ uart_send('o');
+ uart_send('n');
+ uart_send('g');
+ uart_send(' ');
+ uart_send('i');
+ uart_send('n');
+ uart_send('p');
+ uart_send('u');
+ uart_send('t');
+ uart_send(',');
+ uart_send(' ');
+ uart_send('a');
+ uart_send('s');
+ uart_send('s');
+ uart_send('u');
+ uart_send('m');
+ uart_send('e');
+ uart_send(' ');
+ uart_send('z');
+ uart_send('e');
+ uart_send('r');
+ uart_send('o');
+ uart_send('\r');
+ uart_send('\n');
+ return 0;
+ }
+
+ temp = (temp << 4) + rchar;
+ }
+ uart_send(' ');
+ return temp;
+}
+
+unsigned int crc32(unsigned char *message, unsigned int length) {
+ unsigned int i, j;
+ unsigned int byte, crc, mask;
+ i = 0;
+ crc = 0xFFFFFFFF;
+ while (i < length) {
+ byte = message[i]; // Get next byte.
+ crc = crc ^ byte;
+ for (j = 8; j >= 1; j--) { // Do eight times.
+ mask = -(crc & 1);
+ crc = (crc >> 1) ^ (0xEDB88320 & mask);
+ }
+ i = i + 1;
+ }
+ return ~crc;
+}
+
//------------------------------------------------------------------------
unsigned char xstring[256];
//------------------------------------------------------------------------
@@ -42,7 +113,8 @@ int notmain ( void )
unsigned int addr;
unsigned int block;
unsigned int state;
-
+ unsigned int rdata;
+ unsigned int raddr;
unsigned int crc;
uart_init();
@@ -87,13 +159,7 @@ int notmain ( void )
if(xstring[state]==0x04)
{
uart_send(0x06);
- for(ra=0;ra<30;ra++) hexstring(ra);
- hexstring(0x11111111);
- hexstring(0x22222222);
- hexstring(0x33333333);
uart_flush();
- BRANCHTO(ARMBASE);
- break;
}
}
switch(state)
@@ -105,6 +171,60 @@ int notmain ( void )
crc=xstring[state];
state++;
}
+ else if (xstring[state]=='p') {
+ uart_send(xstring[state]);
+ raddr = r32bit();
+ xstring[state] = uart_recv();
+ while (xstring[state] != '\n' && xstring[state] != '\r') {
+ xstring[state] = uart_recv();
+ }
+ uart_send('\r');
+ uart_send('\n');
+ uart_send(' ');
+ hexstrings(raddr);
+ uart_send(':');
+ hexstring(GET32(raddr));
+ }
+ else if (xstring[state]=='P') {
+ uart_send(xstring[state]);
+ raddr = r32bit();
+ rdata = r32bit();
+ xstring[state] = uart_recv();
+ while (xstring[state] != '\n' && xstring[state] != '\r') {
+ xstring[state] = uart_recv();
+ }
+ uart_send('\r');
+ uart_send('\n');
+ uart_send(' ');
+ PUT32(raddr, rdata);
+ hexstrings(raddr);
+ uart_send(':');
+ hexstring(GET32(raddr));
+ }
+ else if (xstring[state]=='l') {
+ uart_send('l');
+ uart_send('o');
+ uart_send('a');
+ uart_send('d');
+ uart_send('\r');
+ uart_send('\n');
+ uart_flush();
+ }
+ else if (xstring[state]=='v') {
+ unsigned int len;
+ len = 0;
+ uart_send('v');
+ len = r32bit();
+ uart_flush();
+ hexstring(crc32((unsigned char*)ARMBASE, len));
+ }
+ else if (xstring[state]=='g') {
+ uart_send('g');
+ uart_send('o');
+ hexstring(ARMBASE);
+ uart_flush();
+ BRANCHTO(ARMBASE);
+ }
else
{
//state=0;