x86/boot/compressed/64: Add IDT Infrastructure
authorJoerg Roedel <jroedel@suse.de>
Mon, 7 Sep 2020 13:15:14 +0000 (15:15 +0200)
committerBorislav Petkov <bp@suse.de>
Mon, 7 Sep 2020 17:45:25 +0000 (19:45 +0200)
Add code needed to setup an IDT in the early pre-decompression
boot-code. The IDT is loaded first in startup_64, which is after
EfiExitBootServices() has been called, and later reloaded when the
kernel image has been relocated to the end of the decompression area.

This allows to setup different IDT handlers before and after the
relocation.

Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20200907131613.12703-14-joro@8bytes.org
arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/idt_64.c [new file with mode: 0644]
arch/x86/boot/compressed/idt_handlers_64.S [new file with mode: 0644]
arch/x86/boot/compressed/misc.h
arch/x86/include/asm/desc_defs.h

index 5343079af973dcb900cde8285db4ce5300c02000..c661dc57674eee50faaaffe1f10a4c7d9f70ba25 100644 (file)
@@ -85,6 +85,7 @@ vmlinux-objs-$(CONFIG_EARLY_PRINTK) += $(obj)/early_serial_console.o
 vmlinux-objs-$(CONFIG_RANDOMIZE_BASE) += $(obj)/kaslr.o
 ifdef CONFIG_X86_64
        vmlinux-objs-$(CONFIG_RANDOMIZE_BASE) += $(obj)/kaslr_64.o
+       vmlinux-objs-y += $(obj)/idt_64.o $(obj)/idt_handlers_64.o
        vmlinux-objs-y += $(obj)/mem_encrypt.o
        vmlinux-objs-y += $(obj)/pgtable_64.o
 endif
index 97d37f0a34f535d34ca45899d82434e0ced00e83..c634ed8636dad14be72803f664b2be6515f9c159 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/processor-flags.h>
 #include <asm/asm-offsets.h>
 #include <asm/bootparam.h>
+#include <asm/desc_defs.h>
 #include "pgtable.h"
 
 /*
@@ -410,6 +411,10 @@ SYM_CODE_START(startup_64)
 
 .Lon_kernel_cs:
 
+       pushq   %rsi
+       call    load_stage1_idt
+       popq    %rsi
+
        /*
         * paging_prepare() sets up the trampoline and checks if we need to
         * enable 5-level paging.
@@ -537,6 +542,13 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
        shrq    $3, %rcx
        rep     stosq
 
+/*
+ * Load stage2 IDT
+ */
+       pushq   %rsi
+       call    load_stage2_idt
+       popq    %rsi
+
 /*
  * Do the extraction, and jump to the new kernel..
  */
@@ -690,10 +702,21 @@ SYM_DATA_START_LOCAL(gdt)
        .quad   0x0000000000000000      /* TS continued */
 SYM_DATA_END_LABEL(gdt, SYM_L_LOCAL, gdt_end)
 
+SYM_DATA_START(boot_idt_desc)
+       .word   boot_idt_end - boot_idt - 1
+       .quad   0
+SYM_DATA_END(boot_idt_desc)
+       .balign 8
+SYM_DATA_START(boot_idt)
+       .rept   BOOT_IDT_ENTRIES
+       .quad   0
+       .quad   0
+       .endr
+SYM_DATA_END_LABEL(boot_idt, SYM_L_GLOBAL, boot_idt_end)
+
 #ifdef CONFIG_EFI_STUB
 SYM_DATA(image_offset, .long 0)
 #endif
-
 #ifdef CONFIG_EFI_MIXED
 SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0)
 SYM_DATA(efi_is64, .byte 1)
