| /* |
| * (C) Copyright 2014 Google, Inc |
| * Copyright (C) 1991, 1992, 1993 Linus Torvalds |
| * |
| * Parts of this copied from Linux arch/x86/boot/compressed/head_64.S |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <asm/global_data.h> |
| #include <asm/msr-index.h> |
| #include <asm/processor-flags.h> |
| |
| .code32 |
| .globl cpu_call64 |
| cpu_call64: |
| /* |
| * cpu_call64(ulong pgtable, ulong setup_base, ulong target) |
| * |
| * eax - pgtable |
| * edx - setup_base |
| * ecx - target |
| */ |
| cli |
| push %ecx /* arg2 = target */ |
| push %edx /* arg1 = setup_base */ |
| mov %eax, %ebx |
| |
| /* Load new GDT with the 64bit segments using 32bit descriptor */ |
| leal gdt, %eax |
| movl %eax, gdt+2 |
| lgdt gdt |
| |
| /* Enable PAE mode */ |
| movl $(X86_CR4_PAE), %eax |
| movl %eax, %cr4 |
| |
| /* Enable the boot page tables */ |
| leal (%ebx), %eax |
| movl %eax, %cr3 |
| |
| /* Enable Long mode in EFER (Extended Feature Enable Register) */ |
| movl $MSR_EFER, %ecx |
| rdmsr |
| btsl $_EFER_LME, %eax |
| wrmsr |
| |
| /* After gdt is loaded */ |
| xorl %eax, %eax |
| lldt %ax |
| movl $0x20, %eax |
| ltr %ax |
| |
| /* |
| * Setup for the jump to 64bit mode |
| * |
| * When the jump is performed we will be in long mode but |
| * in 32bit compatibility mode with EFER.LME = 1, CS.L = 0, CS.D = 1 |
| * (and in turn EFER.LMA = 1). To jump into 64bit mode we use |
| * the new gdt/idt that has __KERNEL_CS with CS.L = 1. |
| * We place all of the values on our mini stack so lret can |
| * used to perform that far jump. See the gdt below. |
| */ |
| pop %esi /* setup_base */ |
| |
| pushl $0x10 |
| leal lret_target, %eax |
| pushl %eax |
| |
| /* Enter paged protected Mode, activating Long Mode */ |
| movl $(X86_CR0_PG | X86_CR0_PE), %eax |
| movl %eax, %cr0 |
| |
| /* Jump from 32bit compatibility mode into 64bit mode. */ |
| lret |
| |
| code64: |
| lret_target: |
| pop %eax /* target */ |
| mov %eax, %eax /* Clear bits 63:32 */ |
| jmp *%eax /* Jump to the 64-bit target */ |
| |
| .data |
| gdt: |
| .word gdt_end - gdt |
| .long gdt |
| .word 0 |
| .quad 0x0000000000000000 /* NULL descriptor */ |
| .quad 0x00af9a000000ffff /* __KERNEL_CS */ |
| .quad 0x00cf92000000ffff /* __KERNEL_DS */ |
| .quad 0x0080890000000000 /* TS descriptor */ |
| .quad 0x0000000000000000 /* TS continued */ |
| gdt_end: |