Model Me, Model World

Model Me, Model World

Blayground

光头程序的链接脚本

准备工作

这次又换了工作环境,切换到一台小熨斗八迷你内存电脑,上次的那个 OpenBSD 虚拟机是别想跑起来了。但是看到了得使用 XModem,那么光用 PuTTY 解决不了问题了。于是乎尝试各种奇怪的软件,ExtraPuTTYSecureCRTHyperTerminal Private EditionXShell。不是得付费就是 xmodem 坏掉的。换上TeraTerm终于消停了。

简单测试

拿到树莓派就在想这玩意儿没有闪存来放引导,每次怎么能直接从外置卡中启动呢?唯一说得通的也就是在那块博通芯片内部有个预编程的程序,每次上电先执行那个程序,那个程序呢,居然还能够访问 FAT32 文件系统,然后把固定文件名的文件装载到内存里执行。然后课程作业里有一个推荐的源码仓库,点进去看了下,发现是个很详细的教程,真是好。

根据源码仓库中教程中的指示,设法取得bootcode.binstart.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。工作如下:

  1. 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;

Blog maintained by Tao J

No longer hosted on GitHub Pages — Theme by mattgraham