打印
[ZLG-ARM]

嵌入式系统Boot Loader技术内幕

[复制链接]
3195|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
reeper|  楼主 | 2009-4-2 16:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
 Boot Loader çš„主要任务与典型结构框架

在继续本节的讨论之前,首先我们做一个假定,那就是:假定内核映像与根文件系统映像都被加载到 RAM ä¸­è¿è¡Œã€‚之所以提出这样一个假设前提是因为,在嵌入式系统中内核映像与根文件系统映像也可以直接在 ROM æˆ– Flash è¿™æ ·çš„固态存储设备中直接运行。但这种做法无疑是以运行速度的牺牲为代价的。

从操作系统的角度看,Boot Loader çš„总目标就是正确地调用内核来执行。

另外,由于 Boot Loader çš„实现依赖于 CPU çš„体系结构,因此大多数 Boot Loader éƒ½åˆ†ä¸º stage1 å’Œ stage2 ä¸¤å¤§éƒ¨åˆ†ã€‚依赖于 CPU ä½“系结构的代码,比如设备初始化代码等,通常都放在 stage1 ä¸­ï¼Œè€Œä¸”通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2 åˆ™é€šå¸¸ç”¨C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。

Boot Loader çš„ stage1 é€šå¸¸åŒ…括以下步骤(以执行的先后顺序):

硬件设备初始化。


为加载 Boot Loader çš„ stage2 å‡†å¤‡ RAM ç©ºé—´ã€‚


拷贝 Boot Loader çš„ stage2 åˆ° RAM ç©ºé—´ä¸­ã€‚


设置好堆栈。


跳转到 stage2 çš„ C å…¥å£ç‚¹ã€‚


Boot Loader çš„ stage2 é€šå¸¸åŒ…括以下步骤(以执行的先后顺序):

初始化本阶段要使用到的硬件设备。


检测系统内存映射(memory map)。


å°† kernel æ˜ åƒå’Œæ ¹æ–‡ä»¶ç³»ç»Ÿæ˜ åƒä»Ž flash ä¸Šè¯»åˆ° RAM ç©ºé—´ä¸­ã€‚


为内核设置启动参数。


调用内核。
(一)Boot Loader çš„ stage1
基本的硬件初始化

这是 Boot Loader ä¸€å¼€å§‹å°±æ‰§è¡Œçš„操作,其目的是为 stage2 çš„执行以及随后的 kernel çš„执行准备好一些基本的硬件环境。它通常包括以下步骤(以执行的先后顺序):

1.屏蔽所有的中断。为中断提供服务通常是 OS è®¾å¤‡é©±åŠ¨ç¨‹åºçš„责任,因此在 Boot Loader çš„执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU çš„中断屏蔽寄存器或状态寄存器(比如 ARM çš„ CPSR å¯„存器)来完成。

2.设置 CPU çš„速度和时钟频率。

3.RAM åˆå§‹åŒ–。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。

4.初始化 LED。典型地,通过 GPIO æ¥é©±åŠ¨ LED,其目的是表明系统的状态是 OK è¿˜æ˜¯ Error。如果板子上没有 LED,那么也可以通过初始化 UART å‘串口打印 Boot Loader çš„ ** å­—符信息来完成这一点。

5.关闭 CPU å†…部指令/数据 cache。

(二) ä¸ºåŠ è½½ stage2 å‡†å¤‡ RAM ç©ºé—´

为了获得更快的执行速度,通常把 stage2 åŠ è½½åˆ° RAM ç©ºé—´ä¸­æ¥æ‰§è¡Œï¼Œå› æ­¤å¿…须为加载 Boot Loader çš„ stage2 å‡†å¤‡å¥½ä¸€æ®µå¯ç”¨çš„ RAM ç©ºé—´èŒƒå›´ã€‚

由于 stage2 é€šå¸¸æ˜¯ C è¯­è¨€æ‰§è¡Œä»£ç ï¼Œå› æ­¤åœ¨è€ƒè™‘空间大小时,除了 stage2 å¯æ‰§è¡Œæ˜ è±¡çš„大小外,还必须把堆栈空间也考虑进来。此外,空间大小最好是 memory page å¤§å°(通常是 4KB)的倍数。一般而言,1M çš„ RAM ç©ºé—´å·²ç»è¶³å¤Ÿäº†ã€‚具体的地址范围可以任意安排,比如 blob å°±å°†å®ƒçš„ stage2 å¯æ‰§è¡Œæ˜ åƒå®‰æŽ’到从系统 RAM èµ·å§‹åœ°å€ 0xc0200000 å¼€å§‹çš„ 1M ç©ºé—´å†…执行。但是,将 stage2 å®‰æŽ’到整个 RAM ç©ºé—´çš„最顶 1MB(也即(RamEnd-1MB) - RamEnd)是一种值得推荐的方法。

