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
|
@ -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 <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <math.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
|
#define TEST_CMOV 0
|
||||||
|
|
||||||
|
@ -913,6 +919,316 @@ void test_string(void)
|
||||||
TEST_STRING(cmps, "repnz ");
|
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;
|
static void *call_end __init_call = NULL;
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
|
@ -936,5 +1252,8 @@ int main(int argc, char **argv)
|
||||||
test_lea();
|
test_lea();
|
||||||
test_segs();
|
test_segs();
|
||||||
test_code16();
|
test_code16();
|
||||||
|
test_vm86();
|
||||||
|
test_exceptions();
|
||||||
|
test_self_modifying_code();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue