1/* SPDX-License-Identifier: GPL-2.0+ */ 2/* 3 * (C) Copyright 2014 Google, Inc 4 * Copyright (C) 1991, 1992, 1993 Linus Torvalds 5 * 6 * Parts of this copied from Linux arch/x86/boot/compressed/head_64.S 7 */ 8 9#include <asm/global_data.h> 10#include <asm/msr-index.h> 11#include <asm/processor-flags.h> 12 13.code32 14.globl cpu_call64 15cpu_call64: 16 /* 17 * cpu_call64(ulong pgtable, ulong setup_base, ulong target) 18 * 19 * eax - pgtable 20 * edx - setup_base 21 * ecx - target 22 */ 23 cli 24 push %ecx /* arg2 = target */ 25 push %edx /* arg1 = setup_base */ 26 mov %eax, %ebx 27 28 /* Load new GDT with the 64bit segments using 32bit descriptor */ 29 leal gdt, %eax 30 movl %eax, gdt+2 31 lgdt gdt 32 33 /* Enable PAE mode */ 34 movl $(X86_CR4_PAE), %eax 35 movl %eax, %cr4 36 37 /* Enable the boot page tables */ 38 leal (%ebx), %eax 39 movl %eax, %cr3 40 41 /* Enable Long mode in EFER (Extended Feature Enable Register) */ 42 movl $MSR_EFER, %ecx 43 rdmsr 44 btsl $_EFER_LME, %eax 45 wrmsr 46 47 /* After gdt is loaded */ 48 xorl %eax, %eax 49 lldt %ax 50 movl $0x20, %eax 51 ltr %ax 52 53 /* 54 * Setup for the jump to 64bit mode 55 * 56 * When the jump is performed we will be in long mode but 57 * in 32bit compatibility mode with EFER.LME = 1, CS.L = 0, CS.D = 1 58 * (and in turn EFER.LMA = 1). To jump into 64bit mode we use 59 * the new gdt/idt that has __KERNEL_CS with CS.L = 1. 60 * We place all of the values on our mini stack so lret can 61 * used to perform that far jump. See the gdt below. 62 */ 63 pop %esi /* setup_base */ 64 65 pushl $0x10 66 leal lret_target, %eax 67 pushl %eax 68 69 /* Enter paged protected Mode, activating Long Mode */ 70 movl $(X86_CR0_PG | X86_CR0_PE), %eax 71 movl %eax, %cr0 72 73 /* Jump from 32bit compatibility mode into 64bit mode. */ 74 lret 75 76code64: 77lret_target: 78 pop %eax /* target */ 79 mov %eax, %eax /* Clear bits 63:32 */ 80 jmp *%eax /* Jump to the 64-bit target */ 81 82.globl call64_stub_size 83call64_stub_size: 84 .long . - cpu_call64 85 86 .data 87 .align 16 88 .globl gdt64 89gdt64: 90gdt: 91 .word gdt_end - gdt - 1 92 .long gdt /* Fixed up by code above */ 93 .word 0 94 .quad 0x0000000000000000 /* NULL descriptor */ 95 .quad 0x00af9a000000ffff /* __KERNEL_CS */ 96 .quad 0x00cf92000000ffff /* __KERNEL_DS */ 97 .quad 0x0080890000000000 /* TS descriptor */ 98 .quad 0x0000000000000000 /* TS continued */ 99gdt_end: 100