为了后面的叙述方便,这里把所安排的 RAM ç©ºé—´èŒƒå›´çš„大小记为:stage2_size(字节),把起始地址和终止地址分别记为:stage2_start å’Œ stage2_end(这两个地址均以 4 å­—节边界对齐)。因此:

stage2_end=stage2_start+stage2_size
 

另外,还必须确保所安排的地址范围的的确确是可读写的 RAM ç©ºé—´ï¼Œå› æ­¤ï¼Œå¿…须对你所安排的地址范围进行测试。具体的测试方法可以采用类似于 blob çš„方法,也即:以 memory page ä¸ºè¢«æµ‹è¯•å•ä½ï¼Œæµ‹è¯•æ¯ä¸ª memory page å¼€å§‹çš„两个字是否是可读写的。为了后面叙述的方便,我们记这个检测算法为:test_mempage,其具体步骤如下:

1. å…ˆä¿å­˜ memory page ä¸€å¼€å§‹ä¸¤ä¸ªå­—的内容。

2. å‘这两个字中写入任意的数字。比如:向第一个字写入 0x55,第 2 ä¸ªå­—写入 0xaa。

3. ç„¶åŽï¼Œç«‹å³å°†è¿™ä¸¤ä¸ªå­—的内容读回。显然,我们读到的内容应该分别是 0x55 å’Œ 0xaa。如果不是,则说明这个 memory page æ‰€å æ®çš„地址范围不是一段有效的 RAM ç©ºé—´ã€‚

4. å†å‘这两个字中写入任意的数字。比如:向第一个字写入 0xaa,第 2 ä¸ªå­—中写入 0x55。

5. ç„¶åŽï¼Œç«‹å³å°†è¿™ä¸¤ä¸ªå­—的内容立即读回。显然,我们读到的内容应该分别是 0xaa å’Œ 0x55。如果不是,则说明这个 memory page æ‰€å æ®çš„地址范围不是一段有效的 RAM ç©ºé—´ã€‚

6. æ¢å¤è¿™ä¸¤ä¸ªå­—的原始内容。测试完毕。

为了得到一段干净的 RAM ç©ºé—´èŒƒå›´ï¼Œæˆ‘们也可以将所安排的 RAM ç©ºé—´èŒƒå›´è¿›è¡Œæ¸…零操作。

(三) æ‹·è´ stage2 åˆ° RAM ä¸­

拷贝时要确定两点:(1) stage2 çš„可执行映象在固态存储设备的存放起始地址和终止地址;(2) RAM ç©ºé—´çš„起始地址。

(四) è®¾ç½®å †æ ˆæŒ‡é’ˆ sp

堆栈指针的设置是为了执行 C è¯­è¨€ä»£ç ä½œå¥½å‡†å¤‡ã€‚通常我们可以把 sp çš„值设置为(stage2_end-4),也即在 3.1.2 èŠ‚所安排的那个 1MB çš„ RAM ç©ºé—´çš„最顶端(堆栈向下生长)。

此外,在设置堆栈指针 sp ä¹‹å‰ï¼Œä¹Ÿå¯ä»¥å…³é—­ led ç¯ï¼Œä»¥æç¤ºç”¨æˆ·æˆ‘们准备跳转到 stage2。

(五)跳转到 stage2 çš„ C å…¥å£ç‚¹


在上述一切都就绪后,就可以跳转到 Boot Loader çš„ stage2 åŽ»æ‰§è¡Œäº†ã€‚比如,在 ARM ç³»ç»Ÿä¸­ï¼Œè¿™å¯ä»¥é€šè¿‡ä¿®æ”¹ PC å¯„存器为合适的地址来实现。

