boot.S 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. #include <AK/Platform.h>
  2. #include <Kernel/Prekernel/Prekernel.h>
  3. .code32
  4. .section .stack, "aw", @nobits
  5. stack_bottom:
  6. .skip 32768
  7. stack_top:
  8. .global kernel_cmdline
  9. kernel_cmdline:
  10. .skip 4096
  11. .section .page_tables, "aw", @nobits
  12. .align 4096
  13. #if ARCH(X86_64)
  14. .global boot_pml4t
  15. boot_pml4t:
  16. .skip 4096
  17. #endif
  18. .global boot_pdpt
  19. boot_pdpt:
  20. .skip 4096
  21. .global boot_pd0
  22. boot_pd0:
  23. .skip 4096
  24. .global boot_pd0_pts
  25. boot_pd0_pts:
  26. .skip 4096 * (MAX_KERNEL_SIZE >> 21)
  27. .global boot_pd_kernel
  28. boot_pd_kernel:
  29. .skip 4096
  30. .global boot_pd_kernel_pt0
  31. boot_pd_kernel_pt0:
  32. .skip 4096
  33. .global boot_pd_kernel_image_pts
  34. boot_pd_kernel_image_pts:
  35. .skip 4096 * (MAX_KERNEL_SIZE >> 21)
  36. .global boot_pd_kernel_pt1023
  37. boot_pd_kernel_pt1023:
  38. .skip 4096
  39. .section .text
  40. .global start
  41. .type start, @function
  42. .extern init
  43. .type init, @function
  44. .global reload_cr3
  45. .type reload_cr3, @function
  46. .extern multiboot_info_ptr
  47. .type multiboot_info_ptr, @object
  48. /*
  49. construct the following (64-bit PML4T) page table layout:
  50. (the PML4T part is not used for 32-bit x86)
  51. pml4t:
  52. 0: pdpt (0-512GB)
  53. pdpt
  54. 0: boot_pd0 (0-1GB)
  55. 1: n/a (1-2GB)
  56. 2: n/a (2-3GB)
  57. 3: n/a (3-4GB)
  58. boot_pd0 : 512 PDEs
  59. boot_pd0_pts (0MB - MAX_KERNEL_SIZE) (id 512 4KB pages)
  60. the page tables each contain 512 PTEs that map individual 4KB pages
  61. */
  62. #if ARCH(X86_64)
  63. gdt64:
  64. .quad 0
  65. gdt64code:
  66. .quad (1<<43) | (1<<44) | (1<<47) | (1<<53) /* executable, code segment, present, 64-bit */
  67. .global gdt64ptr
  68. gdt64ptr:
  69. .short . - gdt64 - 1
  70. .quad gdt64
  71. .set code64_sel_value, gdt64code - gdt64
  72. .global code64_sel
  73. code64_sel:
  74. .short code64_sel_value
  75. #endif
  76. start:
  77. jmp real_start
  78. /*
  79. this function assumes that paging is disabled (or everything is mapped 1:1)
  80. param 1: pointer to string ended with null terminator (C string)
  81. */
  82. print_and_halt:
  83. /* from now on, we don't really care about booting because we are missing required CPU features such as PAE or long mode.
  84. the flow from now is like so:
  85. 1. Copy all necessary parts to low memory section in RAM
  86. 2. Jump to that section
  87. 3. In that section we do:
  88. a. exit protected mode to pure 16 bit real mode
  89. b. load the "<missing feature> is not supported" String, call the BIOS print to screen service
  90. c. halt
  91. */
  92. .equ COPIED_STRING_LOCATION, 0x400
  93. .equ GDT_REAL_MODE_LOCATION, 0x45000
  94. .equ EXITING_PROTECTED_MODE_CODE_LOCATION, 0x10000
  95. .equ REAL_MODE_CODE, 0x500
  96. .equ PROTECTED_MODE_16_BIT_CODE, 0x600
  97. movl %esp, %ebp
  98. movl 4(%ebp), %edi
  99. /* Copy string to low memory section */
  100. movl %edi, %esi
  101. xor %ecx, %ecx
  102. pushl %eax
  103. pushl %edi
  104. check_string_length:
  105. movb (%edi), %ah
  106. cmp $0, %ah
  107. je check_string_length_exit
  108. inc %ecx
  109. inc %edi
  110. jmp check_string_length
  111. check_string_length_exit:
  112. popl %edi
  113. popl %eax
  114. /* source address of the code is ESI */
  115. movw %cx, (COPIED_STRING_LOCATION)
  116. mov $COPIED_STRING_LOCATION + 2, %edi /* destination address of the code */
  117. rep movsb
  118. /* Copy gdt_table_real_mode to low memory section */
  119. movl $gdt_table_real_mode, %eax
  120. movl $gdt_table_real_mode_end, %ebx
  121. movl %ebx, %ecx
  122. sub %eax, %ecx
  123. mov %eax, %esi /* source address of the code */
  124. mov $GDT_REAL_MODE_LOCATION, %edi /* destination address of the code */
  125. rep movsb
  126. /* Copy protected_mode_16_bit to real_mode to low memory section */
  127. movl $protected_mode_16_bit, %eax
  128. movl $real_mode, %ebx
  129. movl %ebx, %ecx
  130. sub %eax, %ecx
  131. mov %eax, %esi /* source address of the code */
  132. mov $PROTECTED_MODE_16_BIT_CODE, %edi /* destination address of the code */
  133. rep movsb
  134. /* Copy real_mode to end_of_print_and_halt_function to low memory section */
  135. movl $real_mode, %eax
  136. movl $end_of_print_and_halt_function, %ebx
  137. movl %ebx, %ecx
  138. sub %eax, %ecx
  139. mov %eax, %esi /* source address of the code */
  140. mov $REAL_MODE_CODE, %edi /* destination address of the code */
  141. rep movsb
  142. /* Copy all opcodes from exiting_real_mode label to protected_mode_16_bit label to low memory RAM */
  143. movl $exiting_real_mode, %eax
  144. movl $protected_mode_16_bit, %ebx
  145. movl %ebx, %ecx
  146. sub %eax, %ecx
  147. mov %eax, %esi /* source address of the code */
  148. mov $EXITING_PROTECTED_MODE_CODE_LOCATION, %edi /* destination address of the code */
  149. pushl %edi
  150. rep movsb
  151. popl %edi
  152. pushl %edi
  153. ret
  154. gdt_table_real_mode:
  155. .quad 0 /* Empty entry */
  156. .short 0xffff
  157. .short 0
  158. .byte 0
  159. .byte 0b10011010
  160. .byte 0b00001111
  161. .byte 0x0
  162. .short 0xffff
  163. .short 0
  164. .byte 0
  165. .byte 0b10010010
  166. .byte 0b00001111
  167. .byte 0x0
  168. gdt_table_real_mode_end:
  169. no_long_mode_string:
  170. .asciz "Your computer does not support long mode (64-bit mode). Halting!"
  171. no_pae_string:
  172. .asciz "Your computer does not support PAE. Halting!"
  173. kernel_image_too_big_string:
  174. .asciz "Error: Kernel Image too big for memory slot. Halting!"
  175. /*
  176. This part is completely standalone - it doesn't involve any location from this
  177. near code. It uses arbitrary locations in the low memory section of the RAM.
  178. We don't really worry about where are these locations, because we only want to quickly
  179. print a string and halt.
  180. */
  181. .code32
  182. exiting_real_mode:
  183. /* Build IDT pointer and load it */
  184. mov $0x50000, %eax
  185. pushl %eax
  186. movl $0x3ff, 0(%eax)
  187. add $2, %eax
  188. movl $0, 0(%eax)
  189. popl %eax
  190. lidt (%eax)
  191. /* Build GDT pointer and load it */
  192. mov $0x40000, %eax
  193. pushl %eax
  194. movl $32, 0(%eax)
  195. add $2, %eax
  196. movl $GDT_REAL_MODE_LOCATION, 0(%eax)
  197. popl %eax
  198. lgdt (%eax)
  199. /* far jump to protected_mode_16_bit in 0x5000 */
  200. pushw $8
  201. push $PROTECTED_MODE_16_BIT_CODE
  202. lret
  203. hlt
  204. .code16
  205. protected_mode_16_bit:
  206. xor %eax, %eax
  207. movl $0x10, %eax
  208. movw %ax, %ds
  209. and $0xFE, %al /* switch to pure real mode */
  210. mov %eax, %cr0
  211. mov $0x10, %eax
  212. movl %eax, %cr0
  213. pushw $0
  214. push $REAL_MODE_CODE
  215. lret
  216. hlt
  217. real_mode:
  218. movw $0x7000, %ax
  219. movl $0x0000, %esp
  220. movw %ax, %ss
  221. xor %ax, %ax
  222. movw %ax, %ds
  223. movw %ax, %es
  224. movw %ax, %fs
  225. movw %ax, %gs
  226. mov $0x3, %ax
  227. int $0x10
  228. movb $0x13, %ah
  229. movb $0x0, %bh
  230. movb $0xf, %bl
  231. movw (COPIED_STRING_LOCATION), %cx
  232. movw $0, %dx
  233. movw $COPIED_STRING_LOCATION + 2, %bp
  234. int $0x10
  235. movl $0xdeadcafe, %ebx
  236. cli
  237. hlt
  238. end_of_print_and_halt_function:
  239. .code32
  240. real_start:
  241. cli
  242. cld
  243. mov $end_of_prekernel_image, %esi
  244. cmp $MAX_KERNEL_SIZE, %esi
  245. jbe kernel_not_too_large
  246. movl $kernel_image_too_big_string, %esi
  247. pushl %esi
  248. call print_and_halt
  249. /* We should not return, but just in case, halt */
  250. hlt
  251. kernel_not_too_large:
  252. /* test for PAE presence, save the most important registers from corruption */
  253. pushl %eax
  254. pushl %edx
  255. pushl %ebx
  256. movl $0x1, %eax /* PAE presence is in CPUID input 0x1 */
  257. cpuid
  258. testl $(1 << 6), %edx /* Test if the PAE-bit, which is bit 6, is set in the edx register. */
  259. jnz pae_supported /* If the bit is not set, there is no PAE capability. */
  260. /* Since there is no PAE capability, halt with an error message */
  261. movl $no_pae_string, %esi
  262. pushl %esi
  263. call print_and_halt
  264. /* We should not return, but just in case, halt */
  265. hlt
  266. #if ARCH(X86_64)
  267. pae_supported:
  268. movl $0x80000001, %eax
  269. cpuid
  270. testl $(1 << 29), %edx /* Test if the LM-bit, which is bit 29, is set in the edx register. */
  271. jnz long_mode_supported /* If LM-bit is not enabled, there is no long mode. */
  272. /* Since there is no long mode, halt with an error message */
  273. movl $no_long_mode_string, %esi
  274. pushl %esi
  275. call print_and_halt
  276. /* We should not return, but just in case, halt */
  277. hlt
  278. /* If both PAE and long mode is supported, continue with booting the system */
  279. long_mode_supported:
  280. /* restore the pushed registers and continue with booting */
  281. popl %ebx
  282. popl %edx
  283. popl %eax
  284. #else
  285. /* If PAE is supported, continue with booting the system */
  286. pae_supported:
  287. /* restore the pushed registers and continue with booting */
  288. popl %ebx
  289. popl %edx
  290. popl %eax
  291. #endif
  292. /* We don't know where the bootloader might have put the command line.
  293. * It might be at an inconvenient location that we're not about to map,
  294. * so let's just copy it to a convenient location while we have the whole
  295. * memory space identity-mapped anyway. :^)
  296. */
  297. movl %ebx, %esi
  298. addl $16, %esi
  299. movl (%esi), %esi
  300. movl $1024, %ecx
  301. movl $kernel_cmdline, %edi
  302. rep movsl
  303. #if ARCH(X86_64)
  304. /* clear pml4t */
  305. movl $boot_pml4t, %edi
  306. movl $1024, %ecx
  307. xorl %eax, %eax
  308. rep stosl
  309. /* set up pml4t[0] */
  310. movl $boot_pml4t, %edi
  311. movl $boot_pdpt, 0(%edi)
  312. /* R/W + Present */
  313. orl $0x3, 0(%edi)
  314. #endif
  315. /* clear pdpt */
  316. movl $boot_pdpt, %edi
  317. movl $1024, %ecx
  318. xorl %eax, %eax
  319. rep stosl
  320. /* set up pdpt[0] and pdpt[3] */
  321. movl $boot_pdpt, %edi
  322. #if ARCH(X86_64)
  323. movl $(boot_pd0 + 3), 0(%edi)
  324. #else
  325. movl $(boot_pd0 + 1), 0(%edi)
  326. #endif
  327. /* clear pd0 */
  328. movl $boot_pd0, %edi
  329. movl $1024, %ecx
  330. xorl %eax, %eax
  331. rep stosl
  332. /* clear pd0's PTs */
  333. movl $boot_pd0_pts, %edi
  334. movl $(1024 * (MAX_KERNEL_SIZE >> 21)), %ecx
  335. xorl %eax, %eax
  336. rep stosl
  337. /* add boot_pd0_pts to boot_pd0 */
  338. movl $(MAX_KERNEL_SIZE >> 21), %ecx
  339. movl $boot_pd0, %edi
  340. movl $boot_pd0_pts, %eax
  341. 1:
  342. movl %eax, 0(%edi)
  343. /* R/W + Present */
  344. orl $0x3, 0(%edi)
  345. addl $8, %edi
  346. addl $4096, %eax
  347. loop 1b
  348. /* identity map the 0MB to MAX_KERNEL_SIZE range */
  349. movl $(512 * (MAX_KERNEL_SIZE >> 21)), %ecx
  350. movl $boot_pd0_pts, %edi
  351. xorl %eax, %eax
  352. 1:
  353. movl %eax, 0(%edi)
  354. /* R/W + Present */
  355. orl $0x3, 0(%edi)
  356. addl $8, %edi
  357. addl $4096, %eax
  358. loop 1b
  359. #if ARCH(X86_64)
  360. /* point CR3 to PML4T */
  361. movl $boot_pml4t, %eax
  362. #else
  363. /* point CR3 to PDPT */
  364. movl $boot_pdpt, %eax
  365. #endif
  366. movl %eax, %cr3
  367. /* enable PAE + PSE */
  368. movl %cr4, %eax
  369. orl $0x60, %eax
  370. movl %eax, %cr4
  371. #if ARCH(X86_64)
  372. 1:
  373. /* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
  374. mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
  375. rdmsr /* Read from the model-specific register.*/
  376. or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/
  377. wrmsr /* Write to the model-specific register.*/
  378. #endif
  379. /* enable PG */
  380. movl %cr0, %eax
  381. orl $0x80000000, %eax
  382. movl %eax, %cr0
  383. /* set up stack */
  384. mov $stack_top, %esp
  385. and $-16, %esp
  386. #if ARCH(X86_64)
  387. /* Now we are in 32-bit compatibility mode, We still need to load a 64-bit GDT */
  388. mov $gdt64ptr, %eax
  389. lgdt (%eax)
  390. ljmpl $code64_sel_value, $1f
  391. .code64
  392. 1:
  393. movl %ebx, %ebx
  394. movq %rbx, multiboot_info_ptr
  395. mov $0, %ax
  396. mov %ax, %ss
  397. mov %ax, %ds
  398. mov %ax, %es
  399. mov %ax, %fs
  400. mov %ax, %gs
  401. #else
  402. movl %ebx, multiboot_info_ptr
  403. #endif
  404. call reload_cr3
  405. call init
  406. cli
  407. loop:
  408. hlt
  409. jmp loop
  410. reload_cr3:
  411. #if ARCH(X86_64)
  412. pushq %rax
  413. mov %cr3, %rax
  414. mov %rax, %cr3
  415. popq %rax
  416. #else
  417. pushl %eax
  418. movl %cr3, %eax
  419. movl %eax, %cr3
  420. popl %eax
  421. #endif
  422. ret