tools/objtool: Check for use of the ENQCMD instruction in the kernel
authorFenghua Yu <fenghua.yu@intel.com>
Mon, 7 Feb 2022 23:02:53 +0000 (15:02 -0800)
committerPeter Zijlstra <peterz@infradead.org>
Tue, 15 Mar 2022 09:32:30 +0000 (10:32 +0100)
The ENQCMD instruction implicitly accesses the PASID_MSR to fill in the
pasid field of the descriptor being submitted to an accelerator. But
there is no precise (and stable across kernel changes) point at which
the PASID_MSR is updated from the value for one task to the next.

Kernel code that uses accelerators must always use the ENQCMDS instruction
which does not access the PASID_MSR.

Check for use of the ENQCMD instruction in the kernel and warn on its
usage.

Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Tony Luck <tony.luck@intel.com>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20220207230254.3342514-11-fenghua.yu@intel.com
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
tools/objtool/arch/x86/decode.c

index c10ef78..479e769 100644 (file)
@@ -112,7 +112,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
        const struct elf *elf = file->elf;
        struct insn insn;
        int x86_64, ret;
-       unsigned char op1, op2,
+       unsigned char op1, op2, op3,
                      rex = 0, rex_b = 0, rex_r = 0, rex_w = 0, rex_x = 0,
                      modrm = 0, modrm_mod = 0, modrm_rm = 0, modrm_reg = 0,
                      sib = 0, /* sib_scale = 0, */ sib_index = 0, sib_base = 0;
@@ -139,6 +139,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
 
        op1 = insn.opcode.bytes[0];
        op2 = insn.opcode.bytes[1];
+       op3 = insn.opcode.bytes[2];
 
        if (insn.rex_prefix.nbytes) {
                rex = insn.rex_prefix.bytes[0];
@@ -491,6 +492,14 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
                        /* nopl/nopw */
                        *type = INSN_NOP;
 
+               } else if (op2 == 0x38 && op3 == 0xf8) {
+                       if (insn.prefixes.nbytes == 1 &&
+                           insn.prefixes.bytes[0] == 0xf2) {
+                               /* ENQCMD cannot be used in the kernel. */
+                               WARN("ENQCMD instruction at %s:%lx", sec->name,
+                                    offset);
+                       }
+
                } else if (op2 == 0xa0 || op2 == 0xa8) {
 
                        /* push fs/gs */