(六) Boot Loader çš„ stage2
正如前面所说,stage2 çš„代码通常用 C è¯­è¨€æ¥å®žçŽ°ï¼Œä»¥ä¾¿äºŽå®žçŽ°æ›´å¤æ‚的功能和取得更好的代码可读性和可移植性。但是与普通 C è¯­è¨€åº”用程序不同的是,在编译和链接 boot loader è¿™æ ·çš„程序时,我们不能使用 glibc åº“中的任何支持函数。其原因是显而易见的。这就给我们带来一个问题,那就是从那里跳转进 main() å‡½æ•°å‘¢ï¼Ÿç›´æŽ¥æŠŠ main() å‡½æ•°çš„起始地址作为整个 stage2 æ‰§è¡Œæ˜ åƒçš„入口点或许是最直接的想法。但是这样做有两个缺点:1)无法通过main() å‡½æ•°ä¼ é€’函数参数;2)无法处理 main() å‡½æ•°è¿”回的情况。一种更为巧妙的方法是利用 trampoline(弹簧床)的概念。也即,用汇编语言写一段trampoline å°ç¨‹åºï¼Œå¹¶å°†è¿™æ®µ trampoline å°ç¨‹åºæ¥ä½œä¸º stage2 å¯æ‰§è¡Œæ˜ è±¡çš„执行入口点。然后我们可以在 trampoline æ±‡ç¼–小程序中用 CPU è·³è½¬æŒ‡ä»¤è·³å…¥ main() å‡½æ•°ä¸­åŽ»æ‰§è¡Œï¼›è€Œå½“ main() å‡½æ•°è¿”回时,CPU æ‰§è¡Œè·¯å¾„显然再次回到我们的 trampoline ç¨‹åºã€‚简而言之,这种方法的思想就是:用这段 trampoline å°ç¨‹åºæ¥ä½œä¸º main() å‡½æ•°çš„外部包裹(external wrapper)。

下面给出一个简单的 trampoline ç¨‹åºç¤ºä¾‹(来自blob):

.text

.globl _trampoline
_trampoline:
 bl main
 /* if main ever returns we just call it again */
 b _trampoline
 


可以看出,当 main() å‡½æ•°è¿”回后,我们又用一条跳转指令重新执行 trampoline ç¨‹åºâ€•â€•å½“然也就重新执行 main() å‡½æ•°ï¼Œè¿™ä¹Ÿå°±æ˜¯ trampoline(弹簧床)一词的意思所在。

(七)初始化本阶段要使用到的硬件设备

这通常包括:(1)初始化至少一个串口,以便和终端用户进行 I/O è¾“出信息;(2)初始化计时器等。

在初始化这些设备之前,也可以重新把 LED ç¯ç‚¹äº®ï¼Œä»¥è¡¨æ˜Žæˆ‘们已经进入 main() å‡½æ•°æ‰§è¡Œã€‚

设备初始化完成后,可以输出一些打印信息,程序名字字符串、版本号等。

(八) æ£€æµ‹ç³»ç»Ÿçš„内存映射(memory map)

所谓内存映射就是指在整个 4GB ç‰©ç†åœ°å€ç©ºé—´ä¸­æœ‰å“ªäº›åœ°å€èŒƒå›´è¢«åˆ†é…ç”¨æ¥å¯»å€ç³»ç»Ÿçš„ RAM å•å…ƒã€‚比如,在 SA-1100 CPU ä¸­ï¼Œä»Ž 0xC000,0000 å¼€å§‹çš„ 512M åœ°å€ç©ºé—´è¢«ç”¨ä½œç³»ç»Ÿçš„ RAM åœ°å€ç©ºé—´ï¼Œè€Œåœ¨ Samsung S3C44B0X CPU ä¸­ï¼Œä»Ž 0x0c00,0000 åˆ° 0x1000,0000 ä¹‹é—´çš„ 64M åœ°å€ç©ºé—´è¢«ç”¨ä½œç³»ç»Ÿçš„ RAM åœ°å€ç©ºé—´ã€‚虽然 CPU é€šå¸¸é¢„留出一大段足够的地址空间给系统 RAM,但是在搭建具体的嵌入式系统时却不一定会实现 CPU é¢„留的全部 RAM åœ°å€ç©ºé—´ã€‚也就是说,具体的嵌入式系统往往只把 CPU é¢„留的全部 RAM åœ°å€ç©ºé—´ä¸­çš„一部分映射到 RAM å•å…ƒä¸Šï¼Œè€Œè®©å‰©ä¸‹çš„那部分预留 RAM åœ°å€ç©ºé—´å¤„于未使用状态。由于上述这个事实,因此 Boot Loader çš„ stage2 å¿…须在它想干点什么 (比如,将存储在 flash ä¸Šçš„内核映像读到 RAM ç©ºé—´ä¸­) ä¹‹å‰æ£€æµ‹æ•´ä¸ªç³»ç»Ÿçš„内存映射情况,也即它必须知道 CPU é¢„留的全部 RAM åœ°å€ç©ºé—´ä¸­çš„哪些被真正映射到 RAM åœ°å€å•å…ƒï¼Œå“ªäº›æ˜¯å¤„于 "unused" çŠ¶æ€çš„。

