移植u-boot-2019.10到jz2440

前言

本文主要記錄移植當前最新版u-boot(u-boot-2019.10)到jz2440的過程。做這個移植主要是爲了從這個過程中學習移植u-boot的一般流程、u-boot的使用以及u-boot的實現。本文主要參考的內容是韋東山老師的二期視頻教程,以及網上的相關博文,這些都會在參考文獻中列出。

1 u-boot簡介

u-boot是一種bootloader,用於引導操作系統內核的啓動。字母u表示“通用”的意思,正如其名所說,u-boot目前已經支持arm、x86、mips、riscv等多種體系結構,支持引導linux、VxWorks、Solaris等多種操作系統。如何使用u-boot(主要是使用它提供的命令)來做開發,這個有很多資料講解,u-boot自身也提供了命令的使用說明,因此使用u-boot本身並不困難,這裏不再贅述。

2 第一步——根據自己的硬件初步修改u-boot

2.1 移植的不同硬件層次

我們要在不同的硬件平臺之間移植u-boot,必然需要搞清楚不同硬件平臺之間的差別。而硬件的差別體現在多個層次上,比如說,我們的目標開發板是jz2440,使用的SoC是三星公司的s3c2440,該SoC使用的內核(CPU)是ARM設計的ARM920T,該內核基於armv4t架構。

從一種硬件平臺向另一種硬件平臺移植時,需要知道兩者之間在哪個硬件層次上開始有區別:

  1. 如果是同一種SoC,不同的開發板,那麼區別可能就只存在於板載外設,需要修改或添加一些驅動;
  2. 如果是同一種內核,不同的SoC,不同的開發板,比如TI基於ARM920T做的SoC和三星基於ARM920T做的SoC,以及更下游廠商做的不同開發板,那麼除了板載外設的不同,移植時還要根據不同SoC之間的差別做修改;
  3. 如果是同一種架構,不同的內核,不同的SoC,不同的開發板,比如Cortex-A53的板子和Cortex-A57的板子,那麼移植的時候,板載外設、SoC、內核相關的程序都可能要修改;
  4. 如果什麼都不同,即不同的架構,不同的內核,不同的SoC,不同的開發板,比如ARM9的板子和cortex-A8的板子(同出於ARM,架構還有一定的相似性),更有甚者ARM架構的板子和MIPS架構的板子,那麼移植的工作量就非常大了,基本上,除了u-boot的硬件無關的程序,所有硬件相關的程序都要修改,修改的程度視兩者的差異大小而定。

一般越是上游的廠商面對的不同點越多,而下游的廠商在上游廠商移植的基礎上進行,比如ARM提供armv5架構以及ARM920T內核相關的程序,三星再此基礎上爲其推出的SMDK2410做移植,就只需要提供SoC層面和開發板層面的程序。而下游廠商使用S3C2440做產品,就可以在三星提供的SMDK2410的基礎上做一些修改以支持自己的板子。

最後,值得說明的是
在u-boot中,提取硬件部分的程序時,並沒有嚴格按照上述的四個硬件層次進行。比如u-boot-2019.10\arch\arm\cpu\armv7m目錄下,並沒有再細分cortex-m4cortex-m7等子目錄,可能是這兩個內核的區別對於u-boot來說沒必要去考慮(u-boot也只是使用一部分內核特性)。因此,上述硬件層次的劃分僅有指導意義,還需具體情況具體分析。

2.2 選擇合適的坯子

如上文所述,我們在移植的時候會在SMDK2410的基礎上進行,這是三星推出的開發板,使用的是s3c2410這款SoC。其實很多芯片廠商在推出新款SoC的時候,都會推出基於此SoC的開發板,以幫助用戶使用該SoC設計產品,從而達到推廣該SoC的目的。而三星在推出SMDK2410的時候,也向u-boot官方提供了相應的移植程序,這正是我們移植的重要參照(坯子)。

s3c2410和s3c2440都是基於ARM920T內核的,或許有人會問,爲什麼不直接找一個s3c2440的開發板呢?因爲u-boot官方並沒有直接支持基於s3c2440的板子。不僅如此,就是對SMDK2410的支持,也在最新的版本中移除了。具體是在哪個版本中移除的,我沒有仔細查過。

遇到這種相似的平臺被移除的情況該怎麼辦呢?我們可以往回看,畢竟之前的u-boot是支持的。具體的做法是,下載尚且支持SMDK2410的u-boot版本,將其中的相關目錄和文件拷貝到最新的版本中,然後在根據需要去修改。至於找哪個之前的版本呢?通常越接近最新版本兼容性越好。這裏我就隨便選擇了u-boot-2015.10

2.3 將舊版本中支持SMDK2410的文件拷貝到新版u-boot

