lkml.org 
[lkml]   [2017]   [Mar]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[v5 09/20] x86/insn-eval: Add functions to get default operand and address sizes
Date
These functions read the default values of the address and operand sizes
as specified in the segment descriptor. This information is determined
from the D and L bits. Hence, it can be used for both IA-32e 64-bit and
32-bit legacy modes. For virtual-8086 mode, the default address and
operand sizes are always 2 bytes.

The D bit is only meaningful for code segments. Thus, these functions
always use the code segment selector contained in regs.

Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Adam Buchbinder <adam.buchbinder@gmail.com>
Cc: Colin Ian King <colin.king@canonical.com>
Cc: Lorenzo Stoakes <lstoakes@gmail.com>
Cc: Qiaowei Ren <qiaowei.ren@intel.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Thomas Garnier <thgarnie@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Borislav Petkov <bp@suse.de>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Ravi V. Shankar <ravi.v.shankar@intel.com>
Cc: x86@kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
---
arch/x86/include/asm/insn-eval.h | 2 +
arch/x86/lib/insn-eval.c | 80 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 82 insertions(+)

diff --git a/arch/x86/include/asm/insn-eval.h b/arch/x86/include/asm/insn-eval.h
index b201742..a0d81fc 100644
--- a/arch/x86/include/asm/insn-eval.h
+++ b/arch/x86/include/asm/insn-eval.h
@@ -15,6 +15,8 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs);
int insn_get_reg_offset_modrm_rm(struct insn *insn, struct pt_regs *regs);
int insn_get_reg_offset_sib_base(struct insn *insn, struct pt_regs *regs);
int insn_get_reg_offset_sib_base(struct insn *insn, struct pt_regs *regs);
+unsigned char insn_get_seg_default_address_bytes(struct pt_regs *regs);
+unsigned char insn_get_seg_default_operand_bytes(struct pt_regs *regs);
unsigned long insn_get_seg_base(struct pt_regs *regs, struct insn *insn,
int regoff, bool use_default_seg);

diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 383ca83..cda6c71 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -421,6 +421,86 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, struct insn *insn,
}

/**
+ * insn_get_seg_default_address_bytes - Obtain default address size of segment
+ * @regs: Set of registers containing the segment selector
+ *
+ * Obtain the default address size as indicated in the segment descriptor
+ * selected in regs' code segment selector. In protected mode, the default
+ * address is determined by inspecting the L and D bits of the segment
+ * descriptor. In virtual-8086 mode, the default is always two bytes.
+ *
+ * Return: Default address size of segment
+ */
+unsigned char insn_get_seg_default_address_bytes(struct pt_regs *regs)
+{
+ struct desc_struct *desc;
+ unsigned short seg;
+ int ret;
+
+ if (v8086_mode(regs))
+ return 2;
+
+ seg = (unsigned short)regs->cs;
+
+ ret = get_desc(seg, &desc);
+ if (ret)
+ return 0;
+
+ switch ((desc->l << 1) | desc->d) {
+ case 0: /* Legacy mode. 16-bit addresses. CS.L=0, CS.D=0 */
+ return 2;
+ case 1: /* Legacy mode. 32-bit addresses. CS.L=0, CS.D=1 */
+ return 4;
+ case 2: /* IA-32e 64-bit mode. 64-bit addresses. CS.L=1, CS.D=0 */
+ return 8;
+ case 3: /* Invalid setting. CS.L=1, CS.D=1 */
+ /* fall through */
+ default:
+ return 0;
+ }
+}
+
+/**
+ * insn_get_seg_default_operand_bytes - Obtain default operand size of segment
+ * @regs: Set of registers containing the segment selector
+ *
+ * Obtain the default operand size as indicated in the segment descriptor
+ * selected in regs' code segment selector. In protected mode, the default
+ * operand size is determined by inspecting the L and D bits of the segment
+ * descriptor. In virtual-8086 mode, the default is always two bytes.
+ *
+ * Return: Default operand size of segment
+ */
+unsigned char insn_get_seg_default_operand_bytes(struct pt_regs *regs)
+{
+ struct desc_struct *desc;
+ unsigned short seg;
+ int ret;
+
+ if (v8086_mode(regs))
+ return 2;
+
+ seg = (unsigned short)regs->cs;
+
+ ret = get_desc(seg, &desc);
+ if (ret)
+ return 0;
+
+ switch ((desc->l << 1) | desc->d) {
+ case 0: /* Legacy mode. 16-bit or 8-bit operands CS.L=0, CS.D=0 */
+ return 2;
+ case 1: /* Legacy mode. 32- or 8 bit operands CS.L=0, CS.D=1 */
+ /* fall through */
+ case 2: /* IA-32e 64-bit mode. 32- or 8-bit opnds. CS.L=1, CS.D=0 */
+ return 4;
+ case 3: /* Invalid setting. CS.L=1, CS.D=1 */
+ /* fall through */
+ default:
+ return 0;
+ }
+}
+
+/**
* insn_get_reg_offset_modrm_rm - Obtain register in r/m part of ModRM byte
* @insn: Instruction structure containing the ModRM byte
* @regs: Set of registers indicated by the ModRM byte
--
2.9.3
\
 
 \ /
  Last update: 2017-03-03 22:47    [W:0.959 / U:0.012 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site