linux boot -main

来到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 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;
	}
}

/*
 * 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 filter
	   it 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_64
	struct 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#L96
struct 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 opposed
		to %ebx = 0 don't always report the SMAP signature on
		the final, failing, probe. */
		if (oreg.eflags & X86_EFLAGS_CF)
			break;

		/* Some BIOSes stop returning SMAP in the middle of
		the search loop.  We don't know exactly how the BIOS
		screwed up the map at that point, we might have a
		partial map, the full map, or complete garbage, so
		just return failure. */
		if (oreg.eax != SMAP) {
			count = 0;
			break;
		}
		//指针前移
		*desc++ = buf;
		//个数加1
		count++;
	} 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);
			else
				sprintf(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... */
			else
				ch++;
		}
	}
	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 to
		video_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();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值