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// 设置帧缓冲区(同时在这里设置qemu的分辨率, 默认为: 1440*900, 还支持: 640*480, 等)
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_apu_cr3
259
260    // 2. 设置临时页表
261    // 最高级
262    mov $__PML4E, %eax
263    mov $__PDPTE, %ebx
264    or $0x3, %ebx
265    mov %ebx, 0(%eax)
266
267    mov $__PML4E, %eax
268    // 加256个表项, 映射高地址
269    add $2048, %eax
270    mov %ebx, 0(%eax)
271
272    // 次级
273    mov $__PDPTE, %eax
274    mov $__PDE, %ebx
275    or $0x3, %ebx
276    mov %ebx, 0(%eax)
277
278    // 次低级
279    mov $__PDE, %eax
280    mov $50, %ecx
281    mov $__PT_S, %ebx
282    or $0x3, %ebx
283.fill_pde_64:
284    mov %ebx, 0(%eax)
285    add $0x1000, %ebx
286    add $8, %eax
287    loop .fill_pde_64
288
289    // 最低级
290    // 循环 512*50=25600 次,填满50页
291    mov $25600, %ecx
292    mov $__PT_S, %eax
293    mov $0x3, %ebx
294.fill_pt_64:
295    mov %ebx, 0(%eax)
296    add $0x1000, %ebx
297    add $8, %eax
298    loop .fill_pt_64
299
300
301// ==== 加载CR3寄存器
302
303load_cr3:
304
305    movq $__PML4E, %rax //设置页目录基地址
306
307    movq %rax, %cr3
308    jmp to_switch_seg
309
310load_apu_cr3:
311    // 由于内存管理模块重置了页表,因此ap核心初始化的时候,需要使用新的内核页表。
312    // 这个页表的值由smp模块设置到__APU_START_CR3变量中
313
314    // 加载__APU_START_CR3中的值
315    movq $__APU_START_CR3, %rax
316    movq 0(%rax), %rax
317    movq %rax, %cr3
318    jmp to_switch_seg
319
320to_switch_seg:
321
322    movq switch_seg(%rip), %rax
323    // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
324    // 实在是太妙了!Amazing!
325    pushq $0x08 //段选择子
326    pushq %rax
327    lretq
328
329// 64位模式的代码
330switch_seg:
331
332    .quad entry64
333
334
335entry64:
336
337    movq $0x10, %rax
338    movq %rax, %ds
339    movq %rax, %es
340    movq %rax, %gs
341    movq %rax, %ss
342
343    movq head_stack_start(%rip), %rsp //rsp的地址
344
345    // 重新加载GDT和IDT,加载到高地址
346    leaq GDT_Table(%rip), %r8
347    leaq GDT_END(%rip), %r9
348
349    subq %r8, %r9
350    movq %r9, %r13    // GDT size
351
352    leaq IDT_Table(%rip), %r8
353    leaq IDT_END(%rip), %r9
354
355    subq %r8, %r9
356    movq %r9, %r12    // IDT size
357
358    lgdt GDT_POINTER64(%rip)
359    lidt IDT_POINTER64(%rip)
360
361    // 分支,判断是否为apu
362    movq	$0x1b,	%rcx		// 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu
363	rdmsr
364	bt	$8,	%rax
365	jnc	start_smp
366
367setup_IDT:
368    // 该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,
369    leaq m_ignore_int(%rip),  %rdx // 将ignore_int的地址暂时存到中段描述符的高8B
370    movq $(0x08 << 16), %rax  // 设置段选择子。由IDT结构和段选择子结构可知,本行设置段基地址为0x100000,TI=0,RPL=0
371    movw %dx, %ax
372
373    movq $ (0x8e00 << 32), %rcx // 设置Type=1110 P=1 DPL=00 0=0
374    addq %rcx, %rax
375
376    // 把ignore_int的地址填写到正确位置, rax存低8B, rdx存高8B
377    movl %edx, %ecx
378    shrl $16, %ecx // 去除低16位
379    shlq $48, %rcx
380    addq %rcx, %rax // 填写段内偏移31:16
381
382    shrq $32, %rdx // (已经填写了32位,故右移32)
383
384    leaq IDT_Table(%rip), %rdi // 获取中断描述符表的首地址,存储到rdi
385    mov $256, %rcx  // 初始化每个中断描述符
386
387repeat_set_idt:
388    // ====== 循环,初始化总共256个中断描述符 ===
389    movq %rax, (%rdi)   // 保存低8B
390    movq %rdx, 8(%rdi)  // 保存高8B
391
392    addq $0x10, %rdi // 转到下一个IDT表项
393    dec %rcx
394    jne repeat_set_idt
395
396SetUp_TSS64:
397    // == 设置64位的任务状态段表 ===
398    //rdx保存高8B, rax保存低8B
399    leaq TSS64_Table(%rip), %rdx    // 获取定义在process.c中的initial_tss[0]的地址
400
401    movq $0xffff800000000000, %r8
402    addq %r8, %rdx
403
404    xorq %rax, %rax
405    xorq %rcx, %rcx
406
407    // 设置TSS描述符的47:40位为1000 1001
408    movq $0x89, %rax
409    shlq $40, %rax
410
411    // 设置段基地址31:24
412    movl %edx, %ecx
413    shrl $24, %ecx
414    shlq $56, %rcx
415    addq %rcx, %rax
416
417    xorq %rcx, %rcx
418
419    // 设置段基地址23:00
420    movl %edx, %ecx
421    andl $0xffffff, %ecx // 清空ecx的中有效值的高8位(也就是上面已经赋值了的)
422    shlq $16, %rcx
423    addq %rcx, %rax
424
425    addq $103, %rax // 设置段长度
426
427    leaq GDT_Table(%rip), %rdi
428    movq %rax, 80(%rdi) // 把低八B存储到GDT第10项
429    shrq $32, %rdx
430    movq %rdx, 88(%rdi) // 高8B存到GDT第11项
431
432    // 装载任务状态段寄存器(已改为在main.c中使用load_TR宏进行装载)
433    // mov $0x50, %ax // 设置起始地址为80
434    // ltr %ax
435
436    //now enable SSE and the like
437    movq %cr0, %rax
438    and $0xFFFB, %ax		//clear coprocessor emulation CR0.EM
439    or $0x2, %ax			//set coprocessor monitoring  CR0.MP
440    movq %rax, %cr0
441    movq %cr4, %rax
442    or $(3 << 9), %ax		//set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
443    movq %rax, %cr4
444
445
446
447    movq	go_to_kernel(%rip),	%rax		/* movq address */
448	pushq	$0x08
449	pushq	%rax
450
451
452
453    movq mb2_info, %r15
454    movq mb2_magic, %r14
455
456
457	lretq
458
459go_to_kernel:
460    .quad Start_Kernel
461
462start_smp:
463
464
465    //now enable SSE and the like
466    movq %cr0, %rax
467    and $0xFFFB, %ax		//clear coprocessor emulation CR0.EM
468    or $0x2, %ax			//set coprocessor monitoring  CR0.MP
469    movq %rax, %cr0
470    movq %cr4, %rax
471    or $(3 << 9), %ax		//set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
472    movq %rax, %cr4
473
474
475	movq	go_to_smp_kernel(%rip),	%rax		/* movq address */
476	pushq	$0x08
477	pushq	%rax
478
479/*
480    // 重新加载GDT和IDT,加载到高地址
481    leaq GDT_Table(%rip), %r8
482    leaq GDT_END(%rip), %r9
483
484    subq %r8, %r9
485    movq %r9, %r13    // GDT size
486
487    leaq IDT_Table(%rip), %r8
488    leaq IDT_END(%rip), %r9
489
490    subq %r8, %r9
491    movq %r9, %r12    // IDT size
492
493    lgdt GDT_POINTER64(%rip)
494    lidt IDT_POINTER64(%rip)
495*/
496	lretq
497
498go_to_smp_kernel:
499
500	.quad	smp_ap_start
501
502// ==== 异常/中断处理模块 ignore int: 忽略中断
503// (该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,从而重设ignore_int的中断入点)
504m_ignore_int:
505// 切换到c语言的ignore_int
506    movq go_to_ignore_int(%rip), %rax
507    pushq $0x08
508    pushq %rax
509    lretq
510
511
512
513go_to_ignore_int:
514    .quad ignore_int_handler
515
516
517ENTRY(head_stack_start)
518    .quad initial_proc_union + 32768
519
520
521// 初始化页表
522.align 0x1000 //设置为4k对齐
523__PML4E:
524    .skip 0x1000
525__PDPTE:
526	.skip 0x1000
527
528// 三级页表
529__PDE:
530    .skip 0x1000
531
532// 预留50个四级页表,总共表示100M的内存空间。这50个页表占用200KB的空间
533__PT_S:
534    .skip 0x32000
535
536
537.global __APU_START_CR3
538__APU_START_CR3:
539    .quad 0
540
541// GDT表
542
543.align 16
544.global GDT_Table // 使得GDT可以被外部程序引用或者访问
545
546GDT_Table:
547    .quad 0x0000000000000000 // 0 空描述符 0x00
548    .quad 0x0020980000000000 // 1 内核64位代码段描述符 0x08
549    .quad 0x0000920000000000 // 2 内核64位数据段描述符 0x10
550    .quad 0x0000000000000000 // 3 用户32位代码段描述符 0x18
551    .quad 0x0000000000000000 // 4 用户32位数据段描述符 0x20
552    .quad 0x0020f80000000000 // 5 用户64位代码段描述符 0x28
553    .quad 0x0000f20000000000 // 6 用户64位数据段描述符 0x30
554    .quad 0x00cf9a000000ffff // 7 内核32位代码段描述符 0x38
555    .quad 0x00cf92000000ffff // 8 内核32位数据段描述符 0x40
556    .fill 100, 8, 0           // 10-11 TSS(跳过了第9段)  重复十次填充8字节的空间,赋值为0   长模式下,每个TSS长度为128bit
557GDT_END:
558
559.global GDT_POINTER
560GDT_POINTER:
561GDT_LIMIT: .word GDT_END - GDT_Table - 1 // GDT的大小
562GDT_BASE: .quad GDT_Table
563
564.global GDT_POINTER64
565GDT_POINTER64:
566GDT_LIMIT64: .word GDT_END - GDT_Table - 1 // GDT的大小
567GDT_BASE64: .quad GDT_Table + 0xffff800000000000
568
569// IDT 表
570.global IDT_Table
571
572IDT_Table:
573    .fill 512, 8, 0 // 设置512*8字节的IDT表的空间
574IDT_END:
575
576.global IDT_POINTER
577IDT_POINTER:
578IDT_LIMIT: .word IDT_END - IDT_Table - 1
579IDT_BASE: .quad IDT_Table
580
581.global IDT_POINTER64
582IDT_POINTER64:
583IDT_LIMIT64: .word IDT_END - IDT_Table - 1
584IDT_BASE64: .quad IDT_Table + 0xffff800000000000
585
586// 64位的TSS表
587.global TSS64_Table
588
589TSS64_Table:
590    .fill 13, 8, 0
591TSS64_END:
592
593.section .bootstrap.data
594mb2_magic: .quad 0
595mb2_info: .quad 0
596
597.code32
598// 临时页表 4KB/页
599.align 0x1000
600.global pml4
601pml4:
602    .skip 0x1000
603pdpt:
604    .skip 0x1000
605pd:
606    .skip 0x1000
607pt:
608    .skip 0x1000
609
610// 临时 GDT
611.align 16
612gdt64:
613null_desc:
614    .short 0xFFFF
615    .short 0
616    .byte 0
617    .byte 0
618    .byte 0
619    .byte 0
620code_desc:
621    .short 0
622    .short 0
623    .byte 0
624    .byte 0x9A
625    .byte 0x20
626    .byte 0
627data_desc:
628    .short 0
629    .short 0
630    .byte 0
631    .byte 0x92
632    .byte 0
633    .byte 0
634user_code_desc:
635    .short 0
636    .short 0
637    .byte 0
638    .byte 0xFA
639    .byte 0x20
640    .byte 0
641user_data_desc:
642    .short 0
643    .short 0
644    .byte 0
645    .byte 0xF2
646    .byte 0
647    .byte 0
648gdt64_pointer:
649    .short gdt64_pointer-gdt64-1
650    .quad gdt64
651gdt64_pointer64:
652    .short gdt64_pointer-gdt64-1
653    .quad gdt64
654