1. 程式人生 > >全志A10 Bootload載入過程分析

全志A10 Bootload載入過程分析

A10的啟動過程大概可分為5步:BootRom,SPL,Uboot,Kernel,RootFileSystem。本文只關注映象的載入過程,分析RootRom->SPL->Uboot的啟動流程。 系統上電後,ARM處理器在復位時從地址0x000000開始執行指令,把板上ROM或Flash對映到這一地址。A10將啟動裝置選擇程式固化在CPU內部的一個32KB ROM中,預設的啟動時序為SD Card0,NAND FLASH,SD Card2,SPI NOR FLASH。另外通過外部的一個啟動選擇引腳可以使其跳轉到USB啟動模式。通常情況下,啟動選擇引腳狀態連線50K內部上拉電阻。在上電後,執行儲存在ROM中的啟動程式碼,將自動檢測啟動選擇引腳狀態。只有當該引腳狀態為低電平時選擇USB啟動模式。 啟動裝置選擇程式的流程圖: 在選擇啟動裝置後將載入並執行bootload程式,CPU通過拷貝或對映bootload程式到記憶體,然後執行bootload的第一條指令。通過閱讀官方的uboot燒寫方法,發現A10通過uboot引導系統,但卻沒有載入整個uboot,而是在此之前先載入了uboot SPL。什麼是SPL?通過查閱uboot的官網資料得知,SPL是一個迷你版的uboot,全拼為Second Program Loader。適用於SOC的內部SROM<64K的情況,用它來載入完整的uboot程式到SDROM,並通過完整uboot載入核心來啟動系統。 SPL程式流程如下: 1.初始化ARM處理器 2.初始化串列埠控制檯 3.配置時鐘和最基礎的分頻 4.初始化SDRAM 5.配置引腳多路複用功能 6.啟動裝置初始化(即上面選擇的啟動裝置) 7.載入完整的uboot程式並轉交控制權 搞清楚了上面的概念,可以知道Cubieboard出廠已經燒寫了NandFlash中的程式,即在啟動選擇時使用的是NandFlash。現在根據全志A10上的步驟,我們嘗試用SDC1(即Cubieboard上卡槽中的TF卡)來啟動系統。 下載並編譯uboot #git clone https://github.com/linux-sunxi/u-boot-sunxi.git #cd u-boot-sunxi #export CROSS_COMPILE=arm-linux-gnueabihf- #make cubieboard 為TF卡燒寫載入程式 #dd if=/dev/zero of=/dev/sdb bs=1M count=1 # 清空SD卡(包括分割槽表) 嘗試不建立分割槽表,直接燒寫載入程式,無法啟動系統。 因此按需求給TF卡分割槽,我只建立了唯一一個200M主分割槽,此處不詳述分割槽過程,下面是我使用的TF卡的分割槽表資訊。 $ sudo fdisk -l /dev/sdb Disk /dev/sdb: 1977 MB, 1977614336 bytes 52 heads, 30 sectors/track, 2475 cylinders, total 3862528 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000 Device Boot Start End Blocks Id System /dev/sdb1 2048 411647 204800 83 Linux 通過A10相關資料,通過以下2條命令燒寫spl和uboot到指定位置,暫時不清楚如何確定這2個讀取位置。 #dd if=spl/sunxi-spl.bin of=/dev/sdb bs=1024 seek=8 #dd if=u-boot.bin of=/dev/sdb bs=1024 seek=32 燒寫完成後插入TF卡到CB的卡槽中,連線USB轉TTL串列埠線,開啟串列埠終端,上電。 串列埠列印資訊如下: U-
Boot SPL 2012.10-04277-g7aa9f04 (Mar10 2013 - 00:36:40)
DRAM: 1024MB
SUNXI SD/MMC:0


U-Boot 2012.10-04277-g7aa9f04 (Mar10 2013 - 00:36:40) Allwinner Technology

CPU: SUNXI Family
Board: Cubieboard
I2C: ready
DRAM: 1 GiB
MMC: SUNXI SD/MMC:0
*** Warning- bad CRC, using default environment

In: serial
Out: serial
Err: serial
Hit any key to stop autoboot:
0

