The insn-eval.c library was recently imported from the kernel source
and contains a significant amount of code to support legacy x86
operating modes, such as 32-bit protected mode, 16-bit addressing, and
v8086 mode.

The KVM selftests, particularly for features like SEV-ES, exclusively
target the 64-bit architecture with guest kernel running in kernel
mode.

Simplify the decoder by removing all logic not relevant to 64-bit mode.
This involves:
- Remove all CONFIG_X86_32 and v8086_mode conditional logic.
- Simplify resolve_default_seg to always assume 64-bit
  segmentation rules (i.e., most segments are ignored).
- Rework insn_get_seg_base to only handle the 64-bit model where
  FS/GS bases are read from MSRs.
- Delete support for 16-bit address decoding.
- Remove complex segment descriptor lookups, which are not used in the
  64-bit flat memory model.

This makes the library smaller and easier to maintain for its intended
purpose within the selftests.

Signed-off-by: Neeraj Upadhyay <[email protected]>
---
 .../testing/selftests/kvm/lib/x86/insn-eval.c | 254 ++----------------
 1 file changed, 28 insertions(+), 226 deletions(-)

diff --git a/tools/testing/selftests/kvm/lib/x86/insn-eval.c 
b/tools/testing/selftests/kvm/lib/x86/insn-eval.c
index a47c01977e72..cf751e4e36ec 100644
--- a/tools/testing/selftests/kvm/lib/x86/insn-eval.c
+++ b/tools/testing/selftests/kvm/lib/x86/insn-eval.c
@@ -178,52 +178,7 @@ static bool check_seg_overrides(struct insn *insn, int 
regoff)
  */
 static int resolve_default_seg(struct insn *insn, struct pt_regs *regs, int 
off)
 {
-       if (any_64bit_mode(regs))
-               return INAT_SEG_REG_IGNORE;
-       /*
-        * Resolve the default segment register as described in Section 3.7.4
-        * of the Intel Software Development Manual Vol. 1:
-        *
-        *  + DS for all references involving r[ABCD]X, and rSI.
-        *  + If used in a string instruction, ES for rDI. Otherwise, DS.
-        *  + AX, CX and DX are not valid register operands in 16-bit address
-        *    encodings but are valid for 32-bit and 64-bit encodings.
-        *  + -EDOM is reserved to identify for cases in which no register
-        *    is used (i.e., displacement-only addressing). Use DS.
-        *  + SS for rSP or rBP.
-        *  + CS for rIP.
-        */
-
-       switch (off) {
-       case offsetof(struct pt_regs, ax):
-       case offsetof(struct pt_regs, cx):
-       case offsetof(struct pt_regs, dx):
-               /* Need insn to verify address size. */
-               if (insn->addr_bytes == 2)
-                       return -EINVAL;
-
-               fallthrough;
-
-       case -EDOM:
-       case offsetof(struct pt_regs, bx):
-       case offsetof(struct pt_regs, si):
-               return INAT_SEG_REG_DS;
-
-       case offsetof(struct pt_regs, di):
-               if (is_string_insn(insn))
-                       return INAT_SEG_REG_ES;
-               return INAT_SEG_REG_DS;
-
-       case offsetof(struct pt_regs, bp):
-       case offsetof(struct pt_regs, sp):
-               return INAT_SEG_REG_SS;
-
-       case offsetof(struct pt_regs, ip):
-               return INAT_SEG_REG_CS;
-
-       default:
-               return -EINVAL;
-       }
+       return INAT_SEG_REG_IGNORE;
 }
 
 /**
@@ -288,12 +243,8 @@ static int resolve_seg_reg(struct insn *insn, struct 
pt_regs *regs, int regoff)
         * be used. Hence, it is not necessary to inspect the instruction,
         * which may be invalid at this point.
         */
-       if (regoff == offsetof(struct pt_regs, ip)) {
-               if (any_64bit_mode(regs))
-                       return INAT_SEG_REG_IGNORE;
-               else
-                       return INAT_SEG_REG_CS;
-       }
+       if (regoff == offsetof(struct pt_regs, ip))
+               return INAT_SEG_REG_IGNORE;
 
        if (!insn)
                return -EINVAL;
