GRUB(四)common.c註釋

接上一篇,asm.s中main函數最後調用的是init_bios_info,這個函數將調用bios中斷功能讀取內存,磁盤,cdrom等信息。其實現如下:

/* This queries for BIOS information.  */
void
init_bios_info(void)
{
#ifndef STAGE1_5
	unsigned long cont, memtmp, addr;
	int drive;
#endif

	/*
	 * get_memsize(i)在asm.S中定義
	 * 調用BIOS返回內存大小,以KB爲單位.
	 * 輸入參數i == 0時返回常規內存大小,爲1時返回拓展內存大小
	 */

	mbi.mem_lower = get_memsize(0);	/* int12 --------safe enough */
	mbi.mem_upper = get_memsize(1);	/* int15/88 -----safe enough */

#ifndef STAGE1_5
  /*
   *  讀寫超過1MB的內存的地址時需要調用gateA20開啓A20門(A20地址線對應2^20 = 1MB)
   */

#ifndef GRUB_UTIL
	debug = 1;
	printf("Turning on gate A20... ");
	/*
	 * gateA20在asm.S中定義
	 * 訪問大於1M的內存時需要啓用A20門.
	 * 當輸入參數linear=0時關閉A20,爲1時開啓。
	 * 返回值:	0代表失敗,1代表成功。
	 */
	if (gateA20(1))			/* int15/24 -----safe enough */
		printf("Success.\n");
	else
		printf("Failure!\n");
#endif

	/* 保存拓展內存大小的值到EXTENDED_MEMORY, in order to tell it to non-Multiboot OSes.  */
	extended_memory = mbi.mem_upper;

	/*
	 * “ mbi.mem_upper”變量僅識別第一個存儲區域中的高端存儲。
	 * 如果有多個內存區域,則其餘區域將報告給兼容Multiboot的OS,否則GRUB不會使用。
	 */
	addr = get_code_end(); /* get_code_end()在asm.S中定義,返回代碼結尾的地址(也是BSS段的結尾)。*/
	/*addr >>> |BSS|addr*/
	mbi.mmap_addr = addr;  /* mbi.mmap_addr是內存描述符Entry列表的開始位置*/
	mbi.mmap_length = 0;   /* mbi.mmap_length是內存描述符Entry列表的總長度 */
	cont = 0;

	do
	{
		/* get_mmap_entry: 見asm.S,使用int15/e820獲取從內存地址cont開始的地址範圍描述符Entry,
		並將其寫入到addr指向的內存。*/
		cont = get_mmap_entry((void *)addr, cont);

		/* addr的頭四個字節爲長度,如果該值爲0則表示全部讀取完或者是發生錯誤,退出。 */
		if (!*((unsigned long *)addr))
			break;

		/* 循環讀取依次累加直到讀取完畢 */
		mbi.mmap_length += *((unsigned long *)addr) + 4;
		addr += *((unsigned long *)addr) + 4;
	} while (cont);
	/*addr >>> |BSS|N個內存描述符|addr */
	if (mbi.mmap_length)
	{
		unsigned long long max_addr;

		/*
		 *  獲取"lower memory"和"upper memory" (直到第一個內存空洞)
		 *  然後將它們賦予"mbi.mem_{lower,upper}"。
		 *  這兩個值用於那些不關心內存地址映射但是可能關心可用內存總數的操作系統
		 */
		mbi.mem_lower = mmap_avail_at(0) >> 10; /*從0x0000開始的可用KB數*/
		mbi.mem_upper = mmap_avail_at(0x100000) >> 10; /* 從1MB(0x100000 = 1M)開始的可用KB數 */

		/* 找到最大可用的地址,忽略任何內存孔洞。 */
		for (max_addr = 0, addr = mbi.mmap_addr;
			addr < mbi.mmap_addr + mbi.mmap_length;
			addr += *((unsigned long *)addr) + 4)
		{
			struct AddrRangeDesc *desc = (struct AddrRangeDesc *) addr;

			if (desc->Type == MB_ARD_MEMORY && desc->Length > 0
				&& desc->BaseAddr + desc->Length > max_addr)
				max_addr = desc->BaseAddr + desc->Length;
		}

		extended_memory = (max_addr - 0x100000) >> 10;
	}
	else if ((memtmp = get_eisamemsize()) != -1)		
	{
		/* get_mmap_entry使用int15/e820獲取內存描述失敗,改爲使用int15/e801得到EISA內存可用地址。
		 返回值的低16位是在1M和16M之間的內存數量(以1K爲單位), 高16位是大於16M的數量(以64K爲單位)*/
		cont = memtmp & ~0xFFFF; // 高16位
		memtmp = memtmp & 0xFFFF; // 低16位

		if (cont != 0) /*>16MB的地址*/
			extended_memory = (cont >> 10) + 0x3c00; /*這15KB是什麼?*/
		else
			extended_memory = memtmp;

		if (!cont || (memtmp == 0x3c00))
			memtmp += (cont >> 10);
		else
		{
			/* XXX should I do this at all ??? */

			mbi.mmap_addr = (unsigned long)fakemap;
			mbi.mmap_length = sizeof(fakemap);
			fakemap[0].Length = (mbi.mem_lower << 10);
			fakemap[1].Length = (memtmp << 10);
			fakemap[2].Length = cont;
		}

		mbi.mem_upper = memtmp;
	}

	saved_mem_upper = mbi.mem_upper;

	/* 獲取驅動器信息 */
	/* FIXME: This should be postponed until a Multiboot kernel actually
	   requires it, because this could slow down the start-up
	   unreasonably.  */
	mbi.drives_length = 0;
	mbi.drives_addr = addr;

	/* 目前,GRUB不會探測軟盤,因爲將軟盤驅動器映射到BIOS驅動器很簡單。 */
#ifdef GRUB_UTIL
#define FIND_DRIVES 8
#else
#define FIND_DRIVES (*((char *)0x475)) /*注:BIOS在0x475的地方存放了最多支持多少個磁盤*/
#endif
	if (debug)
		grub_printf("hard drives: %d, int13: %X, int15: %X\n", FIND_DRIVES, *(unsigned long *)0x4C, *(unsigned long *)0x54);
	//#ifndef GRUB_UTIL
	//  FIND_DRIVES = 1;
	//#endif
	for (drive = 0x80; drive < 0x80 + FIND_DRIVES; drive++)
		//  for (drive = 0x80 + FIND_DRIVES - 1; drive >= 0x80; drive--)
	{
#undef FIND_DRIVES
		//      struct geometry tmp_geom;
		struct drive_info *info = (struct drive_info *) addr;
		unsigned short *port;

		/* 獲取磁盤佈局以確認其存在*/
		if (debug)
			grub_printf("get_diskinfo(%X), ", drive);
		/* 讀取硬盤參數,內部使用了INT13/08(標準模式)或INT13/4800(拓展模式)*/
		if (get_diskinfo(drive, &tmp_geom)) 
			break;
		if (debug)
			grub_printf(" %sC/H/S=%d/%d/%d, Sector Count/Size=%d/%d\n",
			(tmp_geom.flags & BIOSDISK_FLAG_LBA_EXTENSION) ? "LBA, " : "",
				tmp_geom.cylinders, tmp_geom.heads, tmp_geom.sectors,
				tmp_geom.total_sectors, tmp_geom.sector_size);

		/* 清空I/O map. */
		grub_memset((char *)io_map, 0, IO_MAP_SIZE * sizeof(unsigned short));

		/* Disable to probe I/O ports temporarily, because this doesn't
	   work with some BIOSes (maybe they are too buggy).  */
#if 0
	   /* Track the int13 handler.  */
		track_int13(drive);
#endif

		/* 保存這個磁盤的佈局信息到addr指向的位置.  */
		/* addr >>> |BSS|N個內存描述符(可選)|N個硬盤信息drive_info|addr */
		info->drive_number = drive;
		info->drive_mode = ((tmp_geom.flags & BIOSDISK_FLAG_LBA_EXTENSION)
			? MB_DI_LBA_MODE : MB_DI_CHS_MODE);
		info->drive_cylinders = tmp_geom.cylinders;
		info->drive_heads = tmp_geom.heads;
		info->drive_sectors = tmp_geom.sectors;

		addr += sizeof(struct drive_info);
		for (port = io_map; *port; port++, addr += sizeof(unsigned short))
			*((unsigned short *)addr) = *port;

		info->size = addr - (unsigned long)info;
		mbi.drives_length += info->size;
	}
	

	/* 設置boot loader的名字爲GNU GRUB x.x. */
	mbi.boot_loader_name = (unsigned long) "GNU GRUB " VERSION;

	/*
	 *  初始化其他Multiboot Info的標誌位.
	 */

	mbi.flags = (MB_INFO_MEMORY | MB_INFO_CMDLINE | MB_INFO_BOOTDEV
		| MB_INFO_DRIVE_INFO | MB_INFO_CONFIG_TABLE
		| MB_INFO_BOOT_LOADER_NAME);

#if 0

	/* Only multi_boot need this. So we should not initialize it here.
	 * Moved to boot_func in builtins.c
	 */

	 /* Get the ROM configuration table by INT 15, AH=C0h.  */
	mbi.config_table = get_rom_config_table();

	/* Get the APM BIOS table.  */
	get_apm_info();
	if (apm_bios_info.version)
		mbi.apm_table = (unsigned long)&apm_bios_info;

	if (apm_bios_info.version)
		mbi.flags |= MB_INFO_APM_TABLE;

#endif

#endif /* STAGE1_5 */

#if !defined(STAGE1_5) && !defined(GRUB_UTIL)
	/* 下面這段代碼設置CDROM驅動器,如果force_cdrom_as_boot_device被設置了,我們將強制從cdrom啓動。 */

  //#if 0
  //    /* Get the geometry.  */
  //    if (get_diskinfo (boot_drive, &tmp_geom)
  //	|| ! (geom.flags & BIOSDISK_FLAG_CDROM))
  //      cdrom_drive = GRUB_INVALID_DRIVE;
  //    else
  //      cdrom_drive = boot_drive;
  //#else
	 
	if (debug)
		printf("boot drive=%X, ", boot_drive);
	/* 獲取佈局,boot_drive在asm.S中定義和設置,是BIOS設置的啓動設備ID */
	cdrom_drive = get_cdinfo(boot_drive, &tmp_geom);
	if (!cdrom_drive || cdrom_drive != boot_drive)
		cdrom_drive = GRUB_INVALID_DRIVE;
	if (debug)
		printf("%s\n", cdrom_drive == GRUB_INVALID_DRIVE ? "Not CD" : "Is CD");
	//#endif
#endif

#if !defined(STAGE1_5) && !defined(GRUB_UTIL)
	/* boot_drive不是cdrom或者查詢boot_drive的信息失敗了,下面遍歷ID=7F 以及88~FF的設備查找cdrom */
	if (cdrom_drive == GRUB_INVALID_DRIVE)
	{
		int err;
		int version;

		/*CDRom的信息放在內存0x600的地方*/
		struct drive_parameters *drp = (struct drive_parameters *)0x600;

#ifdef GRUB_UTIL
#define FIND_DRIVES 8
#else
#define FIND_DRIVES (*((char *)0x475))
#endif
		for (drive = 0x7F; drive < 0xff; drive++)
		{
			//    struct geometry tmp_geom;
			/* 這幾個值已經在上面調用get_diskinfo的時候處理過了所以我們跳過它們。 */
			if (drive >= 0x80 && drive < 0x80 + FIND_DRIVES)
				continue;

			/* Get the geometry.  */
			if (debug)
				grub_printf("\rget_cdinfo(%X),", drive);
			cdrom_drive = get_cdinfo(drive, &tmp_geom);
			//    if (debug)
			//	grub_printf ("cdrom_drive=%X", cdrom_drive);
			if (cdrom_drive)
				break;

			cdrom_drive = GRUB_INVALID_DRIVE;

			/* Some buggy BIOSes will hang at EBIOS `Get Drive Parameters' call
			 * (INT 13h function 48h). So we only do further checks for Bochs.
			 */

			if (bios_id != 1)		/* if it is not Bochs ... */
				continue;		/* ... skip and try next drive. */

				  /* When qemu has a cdrom attached but not booted from cdrom, its
				   * `get bootable cdrom status call', the int13/ax=4B01, returns CF=0
				   * but with a wrong `Bootable CD-ROM Specification Packet' as follows:
				   *
				   * The first byte(packet size) is 0x13, all the rest bytes are 0's.
				   *
				   * So we need to call get_diskinfo() here as a workaround.
				   *
				   *		(The bug was reported by Jacopo Lazzari. Thanks!)
				   */

			if (drive >= 0x80)
			{
				//printf("check drive: 0x%X\t\t", drive);

				//printf (" check_int13_extensions (0x%X) ...", drive);
				/* 檢查這個驅動器是否支持int13拓展指令,不支持則繼續找下一個。 */
				version = check_int13_extensions(drive);
				//printf (" OK! version=0x%X\n", version);

				if (!(version & 1)) /* not support functions 42h-44h, 47h-48h */
					continue;	/* failure, try next drive. */

				  /* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand
					 the greater buffer size for the "get drive parameters" int
					 0x13 call in its own way.  Supposedly the BIOS assumes even
					 bigger space is available and thus corrupts the stack.
					 This is why we specify the exactly necessary size of 0x42
					 bytes. */

					 /* It is safe to clear out DRP.  */
				grub_memset(drp, 0, sizeof(struct drive_parameters));

				drp->size = sizeof(struct drive_parameters) - 16;

				//printf (" biosdisk_int13_extensions (0x4800, 0x%X) ...", drive);
				/* 取驅動器的信息保存到drp變量(drive_parameters) */
				err = biosdisk_int13_extensions(0x4800, drive, drp);
				//printf (" OK! err=0x%X\n", err);
				if (!err && drp->bytes_per_sector == ISO_SECTOR_SIZE)
				{
					/* 假設它是CDROM,完成搜索 */
					cdrom_drive = drive;
					break;
				}
				//printf("not cdrom\r", drive);
			} /* if (drive >= 0x80) */

		} /* for (drive = 0x7F; drive < 0xff; drive++) */
	} /* if (cdrom_drive == GRUB_INVALID_DRIVE) */
#undef FIND_DRIVES

	if (debug)
		grub_printf(" cdrom_drive == %X.\n", cdrom_drive);
#endif /* ! STAGE1_5 && ! GRUB_UTIL */

	/* check if the no-emulation-mode bootable cdrom exists. */

	/* 如果cdrom存在,且force_cdrom_as_boot_device標誌被設置,則強制這個cdrom爲啓動設備
	(通過改寫asm.S中的boot_drive全局變量)。*/
	if (saved_entryno == 0 && force_cdrom_as_boot_device)
		force_cdrom_as_boot_device = 0;
	if (cdrom_drive != GRUB_INVALID_DRIVE && force_cdrom_as_boot_device)
	{
		boot_drive = cdrom_drive;	/* force it to be the boot drive */
	}

	if (boot_drive == cdrom_drive)
		/* 強制它是一個沒有分區表的"whole drive" */
		install_partition = 0xFFFFFF;

	/* 設置啓動設備和啓動分區(注install_partition在asm.S中定義,這個值一般在grub安裝MBR時一併寫入) */
	saved_drive = boot_drive;
	saved_partition = install_partition;

#ifndef GRUB_UTIL
#ifndef STAGE1_5
	debug = 0;
#if 0
	if (cdrom_drive == GRUB_INVALID_DRIVE)
	{
		min_cdrom_id = 0xE0;
	}
	else {
		min_cdrom_id = cdrom_drive <= 0xE0 ? cdrom_drive + 1 : cdrom_drive - 0x20;
	}
#else
	min_cdrom_id = (cdrom_drive < 0xE0 && cdrom_drive >= 0xC0) ? 0xE0 : 0xC0;
	//min_cdrom_id = 0xC0 | ((cdrom_drive < 0xE0) << 5);
#endif

  /* 如果grub.exe作爲Linux內核引導則檢查initrd磁盤,我們只拿來引導Windows系統,所以下面這段我們不關心。 */
  /* the real mode zero page(only the beginning 2 sectors, the boot params) is loaded at 0xA00 */
  /* check the header signature "HdrS" (0x53726448) */

	ram_drive = 0x7f;	/* the default ram_drive is a floppy. */
	if (*(unsigned long*)(int*)(0xA00 + 0x202) == 0x53726448)
	{
		unsigned long initrd_addr;
		unsigned long initrd_size;

		initrd_addr = *(unsigned long*)(int*)(0xA00 + 0x218);
		initrd_size = *(unsigned long*)(int*)(0xA00 + 0x21c);
		if (initrd_addr && initrd_size)
		{
			rd_base = initrd_addr;
			rd_size = initrd_size;

			/* 檢查是否有分區表(AA55是MBR中分區表的結尾標誌) */
			if (*(unsigned short *)(initrd_addr + 0x40) == 0xAA55)
			{
#if 0
				initrd_addr += 0x1BE;	/* here begins the partition table */
				initrd_size = 0;	/* count valid partition entries */

				for (i = 0; i < 4; i++)
				{
					if ((unsigned char)(*(char*)(initrd_addr + i * 16) & 0x7F))
						break;
					if ((*(long long *)(initrd_addr + i * 16)) | (*(long long *)(initrd_addr + i * 16 + 8)))
					{
						if (!(*(long *)(initrd_addr + i * 16 + 8)) || !(*(long long *)(initrd_addr + i * 16 + 12)))
							break;
						if (!(*(char *)(initrd_addr + i * 16 + 2)) & 0x3f)
							break;
						if (!(*(char *)(initrd_addr + i * 16 + 6)) & 0x3f)
							break;
						initrd_size++;
					}
				}

				if (i == 4 && initrd_size)	/* partition table is valid. */
					ram_drive = 0xfe;		/* let it be a harddrive */
#else
				if (!probe_mbr((struct master_and_dos_boot_sector *)initrd_addr, 0, initrd_size, 0))
					ram_drive = 0xfe;	/* partition table is valid, so let it be a harddrive */
				else
					grub_printf("\nUnrecognized partition table for RAM DRIVE; assuming floppy. Please rebuild\nit using a Microsoft-compatible FDISK tool, if the INITRD is a hard-disk image.\n");
#endif
		}
	}
  }
#endif /* ! STAGE1_5 */
#endif /* ! GRUB_UTIL */

	/* 開始CMAIN函數 */

	grub_printf("Starting cmain() ... ");

	cmain();
}

