xref: /DragonOS/kernel/src/arch/x86_64/asm/head.S (revision 01c18c64b14b4ebabd98fa92c587c26874275eb1)
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