-
-
[原创]Linux内核基础之boot
-
发表于: 2020-10-3 11:18 6889
-
title: Linux内核基础学习笔记
date: 2020-07-16 14:57:02
tags:
categories: kernel
1.按下电源开关,主板发送信号到电源,电源准备合适的电量,向主板发送备妥信号,启动CPU。
2.CPU启动,寄存器复位。
3.CPU此时工作在实模式,采用分段管理内存,无分页, Cache、TLB(Translation Lookup Table)、BLB(Branch Target Buffer)这三个部件的内容被清空(Invalidate)。
4.根据复位后的cs:ip此时逻辑地址为:0xffff0000:0xfff0
即4GB-16字节。指向第一条代码:reset vector。此时这个地址是被映射到ROM中的。
5.reset vector包括一个FAR jump跳到BIOS的入口。
6.BIOS做初始化与检查。然后寻找引导程序。对于硬盘,找MBR
7.BIOS将控制权交给引导程序。一般采用GRUB2引导
8.引导完成,调用grub_main
初始化,置于normal模式。
9.grub_normal_execute
显示可用操作系统
10.操作系统选定,grub_menu_execute_entry
开始执行,它将调用 GRUB 的 boot 命令,来引导被选中的操作系统。
11.bootloader完毕,交还控制权给kernel,kernel代码从以下开始执行
1.内核设置的起点是arch/x86/boot/header.S
中的_start
函数开始
2._start
函数做一个短跳转到start_of_setup - 1f
1.main.c首先调用copy_boot_params();
拷贝启动参数到boot_params.hdr
通过调用:memcpy(&boot_params.hdr, &hdr, sizeof(hdr));
2.接下来进行控制台初始化console_init();
使用0x10中断输出一些字符。
3.接下来做堆初始化init_heap();
计算堆的位置
4.然后调用validate_cpu()
检查cpu类型是否与kernel相合适,是否可以正常启动。当不满足cpu等级或一些特定的feature时不许启动
5.告诉BIOS我们的cpu模式
6.内存布局探测:detect_memory();
调用detect_memory_e820();
循环探测,最终信息存在e820_entries;
7.键盘初始化:keyboard_init();
用0x16中断获取键盘状态。
8.一系列系统参数查询:机器型号,BIOS版本,高级电源管理等
9.set_video();
设置显示模式。
10.go_to_protected_mode();
进入保护模式之前最后的准备(所有的x86 CPU都是在实模式下引导,来确保传统操作系统的兼容性。为了使用保护模式的特性,要由程序主动地切换到保护模式。在现今的电脑上,这种切换通常是操作系统在引导时候完成的第一件任务。当CPU在保护模式下运行时,可以使用虚拟86模式来运行为实模式设计的代码。)
首先调用realmode_switch_hook();
如果发现real_mode的hook函数,那么调用他。else的话清楚中断标志IF(禁止外部中断),然后禁止NMI中断(非可屏蔽中断比如时钟中断)。最后调用io_delay
等待操作完成
然后是enable_a20()
激活A20总线,之后使用reset_coprocessor();
与mask_all_interrupts();
复位数字协处理器、屏蔽中断控制器的所有中断、和主中断控制器上除IRQ2以外的所有中断(IRQ2是主中断控制器上的级联中断,所有从中断控制器的中断将通过这个级联中断报告给 CPU )
至此,所有进入保护模式之前的准备工作已经完成,给出整体的代码
1.首先是setup_idt();
用来设置中断描述符表(IDT)
lidtl将为空的null_idt载入寄存器IDT
2.接下来setup_gdt();
来设置全局描述符表即GDT表,其中,定义了boot_gdt[]
数组,这个数组中的内容就是我们要传给GDTR的段描述符的信息。
其中GDT_ENTRY是一个宏定义,传入三个参数(标志,基地址,段长度):
标志字段二进制展开后每一位都有自己的含义。之后获取gdt长度,以及指针,最后载入GDTR寄存器。
3.最后调用protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4));
完成从实模式到保护模式的跳转。
接受两个参数:保护模式进入地址,bootparams结构的地址。
跳转之后我们就在保护模式下执行以下代码了
1.jmpl *%eax # Jump to the 32-bit entrypoint
时eax存储的是32位的入口点。
2.到达32位入口点,我们可以看到此时的目录:arch/x86/boot/compressed/head_64.S
其中的compressed,因为bzimage 是由 vmlinux + 头文件 + 内核启动代码 被 gzip 压缩之后获得的。之前的属于内核启动代码。而head_64.S做内核解压前的准备。
其中HEAD代表```#define HEAD .section ".head.text","ax"```也就是说这个段是可执行的。
3.首先调用cld清空DF DF与串操作
4.通过KEEP_SEGMENTS
标记来给段寄存器做正确的赋值。
5.计算我们代码和编译运行之间的位置偏差。通过:定义一个标签并且跳转到它,然后把栈顶抛出到一个寄存器中。
esi指向bootparams
结构体,把结构体中scratch+4的地址放进esp制造出一个4字节临时栈。然后通过call 1f。此时1f标签的地址在栈顶,再把他pop到ebp然后ebp-1b就得到了startup_32
的加载地址。
6.先找到boot_stack_end
的实际地址,然后通过call verify_cpu
之后test eax中的返回值,验证cpu是否支持长模式和SSE。如果不支持,就hlt掉。
7.计算内核解压缩地址,此时ebp中的是startup_32
的实际物理地址,当CONFIG_RELOCATABLE打开时,我们将ebp放在ebx中,然后做对齐(2M)然后与$LOAD_PHYSICAL_ADDR(对齐后内核加载位置的物理地址)比较,最后给startup_32
加上偏移获得解压缩地址。结束后,ebp包含了加载时的地址,ebx包含了内核解压缩的目标地址。
8.更新全局描述符表。全局描述符表 保存在 48位 GDTR-全局描述符表寄存器 中,由两个部分组成:
GDT基地址(32位)
gdt偏移量整体结构如下
9.打开PAE模式
10.初期页表初始化,建立初期的4G启动页表。linux内核使用4级页表,一般建立六个页表,每张表4k
其中pgtable定义如下,大小24k
开始构建4级页表
其中0x1007是四级页表的大小4096+7(页表项标记PRESENT+RW+USER
)最后我们把第一个PDP(页目录指针)项地址写入PML4
11.在PDP表(页目录指针表、三级页表)建立4个2级页表(Page Directory)他们都带有PRESENT+RW+USE 标记
12.建立2048个2M页表项
最终我们拥有了一个4G表,映射了4G大小的内存。
13.最后将PML4的地址放入cr3
14.设置MSR中的EFER.LME(Extended Feature Enable Register)标记为 0xC0000080
15.切换向长模式
之后打开分页与保护模式
最后执行lret,因为startup_64已经入栈,这里就跳向了64位模式的起始位置。
至此正式从32位保护模式进入64位长模式
1.进入64位长模式后首先设置段寄存器的值,然后是计算内核编译时的位置和它被加载的位置的差(类似32位)rbp
最后包含解压后内核的起始地址。rbx
包含用于解压的重定位内核代码的地址。
2.设置栈指针与标志寄存器重置
3.复制压缩的内核到buffer
压缩了的代码镜像存放在这份复制了的代码(从startup_32到当前的代码)和解压了的代码之间。
最后跳转到.text解压
4.进入.text中首先清空bss节,然后调用:
进行内核的解压。
5.在解压函数中会初始化控制台,然后拿heap的位置。下一步调用:
6.choose_kernel_location
分析如下:
通过调用find_random_addr(choice, output_size);
查找一个随机地址。
7.返回找到满足要求的随机地址。并且验证地址合法性。然后调用decompress(input_data, input_len, NULL, NULL, output, NULL, error);
进行解压,其具体实现取决于选择什么算法。
8.最后调用的两个函数是:parse_elf(output);
与handle_relocations(output, output_len);
他们的作用是把解压后的内核移动到正确的位置。Linux内核就像是一个ELF可执行文件,我们进行原地解压,然后移动可加载段到正确的地址。
9.parse_elf(output);
核心如下:
取内核的头,检查ELF签名标志。
12.解压后的内核部分定义在:arch/x86/kernel/head_64.S
注意与arch/x86/boot/compressed/head_64.S
是不一样的。
最终的startup_64
就是__START_KERNEL;
即(不考虑kaslr):
至此:我们进入了64位长模式,内核解压与重定位完成,正式启动,进入内核!
Linux 内核揭密
Linux下的lds链接脚本详解
ELF文件格式分析
e820与kernel物理内存映射
Linux四级页表(x64)
```
IP
0xfff0
CS selector
0xf000
CS base
0xffff0000
```
```
IP
0xfff0
CS selector
0xf000
CS base
0xffff0000
```
0x1000
+
X
+
sizeof(KernelBootSector)
+
1
/
/
X 是 kernel bootsector 被引导入内存的位置
0x1000
+
X
+
sizeof(KernelBootSector)
+
1
/
/
X 是 kernel bootsector 被引导入内存的位置
.globl _start
_start:
# Explicitly enter this as bytes, or the assembler
# tries to generate a 3-byte jump here, which causes
# everything else to push off to the wrong offset.
.byte
0xeb
# short (2-byte) jump
.byte start_of_setup
-
1f
.globl _start
_start:
# Explicitly enter this as bytes, or the assembler
# tries to generate a 3-byte jump here, which causes
# everything else to push off to the wrong offset.
.byte
0xeb
# short (2-byte) jump
.byte start_of_setup
-
1f
.section
".entrytext"
,
"ax"
start_of_setup:
# Force %es = %ds
movw
%
ds,
%
ax
movw
%
ax,
%
es
cld
movw
%
ss,
%
dx
cmpw
%
ax,
%
dx
# %ds == %ss?
movw
%
sp,
%
dx
je
2f
# -> assume %sp is reasonably set
# Invalid %ss, make up a new stack
movw $_end,
%
dx
testb $CAN_USE_HEAP, loadflags
jz
1f
movw heap_end_ptr,
%
dx
1
: addw $STACK_SIZE,
%
dx
jnc
2f
xorw
%
dx,
%
dx
# Prevent wraparound
2
:
# Now %dx should point to the end of our stack space
andw $~
3
,
%
dx
# dword align (might as well...)
jnz
3f
movw $
0xfffc
,
%
dx
# Make sure we're not zero
3
: movw
%
ax,
%
ss
movzwl
%
dx,
%
esp
# Clear upper half of %esp
sti
# Now we should have a working stack
# We will have entered with %cs = %ds+0x20, normalize %cs so
# it is on par with the other segments.
pushw
%
ds
pushw $
6f
lretw
6
:
# Check signature at end of setup
cmpl $
0x5a5aaa55
, setup_sig
jne setup_bad
# Zero the bss
movw $__bss_start,
%
di
movw $_end
+
3
,
%
cx
xorl
%
eax,
%
eax
subw
%
di,
%
cx
shrw $
2
,
%
cx
rep; stosl
# Jump to C code (should not return)
calll main
.section
".entrytext"
,
"ax"
start_of_setup:
# Force %es = %ds
movw
%
ds,
%
ax
movw
%
ax,
%
es
cld
movw
%
ss,
%
dx
cmpw
%
ax,
%
dx
# %ds == %ss?
movw
%
sp,
%
dx
je
2f
# -> assume %sp is reasonably set
# Invalid %ss, make up a new stack
movw $_end,
%
dx
testb $CAN_USE_HEAP, loadflags
jz
1f
movw heap_end_ptr,
%
dx
1
: addw $STACK_SIZE,
%
dx
jnc
2f
xorw
%
dx,
%
dx
# Prevent wraparound
2
:
# Now %dx should point to the end of our stack space
andw $~
3
,
%
dx
# dword align (might as well...)
jnz
3f
movw $
0xfffc
,
%
dx
# Make sure we're not zero
3
: movw
%
ax,
%
ss
movzwl
%
dx,
%
esp
# Clear upper half of %esp
sti
# Now we should have a working stack
# We will have entered with %cs = %ds+0x20, normalize %cs so
# it is on par with the other segments.
pushw
%
ds
pushw $
6f
lretw
6
:
# Check signature at end of setup
cmpl $
0x5a5aaa55
, setup_sig
jne setup_bad
# Zero the bss
movw $__bss_start,
%
di
movw $_end
+
3
,
%
cx
xorl
%
eax,
%
eax
subw
%
di,
%
cx
shrw $
2
,
%
cx
rep; stosl
# Jump to C code (should not return)
calll main
.globl hdr
hdr:
setup_sects: .byte
0
/
*
Filled
in
by build.c
*
/
root_flags: .word ROOT_RDONLY
syssize: .
long
0
/
*
Filled
in
by build.c
*
/
ram_size: .word
0
/
*
Obsolete
*
/
vid_mode: .word SVGA_MODE
root_dev: .word
0
/
*
Filled
in
by build.c
*
/
boot_flag: .word
0xAA55
.globl hdr
hdr:
setup_sects: .byte
0
/
*
Filled
in
by build.c
*
/
root_flags: .word ROOT_RDONLY
syssize: .
long
0
/
*
Filled
in
by build.c
*
/
ram_size: .word
0
/
*
Obsolete
*
/
vid_mode: .word SVGA_MODE
root_dev: .word
0
/
*
Filled
in
by build.c
*
/
boot_flag: .word
0xAA55
static void init_heap(void)
{
char
*
stack_end;
if
(boot_params.hdr.loadflags & CAN_USE_HEAP) {
asm(
"leal %P1(%%esp),%0"
:
"=r"
(stack_end) :
"i"
(
-
STACK_SIZE));
heap_end
=
(char
*
)
((size_t)boot_params.hdr.heap_end_ptr
+
0x200
);
if
(heap_end > stack_end)
heap_end
=
stack_end;
}
else
{
/
*
Boot protocol
2.00
only, no heap available
*
/
puts(
"WARNING: Ancient bootloader, some functionality "
"may be limited!\n"
);
}
}
static void init_heap(void)
{
char
*
stack_end;
if
(boot_params.hdr.loadflags & CAN_USE_HEAP) {
asm(
"leal %P1(%%esp),%0"
:
"=r"
(stack_end) :
"i"
(
-
STACK_SIZE));
heap_end
=
(char
*
)
((size_t)boot_params.hdr.heap_end_ptr
+
0x200
);
if
(heap_end > stack_end)
heap_end
=
stack_end;
}
else
{
/
*
Boot protocol
2.00
only, no heap available
*
/
puts(
"WARNING: Ancient bootloader, some functionality "
"may be limited!\n"
);
}
}
int
validate_cpu(void)
{
u32
*
err_flags;
int
cpu_level, req_level;
check_cpu(&cpu_level, &req_level, &err_flags);
if
(cpu_level < req_level) {
printf(
"This kernel requires an %s CPU, "
,
cpu_name(req_level));
printf(
"but only detected an %s CPU.\n"
,
cpu_name(cpu_level));
return
-
1
;
}
if
(err_flags) {
puts(
"This kernel requires the following features "
"not present on the CPU:\n"
);
show_cap_strs(err_flags);
putchar(
'\n'
);
return
-
1
;
}
else
if
(check_knl_erratum()) {
return
-
1
;
}
else
{
return
0
;
}
}
int
validate_cpu(void)
{
u32
*
err_flags;
int
cpu_level, req_level;
check_cpu(&cpu_level, &req_level, &err_flags);
if
(cpu_level < req_level) {
printf(
"This kernel requires an %s CPU, "
,
cpu_name(req_level));
printf(
"but only detected an %s CPU.\n"
,
cpu_name(cpu_level));
return
-
1
;
}
if
(err_flags) {
puts(
"This kernel requires the following features "
"not present on the CPU:\n"
);
show_cap_strs(err_flags);
putchar(
'\n'
);
return
-
1
;
}
else
if
(check_knl_erratum()) {
return
-
1
;
}
else
{
return
0
;
}
}
if
(test_bit(X86_FEATURE_LM, cpu.flags))
cpu.level
=
64
;
if
(test_bit(X86_FEATURE_LM, cpu.flags))
cpu.level
=
64
;
#define X86_FEATURE_LM ( 1*32+29) /* Long Mode (x86-64, 64-bit support) */
#define X86_FEATURE_LM ( 1*32+29) /* Long Mode (x86-64, 64-bit support) */
/
*
Tell the BIOS what CPU mode we intend to run
in
.
*
/
set_bios_mode();
/
*
Tell the BIOS what CPU mode we intend to run
in
.
*
/
set_bios_mode();
void go_to_protected_mode(void)
{
/
*
Hook before leaving real mode, also disables interrupts
*
/
realmode_switch_hook();
/
*
Enable the A20 gate
*
/
if
(enable_a20()) {
puts(
"A20 gate not responding, unable to boot...\n"
);
die();
}
/
*
Reset coprocessor (IGNNE
#) */
reset_coprocessor();
/
*
Mask
all
interrupts
in
the PIC
*
/
mask_all_interrupts();
/
*
Actual transition to protected mode...
*
/
setup_idt();
setup_gdt();
protected_mode_jump(boot_params.hdr.code32_start,
(u32)&boot_params
+
(ds() <<
4
));
}
void go_to_protected_mode(void)
{
/
*
Hook before leaving real mode, also disables interrupts
*
/
realmode_switch_hook();
/
*
Enable the A20 gate
*
/
if
(enable_a20()) {
puts(
"A20 gate not responding, unable to boot...\n"
);
die();
}
/
*
Reset coprocessor (IGNNE
#) */
reset_coprocessor();
/
*
Mask
all
interrupts
in
the PIC
*
/
mask_all_interrupts();
/
*
Actual transition to protected mode...
*
/
setup_idt();
setup_gdt();
protected_mode_jump(boot_params.hdr.code32_start,
(u32)&boot_params
+
(ds() <<
4
));
}
static void realmode_switch_hook(void)
{
if
(boot_params.hdr.realmode_swtch) {
asm volatile(
"lcallw *%0"
: :
"m"
(boot_params.hdr.realmode_swtch)
:
"eax"
,
"ebx"
,
"ecx"
,
"edx"
);
}
else
{
asm volatile(
"cli"
);
outb(
0x80
,
0x70
);
/
*
Disable NMI
*
/
io_delay();
}
}
static void realmode_switch_hook(void)
{
if
(boot_params.hdr.realmode_swtch) {
asm volatile(
"lcallw *%0"
: :
"m"
(boot_params.hdr.realmode_swtch)
:
"eax"
,
"ebx"
,
"ecx"
,
"edx"
);
}
else
{
asm volatile(
"cli"
);
outb(
0x80
,
0x70
);
/
*
Disable NMI
*
/
io_delay();
}
}
void main(void)
{
/
*
First, copy the boot header into the
"zeropage"
*
/
copy_boot_params();
/
*
Initialize the early
-
boot console
*
/
console_init();
if
(cmdline_find_option_bool(
"debug"
))
puts(
"early console in setup code\n"
);
/
*
End of heap check
*
/
init_heap();
/
*
Make sure we have
all
the proper CPU support
*
/
if
(validate_cpu()) {
puts(
"Unable to boot - please use a kernel appropriate "
"for your CPU.\n"
);
die();
}
/
*
Tell the BIOS what CPU mode we intend to run
in
.
*
/
set_bios_mode();
/
*
Detect memory layout
*
/
detect_memory();
/
*
Set
keyboard repeat rate (why?)
and
query the lock flags
*
/
keyboard_init();
/
*
Query Intel SpeedStep (IST) information
*
/
query_ist();
/
*
Query APM information
*
/
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
query_apm_bios();
#endif
/
*
Query EDD information
*
/
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
query_edd();
#endif
/
*
Set
the video mode
*
/
set_video();
/
*
Do the last things
and
invoke protected mode
*
/
go_to_protected_mode();
}
void main(void)
{
/
*
First, copy the boot header into the
"zeropage"
*
/
copy_boot_params();
/
*
Initialize the early
-
boot console
*
/
console_init();
if
(cmdline_find_option_bool(
"debug"
))
puts(
"early console in setup code\n"
);
/
*
End of heap check
*
/
init_heap();
/
*
Make sure we have
all
the proper CPU support
*
/
if
(validate_cpu()) {
puts(
"Unable to boot - please use a kernel appropriate "
"for your CPU.\n"
);
die();
}
/
*
Tell the BIOS what CPU mode we intend to run
in
.
*
/
set_bios_mode();
/
*
Detect memory layout
*
/
detect_memory();
/
*
Set
keyboard repeat rate (why?)
and
query the lock flags
*
/
keyboard_init();
/
*
Query Intel SpeedStep (IST) information
*
/
query_ist();
/
*
Query APM information
*
/
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
query_apm_bios();
#endif
/
*
Query EDD information
*
/
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
query_edd();
#endif
/
*
Set
the video mode
*
/
set_video();
/
*
Do the last things
and
invoke protected mode
*
/
go_to_protected_mode();
}
/
*
Actual transition to protected mode...
*
/
setup_idt();
setup_gdt();
protected_mode_jump(boot_params.hdr.code32_start,
(u32)&boot_params
+
(ds() <<
4
));
/
*
Actual transition to protected mode...
*
/
setup_idt();
setup_gdt();
protected_mode_jump(boot_params.hdr.code32_start,
(u32)&boot_params
+
(ds() <<
4
));
/
*
*
Set
up the IDT
*
/
static void setup_idt(void)
{
static const struct gdt_ptr null_idt
=
{
0
,
0
};
asm volatile(
"lidtl %0"
: :
"m"
(null_idt));
}
/
*
*
Set
up the IDT
*
/
static void setup_idt(void)
{
static const struct gdt_ptr null_idt
=
{
0
,
0
};
asm volatile(
"lidtl %0"
: :
"m"
(null_idt));
}
static void setup_gdt(void)
{
static const u64 boot_gdt[] __attribute__((aligned(
16
)))
=
{
/
*
CS: code, read
/
execute,
4
GB, base
0
*
/
[GDT_ENTRY_BOOT_CS]
=
GDT_ENTRY(
0xc09b
,
0
,
0xfffff
),
/
*
DS: data, read
/
write,
4
GB, base
0
*
/
[GDT_ENTRY_BOOT_DS]
=
GDT_ENTRY(
0xc093
,
0
,
0xfffff
),
/
*
TSS:
32
-
bit tss,
104
bytes, base
4096
*
/
/
*
We only have a TSS here to keep Intel VT happy;
we don't actually use it
for
anything.
*
/
[GDT_ENTRY_BOOT_TSS]
=
GDT_ENTRY(
0x0089
,
4096
,
103
),
};
/
*
Xen HVM incorrectly stores a pointer to the gdt_ptr, instead
of the gdt_ptr contents. Thus, make it static so it will
stay
in
memory, at least
long
enough that we switch to the
proper kernel GDT.
*
/
static struct gdt_ptr gdt;
gdt.
len
=
sizeof(boot_gdt)
-
1
;
gdt.ptr
=
(u32)&boot_gdt
+
(ds() <<
4
);
asm volatile(
"lgdtl %0"
: :
"m"
(gdt));
}
static void setup_gdt(void)
{
static const u64 boot_gdt[] __attribute__((aligned(
16
)))
=
{
/
*
CS: code, read
/
execute,
4
GB, base
0
*
/
[GDT_ENTRY_BOOT_CS]
=
GDT_ENTRY(
0xc09b
,
0
,
0xfffff
),
/
*
DS: data, read
/
write,
4
GB, base
0
*
/
[GDT_ENTRY_BOOT_DS]
=
GDT_ENTRY(
0xc093
,
0
,
0xfffff
),
/
*
TSS:
32
-
bit tss,
104
bytes, base
4096
*
/
/
*
We only have a TSS here to keep Intel VT happy;
we don't actually use it
for
anything.
*
/
[GDT_ENTRY_BOOT_TSS]
=
GDT_ENTRY(
0x0089
,
4096
,
103
),
};
/
*
Xen HVM incorrectly stores a pointer to the gdt_ptr, instead
of the gdt_ptr contents. Thus, make it static so it will
stay
in
memory, at least
long
enough that we switch to the
proper kernel GDT.
*
/
static struct gdt_ptr gdt;
gdt.
len
=
sizeof(boot_gdt)
-
1
;
gdt.ptr
=
(u32)&boot_gdt
+
(ds() <<
4
);
asm volatile(
"lgdtl %0"
: :
"m"
(gdt));
}
#define GDT_ENTRY(flags, base, limit) \
((((base) & _AC(
0xff000000
,ULL)) << (
56
-
24
)) | \
(((flags) & _AC(
0x0000f0ff
,ULL)) <<
40
) | \
(((limit) & _AC(
0x000f0000
,ULL)) << (
48
-
16
)) | \
(((base) & _AC(
0x00ffffff
,ULL)) <<
16
) | \
(((limit) & _AC(
0x0000ffff
,ULL))))
#define GDT_ENTRY(flags, base, limit) \
((((base) & _AC(
0xff000000
,ULL)) << (
56
-
24
)) | \
(((flags) & _AC(
0x0000f0ff
,ULL)) <<
40
) | \
(((limit) & _AC(
0x000f0000
,ULL)) << (
48
-
16
)) | \
(((base) & _AC(
0x00ffffff
,ULL)) <<
16
) | \
(((limit) & _AC(
0x0000ffff
,ULL))))
/
*
pmjump.S
*
/
void __attribute__((noreturn))
protected_mode_jump(u32 entrypoint, u32 bootparams);
/
*
pmjump.S
*
/
void __attribute__((noreturn))
protected_mode_jump(u32 entrypoint, u32 bootparams);
GLOBAL(protected_mode_jump)
movl
%
edx,
%
esi
# bootparams地址放入esi
xorl
%
ebx,
%
ebx
movw
%
cs,
%
bx
# cs放入bx
shll $
4
,
%
ebx
addl
%
ebx,
2f
jmp
1f
# Short jump to serialize on 386/486
1
:
movw $__BOOT_DS,
%
cx
movw $__BOOT_TSS,
%
di
movl
%
cr0,
%
edx
#
orb $X86_CR0_PE,
%
dl
# 设置 CR0 寄存器相应的位使 CPU 进入保护模式:
movl
%
edx,
%
cr0
# Transition to 32-bit mode
.byte
0x66
,
0xea
# ljmpl opcode
2
: .
long
in_pm32
# offset
.word __BOOT_CS
# segment
# 执行长跳转到代码段
ENDPROC(protected_mode_jump)
GLOBAL(protected_mode_jump)
movl
%
edx,
%
esi
# bootparams地址放入esi
xorl
%
ebx,
%
ebx
movw
%
cs,
%
bx
# cs放入bx
shll $
4
,
%
ebx
addl
%
ebx,
2f
jmp
1f
# Short jump to serialize on 386/486
1
:
movw $__BOOT_DS,
%
cx
movw $__BOOT_TSS,
%
di
movl
%
cr0,
%
edx
#
orb $X86_CR0_PE,
%
dl
# 设置 CR0 寄存器相应的位使 CPU 进入保护模式:
movl
%
edx,
%
cr0
# Transition to 32-bit mode
.byte
0x66
,
0xea
# ljmpl opcode
2
: .
long
in_pm32
# offset
.word __BOOT_CS
# segment
# 执行长跳转到代码段
ENDPROC(protected_mode_jump)
.code32
.section
".text32"
,
"ax"
GLOBAL(in_pm32)
# 进入32位保护模式首先重置段寄存器
movl
%
ecx,
%
ds
movl
%
ecx,
%
es
movl
%
ecx,
%
fs
movl
%
ecx,
%
gs
movl
%
ecx,
%
ss
# The 32-bit code sets up its own stack, but this way we do have
# a valid stack if some debugging hack wants to use it.
addl
%
ebx,
%
esp
# Set up TR to make Intel VT happy
ltr
%
di
# 清空通用寄存器
# 32-bit boot protocol
xorl
%
ecx,
%
ecx
xorl
%
edx,
%
edx
xorl
%
ebx,
%
ebx
xorl
%
ebp,
%
ebp
xorl
%
edi,
%
edi
# Set up LDTR to make Intel VT happy
lldt
%
cx
jmpl
*
%
eax
# Jump to the 32-bit entrypoint
ENDPROC(in_pm32)
.code32
.section
".text32"
,
"ax"
GLOBAL(in_pm32)
# 进入32位保护模式首先重置段寄存器
movl
%
ecx,
%
ds
movl
%
ecx,
%
es
movl
%
ecx,
%
fs
movl
%
ecx,
%
gs
movl
%
ecx,
%
ss
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课