head.S 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  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. // 直接用 -m64 编译出来的是 64 位代码,
  61. // 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。
  62. // 得加一层跳转到 64 位的 -m32 代码,开启 long 模式后再跳转到以 -m64 编译的代码中
  63. // 对于 x86_64,需要在启动阶段进入长模式(IA32E),这意味着需要一个临时页表
  64. // See https://wiki.osdev.org/Creating_a_64-bit_kernel:
  65. // With a 32-bit bootstrap in your kernel
  66. // 这部分是从保护模式启动 long 模式的代码
  67. // 工作在 32bit
  68. // 声明这一段代码以 32 位模式编译
  69. .code32
  70. // multiboot2 文件头
  71. // 计算头长度
  72. .SET HEADER_LENGTH, multiboot_header_end - multiboot_header
  73. // 计算校验和
  74. .SET CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + HEADER_LENGTH)
  75. // 8 字节对齐
  76. .section .multiboot_header
  77. .align MULTIBOOT_HEADER_ALIGN
  78. // 声明所属段
  79. multiboot_header:
  80. // 魔数
  81. .long MULTIBOOT2_HEADER_MAGIC
  82. // 架构
  83. .long MULTIBOOT_ARCHITECTURE_I386
  84. // 头长度
  85. .long HEADER_LENGTH
  86. // 校验和
  87. .long CHECKSUM
  88. // 添加其它内容在此,详细信息见 Multiboot2 Specification version 2.0.pdf
  89. // 设置帧缓冲区(同时在这里设置qemu的分辨率, 默认为: 1440*900, 还支持: 640*480, 等)
  90. .align 8
  91. framebuffer_tag_start:
  92. .short MULTIBOOT_HEADER_TAG_FRAMEBUFFER
  93. .short MULTIBOOT_HEADER_TAG_OPTIONAL
  94. .long framebuffer_tag_end - framebuffer_tag_start
  95. .long 1440 // 宽
  96. .long 900 // 高
  97. .long 32
  98. framebuffer_tag_end:
  99. .align 8
  100. .short MULTIBOOT_HEADER_TAG_END
  101. // 结束标记
  102. .short 0
  103. .long 8
  104. multiboot_header_end:
  105. .section .bootstrap
  106. .global _start
  107. .type _start, @function
  108. # 在 multiboot2.cpp 中定义
  109. .extern _start64
  110. .extern boot_info_addr
  111. .extern multiboot2_magic
  112. ENTRY(_start)
  113. // 关中断
  114. cli
  115. // multiboot2_info 结构体指针
  116. mov %ebx, mb2_info
  117. //mov %ebx, %e8
  118. // 魔数
  119. mov %eax, mb2_magic
  120. //mov %eax, %e9
  121. / 从保护模式跳转到长模式
  122. // 1. 允许 PAE
  123. mov %cr4, %eax
  124. or $(1<<5), %eax
  125. mov %eax, %cr4
  126. // 2. 设置临时页表
  127. // 最高级
  128. mov $pml4, %eax
  129. mov $pdpt, %ebx
  130. or $0x3, %ebx
  131. mov %ebx, 0(%eax)
  132. // 次级
  133. mov $pdpt, %eax
  134. mov $pd, %ebx
  135. or $0x3, %ebx
  136. mov %ebx, 0(%eax)
  137. // 次低级
  138. mov $pd, %eax
  139. mov $pt, %ebx
  140. or $0x3, %ebx
  141. mov %ebx, 0(%eax)
  142. // 最低级
  143. // 循环 512 次,填满一页
  144. mov $512, %ecx
  145. mov $pt, %eax
  146. mov $0x3, %ebx
  147. .fill_pt:
  148. mov %ebx, 0(%eax)
  149. add $0x1000, %ebx
  150. add $8, %eax
  151. loop .fill_pt
  152. .global enter_head_from_ap_boot
  153. enter_head_from_ap_boot:
  154. // 填写 CR3
  155. mov $pml4, %eax
  156. mov %eax, %cr3
  157. // 3. 切换到 long 模式
  158. mov $0xC0000080, %ecx
  159. rdmsr
  160. or $(1<<8), %eax
  161. wrmsr
  162. // 4. 开启分页
  163. mov %cr0, %eax
  164. or $(1<<31), %eax
  165. mov %eax, %cr0
  166. // 5. 重新设置 GDT
  167. mov $gdt64_pointer, %eax
  168. lgdt 0(%eax)
  169. jmp $0x8, $ready_to_start_64
  170. hlt
  171. ret
  172. .code64
  173. .global ready_to_start_64
  174. ready_to_start_64:
  175. mov $0x10, %ax
  176. mov %ax, %ds
  177. mov %ax, %es
  178. mov %ax, %fs
  179. mov %ax, %ss
  180. mov $0x7e00, %esp
  181. //6. 跳转到start64
  182. movq switch_to_start64(%rip), %rax
  183. pushq $0x08 //段选择子
  184. pushq %rax
  185. lretq
  186. switch_to_start64:
  187. .quad _start64
  188. .code64
  189. is_from_ap:
  190. hlt
  191. .global _start64
  192. .type _start64, @function
  193. .extern Start_Kernel
  194. ENTRY(_start64)
  195. // 初始化寄存器
  196. mov $0x10, %ax
  197. mov %ax, %ds
  198. mov %ax, %es
  199. mov %ax, %fs
  200. mov %ax, %ss
  201. mov $0x7e00, %esp
  202. // === 加载GDTR ====
  203. lgdt GDT_POINTER(%rip) //这里我没搞明白rip相对寻址, 看了文档,大概是用来实现PIC的(position independent code)
  204. //lgdt $GDT_POINTER
  205. // === 加载IDTR ====
  206. lidt IDT_POINTER(%rip)
  207. //lidt $IDT_POINTER
  208. movq GDT_POINTER(%rip), %r12
  209. movq head_stack_start(%rip), %rsp
  210. // 分支,判断是否为apu
  211. movq $0x1b, %rcx // 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu
  212. rdmsr
  213. bt $8, %rax
  214. jnc load_apu_cr3
  215. // 2. 设置临时页表
  216. // 最高级
  217. mov $__PML4E, %eax
  218. mov $__PDPTE, %ebx
  219. or $0x3, %ebx
  220. mov %ebx, 0(%eax)
  221. mov $__PML4E, %eax
  222. // 加256个表项, 映射高地址
  223. add $2048, %eax
  224. mov %ebx, 0(%eax)
  225. // 次级
  226. mov $__PDPTE, %eax
  227. mov $__PDE, %ebx
  228. or $0x3, %ebx
  229. mov %ebx, 0(%eax)
  230. // 次低级
  231. mov $__PDE, %eax
  232. mov $50, %ecx
  233. mov $__PT_S, %ebx
  234. or $0x3, %ebx
  235. .fill_pde_64:
  236. mov %ebx, 0(%eax)
  237. add $0x1000, %ebx
  238. add $8, %eax
  239. loop .fill_pde_64
  240. // 最低级
  241. // 循环 512*25=12800 次,填满25页,共50M
  242. mov $12800, %ecx
  243. mov $__PT_S, %eax
  244. mov $0x3, %ebx
  245. .fill_pt_64:
  246. mov %ebx, 0(%eax)
  247. add $0x1000, %ebx
  248. add $8, %eax
  249. loop .fill_pt_64
  250. // 50-100M填0,共25个页表
  251. mov $12800, %ecx
  252. .fill_pt_64_2:
  253. mov $0, 0(%eax)
  254. add $8, %eax
  255. loop .fill_pt_64_2
  256. // ==== 加载CR3寄存器
  257. load_cr3:
  258. movq $__PML4E, %rax //设置页目录基地址
  259. movq %rax, %cr3
  260. jmp to_switch_seg
  261. load_apu_cr3:
  262. // 由于内存管理模块重置了页表,因此ap核心初始化的时候,需要使用新的内核页表。
  263. // 这个页表的值由smp模块设置到__APU_START_CR3变量中
  264. // 加载__APU_START_CR3中的值
  265. movq $__APU_START_CR3, %rax
  266. movq 0(%rax), %rax
  267. movq %rax, %cr3
  268. jmp to_switch_seg
  269. to_switch_seg:
  270. movq switch_seg(%rip), %rax
  271. // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
  272. // 实在是太妙了!Amazing!
  273. pushq $0x08 //段选择子
  274. pushq %rax
  275. lretq
  276. // 64位模式的代码
  277. switch_seg:
  278. .quad entry64
  279. entry64:
  280. movq $0x10, %rax
  281. movq %rax, %ds
  282. movq %rax, %es
  283. movq %rax, %gs
  284. movq %rax, %ss
  285. movq head_stack_start(%rip), %rsp //rsp的地址
  286. // 重新加载GDT和IDT,加载到高地址
  287. leaq GDT_Table(%rip), %r8
  288. leaq GDT_END(%rip), %r9
  289. subq %r8, %r9
  290. movq %r9, %r13 // GDT size
  291. leaq IDT_Table(%rip), %r8
  292. leaq IDT_END(%rip), %r9
  293. subq %r8, %r9
  294. movq %r9, %r12 // IDT size
  295. lgdt GDT_POINTER64(%rip)
  296. lidt IDT_POINTER64(%rip)
  297. // 分支,判断是否为apu
  298. movq $0x1b, %rcx // 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu
  299. rdmsr
  300. bt $8, %rax
  301. jnc start_smp
  302. setup_IDT:
  303. // 该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,
  304. leaq m_ignore_int(%rip), %rdx // 将ignore_int的地址暂时存到中段描述符的高8B
  305. movq $(0x08 << 16), %rax // 设置段选择子。由IDT结构和段选择子结构可知,本行设置段基地址为0x100000,TI=0,RPL=0
  306. movw %dx, %ax
  307. movq $ (0x8e00 << 32), %rcx // 设置Type=1110 P=1 DPL=00 0=0
  308. addq %rcx, %rax
  309. // 把ignore_int的地址填写到正确位置, rax存低8B, rdx存高8B
  310. movl %edx, %ecx
  311. shrl $16, %ecx // 去除低16位
  312. shlq $48, %rcx
  313. addq %rcx, %rax // 填写段内偏移31:16
  314. shrq $32, %rdx // (已经填写了32位,故右移32)
  315. leaq IDT_Table(%rip), %rdi // 获取中断描述符表的首地址,存储到rdi
  316. mov $256, %rcx // 初始化每个中断描述符
  317. repeat_set_idt:
  318. // ====== 循环,初始化总共256个中断描述符 ===
  319. movq %rax, (%rdi) // 保存低8B
  320. movq %rdx, 8(%rdi) // 保存高8B
  321. addq $0x10, %rdi // 转到下一个IDT表项
  322. dec %rcx
  323. jne repeat_set_idt
  324. //now enable SSE and the like
  325. movq %cr0, %rax
  326. and $0xFFFB, %ax //clear coprocessor emulation CR0.EM
  327. or $0x2, %ax //set coprocessor monitoring CR0.MP
  328. movq %rax, %cr0
  329. movq %cr4, %rax
  330. or $(3 << 9), %ax //set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
  331. movq %rax, %cr4
  332. movq go_to_kernel(%rip), %rax /* movq address */
  333. pushq $0x08
  334. pushq %rax
  335. movq mb2_info, %r15
  336. movq mb2_magic, %r14
  337. lretq
  338. go_to_kernel:
  339. .quad Start_Kernel
  340. start_smp:
  341. //now enable SSE and the like
  342. movq %cr0, %rax
  343. and $0xFFFB, %ax //clear coprocessor emulation CR0.EM
  344. or $0x2, %ax //set coprocessor monitoring CR0.MP
  345. movq %rax, %cr0
  346. movq %cr4, %rax
  347. or $(3 << 9), %ax //set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
  348. movq %rax, %cr4
  349. movq go_to_smp_kernel(%rip), %rax /* movq address */
  350. pushq $0x08
  351. pushq %rax
  352. /*
  353. // 重新加载GDT和IDT,加载到高地址
  354. leaq GDT_Table(%rip), %r8
  355. leaq GDT_END(%rip), %r9
  356. subq %r8, %r9
  357. movq %r9, %r13 // GDT size
  358. leaq IDT_Table(%rip), %r8
  359. leaq IDT_END(%rip), %r9
  360. subq %r8, %r9
  361. movq %r9, %r12 // IDT size
  362. lgdt GDT_POINTER64(%rip)
  363. lidt IDT_POINTER64(%rip)
  364. */
  365. lretq
  366. go_to_smp_kernel:
  367. .quad smp_ap_start
  368. // ==== 异常/中断处理模块 ignore int: 忽略中断
  369. // (该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,从而重设ignore_int的中断入点)
  370. m_ignore_int:
  371. // 切换到c语言的ignore_int
  372. movq go_to_ignore_int(%rip), %rax
  373. pushq $0x08
  374. pushq %rax
  375. lretq
  376. go_to_ignore_int:
  377. .quad ignore_int_handler
  378. ENTRY(head_stack_start)
  379. .quad initial_proc_union + 32768
  380. .align 32768
  381. initial_proc_union:
  382. .fill 32768, 1, 0
  383. // 初始化页表
  384. .align 0x1000 //设置为4k对齐
  385. __PML4E:
  386. .skip 0x1000
  387. __PDPTE:
  388. .skip 0x1000
  389. // 三级页表
  390. __PDE:
  391. .skip 0x1000
  392. // 预留50个四级页表,总共表示100M的内存空间。这50个页表占用200KB的空间
  393. __PT_S:
  394. .skip 0x32000
  395. .global __APU_START_CR3
  396. __APU_START_CR3:
  397. .quad 0
  398. // GDT表
  399. .align 16
  400. .global GDT_Table // 使得GDT可以被外部程序引用或者访问
  401. GDT_Table:
  402. .quad 0x0000000000000000 // 0 空描述符 0x00
  403. .quad 0x0020980000000000 // 1 内核64位代码段描述符 0x08
  404. .quad 0x0000920000000000 // 2 内核64位数据段描述符 0x10
  405. .quad 0x0000000000000000 // 3 用户32位代码段描述符 0x18
  406. .quad 0x0000000000000000 // 4 用户32位数据段描述符 0x20
  407. .quad 0x0020f80000000000 // 5 用户64位代码段描述符 0x28
  408. .quad 0x0000f20000000000 // 6 用户64位数据段描述符 0x30
  409. .quad 0x00cf9a000000ffff // 7 内核32位代码段描述符 0x38
  410. .quad 0x00cf92000000ffff // 8 内核32位数据段描述符 0x40
  411. .fill 100, 8, 0 // 10-11 TSS(跳过了第9段) 重复十次填充8字节的空间,赋值为0 长模式下,每个TSS长度为128bit
  412. GDT_END:
  413. .global GDT_POINTER
  414. GDT_POINTER:
  415. GDT_LIMIT: .word GDT_END - GDT_Table - 1 // GDT的大小
  416. GDT_BASE: .quad GDT_Table
  417. .global GDT_POINTER64
  418. GDT_POINTER64:
  419. GDT_LIMIT64: .word GDT_END - GDT_Table - 1 // GDT的大小
  420. GDT_BASE64: .quad GDT_Table + 0xffff800000000000
  421. // IDT 表
  422. .global IDT_Table
  423. IDT_Table:
  424. .fill 512, 8, 0 // 设置512*8字节的IDT表的空间
  425. IDT_END:
  426. .global IDT_POINTER
  427. IDT_POINTER:
  428. IDT_LIMIT: .word IDT_END - IDT_Table - 1
  429. IDT_BASE: .quad IDT_Table
  430. .global IDT_POINTER64
  431. IDT_POINTER64:
  432. IDT_LIMIT64: .word IDT_END - IDT_Table - 1
  433. IDT_BASE64: .quad IDT_Table + 0xffff800000000000
  434. .section .bootstrap.data
  435. mb2_magic: .quad 0
  436. mb2_info: .quad 0
  437. .code32
  438. // 临时页表 4KB/页
  439. .align 0x1000
  440. .global pml4
  441. pml4:
  442. .skip 0x1000
  443. pdpt:
  444. .skip 0x1000
  445. pd:
  446. .skip 0x1000
  447. pt:
  448. .skip 0x1000
  449. // 临时 GDT
  450. .align 16
  451. gdt64:
  452. null_desc:
  453. .short 0xFFFF
  454. .short 0
  455. .byte 0
  456. .byte 0
  457. .byte 0
  458. .byte 0
  459. code_desc:
  460. .short 0
  461. .short 0
  462. .byte 0
  463. .byte 0x9A
  464. .byte 0x20
  465. .byte 0
  466. data_desc:
  467. .short 0
  468. .short 0
  469. .byte 0
  470. .byte 0x92
  471. .byte 0
  472. .byte 0
  473. user_code_desc:
  474. .short 0
  475. .short 0
  476. .byte 0
  477. .byte 0xFA
  478. .byte 0x20
  479. .byte 0
  480. user_data_desc:
  481. .short 0
  482. .short 0
  483. .byte 0
  484. .byte 0xF2
  485. .byte 0
  486. .byte 0
  487. gdt64_pointer:
  488. .short gdt64_pointer-gdt64-1
  489. .quad gdt64
  490. gdt64_pointer64:
  491. .short gdt64_pointer-gdt64-1
  492. .quad gdt64