1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include <asm/trap_pf.h> |
3 | #include <asm/segment.h> |
4 | #include <asm/trapnr.h> |
5 | #include "misc.h" |
6 | |
7 | static void set_idt_entry(int vector, void (*handler)(void)) |
8 | { |
9 | unsigned long address = (unsigned long)handler; |
10 | gate_desc entry; |
11 | |
12 | memset(s: &entry, c: 0, n: sizeof(entry)); |
13 | |
14 | entry.offset_low = (u16)(address & 0xffff); |
15 | entry.segment = __KERNEL_CS; |
16 | entry.bits.type = GATE_TRAP; |
17 | entry.bits.p = 1; |
18 | entry.offset_middle = (u16)((address >> 16) & 0xffff); |
19 | entry.offset_high = (u32)(address >> 32); |
20 | |
21 | memcpy(to: &boot_idt[vector], from: &entry, len: sizeof(entry)); |
22 | } |
23 | |
24 | /* Have this here so we don't need to include <asm/desc.h> */ |
25 | static void load_boot_idt(const struct desc_ptr *dtr) |
26 | { |
27 | asm volatile("lidt %0" ::"m" (*dtr)); |
28 | } |
29 | |
30 | /* Setup IDT before kernel jumping to .Lrelocated */ |
31 | void load_stage1_idt(void) |
32 | { |
33 | boot_idt_desc.address = (unsigned long)boot_idt; |
34 | |
35 | |
36 | if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT)) |
37 | set_idt_entry(X86_TRAP_VC, handler: boot_stage1_vc); |
38 | |
39 | load_boot_idt(dtr: &boot_idt_desc); |
40 | } |
41 | |
42 | /* |
43 | * Setup IDT after kernel jumping to .Lrelocated. |
44 | * |
45 | * initialize_identity_maps() needs a #PF handler to be setup |
46 | * in order to be able to fault-in identity mapping ranges; see |
47 | * do_boot_page_fault(). |
48 | * |
49 | * This #PF handler setup needs to happen in load_stage2_idt() where the |
50 | * IDT is loaded and there the #VC IDT entry gets setup too. |
51 | * |
52 | * In order to be able to handle #VCs, one needs a GHCB which |
53 | * gets setup with an already set up pagetable, which is done in |
54 | * initialize_identity_maps(). And there's the catch 22: the boot #VC |
55 | * handler do_boot_stage2_vc() needs to call early_setup_ghcb() itself |
56 | * (and, especially set_page_decrypted()) because the SEV-ES setup code |
57 | * cannot initialize a GHCB as there's no #PF handler yet... |
58 | */ |
59 | void load_stage2_idt(void) |
60 | { |
61 | boot_idt_desc.address = (unsigned long)boot_idt; |
62 | |
63 | set_idt_entry(X86_TRAP_PF, handler: boot_page_fault); |
64 | |
65 | #ifdef CONFIG_AMD_MEM_ENCRYPT |
66 | /* |
67 | * Clear the second stage #VC handler in case guest types |
68 | * needing #VC have not been detected. |
69 | */ |
70 | if (sev_status & BIT(1)) |
71 | set_idt_entry(X86_TRAP_VC, handler: boot_stage2_vc); |
72 | else |
73 | set_idt_entry(X86_TRAP_VC, NULL); |
74 | #endif |
75 | |
76 | load_boot_idt(dtr: &boot_idt_desc); |
77 | } |
78 | |
79 | void cleanup_exception_handling(void) |
80 | { |
81 | /* |
82 | * Flush GHCB from cache and map it encrypted again when running as |
83 | * SEV-ES guest. |
84 | */ |
85 | sev_es_shutdown_ghcb(); |
86 | |
87 | /* Set a null-idt, disabling #PF and #VC handling */ |
88 | boot_idt_desc.size = 0; |
89 | boot_idt_desc.address = 0; |
90 | load_boot_idt(dtr: &boot_idt_desc); |
91 | } |
92 | |