Linux 2.6 版内核引导过程浅谈(2)

对于BIOS,主要由两家制造商制造,驻留在主板的ROM里。有了BIOS,硬件制造商可以只需要关注硬件而不需要关注软件。BIOS的服务程序,是通过调用中断服务程序来实现的。BIOS加载bootloader程序,Bootloader也可以通过BIOS提供的中断,向BIOS获取系统的信息。

1)电源启动时钟发生器并在总线上产生一个#POWERGOOD的中断。

2)产生CPU的RESET中断(此时CPU处于8086工作模式)。

3) %ds=%es=%fs=%gs=%ss=0,%cs=0xFFFF0000,

%eip = 0x0000FFF0 (ROM BIOS POST code).

(指令指针eip,数据段寄存器ds,代码段寄存器cs)

4)在中断无效状态下执行所有POST检查。

5)在地址0初始化中断向量表IVT。

6) 0x19中断以启动设备号为参数调用BIOS启动装载程序。这个程序从启动设备   (硬盘)的0扇面1扇区读取数据到内存物理地址0x7C00开始装载。

就是说,CPU 在  BIOS的入口(CS:IP=FFFF:0000)处执行BIOS的汇编程序,BIOS程序功能有系统硬件的检测,提供中断访问接口以访问硬件。而后被BIOS程序通过中断0x19调用磁盘MBR上的bootloader程序,将bootloader程序加载到ox7c00处,而后跳转到0x7c00,这样,位于0x7c00处的bootloader程序,就可以执行了。

从BIOS执行MBR中的程序开始,就是linux的代码在做的事情了。

5. BOOTLOADER

bootloader程序是为计算机加载(load)计算机操作系统的。boot(引导)是bootstrap的简写,bootstrap是引导指令的意思。

传统意义上,由于CPU加电之后,CPU只能访问ROM或者RAM里的数据,而这个时候是没有计算机操作系统的,所以需要有一段程序能够完成加载存储在非易失性存储介质(比如硬盘)上的操作系统到RAM中的功能。这段程序存储在ROM里,BIOS就是这类程序中的一种。

还有一种bootloader程序,位于硬盘上,被BIOS调用,用于加载内核。这样的bootloader程序,在PC机上主要有grub、lilo、syslinux等。

GRUB(GRand Unified Bootloader)是当前linux诸多发行版本默认的引导程序。嵌入式系统上,最常见的bootloader是U-BOOT。

这样的bootloader一般位于MBR的最前部。在linux系统中,bootloader也可以写入文件系统所在分区中。比如,grub程序就非常强大。Gurb运行后,将初始化设置内核运行所需的环境。然后加载内核镜像。

grub磁盘引导全过程

stage1 :

grub读取磁盘第一个512字节(硬盘的 0 道 0 面 1 扇区)被称为MBR(主引导记录),也称为bootsect)。MBR由一部分bootloader的引导代码、分区表和魔数三部分组成。

stage1_5 :

识别各种不同的文件系统格式。这使得grub识别到文件系统。

stage2 :

加载系统引导菜单,加载vmlinuz和initrd

6. 内核加载过程

启动过程是和体系结构相关的,对于x86体系结构,对于2.6的内核,可以分为以下过程:

1) BOIS选择启动设备。

2)从启动设备装载bootsector,。

3)Bootsector中的grub程序装载bzImage(包含有setup、解压缩程序和内核映像)。

4)在保护模式下解压内核。

5)汇编代码执行低级初始化(主要是对硬件如CPU和内存的初始化)。

6)执行上层C语言的初始化。

bootsect.o setup.o 解压缩程序misc.o以及内核镜像vmlinuz被压缩成bzImage文件.linux 2.6中,bootsect.S和setup.S被整合为header.S。

注 :ELF可重定位object文件(.o)  静态链接库文件.a),这是和启动密切相关的两个文件。因为.o和.a文件被链接成为可执行文件vmlinux。

bzImage的文件构成图如下:

vmlinuz构成:

1. 第一个512字节  (以前是在arch/i386/boot/bootsect.S)

2. 第二个一段代码,若干不多个512字节 (以前是在arch/i386/boot/setup.S)

3. 保护模式下的内核代码(在\arch\x86\boot\main.c)

Vmlinuz文件

vmlinux是采用linux所支持的可执行文件格式的包含有linux内核的静态链接的可执行文件,传统上,vmlinux被称为可引导的内核镜像。vmlinuz是vmlinux的压缩文件。

bzImage文件

使用make bzImage 命令编译内核源代码,可以得到采用zlib算法压缩的zImage文件,即big zImage文件。老的zImage解压缩内核到低端内存,bzImage解压缩内核到高端内存(1M(0x100000)以上),在保护模式下执行。

bzImage文件包含有bootsect.o + setup.o + misc.o + piggy.o.

Initrd文件

initrd是initialized ram disk 的意思。主要用于加载硬件驱动模块,辅助内核的启动。

header.S

D:\linux-2.6.35\arch\x86\boot\header.S 部分代码:

第一部分定义了3个节,

.bstext,

.bsdata,

.header,

这3个节共同构成了vmlinuz的第一个512字节

