//------------------------------------------------------------------- // vesainfo.cpp // // This program uses the Linux 'vm86()' system-call to execute // a built-in ROM-BIOS procedure (supplied by the manufacturer // of any VESA-compliant video display controller) which gives // information about capabilities of the SVGA graphics system. // Note that our custom 'dos.c' device-driver module is needed // here to support the memory-mapping of pc-bios system areas. // // Reference: // VESA BIOS Extensions (VBE) Core Functions Standard, version // 3.0, Video Electronics Standards Association (1998). // // programmer: ALLAN CRUSE // written on: 13 JUL 2003 // revised on: 07 DEC 2003 //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for close() #include // for strncpy() #include // for signal() #include // for iopl() #include // for mmap() #include // for vm86_struct #define ARENA_BASE 0x00030000 #define ARENA_SIZE 0x00010000 typedef struct { unsigned char VbeSignature[4]; unsigned char VbeVersion[2]; unsigned short OemStringPtr[2]; unsigned char Capabilities[4]; unsigned short VideoModePtr[2]; unsigned short TotalMemory[1]; unsigned char OemSoftwareRev[2]; unsigned short OemVendorNamePtr[2]; unsigned short OemProductNamePtr[2]; unsigned short OemProductRevPtr[2]; unsigned char Reserved[222]; unsigned char OemData[256]; } VbeInfoBlk; struct vm86_struct vm; void my_sigactn( int signum ) { unsigned char *cp = (unsigned char*)( 16L*vm.regs.cs + vm.regs.eip ); printf( "\nouch! signum=%d \n", signum ); printf( "cs:ip = %04X:%04X %02X \n", vm.regs.cs, vm.regs.eip, cp[0] ); exit(1); } int vm86( struct vm86_struct *vm ) { int retval; asm(" .equ sys_VM86, 113 "); asm(" movl $sys_VM86, %eax "); asm(" movl %0, %%ebx " :: "m" (vm) ); asm(" int $0x80 "); asm(" movl %%eax, %0 " : "=m" (retval) ); return retval; } int my_emulate( struct vm86_struct *vm ) { unsigned long address = 16L * vm->regs.cs + vm->regs.eip; unsigned char *cp = (unsigned char*)address; unsigned int edx = vm->regs.edx; unsigned int eax = vm->regs.eax; if ( cp[0] == 0x66 ) switch( cp[1] ) { case 0xEF: outl( eax, edx ); // outl vm->regs.eip += 2; return 0; case 0xED: eax = inw( edx ); // inl vm->regs.eax = eax; vm->regs.eip += 2; return 0; } else switch( cp[0] ) { case 0xEE: outb( eax, edx ); // outb vm->regs.eip += 1; return 0; case 0xEF: outw( eax, edx ); // outw vm->regs.eip += 1; return 0; case 0xEC: eax |= 0xFF; eax &= inb( edx ); // inb vm->regs.eax = eax; vm->regs.eip += 1; return 0; case 0xED: eax |= 0xFFFF; eax &= inw( edx ); // inw vm->regs.eax = eax; vm->regs.eip += 1; return 0; } return -1; // unemulated instruction encountered } int setup_memory_mappings( void ) { int base, size, prot, flag, fd; void *mm; // insure our custom device-driver gets installed system( "/sbin/insmod dos.o ; clear" ); // setup memory-mapped areas if ( ( fd = open( "/dev/zero", O_RDWR ) ) < 0 ) return -1; prot = PROT_READ | PROT_WRITE | PROT_EXEC; flag = MAP_FIXED | MAP_PRIVATE; base = ARENA_BASE; size = ARENA_SIZE; mm = (void*)base; if ( mmap( mm, size, prot, flag, fd, base ) == MAP_FAILED ) return -1; //printf( "mapped real-mode program-arena successfully \n" ); close( fd ); if ( ( fd = open( "/dev/dos", O_RDWR ) ) < 0 ) return -1; flag = MAP_FIXED | MAP_SHARED; base = 0x00000000; size = 0x00001000; mm = (void*)base; if ( mmap( mm, size, prot, flag, fd, base ) == MAP_FAILED ) return -1; //printf( "mapped IVT and Bios-Data Area successfully \n" ); base = 0x000A0000; size = 0x00060000; mm = (void*)base; if ( mmap( mm, size, prot, flag, fd, base ) == MAP_FAILED ) return -1; //printf( "mapped VRAM and BIOS successfully \n" ); close( fd ); return 0; } void initialize_memory_areas( long base, long size ) { char halt_op[] = "\364"; // 'hlt' opcode int init_ip = sizeof( VbeInfoBlk ); int init_cs = base >> 4; // 1. initialize real-mode data-segment strncpy( (char*)base, "VBE2", 4 ); // 2. initialize real-mode code-segment strncpy( (char*)(base+init_ip), halt_op, 1 ); // 3. initialize real-mode stack-segment short *wp = (short*)( base + size - 6 ); wp[0] = init_ip; // IP-image wp[1] = init_cs; // CS-image wp[2] = 0x0200; // FLAGS: IF=1, IOPL=0 } void display_vesa_info( VbeInfoBlk *vbe ) { unsigned short *wp; unsigned char *cp; cp = vbe->VbeSignature; printf( "\n%s ", cp ); cp = vbe->VbeVersion; printf( "%d.%d ", cp[1], cp[0] ); wp = vbe->OemStringPtr; cp = (unsigned char*)(int)wp[0]; cp += ( wp[1] ) ? 16L*wp[1] : ARENA_BASE; printf( "\nOemStringPtr: \'%s\' ", cp ); cp = vbe->Capabilities; printf( "\nCapabilities: " ); for (int i = 0; i < 4; i++) printf( "%02X ", cp[i] ); wp = vbe->TotalMemory; printf( "\nTotalMemory: %d KB ", wp[0] * 64 ); cp = vbe->OemSoftwareRev; printf( "\nOemSoftwareRev: %d.%d ", cp[1], cp[0] ); wp = vbe->OemVendorNamePtr; cp = (unsigned char*)(int)wp[0]; cp += ( wp[1] ) ? 16L*wp[1] : ARENA_BASE; printf( "\nOemVendorNamePtr: \'%s\' ", cp ); wp = vbe->OemProductNamePtr; cp = (unsigned char*)(int)wp[0]; cp += ( wp[1] ) ? 16L*wp[1] : ARENA_BASE; printf( "\nOemProductNamePtr: \'%s\' ", cp ); wp = vbe->OemProductRevPtr; cp = (unsigned char*)(int)wp[0]; cp += ( wp[1] ) ? 16L*wp[1] : ARENA_BASE; printf( "\nOemProductRevPtr: \'%s\' ", cp ); char station[ 64 ]; gethostname( station, 63 ); printf( "\n\nList of VESA video modes supported " ); printf( "on station \'%s\': \n", station ); wp = vbe->VideoModePtr; short *vp = (short*)( 16L * wp[1] + wp[0] ); for (int i = 0; i < 64; i++) { if ( vp[i] == ~0 ) break; if ( ( i % 12 ) == 0 ) printf( "\n " ); printf( "%04X ", vp[i] ); } printf( "\n\n" ); } void initialize_vm_structure( long base, long size ) { short *intvector = (short*)(0x10 * 4); vm.cpu_type = CPU_386; vm.regs.ss = base >> 4; vm.regs.esp = size - 6; vm.regs.eflags = 0x00023200; // VM=1, IOPL=3, IF=1 vm.regs.cs = intvector[1]; vm.regs.eip = intvector[0]; vm.regs.eax = 0x4F00; vm.regs.es = ARENA_BASE >> 4; vm.regs.edi = 0; } int main( int argc, char **argv ) { // enable input/output permission if ( iopl( 3 ) ) { perror( "iopl" ); exit(1); } // setup required memory-mappings if ( setup_memory_mappings() ) { perror( "mmap" ); exit(1); } // initialize the real-mode program-arena initialize_memory_areas( ARENA_BASE, ARENA_SIZE ); // initialize the 'vm86_struct' structure initialize_vm_structure( ARENA_BASE, ARENA_SIZE ); // setup our signal-handler (in case of 'bugs') signal( SIGSEGV, my_sigactn ); signal( SIGILL, my_sigactn ); // loop to execute 'vm86()' system-calls while ( vm86( &vm ) ) if ( my_emulate( &vm ) ) break; // show VESA Information if we executed to completion unsigned char *cp; cp = (unsigned char*)( 16L * vm.regs.cs + vm.regs.eip ); if ( cp[0] == 0xF4 ) { VbeInfoBlk *vbe = (VbeInfoBlk*)ARENA_BASE; display_vesa_info( vbe ); exit(0); } // otherwise show diagnostic information (for debugging) printf( "\nunemulated instruction encountered: "); printf( "\nCS:EIP=%04X:%08X ", vm.regs.cs, vm.regs.eip ); printf( "%02X %02X %02X \n\n", cp[0], cp[1], cp[2] ); }