uClinux开发环境介绍

以下是我对uClinux 开发环境的一些想法,欢迎拍砖。

Author: Hily Jiang
Email: hilyjiang at Gmail
Blog: http://hily.me/blog/

目前开发基于 uClinux 平台应用的开发环境主要有三种:

1. 装Linux系统
    这是目前我的项目组中开发时使用的主要方式。
    环境配置:
        操作系统:  各种 linux 发行版
        开发工具链:arm-elf-tools
        终端调试器:minicom
        代码编辑工具:gedit/vim/Emacs/Kate/KScope等
        CVS工具:cvs命令行,也可以使用图形界面的cvs客户端,如gcvs、TkCVS。
    Linux发行版在这里推荐使用 Ubuntu(当前最新版本为7.10),软件维护和更新都很快,在开发之余可以充分体验Linux的乐趣。
    代码编辑工具,强烈推荐KScope,经过一段时间试用,感觉很不错,支持基于变量/函数名的声明、定义、引用的查找,还有自动完成功能。

2. Windows + 虚拟机
    虚拟机中装Linux进行编译,代码放在虚拟机中,共享到Windows下进行编辑,产生的映像在Windows下进行烧写。
    环境配置:
        操作系统:  Windows 系列
        终端调试器:超级终端等
        代码编辑工具:UltraEdit、EditPlus、SourceInsight等
        CVS工具:TortoiseCVS、WinCVS等
        虚拟机:  VMWare
           – 操作系统:  各种 linux 发行版
           – 开发工具链:arm-elf-tools
           – 与 PC 主机文件交互:Samba或VMWare-tools。

3. Windows + 远程编译
        与上一方案类似,只是代码是放在服务器上的,通过ssh或telnet登录到服务器上进行编译。生成的映像也是在Windows下进行烧写。
    环境配置:
        操作系统:  Windows 系列
        终端调试器:超级终端等
        代码编辑工具:UltraEdit、EditPlus、SourceInsight等
        CVS工具:TortoiseCVS、WinCVS等
        远程连接工具:  支持SSH或Telnet的连接工具,如Putty、SecureCRT等。
           – 服务器操作系统:  各种linux发行版
           – 开发工具链:arm-elf-tools
           – 与客户端的文件交互:NFS或Samba。

— EOF —

SQLite移植手记

前几天成功地把Berkeley DB移植到uClinux上,虽然可以正常工作了,但是文件还是太大了些。今天来试一个稍微小一点的,它叫SQLite。 SQLite实现了大部分SQL92标准的SQL语句,同时支持ACID。还有其它许多特性这里不做深究,因为这在嵌入式领域来说应该是够用了。

Hily Jiang
Email&Gtalk: hilyjiang at Gmail
Blog: http://hily.me/blog/

下载:
下载页面:http://www.sqlite.org/download.html
我使用的还是当前最新版本:sqlite-3.3.7.tar.gz
(写完的时候已经更新出3.3.8版本了,真快啊……)

安装:
时间不多,简单介绍安装过程:
解压sqlite到uclinux-dist/user/sqlite/

============ 对uClinux的修改 ============
1. 下载sqlite,解压到uclinux-dist/user/下
2. 编辑uclinux-dist/user/下的Makefile,增加:

dir_$(CONFIG_USER_SQLITE_SQLITE)            += sqlite

3. uclinux-dist/config/Configure.help中增加:

CONFIG_USER_SQLITE_SQLITE
  SQLite Database.

4. uclinux-dist/config/config.in最后增加:

mainmenu_option next_comment
comment 'Database'
bool 'sqlite'			CONFIG_USER_SQLITE_SQLITE
endmenu

============ 对SQLite的修改 ============
1. uclinux-dist/user/sqlite/main.mk中:
TCCX修改为:

TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src $(CFLAGS)

LIBOBJ修改为(一些模块不需要,比如tcl):

LIBOBJ+= alter.o analyze.o attach.o auth.o btree.o build.o \
         callback.o complete.o date.o delete.o \
         expr.o func.o hash.o insert.o loadext.o \
         main.o opcodes.o os.o os_unix.o \
         pager.o parse.o pragma.o prepare.o printf.o random.o \
         select.o table.o tokenize.o trigger.o \
         update.o util.o vacuum.o \
         vdbe.o vdbeapi.o vdbeaux.o vdbefifo.o vdbemem.o \
         where.o utf.o legacy.o vtab.o

sqlite3$(EXE)规则部分修改为:

shell.o: $(TOP)/src/shell.c sqlite3.h
	$(TCCX) $(READLINE_FLAGS) -c $(TOP)/src/shell.c

sqlite3$(EXE): shell.o libsqlite3.a
	$(TCC) $(LDFLAGS) -o $@ shell.o \
	libsqlite3.a $(LIBREADLINE) $(THREADLIB) $(LDLIBS)

