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