GRUB4DOS(七) chainloader命令源碼註釋

chainloader命令的使用方法:

"chainloader [--force] [--load-segment=LS] [--load-offset=LO]

[--load-length=LL] [--skip-length=SL] [--boot-cs=CS] [--boot-ip=IP]

[--ebx=EBX] [--edx=EDX] [--sdi] [--disable-a20] [--pcdos] [--msdos] FILE"

說明:

加載鏈式加載器 FILE。

  1. 若指定了 --force, 則強制加載而不檢查第一扇區尾部的合法引導標籤(55 AA)是否存在。
  2. LS:LO 用於指定有別於 0000:7C00的啓動映像加載地址。
  3. LL 用於指定啓動映像的長度(在 512 字節至 640K之間)。
  4. SL 用於指定裝入啓動映像之前需要從映像開頭跳過的長度(以字節計數)。
  5. CS:IP 用於指定讓啓動映像獲得控制需要跳轉到的地址。
  6. EBX/EDX分別用於指定在啓動映像獲得控制的那一刻 EBX/EDX 寄存器應有的值。
  7. 用--sdi 可以強制把 FILE 當作 Windows XP 的一個內存啓動文件格式(System Deployment Image)來對待(請參考微軟相應文檔)。
  8. 如果你希望在控制傳遞給啓動映像時切斷 CPU 的地址線 A20,可以用 --disable-a20 參數做到。

 

/* chainloader 
 * arg: 命令的參數, flags: 運行位置(MENU/SCRIPT等等)
 * 根據參數設置當前引導設備。
 * 打開並讀取arg中的文件(或設備)經過合適處理後將引導代碼載入內存,
 * 並根據引導代碼的類型爲引導代碼設置運行環境,如load_segment,chainloader_edx等等。
 */