common.c的其他函數:


/* static for BIOS memory map fakery */
static struct AddrRangeDesc fakemap[3] =
{
  {20, 0, 0, MB_ARD_MEMORY},
  {20, 0x100000, 0, MB_ARD_MEMORY},
  {20, 0x1000000, 0, MB_ARD_MEMORY}
};

/*
取從參數bottom開始的可用內存數量。
有個大問題,內存區域並不保證:
   (1) 連續可用
   (2) 以升序排列
   (3) 不重疊.
   因此這是不成熟的.  
   */
static unsigned long
mmap_avail_at(unsigned long bottom)
{
	unsigned long long top;
	unsigned long addr;
	int cont;

	top = bottom;
	do
	{
		for (cont = 0, addr = mbi.mmap_addr;
			addr < mbi.mmap_addr + mbi.mmap_length;
			addr += *((unsigned long *)addr) + 4)
		{
			struct AddrRangeDesc *desc = (struct AddrRangeDesc *) addr;

			if (desc->Type == MB_ARD_MEMORY
				&& desc->BaseAddr <= top
				&& desc->BaseAddr + desc->Length > top)
			{
				top = desc->BaseAddr + desc->Length;
				cont++;
			}
		}
	} while (cont);

	/* For now, GRUB assumes 32bits addresses, so...  */
	if (top > 0xFFFFFFFF)
		top = 0xFFFFFFFF;

	return (unsigned long)top - bottom;
}

