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