| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | |
| 3 | #include "misc.h" |
| 4 | #include "error.h" |
| 5 | #include "sev.h" |
| 6 | |
| 7 | #include <linux/kernel.h> |
| 8 | #include <linux/string.h> |
| 9 | #include <asm/insn.h> |
| 10 | #include <asm/pgtable_types.h> |
| 11 | #include <asm/ptrace.h> |
| 12 | #include <asm/sev.h> |
| 13 | #include <asm/trapnr.h> |
| 14 | #include <asm/trap_pf.h> |
| 15 | #include <asm/fpu/xcr.h> |
| 16 | |
| 17 | #define __BOOT_COMPRESSED |
| 18 | #undef __init |
| 19 | #define __init |
| 20 | |
| 21 | /* Basic instruction decoding support needed */ |
| 22 | #include "../../lib/inat.c" |
| 23 | #include "../../lib/insn.c" |
| 24 | |
| 25 | /* |
| 26 | * Copy a version of this function here - insn-eval.c can't be used in |
| 27 | * pre-decompression code. |
| 28 | */ |
| 29 | bool insn_has_rep_prefix(struct insn *insn) |
| 30 | { |
| 31 | insn_byte_t p; |
| 32 | |
| 33 | insn_get_prefixes(insn); |
| 34 | |
| 35 | for_each_insn_prefix(insn, p) { |
| 36 | if (p == 0xf2 || p == 0xf3) |
| 37 | return true; |
| 38 | } |
| 39 | |
| 40 | return false; |
| 41 | } |
| 42 | |
| 43 | enum es_result vc_decode_insn(struct es_em_ctxt *ctxt) |
| 44 | { |
| 45 | char buffer[MAX_INSN_SIZE]; |
| 46 | int ret; |
| 47 | |
| 48 | memcpy(to: buffer, from: (unsigned char *)ctxt->regs->ip, MAX_INSN_SIZE); |
| 49 | |
| 50 | ret = insn_decode(insn: &ctxt->insn, kaddr: buffer, MAX_INSN_SIZE, m: INSN_MODE_64); |
| 51 | if (ret < 0) |
| 52 | return ES_DECODE_FAILED; |
| 53 | |
| 54 | return ES_OK; |
| 55 | } |
| 56 | |
| 57 | extern void sev_insn_decode_init(void) __alias(inat_init_tables); |
| 58 | |
| 59 | /* |
| 60 | * Only a dummy for insn_get_seg_base() - Early boot-code is 64bit only and |
| 61 | * doesn't use segments. |
| 62 | */ |
| 63 | static unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx) |
| 64 | { |
| 65 | return 0UL; |
| 66 | } |
| 67 | |
| 68 | static enum es_result vc_write_mem(struct es_em_ctxt *ctxt, |
| 69 | void *dst, char *buf, size_t size) |
| 70 | { |
| 71 | memcpy(to: dst, from: buf, len: size); |
| 72 | |
| 73 | return ES_OK; |
| 74 | } |
| 75 | |
| 76 | static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, |
| 77 | void *src, char *buf, size_t size) |
| 78 | { |
| 79 | memcpy(to: buf, from: src, len: size); |
| 80 | |
| 81 | return ES_OK; |
| 82 | } |
| 83 | |
| 84 | static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size) |
| 85 | { |
| 86 | return ES_OK; |
| 87 | } |
| 88 | |
| 89 | static bool fault_in_kernel_space(unsigned long address) |
| 90 | { |
| 91 | return false; |
| 92 | } |
| 93 | |
| 94 | #define sev_printk(fmt, ...) |
| 95 | |
| 96 | #include "../../coco/sev/vc-shared.c" |
| 97 | |
| 98 | void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code) |
| 99 | { |
| 100 | struct es_em_ctxt ctxt; |
| 101 | enum es_result result; |
| 102 | |
| 103 | if (!boot_ghcb && !early_setup_ghcb()) |
| 104 | sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ); |
| 105 | |
| 106 | vc_ghcb_invalidate(ghcb: boot_ghcb); |
| 107 | result = vc_init_em_ctxt(ctxt: &ctxt, regs, exit_code); |
| 108 | if (result != ES_OK) |
| 109 | goto finish; |
| 110 | |
| 111 | result = vc_check_opcode_bytes(ctxt: &ctxt, exit_code); |
| 112 | if (result != ES_OK) |
| 113 | goto finish; |
| 114 | |
| 115 | switch (exit_code) { |
| 116 | case SVM_EXIT_RDTSC: |
| 117 | case SVM_EXIT_RDTSCP: |
| 118 | result = vc_handle_rdtsc(ghcb: boot_ghcb, ctxt: &ctxt, exit_code); |
| 119 | break; |
| 120 | case SVM_EXIT_IOIO: |
| 121 | result = vc_handle_ioio(ghcb: boot_ghcb, ctxt: &ctxt); |
| 122 | break; |
| 123 | case SVM_EXIT_CPUID: |
| 124 | result = vc_handle_cpuid(ghcb: boot_ghcb, ctxt: &ctxt); |
| 125 | break; |
| 126 | default: |
| 127 | result = ES_UNSUPPORTED; |
| 128 | break; |
| 129 | } |
| 130 | |
| 131 | finish: |
| 132 | if (result == ES_OK) |
| 133 | vc_finish_insn(ctxt: &ctxt); |
| 134 | else if (result != ES_RETRY) |
| 135 | sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ); |
| 136 | } |
| 137 | |