common.c中調用了部分asm.s中的函數,主要如下:


#ifndef STAGE1_5
/* get_code_end() :返回代碼結尾的地址(也是BSS段的結尾)
 * 返回$end(或$_end)標號所在的地址 + 2。這個標號在編譯時由編譯腳本添加。
 * This is here so that it can be replaced by asmstub.c.
 */
ENTRY(get_code_end)
	/* will be the end of the bss */
# if defined(HAVE_END_SYMBOL)
	movl	$end, %eax
# elif defined(HAVE_USCORE_END_SYMBOL)
	movl	$_end, %eax
# endif
	shrl	$2, %eax		/* Round up to the next word. */
	incl	%eax
	shll	$2, %eax
	ret
#endif /* ! STAGE1_5 */

/*
 *
 * get_memsize(i) :  返回內存大小,以KB爲單位. 
 * 輸入參數i == 0時返回常規內存大小,爲1時返回拓展內存大小
 * 調用BIOS "INT 12H" 獲取常規內存大小
 * 調用BIOS "INT 15H, AH=88H"獲取拓展內存大小
 * 返回值存放在AX。
 */

ENTRY(get_memsize)
	pushl	%ebp
	pushl	%ebx

	movl	0xc(%esp), %ebx

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	//sti		/* it is not bad keeping interrupt off */

	cmpb	$0x1, %bl
	//DATA32	je	xext
	je	1f

	int	$0x12
	//DATA32	jmp	xdone
	jmp	2f