** Unable to use mmc0:1for fatload **
Loading file "uEnv.txt" from mmc device 0:1
Failed to mount ext2 filesystem...
** Bad ext2 partitionor disk - mmc0:1**
ext2load - load binary file from a Ext2 filesystem

Usage:
ext2load <interface><dev[:part]> [addr] [filename] [bytes]
- load binary file 'filename'
from 'dev' on 'interface'
to address 'addr' from ext2 filesystem
Loading file "boot/uEnv.txt" from mmc device 0:1
Failed to mount ext2 filesystem...
** Bad ext2 partitionor disk - mmc0:1**
ext2load - load binary file from a Ext2 filesystem

Usage:
ext2load <interface><dev[:part]> [addr] [filename] [bytes]
- load binary file 'filename' from 'dev' on 'interface'
to address 'addr' from ext2 filesystem

** Unable to use mmc0:1for fatload **
Loading file "boot.scr" from mmc device 0:1
Failed to mount ext2 filesystem...
** Bad ext2 partitionor disk - mmc0:1**
ext2load - load binary file from a Ext2 filesystem

Usage:
ext2load <interface><dev[:part]> [addr] [filename] [bytes]
- load binary file 'filename' from 'dev' on 'interface'
to address 'addr' from ext2 filesystem
Loading file "boot/boot.scr" from mmc device 0:1
Failed to mount ext2 filesystem...
** Bad ext2 partitionor disk - mmc0:1**
ext2load - load binary file from a Ext2 filesystem

Usage:
ext2load <interface><dev[:part]> [addr] [filename] [bytes]
- load binary file 'filename' from 'dev' on 'interface'
to address 'addr' from ext2 filesystem

** Unable to use mmc0:1for fatload **
sun4i# 通過串列埠資訊可以看到SPL和Uboot已經成功載入,uboot最後通過ext2load命令讀取uEnv.txt和boot.scr檔案失敗後退回控制檯。 下面通過原始碼分析SPL載入完整Uboot的過程。 /u-boot-sunxi/common/spl/spl.c - board_init_r void board_init_r(gd_t *dummy1, ulong dummy2)
{
u32 boot_device;
debug(">>spl:board_init_r()\n");
puts(">>spl:board_init_r()\n");

#ifdef CONFIG_SYS_SPL_MALLOC_START
mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
CONFIG_SYS_SPL_MALLOC_SIZE);
#endif

timer_init();

#ifdef CONFIG_SPL_BOARD_INIT
spl_board_init();
#endif

boot_device = spl_boot_device(); //檢測啟動裝置,此處返回BOOT_DEVICE_MMC1
debug("boot device - %d\n", boot_device);
switch (boot_device) {
#ifdef CONFIG_SPL_RAM_DEVICE
case BOOT_DEVICE_RAM:
spl_ram_load_image();
break;
#endif
#ifdef CONFIG_SPL_MMC_SUPPORT
case BOOT_DEVICE_MMC1:
case BOOT_DEVICE_MMC2:
case BOOT_DEVICE_MMC2_2:
spl_mmc_load_image(); //通過mmc讀取image
break;
#endif
#ifdef CONFIG_SPL_NAND_SUPPORT
case BOOT_DEVICE_NAND:
spl_nand_load_image();
break;
#endif
#ifdef CONFIG_SPL_NOR_SUPPORT
case BOOT_DEVICE_NOR:
spl_nor_load_image();
break;
#endif
#ifdef CONFIG_SPL_YMODEM_SUPPORT
case BOOT_DEVICE_UART:
spl_ymodem_load_image();
break;
#endif
#ifdef CONFIG_SPL_SPI_SUPPORT
case BOOT_DEVICE_SPI:
spl_spi_load_image();
break;
#endif
#ifdef CONFIG_SPL_ETH_SUPPORT
case BOOT_DEVICE_CPGMAC:
#ifdef CONFIG_SPL_ETH_DEVICE
spl_net_load_image(CONFIG_SPL_ETH_DEVICE);
#else
spl_net_load_image(NULL);
#endif
break;
#endif
default:
debug("SPL: Un-supported Boot Device\n");
hang();
}