去掉install,增加:

distclean: clean
	rm -f config.h

2. 拷贝Makefile.linux-gcc为Makefile,修改如下:

TCC = $(CROSS)gcc
AR = $(CROSS)ar cr
RANLIB = $(CROSS)ranlib
#TCL_FLAGS = -I/home/drh/tcltk/8.4linux
#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl

 

编译:
在make menuconfig的user application部分可以看到刚添加的Database –>菜单,进入并选择SQLite,保存退出后按原先的步骤重新编译内核即可。如果只需要sqlite的库,那么make user_only就可以了。
编译完成后会在user/sqlite目录下生成库libsqlite3.a。

测试一下:
编写一个测试程序sqlitetest.c,代码如下(来自官方quick start):

#include <stdio.h>
#include <sqlite3.h>

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  int i;
  for(i=0; i
然后为它写一个Makefile,大致如下:
UCLINUX_PATH = /home/uClinux-dist
SQLITE_PATH = $(UCLINUX_PATH)/user/sqlite

CROSS = arm-elf-
CPU_CFLAGS = -O3 -Wall -mapcs-32 -mtune=arm7tdmi -fno-builtin -msoft-float -Os  \
	-D__uClinux__ -D__ARM_CPU__  \
	-I$(UCLINUX_PATH)/lib/uClibc/include -I$(UCLINUX_PATH)/linux-2.4.x/include \
	-I$(SQLITE_PATH) \
	-D_DEBUG_
CPU_LDFLAGS = -nostartfiles -Wl, -elf2flt -L$(UCLINUX_PATH)/lib/uClibc/lib
CPU_ARFLAGS = r
CPU_LDLIBS = $(UCLINUX_PATH)/lib/uClibc/lib/crt0.o $(UCLINUX_PATH)/lib/uClibc/lib/crti.o \
	$(UCLINUX_PATH)/lib/uClibc/lib/crtn.o -lc
MY_LDFLAGS = -L$(SQLITE_PATH)
MY_LDLIBS = -lsqlite3

CFLAGS = $(CPU_CFLAGS)
LDFLAGS = $(CPU_LDFLAGS) $(MY_LDFLAGS)
LDLIBS = $(CPU_LDLIBS) $(MY_LDLIBS)

TOPDIR = ./

CC = $(CROSS)gcc

EXEC = sqlitetest
CSRC = sqlitetest.c
OBJS = $(patsubst %.c,%.o, $(CSRC))

all: $(EXEC)

$(OBJS): %.o : %.c
	$(CC) $(CFLAGS) -c $< -o $@

$(EXEC): $(OBJS)
	$(CC) $(OBJS) $(LDFLAGS) $(LDLIBS) -o $@

clean:
	-rm -f $(EXEC) *.elf *.gdb *.o

运行make编译测试程序,生成的程序大小约300KB:

linux:/home/work/sqlite # ll
总用量 688
drwxr-xr-x  2 root root    264 2006-10-09 11:25 .
drwxr-xr-x  4 root root    160 2006-10-09 11:21 ..
-rw-------  1 root root    982 2006-10-09 11:25 Makefile
-rwxr--r--  1 root root 315584 2006-10-09 11:25 sqlitetest
-rw-------  1 root root    788 2006-10-09 11:21 sqlitetest.c
-rwxr-xr-x  1 root root 396538 2006-10-09 11:25 sqlitetest.gdb
-rw-r--r--  1 root root   1600 2006-10-09 11:25 sqlitetest.o

接着将测试程序下载到目标板,测试运行结果如下:

# /home/sqlitetest /home/testdb.db "CREATE TABLE my_table(id int, name varchar(20))"
# /home/sqlitetest /home/testdb.db "INSERT INTO my_table values(1, 'jianglj')"
# /home/sqlitetest /home/testdb.db "INSERT INTO my_table values(2, 'Hily Jiang')"
# /home/sqlitetest /home/testdb.db "SELECT * FROM my_table"
id = 1
name = jianglj

id = 2
name = Hily Jiang

#

 
继续寻找更小的数据库,知道的朋友不妨推荐一下 :-)

-- EOF --

uClinux上Berkeley DB v4.5.20移植手记

Berkeley DB 是一个很棒的开源的嵌入式数据库,它主要运行在Unix/Linux上。现在它已成为Oracle的一部分,叫作Oracle Berkeley DB
下面主要介绍一下它在我最近玩的uClinux上的移植过程。

Hily Jiang
Email&Gtalk: hilyjiang at Gmail
Blog: http://hily.me/blog/

下载页面:
http://dev.sleepycat.com/downloads/releasehistorybdb.html
我使用的是当前最新版的4.5.20(点击下载)

