| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 | // 这是内核执行头程序// Created by longjin.// 2022/01/20#include "common/asm.h"// 以下是来自 multiboot2 规范的定义//  How many bytes from the start of the file we search for the header.#define MULTIBOOT_SEARCH 32768#define MULTIBOOT_HEADER_ALIGN 8//  The magic field should contain this.#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6//  This should be in %eax.#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289//  Alignment of multiboot modules.#define MULTIBOOT_MOD_ALIGN 0x00001000//  Alignment of the multiboot info structure.#define MULTIBOOT_INFO_ALIGN 0x00000008//  Flags set in the 'flags' member of the multiboot header.#define MULTIBOOT_TAG_ALIGN 8#define MULTIBOOT_TAG_TYPE_END 0#define MULTIBOOT_TAG_TYPE_CMDLINE 1#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2#define MULTIBOOT_TAG_TYPE_MODULE 3#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4#define MULTIBOOT_TAG_TYPE_BOOTDEV 5#define MULTIBOOT_TAG_TYPE_MMAP 6#define MULTIBOOT_TAG_TYPE_VBE 7#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9#define MULTIBOOT_TAG_TYPE_APM 10#define MULTIBOOT_TAG_TYPE_EFI32 11#define MULTIBOOT_TAG_TYPE_EFI64 12#define MULTIBOOT_TAG_TYPE_SMBIOS 13#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15#define MULTIBOOT_TAG_TYPE_NETWORK 16#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17#define MULTIBOOT_TAG_TYPE_EFI_BS 18#define MULTIBOOT_TAG_TYPE_EFI32_IH 19#define MULTIBOOT_TAG_TYPE_EFI64_IH 20#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21#define MULTIBOOT_HEADER_TAG_END 0#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1#define MULTIBOOT_HEADER_TAG_ADDRESS 2#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6#define MULTIBOOT_HEADER_TAG_EFI_BS 7#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10#define MULTIBOOT_ARCHITECTURE_I386 0#define MULTIBOOT_ARCHITECTURE_MIPS32 4#define MULTIBOOT_HEADER_TAG_OPTIONAL 1#define MULTIBOOT_LOAD_PREFERENCE_NONE 0#define MULTIBOOT_LOAD_PREFERENCE_LOW 1#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2// 直接用 -m64 编译出来的是 64 位代码,// 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。// 得加一层跳转到 64 位的 -m32 代码,开启 long 模式后再跳转到以 -m64 编译的代码中// 对于 x86_64,需要在启动阶段进入长模式(IA32E),这意味着需要一个临时页表// See https://wiki.osdev.org/Creating_a_64-bit_kernel: // With a 32-bit bootstrap in your kernel// 这部分是从保护模式启动 long 模式的代码// 工作在 32bit// 声明这一段代码以 32 位模式编译.code32// multiboot2 文件头// 计算头长度.SET HEADER_LENGTH, multiboot_header_end - multiboot_header// 计算校验和.SET CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + HEADER_LENGTH)// 8 字节对齐.section .multiboot_header.align MULTIBOOT_HEADER_ALIGN// 声明所属段multiboot_header:    // 魔数    .long MULTIBOOT2_HEADER_MAGIC    // 架构    .long MULTIBOOT_ARCHITECTURE_I386    // 头长度    .long HEADER_LENGTH    // 校验和    .long CHECKSUM    // 添加其它内容在此,详细信息见 Multiboot2 Specification version 2.0.pdf// 设置帧缓冲区.align 8framebuffer_tag_start:    .short MULTIBOOT_HEADER_TAG_FRAMEBUFFER    .short MULTIBOOT_HEADER_TAG_OPTIONAL    .long framebuffer_tag_end - framebuffer_tag_start    .long 1440    .long 900    .long 32framebuffer_tag_end:.align 8	.short MULTIBOOT_HEADER_TAG_END    // 结束标记    .short 0    .long 8multiboot_header_end:.section .bootstrap.global _start.type _start, @function# 在 multiboot2.cpp 中定义.extern _start64.extern boot_info_addr.extern multiboot2_magicENTRY(_start)    // 关中断    cli        // multiboot2_info 结构体指针    mov %ebx, mb2_info    //mov %ebx, %e8    // 魔数    mov %eax, mb2_magic    //mov %eax, %e9    / 从保护模式跳转到长模式    // 1. 允许 PAE    mov %cr4, %eax    or $(1<<5), %eax    mov %eax, %cr4    // 2. 设置临时页表    // 最高级    mov $pml4, %eax    mov $pdpt, %ebx    or $0x3, %ebx    mov %ebx, 0(%eax)    // 次级    mov $pdpt, %eax    mov $pd, %ebx    or $0x3, %ebx    mov %ebx, 0(%eax)    // 次低级    mov $pd, %eax    mov $pt, %ebx    or $0x3, %ebx    mov %ebx, 0(%eax)    // 最低级    // 循环 512 次,填满一页    mov $512, %ecx    mov $pt, %eax    mov $0x3, %ebx.fill_pt:    mov %ebx, 0(%eax)    add $0x1000, %ebx    add $8, %eax    loop .fill_pt.global enter_head_from_ap_bootenter_head_from_ap_boot:    // 填写 CR3    mov $pml4, %eax    mov %eax, %cr3    // 3. 切换到 long 模式    mov $0xC0000080, %ecx    rdmsr    or $(1<<8), %eax    wrmsr    // 4. 开启分页    mov %cr0, %eax    or $(1<<31), %eax    mov %eax, %cr0    // 5. 重新设置 GDT    mov $gdt64_pointer, %eax    lgdt 0(%eax)    jmp $0x8, $ready_to_start_64    hlt    ret.code64.global ready_to_start_64ready_to_start_64:    mov $0x10, %ax    mov %ax, %ds    mov %ax, %es    mov %ax, %fs    mov %ax, %ss    mov $0x7e00, %esp        //6. 跳转到start64    movq switch_to_start64(%rip), %rax    pushq $0x08 //段选择子    pushq %rax    lretqswitch_to_start64:    .quad _start64.code64is_from_ap:        hlt.global _start64.type _start64, @function.extern Start_KernelENTRY(_start64)    // 初始化寄存器    mov $0x10, %ax    mov %ax, %ds    mov %ax, %es    mov %ax, %fs    mov %ax, %ss    mov $0x7e00, %esp    // === 加载GDTR ====    lgdt GDT_POINTER(%rip) //这里我没搞明白rip相对寻址, 看了文档,大概是用来实现PIC的(position independent code)    //lgdt $GDT_POINTER// === 加载IDTR ====    lidt IDT_POINTER(%rip)    //lidt $IDT_POINTER    movq GDT_POINTER(%rip), %r12    movq head_stack_start(%rip), %rsp    // 分支,判断是否为apu    movq	$0x1b,	%rcx		// 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu	rdmsr	bt	$8,	%rax	jnc	load_cr3    // 2. 设置临时页表    // 最高级    mov $__PML4E, %eax    mov $__PDPTE, %ebx    or $0x3, %ebx    mov %ebx, 0(%eax)    mov %ebx, 256(%eax)    // 次级    mov $__PDPTE, %eax    mov $__PDE, %ebx    or $0x3, %ebx    mov %ebx, 0(%eax)// ==== 加载CR3寄存器load_cr3:    movq $__PML4E, %rax //设置页目录基地址        movq %rax, %cr3        movq switch_seg(%rip), %rax    // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器    // 实在是太妙了!Amazing!    pushq $0x08 //段选择子    pushq %rax    lretq// 64位模式的代码switch_seg:    .quad entry64entry64:    movq $0x10, %rax    movq %rax, %ds    movq %rax, %es    movq %rax, %gs    movq %rax, %ss    movq head_stack_start(%rip), %rsp //rsp的地址        // 重新加载GDT和IDT,加载到高地址    leaq GDT_Table(%rip), %r8    leaq GDT_END(%rip), %r9    subq %r8, %r9    movq %r9, %r13    // GDT size    leaq IDT_Table(%rip), %r8    leaq IDT_END(%rip), %r9    subq %r8, %r9    movq %r9, %r12    // IDT size    lgdt GDT_POINTER64(%rip)    lidt IDT_POINTER64(%rip)    // 分支,判断是否为apu    movq	$0x1b,	%rcx		// 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu	rdmsr	bt	$8,	%rax	jnc	start_smpsetup_IDT:    // 该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,    leaq m_ignore_int(%rip),  %rdx // 将ignore_int的地址暂时存到中段描述符的高8B    movq $(0x08 << 16), %rax  // 设置段选择子。由IDT结构和段选择子结构可知,本行设置段基地址为0x100000,TI=0,RPL=0    movw %dx, %ax    movq $ (0x8e00 << 32), %rcx // 设置Type=1110 P=1 DPL=00 0=0    addq %rcx, %rax    // 把ignore_int的地址填写到正确位置, rax存低8B, rdx存高8B    movl %edx, %ecx    shrl $16, %ecx // 去除低16位    shlq $48, %rcx    addq %rcx, %rax // 填写段内偏移31:16    shrq $32, %rdx // (已经填写了32位,故右移32)    leaq IDT_Table(%rip), %rdi // 获取中断描述符表的首地址,存储到rdi    mov $256, %rcx  // 初始化每个中断描述符repeat_set_idt:    // ====== 循环,初始化总共256个中断描述符 ===    movq %rax, (%rdi)   // 保存低8B    movq %rdx, 8(%rdi)  // 保存高8B    addq $0x10, %rdi // 转到下一个IDT表项    dec %rcx    jne repeat_set_idtSetUp_TSS64:    // == 设置64位的任务状态段表 ===    //rdx保存高8B, rax保存低8B    leaq TSS64_Table(%rip), %rdx    // 获取定义在process.c中的initial_tss[0]的地址    movq $0xffff800000000000, %r8    addq %r8, %rdx    xorq %rax, %rax    xorq %rcx, %rcx    // 设置TSS描述符的47:40位为1000 1001    movq $0x89, %rax    shlq $40, %rax    // 设置段基地址31:24    movl %edx, %ecx    shrl $24, %ecx    shlq $56, %rcx    addq %rcx, %rax    xorq %rcx, %rcx    // 设置段基地址23:00    movl %edx, %ecx    andl $0xffffff, %ecx // 清空ecx的中有效值的高8位(也就是上面已经赋值了的)    shlq $16, %rcx    addq %rcx, %rax    addq $103, %rax // 设置段长度    leaq GDT_Table(%rip), %rdi    movq %rax, 80(%rdi) // 把低八B存储到GDT第10项    shrq $32, %rdx    movq %rdx, 88(%rdi) // 高8B存到GDT第11项    // 装载任务状态段寄存器(已改为在main.c中使用load_TR宏进行装载)    // mov $0x50, %ax // 设置起始地址为80    // ltr %ax        //now enable SSE and the like    movq %cr0, %rax    and $0xFFFB, %ax		//clear coprocessor emulation CR0.EM    or $0x2, %ax			//set coprocessor monitoring  CR0.MP    movq %rax, %cr0    movq %cr4, %rax    or $(3 << 9), %ax		//set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time    movq %rax, %cr4        movq	go_to_kernel(%rip),	%rax		/* movq address */	pushq	$0x08	pushq	%rax        movq mb2_info, %r15    movq mb2_magic, %r14    	lretqgo_to_kernel:    .quad Start_Kernelstart_smp:    //now enable SSE and the like    movq %cr0, %rax    and $0xFFFB, %ax		//clear coprocessor emulation CR0.EM    or $0x2, %ax			//set coprocessor monitoring  CR0.MP    movq %rax, %cr0    movq %cr4, %rax    or $(3 << 9), %ax		//set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time    movq %rax, %cr4	movq	go_to_smp_kernel(%rip),	%rax		/* movq address */	pushq	$0x08	pushq	%rax/*    // 重新加载GDT和IDT,加载到高地址    leaq GDT_Table(%rip), %r8    leaq GDT_END(%rip), %r9    subq %r8, %r9    movq %r9, %r13    // GDT size    leaq IDT_Table(%rip), %r8    leaq IDT_END(%rip), %r9    subq %r8, %r9    movq %r9, %r12    // IDT size    lgdt GDT_POINTER64(%rip)    lidt IDT_POINTER64(%rip)*/	lretqgo_to_smp_kernel:	.quad	smp_ap_start// ==== 异常/中断处理模块 ignore int: 忽略中断// (该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,从而重设ignore_int的中断入点)m_ignore_int:// 切换到c语言的ignore_int    movq go_to_ignore_int(%rip), %rax    pushq $0x08    pushq %rax    lretqgo_to_ignore_int:    .quad ignore_int_handlerENTRY(head_stack_start)    .quad initial_proc_union + 32768// 初始化页表.align 0x1000 //设置为4k对齐//.org 0x1000 //设置页表位置为内核执行头程序的0x1000处__PML4E:    .quad 0x103007 // 用户访问,可读写,已存在, 地址在31~12位    .fill	255,8,0	.quad 0x103003	.fill	255,8,0.org	0x2000__PDPTE:	.quad	0x104003 // 用户访问,可读写,已存在	.fill	511,8,0.org	0x3000__PDE:	.quad	0x000083    // 用户访问,可读写,已存在	.quad	0x200083	.quad	0x400083	.quad	0x600083	.quad	0x800083    .quad	0xa00083	.quad	0xc00083	.quad	0xe00083	.quad	0x1000083	.quad	0x1200083	.quad	0x1400083	.quad	0x1600083	.quad	0x1800083	.quad	0x1a00083	.quad	0x1c00083	.quad	0x1e00083	.quad	0x2000083	.quad	0x2200083	.quad	0x2400083	.quad	0x2600083	.quad	0x2800083	.quad	0x2a00083	.quad	0x2c00083	.quad	0x2e00083	.quad	0x3000083	.quad	0x3200083	.quad	0x3400083	.quad	0x3600083	.quad	0xe0000083		/*虚拟地址0x 3000000 初始情况下,帧缓冲区映射到这里*/ 	.quad	0xe0200083	.quad	0xe0400083	.quad	0xe0600083		/*0x1000000*/	.quad	0xe0800083	.quad	0xe0a00083	.quad	0xe0c00083	.quad	0xe0e00083    .quad	0xe1000083    .quad	0xe1200083    .quad	0xe1400083    .quad	0xe1600083    .quad	0xe1800083    .quad	0xe1a00083    .quad	0xe1c00083    .quad	0xe1e00083	.fill	468,8,0// GDT表.align 16.global GDT_Table // 使得GDT可以被外部程序引用或者访问GDT_Table:    .quad 0x0000000000000000 // 0 空描述符 0x00    .quad 0x0020980000000000 // 1 内核64位代码段描述符 0x08    .quad 0x0000920000000000 // 2 内核64位数据段描述符 0x10    .quad 0x0000000000000000 // 3 用户32位代码段描述符 0x18    .quad 0x0000000000000000 // 4 用户32位数据段描述符 0x20    .quad 0x0020f80000000000 // 5 用户64位代码段描述符 0x28    .quad 0x0000f20000000000 // 6 用户64位数据段描述符 0x30    .quad 0x00cf9a000000ffff // 7 内核32位代码段描述符 0x38    .quad 0x00cf92000000ffff // 8 内核32位数据段描述符 0x40    .fill 100, 8, 0           // 10-11 TSS(跳过了第9段)  重复十次填充8字节的空间,赋值为0   长模式下,每个TSS长度为128bitGDT_END:.global GDT_POINTERGDT_POINTER:GDT_LIMIT: .word GDT_END - GDT_Table - 1 // GDT的大小GDT_BASE: .quad GDT_Table.global GDT_POINTER64GDT_POINTER64:GDT_LIMIT64: .word GDT_END - GDT_Table - 1 // GDT的大小GDT_BASE64: .quad GDT_Table + 0xffff800000000000// IDT 表.global IDT_TableIDT_Table:    .fill 512, 8, 0 // 设置512*8字节的IDT表的空间IDT_END:.global IDT_POINTERIDT_POINTER:IDT_LIMIT: .word IDT_END - IDT_Table - 1IDT_BASE: .quad IDT_Table.global IDT_POINTER64IDT_POINTER64:IDT_LIMIT64: .word IDT_END - IDT_Table - 1IDT_BASE64: .quad IDT_Table + 0xffff800000000000// 64位的TSS表.global TSS64_TableTSS64_Table:    .fill 13, 8, 0TSS64_END:.section .bootstrap.datamb2_magic: .quad 0mb2_info: .quad 0.code32// 临时页表 4KB/页.align 0x1000.global pml4pml4:    .skip 0x1000pdpt:    .skip 0x1000pd:    .skip 0x1000pt:    .skip 0x1000// 临时 GDT.align 16gdt64:null_desc:    .short 0xFFFF    .short 0    .byte 0    .byte 0    .byte 0    .byte 0code_desc:    .short 0    .short 0    .byte 0    .byte 0x9A    .byte 0x20    .byte 0data_desc:    .short 0    .short 0    .byte 0    .byte 0x92    .byte 0    .byte 0user_code_desc:    .short 0    .short 0    .byte 0    .byte 0xFA    .byte 0x20    .byte 0user_data_desc:    .short 0    .short 0    .byte 0    .byte 0xF2    .byte 0    .byte 0gdt64_pointer:    .short gdt64_pointer-gdt64-1    .quad gdt64gdt64_pointer64:    .short gdt64_pointer-gdt64-1    .quad gdt64
 |