//xext:
1:
	movb	$0x88, %ah
	int	$0x15

//xdone:
2:
	movw	%ax, %bx

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movw	%bx, %ax
	popl	%ebx
	popl	%ebp
	ret


#ifndef STAGE1_5

/*
 *
 * get_eisamemsize() : 使用INT15/0xe801獲取EISA內存大小, 返回值的低16位是
 *		在1M和16M之間的內存數量(以1K爲單位), 高16位是大於16M的數量(以64K爲單位)
 *		如果發生錯誤則返回-1。
 *	BIOS call "INT 15H, AH=E801H" to get EISA memory map,
 *		AX = memory between 1M and 16M in 1K parts.
 *		BX = memory above 16M in 64K parts.
 *      CX = Configured 1 Number of contiguous KB between 1 and 16 MB, maximum 0x3C00 = 15 MB.
 *      DX = Configured 2 Number of contiguous 64 KB blocks between 16 MB and 4 GB.
 */

ENTRY(get_eisamemsize)
	pushl	%ebp
	pushl	%ebx
	pushl	%ecx
	pushl	%edx

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	//sti		/* it is not bad keeping interrupt off */

	movw	$0xe801, %ax
	int	$0x15
	jc	1f
	testb	$0x80, %ah
	jnz	1f

	shll	$16, %ebx  /*返回值的高16位*/
	movw	%ax, %bx   /*返回值的低16位*/
	testl	%ebx, %ebx
	jnz	3f
	movw	%cx, %ax   /*如果AX,BX返回值爲空,則使用CX,DX的返回值(Configured 1/Configured 2)*/
	movw	%dx, %bx
	shll	$16, %ebx
	movw	%ax, %bx