@@ -312,11 +263,8 @@ static int resolve_seg_reg(struct insn *insn, struct 
pt_regs *regs, int regoff)
         * In long mode, segment override prefixes are ignored, except for
         * overrides for FS and GS.
         */
-       if (any_64bit_mode(regs)) {
-               if (idx != INAT_SEG_REG_FS &&
-                   idx != INAT_SEG_REG_GS)
-                       idx = INAT_SEG_REG_IGNORE;
-       }
+       if (idx != INAT_SEG_REG_FS && idx != INAT_SEG_REG_GS)
+               idx = INAT_SEG_REG_IGNORE;
 
        return idx;
 }
@@ -327,11 +275,9 @@ static int resolve_seg_reg(struct insn *insn, struct 
pt_regs *regs, int regoff)
  * @seg_reg_idx:       Segment register index to use
  *
  * Obtain the segment selector from any of the CS, SS, DS, ES, FS, GS segment
- * registers. In CONFIG_X86_32, the segment is obtained from either pt_regs or
- * kernel_vm86_regs as applicable. In CONFIG_X86_64, CS and SS are obtained
- * from pt_regs. DS, ES, FS and GS are obtained by reading the actual CPU
- * registers. This done for only for completeness as in CONFIG_X86_64 segment
- * registers are ignored.
+ * registers. CS and SS are obtained from pt_regs. DS, ES, FS and GS are
+ * obtained by reading the actual CPU registers. This done for only for
+ * completeness as in X86_64 segment registers are ignored.
  *
  * Returns:
  *