在u-boot-2015.10中,搜索s3csmdk等關鍵詞,發現對SMDK2410的支持主要體現在六個部分

  1. u-boot-2015.10\arch\arm\cpu\arm920t目錄下的s3c24x0目錄,其中包含了一些SoC相關的源碼;
  2. u-boot-2015.10\board\samsung目錄下的smdk2410目錄,其中包含了一些板級支持的源碼;
  3. u-boot-2015.10\arch\arm\include\asm目錄下的arch-s3c24x0目錄,其中包含了一些頭文件;
  4. u-boot-2015.10\drivers\xxx諸多目錄下的一些驅動源文件,這些源文件主要用於驅動s3c24x0的i2c、rtc、nand、usb等外設;
  5. u-boot-2015.10\include\configs目錄下的smdk2410.h文件,這個頭文件主要包含了一些配置宏;
  6. u-boot-2015.10\configs目錄下的smdk2410_defconfig文件,這是一個對於SMDK2410開發板的默認配置。

現在我也不能確定這些文件是否全都被使用,我們可以先將這些文件都拷貝到u-boot-2019.10,如果有什麼問題,後面編譯測試暴露出來後,再進行修改。

2.3.1 拷貝s3c24x0目錄

將u-boot-2015.10/arch/arm/cpu/arm920t/中的s3c24x0目錄整個拷貝到u-boot-2019.10/arch/arm/cpu/arm920t/目錄下。此外,還需要修改u-boot-2019.10/arch/arm/cpu/arm920t目錄下的Makefile:

...
obj-$(CONFIG_EP93XX) += ep93xx/
obj-$(CONFIG_IMX) += imx/

# 仿照上面的內容添加如下一行
obj-$(CONFIG_S3C24X0) += s3c24x0/
...

修改Makefile的目的是告訴u-boot的編譯體系,如果CONFIG_S3C24X0=y,就編譯s3c24x0目錄下的源文件。

2.3.2 拷貝smdk2410目錄

在u-boot-2019.10/board目錄下創建新目錄100ask/jz2440,並將u-boot-2015.10中的smdk2410目錄下的所有文件拷貝到新創建的目錄下。此外,還需要修改的內容如下:
① 修改源文件名
smdk2410.c修改爲jz2440.c

② 修改源文件中2410相關內容

int board_init(void)
{
	/* arch number of JZ2440-Board */
	// gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
	// 將這句程序修改如下:
	gd->bd->bi_arch_number = MACH_TYPE_JZ2440;
	...
}

MACH_TYPE_JZ2440並沒有在u-boot中定義,因此我們要添加它的定義。至於在哪裏添加,我們可以參考MACH_TYPE_SMDK2410,使用Linux的grep命令不難找到這個宏定義在u-boot-2019.10/arch/arm/include/asm/mach-types.h,在其中增加宏定義:

// MACH_TYPE_SMDK2440與目標平臺JZ2440最爲接近
#define MACH_TYPE_JZ2440               MACH_TYPE_SMDK2440

以上兩步的修改僅僅是將2410的痕跡抹去,畢竟我們是要移植到2440平臺。這些修改只是名稱層面的,沒有什麼實質的修改。

③ 修改Makefile

# obj-y	:= smdk2410.o
# 將上式修改爲
obj-y	:= jz2440.o

④ 修改Kconfig
將原來的內容做如下修改:

if TARGET_JZ2440

config SYS_BOARD
	default "jz2440"

config SYS_VENDOR
	default "100ask"

config SYS_SOC
	default "s3c24x0"

config SYS_CONFIG_NAME
	default "jz2440"

endif

這個目錄下存在Kconfig,也就意味着我們要在恰當的上層Kconfig中使用source來引用這個Kconfig,從而將這個目錄下的內容納入u-boot的配置體系。這裏所說的“恰當的目錄”其實是arch/arm/Kconfig,屬於arm體系的board的Kconfig會在其中被引用。

arch/arm/Kconfig中加上這麼一句:

......
source "board/xilinx/zynq/Kconfig"
source "board/xilinx/zynqmp/Kconfig"
# 添加如下內容
source "board/100ask/jz2440/Kconfig"
......

此外,需要注意到TARGET_JZ2440是我們後來改的,在原來的配置體系中沒有定義。同樣是arch/arm/Kconfig,其中定義了目標開發板的選項羣

choice
	prompt "Target select"
	default TARGET_HIKEY
......
endchoice

我們在endchoice之前添加如下內容:

config TARGET_JZ2440
	bool "support jz2440"
	select CPU_ARM920T
	help
	  "support jz2440, a board based on s3c2440"

2.3.3 拷貝arch-s3c24x0目錄

將u-boot-2015.10/arch/arm/include/asm中的arch-s3c24x0目錄整個拷貝到u-boot-2019.10/arch/arm/include/asm目錄下即可。

2.3.4 拷貝零散分佈的驅動源程序

將這些驅動源文件列表如下:

