2009年12月19日 星期六

Linux作業系統中記憶體buffer和cache的區別

Free

  free 命令相對於top 提供了更簡潔的查看系統記憶體使用情況:
 
  $ free
  total used free shared buffers cachedMem: 255268 238332 16936 0 85540 126384-/+ buffers/cache: 26408 228860Swap: 265000 0 265000
 
Mem:表示物理記憶體統計
  -/+ buffers/cached:表示物理記憶體的緩存統計
  Swap:表示硬盤上交換分區的使用情況。
 
  系統的總物理記憶體:255268Kb256M),但系統當前真正可用的記憶體b並不是第一行free 標記的 16936Kb,它僅代表未被分配的記憶體。
 
  我們使用total1used1free1used2free2 等名稱來代表上面統計數據的各值,12 分別代表第一行和第二行的數據。
 
  total1:表示物理記憶體總量。
 
  used1:表示總計分配給緩存(包含buffers cache )使用的數量,但其中可能部分緩存並未實際使用。
 
  free1:未被分配的記憶體。
 
  shared1:共用記憶體,一般系統不會用到。
 
  buffers1:系統分配但未被使用的buffers 數量。
 
  cached1:系統分配但未被使用的cache 數量。buffer cache 的區別見後面。
 
  used2:實際使用的buffers cache 總量,也是實際使用的記憶體總量。
 
  free2:未被使用的buffers cache 和未被分配的記憶體之和,這就是系統當前實際可用記憶體。
 
  可以整理出如下等式:
  total1 = used1 + free1total1 = used2 + free2used1 = buffers1 + cached1 + used 2f ree2 = buffers1 + cached1 + free1
 
  buffer cache 的區別
  A buffer is something that has yet to be "written" to disk. A cache is something that has been "read" from the disk and stored for later use.
 
  更詳細的解釋參考:Difference Between Buffer and Cache
 
  對於共用記憶體(Shared memory),主要用於在UNIX 環境下不同進程之間共用數據,是進程間通信的一種方法,一般的應用程式不會申請使用共用記憶體,筆者也沒有去驗證共用記憶體對上面等式的影響。如果你有興趣,請參考:What is Shared Memory?
 
  cache buffer的區別:
  Cache:高速緩存,是位於CPU與主記憶體間的一種容量較小但速度很高的記憶體。由於CPU的速度遠高於主記憶體,CPU直接從記憶體中存取數據要等待一定時間週期,Cache中保存著CPU剛用過或迴圈使用的一部分數據,當CPU再次使用該部分數據時可從Cache中直接調用,這樣就減少了CPU的等待時間,提高了系統的效率。Cache又分為一級Cache(L1 Cache)和二級Cache(L2 Cache)L 1 C ache集成在CPU內部,L 2 C ache早期一般是焊在主板上,現在也都集成在CPU內部,常見的容量有256KB512KB L2 Cache
 
  Buffer:緩衝區,一個用於存儲速度不同步的設備或優先級不同的設備之間傳輸數據的區域。通過緩衝區,可以使進程之間的相互等待變少,從而使從速度慢的設備讀入數據時,速度快的設備的操作進程不發生間斷。
 
  Free中的buffercache:(它們都是佔用記憶體):
 
  buffer : 作為buffer cache的記憶體,是塊設備的讀寫緩衝區
 
  cache: 作為page cache的記憶體, 文件系統的cache
 
  如果 cache 的值很大,說明cache住的文件數很多。如果頻繁訪問到的文件都能被cache住,那麼磁片的讀IO bi會非常小。

2009年11月5日 星期四

建立root fs 系統檔案

製作root.cramfs系统檔案

主機:
ubuntu 8.04 (linux-2.6.24-19)
gcc-4.2

目標板:
s3c2440
linux-2.6.26
cramfs


工具:
busybox-1.9.2
arm-linux-gcc-4.0.3
-------------------------------------------------------------
參考資料:
BusyBox——嵌入式Linux中的瑞士軍刀
使用busybox-1.9.2製作根文件系統
使用Busybox製作CRAMFS文件系統成功
-------------------------------------------------------------
1. 修改Makefile



#ARCH        ?= $(SUBARCH)

#CROSS_COMPILE    ?=
ARCH        := arm
CROSS_COMPILE    :=  /usr/local/arm/4.0.3/bin/arm-linux-


2. 修改applets/applets.c中的警告信息



//#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__)

#if 0
#warning Static linking against glibc produces buggy executables
#warning (glibc does not cope well with ld --gc-sections).
#warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
#warning Note that glibc is unsuitable for static linking anyway.
#warning If you still want to do it, remove -Wl,--gc-sections
#warning from scripts/trylink and remove this warning.
#error Aborting compilation.
#endif


