-
Notifications
You must be signed in to change notification settings - Fork 15
/
bootasm.S
105 lines (89 loc) · 4.53 KB
/
bootasm.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include "asm.h"
#include "memlayout.h"
#include "mmu.h"
# Start the first CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.
.code16 # Assemble for 16-bit mode
.globl start # 声明start对外开放,这样Makefile中才可以使用
start:
cli # BIOS enabled interrupts; disable
# Zero data segment registers DS, ES, and SS. (cs在执行代码时会被用到,所以不能被修改)
xorw %ax,%ax # Set %ax to zero
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
# Physical address line A20 is tied to zero so that the first PCs
# with 2 MB would run software that assumed 1 MB. Undo that.
# 有关A20更多信息,请看下面的链接:
# https://wiki.osdev.org/A20_Line
# 有关seta20.1和seta20.2代码是什么意思,请看下面的链接:
# https://www.win.tue.nl/~aeb/linux/kbd/A20.html
# https://wiki.osdev.org/I/O_Ports
# https://wiki.osdev.org/"8042"_PS/2_Controller
seta20.1:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.1
movb $0xd1,%al # 0xd1 -> port 0x64
outb %al,$0x64
seta20.2:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.2
movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map directly to physical addresses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc # 初始化gdt
movl %cr0, %eax
orl $CR0_PE, %eax # 开启保护模式(cr0寄存器字段描述请看:https://wiki.osdev.org/CPU_Registers_x86)
movl %eax, %cr0
//PAGEBREAK!
# Complete the transition to 32-bit protected mode by using a long jmp
# to reload %cs and %eip. The segment descriptors are set up with no
# translation, so that the mapping is still the identity mapping.
# SEG_KCODE值为1,表示引用gdt中下标为1的segment descriptor,即内核代码
# 左移3位的原因是segment selector的低3位要用于表示其他意义
# 有关segment selector的具体格式请看intel开发手册卷3的3.4.2部分
ljmp $(SEG_KCODE<<3), $start32
.code32 # Tell assembler to generate 32-bit code now.
start32:
# Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector,SEG_KDATA值为2,表示指向gdt下标为2的segment descriptor
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
movw $0, %ax # Zero segments not ready for use,gdt的0下标指向的数据为null
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
# Set up the stack pointer and call into C.
# start为该代码的起始地址,即0x7c00
# 下面将start值放入%esp中用来设置堆栈起始地址
# 因为堆栈是向下增长的,所以说内存区域0x7c00地址往下是堆栈区,0x7c00地址往上是代码区
movl $start, %esp
# 调用bootmain.c中的bootmain方法
call bootmain
# 下面的逻辑不应该被执行,除非代码bug了
# If bootmain returns (it shouldn't), trigger a Bochs
# breakpoint if running under Bochs, then loop.
movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
movw %ax, %dx
outw %ax, %dx
movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00
outw %ax, %dx
spin:
jmp spin
# Bootstrap GDT
# 有关gdt和gdtdesc的详细格式说明,请阅读 Intel® 64 and IA-32 Architectures Software Developer’s Manual 的第3章的第3.4和3.5节
# 地址为:https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf
.p2align 2 # force 4 byte alignment
gdt:
SEG_NULLASM # null seg,gdt的地一个descriptor不用
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1,gdt的地址范围是[base, base+limit],包含最后一个字节,所以减1
.long gdt # address gdt