目錄 文件名
u-boot-2015.10/drivers/usb/host ohci-s3c24xx.cohci-s3c24xx.h
u-boot-2015.10/include/usb s3c_udc.h
u-boot-2015.10/drivers/usb/gadget s3c_udc_otg.cs3c_udc_otg_phy.cs3c_udc_otg_xfer_dma.c
u-boot-2015.10/drivers/i2c s3c24x0_i2c.cs3c24x0_i2c.h
u-boot-2015.10/drivers/rtc s3c24x0_rtc.c
u-boot-2015.10/drivers/mtd/nand s3c2410_nand.c
u-boot-2015.10/drivers/gpio s3c2440_gpio.c
u-boot-2015.10/drivers/serial serial_s3c24x0.c

同時,還要修改Makefile,參照u-boot-2015.10中的相應Makefile修改即可,比如將u-boot-2015.10/drivers/usb/host目錄下Makefile中的:

obj-$(CONFIG_USB_OHCI_S3C24XX) += ohci-s3c24xx.o

添加到u-boot-2019.10/drivers/usb/host目錄下的Makefile中,不再贅述。

需要說明的是,上述驅動程序我們只是簡單的拷貝,以及將命名中的2410改爲了2440。但這種層次的修改是不夠的,可能部分驅動程序不適用於2440,需要修改源程序本身,這些修改我們放在後面說。

2.3.5 拷貝smdk2410.h

將u-boot-2015.10/include/configs目錄下的smdk2410.h拷貝到u-boot-2019.10/include/configs目錄下,並將文件名修改爲jz2440.h。查看這個文件的內容,對其中與2410smdk2410相關的內容做如下修改:

// #define CONFIG_S3C2410改爲
#define CONFIG_S3C2440
// #define CONFIG_SMDK2410改爲
#define CONFIG_JZ2440
......
// #define CONFIG_NAND_S3C2410改爲
#define CONFIG_NAND_S3C2440
// #define CONFIG_SYS_S3C2410_NAND_HWECC改爲
#define CONFIG_SYS_S3C2440_NAND_HWECC
......

此外,應該在所有原來的宏名出現的地方做同步的修改:

通過在源碼中搜索發現,CONFIG_S3C2410和CONFIG_S3C2440是相互替代的關係(目標板的SoC不可能既是2410又是2440),而CONFIG_SMDK2410和CONFIG_SYS_S3C2410_NAND_HWECC沒有使用到,因此僅在這個頭文件中做修改即可。

CONFIG_NAND_S3C2410在u-boot-2015.10中決定了3c2410_nand.c是否要編譯:

obj-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o

之前拷貝3c2410_nand.c的時候已經將其命名改爲了3c2440_nand.c,且在相應的Makefile中添加了:

obj-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o

因此這個配置項也僅需要在這個頭文件中做修改。

2.3.6 拷貝smdk2410_defconfig文件

將u-boot-2015.10/configs/smdk2410_defconfig拷貝到u-boot-2019.10/configs目錄下,並重命名爲jz2440_defconfig,並將其中內容修改如下:

CONFIG_ARM=y

# 聯繫上文中的TARGET_JZ2440
CONFIG_TARGET_JZ2440=y

CONFIG_SYS_PROMPT="JZ2440 # "
# CONFIG_CMD_SETEXPR is not set

至此,我已經將能想到的可能用到的文件都拷貝到u-boot-2019.10了,至於這些文件會不會都被用到,是不是還有一些需要修改的地方尚未被注意到,或者還有一些需要用到的文件沒有拷過去,這些只能通過閱讀源碼、實際編譯測試等方式來發現了。接下來就進行編譯,應該不可能一次通過,編譯暴露出的問題正好指引後面的移植工作。

3 第二步——編譯並解決出現的問題

關於u-boot的編譯體系及其編譯過程,不在這裏多說。值得一提的是,我使用的交叉編譯工具鏈版本爲4.3.2。下面就開始編譯u-boot-2019.10:


1 執行make jz2440_defconfig,使用默認配置。

出現錯誤

board/jz2440/Kconfig:15: 'endif' in different file than 'if'

分析與解決
endif後面要加一個空行,這是Kconfig格式的問題。


2 執行make CROSS_COMPILE=arm-linux-進行編譯

出現提示

scripts/kconfig/conf  --syncconfig Kconfig
.config:151:warning: symbol value '' invalid for SYS_TEXT_BASE
*
* Restart config...
*

分析與解決
看提示,應該是SYS_TEXT_BASE(鏈接基地址)沒有配置。搜索SYS_TEXT_BASE,發現該配置項通常在xxx_defconfig中配置,所以在jz2440_defconfig中添加:

CONFIG_SYS_TEXT_BASE=0x00000000

3 再次編譯

出現錯誤

cc1: error: unrecognized command line option "-std=gnu11"

分析與解決
從錯誤提示來看,錯誤的原因是工具鏈的版本太老,不支-std=gnu11選項。爲了解決這個問題,很是折騰了一番還沒有結果。新版的工具鏈不一定支持arm9,而且更換了更新的工具鏈之後,-std=gnu11選項這裏沒有報錯,但又出現的了新的問題,諸如:

*** Your GCC is older than 6.0 and is not supported

將GCC版本校驗註釋掉,再編譯,又出錯:

error while loading shared libraries: libstdc++.so.6: wrong ELF class: ELFCLASS64

我對工具鏈理解不深,因此如果再在工具鏈上耗下去,就偏離了學習u-boot的主線,所以決定還是硬着頭皮用現在的工具鏈(gcc version 4.3.2)。畢竟,沒有c11的特性,難道u-boot這個項目就做不成了?當然用舊的工具鏈需要做些修改:

# u-boot-2019.10/Makefile中將-std=gnu11改爲-std=gnu99
# CSTD_FLAG := -std=gnu11
CSTD_FLAG := -std=gnu99
------------------------------------------------------------
# u-boot-2019.10/arch/arm/config.mk中註釋掉GCC版本的校驗
# archprepare: checkgcc6

4 再次編譯

出現錯誤
配置頭文件jz2440.h中出現了一大堆宏重複定義的警告,還有個別類型的重複定義。

分析與解決
重複的宏定義同時出現在u-boot-2019.10/include/configs/jz2440.hu-boot-2019.10/include/generated/autoconf.h中,後者就是從.config文件中生成的。看樣子,u-boot的配置被分爲兩個部分,一個部分源於Kconfig配置體系生成的.config文件;另一個部分需要手動配置,通常放在u-boot-2019.10/include/configs/<board name>.h中。可能是一部分原先需要手動配置的項,現在被納入了Kconfig配置體系,因此出現了重複定義。我們只需要把重複的項註釋掉即可(註釋jz2440.h中的,不去動自動生成的autoconf.h)。


5 再次編譯

出現錯誤

arch/arm/cpu/arm920t/s3c24x0/cpu_info.c:15: error: 'get_FCLK' undeclared here (not in a function)
arch/arm/cpu/arm920t/s3c24x0/cpu_info.c:16: error: 'get_HCLK' undeclared here (not in a function)
arch/arm/cpu/arm920t/s3c24x0/cpu_info.c:17: error: 'get_PCLK' undeclared here (not in a function)

分析與解決
簡單搜索過程可知,get_FCLKget_HCLKget_PCLK等函數都是定義在與cpu_info.c相同目錄的speed.c中,而在cpu_info.c中使用時,並未聲明,因此我們在cpu_info.c的開頭添加如下聲明即可:

/* 添加函數聲明 */
ulong get_FCLK(void);
ulong get_HCLK(void);
ulong get_PCLK(void);
ulong get_UCLK(void);

6 再次編譯

出現錯誤

arch/arm/cpu/arm920t/s3c24x0/timer.c: In function 'timer_init':
arch/arm/cpu/arm920t/s3c24x0/timer.c:38: warning: implicit declaration of function 'get_PCLK'
arch/arm/cpu/arm920t/s3c24x0/timer.c: In function 'get_timer':
arch/arm/cpu/arm920t/s3c24x0/timer.c:60: warning: implicit declaration of function 'get_timer_masked'
arch/arm/cpu/arm920t/s3c24x0/timer.c: At top level:
arch/arm/cpu/arm920t/s3c24x0/timer.c:76: error: conflicting types for 'get_timer_masked'
arch/arm/cpu/arm920t/s3c24x0/timer.c:60: error: previous implicit declaration of 'get_timer_masked' was here

分析與解決
仍然是函數聲明的問題,在timer.c的開頭添加函數聲明:

/* 添加函數聲明 */
ulong get_timer_masked(void);
ulong get_PCLK(void);

7 再次編譯

出現錯誤

board/100ask/jz2440/jz2440.c: In function 'board_init':
board/100ask/jz2440/jz2440.c:100: error: 'MACH_TYPE_JZ2440' undeclared (first use in this function)
board/100ask/jz2440/jz2440.c:100: error: (Each undeclared identifier is reported only once
board/100ask/jz2440/jz2440.c:100: error: for each function it appears in.)

分析與解決
MACH_TYPE_JZ2440定義在u-boot-2019.10/arch/arm/include/asm/mach-types.h中,只要在jz2440.c中包含這個頭文件即可:

#include <asm/mach-types.h>

8 再次編譯
出現錯誤

cmd/reginfo.c:9:21: error: asm/ppc.h: No such file or directory
cmd/reginfo.c: In function 'do_reginfo':
cmd/reginfo.c:14: warning: implicit declaration of function 'print_reginfo'

分析與解決
搜索print_reginfo發現這個函數定義在powerpc架構下,而我們是arm架構,因此可以將整個打印寄存器信息的功能都註釋掉,即註釋掉jz2440.h中的相應配置項:

// #define CONFIG_CMD_REGINFO

9 再次編譯

出現錯誤