(1) å†…存映射的描述

可以用如下数据结构来描述 RAM åœ°å€ç©ºé—´ä¸­çš„一段连续(continuous)的地址范围:

typedef struct memory_area_struct {
 u32 start; /* the base address of the memory region */
 u32 size; /* the byte number of the memory region */
 int used;
} memory_area_t;
 


这段 RAM åœ°å€ç©ºé—´ä¸­çš„连续地址范围可以处于两种状态之一:(1)used=1,则说明这段连续的地址范围已被实现,也即真正地被映射到 RAM å•å…ƒä¸Šã€‚(2)used=0,则说明这段连续的地址范围并未被系统所实现,而是处于未使用状态。

基于上述 memory_area_t æ•°æ®ç»“构,整个 CPU é¢„留的 RAM åœ°å€ç©ºé—´å¯ä»¥ç”¨ä¸€ä¸ª memory_area_t ç±»åž‹çš„数组来表示,如下所示:

memory_area_t memory_map[NUM_MEM_AREAS] = {
 [0 ... (NUM_MEM_AREAS - 1)] = {
  .start = 0,
  .size = 0,
  .used = 0
 },
};
 


(2) å†…存映射的检测

下面我们给出一个可用来检测整个 RAM åœ°å€ç©ºé—´å†…存映射情况的简单而有效的算法:

/* æ•°ç»„初始化 */
for(i = 0; i < NUM_MEM_AREAS; i++)
 memory_map.used = 0;

/* first write a 0 to all memory locations */
for(addr = MEM_START; addr < MEM_END; addr += PAGE_SIZE)
 * (u32 *)addr = 0;

for(i = 0, addr = MEM_START; addr < MEM_END; addr += PAGE_SIZE) {
     /*
      * æ£€æµ‹ä»ŽåŸºåœ°å€ MEM_START+i*PAGE_SIZE å¼€å§‹,大小为
* PAGE_SIZE çš„地址空间是否是有效的RAM地址空间。
      */
     è°ƒç”¨3.1.2节中的算法test_mempage()ï¼›
     if ( current memory page isnot a valid ram page) {
  /* no RAM here */
  if(memory_map.used )
   i++;
  continue;
 }
 
 /*
  * å½“前页已经是一个被映射到 RAM çš„有效地址范围
  * ä½†æ˜¯è¿˜è¦çœ‹çœ‹å½“前页是否只是 4GB åœ°å€ç©ºé—´ä¸­æŸä¸ªåœ°å€é¡µçš„别名?
  */
 if(* (u32 *)addr != 0) { /* alias? */
  /* è¿™ä¸ªå†…存页是 4GB åœ°å€ç©ºé—´ä¸­æŸä¸ªåœ°å€é¡µçš„别名 */
  if ( memory_map.used )
   i++;
  continue;
 }
 
 /*
  * å½“前页已经是一个被映射到 RAM çš„有效地址范围
  * è€Œä¸”它也不是 4GB åœ°å€ç©ºé—´ä¸­æŸä¸ªåœ°å€é¡µçš„别名。
  */
 if (memory_map.used == 0) {
  memory_map.start = addr;
  memory_map.size = PAGE_SIZE;
  memory_map.used = 1;
 } else {
  memory_map.size += PAGE_SIZE;
 }
} /* end of for (…) */
 


在用上述算法检测完系统的内存映射情况后,Boot Loader ä¹Ÿå¯ä»¥å°†å†…存映射的详细信息打印到串口。

(九)加载内核映像和根文件系统映像

(1) è§„划内存占用的布局

这里包括两个方面:(1)内核映像所占用的内存范围;(2)根文件系统所占用的内存范围。在规划内存占用的布局时,主要考虑基地址和映像的大小两个方面。

