1// 这是内核执行头程序 2// Created by longjin. 3// 2022/01/20 4 5#include "common/asm.h" 6#include <asm/apu_boot.h> 7 8// 以下是来自 multiboot2 规范的定义 9// How many bytes from the start of the file we search for the header. 10#define MULTIBOOT_SEARCH 32768 11#define MULTIBOOT_HEADER_ALIGN 8 12 13// The magic field should contain this. 14#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 15 16 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// This should be in %eax. 74#define MULTIBOOT_BOOTLOADER_MAGIC 0x2badb002 75#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 76 77// 存储到boot_entry_type的值 78#define BOOT_ENTRY_TYPE_MULTIBOOT 1 79#define BOOT_ENTRY_TYPE_MULTIBOOT2 2 80#define BOOT_ENTRY_TYPE_LINUX_32 3 81#define BOOT_ENTRY_TYPE_LINUX_64 4 82 83// 直接用 -m64 编译出来的是 64 位代码, 84// 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。 85// 得加一层跳转到 64 位的 -m32 代码,开启 long 模式后再跳转到以 -m64 编译的代码中 86// 对于 x86_64,需要在启动阶段进入长模式(IA32E),这意味着需要一个临时页表 87// See https://wiki.osdev.org/Creating_a_64-bit_kernel: 88// With a 32-bit bootstrap in your kernel 89 90// 这部分是从保护模式启动 long 模式的代码 91// 工作在 32bit 92// 声明这一段代码以 32 位模式编译 93.code32 94 95.section ".multiboot_header", "a" 96 97MB_MAGIC = 0x1BADB002 98MB_FLAGS = 0 99MB_CHECKSUM = -(MB_MAGIC + MB_FLAGS) 100 101.code32 102multiboot_header: 103 .align 8 104 .long MB_MAGIC 105 .long MB_FLAGS 106 .long MB_CHECKSUM 107 108 109// multiboot2 文件头 110// 计算头长度 111.SET MB2_HEADER_LENGTH, multiboot2_header_end - multiboot2_header 112// 计算校验和 113.SET MB2_CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + MB2_HEADER_LENGTH) 114// 8 字节对齐 115.code32 116.section .multiboot2_header 117.align MULTIBOOT_HEADER_ALIGN 118// 声明所属段 119 120multiboot2_header: 121 // 魔数 122 .long MULTIBOOT2_HEADER_MAGIC 123 // 架构 124 .long MULTIBOOT_ARCHITECTURE_I386 125 // 头长度 126 .long MB2_HEADER_LENGTH 127 // 校验和 128 .long MB2_CHECKSUM 129 // 添加其它内容在此,详细信息见 Multiboot2 Specification version 2.0.pdf 130 131// 设置帧缓冲区(同时在这里设置qemu的分辨率, 默认为: 1440*900, 还支持: 640*480, 等) 132.align 8 133framebuffer_tag_start: 134 .short MULTIBOOT_HEADER_TAG_FRAMEBUFFER 135 .short MULTIBOOT_HEADER_TAG_OPTIONAL 136 .long framebuffer_tag_end - framebuffer_tag_start 137 .long 1440 // 宽 138 .long 900 // 高 139 .long 32 140framebuffer_tag_end: 141.align 8 142 .short MULTIBOOT_HEADER_TAG_END 143 // 结束标记 144 .short 0 145 .long 8 146multiboot2_header_end: 147 148.section .bootstrap 149 150 151.global _start 152.type _start, @function 153 154.extern _start64 155.extern boot_info_addr 156.extern multiboot2_magic 157ENTRY(_start) 158 // 关中断 159 cli 160 161 // multiboot2_info/ multiboot_info 结构体指针 162 mov %ebx, mb_entry_info 163 //mov %ebx, %e8 164 // multiboot魔数 165 mov %eax, mb_entry_magic 166 167 mov $MULTIBOOT_BOOTLOADER_MAGIC, %ebx 168 cmp %eax, %ebx 169 je bl_magic_is_mb 170 mov $MULTIBOOT2_BOOTLOADER_MAGIC, %ebx 171 cmp %eax, %ebx 172 je bl_magic_is_mb2 173 jmp halt // unreachable 174 175bl_magic_is_mb: 176 mov $BOOT_ENTRY_TYPE_MULTIBOOT, %ebx 177 mov %ebx, boot_entry_type 178 jmp protected_mode_setup 179bl_magic_is_mb2: 180 mov $BOOT_ENTRY_TYPE_MULTIBOOT2, %ebx 181 mov %ebx, boot_entry_type 182 jmp protected_mode_setup 183 184protected_mode_setup: 185 //mov %eax, %e9 186 / 从保护模式跳转到长模式 187 // 1. 允许 PAE 188 mov %cr4, %eax 189 or $(1<<5), %eax 190 mov %eax, %cr4 191 // 2. 设置临时页表 192 // 最高级 193 mov $pml4, %eax 194 mov $pdpt, %ebx 195 or $0x3, %ebx 196 mov %ebx, 0(%eax) 197 198 // 次级 199 mov $pdpt, %eax 200 mov $pd, %ebx 201 or $0x3, %ebx 202 mov %ebx, 0(%eax) 203 204 // 次低级 205 mov $pd, %eax 206 mov $pt, %ebx 207 or $0x3, %ebx 208 mov %ebx, 0(%eax) 209 210 // 最低级 211 // 循环 512 次,填满一页 212 mov $512, %ecx 213 mov $pt, %eax 214 mov $0x3, %ebx 215.fill_pt: 216 mov %ebx, 0(%eax) 217 add $0x1000, %ebx 218 add $8, %eax 219 loop .fill_pt 220 221.global enter_head_from_ap_boot 222enter_head_from_ap_boot: 223 // 填写 CR3 224 mov $pml4, %eax 225 mov %eax, %cr3 226 227 // 3. 切换到 long 模式 228 mov $0xC0000080, %ecx 229 rdmsr 230 or $(1<<8), %eax 231 wrmsr 232 233 // 4. 开启分页 234 mov %cr0, %eax 235 or $(1<<31), %eax 236 mov %eax, %cr0 237 238 // 5. 重新设置 GDT 239 mov $gdt64_pointer, %eax 240 lgdt 0(%eax) 241 242 jmp $0x8, $ready_to_start_64 243 hlt 244 ret 245.code64 246.global ready_to_start_64 247ready_to_start_64: 248 249 mov $0x10, %ax 250 mov %ax, %ds 251 mov %ax, %es 252 mov %ax, %fs 253 mov %ax, %ss 254 mov $0x7e00, %esp 255 256 257 //6. 跳转到start64 258 movq switch_to_start64(%rip), %rax 259 pushq $0x08 //段选择子 260 pushq %rax 261 lretq 262 263switch_to_start64: 264 .quad _start64 265 266 267.code64 268halt: 269 cli 270 hlt 271 jmp halt 272 273.global _start64 274.type _start64, @function 275.extern Start_Kernel 276ENTRY(_start64) 277 278 279 // 初始化寄存器 280 mov $0x10, %ax 281 mov %ax, %ds 282 mov %ax, %es 283 mov %ax, %fs 284 mov %ax, %ss 285 mov $0x7e00, %esp 286 287// === 加载GDTR ==== 288 lgdt GDT_POINTER(%rip) //这里我没搞明白rip相对寻址, 看了文档,大概是用来实现PIC的(position independent code) 289 //lgdt $GDT_POINTER 290// === 加载IDTR ==== 291 lidt IDT_POINTER(%rip) 292 //lidt $IDT_POINTER 293 movq GDT_POINTER(%rip), %r12 294 295 // 分支,判断是否为apu 296 movq $0x1b, %rcx // 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu 297 rdmsr 298 bt $8, %rax 299 jnc load_apu_cr3 300 301 // BSP处理器 302 movq head_stack_start(%rip), %rsp 303 304 // 2. 设置临时页表 305 // 最高级 306 mov $__PML4E, %eax 307 mov $__PDPTE, %ebx 308 or $0x3, %ebx 309 mov %ebx, 0(%eax) 310 311 mov $__PML4E, %eax 312 // 加256个表项, 映射高地址 313 add $2048, %eax 314 mov %ebx, 0(%eax) 315 316 // 次级 317 mov $__PDPTE, %eax 318 mov $__PDE, %ebx 319 or $0x3, %ebx 320 mov %ebx, 0(%eax) 321 322 // 次低级 323 mov $__PDE, %eax 324 mov $50, %ecx 325 mov $__PT_S, %ebx 326 or $0x3, %ebx 327.fill_pde_64: 328 mov %ebx, 0(%eax) 329 add $0x1000, %ebx 330 add $8, %eax 331 loop .fill_pde_64 332 333 // 最低级 334 // 循环 512*25=12800 次,填满25页,共50M 335 mov $12800, %ecx 336 mov $__PT_S, %eax 337 mov $0x3, %ebx 338.fill_pt_64: 339 mov %ebx, 0(%eax) 340 add $0x1000, %ebx 341 add $8, %eax 342 loop .fill_pt_64 343 344 // 50-100M填0,共25个页表 345 mov $12800, %ecx 346.fill_pt_64_2: 347 movq $0, 0(%eax) 348 add $8, %eax 349 loop .fill_pt_64_2 350 351 352 353// ==== 加载CR3寄存器 354 355load_cr3: 356 357 movq $__PML4E, %rax //设置页目录基地址 358 359 movq %rax, %cr3 360 jmp to_switch_seg 361 362load_apu_cr3: 363 // 由于内存管理模块重置了页表,因此ap核心初始化的时候,需要使用新的内核页表。 364 // 这个页表的值由smp模块设置到__APU_START_CR3变量中 365 // 加载__APU_START_CR3中的值 366 movq $__APU_START_CR3, %rax 367 movq 0(%rax), %rax 368 movq %rax, %cr3 369 movq _apu_boot_tmp_stack_top_addr(%rip), %rsp 370 jmp to_switch_seg 371 372to_switch_seg: 373 374 movq switch_seg(%rip), %rax 375 // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器 376 // 实在是太妙了!Amazing! 377 pushq $0x08 //段选择子 378 pushq %rax 379 lretq 380 381// 64位模式的代码 382switch_seg: 383 384 .quad entry64 385 386 387entry64: 388 389 movq $0x10, %rax 390 movq %rax, %ds 391 movq %rax, %es 392 movq %rax, %gs 393 movq %rax, %ss 394 395 // 分支,判断是否为apu,然后设置栈指针· 396 movq $0x1b, %rcx // 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu 397 rdmsr 398 bt $8, %rax 399 jnc __set_ap_tmp_stack_start2 400__set_bsp_stack_start2: 401 movq head_stack_start(%rip), %rsp 402 jmp __set_stack_start2_ok 403__set_ap_tmp_stack_start2: 404 // 设置ap核心的临时栈 405 movq _apu_boot_tmp_stack_top_addr(%rip), %rsp 406 jmp __set_stack_start2_ok 407 408__set_stack_start2_ok: 409 410 411 // 重新加载GDT和IDT,加载到高地址 412 leaq GDT_Table(%rip), %r8 413 leaq GDT_END(%rip), %r9 414 415 subq %r8, %r9 416 movq %r9, %r13 // GDT size 417 418 leaq IDT_Table(%rip), %r8 419 leaq IDT_END(%rip), %r9 420 421 subq %r8, %r9 422 movq %r9, %r12 // IDT size 423 424 lgdt GDT_POINTER64(%rip) 425 lidt IDT_POINTER64(%rip) 426 427 // 分支,判断是否为apu 428 movq $0x1b, %rcx // 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu 429 rdmsr 430 bt $8, %rax 431 jnc start_smp 432 433setup_IDT: 434 // 该部分代码只在启动初期使用,后面的c文件中会重新设置IDT, 435 leaq m_ignore_int(%rip), %rdx // 将ignore_int的地址暂时存到中段描述符的高8B 436 movq $(0x08 << 16), %rax // 设置段选择子。由IDT结构和段选择子结构可知,本行设置段基地址为0x100000,TI=0,RPL=0 437 movw %dx, %ax 438 439 movq $ (0x8e00 << 32), %rcx // 设置Type=1110 P=1 DPL=00 0=0 440 addq %rcx, %rax 441 442 // 把ignore_int的地址填写到正确位置, rax存低8B, rdx存高8B 443 movl %edx, %ecx 444 shrl $16, %ecx // 去除低16位 445 shlq $48, %rcx 446 addq %rcx, %rax // 填写段内偏移31:16 447 448 shrq $32, %rdx // (已经填写了32位,故右移32) 449 450 leaq IDT_Table(%rip), %rdi // 获取中断描述符表的首地址,存储到rdi 451 mov $256, %rcx // 初始化每个中断描述符 452 453repeat_set_idt: 454 // ====== 循环,初始化总共256个中断描述符 === 455 movq %rax, (%rdi) // 保存低8B 456 movq %rdx, 8(%rdi) // 保存高8B 457 458 addq $0x10, %rdi // 转到下一个IDT表项 459 dec %rcx 460 jne repeat_set_idt 461 462 463 //now enable SSE and the like 464 movq %cr0, %rax 465 and $0xFFFB, %ax //clear coprocessor emulation CR0.EM 466 or $0x2, %ax //set coprocessor monitoring CR0.MP 467 movq %rax, %cr0 468 movq %cr4, %rax 469 or $(3 << 9), %ax //set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time 470 movq %rax, %cr4 471 472 473 movq go_to_kernel(%rip), %rax /* movq address */ 474 pushq $0x08 475 pushq %rax 476 477 478 // 传参 479 movq mb_entry_info, %rdi 480 movq mb_entry_magic, %rsi 481 movq %r13, %rdx // GDT size 482 movq %r12, %r10 // IDT size 483 movq boot_entry_type, %r8 484 485 lretq 486 487go_to_kernel: 488 .quad kernel_main 489 490start_smp: 491 492 493 //now enable SSE and the like 494 movq %cr0, %rax 495 and $0xFFFB, %ax //clear coprocessor emulation CR0.EM 496 or $0x2, %ax //set coprocessor monitoring CR0.MP 497 movq %rax, %cr0 498 movq %cr4, %rax 499 or $(3 << 9), %ax //set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time 500 movq %rax, %cr4 501 502 503 movq go_to_smp_kernel(%rip), %rax /* movq address */ 504 pushq $0x08 505 pushq %rax 506 507/* 508 // 重新加载GDT和IDT,加载到高地址 509 leaq GDT_Table(%rip), %r8 510 leaq GDT_END(%rip), %r9 511 512 subq %r8, %r9 513 movq %r9, %r13 // GDT size 514 515 leaq IDT_Table(%rip), %r8 516 leaq IDT_END(%rip), %r9 517 518 subq %r8, %r9 519 movq %r9, %r12 // IDT size 520 521 lgdt GDT_POINTER64(%rip) 522 lidt IDT_POINTER64(%rip) 523*/ 524 lretq 525 526go_to_smp_kernel: 527 528 .quad smp_ap_start 529 530// ==== 异常/中断处理模块 ignore int: 忽略中断 531// (该部分代码只在启动初期使用,后面的c文件中会重新设置IDT,从而重设ignore_int的中断入点) 532m_ignore_int: 533// 切换到c语言的ignore_int 534 movq go_to_ignore_int(%rip), %rax 535 pushq $0x08 536 pushq %rax 537 lretq 538 539 540 541go_to_ignore_int: 542 .quad ignore_int_handler 543 544ENTRY(head_stack_start) 545 .quad BSP_IDLE_STACK_SPACE + 32768 546 547ENTRY(_apu_boot_tmp_stack_top_addr) 548 .quad _apu_boot_tmp_stack_start + APU_BOOT_TMP_STACK_SIZE 549 550// 初始化页表 551.align 0x1000 //设置为4k对齐 552__PML4E: 553 .skip 0x1000 554__PDPTE: 555 .skip 0x1000 556 557// 三级页表 558__PDE: 559 .skip 0x1000 560 561// 预留50个四级页表,总共表示100M的内存空间。这50个页表占用200KB的空间 562__PT_S: 563 .skip 0x32000 564 565 566.global __APU_START_CR3 567__APU_START_CR3: 568 .quad 0 569 570// GDT表 571 572.align 16 573.global GDT_Table // 使得GDT可以被外部程序引用或者访问 574 575GDT_Table: 576 .quad 0x0000000000000000 // 0 空描述符 0x00 577 .quad 0x0020980000000000 // 1 内核64位代码段描述符 0x08 578 .quad 0x0000920000000000 // 2 内核64位数据段描述符 0x10 579 .quad 0x0000000000000000 // 3 用户32位代码段描述符 0x18 580 .quad 0x0000000000000000 // 4 用户32位数据段描述符 0x20 581 .quad 0x00cff3000000ffff // 5 用户64位数据段描述符 0x28 582 .quad 0x00affb000000ffff // 6 用户64位代码段描述符 0x30 583 .quad 0x00cf9a000000ffff // 7 内核32位代码段描述符 0x38 584 .quad 0x00cf92000000ffff // 8 内核32位数据段描述符 0x40 585 .fill 100, 8, 0 // 10-11 TSS(跳过了第9段) 重复十次填充8字节的空间,赋值为0 长模式下,每个TSS长度为128bit 586GDT_END: 587 588.global GDT_POINTER 589GDT_POINTER: 590GDT_LIMIT: .word GDT_END - GDT_Table - 1 // GDT的大小 591GDT_BASE: .quad GDT_Table 592 593.global GDT_POINTER64 594GDT_POINTER64: 595GDT_LIMIT64: .word GDT_END - GDT_Table - 1 // GDT的大小 596GDT_BASE64: .quad GDT_Table + 0xffff800000000000 597 598// IDT 表 599.global IDT_Table 600 601IDT_Table: 602 .fill 512, 8, 0 // 设置512*8字节的IDT表的空间 603IDT_END: 604 605.global IDT_POINTER 606IDT_POINTER: 607IDT_LIMIT: .word IDT_END - IDT_Table - 1 608IDT_BASE: .quad IDT_Table 609 610.global IDT_POINTER64 611IDT_POINTER64: 612IDT_LIMIT64: .word IDT_END - IDT_Table - 1 613IDT_BASE64: .quad IDT_Table + 0xffff800000000000 614 615 616 617.section .bootstrap.data 618mb_entry_magic: .quad 0 619mb_entry_info: .quad 0 620// 引导协议类型 621boot_entry_type: .quad 0 622 623.code32 624// 临时页表 4KB/页 625.align 0x1000 626.global pml4 627pml4: 628 .skip 0x1000 629pdpt: 630 .skip 0x1000 631pd: 632 .skip 0x1000 633pt: 634 .skip 0x1000 635 636// 临时 GDT 637.align 16 638gdt64: 639null_desc: 640 .short 0xFFFF 641 .short 0 642 .byte 0 643 .byte 0 644 .byte 0 645 .byte 0 646code_desc: 647 .short 0 648 .short 0 649 .byte 0 650 .byte 0x9A 651 .byte 0x20 652 .byte 0 653data_desc: 654 .short 0 655 .short 0 656 .byte 0 657 .byte 0x92 658 .byte 0 659 .byte 0 660user_code_desc: 661 .short 0 662 .short 0 663 .byte 0 664 .byte 0xFA 665 .byte 0x20 666 .byte 0 667user_data_desc: 668 .short 0 669 .short 0 670 .byte 0 671 .byte 0xF2 672 .byte 0 673 .byte 0 674gdt64_pointer: 675 .short gdt64_pointer-gdt64-1 676 .quad gdt64 677gdt64_pointer64: 678 .short gdt64_pointer-gdt64-1 679 .quad gdt64 680