U-BOOT启动过程
- 格式:doc
- 大小:472.00 KB
- 文档页数:31
IMX6uboot的启动流程⽹上看了后,做了个记录,主要是⼀个流程,具体代码没有分析,有空再细看。
cpu在上电之后,它们会⼲些什么?答:检查电压⼤⼩,确定启动模式等。
简单的检查之后呢?答:⼀般从固化在cpu内部的rom⾥⾯执⾏⼀⼩段code。
这⼀⼩段code具体做了些什么呢?各个cpu⼚商会不同,具体我也不知道。
但是我们应该知道,这⼩段code必须完成确认启动模式,并初始化启动设备,搬移烧录在启动设备⾥⾯的代码到ddr⾥⾯。
ok,搬移了代码后,cpu如何识别代码?将doc,txt⽂件烧进去⾏么?答:当然不⾏,烧录的⽂件也是有格式要求的。
格式在哪⾥定呢?稍等,先要知道⽣成的uboot.bin⽂件需要有个指导⽂件,就是uboot.lds,它的作⽤是在编译过程中,决定各个可执⾏程序段的位置。
其代码如下:1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")2 OUTPUT_ARCH(arm)3 ENTRY(_start)4 SECTIONS5 {6 . = 0x00000000;78 . = ALIGN(4);9 .text :10 {11/* WARNING - the following is hand-optimized to fit within */12/* the sector layout of our flash chips! XXX FIXME XXX */13 board/freescale/mx6q_sabreauto/flash_header.o (.text.flasheader)14 cpu/arm_cortexa8/start.o15 board/freescale/mx6q_sabreauto/libmx6q_sabreauto.a (.text)16 lib_arm/libarm.a (.text)17 net/libnet.a (.text)18 drivers/mtd/libmtd.a (.text)19 drivers/mmc/libmmc.a (.text)2021 . = DEFINED(env_offset) ? env_offset : .;22 common/env_embedded.o(.text)2324 *(.text)25 }2627 . = ALIGN(4);28 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }2930 . = ALIGN(4);31 .data : { *(.data) }3233 . = ALIGN(4);34 .got : { *(.got) }3536 . = .;37 __u_boot_cmd_start = .;38 .u_boot_cmd : { *(.u_boot_cmd) }39 __u_boot_cmd_end = .;4041 . = ALIGN(4);42 _end_of_copy = .; /* end_of ROM copy code here */43 __bss_start = .;44 .bss : { *(.bss) }45 _end = .;46 }View Code代码咱不分析,只看.text :{/* WARNING - the following is hand-optimized to fit within *//* the sector layout of our flash chips! XXX FIXME XXX */board/freescale/mx6q_sabreauto/flash_header.o (.text.flasheader)......}它的第⼀要存储的⽂件是flash_header的内容。
[精]UBOOT2017+FIT启动流程详尽分析开发环境:Nanopi-neo-plus2软件版本:uboot-2017软件版本:linux-4.14买这个板⼦有⼀段时间了,并没有全⾝⼼的投⼊在上⾯,有时间了的话就搞⼀搞,这篇随笔算是对这个版本的 uboot 启动流程做个⼤概的梳理和记录,体系结构相关的内容不作分析。
我这⾥会从 SPL(Secondary programloader) 阶段开始⼊⼿,按流程整理出 SPL 是怎样⼀步⼀步启动的 uboot,⽽ uboot ⼜是怎样加载并启动的 kernel。
废话不多说,以内容为重点来打通整体脉络。
⼀、从SPL⼊⼝点开始:阅读uboot源码时需注意:源码中存在众多 CONFIG_SPL_BUILD 宏的区分,使⽤了该宏的代码段,只有在 SPL 阶段时才会被编译进程序。
[ start.S armV8 ]_start:b resetreset: ... bl lowlevel_init ... bl _main[ lowlevel_init.S armV8 ]ENTRY(lowlevel_init) ldr w0, =CONFIG_SPL_STACK bic sp, x0, #0xf stp x29, x30, [sp, #-16]! bl s_initENDPROC(lowlevel_init)[ crt0_64.S arm/lib ]ENTRY(_main) ldr x0, =(CONFIG_SPL_STACK) bic sp, x0, #0xf ... mov x18, x0 bl board_init_f_init_reserve mov x0, #0 bl board_init_f ... mov x0, x18 /* gd_t */ ldr x1, [x18, #GD_RELOCADDR] /* dest_addr */ b board_init_r /* PC relative jump */ENDPROC(_main)[ Board.c mach-sunxi ]void board_init_f(ulong dummy){spl_init();preloader_console_init();#ifdef CONFIG_SPL_I2C_SUPPORT/* Needed early by sunxi_board_init if PMU is enabled */i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);#endifsunxi_board_init();#ifdef CONFIG_SPL_BUILDspl_mem_test();#endif}[ spl.c spl ]void board_init_r(gd_t *dummy1, ulong dummy2){...board_boot_order(spl_boot_list);if (boot_from_devices(&spl_image, spl_boot_list,ARRAY_SIZE(spl_boot_list))) {puts("SPL: failed to boot from all boot devices\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_fixup_fdt(); spl_board_prepare_for_linux(); jump_to_image_linux(&spl_image);#endif default: debug("Unsupported OS image.. Jumping nevertheless..\n"); } ... if (CONFIG_IS_ENABLED(ATF_SUPPORT)) { debug("loaded - jumping to U-Boot via ATF BL31.\n"); bl31_entry(); } debug("loaded - jumping to U-Boot...\n"); spl_board_prepare_for_boot(); jump_to_image_no_args(&spl_image);}[ spl.c spl ]board_boot_order(spl_boot_list);==>boot_source = readb(SPL_ADDR + 0x28);switch (boot_source) {case SUNXI_BOOTED_FROM_MMC0:return BOOT_DEVICE_MMC1;case SUNXI_BOOTED_FROM_NAND:return BOOT_DEVICE_NAND;case SUNXI_BOOTED_FROM_MMC2:return BOOT_DEVICE_MMC2;case SUNXI_BOOTED_FROM_SPI:return BOOT_DEVICE_SPI;};[ spl.c spl ]static int boot_from_devices(struct spl_image_info *spl_image,u32 spl_boot_list[], int count){loader = spl_ll_find_loader(spl_boot_list[i]);==> struct spl_image_loader *drv = ll_entry_start(struct spl_image_loader, spl_image_loader); const int n_ents = ll_entry_count(struct spl_image_loader, spl_image_loader); struct spl_image_loader *entry; for (entry = drv; entry != drv + n_ents; entry++) { if (boot_device == entry->boot_device) return entry; } ... if (loader && !spl_load_image(spl_image, loader)) return 0;}代码中使⽤了 ll_entry_start 宏,就可以联想到 ll_entry_declare 声明,随后通过搜索可找到#define SPL_LOAD_IMAGE(__name) \ll_entry_declare(struct spl_image_loader, __name, spl_image_loader)宏定义,进⼀步找到#define SPL_LOAD_IMAGE_METHOD(_name, _priority, _boot_device, _method) \SPL_LOAD_IMAGE(_method ## _priority ## _boot_device) = { \.name = _name, \.boot_device = _boot_device, \.load_image = _method, \}我们假设设备以SD卡的⽅式启动,SD对应着 BOOT_DEVICE_MMC1,那么通过搜索 SPL_LOAD_IMAGE_METHOD 筛选 BOOT_DEVICE_MMC1 就可以定位到驱动的本源,即[ spl_mmc.c spl ]SPL_LOAD_IMAGE_METHOD("MMC1", 0, BOOT_DEVICE_MMC1, spl_mmc_load_image);继续分析启动流程spl_load_image(spl_image, loader)==>int spl_mmc_load_image(struct spl_image_info *spl_image,struct spl_boot_device *bootdev){...spl_boot_mode(bootdev->boot_device);return MMCSD_MODE_RAW;.../* 通过宏 CONFIG_SPL_OS_BOOT 选择,spl直接启动OS还是启动uboot,这⾥返回1,启动uboot */spl_start_uboot()return1;...mmc_load_image_raw_sector(spl_image, mmc, CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);...mmc_load_legacy(spl_image, mmc, sector, header);spl_parse_image_header(spl_image, header);spl_set_header_raw_uboot(spl_image); spl_image->size = CONFIG_SYS_MONITOR_LEN; spl_image->entry_point = CONFIG_SYS_UBOOT_START; spl_image->load_addr = CONFIG_SYS_TEXT_BASE; spl_image->os = IH_OS_U_BOOT; spl_image->name = "U-Boot"; ... }回到 board_init_r 继续分析,spl_image->os 赋值为 IH_OS_U_BOOT ,所以 break 直接跳出;接下来有执⾏ATF的bl31部分(这部分暂不做分析),最后执⾏ jump_to_image_no_args 跳转到 spl_image->entry_point ,也就是正式的uboot阶段。
u-boot简单使⽤⼿册(转⾃)1.u-boot 简介-----------------------------------------------------------------1.u-boot2.它有两种模式引导模式(⽤于引导OS)下载模式(⽤于开发,在u-boot启动按任意键)进⼊控制台。
在控制台执⾏ boot 命令,则进⼊引导模式去引导OS。
2.u-boot 控制台常⽤命令--------------------------------------------------------------------help 显⽰帮助命令boot ⽤引导常数来引导OS。
bootm 从内存引导OS。
.bootp/tftpboot 从TFTP/BOOTP引导OSgo 从某⼀个地址开始直接运⾏程序mtdparts 管理flash上的分区ping 简单发送⼀个ICMP包测试。
printenv 打印环境变量setenv 设置环境变量saveenv 把内存的环境变量存到flash上的param分区.usbslave 从USB下载⽂件 (PC机要运⾏dnw)version 显⽰u-boot 版本4. 设置环境变量printenv 打印环境变量bootargs=noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0bootcmd=nboot 0x32000000 kernel; bootm 0x32000000bootdelay=0baudrate=115200ethaddr=0a:1b:2c:3d:4e:5fipaddr=192.168.1.6serverip=192.168.1.8netmask=255.255.255.0stdin=serialstdout=serialstderr=serialmtdids=nand0=nandflash0mtdparts=mtdparts=nandflash0:256k@0(bios),128k(params),128k(toc),512k(eboot),1024k(log)partition=nand0,0mtddevnum=0mtddevname=biosEnvironment size: 453/131068 bytes经常修改是 bootargs ( 启动参数)ipaddr (开发板IP地址)serverip( 默认tftp/NFS/...下载主机)setenv 命令只是改变内存的环境变量值,如果不调⽤saveenv,下次的设置仍然是旧的配置。
凌FL2440超详细U-BOOT作业(UBoot介绍+H-jtag使用+Uboot使用)Bootloader是高端嵌入式系统开发不可或缺的部分。
它是在操作系统内核启动之前运行的一段小程序。
通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
现在主流的bootloader有U-BOOT、vivi、Eboot等。
本次作业先做Uboot的烧写吧。
希望通过这个帖子,能让更多的初学者朋友了解一些UBoot的知识,也希望高手朋友对我的不足予以斧正。
首先说一下什么是Uboot:U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。
从FAD SROM、8xxROM、PPCBOOT逐步发展演化而来。
其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。
但是U-Boot不仅仅支持嵌入式Linu x系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。
其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。
这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。
这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。
uboot启动流程分析Uboot启动流程分析。
Uboot是一种常用的嵌入式系统启动加载程序,它的启动流程对于嵌入式系统的正常运行至关重要。
本文将对Uboot启动流程进行分析,以便更好地理解其工作原理。
首先,Uboot的启动流程可以分为以下几个步骤,Reset、初始化、设备初始化、加载内核。
接下来我们将逐一进行详细的分析。
Reset阶段是整个启动流程的起点,当系统上电或者复位时,CPU会跳转到Uboot的入口地址开始执行。
在这个阶段,Uboot会进行一些基本的硬件初始化工作,包括设置栈指针、初始化CPU寄存器等。
接着是初始化阶段,Uboot会进行一系列的初始化工作,包括初始化串口、初始化内存控制器、初始化时钟等。
这些初始化工作是为了确保系统能够正常地运行,并为后续的工作做好准备。
设备初始化阶段是Uboot启动流程中的一个重要环节,它包括对外设的初始化和检测。
在这个阶段,Uboot会初始化各种外设,如网卡、存储设备等,并对其进行检测,以确保它们能够正常工作。
最后一个阶段是加载内核,Uboot会从存储设备中加载操作系统的内核镜像到内存中,并跳转到内核的入口地址开始执行。
在这个过程中,Uboot会进行一些必要的设置,如传递启动参数给内核,并最终将控制权交给内核。
总的来说,Uboot的启动流程是一个非常重要的过程,它涉及到系统的硬件初始化、外设的初始化和内核的加载等工作。
只有当这些工作都顺利完成时,系统才能够正常地启动运行。
因此,对Uboot启动流程的深入理解对于嵌入式系统的开发和调试具有重要意义。
通过本文对Uboot启动流程的分析,相信读者对Uboot的工作原理有了更清晰的认识。
希望本文能够对大家有所帮助,谢谢阅读!。
(一)U-Boot启动过程--详细版的完全分析我们知道,bootloader是系统上电后最初加载运行的代码。
它提供了处理器上电复位后最开始需要执行的初始化代码。
在PC机上引导程序一般由BIOS开始执行,然后读取硬盘中位于MBR(Main Boot Record,主引导记录)中的Bootloader(例如LILO或GRUB),并进一步引导操作系统的启动。
然而在嵌入式系统中通常没有像BIOS那样的固件程序,因此整个系统的加载启动就完全由bootloader来完成。
它主要的功能是加载与引导内核映像一个嵌入式的存储设备通过通常包括四个分区:第一分区:存放的当然是u-boot第二个分区:存放着u-boot要传给系统内核的参数第三个分区:是系统内核(kernel)第四个分区:则是根文件系统如下图所示:u-boot是一种普遍用于嵌入式系统中的Bootloader。
Bootloader介绍Bootloader是进行嵌入式开发必然会接触的一个概念,它是嵌入式学院<嵌入式工程师职业培训班>二期课程中嵌入式linux系统开发方面的重要内容。
本篇文章主要讲解Bootloader 的基本概念以及内部原理,这部分内容的掌握将对嵌入式linux系统开发的学习非常有帮助!Bootloader的定义:Bootloader是在操作系统运行之前执行的一小段程序,通过这一小段程序,我们可以初始化硬件设备、建立内存空间的映射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备。
意思就是说如果我们要想让一个操作系统在我们的板子上运转起来,我们就必须首先对我们的板子进行一些基本配置和初始化,然后才可以将操作系统引导进来运行。
具体在Bootloader中完成了哪些操作我们会在后面分析到,这里我们先来回忆一下PC的体系结构:PC机中的引导加载程序是由BIOS和位于硬盘MBR中的OS Boot Loader(比如LILO和GRUB等)一起组成的,BIOS在完成硬件检测和资源分配后,将硬盘MBR中的Boot Loader读到系统的RAM中,然后将控制权交给OS Boot Loader。
U-Boot工作过程U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:(1)第一阶段的功能硬件设备初始化加载U-Boot第二阶段代码到RAM空间设置好栈跳转到第二阶段代码入口(2)第二阶段的功能初始化本阶段使用的硬件设备检测系统内存映射将内核从Flash读取到RAM中为内核设置启动参数调用内核1.1.1 U-Boot启动第一阶段代码分析第一阶段对应的文件是cpu/arm920t/和board/samsung/mini2440/。
U-Boot启动第一阶段流程如下:图 U-Boot启动第一阶段流程根据cpu/arm920t/中指定的连接方式:ENTRY(_start)SECTIONS{. = 0x00000000;. = ALIGN(4);.text :{cpu/arm920t/ (.text)board/samsung/mini2440/ (.text)board/samsung/mini2440/ (.text)*(.text)}… …}第一个链接的是cpu/arm920t/,因此的入口代码在cpu/arm920t/中,其源代码在cpu/arm920t/中。
下面我们来分析cpu/arm920t/的执行。
1. 硬件设备初始化(1)设置异常向量cpu/arm920t/开头有如下的代码:.globl _start_start: b start_code /* 复位*/ldr pc, _undefined_instruction /*未定义指令向量 */ldr pc, _software_interrupt /* 软件中断向量 */ldr pc, _prefetch_abort /* 预取指令异常向量 */ldr pc, _data_abort /* 数据操作异常向量 */ldr pc, _not_used /* 未使用 */ldr pc, _irq /* irq中断向量 */ldr pc, _fiq /* fiq中断向量 */ /* 中断向量表入口地址 */_undefined_instruction: .word undefined_instruction_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq.balignl 16,0xdeadbeef以上代码设置了ARM异常向量表,各个异常向量介绍如下:表 ARM异常向量表在cpu/arm920t/中还有这些异常对应的异常处理程序。
uboot 代码运行流程U-boot是一款ARM处理器启动管理程序,在ARM开发板研发中具有非常广泛的应用,它可以在开发板上提供各种服务和支持,使得用户能够轻松地启动系统和进行应用开发。
本文将从U-boot代码运行流程方面来介绍U-boot的一般执行过程。
U-boot代码运行流程主要可以分为两个方面,即bootloader代码执行流程和内核启动代码执行流程。
1. Bootloader代码执行流程Bootloader是指从CPU开始运行到操作系统启动、执行前的代码。
它的主要功能是加载操作系统内核、初始化硬件环境以及对操作系统进行配置等。
U-boot在启动过程中需要经历以下几个步骤:1)CPU复位在系统上电或者发生复位时,CPU内部控制块将根据复位信号重新启动,并从指定地址处读取第一条指令。
2)初始化硬件U-boot对内存、Flash、串口等硬件进行初始化,然后启动U-boot的“彩屏”启动画面。
3)读取启动设备U-boot通过Boot Device Driver从存储设备(SD卡、NAND Flash等)中读取启动文件,然后执行它。
默认情况下,U-boot会从SD卡中读取启动文件。
4)解析启动文件U-boot解析启动文件,提取内核映像、设备树和命令行参数等关键信息。
然后执行其它操作,如设备树重定位、加入命令行参数等。
5)启动内核U-boot将内核映像从Flash中加载到内存中,并将控制权转交给内核。
此时内核开始执行,U-boot自己退出。
2. 内核启动代码执行流程内核启动代码主要是内核启动过程中的初始化工作。
启动过程中,操作系统需要对内存进行初始化、加载一些关键模块、初始化驱动程序等工作,以实现操作系统本身的功能。
1)内核初始化内核初始化包括内存管理、进程管理、文件系统、驱动程序初始化等。
此时内核会创建init进程和kthreadd进程。
2)加载模块内核启动后加载模块,模块提供了丰富的功能,如网络支持、图形界面等。
am335xu-boot启动过程分析 u-boot属于两阶段的bootloader,第⼀阶段的⽂件为 arch/arm/cpu/armv7/start.S 和 arch/arm/cpu/armv7/lowlevel_init.S,前者是平台相关的,后者是开发板相关的。
1. u-boot第⼀阶段代码分析 (1)硬件设备初始化 将CPU的⼯作模式设为管理模式(SVC); 关闭中断; 禁⽤MMU,TLB ; 板级初始化; (2)为加载Bootloader的第⼆阶段代码准备RAM空间 加载u-boot.img,跳转到u-boot.img; 上述⼯作,也就是uboot-spl代码流程的核⼼。
代码如下:arch/arm/cpu/armv7/start.S1/*2 * the actual reset code3*/4reset:5 bl save_boot_params6/*7 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,8 * except if in HYP mode already9*/10 mrs r0, cpsr11 and r1, r0, #0x1f @ mask mode bits12 teq r1, #0x1a @ test for HYP mode13 bicne r0, r0, #0x1f @ clear all mode bits14 orrne r0, r0, #0x13 @ set SVC mode15 orr r0, r0, #0xc0 @ disable FIQ and IRQ16 msr cpsr,r017@@ 以上通过设置CPSR寄存器⾥设置CPU为SVC模式,禁⽌中断18@@ 具体操作可以参考《[kernel 启动流程] (第⼆章)第⼀阶段之——设置SVC、关闭中断》的分析1920/* the mask ROM code should have PLL and others stable */21#ifndef CONFIG_SKIP_LOWLEVEL_INIT22 bl cpu_init_cp1523@@ 调⽤cpu_init_cp15,初始化协处理器CP15,从⽽禁⽤MMU和TLB。
·1 引言在专用的嵌入式板子运行 GNU/Linux 系统已经变得越来越流行。
一个嵌入式 Linux 系统从软件的角度看通常可以分为四个层次:1. 引导加载程序。
固化在固件(firmware)中的 boot 代码,也就是 Boot Loader,它的启动通常分为两个阶段。
2. Linux 核。
特定于嵌入式板子的定制核以及核的启动参数。
3. 文件系统。
包括根文件系统和建立于 Flash 存设备之上文件系统,root fs。
4. 用户应用程序。
特定于用户的应用程序。
有时在用户应用程序和核层之间可能还会包括一个嵌入式图形用户界面。
常用的嵌入式 GUI 有:MicroWindows 和 MiniGUI 等。
引导加载程序是系统加电后运行的第一段软件代码。
回忆一下 PC 的体系结构我们可以知道,PC 机中的引导加载程序由 BIOS(其本质就是一段固件程序)和位于硬盘 MBR 中的 OS Boot Loader(比如,LILO 和 GRUB 等)一起组成。
BIOS 在完成硬件检测和资源分配后,将硬盘 MBR 中的 Boot Loader 读到系统的 RAM 中,然后将控制权交给 OS Boot Loader。
Boot Loader 的主要运行任务就是将核映象从硬盘上读到 RAM 中,然后跳转到核的入口点去运行,也即开始启动操作系统。
而在嵌入式系统中,通常并没有像 BIOS 那样的固件程序(注,有的嵌入式 CPU 也会嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由 Boot Loader 来完成。
比如在一个基于 ARM7TDMI core 的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000 处开始执行,而在这个地址处安排的通常就是系统的 Boot Loader 程序。
·2 bootloader简介简单地说,Boot Loader (引导加载程序)就是在操作系统核运行之前运行的一段小程序,它的作用就是加载操作系统,它是系统加电后运行的第一段软件代码。
U-Boot启动过程U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:(1)第一阶段的功能Ø硬件设备初始化Ø加载U-Boot第二阶段代码到RAM空间Ø设臵好栈Ø跳转到第二阶段代码入口(2)第二阶段的功能Ø初始化本阶段使用的硬件设备Ø检测系统内存映射Ø将内核从Flash读取到RAM中Ø为内核设臵启动参数Ø调用内核1.1.1U-Boot启动第一阶段代码分析第一阶段对应的文件是cpu/arm920t/start.S和board/samsung/mini2440/lowlevel_init.S。
U-Boot启动第一阶段流程如下:图 2.1 U-Boot启动第一阶段流程根据cpu/arm920t/u-boot.lds中指定的连接方式:ENTRY(_start)SECTIONS{. = 0x00000000;. = ALIGN(4);.text :{cpu/arm920t/start.o (.text)board/samsung/mini2440/lowlevel_init.o (.text)board/samsung/mini2440/nand_read.o (.text)*(.text)}……}第一个链接的是cpu/arm920t/start.o,因此u-boot.bin的入口代码在cpu/arm920t/start.o中,其源代码在cpu/arm920t/start.S中。
下面我们来分析cpu/arm920t/start.S的执行。
1.硬件设备初始化(1)设臵异常向量cpu/arm920t/start.S开头有如下的代码:.globl _start_start: b start_code /* 复位*/ldr pc, _undefined_instruction /* 未定义指令向量 */ldr pc, _software_interrupt /* 软件中断向量*/ldr pc, _prefetch_abort /* 预取指令异常向量 */ldr pc, _data_abort /* 数据操作异常向量 */ldr pc, _not_used /* 未使用 */ldr pc, _irq /* irq中断向量 */ldr pc, _fiq /* fiq中断向量 *//* 中断向量表入口地址 */_undefined_instruction: .word undefined_instruction_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq.balignl 16,0xdeadbeef以上代码设臵了ARM异常向量表,各个异常向量介绍如下:表 2.1 ARM异常向量表在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。
当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。
其中复位异常向量的指令“b start_code”决定了U-Boot启动后将自动跳转到标号“start_code”处执行。
(2)CPU进入SVC模式start_code:/** set the cpu to SVC32 mode*/mrs r0, cpsrbic r0, r0, #0x1f /* 工作模式位清零 */orr r0, r0, #0xd3 /* 工作模式位设臵为“10011”(管理模式),并将中断禁止位和快中断禁止位臵1 */msr cpsr, r0以上代码将CPU的工作模式位设臵为管理模式,并将中断禁止位和快中断禁止位臵一,从而屏蔽了IRQ和FIQ中断。
(3)设臵控制寄存器地址#if defined(CONFIG_S3C2400)#define pWTCON 0x15300000#define INTMSK 0x14400008#define CLKDIVN 0x14800014#else /* s3c2410与s3c2440下面4个寄存器地址相同 */#define pWTCON 0x53000000 /*看门狗寄存器*/#define INTMSK 0x4A000008 /*中断屏蔽寄存器*/#define INTSUBMSK 0x4A00001C /*次级中断屏蔽寄存器*/#define CLKDIVN 0x4C000014 /*时钟分频寄存器*/#endif对于s3c2440开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四个寄存器的地址的设臵。
(4)关闭看门狗ldr r0, =pWTCONmov r1, #0x0str r1, [r0] /*看门狗控制器的最低位为0时,看门狗不输出复位信号 */以上代码向看门狗控制寄存器写入0,关闭看门狗。
否则在U-Boot启动过程中,CPU将不断重启。
(5)屏蔽中断/** mask all IRQs by setting all bits in the INTMR - default*/mov r1, #0xffffffff /*某位被臵1则对应的中断被屏蔽*/ldr r0, =INTMSKstr r1, [r0]INTMSK是主中断屏蔽寄存器,每一位对应SRCPND(中断源引脚寄存器)中的一位,表明SRCPND 相应位代表的中断请求是否被CPU所处理。
INTMSK寄存器是一个32位的寄存器,每位对应一个中断,向其中写入0xffffffff就将INTMSK 寄存器全部位臵一,从而屏蔽对应的中断。
#if defined(CONFIG_S3C2440)ldr r1, =0x7fffldr r0, =INTSUBMSKstr r1, [r0]#endifINTSUBMSK是次级中断屏蔽器,每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。
INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低15位。
向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)臵一,从而屏蔽对应的中断。
为什么要屏蔽所有中断?中断处理中ldr pc是将代码的编译地址放在了指针上,而这段时间还没有搬移代码,所以编译地址上面没有这个代码,如果进行跳转就会跳转到空指针上面(6)设臵MPLLCON,UPLLCON, CLKDIVN#if defined(CONFIG_S3C2440)#define MPLLCON 0x4C000004#define UPLLCON 0x4C000008ldr r0, =CLKDIVNmov r1, #5str r1, [r0]ldr r0, =MPLLCONldr r1, =0x7F021str r1, [r0]ldr r0, =UPLLCONldr r1, =0x38022str r1, [r0]# else/* FCLK:HCLK:PCLK = 1:2:4 *//* default FCLK is 120 MHz ! */ldr r0, =CLKDIVNmov r1, #3str r1, [r0]#endifCPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。
但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。
这就需要设臵CLKDIVN,MPLLCON,UPLLCON这3个寄存器。
CLKDIVN寄存器用于设臵FCLK,HCLK,PCLK三者间的比例,可以根据表2.2来设臵。
表 2.2 S3C2440 的CLKDIVN寄存器格式设臵CLKDIVN为5,就将HDIVN设臵为二进制的10,由于CAMDIVN[9]没有被改变过,取默认值0,因此HCLK = FCLK/4。
PDIVN被设臵为1,因此PCLK= HCLK/2。
因此分频比FCLK:HCLK:PCLK = 1:4:8 。
MPLLCON寄存器用于设臵FCLK与Fin的倍数。
MPLLCON的位[19:12]称为MDIV,位[9:4]称为PDIV,位[1:0]称为SDIV。
对于S3C2440,FCLK与Fin的关系如下面公式:MPLL(FCLK) = (2×m×Fin)/(p×2^s)其中: m=MDIV+8,p=PDIV+2,s=SDIVMPLLCON与UPLLCON的值可以根据参考文献4中“PLL VALUE SELECTION TABLE”设臵。
该表部分摘录如下:表 2.3 推荐PLL值当mini2440系统主频设臵为405MHZ,USB时钟频率设臵为48MHZ时,系统可以稳定运行,因此设臵MPLLCON与UPLLCON为:MPLLCON=(0x7f<<12) | (0x02<<4) | (0x01) = 0x7f021UPLLCON=(0x38<<12) | (0x02<<4) | (0x02) = 0x38022为什么要设臵时钟?起始可以不设,系统能不能跑起来和频率没有任何关系,频率的设臵是要让外围的设备能承受所设臵的频率,如果频率过高则会导致cpu操作外围设备失败说白了:设臵频率,就为了CPU能去操作外围设备(7)关闭MMU,cache接着往下看:#ifndef CONFIG_SKIP_LOWLEVEL_INITbl cpu_init_crit#endifcpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。
下面分析一下cpu_init_crit到底做了什么:320#ifndef CONFIG_SKIP_LOWLEVEL_INIT321 cpu_init_crit:322 /*323 * 使数据cache与指令cache无效 */324 */325 mov r0, #0326 mcr p15, 0, r0, c7, c7, 0 /*向c7写入0将使ICache与DCache无效*/327 mcr p15, 0, r0, c8, c7, 0 /*向c8写入0将使TLB失效*/328329 /*330 * disable MMU stuff and caches331 */332 mrc p15, 0, r0, c1, c0, 0 /* 读出控制寄存器到r0中 */333 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)334 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)335 orr r0, r0, #0x00000002 @ set bit 1 (A) Align336 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache337 mcr p15, 0, r0, c1, c0, 0 /* 保存r0到控制寄存器 */338339 /*340 * before relocating, we have to setup RAM timing341 * because memory timing is board-dependend, you will342 * find a lowlevel_init.S in your board directory.343 */344 mov ip, lr345346 bl lowlevel_init347348 mov lr, ip349 mov pc, lr350#endif /* CONFIG_SKIP_LOWLEVEL_INIT */代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。