cmd/ubi.c: In function 'display_ubi_info':
cmd/ubi.c:64: error: 'CONFIG_MTD_UBI_WL_THRESHOLD' undeclared (first use in this function)
cmd/ubi.c:64: error: (Each undeclared identifier is reported only once

分析與解決
UBI的配置項在.config中是被註釋的:

# CONFIG_MTD_UBI is not set

CONFIG_MTD_UBI_WL_THRESHOLD是依賴於CONFIG_MTD_UBI的:
在這裏插入圖片描述
也就是說UBI相關的配置是沒有選中的,UBI相關的源文件也不應該被編譯,但實際被編譯了。這是因爲jz2440.h中配置了相關選項,爲什麼會出現這種前後矛盾的情況呢?上文已經說過u-boot的配置被割裂成兩部分,一部分可以通過圖形化配置,配置結果保存在.config文件中;另一部分保存在u-boot-2019.10/include/configs目錄下的用於配置的頭文件中,需要手動用宏來配置,本文中這個配置頭文件就是jz2440.h。這兩部分應該是互補的,而不應該相互矛盾,但我們的jz2440.h是從之前的版本拷貝過來的,本身就不是u-boot-2019.10自帶的,因此存在一些矛盾之處也不足爲奇。對於眼下這個問題,我們把jz2440.h中的UBI相關的配置項註釋掉即可:

// #define CONFIG_CMD_UBI
// #define CONFIG_CMD_UBIFS

10 再次編譯

出現警告和錯誤

drivers/mtd/nand/raw/nand_base.c: In function 'nand_maximize_ecc':
drivers/mtd/nand/raw/nand_base.c:4887: warning: 'best_ecc_bytes' may be used uninitialized in this function
drivers/mtd/nand/raw/nand_base.c:4887: warning: 'best_strength' may be used uninitialized in this function
drivers/mtd/nand/raw/nand_base.c: In function 'nand_match_ecc_req':
drivers/mtd/nand/raw/nand_base.c:4803: warning: 'best_ecc_bytes' may be used uninitialized in this function
drivers/mtd/nand/raw/nand_base.c:4803: warning: 'best_strength' may be used uninitialized in this function
drivers/mtd/nand/raw/nand_base.c:4803: warning: 'best_step' may be used uninitialized in this function
......
drivers/serial/serial_s3c24x0.c: In function '_serial_setbrg':
drivers/serial/serial_s3c24x0.c:79: warning: implicit declaration of function 'get_PCLK'
......
drivers/usb/host/ohci-s3c24xx.c:1330: error: conflicting types for 'submit_int_msg'
include/usb.h:186: error: previous declaration of 'submit_int_msg' was here

分析與解決
開頭一大堆的未初始化警告,源碼中本就沒有初始化,因此不去管它們。而警告get_PCLK未聲明,只需聲明一下即可。最後的一個錯誤是因爲submit_int_msg函數的原型在u-boot-2019.10中已經發生了改變:

// 舊的原型是:
// int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval)
// 修改爲如下:
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval, bool nonblock)

11 再次編譯

出現警告和錯誤

arm-linux-ld: ERROR: Source object drivers/dma/built-in.o has EABI version 0, but target u-boot has EABI version 5
cmd/built-in.o: In function `do_fat_fsinfo':
/root/work/u-boot/u-boot-2019.10/cmd/fat.c:84: undefined reference to `fat_set_blk_dev'
/root/work/u-boot/u-boot-2019.10/cmd/fat.c:89: undefined reference to `file_fat_detectfs'
......
cmd/built-in.o: In function `usb_find_device':
/root/work/u-boot/u-boot-2019.10/cmd/usb.c:308: undefined reference to `usb_get_dev_index'
env/built-in.o: In function `env_flash_save':
/root/work/u-boot/u-boot-2019.10/env/flash.c:270: undefined reference to `flash_sect_protect'
/root/work/u-boot/u-boot-2019.10/env/flash.c:278: undefined reference to `flash_sect_erase'
/root/work/u-boot/u-boot-2019.10/env/flash.c:282: undefined reference to `flash_write'
/root/work/u-boot/u-boot-2019.10/env/flash.c:300: undefined reference to `flash_perror'
/root/work/u-boot/u-boot-2019.10/env/flash.c:305: undefined reference to `flash_sect_protect'
arm-linux-ld: BFD (Sourcery G++ Lite 2008q3-72) 2.18.50.20080215 assertion fail /scratch/julian/lite-respin/linux/obj/binutils-src-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:9537
arm-linux-ld: BFD (Sourcery G++ Lite 2008q3-72) 2.18.50.20080215 assertion fail /scratch/julian/lite-respin/linux/obj/binutils-src-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:9537
......
arm-linux-ld: BFD (Sourcery G++ Lite 2008q3-72) 2.18.50.20080215 assertion fail /scratch/julian/lite-respin/linux/obj/binutils-src-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:9771
Segmentation fault (core dumped)
make: *** [u-boot] Error 139
make: *** Deleting file `u-boot'

