The Intel manual states, "Move lower 16 bits of r/m64 to segment register,"
which is somewhat ambiguous. Therefore, I have written the following test to
verify this.

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys mman.h="">

int main (int argc, char** argv) {
    uint16_t gs;
    int ps = getpagesize();
    __asm__("mov %%gs, %0" : "=r" (gs));
    void* p = mmap(NULL, ps * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (p == MAP_FAILED) {
        perror("mmap");
        abort();
    }
    if (mprotect(p, ps, PROT_READ | PROT_WRITE) != 0) {
        perror("mprotect");
        abort();
    }

    uint16_t* page_bnd = (uint16_t*)(p + ps - 2);
    *page_bnd = gs;
    __asm__ volatile ("mov (%0), %%gs" :: "r" (page_bnd));
    __asm__ volatile (".byte 0x48 \n\t mov (%0), %%gs" :: "r" (page_bnd));
    return 0;
}

The loading of a 2-byte segment at the page boundary did not trigger an 
exception,
indicating that the processor actually performed only a 2-byte load.

Additionally, the previous translation also only involved a two-byte load.

    case 0x8e: /* mov seg, Gv */
        modrm = x86_ldub_code(env, s);
        reg = (modrm &gt;&gt; 3) &amp; 7;
        if (reg &gt;= 6 || reg == R_CS)
            goto illegal_op;
        gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
        gen_movl_seg_T0(s, reg);
        break;

Therefore, a two-byte load seems reasonable and should not cause any errors.


Thank you!

Xinyu Li


&gt; -----Original Messages-----
&gt; From: "Paolo Bonzini" <pbonz...@redhat.com>
&gt; Sent Time: 2024-06-03 15:34:47 (Monday)
&gt; To: lixinyu...@ict.ac.cn, qemu-devel@nongnu.org
&gt; Cc: richard.hender...@linaro.org, edua...@habkost.net, "Xinyu Li" 
<lixi...@loongson.cn>
&gt; Subject: Re: [PATCH] target/i386: fix memory opsize for Mov to/from Seg
&gt; 
&gt; On 6/2/24 12:05, lixinyu...@ict.ac.cn wrote:
&gt; &gt; From: Xinyu Li <lixi...@loongson.cn>
&gt; &gt; 
&gt; &gt; This commit fixes an issue with MOV instructions (0x8C and 0x8E)
&gt; &gt; involving segment registers by explicitly setting the memory operand
&gt; &gt; size to 16 bits. It introduces a new flag X86_SPECIAL_MovSeg to handle
&gt; &gt; this specification correctly.
&gt; &gt; 
&gt; &gt; Signed-off-by: Xinyu Li <lixinyu...@ict.ac.cn>
&gt; &gt; ---
&gt; &gt;   target/i386/tcg/decode-new.c.inc | 12 ++++++++++--
&gt; &gt;   target/i386/tcg/decode-new.h     |  2 ++
&gt; &gt;   2 files changed, 12 insertions(+), 2 deletions(-)
&gt; &gt; 
&gt; &gt; diff --git a/target/i386/tcg/decode-new.c.inc 
b/target/i386/tcg/decode-new.c.inc
&gt; &gt; index 0ec849b003..ab7dbb45f9 100644
&gt; &gt; --- a/target/i386/tcg/decode-new.c.inc
&gt; &gt; +++ b/target/i386/tcg/decode-new.c.inc
&gt; &gt; @@ -202,6 +202,7 @@
&gt; &gt;   #define avx_movx .special = X86_SPECIAL_AVXExtMov,
&gt; &gt;   #define sextT0 .special = X86_SPECIAL_SExtT0,
&gt; &gt;   #define zextT0 .special = X86_SPECIAL_ZExtT0,
&gt; &gt; +#define movseg .special = X86_SPECIAL_MovSeg,
&gt; &gt;   
&gt; &gt;   #define vex1 .vex_class = 1,
&gt; &gt;   #define vex1_rep3 .vex_class = 1, .vex_special = X86_VEX_REPScalar,
&gt; &gt; @@ -1576,9 +1577,9 @@ static const X86OpEntry opcodes_root[256] = {
&gt; &gt;       [0x89] = X86_OP_ENTRY3(MOV, E,v, G,v, None, None),
&gt; &gt;       [0x8A] = X86_OP_ENTRY3(MOV, G,b, E,b, None, None),
&gt; &gt;       [0x8B] = X86_OP_ENTRY3(MOV, G,v, E,v, None, None),
&gt; &gt; -    [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None),
&gt; &gt; +    [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None, movseg),
&gt; 
&gt; Indeed this was a mistake, thanks!  The manual doesn't list it
&gt; in Table A-2, but the description of the instruction mentions
&gt; "r16/r32/m16" which is what you are implementing it.
&gt; 
&gt; &gt;       [0x8D] = X86_OP_ENTRY3(LEA, G,v, M,v, None, None, noseg),
&gt; &gt; -    [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,v, None, None),
&gt; &gt; +    [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,v, None, None, movseg),
&gt; 
&gt; This is also wrong, but here the manual is correct.  It can be
&gt; written as "S,w, E,w" instead without special casing it.
&gt; 
&gt; Therefore the new X86InsnSpecial only needs to cover op[0]...
&gt; 
&gt; &gt; +    /* Memory operand size of Mov to/from Seg is MO_16 */
&gt; &gt; +    X86_SPECIAL_MovSeg,
&gt; 
&gt; ... and I would call it Op0_Mw, for consistency with other similar
&gt; entries of X86InsnSpecial.
&gt; 
&gt; So I queued your patch with a few small changes:
&gt; 
&gt; diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h
&gt; index 51ef0e621b9..d6335597a13 100644
&gt; --- a/target/i386/tcg/decode-new.h
&gt; +++ b/target/i386/tcg/decode-new.h
&gt; @@ -203,6 +203,8 @@ typedef enum X86InsnSpecial {
&gt;       /* When loaded into s-&gt;T0, register operand 1 is zero/sign 
extended.  */
&gt;       X86_SPECIAL_SExtT0,
&gt;       X86_SPECIAL_ZExtT0,
&gt; +    /* Memory operand size of MOV from segment register is MO_16 */
&gt; +    X86_SPECIAL_Op0_Mw,
&gt;   } X86InsnSpecial;
&gt;   
&gt;   /*
&gt; diff --git a/target/i386/tcg/decode-new.c.inc 
b/target/i386/tcg/decode-new.c.inc
&gt; index 0ec849b0035..599047df94a 100644
&gt; --- a/target/i386/tcg/decode-new.c.inc
&gt; +++ b/target/i386/tcg/decode-new.c.inc
&gt; @@ -202,6 +202,7 @@
&gt;   #define avx_movx .special = X86_SPECIAL_AVXExtMov,
&gt;   #define sextT0 .special = X86_SPECIAL_SExtT0,
&gt;   #define zextT0 .special = X86_SPECIAL_ZExtT0,
&gt; +#define op0_Mw .special = X86_SPECIAL_Op0_Mw,
&gt;   
&gt;   #define vex1 .vex_class = 1,
&gt;   #define vex1_rep3 .vex_class = 1, .vex_special = X86_VEX_REPScalar,
&gt; @@ -1576,9 +1577,9 @@ static const X86OpEntry opcodes_root[256] = {
&gt;       [0x89] = X86_OP_ENTRY3(MOV, E,v, G,v, None, None),
&gt;       [0x8A] = X86_OP_ENTRY3(MOV, G,b, E,b, None, None),
&gt;       [0x8B] = X86_OP_ENTRY3(MOV, G,v, E,v, None, None),
&gt; -    [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None),
&gt; +    [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None, op0_Mw),
&gt;       [0x8D] = X86_OP_ENTRY3(LEA, G,v, M,v, None, None, noseg),
&gt; -    [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,v, None, None),
&gt; +    [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,w, None, None),
&gt;       [0x8F] = X86_OP_GROUPw(group1A, E,v),
&gt;   
&gt;       [0x98] = X86_OP_ENTRY1(CBW,    0,v), /* rAX */
&gt; @@ -2514,6 +2515,13 @@ static void disas_insn(DisasContext *s,
&gt;           s-&gt;override = -1;
&gt;           break;
&gt;   
&gt; +    case X86_SPECIAL_Op0_Mw:
&gt; +        assert(decode.op[0].unit == X86_OP_INT);
&gt; +        if (decode.op[0].has_ea) {
&gt; +            decode.op[0].ot = MO_16;
&gt; +        }
&gt; +        break;
&gt; +
&gt;       default:
&gt;           break;
&gt;       }
&gt; 
&gt; Thank you very much!
&gt; 
&gt; Paolo


</lixinyu...@ict.ac.cn></lixi...@loongson.cn></lixi...@loongson.cn></pbonz...@redhat.com></sys></unistd.h></stdlib.h></stdint.h></stdio.h>

Reply via email to