head.S 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // 这是内核执行头程序
  2. // Created by longjin.
  3. // 2022/01/20
  4. #include "common/asm.h"
  5. // 以下是来自 multiboot2 规范的定义
  6. // How many bytes from the start of the file we search for the header.
  7. #define MULTIBOOT_SEARCH 32768
  8. #define MULTIBOOT_HEADER_ALIGN 8
  9. // The magic field should contain this.
  10. #define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
  11. // This should be in %eax.
  12. #define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
  13. // Alignment of multiboot modules.
  14. #define MULTIBOOT_MOD_ALIGN 0x00001000
  15. // Alignment of the multiboot info structure.
  16. #define MULTIBOOT_INFO_ALIGN 0x00000008
  17. // Flags set in the 'flags' member of the multiboot header.
  18. #define MULTIBOOT_TAG_ALIGN 8
  19. #define MULTIBOOT_TAG_TYPE_END 0
  20. #define MULTIBOOT_TAG_TYPE_CMDLINE 1
  21. #define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
  22. #define MULTIBOOT_TAG_TYPE_MODULE 3
  23. #define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
  24. #define MULTIBOOT_TAG_TYPE_BOOTDEV 5
  25. #define MULTIBOOT_TAG_TYPE_MMAP 6
  26. #define MULTIBOOT_TAG_TYPE_VBE 7
  27. #define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
  28. #define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
  29. #define MULTIBOOT_TAG_TYPE_APM 10
  30. #define MULTIBOOT_TAG_TYPE_EFI32 11
  31. #define MULTIBOOT_TAG_TYPE_EFI64 12
  32. #define MULTIBOOT_TAG_TYPE_SMBIOS 13
  33. #define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
  34. #define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
  35. #define MULTIBOOT_TAG_TYPE_NETWORK 16
  36. #define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
  37. #define MULTIBOOT_TAG_TYPE_EFI_BS 18
  38. #define MULTIBOOT_TAG_TYPE_EFI32_IH 19
  39. #define MULTIBOOT_TAG_TYPE_EFI64_IH 20
  40. #define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
  41. #define MULTIBOOT_HEADER_TAG_END 0
  42. #define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
  43. #define MULTIBOOT_HEADER_TAG_ADDRESS 2
  44. #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
  45. #define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
  46. #define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
  47. #define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
  48. #define MULTIBOOT_HEADER_TAG_EFI_BS 7
  49. #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
  50. #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
  51. #define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
  52. #define MULTIBOOT_ARCHITECTURE_I386 0
  53. #define MULTIBOOT_ARCHITECTURE_MIPS32 4
  54. #define MULTIBOOT_HEADER_TAG_OPTIONAL 1
  55. #define MULTIBOOT_LOAD_PREFERENCE_NONE 0
  56. #define MULTIBOOT_LOAD_PREFERENCE_LOW 1
  57. #define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
  58. #define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
  59. #define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
  60. // 以下是来自 multiboot2 规范的定义
  61. // How many bytes from the start of the file we search for the header.
  62. #define MULTIBOOT_SEARCH 32768
  63. #define MULTIBOOT_HEADER_ALIGN 8
  64. // The magic field should contain this.
  65. #define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
  66. // This should be in %eax.
  67. #define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
  68. // Alignment of multiboot modules.
  69. #define MULTIBOOT_MOD_ALIGN 0x00001000
  70. // Alignment of the multiboot info structure.
  71. #define MULTIBOOT_INFO_ALIGN 0x00000008
  72. // Flags set in the 'flags' member of the multiboot header.
  73. #define MULTIBOOT_TAG_ALIGN 8
  74. #define MULTIBOOT_TAG_TYPE_END 0
  75. #define MULTIBOOT_TAG_TYPE_CMDLINE 1
  76. #define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
  77. #define MULTIBOOT_TAG_TYPE_MODULE 3
  78. #define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
  79. #define MULTIBOOT_TAG_TYPE_BOOTDEV 5
  80. #define MULTIBOOT_TAG_TYPE_MMAP 6
  81. #define MULTIBOOT_TAG_TYPE_VBE 7
  82. #define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
  83. #define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
  84. #define MULTIBOOT_TAG_TYPE_APM 10
  85. #define MULTIBOOT_TAG_TYPE_EFI32 11
  86. #define MULTIBOOT_TAG_TYPE_EFI64 12
  87. #define MULTIBOOT_TAG_TYPE_SMBIOS 13
  88. #define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
  89. #define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
  90. #define MULTIBOOT_TAG_TYPE_NETWORK 16
  91. #define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
  92. #define MULTIBOOT_TAG_TYPE_EFI_BS 18
  93. #define MULTIBOOT_TAG_TYPE_EFI32_IH 19
  94. #define MULTIBOOT_TAG_TYPE_EFI64_IH 20
  95. #define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
  96. #define MULTIBOOT_HEADER_TAG_END 0
  97. #define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
  98. #define MULTIBOOT_HEADER_TAG_ADDRESS 2
  99. #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
  100. #define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
  101. #define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
  102. #define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
  103. #define MULTIBOOT_HEADER_TAG_EFI_BS 7
  104. #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
  105. #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
  106. #define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
  107. #define MULTIBOOT_ARCHITECTURE_I386 0
  108. #define MULTIBOOT_ARCHITECTURE_MIPS32 4
  109. #define MULTIBOOT_HEADER_TAG_OPTIONAL 1
  110. #define MULTIBOOT_LOAD_PREFERENCE_NONE 0
  111. #define MULTIBOOT_LOAD_PREFERENCE_LOW 1
  112. #define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
  113. #define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
  114. #define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
  115. // 直接用 -m64 编译出来的是 64 位代码,
  116. // 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。
  117. // 得加一层跳转到 64 位的 -m32 代码,开启 long 模式后再跳转到以 -m64 编译的代码中
  118. // 对于 x86_64,需要在启动阶段进入长模式(IA32E),这意味着需要一个临时页表
  119. // See https://wiki.osdev.org/Creating_a_64-bit_kernel:
  120. // With a 32-bit bootstrap in your kernel
  121. // 这部分是从保护模式启动 long 模式的代码
  122. // 工作在 32bit
  123. // 声明这一段代码以 32 位模式编译
  124. .code32
  125. // multiboot2 文件头
  126. // 计算头长度
  127. .SET HEADER_LENGTH, multiboot_header_end - multiboot_header
  128. // 计算校验和
  129. .SET CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + HEADER_LENGTH)
  130. // 8 字节对齐
  131. .align MULTIBOOT_HEADER_ALIGN
  132. // 声明所属段
  133. .section .multiboot_header
  134. multiboot_header:
  135. // 魔数
  136. .long MULTIBOOT2_HEADER_MAGIC
  137. // 架构
  138. .long MULTIBOOT_ARCHITECTURE_I386
  139. // 头长度
  140. .long HEADER_LENGTH
  141. // 校验和
  142. .long CHECKSUM
  143. // 添加其它内容在此,详细信息见 Multiboot2 Specification version 2.0.pdf
  144. // 设置帧缓冲区
  145. .align 8
  146. framebuffer_tag_start:
  147. .short MULTIBOOT_HEADER_TAG_FRAMEBUFFER
  148. .short MULTIBOOT_HEADER_TAG_OPTIONAL
  149. .long framebuffer_tag_end - framebuffer_tag_start
  150. .long 1440
  151. .long 900
  152. .long 32
  153. framebuffer_tag_end:
  154. .align 8
  155. .short MULTIBOOT_HEADER_TAG_END
  156. // 结束标记
  157. .short 0
  158. .long 8
  159. multiboot_header_end:
  160. // 临时页表 4KB/页
  161. .section .data
  162. .align 0x1000
  163. pml4:
  164. .skip 0x1000
  165. pdpt:
  166. .skip 0x1000
  167. pd:
  168. .skip 0x1000
  169. pt:
  170. .skip 0x1000
  171. // 临时 GDT
  172. .align 16
  173. gdt64:
  174. null_desc:
  175. .short 0xFFFF
  176. .short 0
  177. .byte 0
  178. .byte 0
  179. .byte 0
  180. .byte 0
  181. code_desc:
  182. .short 0
  183. .short 0
  184. .byte 0
  185. .byte 0x9A
  186. .byte 0x20
  187. .byte 0
  188. data_desc:
  189. .short 0
  190. .short 0
  191. .byte 0
  192. .byte 0x92
  193. .byte 0
  194. .byte 0
  195. user_code_desc:
  196. .short 0
  197. .short 0
  198. .byte 0
  199. .byte 0xFA
  200. .byte 0x20
  201. .byte 0
  202. user_data_desc:
  203. .short 0
  204. .short 0
  205. .byte 0
  206. .byte 0xF2
  207. .byte 0
  208. .byte 0
  209. gdt64_pointer:
  210. .short gdt64_pointer-gdt64-1
  211. .quad gdt64
  212. gdt64_pointer64:
  213. .short gdt64_pointer-gdt64-1
  214. .quad gdt64
  215. .section .text
  216. .global _start
  217. .type _start, @function
  218. # 在 multiboot2.cpp 中定义
  219. .extern boot_info_addr
  220. .extern multiboot2_magic
  221. .extern _start64
  222. _start:
  223. // 关中断
  224. cli
  225. // multiboot2_info 结构体指针
  226. mov %ebx, boot_info_addr
  227. // 魔数
  228. mov %eax, multiboot2_magic
  229. / 从保护模式跳转到长模式
  230. // 1. 允许 PAE
  231. mov %cr4, %eax
  232. or $(1<<5), %eax
  233. mov %eax, %cr4
  234. // 2. 设置临时页表
  235. // 最高级
  236. mov $pml4, %eax
  237. mov $pdpt, %ebx
  238. or $0x3, %ebx
  239. mov %ebx, 0(%eax)
  240. // 次级
  241. mov $pdpt, %eax
  242. mov $pd, %ebx
  243. or $0x3, %ebx
  244. mov %ebx, 0(%eax)
  245. // 次低级
  246. mov $pd, %eax
  247. mov $pt, %ebx
  248. or $0x3, %ebx
  249. mov %ebx, 0(%eax)
  250. // 最低级
  251. // 循环 512 次,填满一页
  252. mov $512, %ecx
  253. mov $pt, %eax
  254. mov $0x3, %ebx
  255. .fill_pt:
  256. mov %ebx, 0(%eax)
  257. add $0x1000, %ebx
  258. add $8, %eax
  259. loop .fill_pt
  260. // 填写 CR3
  261. mov $pml4, %eax
  262. mov %eax, %cr3
  263. // 3. 切换到 long 模式
  264. mov $0xC0000080, %ecx
  265. rdmsr
  266. or $(1<<8), %eax
  267. wrmsr
  268. // 4. 开启分页
  269. mov %cr0, %eax
  270. or $(1<<31), %eax
  271. mov %eax, %cr0
  272. // 5. 重新设置 GDT
  273. mov $gdt64_pointer, %eax
  274. lgdt 0(%eax)
  275. // 6. 跳转到 64 位代码执行
  276. jmp $0x8, $_start64
  277. hlt
  278. ret
  279. .section .text
  280. .code64
  281. .global _start64
  282. .type _start64, @function
  283. .extern Start_Kernel
  284. ENTRY(_start64)
  285. // 初始化寄存器
  286. mov $0x10, %ax
  287. mov %ax, %ds
  288. mov %ax, %es
  289. mov %ax, %fs
  290. mov %ax, %ss
  291. mov $0x7e00, %esp
  292. // === 加载GDTR ====
  293. lgdt GDT_POINTER(%rip) //这里我没搞明白rip相对寻址, 看了文档,大概是用来实现PIC的(position independent code)
  294. // === 加载IDTR ====
  295. lidt IDT_POINTER(%rip)
  296. mov $0x10, %ax
  297. mov %ax, %ds
  298. mov %ax, %es
  299. mov %ax, %fs
  300. mov %ax, %ss
  301. mov %ax, %gs
  302. movq $0x7e00, %rsp
  303. // 2. 设置临时页表
  304. // 最高级
  305. mov $__PML4E, %eax
  306. mov $__PDPTE, %ebx
  307. or $0x7, %ebx
  308. mov %ebx, 0(%eax)
  309. // 次级
  310. mov $__PDPTE, %eax
  311. mov $__PDE, %ebx
  312. or $0x7, %ebx
  313. mov %ebx, 0(%eax)
  314. // ==== 加载CR3寄存器
  315. movq $__PML4E, %rax //设置页目录基地址
  316. movq %rax, %cr3
  317. movq switch_seg(%rip), %rax
  318. // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
  319. // 实在是太妙了!Amazing!
  320. pushq $0x08 //段选择子
  321. pushq %rax
  322. lretq
  323. // 64位模式的代码
  324. switch_seg:
  325. .quad entry64
  326. entry64:
  327. movq $0x10, %rax
  328. movq %rax, %ds
  329. movq %rax, %es
  330. movq %rax, %gs
  331. movq %rax, %ss
  332. movq _stack_start(%rip), %rsp //rsp的地址
  333. setup_IDT:
  334. leaq m_ignore_int(%rip), %rdx // 将ignore_int的地址暂时存到中段描述符的高8B
  335. movq $(0x08 << 16), %rax // 设置段选择子。由IDT结构和段选择子结构可知,本行设置段基地址为0x100000,TI=0,RPL=0
  336. movw %dx, %ax
  337. movq $ (0x8e00 << 32), %rcx // 设置Type=1110 P=1 DPL=00 0=0
  338. addq %rcx, %rax
  339. // 把ignore_int的地址填写到正确位置, rax存低8B, rdx存高8B
  340. movl %edx, %ecx
  341. shrl $16, %ecx // 去除低16位
  342. shlq $48, %rcx
  343. addq %rcx, %rax // 填写段内偏移31:16
  344. shrq $32, %rdx // (已经填写了32位,故右移32)
  345. leaq IDT_Table(%rip), %rdi // 获取中断描述符表的首地址,存储到rdi
  346. mov $256, %rcx // 初始化每个中断描述符
  347. repeat_set_idt:
  348. // ====== 循环,初始化总共256个中断描述符 ===
  349. movq %rax, (%rdi) // 保存低8B
  350. movq %rdx, 8(%rdi) // 保存高8B
  351. addq $0x10, %rdi // 转到下一个IDT表项
  352. dec %rcx
  353. jne repeat_set_idt
  354. SetUp_TSS64:
  355. // == 设置64位的任务状态段表 ===
  356. //rdx保存高8B, rax保存低8B
  357. leaq TSS64_Table(%rip), %rdx
  358. xorq %rax, %rax
  359. xorq %rcx, %rcx
  360. // 设置TSS描述符的47:40位为1000 1001
  361. movq $0x89, %rax
  362. shlq $40, %rax
  363. // 设置段基地址31:24
  364. movl %edx, %ecx
  365. shrl $24, %ecx
  366. shlq $56, %rcx
  367. addq %rcx, %rax
  368. xorq %rcx, %rcx
  369. // 设置段基地址23:00
  370. movl %edx, %ecx
  371. andl $0xffffff, %ecx // 清空ecx的中有效值的高8位(也就是上面已经赋值了的)
  372. shlq $16, %rcx
  373. addq %rcx, %rax
  374. addq $103, %rax // 设置段长度
  375. leaq GDT_Table(%rip), %rdi
  376. movq %rax, 80(%rdi) // 把低八B存储到GDT第10项
  377. shrq $32, %rdx
  378. movq %rdx, 88(%rdi) // 高8B存到GDT第11项
  379. // 装载任务状态段寄存器(已改为在main.c中使用load_TR宏进行装载)
  380. // mov $0x50, %ax // 设置起始地址为80
  381. // ltr %ax
  382. //now enable SSE and the like
  383. movq %cr0, %rax
  384. and $0xFFFB, %ax //clear coprocessor emulation CR0.EM
  385. or $0x2, %ax //set coprocessor monitoring CR0.MP
  386. movq %rax, %cr0
  387. movq %cr4, %rax
  388. or $(3 << 9), %ax //set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
  389. movq %rax, %cr4
  390. call Start_Kernel
  391. go_to_kernel:
  392. .quad Start_Kernel
  393. // ==== 异常/中断处理模块 ignore int: 忽略中断
  394. m_ignore_int:
  395. // 切换到c语言的ignore_int
  396. movq go_to_ignore_int(%rip), %rax
  397. pushq $0x08
  398. pushq %rax
  399. lretq
  400. lretq
  401. go_to_ignore_int:
  402. .quad ignore_int
  403. ENTRY(_stack_start)
  404. .quad initial_proc_union + 32768
  405. // 初始化页表
  406. .align 0x1000 //设置为8byte对齐
  407. .org 0x1000 //设置页表位置为内核执行头程序的0x1000处
  408. __PML4E:
  409. .quad 0x102007 // 用户访问,可读写,已存在, 地址在31~12位
  410. .fill 255,8,0
  411. .quad 0x102007
  412. .fill 255,8,0
  413. .org 0x2000
  414. __PDPTE:
  415. .quad 0x103007 // 用户访问,可读写,已存在
  416. .fill 511,8,0
  417. .org 0x3000
  418. __PDE:
  419. .quad 0x000087 // 用户访问,可读写,已存在
  420. .quad 0x200087
  421. .quad 0x400087
  422. .quad 0x600087
  423. .quad 0x800087
  424. .quad 0xe0000087 /*虚拟地址0x a00000 帧缓冲区映射到这里*/
  425. .quad 0xe0200087
  426. .quad 0xe0400087
  427. .quad 0xe0600087 /*0x1000000*/
  428. .quad 0xe0800087
  429. .quad 0xe0a00087
  430. .quad 0xe0c00087
  431. .quad 0xe0e00087
  432. .fill 499,8,0
  433. // GDT表
  434. .section .data
  435. .align 16
  436. .global GDT_Table // 使得GDT可以被外部程序引用或者访问
  437. GDT_Table:
  438. .quad 0x0000000000000000 // 0 空描述符 0x00
  439. .quad 0x0020980000000000 // 1 内核64位代码段描述符 0x08
  440. .quad 0x0000920000000000 // 2 内核64位数据段描述符 0x10
  441. .quad 0x0000000000000000 // 3 用户32位代码段描述符 0x18
  442. .quad 0x0000000000000000 // 4 用户32位数据段描述符 0x20
  443. .quad 0x0020f80000000000 // 5 用户64位代码段描述符 0x28
  444. .quad 0x0000f20000000000 // 6 用户64位数据段描述符 0x30
  445. .quad 0x00cf9a000000ffff // 7 内核32位代码段描述符 0x38
  446. .quad 0x00cf92000000ffff // 8 内核32位数据段描述符 0x40
  447. .fill 10, 8, 0 // 10-11 TSS(跳过了第9段) 重复十次填充8字节的空间,赋值为0
  448. GDT_END:
  449. GDT_POINTER:
  450. GDT_LIMIT: .word GDT_END - GDT_Table - 1 // GDT的大小
  451. GDT_BASE: .quad GDT_Table
  452. // IDT 表
  453. .global IDT_Table
  454. IDT_Table:
  455. .fill 512, 8, 0 // 设置512*8字节的IDT表的空间
  456. IDT_END:
  457. IDT_POINTER:
  458. IDT_LIMIT: .word IDT_END - IDT_Table - 1
  459. IDT_BASE: .quad IDT_Table
  460. // 64位的TSS表
  461. .global TSS64_Table
  462. TSS64_Table:
  463. .fill 13, 8, 0
  464. TSS64_END:
  465. TSS64_POINTER:
  466. TSS64_LIMIT: .word TSS64_END - TSS64_Table - 1
  467. TSS64_BASE: .quad TSS64_Table