這段警告的意思是告訴你最好用uclibc編譯,而不是用glibc因為glibc比較大,busybox在嵌入式系統中運用比較多,所以會有這樣的要求。
如果沒有註解掉這段警告或者沒有採用uclibc的話,在make install的時候則會出現如下的錯誤:


applets/applets.c:15:2: warning: #warning Static linking against glibc produces buggy executables

applets/applets.c:16:2: warning: #warning (glibc does not cope well with ld --gc-sections).
applets/applets.c:17:2: warning: #warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
applets/applets.c:18:2: warning: #warning Note that glibc is unsuitable for static linking anyway.
applets/applets.c:19:2: warning: #warning If you still want to do it, remove -Wl,--gc-sections
applets/applets.c:20:2: warning: #warning from scripts/trylink and remove this warning.
applets/applets.c:21:2: #error Aborting compilation.
make[1]: *** [applets/applets.o] 錯誤 1
make: *** [applets] 錯誤 2


3. 設定busybox
首先可以先恢復一下預設設定




$ make defconfig

然後,設定busybox可以採用如下命令:


$ make menuconfig





$ make xconfig


配置內容參考
使用busybox-1.9.2製作根文件系統
使用Busybox製作CRAMFS文件系統成功

4. 編譯busybox


$ make install


如果成功,會有如下信息:


--------------------------------------------------

You will probably need to make your busybox binary
setuid root to ensure all configured applets will
work properly.
--------------------------------------------------

並在busybox/_install 目錄下會生成下列文件:

drwxr-xr-x  5 wang wang 4096 2008-09-10 17:32 .

drwxr-xr-x 32 wang wang 4096 2008-09-10 17:32 ..
drwxr-xr-x  2 wang wang 4096 2008-09-10 17:32 bin
lrwxrwxrwx  1 wang wang   11 2008-09-10 17:32 linuxrc -> bin/busybox
drwxr-xr-x  2 wang wang 4096 2008-09-10 17:32 sbin
drwxr-xr-x  4 wang wang 4096 2008-09-10 17:32 usr


5. 修改_install/bin/busybox的屬性


$ chmod 755 ./_install/bin/busybox


必須要要修改屬性,否則在busybox中很多命令會受限制

6. 打包_install文件夾的內容
首先要刪除_install/linuxrc文件,這個文件將在後面重新被創建,這裡先刪除


$ rm ./_install/linuxrc


然後將剩下的三個文件夾打包起來




$ tar cvf 1.tar ./_install/


7. 建立cramfs所需的一些目錄
   首先創建一個文件夾,作為暫時存放cramfs的臨時目錄,以方便在其下建立文件系統。所有命令如下


$ mkdir /opt/rootfs

$ cd /opt/rootfs
$ mkdir bin dev etc home lib mnt proc sbin sys tmp  var usr
$ mkdir etc/init.d


(其中假設你的普通用戶已經取得/opt的操作所有權或者也可以放在用戶目錄下完成; 總之,對於rootfs及其以下目錄,當前用戶必須擁有讀、寫和執行的所有權)

8. 準備啟動所需的文件:linuxrc、rcS、inittab、fstab四個文件
(以下均假定當前路徑在/opt/rootfs)
a. linuxrc

$ vim linuxrc


內容:


#!/bin/sh

echo "mount /etc as ramfs"
/bin/mount -f -t cramfs -o remount,ro /dev/bon/2 /
/bin/mount -t ramfs ramfs /var
/bin/mkdir -p /var/tmp
/bin/mkdir -p /var/run
/bin/mkdir -p /var/log
/bin/mkdir -p /var/lock
/bin/mkdir -p /var/empty
#/bin/mount -t usbdevfs none /proc/bus/usb

exec /sbin/init


更改其所有權:



$ chmod 775 linuxrc



b. rcS


$ vim etc/init.d/rcS


內容:





#!/bin/sh


# mount all filesystem defined in "fstab"
echo "#mount all......."
/bin/mount -a



更改其所有權:


$ chmod 775 etc/init.d/rcS


c. inittab



$ vim etc/inittab

內容:


# This is run first except when booting

::sysinit:/etc/init.d/rcS

# Start an "askfirst" shell on the console
#::askfirst:-/bin/bash
::askfirst:-/bin/sh

# Stuff to do when restarting the init process
::restart:/sbin/init

# Stuff to do before rebooting
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r


d. fstab



$ vim etc/fstab


內容:

none        /proc        proc    defaults    0 0

none        /dev/pts    devpts    mode=0622    0 0
tmpfs        /dev/shm    tmpfs    defaults    0 0


9. 建立節點console、null
否則就會提示「Warning: unable to open an initial console. Kernel panic - not syncing: Attempted to kill init!」的類似錯誤。
創建時,必須以root身份才可以



sudo mknod -m 600 dev/console c 5 1

sudo mknod -m 666 dev/null c 1 3


10. 使用之前打包的_install
將剛才在busybox的_install下的三個文件夾的打包文件複製到rootfs目錄,解壓後刪除打包文件。


$ tar xvf 1.tar

$ rm 1.tar


11. 複製常用的lib文件
我用的是開發板的文件系統中的lib/下的文件,直接拷貝過來用

12. 建立cramfs


$ cd /opt

$ mkcramfs rootfs s3c2440.cramfs


如果沒有mkcramfs命令,請執行下面指令安裝



$sudo apt-get install cramfsprogs


13. 燒錄文件系統到開發板並啟動

啟動信息:


Read chip id = ec76

Nand flash status = c0
Set boot params = root=/dev/mtdblock2 init=/linuxrc load_ramdisk=0 console=ttySAC1,115200 mem=65536K devfs=mount display=shp480
Load Kernel...
Linux version 2.6.26 (wang@wang-desktop) (gcc version 4.1.0) #7 Thu Sep 4 17:18:03 CST 2008
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
Machine: SMDK2440
ATAG_INITRD is deprecated; please update your bootloader.
Memory policy: ECC disabled, Data cache writeback
CPU S3C2440A (id 0x32440001)
S3C244X: core 400.000 MHz, memory 100.000 MHz, peripheral 50.000 MHz
S3C24XX Clocks, (c) 2004 Simtec Electronics
CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on
CPU0: D VIVT write-back cache
CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets
CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 16256
Kernel command line: root=/dev/mtdblock2 init=/linuxrc load_ramdisk=0 console=ttySAC1,115200 mem=65536K devfs=mount display=shp480
irq: clearing pending ext status 00056200
irq: clearing pending ext status 00000200
irq: clearing subpending status 00000092
PID hash table entries: 256 (order: 8, 1024 bytes)
timer tcon=00590000, tcnt a2c1, tcfg 00000200,00000000, usec 00001eb8
Console: colour dummy device 80x30
console [ttySAC1] enabled
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 64MB = 64MB total
Memory: 61708KB available (2732K code, 277K data, 124K init)
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
net_namespace: 192 bytes
NET: Registered protocol family 16
S3C2440: Initialising architecture
S3C2440: IRQ Support
S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics
DMA channel 0 at c4800000, irq 33
DMA channel 1 at c4800040, irq 34
DMA channel 2 at c4800080, irq 35
DMA channel 3 at c48000c0, irq 36
S3C244X: Clock Support, DVS off
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 2048 (order: 2, 16384 bytes)
TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 2048 bind 2048)
TCP reno registered
NET: Registered protocol family 1
NetWinder Floating Point Emulator V0.97 (double precision)
msgmni has been set to 120
io scheduler noop registered
io scheduler anticipatory registered (default)
io scheduler deadline registered
io scheduler cfq registered
Console: switching to colour frame buffer device 30x40
fb0: s3c2410fb frame buffer device
s3c2440-uart.0: s3c2410_serial0 at MMIO 0x50000000 (irq = 70) is a S3C2440
s3c2440-uart.1: s3c2410_serial1 at MMIO 0x50004000 (irq = 73) is a S3C2440
s3c2440-uart.2: s3c2410_serial2 at MMIO 0x50008000 (irq = 76) is a S3C2440
brd: module loaded
usbcore: registered new interface driver ub
dm9000 Ethernet Driver, V1.30
Linux video capture interface: v2.00
NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c $Revision: 1.41 $
S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=1, 10ns Twrph0=4 40ns, Twrph1=1 10ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8-bit)
s3c2410_nand_update_chip: chip c3d784bc: 9
NAND_ECC_NONE selected by board driver. This is not recommended !!
Scanning device for bad blocks
Bad eraseblock 357 at 0x00594000
Bad eraseblock 3335 at 0x0341c000
Bad eraseblock 3995 at 0x03e6c000
Creating 4 MTD partitions on "NAND 64MiB 3,3V 8-bit":
0x00000000-0x00030000 : "boot"
0x00030000-0x00200000 : "kernel"
0x00200000-0x02000000 : "rootfs"
0x02000000-0x04000000 : "ext-fs1"
usbmon: debugfs is not available
s3c2410-ohci s3c2410-ohci: S3C24XX OHCI
s3c2410-ohci s3c2410-ohci: new USB bus registered, assigned bus number 1
s3c2410-ohci s3c2410-ohci: irq 42, io mem 0x49000000
usb usb1: configuration #1 chosen from 1 choice
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 2 ports detected
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
mice: PS/2 mouse device common for all mice
i2c /dev entries driver
s3c2440-i2c s3c2440-i2c: slave address 0x10
s3c2440-i2c s3c2440-i2c: bus frequency set to 390 KHz
s3c2440-i2c s3c2440-i2c: i2c-0: S3C I2C adapter
S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics
s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled
Registered led device: led4
Registered led device: led5
Registered led device: led6
Registered led device: led7
TCP cubic registered
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
VFS: Mounted root (cramfs filesystem) readonly.
Freeing init memory: 124K
mount /etc as ramfs
init started: BusyBox v1.9.2 (2008-09-05 13:54:16 CST)
starting pid 813, tty '': '/etc/init.d/rcS'
#mount all.......
mount: mounting none on /dev/pts failed: No such file or directory
mount: mounting tmpfs on /dev/shm failed: No such file or directory
# starting mdev....
******************************************
 linux-2.6.26 boot           
 2008-9-5            
                      
