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