分析與解決
首先是EABI版本不一致這個錯誤,大概說的是我們編譯源碼的時候用的是老版本的EABI,而u-boot-2019.10在鏈接的時候需要用新版的EABI。這可能就是用舊版編譯工具鏈編譯新版u-boot會出現的問題,我是參照本文參考文獻[2]解決的,關於工具鏈、ABI的知識真是讓人頭疼,後面得找時間專門深入學一下相關內容。

剩下的是一堆鏈接時找不到函數定義的錯誤。搜索了一下,發現還是因爲u-boot的兩部分配置相互矛盾造成的,我們先簡單處理:註釋掉jz2440.h中配置的,但.config中未配置的內容,即註釋掉jz2440.h中的如下配置項:

// #define CONFIG_CMD_USB
......
/************************************************************
 * USB support (currently only works with D-cache off)
 ************************************************************/
// #define CONFIG_USB_OHCI
// #define CONFIG_USB_OHCI_S3C24XX
// #define CONFIG_USB_KEYBOARD
// #define CONFIG_USB_STORAGE
......
// #define CONFIG_ENV_IS_IN_FLASH
......
/*
 * File system
 */
// #define CONFIG_CMD_FAT

需要說明的是,這裏註釋掉相關配置項,只是爲了先編譯通過,而不是說這些配置項所配置的功能用不着。後面如果使用到相關功能,我們還會重新配置。但再來配置的時候,會優先通過Kconfig配置體系來配置,這樣可以統一配置的來源,避免兩部分配置出現重複或矛盾的地方,如此,即便再出問題,也更好分析和解決。


12 再次編譯

這次編譯終於生成了u-boot.bin等文件:
在這裏插入圖片描述
但Makefile仍舊給出了一些警告:

===================== WARNING ======================
This board does not use CONFIG_DM. CONFIG_DM will be
compulsory starting with the v2020.01 release.
Failure to update may result in board removal.
See doc/driver-model/migration.rst for more info.
====================================================
===================== WARNING ======================
This board does not use CONFIG_DM_MMC. Please update
the board to use CONFIG_DM_MMC before the v2019.04 release.
Failure to update by the deadline may result in board removal.
See doc/driver-model/MIGRATION.txt for more info.
====================================================
===================== WARNING ======================
This board does not use CONFIG_DM_ETH (Driver Model
for Ethernet drivers). Please update the board to use
CONFIG_DM_ETH before the v2020.07 release. Failure to
update by the deadline may result in board removal.
See doc/driver-model/migration.rst for more info.
====================================================
  CFGCHK  u-boot.cfg
Error: You must add new CONFIG options using Kconfig
The following new ad-hoc CONFIG options were detected:
CONFIG_CS8900
CONFIG_CS8900_BASE
CONFIG_JZ2440
CONFIG_NAND_S3C2440
CONFIG_RTC_S3C24X0
CONFIG_S3C2440
CONFIG_S3C24X0
CONFIG_S3C24X0_SERIAL
CONFIG_SERIAL1
CONFIG_SYS_GENERIC_BOARD
CONFIG_SYS_HUSH_PARSER
CONFIG_SYS_S3C2440_NAND_HWECC
CONFIG_ZERO_BOOTDELAY_CHECK

Please add these via Kconfig instead. Find a suitable Kconfig
file and add a 'config' or 'menuconfig' option.

這些警告的意思不難解讀,u-boot強制使用CONFIG_DMCONFIG_DM_MMCCONFIG_DM_ETH等配置項,而我們沒有使用。同時,警告信息中給出的CONFIG_CS8900等配置項不是官方維護的,我們需要通過Kconfig配置體系來添加屬於自己的新的配置項。

看來官方也比較傾向於Kconfig配置體系,我個人也覺得兩種配置方式並存不好,這樣會將配置割裂成兩部分,如果兩部分不是嚴格互補的,而是存在重複的配置項,那麼就可能出現CONFIG_XXX在Kconfig配置體系中沒有選中,而頭文件中卻進行了宏定義。導致的結果是,我們的本意是不需要該項功能,但最後該項功能卻被編譯進去了,當然還有可能連編譯都通不過。

爲了使配置體系更合理,我會將上述配置項移到Kconfig配置體系中,jz2440.h中的其他重複項會在後續的移植過程中處理。簡單起見,我們將專門爲jz2440定義的配置項移至u-boot-2019.10/board/100ask/jz2440/Kconfig文件中:

if TARGET_JZ2440
......
config CS8900
	bool
	default y

config CS8900_BASE
	hex
	default 0x19000300

config JZ2440
	bool
	default y

config NAND_S3C2440
	bool
	default y

config RTC_S3C24X0
	bool
	default y

config S3C2440
	bool
	default y

config S3C24X0
	bool
	default y

config S3C24X0_SERIAL
	bool
	default y

config SERIAL1
	int
	default 1

config SYS_GENERIC_BOARD
	bool
	default y

config SYS_HUSH_PARSER
	bool
	default y

config SYS_S3C2440_NAND_HWECC
	bool
	default y