******************************************

Please press Enter to activate this console.
starting pid 815, tty '': '/bin/sh'
/ #
/ #


就可以進入console了~~!!

2009年10月9日 星期五

ubuntu9.04 install svnserver


 使用方式

 Subversion完整的參考文件
 

     (英) http://svnbook.red-bean.com/
     (中) http://svn.stu.edu.tw/svnbook/
 (Q&A) http://subversion.tigris.org/faq.html

  如何安裝 svn server 端
 #apt-get install subversion

 與Apache 結合的套件
 #apt-get install libapache2-svn

  如何使用 svn server 端
 

 建立起 svn 的 repository 的 tree

 其主要有三種類型的指令
 1. 管理 (svnadmin) : 主要是控制及管理project版本的整體的架構
 2. 查詢 (svnlook) : 主要是查詢關於版本的相關資訊
 3. 服務 (svnserve) : 處理其對外的服務設定

 使用最基本的架設....
 建立使用群組
 #groupadd  svnbrook
 將自己(brook)及www-data(apapch2帳戶)添加至該群組 #vi /etc/group
 最後一行就改成 svnbrook:x:1001:brook,www-data #mkdir /home/svn      #建立svn資料庫的父路徑 #cd /home/svn
 #mkdir myproject01   #建立svn資料庫的專案目錄
 #svnadmin create /home/svn/myproject01 #使用svn命令,建立svn資料庫
 
此時你可以在 myproject01 下看到
 conf dav db format hooks locks README.txt
 
#chown -R root:subversion myproject         #更改目錄群組
 
#chmod -R g+rws myproject   #增加群組用戶讀寫及新增目錄權限

 若要用 apache 做為連結管道再設定下述 (在此我們用 ssh 做為連結管道)
 

 #vi /etc/apach2/mods-available/dav_svn.conf

 加入
 
<Location /svn/myproject01>
    DAV svn
    SVNPath /home/svn/myproject01
    AuthType Basic
    AuthName "myproject subversion repository"
   AuthUserFile /etc/subversion/passwd
  <LimitExcept GET PROPFIND OPTIONS REPORT>
     Require valid-user
  </LimitExcept>
 </Location>

 再增加使用者及密碼
 
#cd /etc/subversion
 #touch passwd
 #htpasswd -c passwd brook
 New password:
 Re-type new password:
 Adding passwd for user brook

 重新啟動  apache2
 

 #/etc/init.d/apache2 restart

 你就可以在網頁上看到資訊

  如何使用 svn client 端
 

 其主要指令是 svn 來處理..基本的資料夾加刪...

 當我們是用 ssh 連入 svn server 時
 svn co svn+ssh://使用者id@hostname或hostip/home/svn/myproject
 連續輸入兩次使用者密碼即可進入

 先使用 svn update 更新至最新版本  以避免  conflict
 再使用 svn commit
 要將資料上傳時   再使用 svn add + 增加的資料夾及檔案
 這時會有設定...將要上傳的資料加入 設定中即可

  如何使用 svn 更新檔案

 在 svn server 端  是記載版本的變更

 故..................看不到修定的版本結果

 在 svn server 須使用 checkout 將 更新的資訊取出
 在本機新開的資料夾  brooktmp
 #mkdir brooktmp
 #cd brooktmp
 #svn co file:///home/svn/myproject01
 在 brooktmp 的資料夾中  即有最新版本的  myproject01 的資料夾

  將 svn server 連上網路使用

 你可以使用 Apache2 的 http 網頁設定  或 其 SSL 的 https 設定
 在此我們是使用 ssh 連入

 先用  WINSCP 將新更改的版本  Copy (F5) 置入  電腦 (Server 或另一台電腦)

 再用  svn 更新之   (另一台電腦需再用 ssh+svn 連入 Server)


 reference:
 http://svn.stu.edu.tw/svnbook/book.html#svn-ch-5-sect-4.2.1
 http://www.duduwolf.com/post/setting_up_subversion.asp
 http://www.blogjava.net/pandawang/archive/2006/09/05/67844.html

