diff options
author | nalin <nalin> | 1999-07-18 06:44:42 +0000 |
---|---|---|
committer | nalin <nalin> | 1999-07-18 06:44:42 +0000 |
commit | 75d74b8f61de2e50989bf3b601ad414abdeeb964 (patch) | |
tree | 90d09bfd8fce323226dca355641a4916a5a86cb3 /ddcprobe/bioscall.c | |
parent | 99801f67664b5201f6fe92c4b202ee455ba7707d (diff) | |
download | anaconda-75d74b8f61de2e50989bf3b601ad414abdeeb964.tar.gz anaconda-75d74b8f61de2e50989bf3b601ad414abdeeb964.tar.xz anaconda-75d74b8f61de2e50989bf3b601ad414abdeeb964.zip |
Initial revision
Diffstat (limited to 'ddcprobe/bioscall.c')
-rw-r--r-- | ddcprobe/bioscall.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/ddcprobe/bioscall.c b/ddcprobe/bioscall.c new file mode 100644 index 000000000..7c9029b89 --- /dev/null +++ b/ddcprobe/bioscall.c @@ -0,0 +1,207 @@ +#include <sys/types.h> +#include <sys/io.h> +#include <sys/stat.h> +#include <sys/vm86.h> +#include <sys/syscall.h> +#include <sys/mman.h> +#include <ctype.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <netinet/in.h> +#include "bioscall.h" +#ident "$Id$" + +/* Dump some of the interesting parts of a register struct to stdout. */ +void dump_regs(struct vm86_regs *regs) +{ + printf("ax = 0x%04lx\n", regs->eax & 0xffff); + printf("bx = 0x%04lx\n", regs->ebx & 0xffff); + printf("cx = 0x%04lx\n", regs->ecx & 0xffff); + printf("dx = 0x%04lx\n", regs->edx & 0xffff); + printf("cs = 0x%04x\n", regs->cs & 0xffff); + printf("ip = 0x%08lx\n", regs->eip & 0xffffffff); + printf("ss = 0x%04x\n", regs->ss & 0xffff); + printf("sp = 0x%08lx\n", regs->esp & 0xffffffff); + printf("%04x:%08lx = (%ld)\n", + regs->cs & 0xffff, regs->eip, + regs->cs * 16 + regs->eip); +} + +/* Call vm86, but do I/O that gets trapped. We could skip vm86() altogether, + but then I'm not trying to emulate an entire CPU here. Luckily, none of + the I/O instructions (or push/pop) affect the flags, so we can leave them + alone and just deal with performing the I/O operation that caused a return + to 32-bit mode. */ +void do_vm86(struct vm86_struct *vm, char *memory, unsigned stop_eip) { + int ret; + unsigned start_cs, start_eip; + unsigned char *ip = NULL; + + /* Save the starting instruction address. */ + start_cs = vm->regs.cs; + start_eip = vm->regs.eip; + + /* We'll need to pass I/O through. PCI devices have higher addresses + than we can get access to with ioperm(). */ + if(iopl(3) != 0) { + return; + } + + /* Do it. */ + ret = syscall(SYS_vm86old, vm); + while((vm->regs.cs * 16 + vm->regs.eip) != (start_cs * 16 + stop_eip)) { + ip = &memory[vm->regs.cs * 16 + vm->regs.eip]; +#ifdef DEBUG + printf("Unexpected return:\n"); + dump_regs(&vm->regs); + printf("Offending instructions: %02x %02x %02x %02x\n", + ip[0], ip[1], ip[2], ip[3]); +#endif + switch(ip[0]) { + case 0xe4: { /* in al, literal */ + vm->regs.eax &= 0xffffff00; + vm->regs.eax |= inb(ip[1]); + vm->regs.eip += 2; + break; + } + case 0xe6: { /* out al, literal */ + outb(vm->regs.eax & 0xff, ip[1]); + vm->regs.eip += 2; + break; + } + case 0xec: { /* in al, dx */ + vm->regs.eax &= 0xffffff00; + vm->regs.eax |= inb(vm->regs.edx & 0xffff); + vm->regs.eip++; + break; + } + case 0xed: { /* in ax, dx */ + vm->regs.eax &= 0xffff0000; + vm->regs.eax |= inw(vm->regs.edx & 0xffff); + vm->regs.eip++; + break; + } + case 0xee: { /* out al, dx */ + outb(vm->regs.eax & 0xff, + vm->regs.edx & 0xffff); + vm->regs.eip++; + break; + } + case 0xef: { /* out ax, dx */ + outw(vm->regs.eax & 0xffff, + vm->regs.edx & 0xffff); + vm->regs.eip++; + break; + } + case 0x66: { + /* 32-bit extension prefix. Valid, even in + v86 mode. Weird. */ + vm->regs.eip++; + ip++; + switch(ip[0]) { + case 0xed: { /* in eax, dx */ + vm->regs.eax = + inl(vm->regs.edx & 0xffff); + vm->regs.eip++; + break; + } + case 0xef: { /* out eax, dx */ + outl(vm->regs.eax, + vm->regs.edx & 0xffff); + vm->regs.eip++; + break; + } + default: { + fprintf(stderr, "unhandled " + "32-bit opcode\n"); + exit(1); + } + } + break; + } + case 0x55: { /* push bp */ + vm->regs.esp -= 2; + *(u_int16_t*) &memory[vm->regs.ss * 16 + + vm->regs.esp] + = vm->regs.ebp; + vm->regs.eip++; + break; + } + case 0x5d: { /* pop bp */ + vm->regs.ebp = *(u_int16_t*) + &memory[vm->regs.ss*16 + vm->regs.esp]; + vm->regs.esp += 2; + vm->regs.eip++; + break; + } + default: { + fprintf(stderr, "Unexpected stop!\n"); + dump_regs(&vm->regs); + printf("Offending instructions: %02x %02x %02x %02x\n", + ip[0], ip[1], ip[2], ip[3]); + exit(1); + } + } + ip = &memory[vm->regs.cs * 16 + vm->regs.eip]; +#ifdef DEBUG + printf("Resuming execution:\n"); + dump_regs(&vm->regs); + printf("Offending instructions: %02x %02x %02x %02x\n", + ip[0], ip[1], ip[2], ip[3]); +#endif + ret = syscall(SYS_vm86old, vm); + } +#ifdef DEBUG + printf("Reached stopping point, returning.\n"); +#endif + return; +} + +/* Get a snapshot of the first megabyte of memory for use with vm86. */ +unsigned char *vm86_ram_alloc() +{ + unsigned char *memory; + int fd; + + /* Grab address 0 for this process. */ + memory = mmap(0, 0x100000, PROT_READ | PROT_EXEC | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0x00000); + if(memory == MAP_FAILED) { + perror("error mmap()ing memory for the BIOS"); + return MAP_FAILED; + } + + /* Copy the low megabyte to our mmap()'ed buffer. */ + fd = open("/dev/mem", O_RDONLY); + if(fd == -1) { + perror("reading kernel memory"); + return MAP_FAILED; + } + read(fd, memory, 0x100000); + close(fd); + + return memory; +} + +void vm86_ram_free(unsigned char *ram) +{ + munmap(ram, 0x100000); +} + +void bioscall(unsigned char int_no, struct vm86_regs *regs, unsigned char *mem) +{ + unsigned char call[] = {0xcd, int_no, 0xcd, 0x09}; + struct vm86_struct vm; + memset(&vm, 0, sizeof(vm)); + memcpy(&vm.regs, regs, sizeof(vm.regs)); + vm.regs.cs = BIOSCALL_START_SEG; + vm.regs.eip = BIOSCALL_START_OFS; + memcpy(&mem[BIOSCALL_START_SEG * 16 + BIOSCALL_START_OFS], call, + sizeof(call)); + do_vm86(&vm, mem, BIOSCALL_START_OFS + sizeof(call)); + memcpy(regs, &vm.regs, sizeof(vm.regs)); +} |