head.S 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // 这是内核执行头程序
  2. // Created by longjin.
  3. // 2022/01/20
  4. #include "common/asm.h"
  5. .section .text
  6. .global _start
  7. _start:
  8. // 初始化寄存器
  9. mov $0x10, %ax
  10. mov %ax, %ds
  11. mov %ax, %es
  12. mov %ax, %fs
  13. mov %ax, %ss
  14. mov $0x7e00, %esp
  15. // === 加载GDTR ====
  16. lgdt GDT_POINTER(%rip) //这里我没搞明白rip相对寻址, 看了文档,大概是用来实现PIC的(position independent code)
  17. // === 加载IDTR ====
  18. lidt IDT_POINTER(%rip)
  19. mov $0x10, %ax
  20. mov %ax, %ds
  21. mov %ax, %es
  22. mov %ax, %fs
  23. mov %ax, %ss
  24. mov %ax, %gs
  25. movq $0x7e00, %rsp
  26. // ==== 加载CR3寄存器
  27. movq $0x101000, %rax //设置页目录基地址
  28. movq %rax, %cr3
  29. movq switch_seg(%rip), %rax
  30. // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
  31. // 实在是太妙了!Amazing!
  32. pushq $0x08 //段选择子
  33. pushq %rax
  34. lretq
  35. // 64位模式的代码
  36. switch_seg:
  37. .quad entry64
  38. entry64:
  39. movq $0x10, %rax
  40. movq %rax, %ds
  41. movq %rax, %es
  42. movq %rax, %gs
  43. movq %rax, %ss
  44. movq _stack_start(%rip), %rsp //rsp的地址
  45. setup_IDT:
  46. leaq m_ignore_int(%rip), %rdx // 将ignore_int的地址暂时存到中段描述符的高8B
  47. movq $(0x08 << 16), %rax // 设置段选择子。由IDT结构和段选择子结构可知,本行设置段基地址为0x100000,TI=0,RPL=0
  48. movw %dx, %ax
  49. movq $ (0x8e00 << 32), %rcx // 设置Type=1110 P=1 DPL=00 0=0
  50. addq %rcx, %rax
  51. // 把ignore_int的地址填写到正确位置, rax存低8B, rdx存高8B
  52. movl %edx, %ecx
  53. shrl $16, %ecx // 去除低16位
  54. shlq $48, %rcx
  55. addq %rcx, %rax // 填写段内偏移31:16
  56. shrq $32, %rdx // (已经填写了32位,故右移32)
  57. leaq IDT_Table(%rip), %rdi // 获取中断描述符表的首地址,存储到rdi
  58. mov $256, %rcx // 初始化每个中断描述符
  59. repeat_set_idt:
  60. // ====== 循环,初始化总共256个中断描述符 ===
  61. movq %rax, (%rdi) // 保存低8B
  62. movq %rdx, 8(%rdi) // 保存高8B
  63. addq $0x10, %rdi // 转到下一个IDT表项
  64. dec %rcx
  65. jne repeat_set_idt
  66. SetUp_TSS64:
  67. // == 设置64位的任务状态段表 ===
  68. //rdx保存高8B, rax保存低8B
  69. leaq TSS64_Table(%rip), %rdx
  70. xorq %rax, %rax
  71. xorq %rcx, %rcx
  72. // 设置TSS描述符的47:40位为1000 1001
  73. movq $0x89, %rax
  74. shlq $40, %rax
  75. // 设置段基地址31:24
  76. movl %edx, %ecx
  77. shrl $24, %ecx
  78. shlq $56, %rcx
  79. addq %rcx, %rax
  80. xorq %rcx, %rcx
  81. // 设置段基地址23:00
  82. movl %edx, %ecx
  83. andl $0xffffff, %ecx // 清空ecx的中有效值的高8位(也就是上面已经赋值了的)
  84. shlq $16, %rcx
  85. addq %rcx, %rax
  86. addq $103, %rax // 设置段长度
  87. leaq GDT_Table(%rip), %rdi
  88. movq %rax, 80(%rdi) // 把低八B存储到GDT第10项
  89. shrq $32, %rdx
  90. movq %rdx, 88(%rdi) // 高8B存到GDT第11项
  91. // 装载任务状态段寄存器(已改为在main.c中使用load_TR宏进行装载)
  92. // mov $0x50, %ax // 设置起始地址为80
  93. // ltr %ax
  94. // 切换到内核主程序
  95. movq go_to_kernel(%rip), %rax
  96. pushq $0x08
  97. pushq %rax
  98. lretq
  99. go_to_kernel:
  100. .quad Start_Kernel
  101. // ==== 异常/中断处理模块 ignore int: 忽略中断
  102. m_ignore_int:
  103. // 切换到c语言的ignore_int
  104. movq go_to_ignore_int(%rip), %rax
  105. pushq $0x08
  106. pushq %rax
  107. lretq
  108. lretq
  109. go_to_ignore_int:
  110. .quad ignore_int
  111. ENTRY(_stack_start)
  112. .quad initial_proc_union + 32768
  113. // 初始化页表
  114. .align 8 //设置为8byte对齐
  115. .org 0x1000 //设置页表位置为内核执行头程序的0x1000处
  116. __PML4E:
  117. .quad 0x102007 // 用户访问,可读写,已存在, 地址在31~12位
  118. .fill 255,8,0
  119. .quad 0x102007
  120. .fill 255,8,0
  121. .org 0x2000
  122. __PDPTE:
  123. .quad 0x103007 // 用户访问,可读写,已存在
  124. .fill 511,8,0
  125. .org 0x3000
  126. __PDE:
  127. .quad 0x000087 // 用户访问,可读写,已存在
  128. .quad 0x200087
  129. .quad 0x400087
  130. .quad 0x600087
  131. .quad 0x800087
  132. .quad 0xe0000087 /*0x a00000*/
  133. .quad 0xe0200087
  134. .quad 0xe0400087
  135. .quad 0xe0600087 /*0x1000000*/
  136. .quad 0xe0800087
  137. .quad 0xe0a00087
  138. .quad 0xe0c00087
  139. .quad 0xe0e00087
  140. .fill 499,8,0
  141. // GDT表
  142. .section .data
  143. .global GDT_Table // 使得GDT可以被外部程序引用或者访问
  144. GDT_Table:
  145. .quad 0x0000000000000000 // 0 空描述符 0x00
  146. .quad 0x0020980000000000 // 1 内核64位代码段描述符 0x08
  147. .quad 0x0000920000000000 // 2 内核64位数据段描述符 0x10
  148. .quad 0x0000000000000000 // 3 用户32位代码段描述符 0x18
  149. .quad 0x0000000000000000 // 4 用户32位数据段描述符 0x20
  150. .quad 0x0020f80000000000 // 5 用户64位代码段描述符 0x28
  151. .quad 0x0000f20000000000 // 6 用户64位数据段描述符 0x30
  152. .quad 0x00cf9a000000ffff // 7 内核32位代码段描述符 0x38
  153. .quad 0x00cf92000000ffff // 8 内核32位数据段描述符 0x40
  154. .fill 10, 8, 0 // 10-11 TSS(跳过了第9段) 重复十次填充8字节的空间,赋值为0
  155. GDT_END:
  156. GDT_POINTER:
  157. GDT_LIMIT: .word GDT_END - GDT_Table - 1 // GDT的大小
  158. GDT_BASE: .quad GDT_Table
  159. // IDT 表
  160. .global IDT_Table
  161. IDT_Table:
  162. .fill 512, 8, 0 // 设置512*8字节的IDT表的空间
  163. IDT_END:
  164. IDT_POINTER:
  165. IDT_LIMIT: .word IDT_END - IDT_Table - 1
  166. IDT_BASE: .quad IDT_Table
  167. // 64位的TSS表
  168. .global TSS64_Table
  169. TSS64_Table:
  170. .fill 13, 8, 0
  171. TSS64_END:
  172. TSS64_POINTER:
  173. TSS64_LIMIT: .word TSS64_END - TSS64_Table - 1
  174. TSS64_BASE: .quad TSS64_Table