计算机引导过程一(从BIOS到MBR)
从BIOS到MBR过程图
BIOS
SeaBIOS是一个开源的x86 BIOS,这里以SeaBIOS为例分析,对应的代码为seabios/src/romlayout.S
reset_vector
CPU上电后,IP = 0xFFF0(https://en.wikipedia.org/wiki/Reset_vector),CPU执行的第一条指令位于0xFFF0
ORG 0xfff0 // Power-up Entry Point
.global reset_vector
reset_vector:
ljmpw $SEG_BIOS, $entry_post
SEG_BIOS为0xF000
#define SEG_BIOS 0xf000
entry_post
entry_post位于0xE05B
ORG 0xe05b
entry_post:
cmpl $0, %cs:HaveRunPost // Check for resume/reboot
jnz entry_resume
ENTRY_INTO32 _cfunc32flat_handle_post // Normal entry point
ENTRY_INTO32是一个宏定义,最后跳转到transition32,进入保护模式,其中edx保存_cfunc32flat_handle_post的地址
// Reset stack, transition to 32bit mode, and call a C function.
.macro ENTRY_INTO32 cfunc
xorw %dx, %dx
movw %dx, %ss
movl $ BUILD_STACK_ADDR , %esp
movl $ \cfunc , %edx
jmp transition32
.endm
transition32最后跳转到edx保存的地址,也就是_cfunc32flat_handle_post
// Place CPU into 32bit mode from 16bit mode.
// %edx = return location (in 32bit mode)
// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
DECLFUNC transition32
.global transition32_nmi_off
transition32:
// Disable irqs (and clear direction flag)
cli
cld
// Disable nmi
movl %eax, %ecx
movl $CMOS_RESET_CODE|NMI_DISABLE_BIT, %eax
outb %al, $PORT_CMOS_INDEX
inb $PORT_CMOS_DATA, %al
// enable a20
inb $PORT_A20, %al
orb $A20_ENABLE_BIT, %al
outb %al, $PORT_A20
movl %ecx, %eax
transition32_nmi_off:
// Set segment descriptors
lidtw %cs:pmode_IDT_info
lgdtw %cs:rombios32_gdt_48
// Enable protected mode
movl %cr0, %ecx
andl $~(CR0_PG|CR0_CD|CR0_NW), %ecx
orl $CR0_PE, %ecx
movl %ecx, %cr0
// start 32bit protected mode code
ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f)
.code32
// init data segments
1: movl $SEG32_MODE32_DS, %ecx
movw %cx, %ds
movw %cx, %es
movw %cx, %ss
movw %cx, %fs
movw %cx, %gs
jmpl *%edx
handle_post -> dopost -> reloc_preinit -> maininit -> startBoot,startBoot通过call16_int调用0x19号中断
// Begin the boot process by invoking an int0x19 in 16bit mode.
void VISIBLE32FLAT
startBoot(void)
{
// Clear low-memory allocations (required by PMM spec).
memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR);
dprintf(3, "Jump to int19\n");
struct bregs br;
memset(&br, 0, sizeof(br));
br.flags = F_IF;
call16_int(0x19, &br);
}
_farcall16
_farcall16 -> stack_hop_back -> __stack_hop_back -> call16,其中func为_farcall16
void VISIBLE16
_farcall16(struct bregs *callregs, u16 callregseg)
{
if (need_hop_back()) { // 平坦模式为true,实模式为false
stack_hop_back(_farcall16, callregs, callregseg);
return;
}
ASSERT16();
asm volatile(
"calll __farcall16\n"
: "+a" (callregs), "+m" (*callregs), "+d" (callregseg)
:
: "ebx", "ecx", "esi", "edi", "cc", "memory");
}
#define stack_hop_back(func, eax, edx) ({ \
extern void _cfunc16_ ##func (void); \
__stack_hop_back((u32)(eax), (u32)(edx), _cfunc16_ ##func ); \
})
// Switch back to original caller's stack and call a function.
u32
__stack_hop_back(u32 eax, u32 edx, void *func)
{
if (!MODESEGMENT)
return call16(eax, edx, func);
if (!MODE16 || !on_extra_stack())
return ((u32 (*)(u32, u32))func)(eax, edx);
ASSERT16();
u16 bkup_ss;
u32 bkup_stack_pos, temp;
asm volatile(
// Backup stack_pos and current %ss/%esp
"movl %6, %4\n"
"movw %%ss, %w3\n"
"movl %%esp, %6\n"
// Restore original callers' %ss/%esp
"movl -4(%4), %5\n"
"movl %5, %%ss\n"
"movw %%ds:-8(%4), %%sp\n"
"movl %5, %%ds\n"
// Call func
"calll *%2\n"
// Restore %ss/%esp and stack_pos
"movw %w3, %%ds\n"
"movw %w3, %%ss\n"
"movl %6, %%esp\n"
"movl %4, %6"
: "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss)
, "=&r" (bkup_stack_pos), "=&r" (temp), "+m" (StackPos)
:
: "cc", "memory");
return eax;
}
call16跳转到transition16,进入实模式,其中edx保存call16 1f的地址
// Call a 16bit SeaBIOS function, restoring the mode from last call32().
static u32
call16(u32 eax, u32 edx, void *func)
{
ASSERT32FLAT();
if (getesp() > MAIN_STACK_MAX)
panic("call16 with invalid stack\n");
if (CONFIG_CALL32_SMM && Call16Data.method == C16_SMM)
return call16_smm(eax, edx, func);
extern void transition16big(void);
extern void transition16(void);
void *thunk = transition16;
if (Call16Data.method == C16_BIG || in_post())
thunk = transition16big;
func -= BUILD_BIOS_ADDR;
u32 stackseg = Call16Data.ss;
asm volatile(
// Transition to 16bit mode
" movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n"
" jmp *%%ecx\n" // ecx保存thunk,也就是transition16的地址
// Setup ss/esp and call func
ASM32_SWITCH16
"1:movl %2, %%ecx\n"
" shll $4, %2\n"
" movw %%cx, %%ss\n"
" subl %2, %%esp\n"
" movw %%cx, %%ds\n"
" movl %4, %%edx\n"
" movl %3, %%ecx\n" // ecx保存func,也就是_farcall16
" calll _cfunc16_call16_helper\n"
// Return to 32bit and restore esp
" movl $2f, %%edx\n"
" jmp transition32_nmi_off\n" // 回到保护模式
ASM32_BACK32
"2:addl %2, %%esp\n"
: "+a" (eax), "+c"(thunk), "+r"(stackseg)
: "r" (func), "r" (edx)
: "edx", "cc", "memory");
return eax;
}
transition16最后跳转到edx保存的地址,也就是call16 1f
// Place CPU into 16bit mode from 32bit mode.
// %edx = return location (in 16bit mode)
// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
DECLFUNC transition16
.global transition16big
.code32
transition16:
// Reset data segment limits
movl $SEG32_MODE16_DS, %ecx
movw %cx, %ds
movw %cx, %es
movw %cx, %ss
movw %cx, %fs
movw %cx, %gs
// Jump to 16bit mode
ljmpw $SEG32_MODE16_CS, $1f
transition16big:
movl $SEG32_MODE16BIG_DS, %ecx
movw %cx, %ds
movw %cx, %es
movw %cx, %ss
movw %cx, %fs
movw %cx, %gs
ljmpw $SEG32_MODE16BIG_CS, $1f
.code16
// Disable protected mode
1: movl %cr0, %ecx
andl $~CR0_PE, %ecx
movl %ecx, %cr0
// far jump to flush CPU queue after transition to real mode
ljmpw $SEG_BIOS, $2f
// restore IDT to normal real-mode defaults
2: lidtw %cs:rmode_IDT_info
// Clear segment registers
xorw %cx, %cx
movw %cx, %fs
movw %cx, %gs
movw %cx, %es
movw %cx, %ds
movw %cx, %ss // Assume stack is in segment 0
jmpl *%edx
从call16 1f继续往下执行,_cfunc16_call16_helper -> _farcall16,这是第二次调用_farcall16,这一次已经是实模式,因此_farcall16直接调用__farcall16
// 16bit handler code called from call16() / call16_smm()
u32 VISIBLE16
call16_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx))
{
u8 method = call32_post();
u32 ret = func(eax, edx); // func为_farcall16
call32_prep(method);
return ret;
}
__farcall16先push flags/CS/IP,再通过iretw指令pop IP/CS/flags,跳转到_farcall16的callregs->code
// Far call a 16bit function from 16bit mode with a specified cpu register state
// %eax = address of struct bregs, %edx = segment of struct bregs
// Clobbers: %e[bc]x, %e[ds]i, flags
DECLFUNC __farcall16
__farcall16:
// Save %edx/%eax, %ebp
pushl %ebp
pushl %eax
pushl %edx
// Setup for iretw call
movl %edx, %ds
pushw %cs
pushw $1f // return point
pushw BREGS_flags(%eax) // flags
pushl BREGS_code(%eax) // CS:IP
// Load calling registers and invoke call
RESTOREBREGS_DSEAX
iretw // XXX - just do a lcalll
1:
// Store flags, es, eax
pushfw
cli
cld
pushw %ds
pushl %eax
movw 0x08(%esp), %ds
movl 0x0c(%esp), %eax
SAVEBREGS_POP_DSEAX
popw BREGS_flags(%eax)
movw %ss, %cx
movw %cx, %ds // Restore %ds == %ss
// Remove %edx/%eax, restore %ebp
popl %edx
popl %eax
popl %ebp
retl
如果_farcall16的callregs->code对应的程序,在结尾恢复堆栈且执行ret指令,__farcall16从__farcall16 1f继续往下执行,依次返回_farcall16、_cfunc16_call16_helper,从_cfunc16_call16_helper继续往下执行,跳转到transition32_nmi_off,回到保护模式,继续依次返回call16、__stack_hop_back、stack_hop_back、_farcall16
call16_int
call16_int -> __call16_int,第二个参数是irq_trampoline_XX的地址,irq_trampoline_XX最终调用int指令
#define call16_int(nr, callregs) do { \
extern void irq_trampoline_ ##nr (void); \
__call16_int((callregs), (u32)&irq_trampoline_ ##nr ); \
} while (0)
// IRQ trampolines
.macro IRQ_TRAMPOLINE num
DECLFUNC irq_trampoline_0x\num
irq_trampoline_0x\num :
int $0x\num
lretw
.endm
IRQ_TRAMPOLINE 02
IRQ_TRAMPOLINE 05
IRQ_TRAMPOLINE 10
IRQ_TRAMPOLINE 13
IRQ_TRAMPOLINE 15
IRQ_TRAMPOLINE 16
IRQ_TRAMPOLINE 18
IRQ_TRAMPOLINE 19
IRQ_TRAMPOLINE 1b
IRQ_TRAMPOLINE 1c
IRQ_TRAMPOLINE 4a
__call16_int -> _farcall16,传给_farcall16的callregs->code是irq_trampoline_XX的CS/IP
// Invoke a 16bit software interrupt.
void
__call16_int(struct bregs *callregs, u16 offset)
{
callregs->code.offset = offset; // code.offset保存irq_trampoline_XX的地址
if (!MODESEGMENT) {
callregs->code.seg = SEG_BIOS;
_farcall16((void*)callregs - Call16Data.ss * 16, Call16Data.ss);
return;
}
callregs->code.seg = GET_SEG(CS); // code.seg保存GET_SEG(CS)
_farcall16(callregs, GET_SEG(SS));
}
irq_trampoline_XX在结尾恢复堆栈且执行lretw指令,因此依次返回_farcall16、__call16_int、call16_int
0x19号中断
http://vitaly_filatov.tripod.com/ng/asm/asm_001.12.html
SeaBIOS的0x19号中断服务程序是handle_19
SET_IVT(0x19, FUNC16(entry_19_official));
entry_19_official:
jmp entry_19
entry_19:
ENTRY_INTO32 _cfunc32flat_handle_19
handle_19 -> do_boot -> boot_disk,boot_disk先通过call16_int调用0x13号中断,将MBR加载到0x7C00,再调用call_boot_entry
// Boot from a disk (either floppy or harddrive)
static void
boot_disk(u8 bootdrv, int checksig)
{
u16 bootseg = 0x07c0;
// Read sector
struct bregs br;
memset(&br, 0, sizeof(br));
br.flags = F_IF;
br.dl = bootdrv;
br.es = bootseg; // es = 0x07c0,bx = 0x0000
br.ah = 2;
br.al = 1;
br.cl = 1;
call16_int(0x13, &br);
if (br.flags & F_CF) {
printf("Boot failed: could not read the boot disk\n\n");
return;
}
if (checksig) {
struct mbr_s *mbr = (void*)0;
if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
printf("Boot failed: not a bootable disk\n\n");
return;
}
}
tpm_add_bcv(bootdrv, MAKE_FLATPTR(bootseg, 0), 512);
/* Canonicalize bootseg:bootip */
u16 bootip = (bootseg & 0x0fff) << 4; // bootip = 0x7c00
bootseg &= 0xf000; // bootseg = 0x0000
call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
}
0x13号中断
http://vitaly_filatov.tripod.com/ng/asm/asm_024.3.html
SeaBIOS的0x13号中断服务程序是handle_13
SET_IVT(0x13, FUNC16(entry_13_official));
entry_13_official:
jmp entry_13
DECL_IRQ_ENTRY_ARG 13
.macro DECL_IRQ_ENTRY_ARG num
DECLFUNC entry_\num
IRQ_ENTRY_ARG \num
.endm
.macro IRQ_ENTRY_ARG num
.global entry_\num
entry_\num : // entry_13
pushl $ handle_\num // handle_13
jmp irqentry_arg
.endm
irqentry_arg:
ENTRY_ARG_ST
iretw
.macro ENTRY_ARG_ST
cli
cld
pushl %ecx
pushl %edx
pushl %ebx
pushl %ebp
pushl %esi
pushl %edi
pushw %es
pushw %ds
movw %ss, %cx // Move %ss to %ds
movw %cx, %ds
movl %esp, %ebx // Backup %esp, then zero high bits
movzwl %sp, %esp
movl 28(%esp), %ecx // Get calling function
movl %eax, 28(%esp) // Save %eax
movl %esp, %eax // First arg is pointer to struct bregs
calll *%ecx // ecx保存28(%esp),也就是handle_13的地址
movl %ebx, %esp // Restore %esp (including high bits)
POPBREGS
.endm
.macro POPBREGS
popw %ds
popw %es
popl %edi
popl %esi
popl %ebp
popl %ebx
popl %edx
popl %ecx
popl %eax
.endm
handle_13 -> handle_legacy_disk -> disk_13 -> disk_1302 -> basic_access
// Perform read/write/verify using old-style chs accesses
static void noinline
basic_access(struct bregs *regs, struct drive_s *drive_fl, u16 command)
{
struct disk_op_s dop;
dop.drive_fl = drive_fl;
dop.command = command;
u8 count = regs->al;
u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300);
u16 sector = regs->cl & 0x3f;
u16 head = regs->dh;
if (count > 128 || count == 0 || sector == 0) {
warn_invalid(regs);
disk_ret(regs, DISK_RET_EPARAM);
return;
}
dop.count = count;
struct chs_s chs = getLCHS(drive_fl);
u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector;
// sanity check on cyl heads, sec
if (cylinder >= nlc || head >= nlh || sector > nls) {
warn_invalid(regs);
disk_ret(regs, DISK_RET_EPARAM);
return;
}
// translate lchs to lba
dop.lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nls)
+ (u32)sector - 1);
dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
int status = send_disk_op(&dop);
regs->al = dop.count;
disk_ret(regs, status);
}
call_boot_entry
call_boot_entry -> farcall16 -> _farcall16,传给_farcall16的callregs->code是0x0000/0x7C00,跳转到0x7C00
static void
call_boot_entry(struct segoff_s bootsegip, u8 bootdrv)
{
dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset);
struct bregs br;
memset(&br, 0, sizeof(br));
br.flags = F_IF;
br.code = bootsegip; // code保存0x0000/0x7c00
// Set the magic number in ax and the boot drive in dl.
br.dl = bootdrv;
br.ax = 0xaa55;
farcall16(&br);
}
void
farcall16(struct bregs *callregs)
{
call16_override(0);
_farcall16(callregs, 0);
}
SeaBIOS跳转到0x7C00,将控制权交给MBR,不再返回(0x13号中断返回,0x19号中断不返回)
MBR(boot.img)
查看boot.img的内容:
hexdump -C /boot/grub2/i386-pc/boot.img
00000000 eb 63 90 00 00 00 00 00 00 00 00 00 00 00 00 00 |.c..............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000050 00 00 00 00 00 00 00 00 00 00 00 80 01 00 00 00 |................|
00000060 00 00 00 00 ff fa eb 05 f6 c2 80 74 05 f6 c2 70 |...........t...p|
00000070 74 02 b2 80 ea 79 7c 00 00 31 c0 8e d8 8e d0 bc |t....y|..1......|
00000080 00 20 fb a0 64 7c 3c ff 74 02 88 c2 52 be 05 7c |. ..d|<.t...R..||
00000090 b4 41 bb aa 55 cd 13 5a 52 72 3d 81 fb 55 aa 75 |.A..U..ZRr=..U.u|
000000a0 37 83 e1 01 74 32 31 c0 89 44 04 40 88 44 ff 89 |7...t21..D.@.D..|
000000b0 44 02 c7 04 10 00 66 8b 1e 5c 7c 66 89 5c 08 66 |D.....f..\|f.\.f|
000000c0 8b 1e 60 7c 66 89 5c 0c c7 44 06 00 70 b4 42 cd |..`|f.\..D..p.B.|
000000d0 13 72 05 bb 00 70 eb 76 b4 08 cd 13 73 0d 5a 84 |.r...p.v....s.Z.|
000000e0 d2 0f 83 de 00 be 85 7d e9 82 00 66 0f b6 c6 88 |.......}...f....|
000000f0 64 ff 40 66 89 44 04 0f b6 d1 c1 e2 02 88 e8 88 |d.@f.D..........|
00000100 f4 40 89 44 08 0f b6 c2 c0 e8 02 66 89 04 66 a1 |.@.D.......f..f.|
00000110 60 7c 66 09 c0 75 4e 66 a1 5c 7c 66 31 d2 66 f7 |`|f..uNf.\|f1.f.|
00000120 34 88 d1 31 d2 66 f7 74 04 3b 44 08 7d 37 fe c1 |4..1.f.t.;D.}7..|
00000130 88 c5 30 c0 c1 e8 02 08 c1 88 d0 5a 88 c6 bb 00 |..0........Z....|
00000140 70 8e c3 31 db b8 01 02 cd 13 72 1e 8c c3 60 1e |p..1......r...`.|
00000150 b9 00 01 8e db 31 f6 bf 00 80 8e c6 fc f3 a5 1f |.....1..........|
00000160 61 ff 26 5a 7c be 80 7d eb 03 be 8f 7d e8 34 00 |a.&Z|..}....}.4.|
00000170 be 94 7d e8 2e 00 cd 18 eb fe 47 52 55 42 20 00 |..}.......GRUB .|
00000180 47 65 6f 6d 00 48 61 72 64 20 44 69 73 6b 00 52 |Geom.Hard Disk.R|
00000190 65 61 64 00 20 45 72 72 6f 72 0d 0a 00 bb 01 00 |ead. Error......|
000001a0 b4 0e cd 10 ac 3c 00 75 f4 c3 00 00 00 00 00 00 |.....<.u........|
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 24 12 |..............$.|
000001c0 0f 09 00 52 be bd 7d 31 c0 cd 13 46 8a 0c 84 c9 |...R..}1...F....|
000001d0 75 0f be da 7d e8 cc ff eb 96 46 6c 6f 70 70 79 |u...}.....Floppy|
000001e0 00 bb 00 70 8e c3 31 db b8 01 02 b5 00 b6 00 cd |...p..1.........|
000001f0 13 72 d4 b6 01 b5 4f e9 f1 fe 00 00 00 00 55 aa |.r....O.......U.|
00000200
boot.img位于硬盘的第一个扇区,将硬盘的第一个扇区dump出来:
dd if=/dev/sda of=mbr.img bs=512 count=1
查看mbr.img的内容:
hexdump -C mbr.img
00000000 eb 63 90 00 00 00 00 00 00 00 00 00 00 00 00 00 |.c..............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000050 00 00 00 00 00 00 00 00 00 00 00 80 00 08 00 00 |................|
00000060 00 00 00 00 ff fa 90 90 f6 c2 80 74 05 f6 c2 70 |...........t...p|
00000070 74 02 b2 80 ea 79 7c 00 00 31 c0 8e d8 8e d0 bc |t....y|..1......|
00000080 00 20 fb a0 64 7c 3c ff 74 02 88 c2 52 be 05 7c |. ..d|<.t...R..||
00000090 b4 41 bb aa 55 cd 13 5a 52 72 3d 81 fb 55 aa 75 |.A..U..ZRr=..U.u|
000000a0 37 83 e1 01 74 32 31 c0 89 44 04 40 88 44 ff 89 |7...t21..D.@.D..|
000000b0 44 02 c7 04 10 00 66 8b 1e 5c 7c 66 89 5c 08 66 |D.....f..\|f.\.f|
000000c0 8b 1e 60 7c 66 89 5c 0c c7 44 06 00 70 b4 42 cd |..`|f.\..D..p.B.|
000000d0 13 72 05 bb 00 70 eb 76 b4 08 cd 13 73 0d 5a 84 |.r...p.v....s.Z.|
000000e0 d2 0f 83 de 00 be 85 7d e9 82 00 66 0f b6 c6 88 |.......}...f....|
000000f0 64 ff 40 66 89 44 04 0f b6 d1 c1 e2 02 88 e8 88 |d.@f.D..........|
00000100 f4 40 89 44 08 0f b6 c2 c0 e8 02 66 89 04 66 a1 |.@.D.......f..f.|
00000110 60 7c 66 09 c0 75 4e 66 a1 5c 7c 66 31 d2 66 f7 |`|f..uNf.\|f1.f.|
00000120 34 88 d1 31 d2 66 f7 74 04 3b 44 08 7d 37 fe c1 |4..1.f.t.;D.}7..|
00000130 88 c5 30 c0 c1 e8 02 08 c1 88 d0 5a 88 c6 bb 00 |..0........Z....|
00000140 70 8e c3 31 db b8 01 02 cd 13 72 1e 8c c3 60 1e |p..1......r...`.|
00000150 b9 00 01 8e db 31 f6 bf 00 80 8e c6 fc f3 a5 1f |.....1..........|
00000160 61 ff 26 5a 7c be 80 7d eb 03 be 8f 7d e8 34 00 |a.&Z|..}....}.4.|
00000170 be 94 7d e8 2e 00 cd 18 eb fe 47 52 55 42 20 00 |..}.......GRUB .|
00000180 47 65 6f 6d 00 48 61 72 64 20 44 69 73 6b 00 52 |Geom.Hard Disk.R|
00000190 65 61 64 00 20 45 72 72 6f 72 0d 0a 00 bb 01 00 |ead. Error......|
000001a0 b4 0e cd 10 ac 3c 00 75 f4 c3 00 00 00 00 00 00 |.....<.u........|
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001c0 01 00 ee fe ff ff 01 00 00 00 af 1a c8 6f 00 00 |.............o..|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200
diff boot.img(左边)和mbr.img(右边)的前446个字节
可以看到0x5c-0x5d被改成0008,0x66-0x67被改成9090,两边不一致是因为grub在将boot.img写入MBR时修改了部分字段
我们可以在grub源码中找到修改的地方:
1、0x5c-0x5d被改成0008
// grub/util/setup.c
write_rootdev
kernel_sector = (boot_img + GRUB_BOOT_MACHINE_KERNEL_SECTOR);
/* FIXME: can this be skipped? */
*boot_drive = 0xFF;
grub_set_unaligned64 (kernel_sector, grub_cpu_to_le64 (first_sector));
可以看到boot_img + GRUB_BOOT_MACHINE_KERNEL_SECTOR开始的8个字节被改成first_sector(core.img所在的第一个扇区),其中GRUB_BOOT_MACHINE_KERNEL_SECTOR为0x5c
#define GRUB_BOOT_MACHINE_KERNEL_SECTOR 0x5c
对于MBR分区格式,boot.img放在MBR(0号扇区),core.img放在MBR后面的扇区(从1号扇区开始,也就是左边改之前的0x0000000000000001)
对于GPT分区格式,boot.img放在MBR(0号扇区),MBR后面的扇区被GPT占用,不能再放core.img,因此core.img被移到其它地方(在我的机器上是从2048号扇区开始,也就是右边改之后的0x0000000000000800)
core.img所在的分区称为BIOS boot partition:https://en.wikipedia.org/wiki/BIOS_boot_partition
我们可以通过fdisk/parted命令查看硬盘的分区
$fdisk -l
WARNING: fdisk GPT support is currently new, and therefore in an experimental phase. Use at your own discretion.
Disk /dev/sda: 960.2 GB, 960197124096 bytes, 1875385008 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk label type: gpt
Disk identifier: A45867DB-50B2-4A45-A15F-103A3073A3EC
# Start End Size Type Name
1 2048 10239 4M BIOS boot u0SLb
2 10240 2107391 1G EFI System Znxzm
3 2107392 106964991 50G Microsoft basic D97K4
4 106964992 111159295 2G Microsoft basic mLFxa
5 111159296 1875384319 841.3G Microsoft basic muB2A
$parted /dev/sda
GNU Parted 3.1
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) p
Model: ATA INTEL SSDSC2KB96 (scsi)
Disk /dev/sda: 960GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1049kB 5243kB 4194kB u0SLb bios_grub
2 5243kB 1079MB 1074MB ext4 Znxzm boot
3 1079MB 54.8GB 53.7GB ext4 D97K4
4 54.8GB 56.9GB 2147MB linux-swap(v1) mLFxa
5 56.9GB 960GB 903GB ext4 muB2A
(parted)
其中Type为BIOS boot(fdisk输出结果)/Flags为bios_grub(parted输出结果)的分区就是BIOS boot partition,可以看到它的开始扇区确实是2048
2、0x66-0x67被改成9090
// grub/util/setup.c
SETUP
boot_drive_check = (grub_uint8_t *) (boot_img
+ GRUB_BOOT_MACHINE_DRIVE_CHECK);
/* Copy the possible DOS BPB. */
memcpy (boot_img + GRUB_BOOT_MACHINE_BPB_START,
tmp_img + GRUB_BOOT_MACHINE_BPB_START,
GRUB_BOOT_MACHINE_BPB_END - GRUB_BOOT_MACHINE_BPB_START);
/* If DEST_DRIVE is a hard disk, enable the workaround, which is
for buggy BIOSes which don't pass boot drive correctly. Instead,
they pass 0x00 or 0x01 even when booted from 0x80. */
if (!allow_floppy && !grub_util_biosdisk_is_floppy (dest_dev->disk))
{
/* Replace the jmp (2 bytes) with double nop's. */
boot_drive_check[0] = 0x90;
boot_drive_check[1] = 0x90;
可以看到boot_img + GRUB_BOOT_MACHINE_DRIVE_CHECK开始的2个字节被改成9090了,其中GRUB_BOOT_MACHINE_DRIVE_CHECK为0x66
#define GRUB_BOOT_MACHINE_DRIVE_CHECK 0x66
boot.img对应的代码为grub/grub-core/boot/i386/pc/boot.S
_start
boot.S的入口是_start,跳转到LOCAL(after_BPB)
.globl _start, start;
_start:
start:
/*
* _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00
*/
/*
* Beginning of the sector is compatible with the FAT/HPFS BIOS
* parameter block.
*/
jmp LOCAL(after_BPB) // eb 63 jmp 0x65
nop /* do I care about this ??? */ // 90 nop
/*
* This space is for the BIOS parameter block!!!! Don't change
* the first jump, nor start the code anywhere but right after
* this area.
*/
.org GRUB_BOOT_MACHINE_BPB_START // 0x3
.org 4
scratch
BPB
BPB(BIOS Parameter Block,BIOS参数块)的范围是0x4到0x59,其中scratch包含DAP(Disk Address Packet,磁盘地址数据包)
.macro scratch
/* scratch space */
mode:
.byte 0
disk_address_packet:
sectors:
.long 0
heads:
.long 0
cylinders:
.word 0
sector_start:
.byte 0
head_start:
.byte 0
cylinder_start:
.word 0
/* more space... */
.endm
0x5a-0x5b保存LOCAL(kernel_address)、0x5c-0x5f保存LOCAL(kernel_sector)、0x60-0x63保存LOCAL(kernel_sector_high)、0x64保存boot_drive
.org GRUB_BOOT_MACHINE_BPB_END // 0x5a
/*
* End of BIOS parameter block.
*/
LOCAL(kernel_address):
.word GRUB_BOOT_MACHINE_KERNEL_ADDR // 0x5a-0x5b是00,80,0x8000
.org GRUB_BOOT_MACHINE_KERNEL_SECTOR // 0x5c
LOCAL(kernel_sector):
.long 1 // 0x5c-0x5d被grub改成00,08,00000800
LOCAL(kernel_sector_high):
.long 0
.org GRUB_BOOT_MACHINE_BOOT_DRIVE // 0x64
boot_drive:
.byte 0xff /* the disk to load kernel from */
/* 0xff means use the boot drive */
LOCAL(after_BPB)
从LOCAL(after_BPB)往下执行,DL=0x80,跳转到real_start
LOCAL(after_BPB):
/* general setup */
cli /* we're not safe here! */
/*
* This is a workaround for buggy BIOSes which don't pass boot
* drive correctly. If GRUB is installed into a HDD, check if
* DL is masked correctly. If not, assume that the BIOS passed
* a bogus value and set DL to 0x80, since this is the only
* possible boot drive. If GRUB is installed into a floppy,
* this does nothing (only jump).
*/
.org GRUB_BOOT_MACHINE_DRIVE_CHECK // 0x66
boot_drive_check:
jmp 3f /* grub-setup may overwrite this jump */ // 0x66-0x67被grub改成90,90
testb $0x80, %dl
jz 2f
3:
/* Ignore %dl different from 0-0x0f and 0x80-0x8f. */
testb $0x70, %dl
jz 1f
2:
movb $0x80, %dl
1:
/*
* ljmp to the next instruction because some bogus BIOSes
* jump to 07C0:0000 instead of 0000:7C00.
*/
ljmp $0, $real_start
从real_start往下执行,调用0x13号中断,判断是否支持LBA
real_start:
/* set up %ds and %ss as offset from 0 */
xorw %ax, %ax
movw %ax, %ds // ds = 0
movw %ax, %ss // ss = 0
/* set up the REAL stack */
movw $GRUB_BOOT_MACHINE_STACK_SEG, %sp // sp = 0x2000
sti /* we're safe again */
/*
* Check if we have a forced disk reference here
*/
movb boot_drive, %al
cmpb $0xff, %al
je 1f
movb %al, %dl
1:
/* save drive reference first thing! */
pushw %dx
/* print a notification message on the screen */
MSG(notification_string)
/* set %si to the disk address packet */
movw $disk_address_packet, %si // si指向DAP
/* check if LBA is supported */
movb $0x41, %ah
movw $0x55aa, %bx
int $0x13 // 判断是否支持LBA
/*
* %dl may have been clobbered by INT 13, AH=41H.
* This happens, for example, with AST BIOS 1.04.
*/
popw %dx
pushw %dx
LOCAL(lba_mode)
对于LBA模式,调用0x13号中断,将2048号扇区(diskboot.img)加载到0x70000,跳转到LOCAL(copy_buffer)
LOCAL(lba_mode):
xorw %ax, %ax
movw %ax, 4(%si) // offset为0
incw %ax
/* set the mode to non-zero */
movb %al, -1(%si)
/* the blocks */
movw %ax, 2(%si) // 扇区数为1
/* the size and the reserved byte */
movw $0x0010, (%si) // size为0x10
/* the absolute address */
movl LOCAL(kernel_sector), %ebx
movl %ebx, 8(%si) // 0x00000800
movl LOCAL(kernel_sector_high), %ebx
movl %ebx, 12(%si) // 0x00000000,起始扇区号为0x800 = 2048
/* the segment of buffer address */
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si) // segment为0x7000,segment:offset为0x70000
/*
* BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
* Call with %ah = 0x42
* %dl = drive number
* %ds:%si = segment:offset of disk address packet
* Return:
* %al = 0x0 on success; err code on failure
*/
movb $0x42, %ah
int $0x13 // 将2048号扇区(diskboot.img)加载到0x70000
/* LBA read is not supported, so fallback to CHS. */
jc LOCAL(chs_mode)
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx // bx = 0x7000
jmp LOCAL(copy_buffer)
LOCAL(copy_buffer)
从LOCAL(copy_buffer)往下执行,将0x70000复制到0x8000,跳转到0x8000
LOCAL(copy_buffer):
/*
* We need to save %cx and %si because the startup code in
* kernel uses them without initializing them.
*/
pusha // 8个通用寄存器(ax/bx/cx/dx/sp/bp/si/di)依次入栈
pushw %ds
movw $0x100, %cx // cx = 256
movw %bx, %ds // ds = 0x7000
xorw %si, %si // si = 0
movw $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di // di = 0x8000
movw %si, %es // es = 0
cld // DF = 0
rep
movsw // 将DS:SI(0x70000)复制到ES:DI(0x8000),重复执行256次,每次1个word,共1个扇区
popw %ds
popa // 8个通用寄存器(di/si/bp/sp/dx/cx/bx/ax)依次出栈
/* boot kernel */
jmp *(LOCAL(kernel_address)) // 跳转到0x8000
其中GRUB_BOOT_MACHINE_KERNEL_ADDR为0x8000
#define GRUB_BOOT_MACHINE_KERNEL_ADDR (GRUB_BOOT_MACHINE_KERNEL_SEG << 4)
#define GRUB_BOOT_MACHINE_KERNEL_SEG GRUB_OFFSETS_CONCAT (GRUB_BOOT_, GRUB_MACHINE, _KERNEL_SEG)
#define GRUB_BOOT_I386_PC_KERNEL_SEG 0x800
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)