2009年10月6日 星期二

U-Boot的移植



U-Boot Practically Porting Guide



Author: Aaron Wong aaronwong@engineer.com




U-Boot的移植之()進階篇:從源代碼看系統啟動過程



為什麼要分析源代碼?分析優秀的源代碼本身就是一個學習的過程,也是進行深入研究的必經之路。不過在此我們的主要目的並非要研究U-bootBootloader技術本身,而僅僅是為了成功的並且恰當的將U-Boot移植到我們的開發板上。只有結合源代碼瞭解了U-boot的系統引導過程,才能在移植和調試過程中保持清晰的思路,才能在碰到困難和問題時從根本上加以解決。



在動手分析之前,至少應該對U-Boot的源代碼結構有基本的瞭解,很多參考書都有這方面的介紹,華清遠見的《嵌入式Linux系統開發技術詳解——基於ARM》的講解就比較清晰。



本文以lubbock開發板為例,以系統啟動的流程為線索進行縱向分析:後續的移植工作也將以此開發板為模板。Lubbock使用PXA255處理器。



首先要找到程序入口點。從board/lubbock/u-boot.lds可以發現,u-boot的程序入口為_start,在cpu/pxa/start.o當中。因此首先要分析start.S程序,U-Boot中所有的PXA系列的處理器都從這裡開始執行第一條語句。






.globl _start




_start: b reset




ldr pc, _undefined_instruction




ldr pc, _software_interrupt




ldr pc, _prefetch_abort




ldr pc, _data_abort




ldr pc, _not_used




ldr pc, _irq




ldr pc, _fiq




0x0地址開始是ARM異常向量表,學過ARM體系結構與編程的都明白,非常簡單,不多廢話。一上電的第一條指令是跳轉到reset復位處理程序:






reset:




/* 進入SVC模式 */




#ifndef CONFIG_SKIP_LOWLEVEL_INIT




bl cpu_init_crit /* we do sys-critical inits */




#endif




#ifndef CONFIG_SKIP_RELOCATE_UBOOT




relocate:




......




一般不要定義CONFIG_SKIP_LOWLEVEL_INIT,因此,接下來跳轉到cpu_init_crit處開始執行:






cpu_init_crit:




/* 屏蔽所有中斷 */




/* 設置時鍾源,關閉除FFUART,SRAM,SDRAM,FLASH以外的外設時鍾 */




......




#ifdef CFG_CPUSPEED




ldr r0, CC_BASE /* 時鍾控制寄存器基址 */




ldr r1, cpuspeed




/* cpuspeed: .word CFG_CPUSPEED */