@@ -344,7 +290,6 @@ static short get_segment_selector(struct pt_regs *regs, int 
seg_reg_idx)
 {
        unsigned short sel;
 
-#ifdef CONFIG_X86_64
        switch (seg_reg_idx) {
        case INAT_SEG_REG_IGNORE:
                return 0;
@@ -367,48 +312,6 @@ static short get_segment_selector(struct pt_regs *regs, 
int seg_reg_idx)
        default:
                return -EINVAL;
        }
-#else /* CONFIG_X86_32 */
-       struct kernel_vm86_regs *vm86regs = (struct kernel_vm86_regs *)regs;
-
-       if (v8086_mode(regs)) {
-               switch (seg_reg_idx) {
-               case INAT_SEG_REG_CS:
-                       return (unsigned short)(regs->cs & 0xffff);
-               case INAT_SEG_REG_SS:
-                       return (unsigned short)(regs->ss & 0xffff);
-               case INAT_SEG_REG_DS:
-                       return vm86regs->ds;
-               case INAT_SEG_REG_ES:
-                       return vm86regs->es;
-               case INAT_SEG_REG_FS:
-                       return vm86regs->fs;
-               case INAT_SEG_REG_GS:
-                       return vm86regs->gs;
-               case INAT_SEG_REG_IGNORE:
-               default:
-                       return -EINVAL;
-               }
-       }
-
-       switch (seg_reg_idx) {
-       case INAT_SEG_REG_CS:
-               return (unsigned short)(regs->cs & 0xffff);
-       case INAT_SEG_REG_SS:
-               return (unsigned short)(regs->ss & 0xffff);
-       case INAT_SEG_REG_DS:
-               return (unsigned short)(regs->ds & 0xffff);
-       case INAT_SEG_REG_ES:
-               return (unsigned short)(regs->es & 0xffff);
-       case INAT_SEG_REG_FS:
-               return (unsigned short)(regs->fs & 0xffff);
-       case INAT_SEG_REG_GS:
-               savesegment(gs, sel);
-               return sel;
-       case INAT_SEG_REG_IGNORE:
-       default:
-               return -EINVAL;
-       }
-#endif /* CONFIG_X86_64 */
 }
 
 static const int pt_regoff[] = {
@@ -420,7 +323,6 @@ static const int pt_regoff[] = {
        offsetof(struct pt_regs, bp),
        offsetof(struct pt_regs, si),
        offsetof(struct pt_regs, di),
-#ifdef CONFIG_X86_64
        offsetof(struct pt_regs, r8),
        offsetof(struct pt_regs, r9),
        offsetof(struct pt_regs, r10),
@@ -429,12 +331,6 @@ static const int pt_regoff[] = {
        offsetof(struct pt_regs, r13),
        offsetof(struct pt_regs, r14),
        offsetof(struct pt_regs, r15),
-#else
-       offsetof(struct pt_regs, ds),
-       offsetof(struct pt_regs, es),
-       offsetof(struct pt_regs, fs),
-       offsetof(struct pt_regs, gs),
-#endif
 };
 
 int pt_regs_offset(struct pt_regs *regs, int regno)
@@ -453,9 +349,6 @@ static int get_regno(struct insn *insn, enum reg_type type)
         * Don't possibly decode a 32-bit instructions as
         * reading a 64-bit-only register.
         */
-       if (IS_ENABLED(CONFIG_X86_64) && !insn->x86_64)
-               nr_registers -= 8;
-
        switch (type) {
        case REG_TYPE_RM:
                regno = X86_MODRM_RM(insn->modrm.value);
@@ -687,52 +580,33 @@ static bool get_desc(struct desc_struct *out, unsigned 
short sel)
  */
 unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx)
 {
-       struct desc_struct desc;
+       unsigned long base;
        short sel;
 
        sel = get_segment_selector(regs, seg_reg_idx);
        if (sel < 0)
                return -1L;
 
-       if (v8086_mode(regs))
-               /*
-                * Base is simply the segment selector shifted 4
-                * bits to the right.
-                */
-               return (unsigned long)(sel << 4);
+       /*
+        * Only FS or GS will have a base address, the rest of
+        * the segments' bases are forced to 0.
+        */
 
-       if (any_64bit_mode(regs)) {
+       if (seg_reg_idx == INAT_SEG_REG_FS) {
+               rdmsrq(MSR_FS_BASE, base);
+       } else if (seg_reg_idx == INAT_SEG_REG_GS) {
                /*
-                * Only FS or GS will have a base address, the rest of
-                * the segments' bases are forced to 0.
+                * swapgs was called at the kernel entry point. Thus,
+                * MSR_KERNEL_GS_BASE will have the user-space GS base.
                 */
-               unsigned long base;
-
-               if (seg_reg_idx == INAT_SEG_REG_FS) {
-                       rdmsrq(MSR_FS_BASE, base);
-               } else if (seg_reg_idx == INAT_SEG_REG_GS) {
-                       /*
-                        * swapgs was called at the kernel entry point. Thus,
-                        * MSR_KERNEL_GS_BASE will have the user-space GS base.
-                        */
-                       if (user_mode(regs))
-                               rdmsrq(MSR_KERNEL_GS_BASE, base);
-                       else
-                               rdmsrq(MSR_GS_BASE, base);
-               } else {
-                       base = 0;
-               }
-               return base;
+               if (user_mode(regs))
+                       rdmsrq(MSR_KERNEL_GS_BASE, base);
+               else
+                       rdmsrq(MSR_GS_BASE, base);
+       } else {
+               base = 0;
        }
-
-       /* In protected mode the segment selector cannot be null. */
-       if (!sel)
-               return -1L;
-
-       if (!get_desc(&desc, sel))
-               return -1L;
-
-       return get_desc_base(&desc);
+       return base;
 }
 
 /**
@@ -762,26 +636,7 @@ static unsigned long get_seg_limit(struct pt_regs *regs, 
int seg_reg_idx)
        if (sel < 0)
                return 0;
 
-       if (any_64bit_mode(regs) || v8086_mode(regs))
-               return -1L;
-
-       if (!sel)
-               return 0;
-
-       if (!get_desc(&desc, sel))
-               return 0;
-
-       /*
-        * If the granularity bit is set, the limit is given in multiples
-        * of 4096. This also means that the 12 least significant bits are
-        * not tested when checking the segment limits. In practice,
-        * this means that the segment ends in (limit << 12) + 0xfff.
-        */
-       limit = get_desc_limit(&desc);
-       if (desc.g)
-               limit = (limit << 12) + 0xfff;
-
-       return limit;
+       return -1L;
 }
 
 /**
@@ -805,10 +660,6 @@ int insn_get_code_seg_params(struct pt_regs *regs)
        struct desc_struct desc;
        short sel;
 
-       if (v8086_mode(regs))
-               /* Address and operand size are both 16-bit. */
-               return INSN_CODE_SEG_PARAMS(2, 2);
-
        sel = get_segment_selector(regs, INAT_SEG_REG_CS);
        if (sel < 0)
                return sel;
@@ -1042,10 +893,7 @@ static int get_eff_addr_modrm(struct insn *insn, struct 
pt_regs *regs,
         * following instruction.
         */
        if (*regoff == -EDOM) {
-               if (any_64bit_mode(regs))
-                       tmp = regs->ip + insn->length;
-               else
-                       tmp = 0;
+               tmp = regs->ip + insn->length;
        } else if (*regoff < 0) {
                return -EINVAL;
        } else {
@@ -1277,9 +1125,6 @@ static void __user *get_addr_ref_16(struct insn *insn, 
struct pt_regs *regs)
 
        linear_addr = (unsigned long)(eff_addr & 0xffff) + seg_base;
 
-       /* Limit linear address to 20 bits */
-       if (v8086_mode(regs))
-               linear_addr &= 0xfffff;
 
 out:
        return (void __user *)linear_addr;
@@ -1338,27 +1183,6 @@ static void __user *get_addr_ref_32(struct insn *insn, 
struct pt_regs *regs)
        if (ret)
                goto out;
 
-       /*
-        * In protected mode, before computing the linear address, make sure
-        * the effective address is within the limits of the segment.
-        * 32-bit addresses can be used in long and virtual-8086 modes if an
-        * address override prefix is used. In such cases, segment limits are
-        * not enforced. When in virtual-8086 mode, the segment limit is -1L
-        * to reflect this situation.
-        *
-        * After computed, the effective address is treated as an unsigned
-        * quantity.
-        */
-       if (!any_64bit_mode(regs) && ((unsigned int)eff_addr > seg_limit))
-               goto out;
-
-       /*
-        * Even though 32-bit address encodings are allowed in virtual-8086
-        * mode, the address range is still limited to [0x-0xffff].
-        */
-       if (v8086_mode(regs) && (eff_addr & ~0xffff))
-               goto out;
-
        /*
         * Data type long could be 64 bits in size. Ensure that our 32-bit
         * effective address is not sign-extended when computing the linear
@@ -1366,9 +1190,6 @@ static void __user *get_addr_ref_32(struct insn *insn, 
struct pt_regs *regs)
         */
        linear_addr = (unsigned long)(eff_addr & 0xffffffff) + seg_base;
 
-       /* Limit linear address to 20 bits */
-       if (v8086_mode(regs))
-               linear_addr &= 0xfffff;
 
 out:
        return (void __user *)linear_addr;
@@ -1389,12 +1210,6 @@ static void __user *get_addr_ref_32(struct insn *insn, 
struct pt_regs *regs)
  *
  * -1L on error.
  */
-#ifndef CONFIG_X86_64
-static void __user *get_addr_ref_64(struct insn *insn, struct pt_regs *regs)
-{
-       return (void __user *)-1L;
-}
-#else
 static void __user *get_addr_ref_64(struct insn *insn, struct pt_regs *regs)
 {
        unsigned long linear_addr = -1L, seg_base;
@@ -1431,7 +1246,6 @@ static void __user *get_addr_ref_64(struct insn *insn, 
struct pt_regs *regs)
 out:
        return (void __user *)linear_addr;
 }
-#endif /* CONFIG_X86_64 */
 
 /**
  * insn_get_addr_ref() - Obtain the linear address referred by instruction
@@ -1472,18 +1286,6 @@ int insn_get_effective_ip(struct pt_regs *regs, unsigned 
long *ip)
 {
        unsigned long seg_base = 0;
 
-       /*
-        * If not in user-space long mode, a custom code segment could be in
-        * use. This is true in protected mode (if the process defined a local
-        * descriptor table), or virtual-8086 mode. In most of the cases
-        * seg_base will be zero as in USER_CS.
-        */
-       if (!user_64bit_mode(regs)) {
-               seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
-               if (seg_base == -1L)
-                       return -EINVAL;
-       }
-
        *ip = seg_base + regs->ip;
 
        return 0;
@@ -1563,7 +1365,7 @@ bool insn_decode_from_regs(struct insn *insn, struct 
pt_regs *regs,
 {
        int seg_defs;
 
-       insn_init(insn, buf, buf_size, user_64bit_mode(regs));
+       insn_init(insn, buf, buf_size, true);
 
        /*
         * Override the default operand and address sizes with what is specified
-- 
2.34.1


Reply via email to