static int
chainloader_func(char *arg, int flags)
{
	char *p;

	int force = 0;
	char *filename;

	int is_pcdos = 0;
	int is_msdos = 0;
	int is_drmk = 0;
	int is_romdos = 0;
	int is_drdos = 0;

	chainloader_load_segment_orig = chainloader_load_segment;
	chainloader_load_offset_orig = chainloader_load_offset;
	chainloader_load_length_orig = chainloader_load_length;
	chainloader_skip_length_orig = chainloader_skip_length;
	chainloader_boot_CS_orig = chainloader_boot_CS;
	chainloader_boot_IP_orig = chainloader_boot_IP;
	chainloader_ebx_orig = chainloader_ebx;
	chainloader_ebx_set_orig = chainloader_ebx_set;
	chainloader_edx_orig = chainloader_edx;
	chainloader_edx_set_orig = chainloader_edx_set;
	chainloader_bx_orig = chainloader_bx;
	chainloader_bx_set_orig = chainloader_bx_set;
	chainloader_cx_orig = chainloader_cx;
	chainloader_cx_set_orig = chainloader_cx_set;
	chainloader_disable_A20_orig = chainloader_disable_A20;
	is_sdi_orig = is_sdi;
	is_raw_orig = is_raw;
	is_isolinux_orig = is_isolinux;
	is_grldr_orig = is_grldr;
	is_io_orig = is_io;
	kernel_type_orig = kernel_type;
	grub_memmove((char *)chainloader_file_orig, (char *)chainloader_file, sizeof(chainloader_file));

	chainloader_load_segment = -1;//0x0000;
	chainloader_load_offset = -1;//0x7c00;
	chainloader_load_length = -1;//0x200;
	chainloader_skip_length = 0;
	chainloader_boot_CS = -1;//0x0000;
	chainloader_boot_IP = -1;//0x7c00;
	chainloader_ebx = 0;
	chainloader_ebx_set = 0;
	chainloader_edx = 0;
	chainloader_edx_set = 0;
	chainloader_bx = 0;
	chainloader_bx_set = 0;
	chainloader_cx = 0;
	chainloader_cx_set = 0;
	chainloader_disable_A20 = 0;
	is_sdi = is_raw = 0;
	is_isolinux = 0;
	is_grldr = 0;
	is_io = 0;
	kernel_type = KERNEL_TYPE_CHAINLOADER;

	errnum = 0;
	/* 解析命令行,獲取各參數的值 */
	for (;;)
	{
		if (grub_memcmp(arg, "--force", 7) == 0)
		{
			force = 1;
		}
		else if (grub_memcmp(arg, "--pcdos", 7) == 0)
		{
			is_pcdos = 1;
		}
		else if (grub_memcmp(arg, "--msdos", 7) == 0)
		{
			is_msdos = 1;
		}
		else if (grub_memcmp(arg, "--load-segment=", 15) == 0)
		{
			unsigned long long ull;
			p = arg + 15;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			if (ull >= 0xA000)
			{
				errnum = ERR_INVALID_LOAD_SEGMENT;
				goto failure;
			}
			chainloader_load_segment = ull;
		}
		else if (grub_memcmp(arg, "--load-offset=", 14) == 0)
		{
			unsigned long long ull;
			p = arg + 14;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			if (ull > 0xF800)
			{
				errnum = ERR_INVALID_LOAD_OFFSET;
				goto failure;
			}
			chainloader_load_offset = ull;
		}
		else if (grub_memcmp(arg, "--load-length=", 14) == 0)
		{
			unsigned long long ull;
			p = arg + 14;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			if (ull < 512 || ull > 0xA0000)
			{
				errnum = ERR_INVALID_LOAD_LENGTH;
				goto failure;
			}
			chainloader_load_length = ull;
		}
		else if (grub_memcmp(arg, "--skip-length=", 14) == 0)
		{
			unsigned long long ull;
			p = arg + 14;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			//if (chainloader_skip_length < 0)
			//{
			//	errnum = ERR_INVALID_SKIP_LENGTH;
			//	goto failure;
			//}
			chainloader_skip_length = ull;
		}
		else if (grub_memcmp(arg, "--boot-cs=", 10) == 0)
		{
			unsigned long long ull;
			p = arg + 10;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			if (ull > 0xffff)
			{
				errnum = ERR_INVALID_BOOT_CS;
				goto failure;
			}
			chainloader_boot_CS = ull;
		}
		else if (grub_memcmp(arg, "--boot-ip=", 10) == 0)
		{
			unsigned long long ull;
			p = arg + 10;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			if (ull > 0xffff)
			{
				errnum = ERR_INVALID_BOOT_IP;
				goto failure;
			}
			chainloader_boot_IP = ull;
		}
		else if (grub_memcmp(arg, "--ebx=", 6) == 0)
		{
			unsigned long long ull;
			p = arg + 6;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			chainloader_ebx = ull;
			chainloader_ebx_set = 1;
		}
		else if (grub_memcmp(arg, "--edx=", 6) == 0)
		{
			unsigned long long ull;
			p = arg + 6;
			if (!safe_parse_maxint(&p, &ull))
				goto failure;
			chainloader_edx = ull;
			chainloader_edx_set = 1;
		}
		else if (grub_memcmp(arg, "--sdi", 5) == 0)
		{
			is_sdi = 1;
		}
		else if (grub_memcmp(arg, "--raw", 5) == 0)
		{
			is_raw = 1;
		}
		else if (grub_memcmp(arg, "--disable-a20", 13) == 0)
		{
			chainloader_disable_A20 = 1;
		}
		else
			break;
		arg = skip_to(0, arg);
	}

	if (grub_strlen(saved_dir) + grub_strlen(arg) + 20 >= sizeof(chainloader_file))
	{
		/* 錯誤,需要加載的文件路徑過長,
		Selected item cannot fit into memory */
		errnum = ERR_WONT_FIT;
		goto failure;
	}

	if (*arg == '/')
	{
		// 以‘/’開頭,表示這是一個相對目錄,例如arg="chainloader /BOOT/BOOT10PE"。根據分區ID的格式:
		// - 格式0 0x00FFFFFF,則嘗試忽略分區ID。格式"(%d)%s%s", saved_drive, saved_dir, arg
		// - 格式1 0xXXXXFFXX(16 - 31位 = 0xFF),格式"(%d,%d)%s%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), saved_dir, arg
		// - 格式2 0xXXFFXXXX(32 - 48位 = 0xFF),格式"(%d,%c)%s%s", saved_drive, (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), saved_dir, arg
		// - 格式3 以上值之外,"(%d,%d,%c)%s%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (unsigned long)(unsigned char)((saved_partition >> 8) + 'a', saved_dir, ar
		if (saved_partition != 0xFFFFFF)
		{
			if ((saved_partition & 0xFF00) == 0xFF00) // 
				grub_sprintf(chainloader_file, "(%d,%d)%s%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), saved_dir, arg);
			else
				if ((saved_partition & 0xFF0000) == 0xFF0000)
					grub_sprintf(chainloader_file, "(%d,%c)%s%s", saved_drive, (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), saved_dir, arg);
				else
					grub_sprintf(chainloader_file, "(%d,%d,%c)%s%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), saved_dir, arg);
		}
		else
			grub_sprintf(chainloader_file, "(%d)%s%s", saved_drive, saved_dir, arg);
	}
	else if (*arg == '(' && arg[1] == ')')
	{
		// 文件路徑以'()'一對空的括號開頭,()表示當前的root。
		// - 格式0 0x00FFFFFF,則忽略分區ID和“--find-set=”設置的路徑。格式"(%d)%s", saved_drive, (arg + 2)
		// - 格式1 0xXXXXFFXX(16 - 31位 = 0xFF),格式"(%d,%d)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (arg + 2)
		// - 格式2 0xXXFFXXXX(32 - 48位 = 0xFF),格式"(%d,%c)%s", saved_drive, (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), (arg + 2)
		// - 格式3 以上值之外,格式 "(%d,%d,%c)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), (arg + 2)
		if (saved_partition != 0xFFFFFF)
		{
			if ((saved_partition & 0xFF00) == 0xFF00)
				grub_sprintf(chainloader_file, "(%d,%d)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (arg + 2));
			else
				if ((saved_partition & 0xFF0000) == 0xFF0000)
					grub_sprintf(chainloader_file, "(%d,%c)%s", saved_drive, (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), (arg + 2));
				else
					grub_sprintf(chainloader_file, "(%d,%d,%c)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), (arg + 2));
		}
		else
			grub_sprintf(chainloader_file, "(%d)%s", saved_drive, (arg + 2));
	}
	else if (*arg != '(')
	{
		// 不以'(','/'開頭得文件路徑
		// - 格式0 0x00FFFFFF,則忽略分區ID和“--find-set=”設置的路徑。格式"(%d)%s", saved_drive, arg
		// - 格式1 0xXXXXFFXX(16 - 31位 = 0xFF),格式"(%d,%d)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), arg
		// - 格式2 0xXXFFXXXX(32 - 48位 = 0xFF),格式"(%d,%c)%s", saved_drive, (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), arg
		// - 格式3 以上值之外,格式 "(%d,%d,%c)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), arg
		if (saved_partition != 0xFFFFFF)
		{
			if ((saved_partition & 0xFF00) == 0xFF00)
				grub_sprintf(chainloader_file, "(%d,%d)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), arg);
			else
				if ((saved_partition & 0xFF0000) == 0xFF0000)
					grub_sprintf(chainloader_file, "(%d,%c)%s", saved_drive, (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), arg);
				else
					grub_sprintf(chainloader_file, "(%d,%d,%c)%s", saved_drive, (unsigned long)(unsigned char)(saved_partition >> 16), (unsigned long)(unsigned char)((saved_partition >> 8) + 'a'), arg);
		}
		else
			grub_sprintf(chainloader_file, "(%d)%s", saved_drive, arg);
	}
	else
		/* 以'('開頭,但第二個字符不是')',例如“(fd0)+1” */
		grub_memmove(chainloader_file, arg, 255);

	chainloader_file[255] = 0;

	errnum = ERR_NONE;

	if ((is_sdi) || (is_raw))
		return 1;

	//  if (debug > 0)
	//	printf ("Debug: chainloader_func: set_device(%s) ...", arg);

	  /*
	   arg: "/BOOT/BOOT10PE" | "()BOOT/BOOT10PE" | "BOOT/BOOT10PE" | (fd0)+1
	   這個函數從解析字符串,然後根據其格式推斷出設備ID並將它們賦給
	   current_partition,current_drive等全局變量。返回值是從arg中移除設備名
	   之後的文件路徑。
	  */
	filename = set_device(arg);

	//  if (debug > 0)
	//	/* wipe out debug message. */
	//	printf ("\r                                                                             \r");

	// errnum在set_device()中設置
	if (errnum) {
		/* 未能解析出設備,默認採用root設備 */
		current_drive = saved_drive;
		//current_partition = saved_partition;
		filename = arg;
		errnum = 0;
	}

	if (filename == 0)
		filename = arg;

	/* 下面的代碼是chain_loader加載0xFFFF設備、RAM驅動器和UD設備中的引導時爲chainloader_edx賦值。
	 關於edx:在將控制權移交給引導扇區中的代碼前,我們需要設置DL爲引導磁盤的ID。
	 如0x80表示硬盤0,下面的current_drive在前邊的set_device函數調用中已經設置*/
	if (!chainloader_edx_set)
	{
#ifdef FSYS_FB
		if (current_drive == 0xFFFF || current_drive == ram_drive)
		{
			chainloader_edx = (saved_drive == FB_DRIVE ? (unsigned char)(fb_status >> 8) :
				saved_drive | ((saved_partition >> 8) & 0xFF00));
			chainloader_edx_set = 1;
		}
		else if (current_drive == FB_DRIVE)
		{
			chainloader_edx = (unsigned char)(fb_status >> 8);
			chainloader_edx_set = 1;
		}
#else
		if (current_drive == 0xFFFF || current_drive == ram_drive)
		{
			chainloader_edx = saved_drive | ((saved_partition >> 8) & 0xFF00);
			chainloader_edx_set = 1;
		}
#endif
	}

	/* 如果chain_loader沒有指定文件名或者磁盤的扇區號,那麼檢查可引導的cdrom
	   例如: "chainloader (cd0)" */
	if (*filename == 0 || *filename == ' ' || *filename == '\t')
	{
		//check_bootable_cdrom (current_drive);
		unsigned long i;
		unsigned long tmp;
		unsigned short tmp1;
		unsigned short tmp2;
		//	struct geometry tmp_geom;

		tmp = current_drive;
		/* check bootable type of drive (tmp) */

		/* 獲取當前驅動器的佈局信息以確保驅動器存在。當前驅動器的的值在set_device已設定 */
		if (get_diskinfo(tmp, &tmp_geom, 0))
		{
			errnum = ERR_NO_DISK;
			goto failure;
		}

		/* 打開驅動器 */
		grub_sprintf((char *)SCRATCHADDR, "(0x%X)+0x%lX", tmp, (unsigned long long)tmp_geom.total_sectors);
		if (!grub_open((char *)SCRATCHADDR))
			goto failure;

		/****************************************
		 * read the EL Torito Volume Descriptor *
		 * 光盤之所以能夠啓動是因爲它上面記錄的啓動信息符合ISO9660和El Torito標準
		 * https://www.cnblogs.com/zszmhd/p/3595787.html
		 *   section 0x00 | ...
			 ...
		   --section 0x17 | Boot Record Volume
		   |       ...
		   |->            | Booting Catalog(Initial/Default Entry)
		/****************************************/

		//filemax = 0x12 * 0x800;
		// 從這個扇區開始讀,filepos是全局變量用於修改文件讀寫位置,類似C中的fseek。
		filepos = 0x11 * 0x800;
		/* #define GRUB_READ 0xedde0d90 #define GRUB_WRITE 0x900ddeed*/
		/* 讀取驅動器的第17(0x11)扇區(ISO9660邏輯扇區大小爲2KB即0x800)的前512(0x200)字節到SCRATCHADDR(0x1F000),
		   這個扇區叫做Boot Record Volume
		 * Boot Record Volume Descriptor format
		 *
		 * Offset     Type	Description
		 * ------     ----	----------------------------------------------
		 *   0	      Byte	Boot Record Indicator, must be 0
		 *
		 *  1-5	   Characters	ISO-9660 Identifier, must be "CD001"
		 *
		 *   6	      Byte	Version of this descriptor, must be 1
		 *
		 *  7-26   Characters	Boot System Identifier, must be
		 *			"EL TORITO SPECIFICATION" padded with 0's.
		 *
		 * 27-46   Characters	Unused, must be 0.
		 *
		 * 47-4A     Dword	LBA of the first sector of Boot Catalog. <== 再這裏取Boot Catalog的開始扇區
		 *
		 * 4B-7FF  Characters	Unused, must be 0.
		 *
		 */

		if (grub_read((unsigned long long)SCRATCHADDR, 512, GRUB_READ) != 512)
			goto failure_exec_format;

		/* 檢查EL Torito卷描述 */
		if (memcmp((char *)SCRATCHADDR, "\0CD001\1EL TORITO SPECIFICATION\0", 31))
			goto failure_exec_format;

		/* 獲取引導目錄的起始扇區號(Boot Record Volume的第0x47開始的4個字節) */
		tmp = *(unsigned long *)((char *)SCRATCHADDR + 0x47);

		/********************************************/
		/* 讀引導目錄(Booting Catalog)的頭512字節到SCRATCHADDR緩衝區。*/
		/********************************************/
		filepos = tmp * 0x800;
		if (grub_read((unsigned long long)SCRATCHADDR, 512, GRUB_READ) != 512)
			goto failure_exec_format;

		/******************************
		* check the Validation Entry
		* 檢查前16個WORD,
		* 第一項必須是0x0001,最後一項必須是0xAA55,十六個WORD的總和必須爲0。
		* Validation Entry format
		*
		* Offset     Type	Description
		* ------     ----	----------------------------------------------
		*   0	      Byte	Header ID, must be 01
		*
		*   1	      Byte	Platform ID
		*				0 = 80x86
		*				1 = Power PC
		*				2 = Mac
		*
		*  2-3	      Word	Reserved, must be 0
		*
		*  4-1B   Characters	ID string. This is intended to identify the
		*			manufacturer/developer of the CD-ROM.
		*
		* 1C-1D      Word	Checksum Word. This sum of all the words in
		*			this record should be 0.
		*
		*  1E        Byte	Key byte, must be 55. This value is included
		*			in the checksum.
		*
		*  1F        Byte	Key byte, must be AA. This value is included
		*			in the checksum.
		*
		*******************************/

		tmp1 = 0;
		for (i = 0; i < 16; i++)
		{
			tmp = *(((unsigned short *)(char *)SCRATCHADDR) + i);
			tmp1 += tmp;
			if ((i == 0 && tmp != 1) || (i == 15 && tmp != 0xAA55))
				break;
		}

		if (tmp1 || i < 16)
			goto failure_exec_format;

		/* Specification Packet format
		 *
		 * Offset	Type	Description
		 * ------	----	----------------------------------------------
		 *	0	Byte	Packet Size, currently 13
		 *
		 *	1	Byte	Boot media type. This specifies what media the
		 *			boot image is intended to emulate in bits 0-3.
		 *			Bits 6-7 are specific to the type of system.
		 *
		 *			Bits 0-3 count as follows:
		 *
		 *				0:	No Emulation
		 *				1:	1.2  meg diskette
		 *				2:	1.44 meg diskette
		 *				3:	2.88 meg diskette
		 *				4:	Hard Disk (drive C)
		 *
		 *				5-F:	Reserved, invalid at this time
		 *
		 *			bits 4-5 - Reserved, must be 0
		 *			bit 6 - Image contains an ATAPI driver,
		 *				bytes 8 & 9 refer to IDE interface
		 *			bit 7 - Image contains SCSI drivers,
		 *				bytes 8 & 9 refer to SCSI interface
		 *
		 *	2	Byte	Drive Number. This is the drive number on
		 *			which emulation is being initiated or
		 *			terminated. This must be 0 for a floppy image,
		 *			80 for a bootable hard disk, and 81-FF for a
		 *			"non-bootable" or "no emulation" drive.
		 *
		 *	3	Byte	Controller Index. This specifies the controller
		 *			number of the specified CD drive.
		 *
		 *	4-7	Dword	Logical Block Address for the disk image to be
		 *			emulated.
		 *
		 *	8-9	Word	Device Specification. For SCSI controllers byte
		 *			8 is the LUN and PUN of the CD Drive, byte 9 is
		 *			the Bus number. For IDE controllers the low
		 *			order bit of byte 8 specifies master/slave
		 *			device, 0 = master.
		 *
		 *	A-B	Word	User Buffer Segment. If this field is non-zero
		 *			it specifies the segment of a user supplied 3k
		 *			buffer for caching CD reads. This buffer will
		 *			begin at Segment:0.
		 *
		 *	C-D	Word	Load Segment. This is the load address for the
		 *			initial boot image. If this value is 0, the
		 *			system will use the traditional segment of 7C0.
		 *			If this value is non-zero the system will use
		 *			the specified address. This field is only valid
		 *			for function 4C.
		 *
		 *	E-F	Word	Sector Count. This is the number of virtual
		 *			sectors the system will store at Load Segment
		 *			during the initial boot procedure. This field
		 *			is only valid for function 4C.
		 *
		 *	10	Byte	Bits 0-7 of the cylinder count. This should
		 *			match the value returned in CH when INT 13
		 *			function 08 is invoked.
		 *
		 *	11	Byte	This is the value returned in the CL register
		 *			when INT 13 function 08 is invoked. Bits 0-5
		 *			are the sector count. Bits 6 and 7 are the high
		 *			order 2 bits of the cylinder count.
		 *
		 *	12	Byte	This is the head count, it should match the
		 *			value in DH when INT 13 Function 08 is invoked.
		 *
		 */

		 /***********************************/
		 /* check the Initial/Default Entry */
		 /***********************************/
		 /* Initial/Default Entry format
		 *
		 * Offset	Type	Description
		 * ------	----	----------------------------------------------
		 *	0	Byte	Boot Indicator. 88=Bootable, 00=Not Bootable
		 *
		 *	1	Byte	Boot media type.
		 *			0 = No Emulation
		 *			1 = 1.2 meg diskette
		 *			2 = 1.44 meg diskette
		 *			3 = 2.88 meg diskette
		 *			4 = Hard Disk(drive 80)
		 *
		 *	2	Word	Load Segment. 0 for 7C0
		 *
		 *	4	Byte	System Type.
		 *
		 *	5	Byte	Unused, must be 0.
		 *
		 *	6	Word	Sector Count. This is the number of virtual/
		 *			emulated sectors the system will store at Load
		 *			Segment during the initial boot procedure.
		 *
		 *	8	DWord	加載RBA。 這是虛擬磁盤的起始地址。 CD使用相對/邏輯塊尋址。
		 *
		 *	C	Bytes	Unused, must be 0's.
		 *
		 */
		 /* XXX: assume Initial/Default Entry is always at offset 0x20 */

		tmp = *(unsigned char *)(SCRATCHADDR + 0x20);	/* Booting Catalog+0x20是Boot Indicator. 88=可引導, 00=不可引導 */
		if (tmp != 0x88)
			goto failure_exec_format;

		tmp = *((unsigned char *)SCRATCHADDR + 0x25);	/* Unused byte, must be 0. */
		if (tmp)
			goto failure_exec_format;

		/* Booting Catalog+0x22:引導代碼指定的加載位置(節),當這個值爲0是代表默認的0x7c0(0x07c0:0000,絕對地址0x7c00) */
		tmp2 = *((unsigned short *)((unsigned char *)SCRATCHADDR + 0x22));

		if (debug > 0)
			printf("\nLoad segment: 0x%X\t", (unsigned long)tmp2);

		if (tmp2 == 0)
			tmp2 = 0x7C0;

		//	*(unsigned short *)&(chainloader_file[0x0C]) = tmp2;	/* 0C-0D: load segment */
		/* Booting Catalog+0x24 System Type */
		tmp = *((unsigned char *)SCRATCHADDR + 0x24);
		if (debug > 0)
			printf("System Type: 0x%X\t", (unsigned long)tmp);

		/* Booting Catalog+0x26 扇區數 */
		i = *((unsigned short *)((unsigned char *)SCRATCHADDR + 0x26));
		if (debug > 0)
			printf("Sector Count: 0x%X\n", (unsigned long)i);

		//	*(unsigned short *)&(chainloader_file[0x0E]) = i;	/* 0E-0F: sector count in 512-byte sectors */
		tmp = *((unsigned long *)((unsigned char *)SCRATCHADDR + 0x28));	/* Booting Catalog+0x28 Load RBA */
		if (debug > 0)
			printf("Load RBA: 0x%X\t", (unsigned long)tmp);

		//	*(unsigned long *)&(chainloader_file[4]) = tmp;		/* 04-07: LBA of the image to be emulated */

		/*El Torito定義了3種引導模式,分別是floppy disk emulation,
		  hard disk emulation以及no emulation。
		  前兩種模擬模式,通過將軟盤或者磁盤的image存放在CD-ROM中,
		  由BIOS負責加載到內存中,並將其虛擬成軟盤或者磁盤驅動器,
		  然後從虛擬軟盤或虛擬磁盤中引導OS,從而實現對老OS的支持。
		  現在最常用的是第3種方式,即直接從CD-ROM引導OS (LiveCD採用此方式)。
		  El Torito Bootable CD需要BIOS的支持,當然,現在的BIOS都已經支持完全3種引導模式了。*/
		tmp1 = *((unsigned char *)SCRATCHADDR + 0x21);	/* Boot media type */
		if (tmp1 > 4)
			goto failure_exec_format;
		if (debug > 0)
			printf("Boot Type: %s\n",
				tmp1 == 0 ? "0 = No Emulation" :
				tmp1 == 1 ? "1 = 1.2M floppy" :
				tmp1 == 2 ? "2 = 1.44M floppy" :
				tmp1 == 3 ? "3 = 2.88M floppy" : "Hard Disk");

		//	chainloader_file[1] = tmp1;				/* 01: boot media type */
		//	chainloader_file[2] = ((tmp1 == 0) ? current_drive : (tmp1 == 4) ? 0x80 : 0x00);	/* 02: drive number */

		/* 如果是非模擬模式,直接從CD-ROM引導OS */
		if (tmp1 == 0)
		{
			/* 非模擬模式需要使用2048-byte的扇區大小 */
			if (buf_geom.sector_size != 2048)
				goto failure_exec_format;
			//kernel_type = KERNEL_TYPE_CHAINLOADER;
			/*
			 current_drive: 設備ID(通過前面的檢查可以知道整個設備是一個CDROM)
			 i: Booting Catalog+0x26 扇區數
			 tmp:Booting Catalog+0x28 加載RBA。 這是虛擬磁盤的起始地址,CD使用相對/邏輯塊尋址。
			 tmp2: Booting Catalog+0x22:引導代碼指定的加載位置(節),當這個值爲0是代表默認的0x7c0
			 下面這段設置chainloader_file以及其他需要用到的變量。
			*/
			sprintf(chainloader_file, "(%d)%d+%d", current_drive, tmp, (unsigned long)((i + 3) / 4));
			chainloader_load_segment = tmp2;//0x7c0;
			chainloader_load_offset = 0;//0x07c0:0000(0x7c00);
			chainloader_load_length = i * 512;//0x200;
			chainloader_skip_length = 0;
			chainloader_boot_CS = tmp2;//0x7c0;
			chainloader_boot_IP = 0;//0x07c0:0000(0x7c00);
			//chainloader_ebx = 0;
			//chainloader_ebx_set = 0;
			chainloader_edx = current_drive;
			chainloader_edx_set = 1;
			//chainloader_disable_A20 = 0;

			/* update lba_cd_boot if there are any maps */
			/* 恢復原始的int13/int15處理程序,unset_int13_handler返回0表示成功。 */
			if (!unset_int13_handler(0))
			{
				/* 驅動器映射 */
				if (drive_map_slot_empty(bios_drive_map[0]))
				{
					if (atapi_dev_count == 0)
					{
						errnum = ERR_NO_DRIVE_MAPPED;
						goto failure_exec_format;
					}
				}
				lba_cd_boot = tmp;  // <== 告知引導例程可以從這個扇區開始加載CDROM啓動代碼。
				set_int13_handler(bios_drive_map);
			}

			/* needn't clear disk buffer */
			/* buf_drive = -1; */
			/* buf_track = -1; */
			// 注:這裏並不拷貝啓動代碼到7C00,而是讓boot_func去直接引導CDROM。
			return !(errnum = 0); 
		} /*End if type == "No Emulation"*/

		/* 模擬軟驅或者硬盤模擬啓動。LBA=tmp */

		/*******************************************************/
		/* 在0000:7C00的位置讀取鏡像的前512個字節  */
		/*******************************************************/
		filepos = tmp * 0x800;

		/* 不能使用SCRATCHADDR內存,因爲map_func和geometry_func可能在
		 * 使用它. 因此我們改爲使用0x2B0000下的一個扇區代替。 */
		if (grub_read((unsigned long long)(HMA_ADDR - 0x200), 512, 0xedde0d90) != 512)
			goto failure_exec_format;

		{
			unsigned long heads = 0;
			unsigned long sects = 0;

			if (tmp1 == 4) //CDROM以模擬硬盤的方式啓動
			{
				int err;
				if ((err = probe_mbr((struct master_and_dos_boot_sector *)(HMA_ADDR - 0x200), 0, 1, 0)))
				{
					if (debug > 0)
						printf_warning("Warning! Partition table of HD image is faulty(err=%d).\n", err);
					sects = (*(unsigned char *)(HMA_ADDR - 0x200 + 0x1C4)) & 0x3F;
					probed_total_sectors = *(unsigned long *)(HMA_ADDR - 0x200 + 0x1CA);
					if (sects < 2 || probed_total_sectors < 5)
					{
						errnum = ERR_BAD_PART_TABLE;
						goto failure_exec_format;
					}
					heads = *(unsigned char *)(HMA_ADDR - 0x200 + 0x1C3);
					//if (heads < 255)
					heads++;
					if (debug > 0)
						printf("Use Heads=%d, SectorsPerTrack=%d, TotalSectors=%ld for HD emulation.\n", heads, sects, (unsigned long long)probed_total_sectors);
				}
			}
			else
			{
				//CDROM以模擬軟盤的方式啓動
				if (probe_bpb((struct master_and_dos_boot_sector *)(HMA_ADDR - 0x200)))
				{
					probed_total_sectors = (tmp1 == 1 ? 2400 : tmp1 == 2 ? 2880 : 5760);
				}
			}

			/* 將鏡像映射爲硬盤驅動或者軟盤的參數
			 * "--read-only --heads=%d --sectors-per-track=%d (%d)%d+%d (0x80)"
			 * "--read-only --heads=%d --sectors-per-track=%d (%d)%d+%d (0x00)"
			 */
			sprintf(chainloader_file, "--read-only --heads=%d --sectors-per-track=%d (%d)%d+%d (%d)",
				(unsigned long)heads,
				(unsigned long)sects,
				(unsigned long)current_drive,
				(unsigned long)tmp,  //tmp = CDROM Load RBA
				(unsigned long)((probed_total_sectors + 3) / 4), //0x3FFFFFFF,
				(unsigned long)((tmp1 == 4) ? 0x80 : 0x00));
		}

		// tmp等於硬盤/軟盤數量
		tmp = ((tmp1 == 4) ? (*(char *)0x475) : ((*(char*)0x410) & 1));

		/* file must be closed before calling map_func() */
		grub_close();

		// 等於與調用map命令,map命令行參數見上面的註釋
		disable_map_info = 1;
		map_func(chainloader_file, flags);
		disable_map_info = 0;

		if (errnum)
			goto failure;

		/* 將之前的fd0或者hd0上移 */
		if (tmp)
		{
			// 硬盤: "map (0x80) (0x80 + 硬盤數)""
			// 軟盤: "map (0x00) (0x01)"
			sprintf(chainloader_file, "(%d) (%d)", (unsigned long)((tmp1 == 4) ? 0x80 : 0x00), (unsigned long)((tmp1 == 4) ? (0x80 + tmp) : 0x01));
			map_func(chainloader_file, flags);
			if (errnum)
			{
				if (debug > 0)
				{
					printf("Failed 'map %s'(error=%d). But you may ignore it and continue to 'boot' the CD.\n", chainloader_file, errnum);
				}
				errnum = 0;
			}
		}

		/* rehook */
		unset_int13_handler(0);
		if (drive_map_slot_empty(bios_drive_map[0]))
			if (atapi_dev_count == 0)
			{
				errnum = ERR_NO_DRIVE_MAPPED;
				goto failure;
			}
		set_int13_handler(bios_drive_map);
		buf_drive = -1;
		buf_track = -1;

		grub_memmove((char *)0x7C00, (char *)(HMA_ADDR - 0x200), 512);
		sprintf(chainloader_file, "(%d)+1", (unsigned long)((tmp1 == 4) ? 0x80 : 0x00));

		chainloader_load_segment = 0;//tmp2;
		chainloader_load_offset = 0x7c00;
		chainloader_load_length = 512;
		chainloader_skip_length = 0;
		chainloader_boot_CS = 0;//tmp2;
		chainloader_boot_IP = 0x7c00;
		//chainloader_ebx = 0;
		chainloader_ebx_set = 0;
		chainloader_edx_set = 0;
		chainloader_bx_set = 0;
		chainloader_cx_set = 0;
		//chainloader_disable_A20 = 0;
		saved_drive = ((tmp1 == 4) ? 0x80 : 0x00);
		chainloader_edx = saved_drive;
		saved_partition = ((tmp1 == 4) ? 0x00FFFF : 0xFFFFFF);
		return !(errnum = 0);
	}/* End if(*filename == 0 ...) */

	// 前面已經處置了chainloader的文件名爲空的情況(檢查當前啓動設備是否爲CDROM),下面處理文件名不爲空時。
	/* 打開chainloader文件 */
	grub_open(arg);

	if (errnum)
		goto failure;

	/* 讀取chainloader文件的第一個512字節(一個扇區)到SCRATCHADDR */
	{
		unsigned long len;
		len = grub_read((unsigned long long) SCRATCHADDR, 512, 0xedde0d90);

		if (len != 512)
			goto failure_exec_format;
	}

	if (chainloader_skip_length > filemax)
	{
		errnum = ERR_INVALID_SKIP_LENGTH;
		goto failure_exec_format;
	}

	if (*((long *)SCRATCHADDR) == 0x49445324 /* $SDI */)
	{
		/* 讀取的是一個sdi文件,需要從SDI中啓動 */
		is_sdi = 1;
		if (debug > 0)
			grub_printf("SDI signature: %s\n", (char *)(SCRATCHADDR));
	}
	else
	{
		// DOS:
		// [EB FF "CONFIG"] == 0x4749464E4F43FFEB
		// 501E0100122E802E && is_drdos: packed with pack101 
		// 0A079047EB && is_pcdos: PC-DOS 7.1
		// 070135E9 && is_pcdos : PC-DOS 2000 
		// 060135E9 && is_msdos : MS-DOS 6.x
		// 646F4D206C616552 && is_drmk : DRMK
		// 4F43000000FFFFEB706D6F435141504D && is_drdos : DR-DOS
		// CB5052C03342CA8C: ROM-DOS

		if ((*(long long *)SCRATCHADDR | 0xFF00LL) == 0x4749464E4F43FFEBLL && filemax > 0x4000)
		{
			if (chainloader_load_segment == -1)
				chainloader_load_segment = 0x0060;
		drdos:
			if (chainloader_load_offset == -1)
				chainloader_load_offset = 0;
			if (chainloader_load_length == -1)
				chainloader_load_length = filemax;
			if (!chainloader_ebx_set)
			{
				chainloader_ebx = current_drive | ((current_partition >> 8) & 0xFF00);
				chainloader_ebx_set = 1;
			}

			grub_close();

			/* FIXME: Where should the BPB be placed for FreeDOS's KERNEL.SYS?
			 *	  In the current implementation it is placed at 0000:7C00
			 *	  but has no effect, since the KERNEL.SYS body will later
			 *	  overwrite 0000:7C00 on issuing the `boot' command.
			 */

			if (((current_partition >> 8) & 0xFF00) == 0xFF00) /* check if partition number == 0xFF */
				grub_sprintf((char *)SCRATCHADDR, "(%d)+1", (unsigned long)(unsigned char)current_drive);
			else
				grub_sprintf((char *)SCRATCHADDR, "(%d,%d)+1", (unsigned long)(unsigned char)current_drive, (unsigned long)(unsigned char)(current_partition >> 16));

			if (!grub_open((char *)SCRATCHADDR))
				goto failure;

			/* Read the boot sector of the partition onto 0000:7C00  */
			if (grub_read((unsigned long long) SCRATCHADDR, 512, 0xedde0d90) != 512)
				goto failure_exec_format;

			/* modify the hidden sectors */
			/* FIXME: Does the boot drive number also need modifying? */

			if (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)))
				*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)) = (unsigned long)part_start;

			if (is_pcdos || is_msdos) {
				/* Set data area location to *0x7BFC, root directory entry address to *0x7BF8, root directory entry size to *0x7BDE */
				if (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT_NAME)) == 0x31544146) { /* FAT1(2|6) */
					*(unsigned long *)0x7BF8 = *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_RESERVED_SECTORS))
						+ *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_SECTORS_PER_FAT)) * 2; // root directory entry address
					*(unsigned short *)0x7BDE = *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_MAX_ROOT_ENTRIES)) * 32 / *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_BYTES_PER_SECTOR)); // root directory entry size
					*(unsigned long *)0x7BFC = (unsigned long)part_start
						+ *(unsigned long *)0x7BF8
						+ *(unsigned short *)0x7BDE;
				}
				else if (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_NAME)) == 0x33544146) { /* FAT32 */
					*(unsigned long *)0x7BF8 = *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_RESERVED_SECTORS))
						+ *((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_SECTORS_PER_FAT)) * 2
						+ (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_ROOT)) - 2) * *((unsigned char *)(SCRATCHADDR + BOOTSEC_BPB_SECTORS_PER_CLUSTER)); // root directory entry address
					*(unsigned short *)0x7BDE = *((unsigned char *)(SCRATCHADDR + BOOTSEC_BPB_SECTORS_PER_CLUSTER)); // root directory entry size = 1 cluster
					*(unsigned long *)0x7BFC = (unsigned long)part_start + *(unsigned long *)0x7BF8; // root directory entry is in cluster 2 !!
				}
				else {
					printf_errinfo("Error: Not FAT partition.");
					goto failure_exec_format;
				}

				/* copy directory entry of boot files to 0x500 */
				grub_close();
				if (((current_partition >> 8) & 0xFF00) == 0xFF00) /* check if partition number == 0xFF */
					grub_sprintf((char *)(HMA_ADDR - 0x20), "(%d)%d+%d", (unsigned long)(unsigned char)current_drive, *(unsigned long *)0x7BF8, *(unsigned short *)0x7BDE * *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_BYTES_PER_SECTOR)) / 512);
				else
					grub_sprintf((char *)(HMA_ADDR - 0x20), "(%d,%d)%d+%d", (unsigned long)(unsigned char)current_drive, (unsigned long)(unsigned char)(current_partition >> 16), *(unsigned long *)0x7BF8, *(unsigned short *)0x7BDE * *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_BYTES_PER_SECTOR)) / 512);

				grub_open((char *)(HMA_ADDR - 0x20));
				grub_read((unsigned long long)(HMA_ADDR - 0x10000), (*(unsigned short *)0x7BD8 = *(unsigned short *)0x7BDE * *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_BYTES_PER_SECTOR))), 0xedde0d90);

				/* read 1st FAT(first 32K) to HMA_ADDR - 0x8000 */
				if (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_NAME)) == 0x33544146) { /* FAT32 */
					*(unsigned short *)0x7BDC = 0; // read root directory cluster count
					*(unsigned long *)0x7BD0 = *((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_ROOT)); // last read root directory cluster
					if (((current_partition >> 8) & 0xFF00) == 0xFF00) /* check if partition number == 0xFF */
						grub_sprintf((char *)(HMA_ADDR - 0x20), "(%d)%d+%d", (unsigned long)(unsigned char)current_drive, *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_RESERVED_SECTORS)), *((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_SECTORS_PER_FAT)));
					else
						grub_sprintf((char *)(HMA_ADDR - 0x20), "(%d,%d)%d+%d", (unsigned long)(unsigned char)current_drive, (unsigned long)(unsigned char)(current_partition >> 16), *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_RESERVED_SECTORS)), *((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_SECTORS_PER_FAT)));

					grub_open((char *)(HMA_ADDR - 0x20)); /* read 1st FAT table (first 0x8000 bytes only) */
					grub_read((unsigned long long)(HMA_ADDR - 0x8000), (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_SECTORS_PER_FAT)) > 40 ? 40 : *((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_SECTORS_PER_FAT))) * *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_BYTES_PER_SECTOR)), 0xedde0d90);
				readroot:
					if ((HMA_ADDR - 0x10000 + (1 + *(unsigned short *)0x7BDC) * *(unsigned short *)0x7BD8) < HMA_ADDR && /* don't overrun */
						*(unsigned long *)0x7BD0 * sizeof(unsigned long) < 0x8000 &&
						(*(unsigned long *)0x7BD0 = *(unsigned long *)(HMA_ADDR - 0x8000 + *(unsigned long *)0x7BD0 * sizeof(unsigned long))) != 0xFFFFFFFF) { /* root cluster count > 1 */
						*(unsigned long *)0x7BF8 = *((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_RESERVED_SECTORS))
							+ *((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_FAT32_SECTORS_PER_FAT)) * 2
							+ (*(unsigned long *)0x7BD0 - 2) * *((unsigned char *)(SCRATCHADDR + BOOTSEC_BPB_SECTORS_PER_CLUSTER)); // root directory entry address
						if (((current_partition >> 8) & 0xFF00) == 0xFF00) /* check if partition number == 0xFF */
							grub_sprintf((char *)(HMA_ADDR - 0x20), "(%d)%d+%d", (unsigned long)(unsigned char)current_drive, *(unsigned long *)0x7BF8, *(unsigned short *)0x7BDE);
						else
							grub_sprintf((char *)(HMA_ADDR - 0x20), "(%d,%d)%d+%d", (unsigned long)(unsigned char)current_drive, (unsigned long)(unsigned char)(current_partition >> 16), *(unsigned long *)0x7BF8, *(unsigned short *)0x7BDE);
						grub_open((char *)(HMA_ADDR - 0x20));
						grub_read((unsigned long long)(HMA_ADDR - 0x10000 + (1 + *(unsigned short *)0x7BDC) * *(unsigned short *)0x7BD8), *(unsigned short *)0x7BD8, 0xedde0d90);
						++(*(unsigned short *)0x7BDC);
						goto readroot;
					}
				}
				grub_memmove((void *)0x7C3E, (void *)(((*(short*)0x7A & 0xFFFF) << 4) + (*(short*)0x78 & 0xFFFF)), 15); /* Copy DPT over PBR startup code */

				for (*(unsigned long *)0x7BF4 = HMA_ADDR - 0x10000, *(unsigned long *)0x7BF0 = 0x500; *(char *)(*(unsigned long *)0x7BF4) && *(unsigned long *)0x7BF4 < HMA_ADDR; *(unsigned long *)0x7BF4 += 32) {
					if (*(long long *)(*(unsigned long *)0x7BF4) == *(long long *)0x7BE0) { /* BIO */
						grub_memmove((void *)*(unsigned long *)0x7BF0, (void *)*(unsigned long *)0x7BF4, 32);
						*(unsigned long *)0x7BF0 += 32;
						break;
					}
				}
				for (*(unsigned long *)0x7BF4 = HMA_ADDR - 0x10000; *(char *)(*(unsigned long *)0x7BF4) && *(unsigned long *)0x7BF4 < HMA_ADDR; *(unsigned long *)0x7BF4 += 32) {
					if (*(long long *)(*(unsigned long *)0x7BF4) == *(long long *)0x7BE8) { /* DOS */
						grub_memmove((void *)*(unsigned long *)0x7BF0, (void *)*(unsigned long *)0x7BF4, 32);
						*(unsigned long *)0x7BF0 += 32;
						break;
					}
				}
				if (!chainloader_bx_set)
				{
					chainloader_bx = *(unsigned long *)0x7BFC;
					chainloader_bx_set = 1;
				}
				if (!chainloader_cx_set)
				{
					chainloader_cx = *((unsigned char *)(SCRATCHADDR + BOOTSEC_BPB_MEDIA_DESCRIPTOR)) << 8;
					chainloader_cx_set = 1;
				}
			}

			if (debug > 0)
				grub_printf("Will boot %s from drive=0x%x, partition=0x%x(hidden sectors=0x%lx)\n",
					(is_pcdos ? "PC DOS" : (is_msdos ? "MS-DOS" : (is_drdos ? "DR-DOS" : (is_romdos ? "ROM-DOS" : (is_drmk ? "DRMK" : "FreeDOS"))))), current_drive, (unsigned long)(unsigned char)(current_partition >> 16), (unsigned long long)part_start);
		}
		else if ((*((long long *)SCRATCHADDR) == 0x501E0100122E802ELL && (is_drdos = 1)) /* packed with pack101 */ ||
			is_pcdos || is_msdos ||
			(*((long long *)(SCRATCHADDR)) == 0x0A079047EBLL && (is_pcdos = 1)) /* PC-DOS 7.1 */ ||
			(*((long long *)(SCRATCHADDR)) == 0x070135E9LL && (is_pcdos = 1)) /* PC-DOS 2000 */ ||
			(*((unsigned long *)(SCRATCHADDR)) == 0x060135E9 && (is_msdos = 1)) /* MS-DOS 6.x */ ||
			(*((long long *)(SCRATCHADDR + 6)) == 0x646F4D206C616552LL && (is_drmk = 1)) /* DRMK */ ||
			((*(long long *)SCRATCHADDR | 0xFFFF02LL) == 0x4F43000000FFFFEBLL && (*(((long long *)SCRATCHADDR) + 1) == 0x706D6F435141504DLL) && (is_drdos = 1)))   /* DR-DOS */
		{
			/* contributor: Roy <roytam%gmail%com> */
			if (chainloader_load_segment == -1)
				chainloader_load_segment = 0x0070;
			if ((is_pcdos || is_drmk || is_msdos) && chainloader_load_length == -1)
				chainloader_load_length = filemax < 0x7400 ? filemax : 0x7400;
			if (is_pcdos) {
				*(long long *)0x7BE0 = 0x20204F49424D4249LL; /* IBMBIO.COM */
				*(long long *)0x7BE8 = 0x2020534F444D4249LL; /* IBMDOS.COM */
			}
			else if (is_msdos) {
				*(long long *)0x7BE0 = 0x2020202020204F49LL; /* IO.SYS */
				*(long long *)0x7BE8 = 0x202020534F44534DLL; /* MSDOS.SYS */
			}

			goto drdos;   //<=== DOS引導
		}
		else
		{
			// 非DOS
			if (filemax >= 0x40000 && *((unsigned short *)(SCRATCHADDR)) == 0x5A4D && // MZ header
				*((unsigned short *)(SCRATCHADDR + 0x80)) == 0x4550 && // PE header
				*((unsigned short *)(SCRATCHADDR + 0xDC)) == 0x1 //&& // PE subsystem
		  //  	  (*((unsigned long *) (SCRATCHADDR + 0xA8))) == 0x1000 && // Entry address
		  //  	  (*((unsigned long *) (SCRATCHADDR + 0xB4))) == 0x8000 // Base address
				)
			{
				//
				// FreeLDR引導
				//
				if (chainloader_load_segment == -1)
					chainloader_load_segment = 0;
				if (chainloader_load_offset == -1)
					chainloader_load_offset = (*((unsigned long *)(SCRATCHADDR + 0xB4)));
				if (chainloader_load_length == -1)
					chainloader_load_length = filemax;
				if (chainloader_boot_IP == -1)
					chainloader_boot_IP = (*((unsigned long *)(SCRATCHADDR + 0xB4))) + (*((unsigned long *)(SCRATCHADDR + 0xA8)));
				if (!chainloader_edx_set)
				{
					chainloader_edx = current_drive | ((current_partition >> 8) & 0xFF00);
					chainloader_edx_set = 1;
				}
				grub_close();

				if (debug > 0)
					grub_printf("Will boot FreeLDR from drive=0x%x, partition=0x%x(hidden sectors=0x%lx)\n", current_drive, (unsigned long)(unsigned char)(current_partition >> 16), (unsigned long long)part_start);
			}
			else
			{
				// 非DOS,也非FreeLDR
				if (*(short *)SCRATCHADDR == 0x5A4D && filemax > 0x10000 &&
					*((unsigned short *)(SCRATCHADDR + BOOTSEC_SIG_OFFSET)) == 0
					/* && (*(long *)(SCRATCHADDR + 0xA2)) == 0 */)
				{
					// WinME
					int err;

					/* Read the second sector.  */
					if (grub_read((unsigned long long) SCRATCHADDR, 512, 0xedde0d90) != 512)
						goto failure_exec_format;

					err = (*(short *)SCRATCHADDR != 0x4A42);

					filepos += 0x200;	/* skip the third sector */

					/* Read the fourth sector.  */
					if (grub_read((unsigned long long) SCRATCHADDR, 512, 0xedde0d90) != 512)
						goto failure_exec_format;

					err |= (*((unsigned short *)(SCRATCHADDR + BOOTSEC_SIG_OFFSET)) != 0x534D);

					/* Read the fifth sector.
					 * check the compress signature "CM" of IO.SYS of WinME */
					if (grub_read((unsigned long long) SCRATCHADDR, 512, 0xedde0d90) != 512)
						goto failure_exec_format;

					if (!err)
					{
						if (chainloader_load_segment == -1)
							chainloader_load_segment = 0x0070;
						if (chainloader_load_offset == -1)
							chainloader_load_offset = 0;
						if (chainloader_load_length == -1)
							chainloader_load_length = filemax;
						if (chainloader_skip_length == 0)
							chainloader_skip_length = 0x0800;

						/* WinME support by bean. Thanks! */

						// Input parameter for SYSINIT
						// BX,AX: Start sector for the data area of FAT. It doesn't needs to
						//        be set. However, we should at least clear BX, otherwise,
						//        it would have some minor problem when booting WinME.
						// DI:    Length of the boot code, don't need to be set.
						// BP:    0x7C00, boot sector pointer, don't need to be set.
						// DH:    Media ID (BS[0x15]) , 0xF0 for floppy, 0xF8 for harddisk
						// DL:    Drive number (BS[0x40] for FAT32)

						if (!chainloader_edx_set)
						{
							chainloader_edx = current_drive | ((current_partition >> 8) & 0xFF00);
							chainloader_edx_set = 1;
						}
						if (!chainloader_ebx_set)
						{
							chainloader_ebx = 0;	// clear BX for WinME
							chainloader_ebx_set = 1;
						}

						/* save partition number to the high word */
						chainloader_edx = (chainloader_edx & 0xFF00FFFF) | ((chainloader_edx & 0xFF00) << 8);

						// set media descriptor in DH for WinME
						chainloader_edx = (chainloader_edx & 0xFFFF00FF) | 0xF000 | ((chainloader_edx & 0x80) << 4);

						grub_close();

						/* FIXME: Where should the BPB be placed for MS-DOS's IO.SYS?
						 *	  In the current implementation it is placed at 0000:7C00
						 *	  but has no effect, since the IO.SYS body will later
						 *	  overwrite 0000:7C00 on issuing the `boot' command.
						 */

						if ((chainloader_edx & 0xFF0000) == 0xFF0000) /* check if partition number == 0xFF */
							grub_sprintf((char *)SCRATCHADDR, "(%d)+1", (unsigned long)(unsigned char)chainloader_edx);
						else
							grub_sprintf((char *)SCRATCHADDR, "(%d,%d)+1", (unsigned long)(unsigned char)chainloader_edx, (unsigned long)(unsigned char)(chainloader_edx >> 16));

						if (!grub_open((char *)SCRATCHADDR))
							goto failure;

						/* Read the boot sector of the partition onto 0000:7C00  */
						if (grub_read((unsigned long long) SCRATCHADDR, 512, 0xedde0d90) != 512)
							goto failure_exec_format;

						is_io = 1;
						/* modify the hidden sectors */
						/* FIXME: Does the boot drive number also need modifying? */

						if (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)))
							*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)) = (unsigned long)part_start;
						if (debug > 0)
							grub_printf("Will boot MS-DOS %c.x from drive=0x%x, partition=0x%x(hidden sectors=0x%lx)\n",
								(unsigned long)(((*(unsigned short *)SCRATCHADDR) == 0x4D43) ? '8' : '7'),
								(unsigned long)current_drive, (unsigned long)(unsigned char)(current_partition >> 16), (unsigned long long)part_start);
					}
				}
				else
				{
					// 非DOS,非FreeLDR,非WinME
					if (((*(long *)SCRATCHADDR) & 0x00FF00FF) == 0x000100E9 && filemax > 0x30000 &&
						(*((unsigned short *)(SCRATCHADDR + BOOTSEC_SIG_OFFSET)) != BOOTSEC_SIGNATURE))
					{
						// NTLDR && BOOTMGR
						if (chainloader_load_segment == -1)
							chainloader_load_segment = 0x2000;
						if (chainloader_load_offset == -1)
							chainloader_load_offset = 0;
						if (chainloader_load_length == -1)
							chainloader_load_length = filemax;
						if (!chainloader_edx_set)
						{
							chainloader_edx = current_drive | ((current_partition >> 8) & 0xFF00);
							chainloader_edx_set = 1;
						}

						grub_close();

						// ntldr不可以直接導入到7c00運行,需要載入NTLDR/BOOTMGR所在分區(所在設備)
						// 的第一個扇區的內容
						if ((chainloader_edx & 0xFF00) == 0xFF00)
							grub_sprintf((char *)SCRATCHADDR, "(%d)+1", (unsigned long)(unsigned char)chainloader_edx); //(MBR?)
						else
						{
							// 該分區下的第一個扇區(PBR?)。
							grub_sprintf((char *)SCRATCHADDR, "(%d,%d)+1", (unsigned long)(unsigned char)chainloader_edx, (unsigned long)(unsigned char)(chainloader_edx >> 8));
						}

						if (!grub_open((char *)SCRATCHADDR))
							goto failure;

						/* Read the boot sector of the partition onto 0000:7C00  */
						if (grub_read((unsigned long long) SCRATCHADDR, SECTOR_SIZE, 0xedde0d90) != SECTOR_SIZE)
							goto failure_exec_format;

						/* modify the hidden sectors */
						/* FIXME: Does the boot drive number also need modifying? */

						if (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)))
							*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)) = (unsigned long)part_start;

						/* -------- begin extra work for exFAT -------- */
						if ((unsigned char)(current_partition >> 16) != 0xFF)	/* not whole drive, ie, not unpartitioned floppy/cdrom. */
							*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)) = (unsigned long)part_start;

						if (*((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_BYTES_PER_SECTOR)) == 0)
						{
							*((unsigned short *)(SCRATCHADDR + BOOTSEC_BPB_BYTES_PER_SECTOR)) = 512;
							if ((buf_geom.flags & (BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_BIFURCATE)) && (!(buf_geom.flags & BIOSDISK_FLAG_CDROM)))
							{
								*((unsigned char *)(SCRATCHADDR + 0x02)) = 1; /* LBA must be supported for bootmgr of Win8. */
							}
							else {
								/* Unfortunately LBA is not present. Win8 bootmgr may fail. */
								*((unsigned char *)(SCRATCHADDR + 0x02)) = 0;
								if (!*((unsigned long *)(SCRATCHADDR + 0x18)))
								{
									/* Although Win8 bootmgr might fail, we do our best to
									 * fill the BPB with correct sectors_per_track and
									 * number_of_heads values. */
									*((unsigned short *)(SCRATCHADDR + 0x18)) = buf_geom.sectors;
									*((unsigned short *)(SCRATCHADDR + 0x1A)) = buf_geom.heads;
								}
							}
						}
						/* --------  end extra work for exFAT  -------- */

						if (debug > 0)
							grub_printf("Will boot NTLDR from drive=0x%x, partition=0x%x(hidden sectors=0x%lx)\n", current_drive, (unsigned long)(unsigned char)(current_partition >> 16), (unsigned long long)part_start);
					}
					else
					{
						// 非DOS,非FreeLDR,非WinME,非NTLDR(BOOTMGR)
						if (filemax >= 0x4000 && (*(short *)SCRATCHADDR) == 0x3EEB && //(*(long *)(SCRATCHADDR + 0x40)) == 0x5B0000E8 &&
							(*((unsigned short *)(SCRATCHADDR + BOOTSEC_SIG_OFFSET)) != BOOTSEC_SIGNATURE))
						{
							// GRLDR
							char tmp_buf[16];

							filepos = 0x1FF8;	/* grldr signature, pre_stage2, etc */

							if (grub_read((unsigned long long)(unsigned long)tmp_buf, 16, 0xedde0d90) != 16)
								goto failure_exec_format;

							filepos = 0x200;
							if (*(short *)tmp_buf < 0x40 || *(short *)tmp_buf > 0x1B8)
								goto check_isolinux;
							if (*(long *)(tmp_buf + 8) != 0x008270EA)
								goto check_isolinux;
							if (*(long *)(SCRATCHADDR + *(short *)tmp_buf) != *(long *)(tmp_buf + 4))
								goto check_isolinux;
							if ((*(long long *)(void *)((int)*(short *)tmp_buf + (int)SCRATCHADDR - 5) & 0xFFFFFFFFFFLL) != 0xB8661FFCBBLL)
								goto check_isolinux;

							//	if (chainloader_load_segment == -1)
							//		chainloader_load_segment = 0x2000;
							//	if (chainloader_load_offset == -1)
							//		chainloader_load_offset = 0;
							if (chainloader_load_length == -1)
								chainloader_load_length = filemax;
							if (!chainloader_edx_set)
							{
								chainloader_edx = current_drive | ((current_partition >> 8) & 0xFF00);
								chainloader_edx_set = 1;
							}
							is_grldr = 1;

							grub_close();

							if (debug > 0)
								grub_printf("Will boot GRLDR from drive=0x%x, partition=0x%x(hidden sectors=0x%lx)\n", current_drive, (unsigned long)(unsigned char)(current_partition >> 16), (unsigned long long)part_start);
						}
						else
						{
							// 非DOS,非FreeLDR,非WinME,非NTLDR,非GRLDR
						check_isolinux:

							if (filemax < 0x800)
								goto check_signature;

							/* Read the 2nd, 3rd and 4th sectors. */

							/**********************************************/
							/**** 4 sectors at SCRATCHADDR are used!!! ****/
							/**********************************************/

							filepos = 0x200;

							if (grub_read((unsigned long long) SCRATCHADDR + 0x200, 0x600, 0xedde0d90) != 0x600)
								goto check_signature;

							if (filemax >= 0x4000 && ((*(long *)SCRATCHADDR) & 0x80FFFFFF) == 0xEB5A4D && (*(long *)(SCRATCHADDR + 0x202)) == 0x53726448 &&
								(*((unsigned short *)(SCRATCHADDR + BOOTSEC_SIG_OFFSET)) == BOOTSEC_SIGNATURE) &&
								(*(unsigned char *)(SCRATCHADDR + 0x200)) == 0xEB)	/* GRUB.EXE */
							{
								//GRUB.EXE
								if (chainloader_load_segment == -1)
									chainloader_load_segment = 0x1000;	/* use address != 0x7C00, so that PXE is automatically disabled. */
								if (chainloader_load_offset == -1)
									chainloader_load_offset = 0;
								if (chainloader_boot_CS == -1)
									chainloader_boot_CS = chainloader_load_segment;
								if (chainloader_boot_IP == -1)
									chainloader_boot_IP = chainloader_load_offset + 2;	/* skip "MZ" */
								if (chainloader_load_length == -1)
									chainloader_load_length = filemax;
								if (!chainloader_edx_set)
								{
									chainloader_edx = current_drive | ((current_partition >> 8) & 0xFF00);
									chainloader_edx_set = 1;
								}

								grub_close();

								if (debug > 0)
									grub_printf("Will boot GRUB.EXE from drive=0x%x, partition=0x%x(hidden sectors=0x%lx)\n", current_drive, (unsigned long)(unsigned char)(current_partition >> 16), (unsigned long long)part_start);
							}
							else
							{
								if ((*(long long *)(SCRATCHADDR + 0x200)) == 0xCB5052C03342CA8CLL && (*(long *)(SCRATCHADDR + 0x208) == 0x5441464B))   /* ROM-DOS */
								{
									// ROM-DOS
									/* contributor: Roy <roytam%gmail%com> */
									if (chainloader_load_segment == -1)
										chainloader_load_segment = 0x1000;
									if (chainloader_load_offset == -1)
										chainloader_load_offset = 0;
									if (chainloader_load_length == -1)
										chainloader_load_length = filemax;
									if (chainloader_skip_length == 0)
										chainloader_skip_length = 0x0200;
									*(unsigned long *)0x84 = current_drive | 0xFFFF0000;
									is_romdos = 1;
									goto drdos;
								}
								else
								{
									if (((*(long long *)SCRATCHADDR) & 0xFFFFFFFFFF00FFFFLL) == 0x909000007C00EAFALL && filemax > 0x2000 && filemax < 0x20000) /* ISOLINUX */
									{
										for (p = (char *)(SCRATCHADDR + 0x40); p < (char *)(SCRATCHADDR + 0x7F3); p++)
										{
											if (*(unsigned long *)p == 0xBB0201B8 &&
												*(unsigned long *)(p - 4) == 0 &&
												*(unsigned long *)(p + 4) == 0x06B97C00 &&
												*(unsigned long *)(p + 8) == 0x0180BA00 &&
												*(unsigned short *)(p + 12) == 0x9A9C)
											{
												//ISOLINUX
												goto isolinux_ok;
											}
										}
										/* comment out old code */
										//for (p = (char *)(SCRATCHADDR + 0x40); p < (char *)(SCRATCHADDR + 0x140); p++)
										//{
										//	if (*(unsigned long *)p == 0xD08EC031 &&
										//		*(unsigned short *)(p - 10) == 0x892E &&
										//		*(unsigned short *)(p - 5) == 0x8C2E &&
										//		*(unsigned char *)(p + 4) == 0xBC &&
										//		*(unsigned char *)(p - 8) == 0x26 &&
										//		*(unsigned char *)(p - 3) == 0x16)
										//	{
										//		goto isolinux_ok;
										//	}
										//}
										goto check_signature;	/* it is not isolinux. */
									isolinux_ok:
										if (buf_geom.sector_size != 2048)
										{
											if (debug > 0)
												printf("\nCannot chainload ISOLINUX from a non-CDROM device.\n");
											goto failure_exec_format;
										}

										chainloader_load_segment = 0;
										chainloader_load_offset = 0x7c00;
										chainloader_load_length = filemax;
										chainloader_edx = current_drive;
										chainloader_edx_set = 1;

										is_isolinux = 1;
									}
									else
									{
									check_signature:
										/* If not loading it forcibly, check for the signature.  */
										if (!force
											&& (*((unsigned short *)(SCRATCHADDR + BOOTSEC_SIG_OFFSET))
												!= BOOTSEC_SIGNATURE))
											goto failure_exec_format;
									}
								}
							}
						}
					}
				}
			}
		}
	}
	grub_close();

	/* if BPB exists, we can reliablly modify the hidden sectors. */
	if (!probe_bpb((struct master_and_dos_boot_sector *)SCRATCHADDR))
		if (*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS)))
			*((unsigned long *)(SCRATCHADDR + BOOTSEC_BPB_HIDDEN_SECTORS))
			= (unsigned long)part_start;

	if (chainloader_load_length == -1)
		chainloader_load_length = filemax;
	if (chainloader_load_length > 0xA0000)
		chainloader_load_length = 0xA0000;
    
    // 將啓動代碼賦值到7C00處,該處也是BIOS加載MBR的位置
	grub_memmove((char *)0x7C00, (char *)SCRATCHADDR, 512);
	errnum = ERR_NONE;

	return 1;