str r1, [r0, #CCCR]




mov r0, #2




mcr p14, 0, r0, c6, c0, 0









setspeed_done:




#endif /* CFG_CPUSPEED */




/* 跳轉到lowlevel_init,這裡ipr12,用作暫存寄存器 */




mov ip, lr




bl lowlevel_init




mov lr, ip




/* Memory interfaces are working. Disable MMU and enable I-cache. */




ldr r0, =0x2001




......




/* 關閉MMU,使能I-Cache(可選) */




mov pc, lr /* 這裡是從cpu_init_crit返回到relocate標號 */









可見,在cpu_init_crit中的主要工作是設置時鍾,配置處理器主頻(這時CPU的工作頻率還沒有改變),調用lowlevel_init函數進行底層初始化(包括調整處理器工作頻率、系統總線頻率、存儲器時鍾頻率以及存儲系統的初始化等工作),隨後關閉MMU並使能I-Cache,再返回。



lowlevel_init函數在board/lubbock/lowlevel_init.S中定義,其流程都是按照PXA27X的開發手冊來的,所以不再贅述。僅指出,其中的寄存器在include/asm-arm/arch-pxa/pxa-regs.h頭文件中定義,寄存器初始化值在include/configs/lubbock.h中定義。另外,在後面的實際移植工作中,由於目標板XSBASE270使用的PXA270處理器,可使用adsvix開發板的lowlevel_init.S文件(lubbock中沒有開啟turbo模式)



接著程序的執行線索進行分析。從cpu_init_crit返回後就開始relocate(重定位),即將U-bootFLASH存儲器搬運到SDRAMTEXT_BASE開始的存儲空間(TEXT_BASEboard/lubbock/config.mk中定義),並初始化堆棧(清零.bss),以在SDRAM中開始進入到Bootloader stage 2C程序入口。Relocate部分開始的代碼如下:






/* 之前已定義的部分變量有:




_TEXT_BASE: .word TEXT_BASE




_armboot_start: .word _start




_bss_start: .word __bss_start




_bss_end: .word _end */




relocate: /* relocate U-Boot to RAM */




adr r0, _start /* r0 <- current position of code */




ldr r1, _TEXT_BASE /* test if we run from flash or RAM */




cmp r0, r1 /* don't reloc during debug */




beq stack_setup









ldr r2, _armboot_start /* 讀入_startr2 */




ldr r3, _bss_start /* 讀入__bss_startr3 */




sub r2, r3, r2 /* r2 <- size of armboot */




add r2, r0, r2 /* r2 <- source end address */









copy_loop:




ldmia r0!, {r3-r10} /* copy from source address [r0] */




stmia r1!, {r3-r10} /* copy to target address [r1] */




cmp r0, r2 /* until source end addreee [r2] */




ble copy_loop









/* Set up the stack */




stack_setup:




ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */




sub r0, r0, #CFG_MALLOC_LEN /* malloc area */




sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */




#ifdef CONFIG_USE_IRQ




sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)




#endif




sub sp, r0, #12 /* leave 3 words for abort-stack */









clear_bss:




ldr r0, _bss_start /* find start of bss segment */




ldr r1, _bss_end /* stop here */




mov r2, #0x00000000 /* clear */









clbss_l:str r2, [r0] /* clear loop... */




add r0, r0, #4




cmp r0, r1




ble clbss_l









ldr pc, _start_armboot









_start_armboot: .word start_armboot




這是很經典的一段代碼,相信學習凡是過ARM編程的,都分析過這段代碼,所以也不再贅述。之所以列出這段代碼,一是為了找到C程序入口start_armboot,二是為了給出U-Boot的一個存儲器映射圖:







這個圖可以幫助我們更好地理解後續的C語言代碼以及U-Boot對內存的分配與使用情況。



接下來進入到Bootloader Stage 2C語言代碼部分,入口是start_armboot,對應的源文件是lib_arm/board.c,這一文件對所有的ARM處理器都是通用的,因此在移植的時候不用修改。相關源代碼如下:






DECLARE_GLOBAL_DATA_PTR




/* include/asm-arm/global_data.h中定義的一個全局寄存器變量的聲明:




* #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")




* 用於存放全局數據結構體gd_t的地址。




*/




void start_armboot (void)




{




init_fnc_t **init_fnc_ptr;




char *s;




#ifndef CFG_NO_FLASH




ulong size;




#endif




#if defined(CONFIG_VFD) || defined(CONFIG_LCD)




/* 本次移植暫不配置VFDLCD,後面也將不考慮的部分略去 */




/* 初始化全局數據結構體指針gd */




gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));




....../* memsetlib_generic/string.c中定義*/




memset ((void*)gd, 0, sizeof (gd_t)); /*0填充全局數據表*gd */




gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));




memset (gd->bd, 0, sizeof (bd_t)); /*0填充(初始化) *gd->bd */









monitor_flash_len = _bss_start - _armboot_start;









for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {




if ((*init_fnc_ptr)() != 0) {




hang (); /* 打印錯誤信息並死鎖 */




}




}









#ifndef CFG_NO_FLASH




/* configure available FLASH banks */




size = flash_init (); /* drivers/cfi_flash.c或自定義 */




display_flash_config (size);




#endif /* CFG_NO_FLASH */









/*armboot_start is defined in the board-specific linker script*/




mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);




......




/* initialize environment */




env_relocate ();









/* IP Address */




gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");




/* MAC Address */




{




int i;




ulong reg;




char *s, *e;




char tmp[64];









i = getenv_r ("ethaddr", tmp, sizeof (tmp));




s = (i > 0) ? tmp : NULL;









for (reg = 0; reg < 6; ++reg) {




gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;




if (s)




s = (*e) ? e + 1 : e;




}




}









devices_init (); /* get the devices list going. */