3:
	jmp	2f
1:
	movl	$0xFFFFFFFF, %ebx /* 發生錯誤,返回-1。 */
2:
	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movl	%ebx, %eax

	popl	%edx
	popl	%ecx
	popl	%ebx
	popl	%ebp
	ret

/*
 *
 * get_mmap_entry(addr, cont) :  
 * addr和cont參數:調用INT15/E820(Query System Address Map) BIOS call獲取地址範圍描述符Entry時,
 * Entry的寫入地址和後續值(continuation value,第一次調用爲0)。
 *
 * 返回:設置輸入參數"addr"內存區域的頭四個字節爲這個call返回的大小,如果這個調用失敗了則設置它爲0
 * ,並返回一個新的非零的後續值, 0則表示完成.
 * NOTE: Currently hard-coded for a maximum buffer length of 1024.
 *
 * 在啓動分頁機制之前,我們要設置頁目錄和頁表信息,理論上,我們利用一頁內存(4k)來存放頁目錄,
 * 用1k頁(4M)來存放頁表,可以表示4G的內存。但是我們的內存不一定就是固定4G的,可能是1G,512MB
 * 或者更小,而且除了要知道內存容量的大小,我們更想知道各段內存地址的type,因爲編程時,屬性爲
 * reserved的段不能被程序分配使用。基於以上問題,我們在啓動分頁機制之前,利用0E820h int 15h 
 * 中斷獲取內存信息,以便進行有效合理的設置。
 * 調用中斷int 15h 之前,需要填充如下寄存器:
 *·eax  int 15h 可以完成許多工作,主要有ax的值決定,我們想要獲取內存信息,需要將ax賦值爲0E820H。
 *·ebx  放置着“後續值(continuation value)”,第一次調用時ebx必須爲0.
 *·es:di  指向一個地址範圍描述結構 ARDS(Address Range Descriptor Structure), BIOS將會填充此結構。
 *·ecx es:di所指向的地址範圍描述結構的大小,以字節爲單位。無論es:di所指向的結構如何設置,BIOS
 * 最多將會填充ecx字節。不過,通常情況下無論ecx爲多大,BIOS只填充20字節,有些BIOS忽略ecx的值,總
 * 是填充20字節。
 * ·edx 0534D4150h('SMAP')——BIOS將會使用此標誌,對調用者將要請求的系統映像信息進行校驗,
 * 這些信息被BIOS放置到es:di所指向的結構中。
 *
 * 中斷調用之後,結果存放於下列寄存器之中。
 *·CF  CF=0表示沒有錯誤,否則存在錯誤。
 *·eax   0534D4150h('SMAP')
 *·es:di  返回的地址範圍描述符結構指針,和輸入值相同。
 *·ecx BIOS填充在地址範圍描述符中的字節數量,被BIOS所返回的最小值是20字節。
 *·ebx 這裏放置着爲等到下一個地址描述符所需要的後續值,這個值得實際形勢依賴於具體的BIOS的實現,調用者不必關心它的具體形式,自需在下一次迭代時將其原封不動地放置到ebx中,就可以通過它獲取下一個地址範圍描述符。如果它的值爲0,並且CF沒有進位,表示它是最後一個地址範圍描述符。

 */

ENTRY(get_mmap_entry)
	pushl	%ebp
	pushl	%ebx
	pushl	%edi
	pushl	%esi

	/* place address (+4) in ES:DI */
	movl	0x14(%esp), %eax
	addl	$4, %eax
	movl	%eax, %edi
	andl	$0xf, %edi
	shrl	$4, %eax
	movl	%eax, %esi

	/* set continuation value */
	movl	0x18(%esp), %ebx

	/*...省略*/
	ret

		
/*
 * int gateA20(int linear)
 *
 * 訪問大於1M的內存時需要啓用A20門.
 * 當輸入參數linear=0時關閉A20,爲1時開啓。
 * 返回值:	0代表失敗,1代表成功。
 * This routine is probably overconservative in what it does, but so what?
 * It also eats any keystrokes in the keyboard buffer.  :-(
 */

ENTRY(gateA20)
	pushl	%ebp
	movl	8(%esp), %edx		/* the value of `linear' */
    /*省略*/
    ret

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章