Re: [v6 PATCH 09/21] x86/insn-eval: Add functions to get default operand and address sizes
On Thu, 2017-04-20 at 15:06 +0200, Borislav Petkov wrote: > On Tue, Mar 07, 2017 at 04:32:42PM -0800, Ricardo Neri wrote: > > 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. > > Yeah, we tend to call that customarily 16-bit :) I will call it like this. > > > The D bit is only meaningful for code segments. Thus, these functions > > always use the code segment selector contained in regs. > > > > Cc: Dave Hansen > > Cc: Adam Buchbinder > > Cc: Colin Ian King > > Cc: Lorenzo Stoakes > > Cc: Qiaowei Ren > > Cc: Arnaldo Carvalho de Melo > > Cc: Masami Hiramatsu > > Cc: Adrian Hunter > > Cc: Kees Cook > > Cc: Thomas Garnier > > Cc: Peter Zijlstra > > Cc: Borislav Petkov > > Cc: Dmitry Vyukov > > Cc: Ravi V. Shankar > > Cc: x...@kernel.org > > Signed-off-by: Ricardo Neri > > --- > > 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 > > 0 on error. > > > + */ > > +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) > > Right, so default address and operand size always go together so I don't > think you need two separate functions. > > So what I'd suggest - provided this pans out (I still haven't reviewed > the whole thing) - is to determine the operating mode of the segment: > long, legacy, etc and then return both address and operand sizes. Patch > 17/21 needs them both at the same time AFAICT. It makes sense to me. So far these two functions are used in the same place. Thanks and BR, Ricardo -- To unsubscribe from this list: send t
Re: [v6 PATCH 09/21] x86/insn-eval: Add functions to get default operand and address sizes
On Tue, Mar 07, 2017 at 04:32:42PM -0800, Ricardo Neri wrote: > 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. Yeah, we tend to call that customarily 16-bit :) > The D bit is only meaningful for code segments. Thus, these functions > always use the code segment selector contained in regs. > > Cc: Dave Hansen > Cc: Adam Buchbinder > Cc: Colin Ian King > Cc: Lorenzo Stoakes > Cc: Qiaowei Ren > Cc: Arnaldo Carvalho de Melo > Cc: Masami Hiramatsu > Cc: Adrian Hunter > Cc: Kees Cook > Cc: Thomas Garnier > Cc: Peter Zijlstra > Cc: Borislav Petkov > Cc: Dmitry Vyukov > Cc: Ravi V. Shankar > Cc: x...@kernel.org > Signed-off-by: Ricardo Neri > --- > 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 0 on error. > + */ > +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) Right, so default address and operand size always go together so I don't think you need two separate functions. So what I'd suggest - provided this pans out (I still haven't reviewed the whole thing) - is to determine the operating mode of the segment: long, legacy, etc and then return both address and operand sizes. Patch 17/21 needs them both at the same time AFAICT. -- Regards/Gruss, Boris. SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg) -- -- To unsubscribe from this list: send the line "unsubscribe linux-msdos" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[v6 PATCH 09/21] x86/insn-eval: Add functions to get default operand and address sizes
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 Cc: Adam Buchbinder Cc: Colin Ian King Cc: Lorenzo Stoakes Cc: Qiaowei Ren Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Adrian Hunter Cc: Kees Cook Cc: Thomas Garnier Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Dmitry Vyukov Cc: Ravi V. Shankar Cc: x...@kernel.org Signed-off-by: Ricardo Neri --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-msdos" in the body of a message to majord...@vger.kernel.