linux:/home/work/db # ll
总用量 9078
drwxr-xr-x   2 root root       80 2006-10-08 12:36 .
drwxr-xr-x  11 root root     1120 2006-10-08 12:33 ..
-r-xr-xr-x   1 root users 9281894 2006-10-08 12:36 db-4.5.20.tar.gz

 

(一)解压

linux:/home/work/db # tar -zxf db-4.5.20.tar.gz
linux:/home/work/db # cd db-4.5.20/
linux:/home/work/db/db-4.5.20 # ls
.              db_checkpoint  db_upgrade     libdb_java  README
..             db_deadlock    db_verify      LICENSE     rep
btree          db_dump        dist           lock        repmgr
build_unix     db_dump185     docs           log         rpc_client
build_vxworks  db_hotbackup   env            mod_db4     rpc_server
build_windows  dbinc          examples_c     mp          sequence
clib           dbinc_auto     examples_cxx   mutex       tcl
common         db_load        examples_java  os          test
crypto         dbm            fileops        os_vxworks  txn
cxx            db_printlog    hash           os_windows  xa
db             db_recover     hmac           perl
db185          dbreg          hsearch        php_db4
db_archive     db_stat        java           qam

docs目录下有我们需要的文档,包括快速入门、各种语言的API手册等资料。
(二)配置和编译
建立一个脚本以方便配置。由于unix/linux编译时的工作路径必须是build_unix,因此我们需要在build_unix目录下创建脚本。
我创建了一个名为myconfig的脚本,内容如下:

linux:/home/work/db/db-4.5.20/build_unix # cat -n myconfig
     1  #!/bin/sh
     2
     3  CC=arm-elf-gcc \
     4  CFLAGS="-Os -D__uClinux__ -fno-builtin -I/home/uClinux-dist/linux-2.4.x/include -I/home/uClinux-dist/lib/uClibc/include -I/home/uClinux-dist/lib/uClibc/include/../ " \
     5  LDFLAGS="-Wl,-elf2flt -Wl,-move-rodata -L/home/uClinux-dist/lib/uClibc/lib -L/home/uClinux-dist/lib/uClibc/lib/../ -lc " \
     6  ../dist/configure \
     7    --prefix=/home/work/db/Berkeley.DB \
     8    --build=i686-linux \
     9    --host=arm-elf-linux \
    10    --disable-cryptography \
    11    --disable-hash \
    12    --disable-queue \
    13    --disable-replication \
    14    --disable-statistics \
    15    --disable-verify \
    16    --disable-compat185 \
    17    --disable-cxx \
    18    --disable-debug \
    19    --disable-debug_rop \
    20    --disable-debug_wop \
    21    --disable-diagnostic \
    22    --disable-dump185 \
    23    --disable-java \
    24    --disable-mingw \
    25    --disable-o_direct \
    26    --disable-posixmutexes \
    27    --disable-pthread_api \
    28    --disable-rpc \
    29    --disable-smallbuild \
    30    --disable-tcl \
    31    --disable-test \
    32    --disable-uimutexes \
    33    --enable-umrw \
    34    --disable-shared \
    35    --enable-static \
    36    --enable-fast-install \
    37    --disable-libtool-lock \
    38    --disable-largefile

关于configure配置参数的含义,可以运行”../dist/configure –help”查看帮助,这里不再介绍。
要强调的一点是uClibc只能用静态编译,因此一定要选择–disable-shared。
接着执行./myconfig运行配置并编译安装函数库:

