Chinaunix首页 | 论坛 | 博客
  • 博客访问: 323657
  • 博文数量: 28
  • 博客积分: 609
  • 博客等级: 中士
  • 技术积分: 925
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-17 13:32
文章分类

全部博文(28)

文章存档

2022年(1)

2016年(1)

2015年(2)

2012年(1)

2011年(23)

分类: LINUX

2011-02-22 13:56:52

上次粗略的过了一下Header.S的控制权传递的过程。随着慢慢地进入main.c,发现仍然有必要重新审视一下Header.S。
main()的代码如下:
  1. void main(void)
  2. {
  3.     /* First, copy the boot header into the "zeropage" */
  4.     copy_boot_params();

  5.     /* End of heap check */
  6.     init_heap();

  7.     /* Make sure we have all the proper CPU support */
  8.     if (validate_cpu()) {
  9.         puts("Unable to boot - please use a kernel appropriate "
  10.          "for your CPU.\n");
  11.         die();
  12.     }

  13.     /* Tell the BIOS what CPU mode we intend to run in. */
  14.     set_bios_mode();

  15.     /* Detect memory layout */
  16.     detect_memory();

  17.     /* Set keyboard repeat rate (why?) */
  18.     keyboard_set_repeat();

  19.     /* Query MCA information */
  20.     query_mca();

  21.     /* Query Intel SpeedStep (IST) information */
  22.     query_ist();

  23.     /* Query APM information */
  24. #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
  25.     query_apm_bios();
  26. #endif

  27.     /* Query EDD information */
  28. #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
  29.     query_edd();
  30. #endif

  31.     /* Set the video mode */
  32.     set_video();

  33.     /* Parse command line for 'quiet' and pass it to decompressor. */
  34.     if (cmdline_find_option_bool("quiet"))
  35.         boot_params.hdr.loadflags |= QUIET_FLAG;

  36.     /* Do the last things and invoke protected mode */
  37.     go_to_protected_mode();
  38. }
main()做的第一件事情是保存boot_params。
其代码如下:
  1. static void copy_boot_params(void)

  2.     struct old_cmdline {
  3.         u16 cl_magic;
  4.         u16 cl_offset;
  5.     };
  6.     const struct old_cmdline * const oldcmd =
  7.         (const struct old_cmdline *)OLD_CL_ADDRESS;

  8.     BUILD_BUG_ON(sizeof boot_params != 4096);
  9.     memcpy(&boot_params.hdr, &hdr, sizeof hdr);

  10.     if (!boot_params.hdr.cmd_line_ptr &&
  11.      oldcmd->cl_magic == OLD_CL_MAGIC) {
  12.         /* Old-style command line protocol. */ //如果老的bootloader没有遵照新的hdr的协议传送启动参数给kernel,那么就要将hdr的命令行指针指向老的协议命令行。
  13.         u16 cmdline_seg;

  14.          /*Figure out if the command line falls in the region
  15.           of memory that an old kernel would have copied up
  16.          to 0x90000... */
  17.         if (oldcmd->cl_offset < boot_params.hdr.setup_move_size)
  18.             cmdline_seg = ds();
  19.         else
  20.             cmdline_seg = 0x9000;

  21.         boot_params.hdr.cmd_line_ptr =
  22.             (cmdline_seg << 4) oldcmd->cl_offset;
  23.     }
  24. }
这个函数目的就是将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
  1. # Zero the bss
  2. movw $__bss_start, %di
  3. movw $_end 3, %cx
  4. xorl x, x
  5. subw %di, %cx
  6. shrw $2, %cx
  7. rep; stosl
  8. # Jump to C code (should not return)
  9. calll main
而.bss在内存中的具体位置则由编译器按照Setup.ld的指示生成
  1. . = ALIGN(16);
  2. .bss :
  3. {
  4. __bss_start = .;
  5. *(.bss)
  6. __bss_end = .;
  7. }
main()做的第二件事情是初始化堆。
  1. static void init_heap(void)
  2. {
  3.     char *stack_end;
       /*如果bootloader告诉kernel需要使用heap, bootloader需要把hdr.loadflags的CAN_US_HEAP位置1.*/
  1.     if (boot_params.hdr.loadflags & CAN_USE_HEAP)
  2. /*esp是当前堆栈的底,堆栈的大小是STACK_SIZE,由此计算出堆栈的顶stack_end是esp-STACK_SIZE*/
  3.         asm("leal %P1(%%esp),%0"  
  4.          : "=r" (stack_end) : "i" (-STACK_SIZE));

  5. /*堆的底是由boot_params.hdr.heap_end_ptr指定。这个值应该是由bootloader填入的,堆的大小是0x200。那么heap_end就是heap_end_ptr+0x200*/
  6.        heap_end = (char *)
  7.             ((size_t)boot_params.hdr.heap_end_ptr+0x200)
  1.         if (heap_end > stack_end) //如果堆栈和堆有重叠,那么就减小堆的大小。
  2.             heap_end = stack_end;
  3.     } else {
  4.         /* Boot protocol 2.00 only, no heap available */
  5.         puts("WARNING: Ancient bootloader, some functionality "
  6.          "may be limited!\n");
  7.     }
  8. }
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的信息。
具体代码如下:
  1. initregs(&ireg);
  2.     ireg.ax = 0xe980;     /* IST Support */
  3.     ireg.edx = 0x47534943;     /* Request value */
  4.     intcall(0x15, &ireg, &oreg);

  5.     boot_params.ist_info.signature = oreg.eax;
  6.     boot_params.ist_info.command = oreg.ebx;
  7.     boot_params.ist_info.event = oreg.ecx;
  8.     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。今天太累了,明天再说了。
阅读(7188) | 评论(1) | 转发(8) |
给主人留下些什么吧!~~

harmony20132013-08-12 17:13:55

写的非常详尽准确。

可见楼主x86体系结构和汇编功力不错。