3.3.1 复制初始化头变量
131行是第一个函数,copy_boot_params(),顾名思义用来拷贝启动参数到内存的0号页面中:
29static void copy_boot_params(void) 30{ 31 struct old_cmdline { 32 u16 cl_magic; 33 u16 cl_offset; 34 }; 35 const struct old_cmdline * const oldcmd = 36 (const struct old_cmdline *)OLD_CL_ADDRESS; 37 38 BUILD_BUG_ON(sizeof boot_params != 4096); 39 memcpy(&boot_params.hdr, &hdr, sizeof hdr); 40 41 if (!boot_params.hdr.cmd_line_ptr && 42 oldcmd->cl_magic == OLD_CL_MAGIC) { 43 /* Old-style command line protocol. */ 44 u16 cmdline_seg; 45 46 /* Figure out if the command line falls in the region 47 of memory that an old kernel would have copied up 48 to 0x90000... */ 49 if (oldcmd->cl_offset < boot_params.hdr.setup_move_size) 50 cmdline_seg = ds(); 51 else 52 cmdline_seg = 0x9000; 53 54 boot_params.hdr.cmd_line_ptr = 55 (cmdline_seg << 4) + oldcmd->cl_offset; 56 } 57} |
第35行,const struct old_cmdline * const oldcmd,这里用到const的的概念了。首先,oldcmd是个指针,指向old_cmdline结构。前面加上const,说明是个指针常量。其*前面的const,有说明这个oldcmd指向的内容也是一个常量。好了,这里就是两个常量。
接下来,39行,将hdr的内容拷贝到全局变量boot_params的hdr字段中。注意哈,这两个hdr不是同一个东西一个是header.S文件中定义的数据段中的内容,一个是boot_params数据结构中的一个字段,只是名字叫hdr。在这里第一次见到boot_params,它是在arch/x86/include/asm/Bootparam.h文件中定义。这里第一次出现,接下来的大部分工作都是填充这个boot_params。这个boot_params来头可不小,它刚好是一个页面的大小。在arch/x86/boot/Main.c的第18行:
18 struct boot_params boot_params __attribute__((aligned(16)));
注意,这个变量boot_params是Main.c的全局变量,且未被初始化,所以位于BSS段。grub把vmlinuz加载进内存后,boot_params就位于_bss_start的开始位置。而后面当启动保护模式的分页功能后,第一个页面就是从它开始的(注意,不是从0x0开始的喔)。所以内核注释它为“zeropage”,即所谓的0号页面,足见这个boot_params的重要性:
84/* The so-called "zeropage" */ 85struct boot_params { 86 struct screen_info screen_info; /* 0x000 */ 87 struct apm_bios_info apm_bios_info; /* 0x040 */ 88 __u8 _pad2[4]; /* 0x054 */ 89 __u64 tboot_addr; /* 0x058 */ 90 struct ist_info ist_info; /* 0x060 */ 91 __u8 _pad3[16]; /* 0x070 */ 92 __u8 hd0_info[16]; /* obsolete! */ /* 0x080 */ 93 __u8 hd1_info[16]; /* obsolete! */ /* 0x090 */ 94 struct sys_desc_table sys_desc_table; /* 0x0a0 */ 95 __u8 _pad4[144]; /* 0x0b0 */ 96 struct edid_info edid_info; /* 0x140 */ 97 struct efi_info efi_info; /* 0x1c0 */ 98 __u32 alt_mem_k; /* 0x1e0 */ 99 __u32 scratch; /* Scratch field! */ /* 0x1e4 */ 100 __u8 e820_entries; /* 0x1e8 */ 101 __u8 eddbuf_entries; /* 0x1e9 */ 102 __u8 edd_mbr_sig_buf_entries; /* 0x1ea */ 103 __u8 _pad6[6]; /* 0x1eb */ 104 struct setup_header hdr; /* setup header */ /* 0x1f1 */ 105 __u8 _pad7[0x290-0x1f1-sizeof(struct setup_header)]; 106 __u32 edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 0x290 */ 107 struct e820entry e820_map[E820MAX]; /* 0x2d0 */ 108 __u8 _pad8[48]; /* 0xcd0 */ 109 struct edd_info eddbuf[EDDMAXNR]; /* 0xd00 */ 110 __u8 _pad9[276]; /* 0xeec */ 111} __attribute__((packed)); |
就这个东西,把前面的初始化变量hdr拷贝到boot_params的hdr字段中。它的hdr字段是个setup_header结构,定义在同一个头文件中:
24struct setup_header { 25 __u8 setup_sects; 26 __u16 root_flags; 27 __u32 syssize; 28 __u16 ram_size; 29#define RAMDISK_IMAGE_START_MASK 0x07FF 30#define RAMDISK_PROMPT_FLAG 0x8000 31#define RAMDISK_LOAD_FLAG 0x4000 32 __u16 vid_mode; 33 __u16 root_dev; 34 __u16 boot_flag; 35 __u16 jump; 36 __u32 header; 37 __u16 version; 38 __u32 realmode_swtch; 39 __u16 start_sys; 40 __u16 kernel_version; 41 __u8 type_of_loader; 42 __u8 loadflags; 43#define LOADED_HIGH (1<<0) 44#define QUIET_FLAG (1<<5) 45#define KEEP_SEGMENTS (1<<6) 46#define CAN_USE_HEAP (1<<7) 47 __u16 setup_move_size; 48 __u32 code32_start; 49 __u32 ramdisk_image; 50 __u32 ramdisk_size; 51 __u32 bootsect_kludge; 52 __u16 heap_end_ptr; 53 __u8 ext_loader_ver; 54 __u8 ext_loader_type; 55 __u32 cmd_line_ptr; 56 __u32 initrd_addr_max; 57 __u32 kernel_alignment; 58 __u8 relocatable_kernel; 59 __u8 _pad2[3]; 60 __u32 cmdline_size; 61 __u32 hardware_subarch; 62 __u64 hardware_subarch_data; 63 __u32 payload_offset; 64 __u32 payload_length; 65 __u64 setup_data; 66} __attribute__((packed));
|
细心的朋友可能已经发现了,这些字段跟我们前面在header.S中看到的初始化头变量hdr中的字段是一一对应的,所以在c代码里面才能通过memcpy的方式拷贝过来。
随后的if语句针对老内核,对boot_params.hdr的cmd_line_ptr字段做些调整,我们就不管他了。