linux:/home/work/db/db-4.5.20/build_unix # ./myconfig >/dev/null
configure: WARNING: In the future, Autoconf will not detect cross-tools
whose name does not start with the host triplet.  If you think this
configuration is useful to you, please write to autoconf@gnu.org.
cat: /etc/ld.so.conf.d/*.conf: No such file or directory
linux:/home/work/db/db-4.5.20/build_unix # make >/dev/null && make install >/dev/null
../dist/../hmac/sha1.c:96: warning: `R0' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:40: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:97: warning: `R1' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:42: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:98: warning: `R2' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:44: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:99: warning: `R3' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:46: warning: this is the location of the previous definition
../dist/../hmac/sha1.c:100: warning: `R4' redefined
/usr/local/lib/gcc-lib/arm-elf/2.95.3/../../../../arm-elf/include/sys/ucontext.h:48: warning: this is the location of the previous definition
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
warning: .rodata section contains relocations
strip: /home/work/db/Berkeley.DB/bin/db_archive: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_checkpoint: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_deadlock: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_dump: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_hotbackup: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_load: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_printlog: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_recover: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_stat: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_upgrade: 不可识别的文件格式
strip: /home/work/db/Berkeley.DB/bin/db_verify: 不可识别的文件格式

编译过程中会出现一些warning,不用理它们。
安装完后,会在指定的安装目录/home/work/db/Berkeley.DB下生成函数库:

linux:/home/work/db/db-4.5.20/build_unix # cd /home/work/db/Berkeley.DB/
linux:/home/work/db/Berkeley.DB # ls
.  ..  bin  docs  include  lib
linux:/home/work/db/Berkeley.DB # ll lib/
总用量 1962
drwxr-xr-x  2 root root     104 2006-10-08 12:57 .
drwxr-xr-x  6 root root     144 2006-10-08 12:57 ..
-rw-r--r--  1 root root 1002266 2006-10-08 12:57 libdb-4.5.a
-rw-r--r--  1 root root 1002266 2006-10-08 12:57 libdb.a

(三)数据库操作测试
创建一个测试程序如下:

linux:/home/work/db/Berkeley.DB # cat -n testdb.c
     1  #include
     2  #include
     3  #include
     4
     5  #define DESCRIPTION_SIZE 20
     6  int main()
     7  {
     8      DB *dbp;           /* DB structure handle */
     9      u_int32_t flags;   /* database open flags */
    10      int ret;           /* function return value */
    11      char *description = "Grocery bill.";
    12      char *description1[DESCRIPTION_SIZE + 1];
    13      DBT key, data;
    14      float money;
    15
    16      /* Initialize the structure. This
    17       * database is not opened in an environment,
    18       * so the environment pointer is NULL. */
    19      ret = db_create(&dbp, NULL, 0);
    20      if (ret != 0) {
    21        /* Error handling goes here */
    22          printf("Create fail!\n");
    23      }
    24
    25      /* Database open flags */
    26      flags = DB_CREATE;    /* If the database does not exist,
    27                             * create it.*/
    28
    29      /* open the database */
    30      ret = dbp->open(dbp,        /* DB structure pointer */
    31                      NULL,       /* Transaction pointer */
    32                      "/home/my_db.db", /* On-disk file that holds the database. */
    33                      NULL,       /* Optional logical database name */
    34                      DB_BTREE,   /* Database access method */
    35                      flags,      /* Open flags */
    36                      0);         /* File mode (using defaults) */
    37      if (ret != 0) {
    38        /* Error handling goes here */
    39          printf("Created new database.\n");
    40      }
    41
    42      money = 122.45;
    43
    44      /* Zero out the DBTs before using them. */
    45      memset(&key, 0, sizeof(DBT));
    46      memset(&data, 0, sizeof(DBT));
    47
    48      key.data = &money;
    49      key.size = sizeof(float);
    50
    51      data.data = description;
    52      data.size = strlen(description) +1;
    53
    54      ret = dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE);
    55      if (ret == DB_KEYEXIST) {
    56          dbp->err(dbp, ret,
    57            "Put failed because key %f already exists", money);
    58      }
    59
    60      memset(&data, 0, sizeof(DBT));
    61
    62      data.data = &description1;
    63      data.ulen = DESCRIPTION_SIZE + 1;
    64      data.flags = DB_DBT_USERMEM;
    65      dbp->get(dbp, NULL, &key, &data, 0);
    66
    67      printf("data: %s\n", (char *)data.data);
    68
    69      /* When we're done with the database, close it. */
    70      if (dbp != NULL)
    71          dbp->close(dbp, 0);
    72
    73      return 0;
    74  }

这个程序会在目标板上/home/目录下创建一个文件名为my_db.db的数据库,接着增加一条记录,然后从数据库中读取出新添加的这条记录,最后关闭数据库。
程序要烧写到目标板上,需要进行交叉编译:

linux:/home/work/db/Berkeley.DB # arm-elf-gcc -O3 -Wall -mapcs-32 -mtune=arm7tdmi -fno-builtin -msoft-float -Os -D__uClinux__ -D__ARM_CPU__ -I/home/work/uClinux-dist/lib/uClibc/include -I/home/uClinux-dist/linux-2.4.x/include -I/home/work/db/Berkeley.DB/include -D_DEBUG_ -c testdb.c -o testdb.o
linux:/home/work/db/Berkeley.DB # arm-elf-gcc testdb.o -nostartfiles -Wl, -elf2flt -L/home/uClinux-dist/lib/uClibc/lib -L/home/work/db/Berkeley.DB/lib /home/uClinux-dist/lib/uClibc/lib/crt0.o /home/uClinux-dist/lib/uClibc/lib/crti.o /home/uClinux-dist/lib/uClibc/lib/crtn.o -lc -ldb -o testdb
linux:/home/work/db/Berkeley.DB # ll
总用量 1217
drwxr-xr-x   6 root root    280 2006-10-08 13:22 .
drwxr-xr-x   4 root root    144 2006-10-08 12:50 ..
drwxr-xr-x   2 root root    376 2006-10-08 12:57 bin
drwxr-xr-x  14 root root    384 2006-10-08 12:57 docs
drwxr-xr-x   2 root root     96 2006-10-08 12:57 include
drwxr-xr-x   2 root root    104 2006-10-08 12:57 lib
-rwxr--r--   1 root root 584476 2006-10-08 13:22 testdb
-rw-r--r--   1 root root   2171 2006-10-08 13:22 testdb.c
-rw-------   1 root root   2163 2006-10-08 13:09 testdb.c~
-rwxr-xr-x   1 root root 673683 2006-10-08 13:22 testdb.gdb
-rw-r--r--   1 root root   1540 2006-10-08 13:22 testdb.o

