Jump from IP 0xFFF0 to IP 0x0020 with a CS base that is not page
aligned, and check that EIP wraps.

Signed-off-by: Paul Galbraith <[email protected]>
---
 tests/tcg/i386/system/meson.build          |   7 ++
 tests/tcg/i386/system/test-branch-wrap16.S | 118 +++++++++++++++++++++
 2 files changed, 125 insertions(+)
 create mode 100644 tests/tcg/i386/system/test-branch-wrap16.S

diff --git a/tests/tcg/i386/system/meson.build 
b/tests/tcg/i386/system/meson.build
index d3f73997c7..d831f7e9c1 100644
--- a/tests/tcg/i386/system/meson.build
+++ b/tests/tcg/i386/system/meson.build
@@ -34,6 +34,13 @@ foreach t: tcg_tests['multiarch-softmmu']['tests']
   endforeach
 endforeach
 
+tests += {
+  'test-branch-wrap16.S': {
+    'cflags': cflags,
+    'qemu_args': qemu_def_args,
+  }
+}
+
 if 'qemu-system-i386' in emulators
   tcg_tests += {
     'i386-softmmu': {
diff --git a/tests/tcg/i386/system/test-branch-wrap16.S 
b/tests/tcg/i386/system/test-branch-wrap16.S
new file mode 100644
index 0000000000..2520891ba7
--- /dev/null
+++ b/tests/tcg/i386/system/test-branch-wrap16.S
@@ -0,0 +1,118 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * A 16-bit near branch must truncate EIP to 16 bits.
+ *
+ * The test jumps from IP 0xFFF0 to IP 0x0020 in 16-bit code. The jump
+ * crosses 0xFFFF. For correct 16-bit behaviour the CPU should wrap the
+ * target to 0x0020, rather than branching to 0x10020.
+ *
+ * TCG used to skip the wrap when the untruncated target (CS base +
+ * 0x10020) was on the same guest page as the jump. That only happens
+ * when the CS base is not page aligned, so the code segment starts at
+ * a page boundary plus 0x10.
+ */
+
+#define SEL_CODE32   0x08
+#define SEL_CODE16   0x18
+
+#define RESULT_NONE  0
+#define RESULT_PASS  1
+#define RESULT_FAIL  2
+
+/*
+ * 16-bit relative 'jmp target', hand-assembled because clang rejects a
+ * rel16 displacement outside [-0x8000, 0x7FFF]. But, in 16-bit code, any
+ * 16-bit displacement is valid. GAS correctly accepts any 16-bit offset.
+ */
+.macro jmp_rel16 target
+        .byte 0xE9                              /* jmp rel16 opcode */
+        .word ((\target) - (. + 2)) & 0xFFFF    /* cw: 16-bit offset */
+.endm
+
+        .data
+
+g_result:
+        .long RESULT_NONE
+
+/* 0x08 and 0x10 must match boot.S, which loaded the data selectors */
+gdt16:
+        .quad 0
+        .word 0xFFFF, 0
+        .byte 0, 0x9b, 0xCF, 0
+        .word 0xFFFF, 0
+        .byte 0, 0x93, 0xCF, 0
+gdt16_code16:
+        /* 16-bit code, 64 KiB limit; main fills in the base */
+        .word 0xFFFF, 0
+        .byte 0, 0x9b, 0x00, 0
+gdt16_end:
+
+gdtr16:
+        .word gdt16_end - gdt16 - 1
+        .long gdt16
+
+fail_msg:
+        .asciz "FAIL: branch left EIP untruncated (result=%d)\n"
+pass_msg:
+        .asciz "PASS\n"
+
+        /*
+         * Page aligned plus 0x10. A larger alignment breaks the
+         * multiboot load in boot.S.
+         */
+        .balign 0x1000
+        .skip 0x10
+wrap16_cs_base:
+        .code16
+
+        /* jmp lands here when EIP wraps to 0x0020 */
+        .org wrap16_cs_base + 0x0020
+wrap16_target:
+        movl $g_result, %eax
+        movb $RESULT_PASS, (%eax)
+        ljmpl $SEL_CODE32, $wrap16_back
+
+        .org wrap16_cs_base + 0xFFF0
+        /* the problematic branch, which should wrap EIP */
+        jmp_rel16 wrap16_target
+
+        /* jmp lands here when EIP does not wrap (0x10020) */
+        .org wrap16_cs_base + 0x10020
+        movl $g_result, %eax
+        movb $RESULT_FAIL, (%eax)
+        ljmpl $SEL_CODE32, $wrap16_back
+
+        .code32
+
+        .text
+
+wrap16_run:
+        ljmp $SEL_CODE16, $0xFFF0
+wrap16_back:
+        ret
+
+        .globl main
+main:
+        movl $wrap16_cs_base, %eax
+        movw %ax, gdt16_code16 + 2
+        shrl $16, %eax
+        movb %al, gdt16_code16 + 4
+        movb %ah, gdt16_code16 + 7
+        lgdt gdtr16
+
+        call wrap16_run
+        cmpl $RESULT_PASS, g_result
+        jne test_failed
+        pushl $pass_msg         /* test passed */
+        call ml_printf
+        addl $4, %esp
+        xorl %eax, %eax
+        ret
+test_failed:
+        pushl g_result
+        pushl $fail_msg
+        call ml_printf
+        addl $8, %esp
+        movl $1, %eax
+        ret
-- 
2.55.0.windows.3


Reply via email to