head.S 5.2 KB

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