switch (spl_image.os) {
case IH_OS_U_BOOT:
debug("Jumping to U-Boot\n");
break;
#ifdef CONFIG_SPL_OS_BOOT
case IH_OS_LINUX:
debug("Jumping to Linux\n");
spl_board_prepare_for_linux();
jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
default:
debug("Unsupported OS image.. Jumping nevertheless..\n");
}
jump_to_image_no_args();
} u-boot-sunxi/arch/arm/cpu/armv7/sunxi/board.c - spl_boot_device u32 spl_boot_device(void) {

u32 cfg;

#ifdef CONFIG_SPL_NOR_SUPPORT
/* TODO */
#endif

#ifdef CONFIG_SPL_MMC_SUPPORT
cfg = sunxi_gpio_get_cfgpin(SUNXI_GPC(7));
if( cfg == SUNXI_GPC7_SDC2_CLK )
return BOOT_DEVICE_MMC2;
#endif

#ifdef CONFIG_SPL_NAND_SUPPORT
cfg = sunxi_gpio_get_cfgpin(SUNXI_GPC(2));
if( cfg == SUNXI_GPC2_NCLE )
return BOOT_DEVICE_NAND;
#endif

#ifdef CONFIG_SPL_MMC_SUPPORT
cfg = sunxi_gpio_get_cfgpin(SUNXI_GPF(2));
if( cfg == SUNXI_GPF2_SDC0_CLK )
return BOOT_DEVICE_MMC1;
#endif

/* if we are here, something goes wrong. Fall back on MMC */
return BOOT_DEVICE_MMC1;
} 啟動裝置是通過讀取連線相應裝置的GPIO狀態來判斷的,但在上述條件下執行該段程式碼時並沒有成功檢測到裝置,直到最後返回BOOT_DEVICE_MMC1。 u-boot-sunxi/drivers/mmc/spl_mmc.c - spl_mmc_load_image void spl_mmc_load_image(void)
{
struct mmc *mmc;
int err;
u32 boot_mode;

mmc_initialize(gd->bd);
/* We register only one device. So, the dev id is always 0 */
mmc = find_mmc_device(0);
if (!mmc) {
puts("spl: mmc device not found!!\n");
hang();
}

err = mmc_init(mmc);
if (err) {
printf("spl: mmc init failed: err - %d\n", err);
hang();
}
boot_mode = spl_boot_mode(); // sunxi的程式碼在此處直接返回了MMCSD_MODE_RAW
if (boot_mode == MMCSD_MODE_RAW) {
debug("boot mode - RAW\n");
mmc_load_image_raw(mmc);
#ifdef CONFIG_SPL_FAT_SUPPORT
} else if (boot_mode == MMCSD_MODE_FAT) {
debug("boot mode - FAT\n");
mmc_load_image_fat(mmc);
#endif
} else {
puts("spl: wrong MMC boot mode\n");
hang();
}
} u-boot-sunxi/drivers/mmc/spl_mmc.c - mmc_load_image_raw static void mmc_load_image_raw(struct mmc*mmc)
{
u32 image_size_sectors, err;
const struct image_header *header;

header = (struct image_header*)(CONFIG_SYS_TEXT_BASE -
sizeof(struct image_header));

/* read image header to find the image size & load address */
err = mmc->block_dev.block_read(0,
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR, 1,
(void *)header);

if (err <= 0)
goto end;

spl_parse_image_header(header);

/* convert size to sectors - round up */
image_size_sectors = (spl_image.size + mmc->read_bl_len- 1) /
mmc->read_bl_len;

/* Read the header too to avoid extra memcpy */
err = mmc->block_dev.block_read(0,
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR,
image_size_sectors, (void *)spl_image.load_addr); //此處確定了Uboot讀取位置

end:
if (err <= 0) {
printf("spl: mmc blk read err - %d\n", err);
hang();
}
} u-boot-sunxi/include/configs/sunxi-common.h #define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR (64)/* 32KB offset */
#define CONFIG_SYS_U_BOOT_MAX_SIZE_SECTORS (400)/* 200KB, enough for a full u-boot.bin */ 通過CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR確定了我們把完整的uboot寫入到TF卡從32K位置開始。