对于内核映像,一般将其拷贝到从(MEM_START+0x8000) è¿™ä¸ªåŸºåœ°å€å¼€å§‹çš„大约1MB大小的内存范围内(嵌入式 Linux çš„内核一般都不操过 1MB)。为什么要把从 MEM_START åˆ° MEM_START+0x8000 è¿™æ®µ 32KB å¤§å°çš„内存空出来呢?这是因为 Linux å†…核要在这段内存中放置一些全局数据结构,如:启动参数和内核页表等信息。

而对于根文件系统映像,则一般将其拷贝到 MEM_START+0x0010,0000 å¼€å§‹çš„地方。如果用 Ramdisk ä½œä¸ºæ ¹æ–‡ä»¶ç³»ç»Ÿæ˜ åƒï¼Œåˆ™å…¶è§£åŽ‹åŽçš„大小一般是1MB。

(2)从 Flash ä¸Šæ‹·è´

由于像 ARM è¿™æ ·çš„嵌入式 CPU é€šå¸¸éƒ½æ˜¯åœ¨ç»Ÿä¸€çš„内存地址空间中寻址 Flash ç­‰å›ºæ€å­˜å‚¨è®¾å¤‡çš„,因此从 Flash ä¸Šè¯»å–数据与从 RAM å•å…ƒä¸­è¯»å–数据并没有什么不同。用一个简单的循环就可以完成从 Flash è®¾å¤‡ä¸Šæ‹·è´æ˜ åƒçš„工作:

while(count) {
 *dest++ = *src++; /* they are all aligned with word boundary */
 count -= 4; /* byte number */
};
 


(十) è®¾ç½®å†…核的启动参数

应该说,在将内核映像和根文件系统映像拷贝到 RAM ç©ºé—´ä¸­åŽï¼Œå°±å¯ä»¥å‡†å¤‡å¯åŠ¨ Linux å†…核了。但是在调用内核之前,应该作一步准备工作,即:设置 Linux å†…核的启动参数。

Linux 2.4.x ä»¥åŽçš„内核都期望以标记列表(tagged list)的形式来传递启动参数。启动参数标记列表以标记 ATAG_CORE å¼€å§‹ï¼Œä»¥æ ‡è®° ATAG_NONE ç»“束。每个标记由标识被传递参数的 tag_header ç»“构以及随后的参数值数据结构来组成。数据结构 tag å’Œ tag_header å®šä¹‰åœ¨ Linux å†…核源码的include/asm/setup.h å¤´æ–‡ä»¶ä¸­ï¼š

/* The list ends with an ATAG_NONE node. */
#define ATAG_NONE 0x00000000

struct tag_header {
 u32 size; /* æ³¨æ„ï¼Œè¿™é‡Œsize是字数为单位的 */
 u32 tag;
};
……
struct tag {
 struct tag_header hdr;
 union {
  struct tag_core  core;
  struct tag_mem32 mem;
  struct tag_videotext videotext;
  struct tag_ramdisk ramdisk;
  struct tag_initrd initrd;
  struct tag_serialnr serialnr;
  struct tag_revision revision;
  struct tag_videolfb videolfb;
  struct tag_cmdline cmdline;

  /*
   * Acorn specific
   */
  struct tag_acorn acorn;

  /*
   * DC21285 specific
   */
  struct tag_memclk memclk;
 } u;
};
 


在嵌入式 Linux ç³»ç»Ÿä¸­ï¼Œé€šå¸¸éœ€è¦ç”± Boot Loader è®¾ç½®çš„常见启动参数有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。

比如,设置 ATAG_CORE çš„代码如下:

params = (struct tag *)BOOT_PARAMS;

 params->hdr.tag = ATAG_CORE;
 params->hdr.size = tag_size(tag_core);

 params->u.core.flags = 0;
 params->u.core.pagesize = 0;
 params->u.core.rootdev = 0;

 params = tag_next(params);
 


其中,BOOT_PARAMS è¡¨ç¤ºå†…核启动参数在内存中的起始基地址,指针 params æ˜¯ä¸€ä¸ª struct tag ç±»åž‹çš„指针。宏 tag_next() å°†ä»¥æŒ‡å‘当前标记的指针为参数,计算紧临当前标记的下一个标记的起始地址。注意,内核的根文件系统所在的设备ID就是在这里设置的。

