http://www.linuxsir.org/bbs/thread356982.html 标
题: Linux内核2.6.31启动过程 (x86架构)
1. 构造调试环境
由于bochs内建调试功能, 且支持gdb, 用它调试内核会很方便. 1.1 构建磁盘镜像 代码:
100800+0 records in 100800+0 records out 51609600 bytes (52 MB) copied, 0.734578 s, 70.3 MB/s 注意count必须为63*16的倍数, 否则bochs识别硬盘会有问题. 1.2 挂载磁盘镜像 代码:
然后进行设备初始化: 代码:
代码:
16 heads, 63 sectors/track, 100 cylinders, total 100800 sectors Units = sectors of 1 * 512 = 512 bytes Disk identifier: 0x00000000 Device Boot Start End Blocks Id System /dev/loop0p1 63 100799 50368+ 83 Linux 将分区1挂载到/dev/loop1. 代码:
代码:
代码:
代码:
本文以grub-1.97~beta3为示例, 读者可自行安装其他的引导系统如lilo. 代码:
代码:
代码:
安装grub2到(hd0), 根目录在(hd0,1) 代码:
代码:
代码:
清理一下. 代码:
1.3 启动测试. 给上面的hd0.img配一个bochsrc文件, 可以拿bochs示例dlxlinux的配置文件来改, 只需将硬盘换为: 代码:
2. 从启动到保护模式. 为我们的bochs虚拟机编译一个内核. 不必太复杂, 目前我们只关心启动部分. 2.1 安装内核 退出bochs, 挂载hd0.img: 代码:
代码:
然后将下列配置写到/mnt/img/boot/grub/grub.cfg 代码:
2.2 从loader到内核 先测试一下bochs的调试能力. bochs以调试模式启动后, 会自动停在BIOS中Reset代码, 地址为0xFFFFFFF0. 我们在bochs console里输入: 代码:
并加载用户的选择内核文件(vmlinuz-xxx). loader和内核之间的协议, 在内核源码Document/x86/boot.txt里有详尽的描述. 内核文件包括实模式代码和保护模式代码. loader读取内核文件的头部信息, 从而得知实模式和保护模式代码的大小. 保护模式部分被加载到0x100000(1MB), 实模式启动代码和数据/ 堆栈段位置可以重定位在0x10000开始的低端内存的任何位置. 2.3 内核是怎样链成的... 首先编译内核的保护模式代码, 生成源码根目录的vmlinux. 这是一个elf格式的文件, 可以用readelf查看. 代码:
代码:
接着vmlinux洗净压缩. 这个工作在 arch/x86/boot/compressed目录进行. 读Makefile可以看到先进行objcopy操作, 在compressed目录下生成vmlinux.bin. 代码:
接着根据用户的选择进行压缩, 目前支持gz, bz2, lzma三种方式. 我用默认方式, 生成vimlinux.bin.gz. 代码:
紧接着加入自解压部分, 生成新的vmlinux. 代码:
接着工作转移到boot目录, 对上面的vmlinux进行strip操作, 生成二进制格式的vmlinux.bin. 这里面包括全部的包含模式代码, 启动时第一条语句会被加载到0x100000(1MB). 然后编译实模式代码, 包括已无用的引导扇区和setup部分, 生成setup.elf. 然后进行strip, 生成setup.bin. 最后, 利用内核工具"build", 将setup.bin和vmlinux.bin拷贝在一起, 并填上必要的信息如setup部分的大小等, grub等引导程序可以使用的bzImage诞生了. 2.3 内核实模式代码 实模式代码位于arch/x86/boot. 记得Linux2.6.18之前所有的代码都用汇编写成, 2.6.18之后大部分替换成了C. 入口点在header.S文件, 即包含了无用的"引导扇区代码", 也包含了引导程序能识别的头部信息. 第一条可执行语句在偏移0x200的位置(跳过引导扇区), 执行必要的初始化操作, 然后将控制权交给C程序, 即main.c里的main()函数. 有了main()函数, 接下来的过程就像读文档一样方便了 在main()的最后调用go_to_protected_mode(). 这个函数位于pm.c, 它打开A20, 初始化协处理器(如果有), 关掉所有中断, 设置空的IDT和最基本的GDT, 接着调用protected_mode_jump跳转到保护模式的入口代码0x100000. 这个函数定义在pmjump.S. 注意跳转的时候, boot_params被放在esi寄存器. 2.4 保护模式:自解压过程 内核保护模式的入口在arch/x86/boot/compressed/head_32.S (32位架构). 我们利用bochs的调试功能, 跟着走一遍: 代码:
代码:
在这里, esi还是指向来自与实模式的boot_params. 接下来的任务, 就是拷贝和解压. 目的地在0x1000000(16MB). 解压部分是C语言函数. 最后跳转到0x1000000. 3. 内核启动 3.1 平台相关的初始化 我们将断点设在内核的入口点 0x1000000(16MB), 继续执行, 内核会自己解压, 并停在入口点. 执行反汇编操作: 代码:
<待细化, 欢迎补充> 3.2 平台无关的初始化 start_kernel()是一个高层函数, 与架构无关, 但指挥架构相关的代码完成剩余的初始化部分. <待续> <若有异议, 欢迎讨论一起研究> 个人学习笔记, 如需转载, 请注明出处: 张力辉 <sword...@hotmail.com> |