diff --git a/arch/x86/boot/compressed/idt_64.c b/arch/x86/boot/compressed/idt_64.c
new file mode 100644 (file)
index 0000000..082cd6b
--- /dev/null
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <asm/trap_pf.h>
+#include <asm/segment.h>
+#include <asm/trapnr.h>
+#include "misc.h"
+
+static void set_idt_entry(int vector, void (*handler)(void))
+{
+       unsigned long address = (unsigned long)handler;
+       gate_desc entry;
+
+       memset(&entry, 0, sizeof(entry));
+
+       entry.offset_low    = (u16)(address & 0xffff);
+       entry.segment       = __KERNEL_CS;
+       entry.bits.type     = GATE_TRAP;
+       entry.bits.p        = 1;
+       entry.offset_middle = (u16)((address >> 16) & 0xffff);
+       entry.offset_high   = (u32)(address >> 32);
+
+       memcpy(&boot_idt[vector], &entry, sizeof(entry));
+}
+
+/* Have this here so we don't need to include <asm/desc.h> */
+static void load_boot_idt(const struct desc_ptr *dtr)
+{
+       asm volatile("lidt %0"::"m" (*dtr));
+}
+
+/* Setup IDT before kernel jumping to  .Lrelocated */
+void load_stage1_idt(void)
+{
+       boot_idt_desc.address = (unsigned long)boot_idt;
+
+       load_boot_idt(&boot_idt_desc);
+}
+
+/* Setup IDT after kernel jumping to  .Lrelocated */
+void load_stage2_idt(void)
+{
+       boot_idt_desc.address = (unsigned long)boot_idt;
+
+       load_boot_idt(&boot_idt_desc);
+}
diff --git a/arch/x86/boot/compressed/idt_handlers_64.S b/arch/x86/boot/compressed/idt_handlers_64.S
new file mode 100644 (file)
index 0000000..36dee2f
--- /dev/null
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Early IDT handler entry points
+ *
+ * Copyright (C) 2019 SUSE
+ *
+ * Author: Joerg Roedel <jroedel@suse.de>
+ */
+
+#include <asm/segment.h>
+
+/* For ORIG_RAX */
+#include "../../entry/calling.h"
+
+.macro EXCEPTION_HANDLER name function error_code=0
+SYM_FUNC_START(\name)
+
+       /* Build pt_regs */
+       .if \error_code == 0
+       pushq   $0
+       .endif
+
+       pushq   %rdi
+       pushq   %rsi
+       pushq   %rdx
+       pushq   %rcx
+       pushq   %rax
+       pushq   %r8
+       pushq   %r9
+       pushq   %r10
+       pushq   %r11
+       pushq   %rbx
+       pushq   %rbp
+       pushq   %r12
+       pushq   %r13
+       pushq   %r14
+       pushq   %r15
+
+       /* Call handler with pt_regs */
+       movq    %rsp, %rdi
+       /* Error code is second parameter */
+       movq    ORIG_RAX(%rsp), %rsi
+       call    \function
+
+       /* Restore regs */
+       popq    %r15
+       popq    %r14
+       popq    %r13
+       popq    %r12
+       popq    %rbp
+       popq    %rbx
+       popq    %r11
+       popq    %r10
+       popq    %r9
+       popq    %r8
+       popq    %rax
+       popq    %rcx
+       popq    %rdx
+       popq    %rsi
+       popq    %rdi
+
+       /* Remove error code and return */
+       addq    $8, %rsp
+
+       iretq
+SYM_FUNC_END(\name)
+       .endm
+
+       .text
+       .code64
index 3efce27ba35c471ecedf807688bb0ad4910f9a11..8feb5f6f329e071aad42695ef2f3f7639c5682f2 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/page.h>
 #include <asm/boot.h>
 #include <asm/bootparam.h>
+#include <asm/desc_defs.h>
 
 #define BOOT_CTYPE_H
 #include <linux/acpi.h>
@@ -133,4 +134,8 @@ int count_immovable_mem_regions(void);
 static inline int count_immovable_mem_regions(void) { return 0; }
 #endif
 
+/* idt_64.c */
+extern gate_desc boot_idt[BOOT_IDT_ENTRIES];
+extern struct desc_ptr boot_idt_desc;
+
 #endif /* BOOT_COMPRESSED_MISC_H */
index a91f3b6e4f2a956bf1a23bb65ae13952e1633bb7..5621fb3f2d1a20864babfbc99f7ea29c3b864bd8 100644 (file)
@@ -109,6 +109,9 @@ struct desc_ptr {
 
 #endif /* !__ASSEMBLY__ */
 
+/* Boot IDT definitions */
+#define        BOOT_IDT_ENTRIES        32
+
 /* Access rights as returned by LAR */
 #define AR_TYPE_RODATA         (0 * (1 << 9))
 #define AR_TYPE_RWDATA         (1 * (1 << 9))