下面是设置内存映射情况的示例代码:

for(i = 0; i < NUM_MEM_AREAS; i++) {
  if(memory_map.used) {
   params->hdr.tag = ATAG_MEM;
   params->hdr.size = tag_size(tag_mem32);

   params->u.mem.start = memory_map.start;
   params->u.mem.size = memory_map.size;
   
   params = tag_next(params);
  }
}
 


可以看出,在 memory_map[]数组中,每一个有效的内存段都对应一个 ATAG_MEM å‚数标记。

Linux å†…核在启动时可以以命令行参数的形式来接收信息,利用这一点我们可以向内核提供那些内核不能自己检测的硬件参数信息,或者重载(override)内核自己检测到的信息。比如,我们用这样一个命令行参数字符串"console=ttyS0,115200n8"来通知内核以 ttyS0 ä½œä¸ºæŽ§åˆ¶å°ï¼Œä¸”串口采用 "115200bps、无奇偶校验、8位数据位"这样的设置。下面是一段设置调用内核命令行参数字符串的示例代码:

char *p;

 /* eat leading white space */
 for(p = commandline; *p == ' '; p++)
  ;

 /* skip non-existent command lines so the kernel will still
    * use its default command line.
  */
 if(*p == '')
  return;

 params->hdr.tag = ATAG_CMDLINE;
 params->hdr.size = (sizeof(struct tag_header) + strlen(p) + 1 + 4) >> 2;

 strcpy(params->u.cmdline.cmdline, p);

 params = tag_next(params);
 


请注意在上述代码中,设置 tag_header çš„大小时,必须包括字符串的终止符'',此外还要将字节数向上圆整4个字节,因为 tag_header ç»“构中的size æˆå‘˜è¡¨ç¤ºçš„是字数。

下面是设置 ATAG_INITRD çš„示例代码,它告诉内核在 RAM ä¸­çš„什么地方可以找到 initrd æ˜ è±¡(压缩格式)以及它的大小:

params->hdr.tag = ATAG_INITRD2;
 params->hdr.size = tag_size(tag_initrd);
 
 params->u.initrd.start = RAMDISK_RAM_BASE;
 params->u.initrd.size = INITRD_LEN;
 
 params = tag_next(params);
 


下面是设置 ATAG_RAMDISK çš„示例代码,它告诉内核解压后的 Ramdisk æœ‰å¤šå¤§ï¼ˆå•ä½æ˜¯KB):

params->hdr.tag = ATAG_RAMDISK;
params->hdr.size = tag_size(tag_ramdisk);
 
params->u.ramdisk.start = 0;
params->u.ramdisk.size = RAMDISK_SIZE; /* è¯·æ³¨æ„ï¼Œå•ä½æ˜¯KB */
params->u.ramdisk.flags = 1; /* automatically load ramdisk */
 
params = tag_next(params);
 


最后,设置 ATAG_NONE æ ‡è®°ï¼Œç»“束整个启动参数列表:

static void setup_end_tag(void)
{
 params->hdr.tag = ATAG_NONE;
 params->hdr.size = 0;
}
 


(十一)调用内核

Boot Loader è°ƒç”¨ Linux å†…核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000 åœ°å€å¤„。在跳转时,下列条件要满足:

1. CPU å¯„存器的设置:

R0=0;


R1=机器类型 ID;关于 Machine Type Number,可以参见linux/arch/arm/tools/mach-types。


R2=启动参数标记列表在 RAM ä¸­èµ·å§‹åŸºåœ°å€ï¼›


2. CPU æ¨¡å¼ï¼š

必须禁止中断(IRQs和FIQs);


CPU å¿…é¡» SVC æ¨¡å¼ï¼›


3. Cache å’Œ MMU çš„设置:

MMU å¿…须关闭;


指令 Cache å¯ä»¥æ‰“开也可以关闭;


数据 Cache å¿…须关闭;
如果用 C è¯­è¨€ï¼Œå¯ä»¥åƒä¸‹åˆ—示例代码这样来调用内核:

void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE;
……
theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);
 


注意,theKernel()函数调用应该永远不返回的。如果这个调用返回,则说明出错。

相关帖子

沙发
msleep| | 2009-4-2 17:07 | 只看该作者

学习,要是有u-boot的方法就好了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

139

主题

185

帖子

0

粉丝