.......




jumptable_init ();




console_init_r (); /* fully init console as a device */




enable_interrupts (); /* enable exceptions */









/* Perform network card initialisation if necessary */




#if defined(CONFIG_DRIVER_SMC91111)| |defined (CONFIG_DRIVER_LAN91C96)




if (getenv ("ethaddr"))




smc_set_mac_addr(gd->bd->bi_enetaddr);




#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */









/* Initialize from environment */




if ((s = getenv ("loadaddr")) != NULL) {




load_addr = simple_strtoul (s, NULL, 16);




}









#if defined(CONFIG_CMD_NET)




if ((s = getenv ("bootfile")) != NULL)




copy_filename (BootFile, s, sizeof (BootFile));




#endif




#ifdef BOARD_LATE_INIT




board_late_init ();




#endif




......




/*main_loop() can return to retry autoboot, if so just run it again.*/




for (;;) {




main_loop ();




}




}




gd_t是全局數據表類型,在include/asm-arm/global_data.h中定義如下:






/*




Keep it *SMALL* and remember to set CFG_GBL_DATA_SIZE > sizeof(gd_t)




*/




typedef struct global_data {




bd_t *bd;




unsigned long flags;




unsigned long baudrate;




unsigned long have_console; /* serial_init() was called */




unsigned long reloc_off; /* Relocation Offset */




unsigned long env_addr; /* Address of Environment struct */




unsigned long env_valid; /*Checksum of Environment valid?*/




unsigned long fb_base; /* base address of frame buffer */




.......




void **jt; /* jump table */




} gd_t;




其中,bd_tinclude/asm-arm/u-boot.h中定義如下:






typedef struct bd_info {




int bi_baudrate; /* serial console baudrate */




unsigned long bi_ip_addr; /* IP Address */




unsigned char bi_enetaddr[6]; /* Ethernet adress */




struct environment_s *bi_env;




ulong bi_arch_number; /* unique id for this board */




ulong bi_boot_params; /* where this board expects params*/




struct /* RAM configuration */




{




ulong start;




ulong size;




}bi_dram[CONFIG_NR_DRAM_BANKS];




} bd_t;




jt是函數數組指針,隨後將在jumptable_init()函數中初始化。



lib_arm/board.c的源碼不難分析出系統的啟動流程:首先初始化全局數據表,然後順序執行函數指針數組init_sequence中的一系列初始化函數——由其在本文件中的相關定義可得知初始化流程:






typedef int (init_fnc_t) (void);




init_fnc_t *init_sequence[] = {




cpu_init, /* basic cpu dependent setup -- cpu/pxa/cpu.c */




board_init, /* basic board setup --board/lubbock/lubbock.c */




interrupt_init, /* set up exceptions -- cpu/pxa/interrupts.c */




env_init, /* initialize environment -- common/env_flash.c */




init_baudrate, /* initialze baudrate settings--lib_arm/board.c */




serial_init, /* serial communications setup--cpu/pxa/serial.c */




console_init_f, /* stage 1 init of console -- common/console.c */




display_banner, /* say that we are here -- lib_arm/board.c */




#if defined(CONFIG_DISPLAY_BOARDINFO)




checkboard, /* display board info */




#endif




dram_init, /* configure available RAM banks --board/lubbock/lubbock.c */




display_dram_config, /* lib_arm/board.c */




NULL,




};




在執行這個函數序列的過程中,任何一個函數異常返回都會導致u-boot「死鎖」或說「掛起」在hang()函數的死循環當中。



若一切順利,接下來就調用flash_init()函數初始化CFI FLASH(針對NOR型閃存而言),該函數在drivers/cfi_flash.c中定義,不過,只有在目標板頭文件中」#define CFG_FLASH_CFI_DRIVER」之後該驅動才會被編譯;在lubbocku-boot實現當中,include/configs/lubbock.h中沒有定義CFG_FLASH_CFI_DRIVER,而是在board/lubbock/ flash.c中實現了自己的FLASH驅動,包括flash_init()在內。在移植U-Boot時,可以根據實際情況選擇使用U-Boot自帶的FLASH驅動還是自己編寫新的驅動。如果配置了NAND閃存,還會對其進行初始化;筆者的XSABSE270板沒有銲接NAND FLASH,故對此不作討論。



接下來調用env_relocate()函數初始化環境變量,該函數在common/env_common.c文件中定義。在同一文件中可以發現還定義了一個字符數組default_environment[],用於描述缺省的環境變量,這些都要在include/configs/lubbock.h頭文件中進行設置,包括啟動命令CONFIG_BOOTCOMMAND,波特率CONFIG_BAUDRATEIP地址CONFIG_IPADDR等等。



