added vm86, exceptions and self modifying regression tests
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@174 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
2b413144dc
commit
3a27ad0b57
104
tests/test-i386-vm86.S
Normal file
104
tests/test-i386-vm86.S
Normal file
@ -0,0 +1,104 @@
|
||||
.code16
|
||||
.globl vm86_code_start
|
||||
.globl vm86_code_end
|
||||
|
||||
#define GET_OFFSET(x) ((x) - vm86_code_start + 0x100)
|
||||
|
||||
vm86_code_start:
|
||||
movw $GET_OFFSET(hello_world), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
|
||||
/* prepare int 0x90 vector */
|
||||
xorw %ax, %ax
|
||||
movw %ax, %es
|
||||
es movw $GET_OFFSET(int90_test), 0x90 * 4
|
||||
es movw %cs, 0x90 * 4 + 2
|
||||
|
||||
/* launch int 0x90 */
|
||||
|
||||
int $0x90
|
||||
|
||||
/* test IF support */
|
||||
movw $GET_OFFSET(IF_msg), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
|
||||
pushf
|
||||
popw %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
cli
|
||||
pushf
|
||||
popw %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
sti
|
||||
pushfl
|
||||
popl %edx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
#if 0
|
||||
movw $GET_OFFSET(IF_msg1), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
|
||||
pushf
|
||||
movw %sp, %bx
|
||||
andw $~0x200, (%bx)
|
||||
popf
|
||||
#else
|
||||
cli
|
||||
#endif
|
||||
|
||||
pushf
|
||||
popw %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
pushfl
|
||||
movw %sp, %bx
|
||||
orw $0x200, (%bx)
|
||||
popfl
|
||||
|
||||
pushfl
|
||||
popl %edx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
movb $0x00, %ah
|
||||
int $0x21
|
||||
|
||||
int90_test:
|
||||
pushf
|
||||
pop %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
movw %sp, %bx
|
||||
movw 4(%bx), %dx
|
||||
movb $0xff, %ah
|
||||
int $0x21
|
||||
|
||||
movw $GET_OFFSET(int90_msg), %dx
|
||||
movb $0x09, %ah
|
||||
int $0x21
|
||||
iret
|
||||
|
||||
int90_msg:
|
||||
.string "INT90 started\n$"
|
||||
|
||||
hello_world:
|
||||
.string "Hello VM86 world\n$"
|
||||
|
||||
IF_msg:
|
||||
.string "VM86 IF test\n$"
|
||||
|
||||
IF_msg1:
|
||||
.string "If you see a diff here, your Linux kernel is buggy, please update to 2.4.20 kernel\n$"
|
||||
|
||||
vm86_code_end:
|
||||
|
@ -1,7 +1,13 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/mman.h>
|
||||
#include <asm/vm86.h>
|
||||
|
||||
#define TEST_CMOV 0
|
||||
|
||||
@ -913,6 +919,316 @@ void test_string(void)
|
||||
TEST_STRING(cmps, "repnz ");
|
||||
}
|
||||
|
||||
/* VM86 test */
|
||||
|
||||
static inline void set_bit(uint8_t *a, unsigned int bit)
|
||||
{
|
||||
a[bit / 8] |= (1 << (bit % 8));
|
||||
}
|
||||
|
||||
static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
|
||||
{
|
||||
return (uint8_t *)((seg << 4) + (reg & 0xffff));
|
||||
}
|
||||
|
||||
static inline void pushw(struct vm86_regs *r, int val)
|
||||
{
|
||||
r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
|
||||
*(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
|
||||
}
|
||||
|
||||
#undef __syscall_return
|
||||
#define __syscall_return(type, res) \
|
||||
do { \
|
||||
return (type) (res); \
|
||||
} while (0)
|
||||
|
||||
_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86)
|
||||
|
||||
extern char vm86_code_start;
|
||||
extern char vm86_code_end;
|
||||
|
||||
#define VM86_CODE_CS 0x100
|
||||
#define VM86_CODE_IP 0x100
|
||||
|
||||
void test_vm86(void)
|
||||
{
|
||||
struct vm86plus_struct ctx;
|
||||
struct vm86_regs *r;
|
||||
uint8_t *vm86_mem;
|
||||
int seg, ret;
|
||||
|
||||
vm86_mem = mmap((void *)0x00000000, 0x110000,
|
||||
PROT_WRITE | PROT_READ | PROT_EXEC,
|
||||
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (vm86_mem == MAP_FAILED) {
|
||||
printf("ERROR: could not map vm86 memory");
|
||||
return;
|
||||
}
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
|
||||
/* init basic registers */
|
||||
r = &ctx.regs;
|
||||
r->eip = VM86_CODE_IP;
|
||||
r->esp = 0xfffe;
|
||||
seg = VM86_CODE_CS;
|
||||
r->cs = seg;
|
||||
r->ss = seg;
|
||||
r->ds = seg;
|
||||
r->es = seg;
|
||||
r->fs = seg;
|
||||
r->gs = seg;
|
||||
r->eflags = VIF_MASK;
|
||||
|
||||
/* move code to proper address. We use the same layout as a .com
|
||||
dos program. */
|
||||
memcpy(vm86_mem + (VM86_CODE_CS << 4) + VM86_CODE_IP,
|
||||
&vm86_code_start, &vm86_code_end - &vm86_code_start);
|
||||
|
||||
/* mark int 0x21 as being emulated */
|
||||
set_bit((uint8_t *)&ctx.int_revectored, 0x21);
|
||||
|
||||
for(;;) {
|
||||
ret = vm86(VM86_ENTER, &ctx);
|
||||
switch(VM86_TYPE(ret)) {
|
||||
case VM86_INTx:
|
||||
{
|
||||
int int_num, ah;
|
||||
|
||||
int_num = VM86_ARG(ret);
|
||||
if (int_num != 0x21)
|
||||
goto unknown_int;
|
||||
ah = (r->eax >> 8) & 0xff;
|
||||
switch(ah) {
|
||||
case 0x00: /* exit */
|
||||
goto the_end;
|
||||
case 0x02: /* write char */
|
||||
{
|
||||
uint8_t c = r->edx;
|
||||
putchar(c);
|
||||
}
|
||||
break;
|
||||
case 0x09: /* write string */
|
||||
{
|
||||
uint8_t c, *ptr;
|
||||
ptr = seg_to_linear(r->ds, r->edx);
|
||||
for(;;) {
|
||||
c = *ptr++;
|
||||
if (c == '$')
|
||||
break;
|
||||
putchar(c);
|
||||
}
|
||||
r->eax = (r->eax & ~0xff) | '$';
|
||||
}
|
||||
break;
|
||||
case 0xff: /* extension: write hex number in edx */
|
||||
printf("%08x\n", (int)r->edx);
|
||||
break;
|
||||
default:
|
||||
unknown_int:
|
||||
printf("unsupported int 0x%02x\n", int_num);
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VM86_SIGNAL:
|
||||
/* a signal came, we just ignore that */
|
||||
break;
|
||||
case VM86_STI:
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unhandled vm86 return code (0x%x)\n", ret);
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
the_end:
|
||||
printf("VM86 end\n");
|
||||
munmap(vm86_mem, 0x110000);
|
||||
}
|
||||
|
||||
/* exception tests */
|
||||
#ifndef REG_EAX
|
||||
#define REG_EAX EAX
|
||||
#define REG_EBX EBX
|
||||
#define REG_ECX ECX
|
||||
#define REG_EDX EDX
|
||||
#define REG_ESI ESI
|
||||
#define REG_EDI EDI
|
||||
#define REG_EBP EBP
|
||||
#define REG_ESP ESP
|
||||
#define REG_EIP EIP
|
||||
#define REG_EFL EFL
|
||||
#define REG_TRAPNO TRAPNO
|
||||
#define REG_ERR ERR
|
||||
#endif
|
||||
|
||||
jmp_buf jmp_env;
|
||||
int dump_eip;
|
||||
int dump_si_addr;
|
||||
int v1;
|
||||
int tab[2];
|
||||
|
||||
void sig_handler(int sig, siginfo_t *info, void *puc)
|
||||
{
|
||||
struct ucontext *uc = puc;
|
||||
|
||||
printf("si_signo=%d si_errno=%d si_code=%d",
|
||||
info->si_signo, info->si_errno, info->si_code);
|
||||
if (dump_si_addr) {
|
||||
printf(" si_addr=0x%08lx",
|
||||
(unsigned long)info->si_addr);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("trapno=0x%02x err=0x%08x",
|
||||
uc->uc_mcontext.gregs[REG_TRAPNO],
|
||||
uc->uc_mcontext.gregs[REG_ERR]);
|
||||
if (dump_eip)
|
||||
printf(" EIP=0x%08x", uc->uc_mcontext.gregs[REG_EIP]);
|
||||
printf("\n");
|
||||
longjmp(jmp_env, 1);
|
||||
}
|
||||
|
||||
void test_exceptions(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
volatile int val;
|
||||
|
||||
act.sa_sigaction = sig_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGFPE, &act, NULL);
|
||||
sigaction(SIGILL, &act, NULL);
|
||||
sigaction(SIGSEGV, &act, NULL);
|
||||
sigaction(SIGTRAP, &act, NULL);
|
||||
|
||||
/* test division by zero reporting */
|
||||
dump_eip = 0;
|
||||
dump_si_addr = 0;
|
||||
printf("DIVZ exception (currently imprecise):\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now divide by zero */
|
||||
v1 = 0;
|
||||
v1 = 2 / v1;
|
||||
}
|
||||
|
||||
dump_si_addr = 1;
|
||||
printf("BOUND exception (currently imprecise):\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* bound exception */
|
||||
tab[0] = 1;
|
||||
tab[1] = 10;
|
||||
asm volatile ("bound %0, %1" : : "r" (11), "m" (tab));
|
||||
}
|
||||
|
||||
/* test SEGV reporting */
|
||||
printf("PF exception (currently imprecise):\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now store in an invalid address */
|
||||
*(char *)0x1234 = 1;
|
||||
}
|
||||
|
||||
/* test SEGV reporting */
|
||||
printf("PF exception (currently imprecise):\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* read from an invalid address */
|
||||
v1 = *(char *)0x1234;
|
||||
}
|
||||
|
||||
printf("segment GPF exception (currently imprecise):\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* load an invalid segment */
|
||||
asm volatile ("movl %0, %%fs" : : "r" ((0x1234 << 3) | 0));
|
||||
}
|
||||
|
||||
dump_eip = 1;
|
||||
/* test illegal instruction reporting */
|
||||
printf("UD2 exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* now execute an invalid instruction */
|
||||
asm volatile("ud2");
|
||||
}
|
||||
|
||||
printf("INT exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int $0xfd");
|
||||
}
|
||||
|
||||
printf("INT3 exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("int3");
|
||||
}
|
||||
|
||||
printf("CLI exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
printf("STI exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
printf("INTO exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
/* overflow exception */
|
||||
asm volatile ("addl $1, %0 ; into" : : "r" (0x7fffffff));
|
||||
}
|
||||
|
||||
printf("OUTB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("outb %%al, %%dx" : : "d" (0x4321), "a" (0));
|
||||
}
|
||||
|
||||
printf("INB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("inb %%dx, %%al" : "=a" (val) : "d" (0x4321));
|
||||
}
|
||||
|
||||
printf("REP OUTSB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("rep outsb" : : "d" (0x4321), "S" (tab), "c" (1));
|
||||
}
|
||||
|
||||
printf("REP INSB exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("rep insb" : : "d" (0x4321), "D" (tab), "c" (1));
|
||||
}
|
||||
|
||||
printf("HLT exception:\n");
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
|
||||
printf("single step exception:\n");
|
||||
val = 0;
|
||||
if (setjmp(jmp_env) == 0) {
|
||||
asm volatile ("pushf\n"
|
||||
"orl $0x00100, (%%esp)\n"
|
||||
"popf\n"
|
||||
"movl $0xabcd, %0\n"
|
||||
"movl $0x0, %0\n" : "=m" (val) : : "cc", "memory");
|
||||
}
|
||||
printf("val=0x%x\n", val);
|
||||
}
|
||||
|
||||
/* self modifying code test */
|
||||
uint8_t code[] = {
|
||||
0xb8, 0x1, 0x00, 0x00, 0x00, /* movl $1, %eax */
|
||||
0xc3, /* ret */
|
||||
};
|
||||
|
||||
void test_self_modifying_code(void)
|
||||
{
|
||||
int (*func)(void);
|
||||
|
||||
func = (void *)code;
|
||||
printf("self modifying code:\n");
|
||||
printf("func1 = 0x%x\n", func());
|
||||
code[1] = 0x2;
|
||||
printf("func1 = 0x%x\n", func());
|
||||
}
|
||||
|
||||
static void *call_end __init_call = NULL;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -936,5 +1252,8 @@ int main(int argc, char **argv)
|
||||
test_lea();
|
||||
test_segs();
|
||||
test_code16();
|
||||
test_vm86();
|
||||
test_exceptions();
|
||||
test_self_modifying_code();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user