BOOTSEG = 0x07C0

SYSSEG = 0x1000

ljmp $BOOTSEG, $start2

start2:

movw %cs, %ax

movw %ax, %ds

movw %ax, %es

movw %ax, %ss

xorw %sp, %sp

sti

cld

movw $bugger_off_msg, %si

bugger_off_msg:

.ascii "Direct booting from floppy is no longer supported.\r\n"

.ascii "Please use a boot loader program instead.\r\n"

.ascii "\n"

.ascii "Remove disk and press any key to reboot . . .\r\n"

.byte 0

下面设置内核的属性,setup的过程需要。这些属性是:

.section ".header", "a"

.globl hdr

hdr:

setup_sects: .byte 0 /* Filled in by build.c */

root_flags: .word ROOT_RDONLY

syssize: .long 0 /* Filled in by build.c */

ram_size: .word 0 /* 已经不用了 */

vid_mode: .word SVGA_MODE

root_dev: .word 0 /* Filled in by build.c */

boot_flag: .word 0xAA55

header.S的第二部分,作用如同以前的setup.S

略。

最后,

# Jump to C code (should not return)

calll main

调用main.c

head_32.S

arch/x86/boot/compressed/head_32.S  是汇编写成的32位启动代码。

其前身是linux/boot/head.S文件。

startup发生在在绝对地址0x00001000处的。

head_32.s调用misc.c中的decompress_kernel()函数,将内核vmlinuz解压到0x100000处。

head_32.S 部分源码

/*

* Do the decompression, and jump to the new kernel..

*/

leal z_extract_offset_negative(%ebx), %ebp

/* push arguments for decompress_kernel: */

pushl %ebp /* output address */

pushl $z_input_len /* input_len */

leal input_data(%ebx), %eax

pushl %eax /* input_data */

leal boot_heap(%ebx), %eax

pushl %eax /* heap area */

pushl %esi /* real mode pointer */

call decompress_kernel

addl $20, %esp

零号页面也就是这个

/* The so-called "zeropage" */

struct boot_params {

struct screen_info screen_info; /* 0x000 */

struct apm_bios_info apm_bios_info; /* 0x040 */

__u8  _pad2[4]; /* 0x054 */

__u64  tboot_addr; /* 0x058 */

struct ist_info ist_info; /* 0x060 */

__u8  _pad3[16]; /* 0x070 */

__u8  hd0_info[16]; /* obsolete! */ /* 0x080 */

__u8  hd1_info[16]; /* obsolete! */ /* 0x090 */

struct sys_desc_table sys_desc_table; /* 0x0a0 */

__u8  _pad4[144]; /* 0x0b0 */

struct edid_info edid_info; /* 0x140 */

struct efi_info efi_info; /* 0x1c0 */

__u32 alt_mem_k; /* 0x1e0 */

__u32 scratch; /* Scratch field! */ /* 0x1e4 */

__u8  e820_entries; /* 0x1e8 */

__u8  eddbuf_entries; /* 0x1e9 */

__u8  edd_mbr_sig_buf_entries; /* 0x1ea */

__u8  _pad6[6]; /* 0x1eb */

struct setup_header hdr;    /* setup header */ /* 0x1f1 */

__u8  _pad7[0x290-0x1f1-sizeof(struct setup_header)];

__u32 edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 0x290 */

struct e820entry e820_map[E820MAX]; /* 0x2d0 */

__u8  _pad8[48]; /* 0xcd0 */

struct edd_info eddbuf[EDDMAXNR]; /* 0xd00 */

__u8  _pad9[276]; /* 0xeec */

} __attribute__((packed));

enum {

X86_SUBARCH_PC = 0,

X86_SUBARCH_LGUEST,

X86_SUBARCH_XEN,

X86_SUBARCH_MRST,

X86_NR_SUBARCHS,

};

Misc.c部分源码

D:\linux-2.6.35.4\arch\x86\boot\compressed\misc.c

做字符串显示用的scroll和putstr好难懂啊:

static void scroll(void)   {

int i;

memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2);

for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2)

vidmem[i] = ' ';

}

static void __putstr(int error, const char *s)

{

int x, y, pos;

char c;

#ifndef CONFIG_X86_VERBOSE_BOOTUP

if (!error)

return;

#endif

if (real_mode->screen_info.orig_video_mode == 0 &&

lines == 0 && cols == 0)

return;

x = real_mode->screen_info.orig_x;

y = real_mode->screen_info.orig_y;

while ((c = *s++) != '\0') {

if (c == '\n') {

x = 0;

if (++y >= lines) {

scroll();

y--;

}

} else {

vidmem[(x + cols * y) * 2] = c;

if (++x >= cols) {

x = 0;

if (++y >= lines) {

scroll();

y--;

}

}

}//end else

}//end while

real_mode->screen_info.orig_x = x;

real_mode->screen_info.orig_y = y;

pos = (x + cols * y) * 2; /* Update cursor position */

outb(14, vidport);

outb(0xff & (pos >> 9), vidport+1);

outb(15, vidport);

outb(0xff & (pos >> 1), vidport+1);

}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/25019.html