生成的可执行文件比较大,将近600KB。
烧写到目标机上后运行,结果如下:

# /home/testdb
data: Grocery bill.

搞定~ 

总的来说,Berkeley DB还是比较易用的。就是生成的可执行文件还是太大些 :-(

— EOF —

内核无法解压的问题

每次碰上问题,都要花上很长时间去寻找答案。从上周五开始到今天,除去周末两天回家,我一直被一个blamed bug困扰。其实这个问题不能叫做bug,只是因为碰到的bug太多叫习惯了,应该称之为a mistake更贴切些。

调试了两天依然没个结果,昨天憋不住了,只好向各大论坛还有QQ群求助,却许久无人应答,无果。心不甘,于是继续奋斗ing。

具体症状如我在论坛中所述:
bootloader已经能正常工作,但是当加载内核映像时就没反应了。
连“Uncompressing Linux…”都没有出现。
一些设置:
内核2.6.14
make的是zImage
ZTEXTADDR 0x00010000
ZREALADDR 0x0C008000
ZBSSADDR 0x0C400000
把decompress_kernel改为putstr(“test”);后仍然不能打印出字符。
不知问题出在哪里?或者给个思路?

在这之前已经查阅了许多文章,知道了可能的原因(找不到原文出处):

在 boot loader 程序的设计与实现中,没有什么能够比从串口终端正确地收到打印信息能更令人激动了。此外,向串口终端打印信息也是一个非常重要而又有效的调试手段。但是,我们经常会碰到串口终端显示乱码或根本没有显示的问题。造成这个问题主要有两种原因:(1) boot loader 对串口的初始化设置不正确。(2) 运行在 host 端的终端仿真程序对串口的设置不正确,这包括:波特率、奇偶校验、数据位和停止位等方面的设置。

此外,有时也会碰到这样的问题,那就是:在 boot loader 的运行过程中我们可以正确地向串口终端输出信息,但当 boot loader 启动内核后却无法看到内核的启动输出信息。对这一问题的原因可以从以下几个方面来考虑:

(1) 首先请确认你的内核在编译时配置了对串口终端的支持,并配置了正确的串口驱动程序。

(2) 你的 boot loader 对串口的初始化设置可能会和内核对串口的初始化设置不一致。此外,对于诸如 s3c44b0x 这样的 CPU,CPU 时钟频率的设置也会影响串口,因此如果 boot loader 和内核对其 CPU 时钟频率的设置不一致,也会使串口终端无法正确显示信息。

(3) 最后,还要确认 boot loader 所用的内核基地址必须和内核映像在编译时所用的运行基地址一致,尤其是对于 uClinux 而言。假设你的内核映像在编译时用的基地址是 0xc0008000,但你的 boot loader 却将它加载到 0xc0010000 处去执行,那么内核映像当然不能正确地执行了。

在bootloader的运行过程中已经可以正确地向串口终端输出信息,但是我的情况并不是其中所述的任何一种:
(1) 内核配置时已经设置了CONFIG_SERIAL_S3C44B0X和CONFIG_SERIAL_S3C44B0X_CONSOLE,即内核已支持串口端。
(2) 因为bootloader中已经能正常使用串口,所以内核中就没有再对串口进行初始化设置,因此不存在设置不一致的问题。而CPU的时钟频率我统一设置为60MHz。
(3) 内核尚未解压,因此该问题不用考虑。 

冥思苦想……仍找不出症结所在。

把bootloader中的打印函数移到内核中,命名为test_serial,并设置head.S中start的第一句为bl test_serial,但是仍然没有字符出现。
接着分析了bootloader中串口的写入地址:

if(data==’\n’) { while(!(rUTRSTAT0 & 0x2)); Delay(10); //because the slow response of hyper_terminal WrUTXH0(‘\r’); } while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty. Delay(5); WrUTXH0(data);

其中WrUTXH0宏定义为:

#define WrUTXH0(ch) (*(volatile unsigned char *)(0x1d00023))=(unsigned char)(ch)

即写入地址为0x01d00023。
而在内核中写入的地址:
文件linux-2.6.x/include/asm/arch/uncompress.c:

12 static int s3c44b0x_putc(char c) 13 { 14 while (!(SYSREG_GET(S3C44B0X_UTRSTAT0) & 0x2)); 15 SYSREG_SETB(S3C44B0X_UTXH0, c); 16 if(c == ‘\n’) 17 s3c44b0x_putc(‘\r’); 18 }

S3C44B0X_UTXH0在文件linux-2.6.x/include/asm/arch/s3c44b0x.h中定义:

287 #ifdef CONFIG_CPU_BIG_ENDIAN 288 #define S3C44B0X_UTXH0 0x01d00023 289 #define S3C44B0X_URXH0 0x01d00027 290 #else 291 #define S3C44B0X_UTXH0 0x01d00020 292 #define S3C44B0X_URXH0 0x01d00024 293 #endif

没找到CONFIG_CPU_BIG_ENDIAN,应该是LITTLE ENDIAN,发现地址和bootloader中不一样:0x01d00020。
这就是问题所在吗?
好吧,那咱就把地址改为0x01d00023再瞧瞧:

287 #ifdef CONFIG_CPU_BIG_ENDIAN 288 #define S3C44B0X_UTXH0 0x01d00023 289 #define S3C44B0X_URXH0 0x01d00027 290 #else 291 #define S3C44B0X_UTXH0 0x01d00023 292 #define S3C44B0X_URXH0 0x01d00027 293 #endif

可惜还是没有任何反应,顿时傻掉…… 

将要放弃前试了最后一招,对比了2.4.x中的串口写入函数:

15 static int s3c44b0_putc(char c) 16 { 17 CSR_WRITE(DEBUG_TX_BUFF_BASE, c); 18 while(!(CSR_READ(DEBUG_CHK_STAT_BASE) & DEBUG_TX_DONE_CHECK_BIT)); 19 20 if(c == ‘\n’) 21 s3c44b0_putc(‘\r’); 22 }

DEBUG_TX_BUFF_BASE在linux-2.4.x/include/asm/arch/hardware.h中定义:

#define DEBUG_TX_BUFF_BASE (Base_Addr+0x104020) #define DEBUG_RX_BUFF_BASE (Base_Addr+0x104024) #define DEBUG_UARTLCON_BASE (Base_Addr+0x104000) #define DEBUG_UARTCONT_BASE (Base_Addr+0x104004) #define DEBUG_UARTBRD_BASE (Base_Addr+0x104028) #define DEBUG_CHK_STAT_BASE (Base_Addr+0x104010)

其中Base_Addr为(0x1c00000),故DEBUG_TX_BUFF_BASE值为0x01d04020。发现它和2.6.x中的地址一致,于是把linux-2.6.x/include/asm/arch/uncompress.c中串口写入函数改为:

12 static int s3c44b0x_putc(char c) 13 { 14 while (!(SYSREG_GET(S3C44B0X_UTRSTAT1) & 0x2)); 15 SYSREG_SETB(S3C44B0X_UTXH1, c); 16 if(c == ‘\n’) 17 s3c44b0x_putc(‘\r’); 18 }

 

make后终于看到那行激动人心的字符:

Uncompressing Linux……………………….. done, booting the kernel.

虽然没有成功启动内核,但总算是跳过了这一坎,往前进了一步。
庆幸自己没有放弃……

— EOF —

为uClinux 2.6.x建立bzImage映像

又是一个相当郁闷的过程……

在之前我写的那篇《将uClinux(linux-2.6.14核)移植到Samsung S3C44B0X》中,由make(或make linux)生成的映像文件为linux-2.6.x/arch/arm/boot/Image,由于没有经过压缩,文件比较大,为1.3MB。于是就想到2.4里对映像文件进行压缩的方法。

打开Makefile,发现在make linux对应块中的“LINUXTARGET”没有定义:

    271 linux linux%_only:
    272         @if [ $(LINUXDIR) != linux-2.5.x -a $(LINUXDIR) != linux-2.6.x -a ! -f $(LINUXDIR)/.depend ] ; then \
    273                 echo "ERROR: you need to do a 'make dep' first" ; \
    274                 exit 1 ; \
    275         fi
    276         $(MAKEARCH_KERNEL) -C $(LINUXDIR) $(LINUXTARGET) || exit 1
    277         if [ -f $(LINUXDIR)/vmlinux ]; then \
    278                 ln -f $(LINUXDIR)/vmlinux $(LINUXDIR)/linux ; \
    279         fi

对比了一下2.4.x中的Makefile,发现在2.6.x的Makefile中少了对LINUXTARGET的赋值,于是在Makefile头部加上:

     41 BUILD_START_STRING = $(shell date)
     42 BUILD_START_UNIX = $(shell date +%s)
     43 LINUXTARGET = bzImage

ok,make一下,又出错了……

linux:/home/work/uclinux/uClinux-dist # make linux
make ARCH=arm CROSS_COMPILE=arm-elf- -C linux-2.6.x bzImage || exit 1
make[1]: Entering directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
  CHK     include/linux/version.h
make[2]: “include/asm-arm/mach-types.h”是最新的。
  CHK     include/linux/compile.h
  CHK     usr/initramfs_list
  Kernel: arch/arm/boot/Image is ready
  AS      arch/arm/boot/compressed/head-s3c44b0.o
arch/arm/boot/compressed/head-s3c44b0.S:114: unterminated character constant
make[3]: *** [arch/arm/boot/compressed/head-s3c44b0.o] 错误 1
make[2]: *** [arch/arm/boot/compressed/vmlinux] 错误 2
make[1]: *** [zImage] 错误 2
make[1]: Leaving directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
make: *** [linux] 错误 1

打开linux-2.6.x/arch/arm/boot/compressed/head-s3c44b0.S:

    114                                                 @ if delta is zero, we're
    115                 beq     not_relocated           @ running at the address we
    116                                                 @ were linked at.

这是一个该死的错误!!我查错花了大半个下午,却没想到问题出在注释里。AS把注释中的we’re的’re当作一个字符串的开始,真是%^$#%#@……保持其原意改为:

    114                                                 @ if delta is zero, we are
    115                 beq     not_relocated           @ running at the address we
    116                                                 @ were linked at.

make linux通过这个bug,又一个错误出现:

linux:/home/work/uclinux/uClinux-dist # make linux
make ARCH=arm CROSS_COMPILE=arm-elf- -C linux-2.6.x bzImage || exit 1
make[1]: Entering directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
  CHK     include/linux/version.h
make[2]: “include/asm-arm/mach-types.h”是最新的。
  CHK     include/linux/compile.h
  CHK     usr/initramfs_list
  Kernel: arch/arm/boot/Image is ready
  AS      arch/arm/boot/compressed/head-s3c44b0.o
  LD      arch/arm/boot/compressed/vmlinux
arch/arm/boot/compressed/head-s3c44b0.o(.text+0xd4): In function `wont_overwrite':
: undefined reference to `_load_addr'
make[3]: *** [arch/arm/boot/compressed/vmlinux] 错误 1
make[2]: *** [arch/arm/boot/compressed/vmlinux] 错误 2
make[1]: *** [zImage] 错误 2
make[1]: Leaving directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
make: *** [linux] 错误 1

打开linux-2.6.x/arch/arm/boot/compressed/Makefile,对比2.4.x版中的Makefile,发现其中没有对LOAD_ADDR进行替换:

     85 SEDFLAGS	= s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/

因此将其改为:

     85 SEDFLAGS	= s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;

然后打开linux-2.6.x/arch/arm/boot/compressed/vmlinux.lds.in:

     10 OUTPUT_ARCH(arm)
     11 ENTRY(_start)
     12 SECTIONS
     13 {
     14   . = TEXT_START;
     15   _text = .;
     16
     17   .text : {
     18     _start = .;

在第14行插入_load_addr的定义:

     10 OUTPUT_ARCH(arm)
     11 ENTRY(_start)
     12 SECTIONS
     13 {
     14   . = LOAD_ADDR;
     15   _load_addr = .;
     16
     17   . = TEXT_START;
     18   _text = .;
     19
     20   .text : {
     21     _start = .;

[注意] 请确保在配置内核时有将CONFIG_ZBOOT_ROM设置为y,否则ZTEXTADDR和ZBSSADDR的取值将不是我们需要的。linux-2.6.x/arch/arm/boot/compressed/Makefile中:

     77 ifeq ($(CONFIG_ZBOOT_ROM),y)
     78 ZTEXTADDR       := $(CONFIG_ZBOOT_ROM_TEXT)
     79 ZBSSADDR        := $(CONFIG_ZBOOT_ROM_BSS)
     80 else
     81 ZTEXTADDR       := 0
     82 ZBSSADDR        := ALIGN(4)
     83 endif

vendors/Samsung/44B0X/config.linux-2.6.x中默认没有设置CONFIG_ZBOOT_ROM,我们可以手动加上“CONFIG_ZBOOT_ROM=y”:

    142 #
    143 # Boot options
    144 #
    145 CONFIG_ZBOOT_ROM=y
    146 CONFIG_ZBOOT_ROM_TEXT=0x00010000
    147 CONFIG_ZBOOT_ROM_BSS=0x0C400000
    148 CONFIG_CMDLINE=""
    149 # CONFIG_XIP_KERNEL is not set

改变了配置选项后,我不知道怎么样局部修改配置,只好先make distclean,然后重新配置内核……
make linux时又出现问题:

linux:/home/work/uclinux/uClinux-dist # make linux
make ARCH=arm CROSS_COMPILE=arm-elf- -C linux-2.6.x bzImage || exit 1
make[1]: Entering directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
  CHK     include/linux/version.h
make[2]: “include/asm-arm/mach-types.h”是最新的。
  CHK     include/linux/compile.h
  CHK     usr/initramfs_list
  Kernel: arch/arm/boot/Image is ready
  AS      arch/arm/boot/compressed/head-s3c44b0.o
  CC      arch/arm/boot/compressed/misc.o
In file included from include/asm/arch/uncompress.h:31,
                 from arch/arm/boot/compressed/misc.c:23:
include/asm/arch/uncompress.c:8: warning: function declaration isn't a prototype
include/asm/arch/uncompress.c: In function `s3c44b0x_decomp_setup':
include/asm/arch/uncompress.c:9: warning: control reaches end of non-void function
include/asm/arch/uncompress.c: In function `s3c44b0x_putc':
include/asm/arch/uncompress.c:17: warning: control reaches end of non-void function
  LD      arch/arm/boot/compressed/vmlinux
arch/arm/boot/compressed/misc.o(.text+0x27b8): In function `flush_window':
/home/work/uclinux/uClinux-dist/linux-2.6.x/arch/arm/boot/compressed/misc.c:283: undefined reference to `putstr'
arch/arm/boot/compressed/misc.o(.text+0x2804): In function `error':
/home/work/uclinux/uClinux-dist/linux-2.6.x/arch/arm/boot/compressed/misc.c:288: undefined reference to `putstr'
arch/arm/boot/compressed/misc.o(.text+0x280c):/home/work/uclinux/uClinux-dist/linux-2.6.x/arch/arm/boot/compressed/misc.c:289: undefined reference to `putstr'
arch/arm/boot/compressed/misc.o(.text+0x2818):/home/work/uclinux/uClinux-dist/linux-2.6.x/arch/arm/boot/compressed/misc.c:290: undefined reference to `putstr'
arch/arm/boot/compressed/misc.o(.text+0x2884): In function `decompress_kernel':
/home/work/uclinux/uClinux-dist/linux-2.6.x/arch/arm/boot/compressed/misc.c:309: undefined reference to `putstr'
arch/arm/boot/compressed/misc.o(.text+0x2894):/home/work/uclinux/uClinux-dist/linux-2.6.x/arch/arm/boot/compressed/misc.c:311: more undefined references to `putstr' follow
make[3]: *** [arch/arm/boot/compressed/vmlinux] 错误 1
make[2]: *** [arch/arm/boot/compressed/vmlinux] 错误 2
make[1]: *** [zImage] 错误 2
make[1]: Leaving directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
make: *** [linux] 错误 1

很想杀人,但幸亏没有,不然这问题解决不了了。
打开linux-2.6.x/include/asm/arch/uncompress.h,把:

 39 #define puts(s)                       s3c44b0x_puts(s)

改为

 39 #define putstr(s)                       s3c44b0x_puts(s)

整个世界清静了……

linux:/home/work/uclinux/uClinux-dist # make linux
make ARCH=arm CROSS_COMPILE=arm-elf- -C linux-2.6.x bzImage || exit 1
make[1]: Entering directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
  CHK     include/linux/version.h
make[2]: “include/asm-arm/mach-types.h”是最新的。
  CHK     include/linux/compile.h
  CHK     usr/initramfs_list
  Kernel: arch/arm/boot/Image is ready
  AS      arch/arm/boot/compressed/head-s3c44b0.o
  CC      arch/arm/boot/compressed/misc.o
In file included from include/asm/arch/uncompress.h:31,
                 from arch/arm/boot/compressed/misc.c:23:
include/asm/arch/uncompress.c: In function `s3c44b0x_putc':
include/asm/arch/uncompress.c:13: warning: control reaches end of non-void function
  LD      arch/arm/boot/compressed/vmlinux
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready
make[1]: Leaving directory `/home/work/uclinux/uClinux-dist/linux-2.6.x'
if [ -f linux-2.6.x/vmlinux ]; then \
        ln -f linux-2.6.x/vmlinux linux-2.6.x/linux ; \
fi

看看结果:

linux:/home/work/uclinux/uClinux-dist # ll linux-2.6.x/arch/arm/boot/
总用量 1980
drwxrwxrwx   4 root root     272 2006-09-07 20:21 .
drwxrwxrwx  41 root root    1304 2006-09-07 17:24 ..
drwxrwxrwx   2 root root     176 2005-10-28 08:02 bootp
drwxrwxrwx   2 root root    1048 2006-09-07 20:21 compressed
-rwxr-xr-x   1 root root 1400132 2006-09-07 20:16 Image
-rw-r--r--   1 root root     106 2006-09-07 20:16 .Image.cmd
-rw-rw-rw-   1 root root    1326 2005-10-28 08:02 install.sh
-rw-rw-rw-   1 root root    2405 2005-10-28 08:02 Makefile
-rwxr-xr-x   1 root root  605384 2006-09-07 20:21 zImage
-rw-r--r--   1 root root     133 2006-09-07 20:21 .zImage.cmd

差点没哭出来……

— EOF —