您的位置:首页 > 运维架构 > Linux

linux boot -main

2012-05-21 17:56 99 查看
来到main()函数后。进行了一系列的设置工作,然后通过调用go_to_protected_mode()进行下一步工作
/* -*- linux-c -*- ------------------------------------------------------- ***   Copyright (C) 1991, 1992 Linus Torvalds*   Copyright 2007 rPath, Inc. - All Rights Reserved*   Copyright 2009 Intel Corporation; author H. Peter Anvin**   This file is part of the Linux kernel, and is made available under*   the terms of the GNU General Public License version 2.** ----------------------------------------------------------------------- *//** Main module for the real-mode kernel code*/#include "boot.h"struct boot_params boot_params __attribute__((aligned(16)));char *HEAP = _end;char *heap_end = _end;		/* Default end of heap = no heap *//** Copy the header into the boot parameter block.  Since this* screws up the old-style command line protocol, adjust by* filling in the new-style command line pointer instead.*//** 将setup_header内容复制到boot_params的.hdr字段* hdr是在header.S第一个512字节尾部开始的那个结构* boot_params是所谓的0页面,因为它最后是被放在0号* 页框的。以下代码中兼容了老式命令行。*/static void copy_boot_params(void){//老式命令行包括魔数和偏移struct old_cmdline {u16 cl_magic;u16 cl_offset;};/** http://lxr.linux.no/linux+v3.3.6/arch/x86/include/asm/setup.h#L22 * 中定义了OLD_CL_ADDRESS=0x020,OLD_CL_MAGIC=0xA33F*/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);/** 如果hdr中的命令行未设置,并且魔数匹配,则是老式的命令行。在启动协议* >=2.02时,命令行参数是由setup_header.cmd_line_ptr提供的。这个字段* 是必须由加载器写入的。小于2.02就是老式的。在boot.txt中有介绍。*/if (!boot_params.hdr.cmd_line_ptr &&oldcmd->cl_magic == OLD_CL_MAGIC) {/* Old-style command line protocol. */u16 cmdline_seg;/* Figure out if the command line falls in the regionof memory that an old kernel would have copied upto 0x90000... */if (oldcmd->cl_offset < boot_params.hdr.setup_move_size)cmdline_seg = ds();elsecmdline_seg = 0x9000;boot_params.hdr.cmd_line_ptr =(cmdline_seg << 4) + oldcmd->cl_offset;}}/** Set the keyboard repeat rate to maximum.  Unclear why this* is done here; this might be possible to kill off as stale code.* 设置键盘reap rate*/static void keyboard_set_repeat(void){struct biosregs ireg;initregs(&ireg);ireg.ax = 0x0305;intcall(0x16, &ireg, NULL);}/** Get Intel SpeedStep (IST) information.* 这是INTEL的speedstep技术*/static void query_ist(void){struct biosregs ireg, oreg;/* Some older BIOSes apparently crash on this call, so filterit from machines too old to have SpeedStep at all. */if (cpu.level < 6)return;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;}/** Tell the BIOS what CPU mode we intend to run in.*/static void set_bios_mode(void){#ifdef CONFIG_X86_64struct biosregs ireg;initregs(&ireg);ireg.ax = 0xec00;ireg.bx = 2;intcall(0x15, &ireg, NULL);#endif}/** 将esp-STACI_SIZE:因栈大小为STACK_SIZE、esp在栈最高处(header.S)* 返回的结果就是栈最低地址(也是堆开始的地方)。如果堆结束大于栈低址* 则堆结束设置到栈低址处。*/static void init_heap(void){char *stack_end;//CAN_USE_HEAP表示是否支持堆(boot.txt)if (boot_params.hdr.loadflags & CAN_USE_HEAP) {asm("leal %P1(%%esp),%0": "=r" (stack_end) : "i" (-STACK_SIZE));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");}}void main(void){/* First, copy the boot header into the "zeropage" *//* 将hdr复制进boot_params.hdr*/copy_boot_params();/* Initialize the early-boot console *//* 控制台初始化*/console_init();if (cmdline_find_option_bool("debug"))puts("early console in setup code\n");/* End of heap check *//* 堆初始化 */init_heap();/* Make sure we have all the proper CPU support *//* CPU检查*/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. *//* 设置bois*/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 *//* 设置video模式*/set_video();/* Do the last things and invoke protected mode *//* 跳到保护模式*/go_to_protected_mode();}//////////////////////////////////////////////////////////////////////////内存检测:有三种内存检测方式,BIOS INT15h中断,88h(理论最大64M,一般16M),E801h(最大4G),E820h(所有),将检测到的内存存入boot_params相关字段detect_memory_e820():boot_params.e820_entries记录entry个数boot_params.e820_map是entry列表detect_memory_e801():boot_params.alt_mem_k存16M以下的部分detect_memory_88()boot_params.screen_info.ext_mem_k记录1M以上部分数据结构:/** /arch/x86/include/asm/e820.h#L58* E820中断会返回这个结构*/struct e820entry {__u64 addr;     /* start of memory segment */__u64 size;     /* size of memory segment */__u32 type;     /* type of memory segment */} __attribute__((packed));struct screen_info {__u8  orig_x;           /* 0x00 */__u8  orig_y;           /* 0x01 */__u16 ext_mem_k;        /* 0x02 */...} __attribute__((packed));///arch/x86/include/asm/bootparam.h#L96struct boot_params {struct screen_info screen_info;...__u32 alt_mem_k;...__u8  e820_entries;...struct e820entry e820_map[E820MAX];}__attribute__((packed));/** arch/x86/boot/memory.c* Memory detection code*/#include "boot.h"#define SMAP    0x534d4150      /* ASCII "SMAP" */static int detect_memory_e820(void){int count = 0;struct biosregs ireg, oreg;//desc指向数组入口struct e820entry *desc = boot_params.e820_map;static struct e820entry buf; /* static so it is zeroed *///初始化寄存器initregs(&ireg);ireg.ax  = 0xe820;ireg.cx  = sizeof buf;ireg.edx = SMAP;ireg.di  = (size_t)&buf;/** Note: at least one BIOS is known which assumes that the* buffer pointed to by one e820 call is the same one as* the previous call, and only changes modified fields.  Therefore,* we use a temporary buffer and copy the results entry by entry.** This routine deliberately does not try to account for* ACPI 3+ extended attributes.  This is because there are* BIOSes in the field which report zero for the valid bit for* all ranges, and we don't currently make any use of the* other attribute bits.  Revisit this if we see the extended* attribute bits deployed in a meaningful way in the future.*/do {//循环调用0x15:E820中断intcall(0x15, &ireg, &oreg);ireg.ebx = oreg.ebx; /* for next iteration... *//* BIOSes which terminate the chain with CF = 1 as opposedto %ebx = 0 don't always report the SMAP signature onthe final, failing, probe. */if (oreg.eflags & X86_EFLAGS_CF)break;/* Some BIOSes stop returning SMAP in the middle ofthe search loop.  We don't know exactly how the BIOSscrewed up the map at that point, we might have apartial map, the full map, or complete garbage, sojust return failure. */if (oreg.eax != SMAP) {count = 0;break;}//指针前移*desc++ = buf;//个数加1count++;} while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_map));//存放检查到的entry数量return boot_params.e820_entries = count;}static int detect_memory_e801(void){struct biosregs ireg, oreg;initregs(&ireg);ireg.ax = 0xe801;intcall(0x15, &ireg, &oreg);if (oreg.eflags & X86_EFLAGS_CF)return -1;/* Do we really need to do this? */if (oreg.cx || oreg.dx) {oreg.ax = oreg.cx;oreg.bx = oreg.dx;}if (oreg.ax > 15*1024) {return -1;      /* Bogus! */} else if (oreg.ax == 15*1024) {boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax;} else {/** This ignores memory above 16MB if we have a memory* hole there.  If someone actually finds a machine* with a memory hole at 16MB and no support for* 0E820h they should probably generate a fake e820* map.*/boot_params.alt_mem_k = oreg.ax;}return 0;}static int detect_memory_88(void){struct biosregs ireg, oreg;initregs(&ireg);ireg.ah = 0x88;intcall(0x15, &ireg, &oreg);boot_params.screen_info.ext_mem_k = oreg.ax;return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */}int detect_memory(void){int err = -1;if (detect_memory_e820() > 0)err = 0;if (!detect_memory_e801())err = 0;if (!detect_memory_88())err = 0;return err;}////////////////////////////////////////////////////////////////////////////通过中断取得系统描述表int query_mca(void){struct biosregs ireg, oreg;u16 len;initregs(&ireg);ireg.ah = 0xc0;intcall(0x15, &ireg, &oreg);if (oreg.eflags & X86_EFLAGS_CF)return -1;      /* No MCA present */set_fs(oreg.es);len = rdfs16(oreg.bx);if (len > sizeof(boot_params.sys_desc_table))len = sizeof(boot_params.sys_desc_table);copy_from_fs(&boot_params.sys_desc_table, oreg.bx, len);return 0;}//////////////////////////////////////////////////////////////////////////* ----------------------------------------------------------------------- *//** Select video mode*/#include "boot.h"#include "video.h"#include "vesa.h"static void store_cursor_position(void){struct biosregs ireg, oreg;initregs(&ireg);ireg.ah = 0x03;//int 0x10 ah=03h是读取光标位置intcall(0x10, &ireg, &oreg);//取出光标位置后,存入启动参数中boot_params.screen_info.orig_x = oreg.dl;boot_params.screen_info.orig_y = oreg.dh;if (oreg.ch & 0x20)boot_params.screen_info.flags |= VIDEO_FLAGS_NOCURSOR;if ((oreg.ch & 0x1f) > (oreg.cl & 0x1f))boot_params.screen_info.flags |= VIDEO_FLAGS_NOCURSOR;}//存储vidio模式static void store_video_mode(void){struct biosregs ireg, oreg;/* N.B.: the saving of the video page here is a bit silly,since we pretty much assume page 0 everywhere. */initregs(&ireg);ireg.ah = 0x0f;intcall(0x10, &ireg, &oreg);/* Not all BIOSes are clean with respect to the top bit */boot_params.screen_info.orig_video_mode = oreg.al & 0x7f;boot_params.screen_info.orig_video_page = oreg.bh;}/** Store the video mode parameters for later usage by the kernel.* This is done by asking the BIOS except for the rows/columns* parameters in the default 80x25 mode -- these are set directly,* because some very obscure BIOSes supply insane values.*/static void store_mode_params(void){u16 font_size;int x, y;/* For graphics mode, it is up to the mode-setting driver(currently only video-vesa.c) to store the parameters */if (graphic_mode)return;store_cursor_position();store_video_mode();if (boot_params.screen_info.orig_video_mode == 0x07) {/* MDA, HGC, or VGA in monochrome mode */video_segment = 0xb000;} else {/* CGA, EGA, VGA and so forth */video_segment = 0xb800;}//下面两句是读fs:0x485处的值set_fs(0);font_size = rdfs16(0x485); /* Font size, BIOS area */boot_params.screen_info.orig_video_points = font_size;x = rdfs16(0x44a);y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1;if (force_x)x = force_x;if (force_y)y = force_y;boot_params.screen_info.orig_video_cols  = x;boot_params.screen_info.orig_video_lines = y;}static unsigned int get_entry(void){char entry_buf[4];int i, len = 0;int key;unsigned int v;do {key = getchar();if (key == '\b') {if (len > 0) {puts("\b \b");len--;}} else if ((key >= '' && key <= '9') ||(key >= 'A' && key <= 'Z') ||(key >= 'a' && key <= 'z')) {if (len < sizeof entry_buf) {entry_buf[len++] = key;putchar(key);}}} while (key != '\r');putchar('\n');if (len == 0)return VIDEO_CURRENT_MODE; /* Default */v = 0;for (i = 0; i < len; i++) {v <<= 4;key = entry_buf[i] | 0x20;v += (key > '9') ? key-'a'+10 : key-'';}return v;}static void display_menu(void){struct card_info *card;struct mode_info *mi;char ch;int i;int nmodes;int modes_per_line;int col;nmodes = 0;for (card = video_cards; card < video_cards_end; card++)nmodes += card->nmodes;modes_per_line = 1;if (nmodes >= 20)modes_per_line = 3;for (col = 0; col < modes_per_line; col++)puts("Mode: Resolution:  Type: ");putchar('\n');col = 0;ch = '';for (card = video_cards; card < video_cards_end; card++) {mi = card->modes;for (i = 0; i < card->nmodes; i++, mi++) {char resbuf[32];int visible = mi->x && mi->y;u16 mode_id = mi->mode ? mi->mode :(mi->y << 8)+mi->x;if (!visible)continue; /* Hidden mode */if (mi->depth)sprintf(resbuf, "%dx%d", mi->y, mi->depth);elsesprintf(resbuf, "%d", mi->y);printf("%c %03X %4dx%-7s %-6s",ch, mode_id, mi->x, resbuf, card->card_name);col++;if (col >= modes_per_line) {putchar('\n');col = 0;}if (ch == '9')ch = 'a';else if (ch == 'z' || ch == ' ')ch = ' '; /* Out of keys... */elsech++;}}if (col)putchar('\n');}#define H(x)    ((x)-'a'+10)#define SCAN    ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n'))static unsigned int mode_menu(void){int key;unsigned int sel;puts("Press <ENTER> to see video modes available, ""<SPACE> to continue, or wait 30 sec\n");kbd_flush();while (1) {key = getchar_timeout();if (key == ' ' || key == 0)return VIDEO_CURRENT_MODE; /* Default */if (key == '\r')break;putchar('\a');  /* Beep! */}for (;;) {display_menu();puts("Enter a video mode or \"scan\" to scan for ""additional modes: ");sel = get_entry();if (sel != SCAN)return sel;probe_cards(1);}}/* Save screen content to the heap */static struct saved_screen {int x, y;int curx, cury;u16 *data;} saved;static void save_screen(void){/* Should be called after store_mode_params() */saved.x = boot_params.screen_info.orig_video_cols;saved.y = boot_params.screen_info.orig_video_lines;saved.curx = boot_params.screen_info.orig_x;saved.cury = boot_params.screen_info.orig_y;if (!heap_free(saved.x*saved.y*sizeof(u16)+512))return;         /* Not enough heap to save the screen */saved.data = GET_HEAP(u16, saved.x*saved.y);set_fs(video_segment);copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16));}static void restore_screen(void){/* Should be called after store_mode_params() */int xs = boot_params.screen_info.orig_video_cols;int ys = boot_params.screen_info.orig_video_lines;int y;addr_t dst = 0;u16 *src = saved.data;struct biosregs ireg;if (graphic_mode)return;         /* Can't restore onto a graphic mode */if (!src)return;         /* No saved screen contents *//* Restore screen contents */set_fs(video_segment);for (y = 0; y < ys; y++) {int npad;if (y < saved.y) {int copy = (xs < saved.x) ? xs : saved.x;copy_to_fs(dst, src, copy*sizeof(u16));dst += copy*sizeof(u16);src += saved.x;npad = (xs < saved.x) ? 0 : xs-saved.x;} else {npad = xs;}/* Writes "npad" blank characters tovideo_segment:dst and advances dst */asm volatile("pushw %%es ; ""movw %2,%%es ; ""shrw %%cx ; ""jnc 1f ; ""stosw \n\t""1: rep;stosl ; ""popw %%es": "+D" (dst), "+c" (npad): "bdS" (video_segment),"a" (0x07200720));}/* Restore cursor position */if (saved.curx >= xs)saved.curx = xs-1;if (saved.cury >= ys)saved.cury = ys-1;initregs(&ireg);ireg.ah = 0x02;         /* Set cursor position */ireg.dh = saved.cury;ireg.dl = saved.curx;intcall(0x10, &ireg, NULL);store_cursor_position();}void set_video(void){//hdr中的vid_mode字段需要被加载器命令行设置u16 mode = boot_params.hdr.vid_mode;//让HEAP=end,HEAP是main.c中的一个局变量RESET_HEAP();store_mode_params();save_screen();probe_cards(0);for (;;) {if (mode == ASK_VGA)mode = mode_menu();if (!set_mode(mode))break;printf("Undefined video mode number: %x\n", mode);mode = ASK_VGA;}boot_params.hdr.vid_mode = mode;vesa_store_edid();store_mode_params();if (do_restore)restore_screen();}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息