config ZERO_BOOTDELAY_CHECK
	bool
	default y

endif

至於CONFIG_DMCONFIG_DM_MMCCONFIG_DM_ETH這幾個配置項,先不去管,後續的移植過程中,若有需要再去配置。


4 第三步——運行測試並進一步修改u-boot

經過上述步驟,我們完成了對u-boot-2019.10的編譯,成功得到了bin文件。但顯然,由於smdk2410jz2440的差別,這個bin文件幾乎不可能順利運行。因此我們需要進一步修改u-boot,同時也要將bin文件燒寫到開發板測試。在測試過程中,如果發現問題,還需要再修改程序,這樣不斷迭代,直到u-boot在目標開發板上運行沒有問題。

4.1 u-boot-2019.10源碼分析

想要修改u-boot,就必須先知道u-boot的源碼是怎麼寫的,否則根本不知道該改哪裏。因此,我們先分析源碼。考慮到本文已經比較長了,所以另一起一篇博客來記錄我對u-boot源碼的分析:u-boot-2019.10源碼分析

4.2 初步修改源碼

4.2.1 關看門狗、關中斷、配置MPLL

在分析源碼的過程中,不難發現,u-boot-2019.10/arch/arm/cpu/arm920t/start.S將關看門狗、關中斷、設置時鐘分頻係數的程序移除了(因爲移除了對SMDK2410的支持,相應的程序也就移除了)。我們自己實現這些功能並添加到start.S中設置SVC模式相關程序的後面:

#if CONFIG_S3C2440
#define WTCON		0x53000000	/* 看門狗控制寄存器 */
#define INTMSK		0x4A000008	/* 中斷屏蔽寄存器 */
#define INTSUBMSK	0x4A00001C	/* 子級中斷屏蔽寄存器 */
#define CLKDIVN		0x4C000014	/* 時鐘分頻寄存器 */
#define MPLLCON 	0x4C000004	/* MPLL控制寄存器 */

#define S3C2440_MPLL_400MHZ ((0x5c<<12) | (0x01<<4) | (0x01<<0))

	/* 關看門狗 */
	ldr	r0, =WTCON
	mov	r1, #0x0
	str	r1, [r0]

	/* 關中斷 */
	/* 屏蔽第一級中斷 */
	mov	r1, #0xffffffff
	ldr	r0, =INTMSK
	str	r1, [r0]
	
	/* 屏蔽第二級中斷 */
	ldr	r1, =0x3ff
	ldr	r0, =INTSUBMSK
	str	r1, [r0]

	/* 配置MPLL(FCLK = 400MHz) */
	ldr r0, =MPLLCON
	ldr r1, =S3C2440_MPLL_400MHZ
	str r1, [r0]

	/* 設置分頻比爲FCLK:HCLK:PCLK = 8:2:1 */
	/* FCLK = 400MHz, HCLK = 100MHz, PCLK = 50MHz */
	ldr r0, =CLKDIVN
	mov r1, #0x05
	str r1, [r0]

	/* 設置異步總線模式(HDIVN不爲0) */
	mrc p15, 0, r0, c1, c0, 0
	orr r0, r0, #0xc0000000
	mcr p15, 0, r0, c1, c0, 0
#endif

同時,通過前面對源碼的分析,在u-boot-2019.10/board/100ask/jz2440/jz2440.c中的board_early_init_f函數中有對MPLL的配置,爲了避免重複且不一致的配置,將該函數中配置MPLL的程序註釋掉。

4.2.2 初始化sdram

由於S3C2410的時鐘頻率和S3C2440的不同,後者時鐘頻率更高。因此原來的sdram初始化使用的參數就不對了。在u-boot-2019.10/board/100ask/jz2440/lowlevel_init.S中,將sdram控制器的參數修改如下:

.long 0x22011110		@BWSCON
.long 0x00000700		@BANKCON0
.long 0x00000700		@BANKCON1
.long 0x00000700		@BANKCON2
.long 0x00000700		@BANKCON3
.long 0x00000700		@BANKCON4
.long 0x00000700		@BANKCON5
.long 0x00018005		@BANKCON6
.long 0x00018005		@BANKCON7
.long 0x008C04F4		@REFRESH
.long 0x000000B1		@BANKSIZE
.long 0x00000030		@MRSRB6
.long 0x00000030		@MRSRB7

4.2.3 初始化串口

爲了能在運行移植後的u-boot時打印出一些信息,必須初始化串口。前面分析源碼的時候已經說過,串口在硬件層面的初始化u-boot已經做了,而u-boot沒做的是註冊串口設備,因此需要在u-boot-2019.10/drivers/serial/serial.c中的serial_initialize函數裏面添加註冊S3C2440的串口的函數:

/* 相當於函數聲明 */
serial_initfunc(s3c24xx_serial_initialize);
......
void serial_initialize(void)
{
	......
#if CONFIG_S3C2440
	/* 註冊S3C2440的串口設備 */
	s3c24xx_serial_initialize();
#endif
	......
}

