140 likes | 365 Views
uCLinux 启动流程. 徐远超 xuyuanchao@263.net http://xuyuanchao.ie.cnu.edu.cn. blob. Src/blob/start.S. Blob 代码分析. 第一阶段的代码在 start.s 中定义,大小为 l KB ,它包括从系统上电后在 0x00000000 地址开始执行的部分。 这部分代码运行在 Flash 中,它包括对 S3C44B0 的一些寄存器的初始化和将 Blob 第二阶段代码从 Flash 拷贝到 SDRAM 中。 除去第一阶段的 lKB 代码,剩下的部分都是第二阶段的代码。
E N D
uCLinux启动流程 徐远超 xuyuanchao@263.net http://xuyuanchao.ie.cnu.edu.cn
blob • Src/blob/start.S
Blob代码分析 • 第一阶段的代码在start.s中定义,大小为l KB,它包括从系统上电后在0x00000000地址开始执行的部分。 • 这部分代码运行在Flash中,它包括对S3C44B0的一些寄存器的初始化和将Blob第二阶段代码从Flash拷贝到SDRAM 中。 • 除去第一阶段的lKB代码,剩下的部分都是第二阶段的代码。 • 第二阶段的起始文件为trampoline.s被复制到SDRAM后,就从第一阶段跳转到这个文件开始执行剩余部分代码。 • 第二阶段最大为63KB,单词trampoline词义为“蹦床”,所以在这个程序中进行一些BSS段设置,堆栈的初始化等工作后,最后跳转到main.c.
relocate: • adr r0, _start • /* relocate the second stage loader */ • add r2, r0, #(64 * 1024) /* blob maximum size is 64kB */ • add r0, r0, #0x400 • ldr r1, BLOB_START • /* r0 = source address • * r1 = target address • * r2 = source end address • */ • copy_loop: • ldmia r0!, {r3-r10} • stmia r1!, {r3-r10} • cmp r0, r2 • ble copy_loop
adr r0, real_vectors • add r2, r0, #1024 • ldr r1, =0x0c000000 • add r1, r1, #0x08 • vector_copy_loop: • ldmia r0!, {r3-r10} • stmia r1!, {r3-r10} • cmp r0, r2 • ble vector_copy_loop • /* turn off the LED. if it stays off it is an indication that • * we didn't make it into the C code • */ • /* bl led_off */ • /* blob is copied to ram, so jump to it */ • ldr r0, BLOB_START • mov pc, r0
Trampoline.S • mrs r0, cpsr • bic r0, r0, #0x80 • msr cpsr, r0 • /* jump to C code */ • bl main • /* if main ever returns we just call it again */ • b _trampoline
Main.c • /* wait 10 seconds before starting autoboot */ • SerialOutputString("Autoboot in progress, press any key to stop "); • for(i = 0; i < blob_status.boot_delay; i++) { • serial_write('.'); • retval = SerialInputBlock(commandline, 1, 1); • if(retval > 0) • break; • } • /* no key was pressed, so proceed booting the kernel */ • if(retval == 0) { • commandline[0] = '\0'; • parse_command("boot"); • }
Linux.c • static int boot_linux(int argc, char *argv[]) • { • void (*theKernel)(int zero, int arch) = (void (*)(int, int))KERNEL_RAM_BASE; • clif(); • setup_start_tag(); • setup_memory_tags(); • setup_commandline_tag(argc, argv); • setup_initrd_tag(); • setup_ramdisk_tag(); • setup_end_tag(); • /* we assume that the kernel is in place */ • SerialOutputString("\nStarting kernel ...\n\n"); • serial_flush_output(); • /* disable subsystems that want to be disabled before kernel boot */ • exit_subsystems(); • /* start kernel */ • theKernel(0, ARCH_NUMBER); • SerialOutputString("Hey, the kernel returned! This should not happen.\n"); • return 0; • }
设置内核的启动参数 • Linux 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数 • 启动参数标记列表以标记 ATAG_CORE 开始,以标记 ATAG_NONE 结束 • 每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成 • 在嵌入式 Linux 系统中,通常需要由 Boot Loader 设置的常见启动参数有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等.
直接跳转到内核的第一条指令处 • 在跳转时,下列条件要满足 • 1. CPU 寄存器的设置 • R0=0;@R1=机器类型 ID;@R2=启动参数标记列表在 RAM 中起始基地址 • 2. CPU 模式 • 必须禁止中断(IRQs和FIQs); • CPU 必须 SVC 模式; • 3. Cache 和 MMU 的设置 • MMU 必须关闭; • 指令 Cache 可以打开也可以关闭; • 数据 Cache 必须关闭.
内核解压缩 为了节省ROM空间,内核都是压缩过的,因此要有专门的解压缩程序解压缩内核。真正完成这个功能的函数是decompress_kernel(),该函数在文件misc.c中定义,其中misc.c在目录uClinux-2.4.x\arch\armnommu\boot\compressed中
解压后,跳转到解压后的地址处执行 • 该函数完成的功能包括处理器结构的初始化、中断的初始化、定时器的初始化、进程相关的初始化以及内存初始化等初始化工作完成内核初始化;最后内核创建一个init线程,在该线程中调用init进程,完成系统的启动。
rest_init static void rest_init(void) { kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); //用kernel_thread建立了一个init进程,执行的是main.c中的init函数 unlock_kernel(); //内核解锁,不受保护 current->need_resched = 1; //该线程的标志位need_resched置位为1 cpu_idle(); //实现了进程init的被调度,从而函数init()被执行。 }
Init函数 • 首先调用do_basic_setup()来完成外部设备以及驱动程的初始化,然后在函数调用完成后,init()会调用函数prepare_namespace()来登录root文件系统,并加载RAMDISK。接下来init()会调用函数free_initmem()来释放内核初始化函数所用的系统内存,并且打开/dev/console设备重新定向控制台,让系统调用execve来执行shell程序init。