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