Add emulation/spoofing of SLDT and STR for both 32- and 64-bit
processes.

Wine users have found a small number of Windows apps using SLDT that
were crashing when run on UMIP-enabled systems.

Reported-by: Andreas Rammhold <andi@notmuch.email>
Originally-by: Ricardo Neri <ricardo.neri-calde...@linux.intel.com>
Signed-off-by: Brendan Shanks <bsha...@codeweavers.com>
---
 arch/x86/kernel/umip.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index 8d5cbe1bbb3b..59dfceac5cc0 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -64,6 +64,8 @@
 #define UMIP_DUMMY_GDT_BASE 0xfffffffffffe0000ULL
 #define UMIP_DUMMY_IDT_BASE 0xffffffffffff0000ULL
 
+#define UMIP_DUMMY_TASK_REGISTER_SELECTOR 0x40
+
 /*
  * The SGDT and SIDT instructions store the contents of the global descriptor
  * table and interrupt table registers, respectively. The destination is a
@@ -244,16 +246,24 @@ static int emulate_umip_insn(struct insn *insn, int 
umip_inst,
                *data_size += UMIP_GDT_IDT_LIMIT_SIZE;
                memcpy(data, &dummy_limit, UMIP_GDT_IDT_LIMIT_SIZE);
 
-       } else if (umip_inst == UMIP_INST_SMSW) {
-               unsigned long dummy_value = CR0_STATE;
+       } else if (umip_inst == UMIP_INST_SMSW || umip_inst == UMIP_INST_SLDT ||
+                  umip_inst == UMIP_INST_STR) {
+               unsigned long dummy_value;
+
+               if (umip_inst == UMIP_INST_SMSW)
+                       dummy_value = CR0_STATE;
+               else if (umip_inst == UMIP_INST_STR)
+                       dummy_value = UMIP_DUMMY_TASK_REGISTER_SELECTOR;
+               else
+                       dummy_value = 0;
 
                /*
-                * Even though the CR0 register has 4 bytes, the number
+                * For these 3 instructions, the number
                 * of bytes to be copied in the result buffer is determined
                 * by whether the operand is a register or a memory location.
                 * If operand is a register, return as many bytes as the operand
                 * size. If operand is memory, return only the two least
-                * siginificant bytes of CR0.
+                * siginificant bytes.
                 */
                if (X86_MODRM_MOD(insn->modrm.value) == 3)
                        *data_size = insn->opnd_bytes;
@@ -261,7 +271,6 @@ static int emulate_umip_insn(struct insn *insn, int 
umip_inst,
                        *data_size = 2;
 
                memcpy(data, &dummy_value, *data_size);
-       /* STR and SLDT  are not emulated */
        } else {
                return -EINVAL;
        }
@@ -383,10 +392,6 @@ bool fixup_umip_exception(struct pt_regs *regs)
        umip_pr_warn(regs, "%s instruction cannot be used by applications.\n",
                        umip_insns[umip_inst]);
 
-       /* Do not emulate (spoof) SLDT or STR. */
-       if (umip_inst == UMIP_INST_STR || umip_inst == UMIP_INST_SLDT)
-               return false;
-
        umip_pr_warn(regs, "For now, expensive software emulation returns the 
result.\n");
 
        if (emulate_umip_insn(&insn, umip_inst, dummy_data, &dummy_data_size,
-- 
2.26.2

Reply via email to