1// 这是内核执行头程序
2// Created by longjin.
3// 2022/01/20
4
5#include "common/asm.h"
6
7// 以下是来自 multiboot2 规范的定义
8//  How many bytes from the start of the file we search for the header.
9#define MULTIBOOT_SEARCH 32768
10#define MULTIBOOT_HEADER_ALIGN 8
11
12//  The magic field should contain this.
13#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
14
15//  This should be in %eax.
16#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
17
18//  Alignment of multiboot modules.
19#define MULTIBOOT_MOD_ALIGN 0x00001000
20
21//  Alignment of the multiboot info structure.
22#define MULTIBOOT_INFO_ALIGN 0x00000008
23
24//  Flags set in the 'flags' member of the multiboot header.
25
26#define MULTIBOOT_TAG_ALIGN 8
27#define MULTIBOOT_TAG_TYPE_END 0
28#define MULTIBOOT_TAG_TYPE_CMDLINE 1
29#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
30#define MULTIBOOT_TAG_TYPE_MODULE 3
31#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
32#define MULTIBOOT_TAG_TYPE_BOOTDEV 5
33#define MULTIBOOT_TAG_TYPE_MMAP 6
34#define MULTIBOOT_TAG_TYPE_VBE 7
35#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
36#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
37#define MULTIBOOT_TAG_TYPE_APM 10
38#define MULTIBOOT_TAG_TYPE_EFI32 11
39#define MULTIBOOT_TAG_TYPE_EFI64 12
40#define MULTIBOOT_TAG_TYPE_SMBIOS 13
41#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
42#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
43#define MULTIBOOT_TAG_TYPE_NETWORK 16
44#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
45#define MULTIBOOT_TAG_TYPE_EFI_BS 18
46#define MULTIBOOT_TAG_TYPE_EFI32_IH 19
47#define MULTIBOOT_TAG_TYPE_EFI64_IH 20
48#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
49
50#define MULTIBOOT_HEADER_TAG_END 0
51#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
52#define MULTIBOOT_HEADER_TAG_ADDRESS 2
53#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
54#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
55#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
56#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
57#define MULTIBOOT_HEADER_TAG_EFI_BS 7
58#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
59#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
60#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
61
62#define MULTIBOOT_ARCHITECTURE_I386 0
63#define MULTIBOOT_ARCHITECTURE_MIPS32 4
64#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
65
66#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
67#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
68#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
69
70#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
71#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
72
73
74
75// 直接用 -m64 编译出来的是 64 位代码,
76// 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。
77// 得加一层跳转到 64 位的 -m32 代码,开启 long 模式后再跳转到以 -m64 编译的代码中
78// 对于 x86_64,需要在启动阶段进入长模式(IA32E),这意味着需要一个临时页表
79// See https://wiki.osdev.org/Creating_a_64-bit_kernel:
80// With a 32-bit bootstrap in your kernel
81
82// 这部分是从保护模式启动 long 模式的代码
83// 工作在 32bit
84// 声明这一段代码以 32 位模式编译
85.code32
86
87// multiboot2 文件头
88// 计算头长度
89.SET HEADER_LENGTH, multiboot_header_end - multiboot_header
90// 计算校验和
91.SET CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + HEADER_LENGTH)
92// 8 字节对齐
93.section .multiboot_header
94.align MULTIBOOT_HEADER_ALIGN
95// 声明所属段
96
97multiboot_header:
98    // 魔数
99    .long MULTIBOOT2_HEADER_MAGIC
100    // 架构
101    .long MULTIBOOT_ARCHITECTURE_I386
102    // 头长度
103    .long HEADER_LENGTH
104    // 校验和
105    .long CHECKSUM
106    // 添加其它内容在此,详细信息见 Multiboot2 Specification version 2.0.pdf
107
108// 设置帧缓冲区
109.align 8
110framebuffer_tag_start:
111    .short MULTIBOOT_HEADER_TAG_FRAMEBUFFER
112    .short MULTIBOOT_HEADER_TAG_OPTIONAL
113    .long framebuffer_tag_end - framebuffer_tag_start
114    .long 1440
115    .long 900
116    .long 32
117framebuffer_tag_end:
118.align 8
119	.short MULTIBOOT_HEADER_TAG_END
120    // 结束标记
121    .short 0
122    .long 8
123multiboot_header_end:
124
125.section .bootstrap
126
127.global _start
128.type _start, @function
129# 在 multiboot2.cpp 中定义
130
131.extern _start64
132.extern boot_info_addr
133.extern multiboot2_magic
134ENTRY(_start)
135    // 关中断
136    cli
137
138    // multiboot2_info 结构体指针
139    mov %ebx, mb2_info
140    //mov %ebx, %e8
141    // 魔数
142    mov %eax, mb2_magic
143
144    //mov %eax, %e9
145    / 从保护模式跳转到长模式
146    // 1. 允许 PAE
147    mov %cr4, %eax
148    or $(1<<5), %eax
149    mov %eax, %cr4
150    // 2. 设置临时页表
151    // 最高级
152    mov $pml4, %eax
153    mov $pdpt, %ebx
154    or $0x3, %ebx
155    mov %ebx, 0(%eax)
156
157    // 次级
158    mov $pdpt, %eax
159    mov $pd, %ebx
160    or $0x3, %ebx
161    mov %ebx, 0(%eax)
162
163    // 次低级
164    mov $pd, %eax
165    mov $pt, %ebx
166    or $0x3, %ebx
167    mov %ebx, 0(%eax)
168
169    // 最低级
170    // 循环 512 次,填满一页
171    mov $512, %ecx
172    mov $pt, %eax
173    mov $0x3, %ebx
174.fill_pt:
175    mov %ebx, 0(%eax)
176    add $0x1000, %ebx
177    add $8, %eax
178    loop .fill_pt
179
180.global enter_head_from_ap_boot
181enter_head_from_ap_boot:
182    // 填写 CR3
183    mov $pml4, %eax
184    mov %eax, %cr3
185
186    // 3. 切换到 long 模式
187    mov $0xC0000080, %ecx
188    rdmsr
189    or $(1<<8), %eax
190    wrmsr
191
192    // 4. 开启分页
193    mov %cr0, %eax
194    or $(1<<31), %eax
195    mov %eax, %cr0
196
197    // 5. 重新设置 GDT
198    mov $gdt64_pointer, %eax
199    lgdt 0(%eax)
200
201    jmp $0x8, $ready_to_start_64
202    hlt
203    ret
204.code64
205.global ready_to_start_64
206ready_to_start_64:
207
208    mov $0x10, %ax
209    mov %ax, %ds
210    mov %ax, %es
211    mov %ax, %fs
212    mov %ax, %ss
213    mov $0x7e00, %esp
214
215
216    //6. 跳转到start64
217    movq switch_to_start64(%rip), %rax
218    pushq $0x08 //段选择子
219    pushq %rax
220    lretq
221
222switch_to_start64:
223    .quad _start64
224
225
226.code64
227is_from_ap:
228
229    hlt
230
231.global _start64
232.type _start64, @function
233.extern Start_Kernel
234ENTRY(_start64)
235
236
237    // 初始化寄存器
238    mov $0x10, %ax
239    mov %ax, %ds
240    mov %ax, %es
241    mov %ax, %fs
242    mov %ax, %ss
243    mov $0x7e00, %esp
244
245// === 加载GDTR ====
246    lgdt GDT_POINTER(%rip) //这里我没搞明白rip相对寻址, 看了文档,大概是用来实现PIC的(position independent code)
247    //lgdt $GDT_POINTER
248// === 加载IDTR ====
249    lidt IDT_POINTER(%rip)
250    //lidt $IDT_POINTER
251    movq GDT_POINTER(%rip), %r12
252    movq head_stack_start(%rip), %rsp
253
254    // 分支,判断是否为apu
255    movq	$0x1b,	%rcx		// 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu
256	rdmsr
257	bt	$8,	%rax
258	jnc	load_cr3
259
260    // 2. 设置临时页表
261    // 最高级
262    mov $__PML4E, %eax
263    mov $__PDPTE, %ebx
264    or $0x3, %ebx
265    mov %ebx, 0(%eax)
266    mov %ebx, 256(%eax)
267
268    // 次级
269    mov $__PDPTE, %eax
270    mov $__PDE, %ebx
271    or $0x3, %ebx
272    mov %ebx, 0(%eax)
273
274
275// ==== 加载CR3寄存器
276
277load_cr3:
278
279    movq $__PML4E, %rax //设置页目录基地址
280
281    movq %rax, %cr3
282
283    movq switch_seg(%rip), %rax
284    // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
285    // 实在是太妙了!Amazing!
286    pushq $0x08 //段选择子
287    pushq %rax
288    lretq
289
290// 64位模式的代码
291switch_seg:
292
293    .quad entry64
294
295
296entry64:
297
298    movq $0x10, %rax
299    movq %rax, %ds
300    movq %rax, %es
301    movq %rax, %gs
302    movq %rax, %ss
303
304    movq head_stack_start(%rip), %rsp //rsp的地址
305
306    // 重新加载GDT和IDT,加载到高地址
307    leaq GDT_Table(%rip), %r8
308    leaq GDT_END(%rip), %r9
309
310    subq %r8, %r9
311    movq %r9, %r13    // GDT size
312
313    leaq IDT_Table(%rip), %r8
314    leaq IDT_END(%rip), %r9
315
316    subq %r8, %r9
317    movq %r9, %r12    // IDT size
318
319    lgdt GDT_POINTER64(%rip)
320    lidt IDT_POINTER64(%rip)
321
322    // 分支,判断是否为apu
323    movq	$0x1b,	%rcx		// 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu
324	rdmsr
325	bt	$8,	%rax
326	jnc	start_smp
327
328setup_IDT:
329    // 该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,
330    leaq m_ignore_int(%rip),  %rdx // 将ignore_int的地址暂时存到中段描述符的高8B
331    movq $(0x08 << 16), %rax  // 设置段选择子。由IDT结构和段选择子结构可知,本行设置段基地址为0x100000,TI=0,RPL=0
332    movw %dx, %ax
333
334    movq $ (0x8e00 << 32), %rcx // 设置Type=1110 P=1 DPL=00 0=0
335    addq %rcx, %rax
336
337    // 把ignore_int的地址填写到正确位置, rax存低8B, rdx存高8B
338    movl %edx, %ecx
339    shrl $16, %ecx // 去除低16位
340    shlq $48, %rcx
341    addq %rcx, %rax // 填写段内偏移31:16
342
343    shrq $32, %rdx // (已经填写了32位,故右移32)
344
345    leaq IDT_Table(%rip), %rdi // 获取中断描述符表的首地址,存储到rdi
346    mov $256, %rcx  // 初始化每个中断描述符
347
348repeat_set_idt:
349    // ====== 循环,初始化总共256个中断描述符 ===
350    movq %rax, (%rdi)   // 保存低8B
351    movq %rdx, 8(%rdi)  // 保存高8B
352
353    addq $0x10, %rdi // 转到下一个IDT表项
354    dec %rcx
355    jne repeat_set_idt
356
357SetUp_TSS64:
358    // == 设置64位的任务状态段表 ===
359    //rdx保存高8B, rax保存低8B
360    leaq TSS64_Table(%rip), %rdx    // 获取定义在process.c中的initial_tss[0]的地址
361
362    movq $0xffff800000000000, %r8
363    addq %r8, %rdx
364
365    xorq %rax, %rax
366    xorq %rcx, %rcx
367
368    // 设置TSS描述符的47:40位为1000 1001
369    movq $0x89, %rax
370    shlq $40, %rax
371
372    // 设置段基地址31:24
373    movl %edx, %ecx
374    shrl $24, %ecx
375    shlq $56, %rcx
376    addq %rcx, %rax
377
378    xorq %rcx, %rcx
379
380    // 设置段基地址23:00
381    movl %edx, %ecx
382    andl $0xffffff, %ecx // 清空ecx的中有效值的高8位(也就是上面已经赋值了的)
383    shlq $16, %rcx
384    addq %rcx, %rax
385
386    addq $103, %rax // 设置段长度
387
388    leaq GDT_Table(%rip), %rdi
389    movq %rax, 80(%rdi) // 把低八B存储到GDT第10项
390    shrq $32, %rdx
391    movq %rdx, 88(%rdi) // 高8B存到GDT第11项
392
393    // 装载任务状态段寄存器(已改为在main.c中使用load_TR宏进行装载)
394    // mov $0x50, %ax // 设置起始地址为80
395    // ltr %ax
396
397    //now enable SSE and the like
398    movq %cr0, %rax
399    and $0xFFFB, %ax		//clear coprocessor emulation CR0.EM
400    or $0x2, %ax			//set coprocessor monitoring  CR0.MP
401    movq %rax, %cr0
402    movq %cr4, %rax
403    or $(3 << 9), %ax		//set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
404    movq %rax, %cr4
405
406
407
408    movq	go_to_kernel(%rip),	%rax		/* movq address */
409	pushq	$0x08
410	pushq	%rax
411
412
413
414    movq mb2_info, %r15
415    movq mb2_magic, %r14
416
417
418	lretq
419
420go_to_kernel:
421    .quad Start_Kernel
422
423start_smp:
424
425
426    //now enable SSE and the like
427    movq %cr0, %rax
428    and $0xFFFB, %ax		//clear coprocessor emulation CR0.EM
429    or $0x2, %ax			//set coprocessor monitoring  CR0.MP
430    movq %rax, %cr0
431    movq %cr4, %rax
432    or $(3 << 9), %ax		//set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
433    movq %rax, %cr4
434
435
436	movq	go_to_smp_kernel(%rip),	%rax		/* movq address */
437	pushq	$0x08
438	pushq	%rax
439
440/*
441    // 重新加载GDT和IDT,加载到高地址
442    leaq GDT_Table(%rip), %r8
443    leaq GDT_END(%rip), %r9
444
445    subq %r8, %r9
446    movq %r9, %r13    // GDT size
447
448    leaq IDT_Table(%rip), %r8
449    leaq IDT_END(%rip), %r9
450
451    subq %r8, %r9
452    movq %r9, %r12    // IDT size
453
454    lgdt GDT_POINTER64(%rip)
455    lidt IDT_POINTER64(%rip)
456*/
457	lretq
458
459go_to_smp_kernel:
460
461	.quad	smp_ap_start
462
463// ==== 异常/中断处理模块 ignore int: 忽略中断
464// (该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,从而重设ignore_int的中断入点)
465m_ignore_int:
466// 切换到c语言的ignore_int
467    movq go_to_ignore_int(%rip), %rax
468    pushq $0x08
469    pushq %rax
470    lretq
471
472
473
474go_to_ignore_int:
475    .quad ignore_int_handler
476
477
478ENTRY(head_stack_start)
479    .quad initial_proc_union + 32768
480
481
482// 初始化页表
483.align 0x1000 //设置为4k对齐
484//.org 0x1000 //设置页表位置为内核执行头程序的0x1000处
485
486__PML4E:
487    .quad 0x103007 // 用户访问,可读写,已存在, 地址在31~12位
488    .fill	255,8,0
489	.quad 0x103003
490	.fill	255,8,0
491
492.org	0x2000
493
494__PDPTE:
495
496	.quad	0x104003 // 用户访问,可读写,已存在
497	.fill	511,8,0
498
499.org	0x3000
500
501__PDE:
502
503	.quad	0x000083    // 用户访问,可读写,已存在
504	.quad	0x200083
505	.quad	0x400083
506	.quad	0x600083
507	.quad	0x800083
508    .quad	0xa00083
509	.quad	0xc00083
510	.quad	0xe00083
511	.quad	0x1000083
512	.quad	0x1200083
513	.quad	0x1400083
514	.quad	0x1600083
515	.quad	0x1800083
516	.quad	0x1a00083
517	.quad	0x1c00083
518	.quad	0x1e00083
519	.quad	0x2000083
520	.quad	0x2200083
521	.quad	0x2400083
522	.quad	0x2600083
523	.quad	0x2800083
524	.quad	0x2a00083
525	.quad	0x2c00083
526	.quad	0x2e00083
527	.quad	0x3000083
528	.quad	0x3200083
529	.quad	0x3400083
530	.quad	0x3600083
531
532
533	.quad	0xe0000083		/*虚拟地址0x 3000000 初始情况下,帧缓冲区映射到这里*/
534	.quad	0xe0200083
535	.quad	0xe0400083
536	.quad	0xe0600083		/*0x1000000*/
537	.quad	0xe0800083
538	.quad	0xe0a00083
539	.quad	0xe0c00083
540	.quad	0xe0e00083
541    .quad	0xe1000083
542    .quad	0xe1200083
543    .quad	0xe1400083
544    .quad	0xe1600083
545    .quad	0xe1800083
546    .quad	0xe1a00083
547    .quad	0xe1c00083
548    .quad	0xe1e00083
549	.fill	468,8,0
550
551
552// GDT表
553
554.align 16
555.global GDT_Table // 使得GDT可以被外部程序引用或者访问
556
557GDT_Table:
558    .quad 0x0000000000000000 // 0 空描述符 0x00
559    .quad 0x0020980000000000 // 1 内核64位代码段描述符 0x08
560    .quad 0x0000920000000000 // 2 内核64位数据段描述符 0x10
561    .quad 0x0000000000000000 // 3 用户32位代码段描述符 0x18
562    .quad 0x0000000000000000 // 4 用户32位数据段描述符 0x20
563    .quad 0x0020f80000000000 // 5 用户64位代码段描述符 0x28
564    .quad 0x0000f20000000000 // 6 用户64位数据段描述符 0x30
565    .quad 0x00cf9a000000ffff // 7 内核32位代码段描述符 0x38
566    .quad 0x00cf92000000ffff // 8 内核32位数据段描述符 0x40
567    .fill 100, 8, 0           // 10-11 TSS(跳过了第9段)  重复十次填充8字节的空间,赋值为0   长模式下,每个TSS长度为128bit
568GDT_END:
569
570.global GDT_POINTER
571GDT_POINTER:
572GDT_LIMIT: .word GDT_END - GDT_Table - 1 // GDT的大小
573GDT_BASE: .quad GDT_Table
574
575.global GDT_POINTER64
576GDT_POINTER64:
577GDT_LIMIT64: .word GDT_END - GDT_Table - 1 // GDT的大小
578GDT_BASE64: .quad GDT_Table + 0xffff800000000000
579
580// IDT 表
581.global IDT_Table
582
583IDT_Table:
584    .fill 512, 8, 0 // 设置512*8字节的IDT表的空间
585IDT_END:
586
587.global IDT_POINTER
588IDT_POINTER:
589IDT_LIMIT: .word IDT_END - IDT_Table - 1
590IDT_BASE: .quad IDT_Table
591
592.global IDT_POINTER64
593IDT_POINTER64:
594IDT_LIMIT64: .word IDT_END - IDT_Table - 1
595IDT_BASE64: .quad IDT_Table + 0xffff800000000000
596
597// 64位的TSS表
598.global TSS64_Table
599
600TSS64_Table:
601    .fill 13, 8, 0
602TSS64_END:
603
604.section .bootstrap.data
605mb2_magic: .quad 0
606mb2_info: .quad 0
607
608.code32
609// 临时页表 4KB/页
610.align 0x1000
611.global pml4
612pml4:
613    .skip 0x1000
614pdpt:
615    .skip 0x1000
616pd:
617    .skip 0x1000
618pt:
619    .skip 0x1000
620
621// 临时 GDT
622.align 16
623gdt64:
624null_desc:
625    .short 0xFFFF
626    .short 0
627    .byte 0
628    .byte 0
629    .byte 0
630    .byte 0
631code_desc:
632    .short 0
633    .short 0
634    .byte 0
635    .byte 0x9A
636    .byte 0x20
637    .byte 0
638data_desc:
639    .short 0
640    .short 0
641    .byte 0
642    .byte 0x92
643    .byte 0
644    .byte 0
645user_code_desc:
646    .short 0
647    .short 0
648    .byte 0
649    .byte 0xFA
650    .byte 0x20
651    .byte 0
652user_data_desc:
653    .short 0
654    .short 0
655    .byte 0
656    .byte 0xF2
657    .byte 0
658    .byte 0
659gdt64_pointer:
660    .short gdt64_pointer-gdt64-1
661    .quad gdt64
662gdt64_pointer64:
663    .short gdt64_pointer-gdt64-1
664    .quad gdt64
665
666