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