made by Rk
本文由浙江大学《嵌入式系统》课程提供强力支持。
感谢翁恺老师 @翁恺BA5AG
/*************************************************************/
David Welch的GitHub的 bootloader05给出了一个非常简单的RPi bootloader,他的代码链接在内存的0x00020000位置,一直在监听串口是 否有XMODEM协议的文件下载,如果有就开始接收数据,并复制到0x00008000位置,传输完成后跳转到 0x00008000去执行。
TA写了一个Python脚本,按照下面的命令调用脚本可以下载并执行用户程序
python xmodem-loader.py -p com3 -baud 115200 output.bin
你的任务是修改bootloader和python脚本实现如下功能:
调用命令 python xmodem-loader.py -p com3 -baud 115200 启动脚本并且与板卡建立串口连接,之后可以发送下面的命令。
load *.bin 下载程序*.bin
go 执行已下载的程序
peek addr 以一个字为单位读取内存中addr位置的数据(addr是4字节对齐,十六进行的形式,长度为8,例如 0x00008000),并以十六进制的形式输出
poke addr data 以一个字为单位修改内存中addr位置的数据为data(addr是4字节对齐,十六进行的形式,长 度为8, data也是十六进行的形式,长度为8)
verify *.bin 验证已下载的程序和*.bin是否完全相同。
在国外大神的README中介绍了相关背景:
This repo serves as a collection of low level examples. No operating树莓派通过板上的GPU加载启动信息,
1、读取SD卡第一个分区根目录里面的bootcode二进制文件以及start.elf。
2、在同一个目录下加载系统配置文件,比如配置CPU主频等,然后切换到读取kernel.img的地址。
3、读取kelnel后加载ARM启动二进制文件然后拷贝到内存,发送重置信号,使得树莓派可以从kernel.img的写入数据区开始加载命令继续执行。
内存被分为GPU和ARM使用两部分,目测对半分。
address bits in the memory controller).
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x1000000
}
SECTIONS
{
.text : { *(.text*) *(.rodata.str1.4) *(.rodata) } > ram
.bss : { *(.bss*) } > ram
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// The raspberry pi firmware at the time this was written defaults
// loading at address 0x8000. Although this bootloader could easily
// load at 0x0000, it loads at 0x8000 so that the same binaries built
// for the SD card work with this bootloader. Change the ARMBASE
// below to use a different location.
#define ARMBASE 0x8000
#define true 1
#define LOAD 0x00
#define GO 0x01
#define PEEK 0x02
#define POKE 0x03
#define VERIFY 0x04
extern void PUT32 ( unsigned int, unsigned int );
extern void PUT16 ( unsigned int, unsigned int );
extern void PUT8 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
extern unsigned int GET8 ( unsigned int );
extern unsigned int GETPC ( void );
extern void BRANCHTO ( unsigned int );
extern void dummy ( unsigned int );
extern void uart_init ( void );
extern unsigned int uart_lcr ( void );
extern void uart_flush ( void );
extern void uart_send ( unsigned int );
extern unsigned int uart_recv ( void );
extern void hexstring ( unsigned int );
extern void hexstrings ( unsigned int );
extern void timer_init ( void );
extern unsigned int timer_tick ( void );
extern void timer_init ( void );
extern unsigned int timer_tick ( void );
void print_pi(char* s) {
int i = 0;
while(s[i] != '\0') {
uart_send(s[i++]);
}
uart_send(0x0D); //send carriage return
uart_send(0x0A); //send new line
}
//------------------------------------------------------------------------
unsigned char xstring[256];
//------------------------------------------------------------------------
int notmain ( void ) {
unsigned int ra;
//unsigned int rb;
unsigned int rx;
unsigned int addr;
unsigned int block;
unsigned int state;
unsigned int crc;
unsigned int error_addr;
uart_init(); //init serial
hexstring(0x12345678); //translate to hex value and send
hexstring(GETPC());
hexstring(ARMBASE);
print_pi("This is Raspberry Pi!");
uart_send(0x04);
timer_init();
//SOH 0x01
//ACK 0x06
//NAK 0x15
//EOT 0x04
//block numbers start with 1
//132 byte packet
//starts with SOH
//block number byte
//255-block number
//128 bytes of data
//checksum byte (whole packet)
//a single EOT instead of SOH when done, send an ACK on it too
addr=ARMBASE; //base RAM address
error_addr = 0; //record the address of error
block=1; //the
state=0; //initial state
crc=0; //check sum
rx=timer_tick();
while(true)
{
ra=timer_tick();
if((ra-rx)>=4000000)
{
uart_send(0x15); //send long time no-response signal
rx+=4000000;
}
if((uart_lcr()&0x01)==0)
{
continue; //test if input string begin with 0x01
}
xstring[state]=uart_recv();
rx=timer_tick();
switch(state)
{
case 0: //initial state, decide which action to take
{
if(xstring[state]==0x01)
{
crc=xstring[state];
state++;
}
else if (xstring[state] == 0x04) //End Of transmission
{ //decide the action
uart_send(0x06);
if (xstring[1] == LOAD)
{
print_pi("This is LOAD command!");
uart_send(0x04);
uart_flush();
}
else if (xstring[1] == VERIFY)
{
if (error_addr == 0)
{
print_pi("Verify successful!");
uart_send(0x04);
}
else
{
print_pi("Verify error");
print_pi("Error Adress:");
hexstring(error_addr);
print_pi("Error Value:");
hexstring(GET32(error_addr));
uart_send(0x04);
}
uart_flush();
}
addr = ARMBASE;
error_addr = 0;
block = 1;
state = 0;
crc = 0;
}
else
{
state=0;
uart_send(0x15);
print_pi("Init Error!");
uart_send(0x04);
uart_flush();
}
break;
}
case 1: //decide the action
{
if (xstring[1] > VERIFY)
{
state = 0;
uart_send(0x15);
print_pi("Command error!");
uart_send(0x04);
uart_flush();
}
else if (xstring[1] == GO)
{
state = 0;
uart_send(0x06);
print_pi("Branch to the base address!");
uart_send(0x04);
uart_flush();
BRANCHTO(ARMBASE);
}
else if (xstring[1] == PEEK)
{
state = 133;
}
else if (xstring[1] == POKE)
{
state = 133;
}
else
{
state++;
}
break;
}
case 2:
{
if(xstring[state] == block)//if the data has the right block number
{
crc += xstring[state];
state++;
}
else
{
state = 0;
uart_send(0x15);
print_pi("Data block error!");
uart_send(0x04);
uart_flush();
}
break;
}
case 132: //receive and verify progress
{
crc &= 0xFF;
if(xstring[state]==crc)
{
if (xstring[1] == LOAD)
{
for(ra=0; ra<128; ra++)
{
PUT8(addr++,xstring[ra+4]);
}
uart_send(0x06);
}
else
{
//Verify progress
for (ra=0; ra<128; ra++,addr++)
{
if (xstring[ra + 4] != (GET8(addr) & 0xff))
{
error_addr = addr; //get the error address
break;
}
}
uart_send(0x06);
}
block=(block+1) & 0xFF; //if the data flow has not stopped
}
else
{
uart_send(0x15);
print_pi("Check sum error!");
uart_send(0x04);
uart_flush();
}
state=0;
break;
}
case 136:
{
if (xstring[1] == PEEK)
{
unsigned int peek_addr = 0;
for (ra = 0; ra < 4; ra++)
{ //generate the address
peek_addr = peek_addr << 8 | xstring[ra + 133];
}
uart_send(0x06);
print_pi("Peek command value:");
hexstring(GET32(peek_addr));
uart_send(0x04);
uart_flush();
state = 0;
}
else
{
state++;
}
break;
}
case 140:
{
if (xstring[1] == POKE)
{
unsigned int poke_addr = 0x00000000;
unsigned int poke_data = 0;
for (ra = 0; ra < 4; ra++)
{
poke_addr = poke_addr << 8 | xstring[ra + 133];
poke_data = poke_data << 8 | xstring[ra + 137];
}
uart_send(0x06);
print_pi("Poke command:");
PUT32(poke_addr, poke_data);
print_pi("Poke address:");
hexstring(poke_addr); //get the Poke address
print_pi("Poke value:");
hexstring(GET32(poke_addr)); //get the value after edit action
uart_send(0x04);
uart_flush();
state = 0;
}
else
{
state = 0;
}
break;
}
default:
{
crc+=xstring[state];
state++;
break;
}
}
}
return(0);
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//
// Copyright (c) 2012 David Welch [email protected]
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//-------------------------------------------------------------------------
.globl GET8
GET8:
ldrb r0,[r0]
bx lr
import sys, getopt
import serial
import time
def open(aport='/dev/tty.usbserial', abaudrate=115200) :#此处修改为本机串口地址,可以参见博文 http://blog.csdn.net/rk2900/article/details/8632713
return serial.Serial(
port=aport,
baudrate=abaudrate, # baudrate
bytesize=8, # number of databits
parity=serial.PARITY_NONE,
stopbits=1,
xonxoff=0, # enable software flow control
rtscts=0, # disable RTS/CTS flow control
timeout=None # set a timeout value, None for waiting forever
)
def printLog(sp):
temp = sp.read()
while ord(temp) != 0x04:
write(temp)
temp = sp.read()
if __name__ == "__main__":
# Import Psyco if available
try:
import psyco
psyco.full()
print "Using Psyco..."
except ImportError:
pass
conf = {
'port': '/dev/tty.usbserial',#此处修改为本机串口地址
'baud': 115200,
}
try:
opts, args = getopt.getopt(sys.argv[1:], "hqVewvrp:b:a:l:")
except getopt.GetoptError, err:
print str(err)
sys.exit(2)
for o, a in opts:
if o == '-p':
conf['port'] = a
elif o == '-b':
conf['baud'] = eval(a)
else:
assert False, "option error!"
sp = open(conf['port'], conf['baud'])
# print args[0]
# print conf['port']
# print conf['baud']
write=sys.stdout.write
isLoaded = False
while True:
print ''
cmd = raw_input('>> ').split(' ');
sp.flushInput()
if cmd[0] == 'go':
if not isLoaded:
confirm = raw_input("No file loaded, sure to go? [Y/N]")
if confirm == '' or confirm[0] == 'N' or confirm[0] == 'n':
continue
success = False
while success == False:
sp.write(chr(0x01))
sp.write(chr(0x01))
sp.flush()
temp=sp.read()
if ord(temp)==0x06:
success = True
else:
print ord(temp)
printLog(sp)
elif cmd[0] == 'peek':
if len(cmd) < 2:
print "Incorrect command, should be 'peek addr'"
continue
addr = int(cmd[1], 16) & 0xffffffff
success = False
while success == False:
sp.write(chr(0x01))
sp.write(chr(0x02))
for i in range(0,4):
sp.write(chr(addr >> 24 & 0xff))
addr = addr << 8
sp.flush()
temp=sp.read()
if ord(temp)==0x06:
success = True
else:
print ord(temp)
printLog(sp)
elif cmd[0] == 'poke':
if len(cmd) < 3:
print "Incorrect command, should be 'poke addr data'"
continue
addr = int(cmd[1], 16) & 0xffffffff
data = int(cmd[2], 16) & 0xffffffff
success = False
while success == False:
sp.write(chr(0x01))
sp.write(chr(0x03))
for i in range(0,4):
sp.write(chr(addr >> 24 & 0xff))
addr = addr << 8
for i in range(0,4):
sp.write(chr(data >> 24 & 0xff))
data = data << 8
sp.flush()
temp=sp.read()
if ord(temp)==0x06:
success = True
else:
print ord(temp)
printLog(sp)
elif cmd[0] == 'load' or cmd[0] == 'verify':
if len(cmd) < 2:
print "Please input the filename"
continue
try:
data = map(lambda c: ord(c), file(cmd[1],"rb").read())
except:
print "File path error"
continue
temp = sp.read()
buf = ""
dataLength = len(data)
blockNum = (dataLength-1)/128+1
print "The size of the image is ",dataLength,"!"
print "Total block number is ",blockNum,"!"
print "Download start,",blockNum,"block(s) in total!"
for i in range(1,blockNum+1):
success = False
while success == False:
sp.write(chr(0x01))
if cmd[0] == 'load':
sp.write(chr(0x00))
else:
sp.write(chr(0x04))
sp.write(chr(i&0xFF))
sp.write(chr(0xFF-i&0xFF))
crc = 0x01+0xFF
for j in range(0,128):
if len(data)>(i-1)*128+j:
sp.write(chr(data[(i-1)*128+j]))
crc += data[(i-1)*128+j]
else:
sp.write(chr(0xff))
crc += 0xff
crc &= 0xff
sp.write(chr(crc))
sp.flush()
temp=sp.read()
sp.flushInput()
if ord(temp)==0x06:
success = True
print "Block",i,"has finished!"
else:
print ord(temp)
print "Error,send again!"
printLog(sp)
sp.write(chr(0x04))
sp.flush()
temp=sp.read()
if ord(temp)==0x06:
if (cmd[0] == 'load'):
isLoaded = True
print "Download has finished!\n"
elif cmd[0] == 'q':
sys.exit(0)
else:
print "Invalid command!"
printLog(sp)
# while True:
# write(sp.read())
sp.close()
POKE操作之前进行Verify:
POKE操作之后进行verify:
百科关于ASCII码:http://baike.baidu.com/view/15482.htm
华官的博客:http://blog.csdn.net/logicworldzju/article/details/8923596
达达的博客:http://www.neohe.tk/raspberrypi-diy-bootloader/
外国人的GitHub:https://github.com/dwelch67/raspberrypi