From patchwork Fri Oct 10 14:21:55 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 398594 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 9ABED1400AB for ; Sat, 11 Oct 2014 01:23:47 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CEEB8A7434; Fri, 10 Oct 2014 16:23:16 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3AOMr7H6TGjv; Fri, 10 Oct 2014 16:23:16 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 3015CA7491; Fri, 10 Oct 2014 16:22:51 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 78FECA73ED for ; Fri, 10 Oct 2014 16:22:34 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id kBun+c9xpPSh for ; Fri, 10 Oct 2014 16:22:34 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-oi0-f73.google.com (mail-oi0-f73.google.com [209.85.218.73]) by theia.denx.de (Postfix) with ESMTPS id EC7DBA7442 for ; Fri, 10 Oct 2014 16:22:29 +0200 (CEST) Received: by mail-oi0-f73.google.com with SMTP id u20so1193636oif.0 for ; Fri, 10 Oct 2014 07:22:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=a5OPHr9peecjT9zg0wtxqyIzruuWYrJ93BAwChZbMdI=; b=DpTGYNHT1DelECN7DdTobF1BPb7J/JwRe75dyW4CHtcPplK9JaxHeWdzIQ+gNAmp2r mseVNcPwF2Ih943nLQeywU+D6cS5A4JJPVuIlkmmv0LhQdhK/zrFgouHvS+nQ/e1AH9m DKFsHd2vlz/inDhkicgteNvT61f6ctvzXMvYMMVdi0C10PXLd6Magmv/f4Wipq2kPqJu Ki3gOe3S0a3SPZo8wGGlwCiciM3gIxBm9F2/TC/lAMFqi2kaQTj3K7OxFpv037BBr2AA hub0wiJ7j+vqeuz0DLTs6ylH5Tq3g8wnCt9gEtHx2NmD9wyINPlrDSG/0jvpA7xIHhhr /P0w== X-Gm-Message-State: ALoCoQnFYyxxkFrWR8kqf86Yhy3qFf3gtjaacyczsn39/0tHyKYlVCF/pc6TheAJA/X6Hu4dwEIf X-Received: by 10.182.215.168 with SMTP id oj8mr3179326obc.19.1412950948529; Fri, 10 Oct 2014 07:22:28 -0700 (PDT) Received: from corpmail-nozzle1-1.hot.corp.google.com ([100.108.1.104]) by gmr-mx.google.com with ESMTPS id k66si326131yho.7.2014.10.10.07.22.28 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 10 Oct 2014 07:22:28 -0700 (PDT) Received: from kaki.bld.corp.google.com ([172.29.216.32]) by corpmail-nozzle1-1.hot.corp.google.com with ESMTP id jQkAZ2KH.1; Fri, 10 Oct 2014 07:22:28 -0700 Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id BFC0122165D; Fri, 10 Oct 2014 08:22:27 -0600 (MDT) From: Simon Glass To: U-Boot Mailing List Date: Fri, 10 Oct 2014 08:21:55 -0600 Message-Id: <1412950921-20052-5-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 2.1.0.rc2.206.gedb03e5 In-Reply-To: <1412950921-20052-1-git-send-email-sjg@chromium.org> References: <1412950921-20052-1-git-send-email-sjg@chromium.org> Cc: Graeme Russ Subject: [U-Boot] [PATCH 04/10] x86: Add support for starting 64-bit kernel X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.13 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Add code to jump to a 64-bit Linux kernel. We need to set up a flat page table structure, a new GDT and then go through a few hoops in the right order. Signed-off-by: Simon Glass --- arch/x86/cpu/Makefile | 2 +- arch/x86/cpu/call64.S | 93 ++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/cpu/cpu.c | 45 ++++++++++++++++++++++ arch/x86/include/asm/cpu.h | 26 +++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 arch/x86/cpu/call64.S diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 415bc24..faadf0b 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -10,4 +10,4 @@ extra-y = start.o extra-$(CONFIG_X86_RESET_VECTOR) += resetvec.o start16.o -obj-y = interrupts.o cpu.o +obj-y = interrupts.o cpu.o call64.o diff --git a/arch/x86/cpu/call64.S b/arch/x86/cpu/call64.S new file mode 100644 index 0000000..74dd5a8 --- /dev/null +++ b/arch/x86/cpu/call64.S @@ -0,0 +1,93 @@ +/* + * (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 +#include +#include + +.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: diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index 8c1eacc..2e25253 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -18,7 +18,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -339,3 +342,45 @@ int print_cpuinfo(void) return 0; } + +#define PAGETABLE_SIZE (6 * 4096) + +/** + * build_pagetable() - build a flat 4GiB page table structure for 64-bti mode + * + * @pgtable: Pointer to a 24iKB block of memory + */ +static void build_pagetable(uint32_t *pgtable) +{ + uint i; + + memset(pgtable, '\0', PAGETABLE_SIZE); + + /* Level 4 needs a single entry */ + pgtable[0] = (uint32_t)&pgtable[1024] + 7; + + /* Level 3 has one 64-bit entry for each GiB of memory */ + for (i = 0; i < 4; i++) { + pgtable[1024 + i * 2] = (uint32_t)&pgtable[2048] + + 0x1000 * i + 7; + } + + /* Level 2 has 2048 64-bit entries, each repesenting 2MiB */ + for (i = 0; i < 2048; i++) + pgtable[2048 + i * 2] = 0x183 + (i << 21UL); +} + +int cpu_jump_to_64bit(ulong setup_base, ulong target) +{ + uint32_t *pgtable; + + pgtable = memalign(4096, PAGETABLE_SIZE); + if (!pgtable) + return -ENOMEM; + + build_pagetable(pgtable); + cpu_call64((ulong)pgtable, setup_base, target); + free(pgtable); + + return -EFAULT; +} diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 32930bd..6c6774a 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -26,4 +26,30 @@ void cpu_disable_paging_pae(void); */ int cpu_has_64bit(void); +/** + * cpu_call64() - Jump to a 64-bit Linux kernel (internal function) + * + * The kernel is uncompressed and the 64-bit entry point is expected to be + * at @target. + * + * This function is used internally - see cpu_jump_to_64bit() for a more + * useful function. + * + * @pgtable: Address of 24KB area containing the page table + * @setup_base: Pointer to the setup.bin information for the kernel + * @target: Pointer to the start of the kernel image + */ +void cpu_call64(ulong pgtable, ulong setup_base, ulong target); + +/** + * cpu_jump_to_64bit() - Jump to a 64-bit Linux kernel + * + * The kernel is uncompressed and the 64-bit entry point is expected to be + * at @target. + * + * @setup_base: Pointer to the setup.bin information for the kernel + * @target: Pointer to the start of the kernel image + */ +int cpu_jump_to_64bit(ulong setup_base, ulong target); + #endif