failure_exec_format:

	grub_close();

	if (errnum == ERR_NONE)
		errnum = ERR_EXEC_FORMAT;

failure:

	chainloader_load_segment = chainloader_load_segment_orig;
	chainloader_load_offset = chainloader_load_offset_orig;
	chainloader_load_length = chainloader_load_length_orig;
	chainloader_skip_length = chainloader_skip_length_orig;
	chainloader_boot_CS = chainloader_boot_CS_orig;
	chainloader_boot_IP = chainloader_boot_IP_orig;
	chainloader_ebx = chainloader_ebx_orig;
	chainloader_ebx_set = chainloader_ebx_set_orig;
	chainloader_edx = chainloader_edx_orig;
	chainloader_edx_set = chainloader_edx_set_orig;
	chainloader_disable_A20 = chainloader_disable_A20_orig;
	is_sdi = is_sdi_orig;
	is_raw = is_raw_orig;
	is_isolinux = is_isolinux_orig;
	is_grldr = is_grldr_orig;
	is_io = is_io_orig;
	kernel_type = kernel_type_orig;
	force = errnum;	/* backup the errnum */
	grub_memmove((char *)chainloader_file, (char *)chainloader_file_orig, sizeof(chainloader_file));
	errnum = force;	/* restore the errnum */
	return 0;		/* return failure */

}

 

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