本帖最后由 yuanlai2010 于 2014-7-31 17:16 编辑
YL-boot流程及部分源码解析(总结)
参与Helper2416开发板助学计划心得
简介:
YL_boot本身就是一个裸机程序,也不算复杂,最近给它加入了启动内核的功能,终于也算得上是个真正的boot了。在后续的学习中,希望能移植一些高速数据传输的模块进去,让它变得更加完善。
YL-boot-for-SD 流程及代码解析
Note0:
绿色的处理框代表是用汇编实现的,白色的使用C语言实现
Note1:
通过串口传进来的文件(十六进制数据)的前56字节的数据先暂时存放在数组中,后续的数据库直接存放在物理地址0x30000038开始的位置,等数据全部传输完后,再把数组中的数据拷贝到0x30000000的位置,这是因为我需要用定时器来判断文件是否传输完毕,所以在物理地址0x30000000处的位置放置了异常向量表(通过MMU映射到0x00000000),文件接收完后就不需要中断功能了,所以可以直接覆盖这段位置的内容。
把数据放在这个位置是为了方便代码的运行,尤其是在开头放置了异常向量表的程序,由于开启MMU。所以用户代码可以直接从0x00000000处开始运行。
Note2:
串口中文件的传输是连续的,字节与字节之间的时间间隔是一定的,每接收到一个字节就重装一次计数器的值,保证不会触发中断,当文件传输完后,不再重装计数器的值,就只能等着计数器的值减少到0,接着触发中断,来关闭这个接收字节的循环,完成文件的接收
Start.S解析
- @******************************************************************************
- @ File:Start.S
- @ 功能:启动文件
- @******************************************************************************
- @
- .set IRQ_STACK_SIZE, 0x100
- @
- @ to set the mode bits in CPSR for different modes
- @
- .set MODE_IRQ, 0x12
- .set MODE_SVC, 0x13
- .equ I_F_BIT, 0xC0
- .text
- .global Entry
- .global UserEntry
- Entry:
- @
- @ Stop Watchdog
- @
- LDR R0, =0x53000000 @ Stop Watchdog
- MOV R1, #0x00
- STR R1, [R0]
- @
- @ Set up the Stack for IRQ mode
- @
- LDR r0, =_stack @ Read the stack address
- MSR cpsr_c, #MODE_IRQ|I_F_BIT @ change to IRQ mode
- MOV sp,r0 @ write the stack pointer
- SUB r0,r0, #IRQ_STACK_SIZE @ give stack space
- @
- @ Set up the Stack for SVC mode
- @
- MSR cpsr_c, #MODE_SVC|I_F_BIT @ change to SVC mode
- MOV sp,r0 @ write the stack pointer
- @
- @ Clear the BSS section here
- @
- Clear_Bss_Section:
- LDR r0, =_bss_start @ Start address of BSS
- LDR r1, =(_bss_end - 0x04) @ End address of BSS
- MOV r2, #0
- Loop:
- STR r2, [r0], #4 @ Clear one word in BSS
- CMP r0, r1
- BLE Loop @ Clear till BSS end
- @Lowlevel_init:
- BL clock_init @ Branch to clock_init
- BL SDRAM_Init @初始化sdram
- BL mmu_init @初始化并使能mmu
- BL CopyVectorTable @拷贝异常向量表到0x00000000
- BL OriginalCopy @跳转到OriginalCopy(main)函数
- @LDR SP, =0x04000000 @设置栈到04000000
- MOV r1, #0 @清除ICaches和DCaches中的数据
- MCR p15, 0, r1, c7, c7, 0
- LDR PC, UserEntry @跳转到SDRAM执行用户程序(0x00000000)
- UserEntry:
- .word 0x0
- .end
复制代码Note0:
当程序从OriginalCopy返回后,需要使无效ICache中的数据,由于开启了MMU和Cache。在接收文件的时候使用了中断,CPU在中断处理的时候从0x00000000的中断向量表中读取了指令,会把这附近的指令全部读取到Cache中,如果没有使无效ICache就跳转到0x00000000执行用户代码的话,就会出现问题咯,因为CPU直接从Cache中读取数据了,这时候读取的就还是原来的中断向量表。所以需要使无效Cache中的数据,让CPU重新从0x00000000(物理地址0x30000000)处读取指令。
CopyVectorTable解析
- /***********************************************************************
- ** EXTERNAL FUNCTION PROTOTYPES
- ***********************************************************************/
- extern void Entry(void);
- extern void UndefInstHandler(void);
- extern void SVC_Handler(void);
- extern void AbortHandler(void);
- extern void IRQHandler(void);
- extern void FIQHandler(void);
- /******************************************************************************
- ** INTERNAL VARIABLE DEFINITIONS
- *******************************************************************************/
- const unsigned int S3C2416_VECTOR_BASE = 0x30000000;
- static unsigned int const vecTbl[14]=
- {
- 0xE59FF018, /* Opcode for loading PC with the contents of [PC + 0x18] */
- 0xE59FF018, /* Opcode for loading PC with the contents of [PC + 0x18] */
- 0xE59FF018, /* Opcode for loading PC with the contents of [PC + 0x18] */
- 0xE59FF018, /* Opcode for loading PC with the contents of [PC + 0x18] */
- 0xE59FF014, /* Opcode for loading PC with the contents of [PC + 0x14] */
- 0xE59FF010, /* Opcode for loading PC with (PC - 8) (eq. to while(1)) */
- 0xE59FF010, /* Opcode for loading PC with the contents of [PC + 0x10] */
- 0xE59FF010, /* Opcode for loading PC with the contents of [PC + 0x10] */
- (unsigned int)Entry,
- (unsigned int)UndefInstHandler,
- (unsigned int)SVC_Handler,
- (unsigned int)AbortHandler,
- (unsigned int)IRQHandler,
- (unsigned int)FIQHandler
- };
- /******************************************************************************
- ** FUNCTION DEFINITIONS
- *******************************************************************************/
- /**
- * \brief This function copies the vector table to a location in OCMC
- * RAM and sets the vector base address register.
- *
- * \param None.
- *
- * \return None.
- *
- **/
- void CopyVectorTable(void)
- {
- unsigned int *dest = (unsigned int *)S3C2416_VECTOR_BASE;
- unsigned int *src = (unsigned int *)vecTbl;
- unsigned int count;
- for(count = 0; count < sizeof(vecTbl)/sizeof(vecTbl[0]); count++)
- {
- dest[count] = src[count];
- }
- }
- /***************************** End Of File ***********************************/
复制代码Note:
由于从IROM启动,并不能直接使用中断向量表,同时要将64M的SDRA重映射到0x00000000处,所以也不同时能将0x40000000处内部SRAM重映射到0x00000000处,为了使用异常向量表,所以只能把异常向量表复制到SDRAM的开始的位置了,由于链接地址的关系,所以需要自己直接将机器码写到到数组中,然后在通过C函数复制到指定位置
OriginalCopy(主函数)解析
Note:
在接收烧写地址的时候,在PC上串口终端输入的是ASCII字符,而烧写地址是个unsigned int 类型的数据,所以需要有一个转换过程,为了能在一个函数中分析其原理,就不它写作一个单独的函数了。
YL-bioot-for_Kernel 流程及代码解析:
Note0:
绿色的处理框代表是用汇编实现的,白色的使用C语言实现
Note1:
相当于YL_boot_for_SD来说,这个流程很简单,各人认为这里最重要的就是设置好启动参数,以及怎么将参数传递给内核。
Original(主函数)解析
- /******************************** include ********************************/
- #include "nand.h"
- #include "uart.h"
- #include "boot_kernel.h"
- /****************************** functions ********************************/
- void original()
- {
- volatile unsigned char *codeindex = (volatile unsigned char *)0x30008000;
- uart_init();
- Nand_Init();
- printuart(" ******** YL-boot ******** \r\n");
- printuart(" EEWORLD ID: yuanlai2010 \r\n");
- printuart(" ******* 2014-07-29 ****** \r\n");
- printuart(" \r\n");
- printuart(" ***** Start Kernel **** \r\n");
- printuart(" Loading Kernel ...... \r\n");
- if(0==Nand_ReadSkipBad(0x40000,
- codeindex,
- 0x300000))
- {
- printuart(" Jumping to Kernel \r\n");
- printuart(" ********** end ********** \r\n\r\n");
- do_bootm_linux();
- }
- else
- {
- printuart(" Loarding Error \r\n");
- printuart(" ********** end ********** \r\n\r\n");
- }
- while(1);
- }
- /**************************** Code end ***********************************/
复制代码Note:
这里除了do_bootm_linux这个函数来说,都只是简单数据代码搬运的代码。关键在于do_bootm_linux这个函数,函数将完成设置启动参数及跳转至内核的工作,这个函数也是参照u-boot源码中do_bootm_linux函数来写的。
do_bootm_linux解析
do_bootm_linux.h
- #ifndef __BOOT_KERNEL_H
- #define __BOOT_KERNEL_H
- /**************************definition of symbols**************************/
- #define tag_next(t) ((struct tag *)((unsigned int *)(t) + (t)->hdr.size))
- #define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
- typedef struct bd_info {
- unsigned int bi_arch_number; /* unique id for this board */
- unsigned int bi_boot_params; /* where this board expects params */
- struct /* RAM configuration */
- {
- unsigned int start;
- unsigned int size;
- } bi_dram;
- }bd_t;
- /* The list ends with an ATAG_NONE node. */
- #define ATAG_NONE 0x00000000
- struct tag_header {
- unsigned int size;
- unsigned int tag;
- };
- /* The list must start with an ATAG_CORE node */
- #define ATAG_CORE 0x54410001
- struct tag_core {
- unsigned int flags; /* bit 0 = read-only */
- unsigned int pagesize;
- unsigned int rootdev;
- };
- #define ATAG_MEM 0x54410002
- struct tag_mem32 {
- unsigned int size;
- unsigned int start; /* physical start address */
- };
- #define ATAG_INITRD 0x54410005
- /* describes where the compressed ramdisk image lives (physical address) */
- #define ATAG_INITRD2 0x54420005
- struct tag_initrd {
- unsigned int start; /* physical start address */
- unsigned int size; /* size of compressed ramdisk image in bytes */
- };
- /* command line: \0 terminated string */
- #define ATAG_CMDLINE 0x54410009
- struct tag_cmdline {
- char cmdline[1]; /* this is the minimum size */
- };
- struct tag {
- struct tag_header hdr;
- union {
- struct tag_core core;
- struct tag_mem32 mem;
- struct tag_initrd initrd;
- struct tag_cmdline cmdline;
- }u;
- };
- void do_bootm_linux(void);
- #endif
复制代码 do_bootm_linux.c- /******************************** include ********************************/
- #include "boot_kernel.h"
- /******************************** functions *******************************/
- static struct tag *params;
- char *commandline = "root=/dev/mtdblock2 console=ttySAC0,115200 rootfstype=yaffs2 mem=64m";
- //root=/dev/mtdblock2 console=ttySAC0,115200 rootfstype=yaffs2 mem=64m
- unsigned int kernel_entry = 0x30008000;
- bd_t bd = {1685,0x30000100,{0x30000000,0x4000000}};
- unsigned int strlen(const char * s)
- {
- const char *sc;
- for (sc = s; *sc != '\0'; ++sc)
- /* nothing */;
- return sc - s;
- }
- char * strcpy(char * dest,const char *src)
- {
- char *tmp = dest;
- while ((*dest++ = *src++) != '\0')
- /* nothing */;
- return tmp;
- }
- static void setup_start_tag (bd_t bd)
- {
- params = (struct tag *) bd.bi_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);
- }
- static void setup_memory_tags (bd_t bd)
- {
- int i;
- params->hdr.tag = ATAG_MEM;
- params->hdr.size = tag_size (tag_mem32);
- params->u.mem.start = bd.bi_dram.start;
- params->u.mem.size = bd.bi_dram.size;
- params = tag_next (params);
- }
- static void setup_commandline_tag (bd_t bd, char *commandline)
- {
- char *p;
- if (!commandline)
- return;
- /* 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 == '\0')
- 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);
- }
- static void setup_initrd_tag (bd_t bd, unsigned int initrd_start, unsigned int initrd_end)
- {
- /* an ATAG_INITRD node tells the kernel where the compressed
- * ramdisk can be found. ATAG_RDIMG is a better name, actually.
- */
- params->hdr.tag = ATAG_INITRD2;
- params->hdr.size = tag_size (tag_initrd);
- params->u.initrd.start = initrd_start;
- params->u.initrd.size = initrd_end - initrd_start;
- params = tag_next (params);
- }
- static void setup_end_tag (bd_t bd)
- {
- params->hdr.tag = ATAG_NONE;
- params->hdr.size = 0;
- }
- static void setup_tag(void)
- {
- setup_start_tag (bd);
- setup_memory_tags (bd);
- setup_commandline_tag (bd, commandline);
- setup_initrd_tag (bd, 0, 0);
- setup_end_tag (bd);
- }
- void do_bootm_linux(void)
- {
- int machid = bd.bi_arch_number;
- void (*theKernel)(int zero, int arch, unsigned int params);
- theKernel = (void (*)(int, int, unsigned int))kernel_entry;
- setup_tag();
- theKernel(0, machid, bd.bi_boot_params);
- }
- /**************************** Code end ***********************************/
复制代码Note0:
由于此boot是专门用于Helper2416开发板,所以所有的参数都是直接在程序中给定的,代码不会去检测硬件信息。
Note1:
对于内核的启动只需要设置好memory_tags和commandline_tag这两个参数就好了,而start_tag和end_tag是必须的,对于initrd_tag不需要,因为Helper2416d的系统并没有用到RAMDISK,所以这里给它的后面两个参数都是0,也可以直接不要这个tag。memory_tags主要用于传递内存信息,commandline_tag指定了根文件系统的位置及指定了终端设备等信息
Note2:
theKernel是一个函数指针,同时这个指针指向的就是zImage的最终存放位置。当执行这条函数的时候,就一去不复返了,此后的BOOT代码将不会在用到了,所有的权限就交给内核了
自编boot体会:
对于编写这个boot只是让学习的裸机编程有个可以应用的地方,同时通过编写这个boot让自己进一步了解了u-boot的工作原理,也锻炼了自己分析复杂源码的能力,为以后深入学习linux内核做好一些准备。同时也发现,再复杂的程序按照步骤一步一步来,总能分析清楚的,不要被复杂的代码所吓到!坚持下去,慢慢发现自己会有进步的!
论坛ID:yuanlai2010
发表时间:2014-07-31