然後是獲取自設置的目標板的網絡地址,包括IP地址和MAC地址。



再然後是調用common/devices.c中定義的devices_init()函數來創建設備列表,並初始化相應的設備,主要是」stdin」,」stdout」,」stderr」以及自定義的設備如I2CLCD等。這些相關代碼是與平台無關的,因此從移植的角度考慮,不必作細緻的研究與分析。



接著調用common/exports.c中定義的jumptable_init()函數,初始化全局數據表中的跳轉表gd->jt,跳轉表是一個函數指針數組,定義了u-boot中基本的常用的函數庫;而gd->jt是這個函數指針數組的首指針。部分代碼如下:






void jumptable_init (void) {




int i;




gd->jt = (void **) malloc (XF_MAX * sizeof (void *));




for (i = 0; i < XF_MAX; i++)




gd->jt[i] = (void *) dummy;




gd->jt[XF_get_version] = (void *) get_version;




gd->jt[XF_malloc] = (void *) malloc;




gd->jt[XF_free] = (void *) free;




gd->jt[XF_getenv] = (void *) getenv;




gd->jt[XF_setenv] = (void *) setenv;




......




}




上面的XF_get_versionXF_mallocXF_free等在include/exports.h的枚舉變量中定義,因此,實際上是作為」Label式整型序號」使用,即XF_get_version=1, XF_malloc=2, XF_free=3 ...,相關代碼如下:






enum { /* include/exports.h */




#define EXPORT_FUNC(x) XF_ ## x ,




#include <_exports.h>




#undef EXPORT_FUNC




XF_MAX



};




EXPORT_FUNC(get_version)




EXPORT_FUNC(getc)




EXPORT_FUNC(tstc)




EXPORT_FUNC(putc)




EXPORT_FUNC(puts)




EXPORT_FUNC(printf)




......... /* include/_exports.h */




由於這些也是平台無關的代碼,因此在移植過程中也不必深究。



然後是調用common/console.c中定義的函數console_init_r()初始化串口控制台,這同樣是平台無關的代碼,所以不必關心。



這時U-Boot的基本功能已經初始化完畢,便可開中斷,並進行附加功能的配置與初始化,包括網卡驅動配置,目標板使用LAN91C1111網卡,對應SMC91111網卡驅動,可以根據需要配置其他的網卡驅動如CS8900等,這些都在include/configs/lubbock.h中定義。



然後是調用board/lubbock/lubbock.c中定義的board_late_init()函數進行板級的後期初始化,實際上是配置stdoutstderr的硬件設備。相關源代碼如下:






int board_late_init(void)




{




setenv("stdout", "serial");




setenv("stderr", "serial");




return 0;




}








最後需要注意的一個很重要的文件是lib_arm/armlinux.c,它實現的功能包括設置內核啟動參數,並負責將這些參數傳遞給內核,最後跳轉到Linux內核入口函數,將控制權交給內核。



具體傳遞哪些參數,是通過在include/configs/lubbock.c中指定條件編譯選項來控制的,對應於lib_arm/armlinux.c中的部分源代碼形式如下:






#if defined (CONFIG_SETUP_MEMORY_TAGS) || \




defined (CONFIG_CMDLINE_TAG) || \




defined (CONFIG_INITRD_TAG) || \




defined (CONFIG_SERIAL_TAG) || \




defined (CONFIG_REVISION_TAG)




static void setup_start_tag (bd_t *bd);









# ifdef CONFIG_SETUP_MEMORY_TAGS




static void setup_memory_tags (bd_t *bd);




# endif




static void setup_commandline_tag (bd_t *bd, char *commandline);









# ifdef CONFIG_INITRD_TAG




static void setup_initrd_tag (bd_t *bd, ulong initrd_start,




ulong initrd_end);




# endif




static void setup_end_tag (bd_t *bd);




static struct tag *params;




#endif




......




void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],




ulong addr, ulong *len_ptr, int verify)




{




......




void (*theKernel)(int zero, int arch, uint params);




......




#ifdef CONFIG_CMDLINE_TAG




char *commandline = getenv ("bootargs");




#endif




theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);




......




theKernel (0, bd->bi_arch_number, bd->bi_boot_params);




}




關於這個參數列表中各個參數的定義及含義,以及參數列表的初始化過程,可以參考Booting ARM Linux一文。內核是如何找到這個參數列表在內存中的位置,以接收這些參數的呢?實際上,參數列表(tag list)在內存中的起始地址會保存在通用寄存器