上次粗略的过了一下Header.S的控制权传递的过程。随着慢慢地进入main.c,发现仍然有必要重新审视一下Header.S。
main()的代码如下:
- void main(void)
-
{
-
/* First, copy the boot header into the "zeropage" */
-
copy_boot_params();
-
-
/* End of heap check */
-
init_heap();
-
-
/* Make sure we have all the proper CPU support */
-
if (validate_cpu()) {
-
puts("Unable to boot - please use a kernel appropriate "
-
"for your CPU.\n");
-
die();
-
}
-
-
/* Tell the BIOS what CPU mode we intend to run in. */
-
set_bios_mode();
-
-
/* Detect memory layout */
-
detect_memory();
-
-
/* Set keyboard repeat rate (why?) */
-
keyboard_set_repeat();
-
-
/* Query MCA information */
-
query_mca();
-
-
/* Query Intel SpeedStep (IST) information */
-
query_ist();
-
-
/* Query APM information */
-
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
-
query_apm_bios();
-
#endif
-
-
/* Query EDD information */
-
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
-
query_edd();
-
#endif
-
-
/* Set the video mode */
-
set_video();
-
-
/* Parse command line for 'quiet' and pass it to decompressor. */
-
if (cmdline_find_option_bool("quiet"))
-
boot_params.hdr.loadflags |= QUIET_FLAG;
-
-
/* Do the last things and invoke protected mode */
-
go_to_protected_mode();
-
}
main()做的第一件事情是保存boot_params。
其代码如下:
- static void copy_boot_params(void)
-
-
struct old_cmdline {
-
u16 cl_magic;
-
u16 cl_offset;
-
};
-
const struct old_cmdline * const oldcmd =
-
(const struct old_cmdline *)OLD_CL_ADDRESS;
-
-
BUILD_BUG_ON(sizeof boot_params != 4096);
-
memcpy(&boot_params.hdr, &hdr, sizeof hdr);
-
-
if (!boot_params.hdr.cmd_line_ptr &&
-
oldcmd->cl_magic == OLD_CL_MAGIC) {
-
/* Old-style command line protocol. */ //如果老的bootloader没有遵照新的hdr的协议传送启动参数给kernel,那么就要将hdr的命令行指针指向老的协议命令行。
-
u16 cmdline_seg;
-
- /*Figure out if the command line falls in the region
- of memory that an old kernel would have copied up
-
to 0x90000... */
-
if (oldcmd->cl_offset < boot_params.hdr.setup_move_size)
-
cmdline_seg = ds();
-
else
-
cmdline_seg = 0x9000;
-
-
boot_params.hdr.cmd_line_ptr =
-
(cmdline_seg << 4) oldcmd->cl_offset;
-
}
-
}
这个函数目的就是将hdr从第一个扇区的497个字节的位置复制到boot_params.hdr里面,从而以后的C代码能够很方便地对于hdr进行操作。
boot_params是一个未经初始化的全局变量,其由编译器负责分配在BSS段的空间。GCC把所有已经定义并且初始化的变量放置在.data段中,而所有已定义但未使用的变量放置在.bss段中。而bss段并不会保存在最后编译的可执行文件中。
.bss段的定义和初始化都是在header.S里面进行的。就在进入main()之前,header.S对bss进行清零操作而确保bss段在进入C代码之后可用。
#In Header.S
- # Zero the bss
-
movw $__bss_start, %di
-
movw $_end 3, %cx
-
xorl x, x
-
subw %di, %cx
-
shrw $2, %cx
-
rep; stosl
-
-
# Jump to C code (should not return)
-
calll main
而.bss在内存中的具体位置则由编译器按照Setup.ld的指示生成
- . = ALIGN(16);
-
.bss :
-
{
-
__bss_start = .;
-
*(.bss)
-
__bss_end = .;
-
}
main()做的第二件事情是初始化堆。- static void init_heap(void)
-
{
-
char *stack_end;
/*如果bootloader告诉kernel需要使用heap, bootloader需要把hdr.loadflags的CAN_US_HEAP位置1.*/
-
if (boot_params.hdr.loadflags & CAN_USE_HEAP) {
- /*esp是当前堆栈的底,堆栈的大小是STACK_SIZE,由此计算出堆栈的顶stack_end是esp-STACK_SIZE*/
-
asm("leal %P1(%%esp),%0"
-
: "=r" (stack_end) : "i" (-STACK_SIZE));
- /*堆的底是由boot_params.hdr.heap_end_ptr指定。这个值应该是由bootloader填入的,堆的大小是0x200。那么heap_end就是heap_end_ptr+0x200*/
- heap_end = (char *)
- ((size_t)boot_params.hdr.heap_end_ptr+0x200);
-
if (heap_end > stack_end) //如果堆栈和堆有重叠,那么就减小堆的大小。
-
heap_end = stack_end;
-
} else {
-
/* Boot protocol 2.00 only, no heap available */
-
puts("WARNING: Ancient bootloader, some functionality "
-
"may be limited!\n");
-
}
-
}
main()的第三件事情就是确保当前的CPU是kernel所支持的CPU,如果不支持就直接退出main
main()的第四件事情是set_bios_mode().
实际上这个函数只在x86 64位的CPU上可用。它执行了一个软中断 INT15, AH=0xEC. 这个中断到底做了什么,网上似乎很难找到。
接下来main执行detect_memory将当前内存使用情况填充到boot_params.e820map, boot_params.alt_mem_k和boot_params.screen_info.ext_mem_k。
代码实际上分别执行了三个软中断:
1. INT 15 AX=E820:遍历所有的连续内存块。这里所称的所有是指主板所能支持的全部物理内存空间。并将这个列表保存在boot_params.e820map中。这些内存块可以是以下的类型
- Type 1: Usable (normal) RAM
- Type 2: Reserved - unusable
- Type 3: ACPI reclaimable memory
- Type 4: ACPI NVS memory
- Type 5: Area containing bad memory
2. INT 15 AX=E801: 用来检查15M到16M内存空间之间的memory hole.PC理论上将15M-16M之间的内存地址空间设置为memory hole为一些需要使用到15M到16M地址空间的ISA设备。该中断的AX/CX返回15M以下内存的大小(其单位是1024,例如AX返回15则15M以下内存大小为15*1024),而BX/DX返回16M之上的内存大小(其单位是64K)。 看起来Linux使用这个中断来计算当前内存中实际可用的内存大小并将其保存在boot_params.alt_mem_k中。
3. INT 15 AH=88:这个软中断返回当前的扩展内存的大小(1M以上的空间)。实际上这个软中断应该是为了兼容80286而存在的,所以返回的值要么是16M要么是64M。返回的值存放在boot_params.screen_info.ext_mem_k中。
关于接下来的keyboard_set_repeat,函数本身的注释如下。似乎没什么用处。不看也罢。
/*
* Set the keyboard repeat rate to maximum. Unclear why this
* is done here; this might be possible to kill off as stale code.
*/
再接着query_mca会调用INT 15 AH=15H查询system description table并将其存放在boot_params.sys_desc_table. 这个和MCA总线相关吗??
然后就是query_ist获得当前CPU的SpeedStep的信息。
具体代码如下:
- initregs(&ireg);
-
ireg.ax = 0xe980; /* IST Support */
-
ireg.edx = 0x47534943; /* Request value */
-
intcall(0x15, &ireg, &oreg);
-
-
boot_params.ist_info.signature = oreg.eax;
-
boot_params.ist_info.command = oreg.ebx;
-
boot_params.ist_info.event = oreg.ecx;
-
boot_params.ist_info.perf_level = oreg.edx;
使用query_apm_bios以检查APM的支持,并将APM的信息存放在boot_params.apm_bios_info
使用query_edd以检查并设置boot_params.eddbuf_entries, boot_params.edd_mbr_sig_buf_entries, boot_params.edd_mbr_sig_buffer和boot_params.eddbuf.
按照磁头,柱面,扇区的寻址方式,INT13只能支持不超过8G的硬盘。这是远远不够的,所以EDD (BIOS Enhanced Disk Drive Services)被加入INT13中来支持大硬盘。query_edd会枚举所有当前硬盘的参数,并且进行记录为后面初始化硬盘驱动作准备
在进入go_to_protected_mode之前还剩下大事情就是set_video。今天太累了,明天再说了。
阅读(7157) | 评论(1) | 转发(8) |