4.2.4 燒寫程序並調試

至此,我們保存上述修改,然後編譯、燒寫,看看做了上述修改後u-boot-2019.10能在jz2440上運行到哪一步。

將開發板設置爲Nor啓動,使用jlink將bin文件燒寫到NorFlash之後,重新上電,令人失望的是沒有任何串口輸出,哪怕是亂碼都沒有。仔細檢查程序以及反彙編文件,沒有看出來有什麼問題。只能請出LED大法了,從reset開始,以點燈的方式一點一點找問題出在哪裏。最後發現是配置MPLL、設置分頻比、設置異步總線模式三者的順序導致的問題,將順序調整如下:

/* 設置分頻比爲FCLK:HCLK:PCLK = 8:2:1 */
/* FCLK = 400MHz, HCLK = 100MHz, PCLK = 50MHz */
ldr r0, =CLKDIVN
mov r1, #0x05
str r1, [r0]

/* 設置異步總線模式(HDIVN不爲0) */
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #0xc0000000
mcr p15, 0, r0, c1, c0, 0

/* 配置MPLL(FCLK = 400MHz) */
ldr r0, =MPLLCON
ldr r1, =S3C2440_MPLL_400MHZ
str r1, [r0]

然後再次編譯燒寫,重新上電後,終於看到了u-boot-2019.10的輸出:



U-Boot 2019.10 (Dec 24 2019 - 14:55:28 +0800)

CPUID: 32440001
FCLK:      400 MHz
HCLK:      100 MHz
PCLK:       50 MHz
DRAM:  64 MiB
WARNING: Caches not enabled
NAND:  0 MiB
MMC:   
In:    serial
Out:   serial
Err:   serial
Net:   CS8900-0
Error: CS8900-0 address not set.

JZ2440 # 

至此,u-boot-2019.10成功在jz2440上面運行起來,但移植工作並未結束。從上述輸出信息就可以看出一些問題,比如NandFlash還未支持,網卡驅動使用的是CS8900的,而jz2440使用的網卡是DM9000。當然,存在的問題還不止這些,要想移植的u-boot能夠用於開發,還有不少事情要做。因此,讓我們愉快的繼續!

4.3 修改程序以支持Nand啓動

根據本文4.1節對u-boot-2019.10源碼的分析,不難看出,其使用了動態鏈接的一些技術,在編譯時添加了-pie選項,因此需要額外保存很多函數和變量的地址,增加了程序的體積。且在重定位之前還要調用init_sequence_f中的許多初始化函數,這使得我們很難確保重定位之前的所有程序都位於bin文件的前4K。然而,S3C2440的Nand啓動只能拷貝NandFlash的前4K至片內的SRAM。因此,要想讓u-boot-2019.10支持Nand啓動,我們需要把重定位提前。同樣爲了確保本文的閱讀體驗,我們另起一文來記錄如何修改u-boot-2019.10以支持S3C2440的Nand啓動:移植u-boot-2019.10到jz2440——修改程序以支持Nand啓動

4.4 修改程序以支持NorFlash

目前我們的u-boot可以從NorFlash啓動,但未能支持NorFlash,注意兩者的區別:從NorFlash啓動只需要讀NorFlash就可以了,而u-boot提供了一些命令如mwerase,這些命令需要寫/擦除NorFlash。NorFlash是不能隨機寫的,只能通過特定的操作序列來實現擦除、寫等功能。而不同型號的NorFlash所需要的操作序列可能不同,所以u-boot不太可能不需要任何修改就支持我們所使用的NorFlash,畢竟選擇什麼型號的NorFlash不是u-boot決定的,而我們至少要通過某種方式(u-boot提供的某種機制)把我們所用的NorFlash的一些信息告訴u-boot。那麼到底要做哪些修改呢?且看移植u-boot-2019.10到jz2440——修改程序以支持NorFlash

4.5 修改程序以支持DM9000C網卡

通過分析源碼可以獲知,當前u-boot使用的網卡驅動是CS8900的驅動,而jz2440實際使用的網卡是DM9000C,因此當前網卡是無法使用的,與網卡相關的一些好用的功能,比如通過tftp下載程序這些都無法使用。現在我們就修改程序來支持網卡:移植u-boot-2019.10到jz2440——修改程序以支持DM9000C網卡

4.6 修改程序以支持NandFlash

當前我們移植的u-boot已經可以從NandFlash啓動了,但還不支持一些NandFlash相關的命令,比如nand read/write等。這是因爲NandFlash相關的程序是我們直接從SMDK2410那裏拷貝來的,尚未經過修改,不適合jz2440。現在我們就修改程序來支持NandFlash:移植u-boot-2019.10到jz2440——修改程序以支持NandFlash。

4.7

4.8

參考文獻

[1] 韋東山老師二期視頻教程
[2] u-boot 2015.01 :has EABI version 0, but target u-boot has EABI version 4
[3] 遊戲進行中的博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章