s3switch/0000775000175000017500000000000011661757163012236 5ustar tormodtormods3switch/s3switch.c0000664000175000017500000002427111661744134014151 0ustar tormodtormod// Simple utility to switch a Savage board between CRT/LCD devices. // T. N. Roberts, 99-Aug-26. // Linux x86 only. // Modified by mADAR // added S3 Virge/GX2 support #include #include #include #include #include #define extern #include #undef extern #include "lrmi.h" #define S3SWITCH_VERSION_STRING "0.1" // Usage: // s3switch [-q] [crt|lcd|both] // Define the Savage chip classes. PCI id's stolen from xf86PciInfo.h #define PCI_CHIP_VIRGEGX2 0x8A10 #define PCI_CHIP_SAVAGE3D 0x8A20 #define PCI_CHIP_SAVAGE3D_MV 0x8A21 #define PCI_CHIP_SAVAGE4 0x8A22 #define PCI_CHIP_SAVAGE2000 0x9102 #define PCI_CHIP_PROSAVAGE_PM 0x8A25 #define PCI_CHIP_PROSAVAGE_KM 0x8A26 #define PCI_CHIP_SAVAGE_MX_MV 0x8c10 #define PCI_CHIP_SAVAGE_MX 0x8c11 #define PCI_CHIP_SAVAGE_IX_MV 0x8c12 #define PCI_CHIP_SAVAGE_IX 0x8c13 #define PCI_CHIP_TWISTERP 0x8d01 #define PCI_CHIP_TWISTERK 0x8d02 #define PCI_CHIP_PROSAVAGE_DDR 0x8d03 #define PCI_CHIP_PROSAVAGE_DDRK 0x8d04 #define PCI_CHIP_SUPSAV_MX128 0x8c22 #define PCI_CHIP_SUPSAV_MX64 0x8c24 #define PCI_CHIP_SUPSAV_MX64C 0x8c26 #define PCI_CHIP_SUPSAV_IX128SDR 0x8c2a #define PCI_CHIP_SUPSAV_IX128DDR 0x8c2b #define PCI_CHIP_SUPSAV_IX64SDR 0x8c2c #define PCI_CHIP_SUPSAV_IX64DDR 0x8c2d #define PCI_CHIP_SUPSAV_IXCSDR 0x8c2e #define PCI_CHIP_SUPSAV_IXCDDR 0x8c2f enum { S3_VIRGEGX2, S3_SAVAGE3D, S3_SAVAGE4, S3_SAVAGEMXIX, S3_SAVAGE2000, S3_PROSAVAGE, S3_SUPERSAVAGE } ChipClass; // Define the device attachment bits. This is CR6D on the non-mobile // chips, and CR6B on the mobiles. // Savage3D does not support LCD, and the Savage4 does not support TV. #define CRT_ACTIVE 0x01 #define LCD_ACTIVE 0x02 #define TV_ACTIVE 0x04 #define CRT_ATTACHED 0x10 #define LCD_ATTACHED 0x20 #define TV_ATTACHED 0x40 #define DUO_ON 0x80 static char * devices[] = { " CRT", " LCD", " TV" }; // Define the TV format bits in CR6B (non-mobile) or CRC0 (mobile). #define TV_FORMAT_MASK 0x0c #define TV_FORMAT_NTSCJ 0x00 #define TV_FORMAT_NTSC 0x04 #define TV_FORMAT_PAL 0x08 // Global state: unsigned int gPCIid = 0; unsigned char jTvFormat = 0; unsigned char jDevices = 0; unsigned char cr79 = 0; void usage() { puts( "Usage: s3switch [-q] [crt|lcd|both|tv] [ntsc|ntscj|pal]" ); puts( " -q requests quiet operation." ); puts( " crt, lcd and tv activates output to those devices. Several devices may be" ); puts( " specified. Only devices which are actually attached may be activated." ); puts( " both is a shortcut for 'crt lcd'." ); puts( " ntscj, ntsc and pal specify the video format for TV output." ); puts( " This is supported on Savage3D only."); puts( " With no parameters, displays all devices currently attached and active."); } void IOAccess( int enable ) { /* Allow or disallow access to I/O ports. */ ioperm( 0x40, 4, enable ); ioperm( 0x4f, 1, enable ); ioperm( 0x61, 1, enable ); ioperm( 0x70, 1, enable ); ioperm( 0x71, 1, enable ); ioperm( 0x80, 1, enable ); ioperm( 0xb2, 1, enable ); ioperm( 0x3b0, 0x30, enable ); ioperm( 0xeb, 1, enable ); } void fetch_bios_data() { // Figure out what kind of Savage it is. outb( 0x2d, 0x3d4 ); gPCIid = inb( 0x3d5 ) << 8; outb( 0x2e, 0x3d4 ); gPCIid |= inb( 0x3d5 ); switch( gPCIid ) { case PCI_CHIP_VIRGEGX2: ChipClass = S3_VIRGEGX2; break; case PCI_CHIP_SAVAGE3D: case PCI_CHIP_SAVAGE3D_MV: ChipClass = S3_SAVAGE3D; break; case PCI_CHIP_SAVAGE4: ChipClass = S3_SAVAGE4; break; case PCI_CHIP_SAVAGE2000: ChipClass = S3_SAVAGE2000; break; case PCI_CHIP_PROSAVAGE_PM: case PCI_CHIP_PROSAVAGE_KM: case PCI_CHIP_TWISTERP: case PCI_CHIP_TWISTERK: case PCI_CHIP_PROSAVAGE_DDR: case PCI_CHIP_PROSAVAGE_DDRK: ChipClass = S3_PROSAVAGE; break; case PCI_CHIP_SAVAGE_MX_MV: case PCI_CHIP_SAVAGE_MX: case PCI_CHIP_SAVAGE_IX_MV: case PCI_CHIP_SAVAGE_IX: ChipClass = S3_SAVAGEMXIX; break; case PCI_CHIP_SUPSAV_MX128: case PCI_CHIP_SUPSAV_MX64: case PCI_CHIP_SUPSAV_MX64C: case PCI_CHIP_SUPSAV_IX128SDR: case PCI_CHIP_SUPSAV_IX128DDR: case PCI_CHIP_SUPSAV_IX64SDR: case PCI_CHIP_SUPSAV_IX64DDR: case PCI_CHIP_SUPSAV_IXCSDR: case PCI_CHIP_SUPSAV_IXCDDR: ChipClass = S3_SUPERSAVAGE; break; default: printf( "PCI id is not a recognized Savage or Virge GX2: %04x\n", gPCIid ); exit(-1); } if( ChipClass == S3_SAVAGEMXIX ) { outb( 0xc0, 0x3d4 ); jTvFormat = inb( 0x3d5 ); outb( 0x6b, 0x3d4 ); jDevices = inb( 0x3d5 ); } else { outb( 0x6b, 0x3d4 ); jTvFormat = inb( 0x3d5 ); outb( 0x6d, 0x3d4 ); jDevices = inb( 0x3d5 ); } outb( 0x79, 0x3d4 ); cr79 = inb( 0x3d5 ); //printf( "Device ID: %04x\n", gPCIid); // The Savage4 and Savage2000 are the only chips which actually detect // the presence of the devices. For the others, we just have to assume. switch( ChipClass ) { case S3_VIRGEGX2: jDevices = (jDevices & 0x0f) | CRT_ATTACHED | TV_ATTACHED; break; case S3_SAVAGE3D: jDevices = (jDevices & 0x0f) | CRT_ATTACHED | TV_ATTACHED; break; case S3_SAVAGE4: case S3_SAVAGE2000: /* These two get it right. */ break; case S3_SAVAGEMXIX: case S3_PROSAVAGE: case S3_SUPERSAVAGE: default: jDevices = (jDevices & 0x0f) | CRT_ATTACHED | TV_ATTACHED | LCD_ATTACHED; break; } } unsigned short set_active_device( int iDevice ) { struct LRMI_regs r; int iResult = 0; if (!LRMI_init()) return 1; /* Go set the active device. */ memset( &r, 0, sizeof(r) ); r.eax = 0x4f14; // S3 extended functions r.ebx = 0x0003; // set active device r.ecx = iDevice; if( ChipClass == S3_SAVAGEMXIX ) r.ecx |= DUO_ON; iResult = LRMI_int( 0x10, &r ); if( !iResult ) { fprintf( stderr, "Could not set device (vm86 failure)\n" ); return 1; } if ( (r.eax & 0xffff) != 0x4f ) { fprintf( stderr, "BIOS returned error code.\n" ); return 1; } return 0; } unsigned short set_tv_state( int state ) { struct LRMI_regs r; int iResult = 0; if (!LRMI_init()) return 1; /* And go set the TV state. */ memset( &r, 0, sizeof(r) ); r.eax = 0x4f14; // S3 extended functions r.ebx = 0x0007; // set tv state r.ecx = state; r.edx = TV_FORMAT_MASK; iResult = LRMI_int( 0x10, &r ); if( !iResult ) { fprintf( stderr, "Could not set TV state (vm86 failure)\n" ); return 1; } if ( (r.eax & 0xffff) != 0x4f ) { fprintf( stderr, "BIOS returned error code.\n" ); return 1; } return 0; } void print_current_state() { int i; printf( "Devices attached: " ); if( !(jDevices & 0x70) ) { // How can this be? printf( "none" ); } else for( i = 0; i < 3; i++ ) if( jDevices & (0x10 << i) ) printf( "%s", devices[i] ); printf( "\nDevices active: " ); if( !(jDevices & 0x07) ) { // How can this be? printf( "none\n" ); } else for( i = 0; i < 3; i++ ) if( jDevices & (0x01 << i) ) printf( "%s", devices[i] ); if( jDevices & TV_ATTACHED ) { static char * szTV[] = { "NTSC-J", "NTSC", "PAL" }; printf( "\nCurrent TV format is %s", szTV[(jTvFormat & TV_FORMAT_MASK) >> 2] ); } printf( "\n" ); } void set_new_state( int newstate ) { // We should prohibit TV on Savage4. if( ((jDevices >> 4) & newstate) != newstate ) { fprintf( stderr, "You attempted to activate a device which is not connected.\n" ); // Alternatively, quiet = 0, return. print_current_state(); exit( -2 ); } set_active_device( newstate ); // If the LCD state changed, we need to adjust cr79 in Savage4. // These values are somewhat magical, and are set by the X server. if( (ChipClass == S3_SAVAGE4) || (ChipClass == S3_SAVAGE2000) ) { if( (jDevices & LCD_ACTIVE) && !(newstate & LCD_ACTIVE) ) { // The LCD was alive and now it isn't. We can increase cr79. if( (cr79 == 5) || (cr79 == 8) ) { cr79 = (cr79 == 5) ? 8 : 0x0e; ioperm( 0x3d4, 2, 1 ); outw( (cr79 << 8) | 0x79, 0x3d4 ); ioperm( 0x3d4, 2, 0 ); } } else if( !(jDevices & LCD_ACTIVE) && (newstate & LCD_ACTIVE) ) { // The LCD was off and now it's on. We must cut back cr79. if( (cr79 == 8) || (cr79 == 0xe) ) { cr79 = (cr79 == 8) ? 5 : 8; ioperm( 0x3d4, 2, 1 ); outw( (cr79 << 8) | 0x79, 0x3d4 ); ioperm( 0x3d4, 2, 0 ); } } } fetch_bios_data(); return; } void set_new_tvstate( int tvstate ) { if( ChipClass == S3_SAVAGE4 ) return; set_tv_state( tvstate ); fetch_bios_data(); return; } int main( int argc, char ** argv ) { int quiet = 0; int newstate = 0; int newtv = 0; // Scan through the argument list. We do very primitive checking here. while( *++argv ) { if( strcmp( *argv, "-q" ) == 0 ) quiet++; else if( strcasecmp( *argv, "crt" ) == 0 ) newstate |= CRT_ACTIVE; else if( strcasecmp( *argv, "lcd" ) == 0 ) newstate |= LCD_ACTIVE; else if( strcasecmp( *argv, "both" ) == 0 ) newstate |= CRT_ACTIVE | LCD_ACTIVE; else if( strcasecmp( *argv, "tv" ) == 0 ) newstate |= TV_ACTIVE; else if( strcasecmp( *argv, "ntsc-j" ) == 0 ) newtv = TV_FORMAT_NTSCJ; else if( strcasecmp( *argv, "ntscj" ) == 0 ) newtv = TV_FORMAT_NTSCJ; else if( strcasecmp( *argv, "ntsc" ) == 0 ) newtv = TV_FORMAT_NTSC; else if( strcasecmp( *argv, "pal" ) == 0 ) newtv = TV_FORMAT_PAL; else if( strcmp( *argv, "-V" ) == 0 ) { printf("s3switch " S3SWITCH_VERSION_STRING "\n"); exit( 0 ); } else if( strcmp( *argv, "-h" ) == 0 ) { usage(); exit( 0 ); } else { fprintf( stderr, "Unknown argument: %s\n", *argv ); usage(); exit( -1 ); } } if( geteuid() != 0 ) { fprintf( stderr, "s3switch must be setuid root.\n" ); exit( -1 ); } IOAccess( 1 ); fetch_bios_data(); if( newtv ) set_new_tvstate( newtv ); if( newstate ) set_new_state( newstate ); if( !quiet ) print_current_state( ); IOAccess( 0 ); return 0; } s3switch/Makefile0000664000175000017500000000013511661562071013665 0ustar tormodtormodCFLAGS = -g CC = gcc all: s3switch s3switch: s3switch.o lrmi.o clean: rm -f s3switch *.o s3switch/ChangeLog0000664000175000017500000001030311661753365014005 0ustar tormodtormodcommit 9f88745ed1f3dde5440d412468c5aaa09e353bbe Author: Tormod Volden Date: Sat Nov 19 16:19:52 2011 +0100 Add -V option to print version Signed-off-by: Tormod Volden commit a9ae553c0f5eeb60bfb4e1dde67f8b08e3b16e7b Author: Tormod Volden Date: Sat Nov 19 15:36:48 2011 +0100 Move privilege check to after arguments parsing So that anyone can run s3switch -h to see usage description. Signed-off-by: Tormod Volden commit 1592c09bc3fbb87dcb8e286a0201006456878533 Author: Tormod Volden Date: Sat Nov 19 14:42:10 2011 +0100 Update lrmi.c and lrmi.h from upstream CVS Main changes: - support for OpenBSD and FreeBSD - reworded license statement (MIT style) Signed-off-by: Tormod Volden commit 5baec03151d4b6c17f0f4e12c575187c6f9bd7c9 Author: Tormod Volden Date: Sat Nov 19 14:34:44 2011 +0100 copyright: Delete duplicated text The same text was written twice. Signed-off-by: Tormod Volden commit 3b209c98859cdf4c09aa9af4d6cd215adb8231e9 Author: Tormod Volden Date: Sun Nov 6 22:12:51 2011 +0100 Add missing format string for printf Signed-off-by: Tormod Volden commit 85b3d9260d1f83671e48208a5ef281451ca25604 Author: Tormod Volden Date: Sun Nov 6 22:01:33 2011 +0100 lrmi.c: Apply lrmi upstream fix to build on 2.6.26+ http://lrmi.cvs.sourceforge.net/viewvc/lrmi/lrmi/lrmi.c?r1=1.13&r2=1.14 It is recommended to link the upstream lrmi instead of using our in-tree copy, but we will leave a buildable lrmi.c here until major distributions catch up and package lrmi. http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=608175 Signed-off-by: Tormod Volden commit 55ed0ba7abcf758330b1000cdd1d914cee93dce5 Author: Ville Skyttä Date: Thu Jul 22 00:00:00 2004 +0200 Patch from GeeXboX 0.97 for ViRGE/GX2 support Extracted from s3switch.tar.bz2 contained in the GeeXboX 0.97 source tarball from http://www.geexbox.org/releases/0.97/geexbox-0.97-full.tar The author seems to be "mADAR". Thanks! commit 1c57df5a09d39ca372436c34c12671daaaab59e5 Author: Ville Skyttä Date: Wed Jul 30 00:00:00 2003 +0200 Avoid segfault on TV signal format change with ThinkPad T23 Fix segfaults seen on IBM ThinkPad T23 when changing TV signal format. Thanks to the upstream author, Tim Roberts, for help in creating this patch. commit bf2f7579fb9b8dcd5cfc6885dde7940ec0b99610 Author: Guido Guenther Date: Sun Oct 26 22:15:02 2003 +0100 Use CFLAGS in Makefile commit 598b297fdca7da7e4d94aa655e7079b7b4f655c9 Author: Guido Guenther Date: Sun Oct 26 22:15:02 2003 +0100 Let TVout work on ProSavageDDR Patch from http://www.mythtv.org/pipermail/mythtv-users/2003-April/003032.html (seems like it originally came from Tim Roberts) http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=207878 commit 6df7f37aa5535ab6a312abf7e8cd4b7dea5e92ad Author: Guido Guenther Date: Wed Apr 23 23:27:30 2003 +0200 Fix includes in s3switch.c to kill gcc warnings commit d1d2a50fb45f6634acbf6ca92871826edd8f7c8f Author: Guido Guenther Date: Sun Jul 8 00:23:33 2001 +0200 Add proper copyright notice commit 913c466af59ed6adef8691b49f563c44b28f4b31 Author: Guido Guenther Date: Sun Jul 8 00:23:33 2001 +0200 Move s3switch.1x man page to s3switch.8 since it is not an X application commit cdf73cc485b0de178c7d81166ab01e53c2225a61 Author: Guido Guenther Date: Sun Jul 8 00:23:33 2001 +0200 Add clean rule to Makefile commit 31999fea5bca32baa7cbe38a54bd88d0888939ab Author: Tim Roberts Date: Thu Sep 12 00:26:00 2002 +0200 Imported Tim Roberts' original code From http://www.probo.com/timr/s3/s3ssrc.zip md5sum 1328b070343ac79c5ed4c613a1113754 s3ssrc.zip s3switch/copyright0000664000175000017500000000240511661735330014162 0ustar tormodtormod Copyright (C) 2000 S3, Incorporated. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of S3, Incorporated shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the S3, Incorporated. s3switch/lrmi.c0000664000175000017500000005403711661735345013354 0ustar tormodtormod/* Linux Real Mode Interface - A library of DPMI-like functions for Linux. Copyright (C) 1998 by Josh Vanderhoof Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL JOSH VANDERHOOF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if defined(__i386__) && (defined(__linux__) || defined(__NetBSD__) \ || defined(__FreeBSD__) || defined(__OpenBSD__)) #include #include #if defined(__linux__) #include #include #ifdef USE_LIBC_VM86 #include #endif #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include #include #include #include #include #include #endif /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */ #if defined(__FreeBSD__) #include #endif #include #include #include #include #include #include "lrmi.h" #define REAL_MEM_BASE ((void *)0x10000) #define REAL_MEM_SIZE 0x40000 #define REAL_MEM_BLOCKS 0x100 struct mem_block { unsigned int size : 20; unsigned int free : 1; }; static struct { int ready; int count; struct mem_block blocks[REAL_MEM_BLOCKS]; } mem_info = { 0 }; static int read_file(char *name, void *p, size_t n) { int fd; fd = open(name, O_RDONLY); if (fd == -1) { perror("open"); return 0; } if (read(fd, p, n) != n) { perror("read"); close(fd); return 0; } close(fd); return 1; } static int map_file(void *start, size_t length, int prot, int flags, char *name, long offset) { void *m; int fd; fd = open(name, (flags & MAP_SHARED) ? O_RDWR : O_RDONLY); if (fd == -1) { perror("open"); return 0; } m = mmap(start, length, prot, flags, fd, offset); if (m == (void *)-1) { perror("mmap"); close(fd); return 0; } close(fd); return 1; } static int real_mem_init(void) { if (mem_info.ready) return 1; if (!map_file((void *)REAL_MEM_BASE, REAL_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, "/dev/zero", 0)) return 0; mem_info.ready = 1; mem_info.count = 1; mem_info.blocks[0].size = REAL_MEM_SIZE; mem_info.blocks[0].free = 1; return 1; } static void real_mem_deinit(void) { if (mem_info.ready) { munmap((void *)REAL_MEM_BASE, REAL_MEM_SIZE); mem_info.ready = 0; } } static void insert_block(int i) { memmove( mem_info.blocks + i + 1, mem_info.blocks + i, (mem_info.count - i) * sizeof(struct mem_block)); mem_info.count++; } static void delete_block(int i) { mem_info.count--; memmove( mem_info.blocks + i, mem_info.blocks + i + 1, (mem_info.count - i) * sizeof(struct mem_block)); } void * LRMI_alloc_real(int size) { int i; char *r = (char *)REAL_MEM_BASE; if (!mem_info.ready) return NULL; if (mem_info.count == REAL_MEM_BLOCKS) return NULL; size = (size + 15) & ~15; for (i = 0; i < mem_info.count; i++) { if (mem_info.blocks[i].free && size < mem_info.blocks[i].size) { insert_block(i); mem_info.blocks[i].size = size; mem_info.blocks[i].free = 0; mem_info.blocks[i + 1].size -= size; return (void *)r; } r += mem_info.blocks[i].size; } return NULL; } void LRMI_free_real(void *m) { int i; char *r = (char *)REAL_MEM_BASE; if (!mem_info.ready) return; i = 0; while (m != (void *)r) { r += mem_info.blocks[i].size; i++; if (i == mem_info.count) return; } mem_info.blocks[i].free = 1; if (i + 1 < mem_info.count && mem_info.blocks[i + 1].free) { mem_info.blocks[i].size += mem_info.blocks[i + 1].size; delete_block(i + 1); } if (i - 1 >= 0 && mem_info.blocks[i - 1].free) { mem_info.blocks[i - 1].size += mem_info.blocks[i].size; delete_block(i); } } #if defined(__linux__) #ifndef TF_MASK #define TF_MASK X86_EFLAGS_TF #define IF_MASK X86_EFLAGS_IF #define IOPL_MASK X86_EFLAGS_IOPL #define VIF_MASK X86_EFLAGS_VIF #define VIP_MASK X86_EFLAGS_VIP #endif #define DEFAULT_VM86_FLAGS (IF_MASK | IOPL_MASK) #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) #define DEFAULT_VM86_FLAGS (PSL_I | PSL_IOPL) #define TF_MASK PSL_T #define VIF_MASK PSL_VIF #endif #define DEFAULT_STACK_SIZE 0x1000 #define RETURN_TO_32_INT 255 #if defined(__linux__) #define CONTEXT_REGS context.vm.regs #define REG(x) x #elif defined(__NetBSD__) || defined(__OpenBSD__) #define CONTEXT_REGS context.vm.substr.regs #define REG(x) vmsc.sc_ ## x #elif defined(__FreeBSD__) #define CONTEXT_REGS context.vm.uc #define REG(x) uc_mcontext.mc_ ## x #endif static struct { int ready; unsigned short ret_seg, ret_off; unsigned short stack_seg, stack_off; #if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__) struct vm86_struct vm; #elif defined(__FreeBSD__) struct { struct vm86_init_args init; ucontext_t uc; } vm; #endif #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) int success; jmp_buf env; void *old_sighandler; int vret; #endif } context = { 0 }; static inline void set_bit(unsigned int bit, void *array) { unsigned char *a = array; a[bit / 8] |= (1 << (bit % 8)); } static inline unsigned int get_int_seg(int i) { return *(unsigned short *)(i * 4 + 2); } static inline unsigned int get_int_off(int i) { return *(unsigned short *)(i * 4); } static inline void pushw(unsigned short i) { CONTEXT_REGS.REG(esp) -= 2; *(unsigned short *)(((unsigned int)CONTEXT_REGS.REG(ss) << 4) + CONTEXT_REGS.REG(esp)) = i; } int LRMI_init(void) { void *m; if (context.ready) return 1; if (!real_mem_init()) return 0; /* Map the Interrupt Vectors (0x0 - 0x400) + BIOS data (0x400 - 0x502) and the ROM (0xa0000 - 0x100000) */ if (!map_file((void *)0, 0x502, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, "/dev/zero", 0)) { real_mem_deinit(); return 0; } if (!read_file("/dev/mem", (void *)0, 0x502)) { munmap((void *)0, 0x502); real_mem_deinit(); return 0; } if (!map_file((void *)0xa0000, 0x100000 - 0xa0000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, "/dev/mem", 0xa0000)) { munmap((void *)0, 0x502); real_mem_deinit(); return 0; } /* Allocate a stack */ m = LRMI_alloc_real(DEFAULT_STACK_SIZE); context.stack_seg = (unsigned int)m >> 4; context.stack_off = DEFAULT_STACK_SIZE; /* Allocate the return to 32 bit routine */ m = LRMI_alloc_real(2); context.ret_seg = (unsigned int)m >> 4; context.ret_off = (unsigned int)m & 0xf; ((unsigned char *)m)[0] = 0xcd; /* int opcode */ ((unsigned char *)m)[1] = RETURN_TO_32_INT; memset(&context.vm, 0, sizeof(context.vm)); /* Enable kernel emulation of all ints except RETURN_TO_32_INT */ #if defined(__linux__) memset(&context.vm.int_revectored, 0, sizeof(context.vm.int_revectored)); set_bit(RETURN_TO_32_INT, &context.vm.int_revectored); #elif defined(__NetBSD__) || defined(__OpenBSD__) set_bit(RETURN_TO_32_INT, &context.vm.int_byuser); #elif defined(__FreeBSD__) set_bit(RETURN_TO_32_INT, &context.vm.init.int_map); #endif context.ready = 1; return 1; } static void set_regs(struct LRMI_regs *r) { CONTEXT_REGS.REG(edi) = r->edi; CONTEXT_REGS.REG(esi) = r->esi; CONTEXT_REGS.REG(ebp) = r->ebp; CONTEXT_REGS.REG(ebx) = r->ebx; CONTEXT_REGS.REG(edx) = r->edx; CONTEXT_REGS.REG(ecx) = r->ecx; CONTEXT_REGS.REG(eax) = r->eax; CONTEXT_REGS.REG(eflags) = DEFAULT_VM86_FLAGS; CONTEXT_REGS.REG(es) = r->es; CONTEXT_REGS.REG(ds) = r->ds; CONTEXT_REGS.REG(fs) = r->fs; CONTEXT_REGS.REG(gs) = r->gs; } static void get_regs(struct LRMI_regs *r) { r->edi = CONTEXT_REGS.REG(edi); r->esi = CONTEXT_REGS.REG(esi); r->ebp = CONTEXT_REGS.REG(ebp); r->ebx = CONTEXT_REGS.REG(ebx); r->edx = CONTEXT_REGS.REG(edx); r->ecx = CONTEXT_REGS.REG(ecx); r->eax = CONTEXT_REGS.REG(eax); r->flags = CONTEXT_REGS.REG(eflags); r->es = CONTEXT_REGS.REG(es); r->ds = CONTEXT_REGS.REG(ds); r->fs = CONTEXT_REGS.REG(fs); r->gs = CONTEXT_REGS.REG(gs); } #define DIRECTION_FLAG (1 << 10) enum { CSEG = 0x2e, SSEG = 0x36, DSEG = 0x3e, ESEG = 0x26, FSEG = 0x64, GSEG = 0x65, }; static void em_ins(int size) { unsigned int edx, edi; edx = CONTEXT_REGS.REG(edx) & 0xffff; edi = CONTEXT_REGS.REG(edi) & 0xffff; edi += (unsigned int)CONTEXT_REGS.REG(es) << 4; if (CONTEXT_REGS.REG(eflags) & DIRECTION_FLAG) { if (size == 4) asm volatile ("std; insl; cld" : "=D" (edi) : "d" (edx), "0" (edi)); else if (size == 2) asm volatile ("std; insw; cld" : "=D" (edi) : "d" (edx), "0" (edi)); else asm volatile ("std; insb; cld" : "=D" (edi) : "d" (edx), "0" (edi)); } else { if (size == 4) asm volatile ("cld; insl" : "=D" (edi) : "d" (edx), "0" (edi)); else if (size == 2) asm volatile ("cld; insw" : "=D" (edi) : "d" (edx), "0" (edi)); else asm volatile ("cld; insb" : "=D" (edi) : "d" (edx), "0" (edi)); } edi -= (unsigned int)CONTEXT_REGS.REG(es) << 4; CONTEXT_REGS.REG(edi) &= 0xffff0000; CONTEXT_REGS.REG(edi) |= edi & 0xffff; } static void em_rep_ins(int size) { unsigned int cx; cx = CONTEXT_REGS.REG(ecx) & 0xffff; while (cx--) em_ins(size); CONTEXT_REGS.REG(ecx) &= 0xffff0000; } static void em_outs(int size, int seg) { unsigned int edx, esi, base; edx = CONTEXT_REGS.REG(edx) & 0xffff; esi = CONTEXT_REGS.REG(esi) & 0xffff; switch (seg) { case CSEG: base = CONTEXT_REGS.REG(cs); break; case SSEG: base = CONTEXT_REGS.REG(ss); break; case ESEG: base = CONTEXT_REGS.REG(es); break; case FSEG: base = CONTEXT_REGS.REG(fs); break; case GSEG: base = CONTEXT_REGS.REG(gs); break; default: case DSEG: base = CONTEXT_REGS.REG(ds); break; } esi += base << 4; if (CONTEXT_REGS.REG(eflags) & DIRECTION_FLAG) { if (size == 4) asm volatile ("std; outsl; cld" : "=S" (esi) : "d" (edx), "0" (esi)); else if (size == 2) asm volatile ("std; outsw; cld" : "=S" (esi) : "d" (edx), "0" (esi)); else asm volatile ("std; outsb; cld" : "=S" (esi) : "d" (edx), "0" (esi)); } else { if (size == 4) asm volatile ("cld; outsl" : "=S" (esi) : "d" (edx), "0" (esi)); else if (size == 2) asm volatile ("cld; outsw" : "=S" (esi) : "d" (edx), "0" (esi)); else asm volatile ("cld; outsb" : "=S" (esi) : "d" (edx), "0" (esi)); } esi -= base << 4; CONTEXT_REGS.REG(esi) &= 0xffff0000; CONTEXT_REGS.REG(esi) |= esi & 0xffff; } static void em_rep_outs(int size, int seg) { unsigned int cx; cx = CONTEXT_REGS.REG(ecx) & 0xffff; while (cx--) em_outs(size, seg); CONTEXT_REGS.REG(ecx) &= 0xffff0000; } static void em_inbl(unsigned char literal) { asm volatile ("inb %w1, %b0" : "=a" (CONTEXT_REGS.REG(eax)) : "d" (literal), "0" (CONTEXT_REGS.REG(eax))); } static void em_inb(void) { asm volatile ("inb %w1, %b0" : "=a" (CONTEXT_REGS.REG(eax)) : "d" (CONTEXT_REGS.REG(edx)), "0" (CONTEXT_REGS.REG(eax))); } static void em_inw(void) { asm volatile ("inw %w1, %w0" : "=a" (CONTEXT_REGS.REG(eax)) : "d" (CONTEXT_REGS.REG(edx)), "0" (CONTEXT_REGS.REG(eax))); } static void em_inl(void) { asm volatile ("inl %w1, %0" : "=a" (CONTEXT_REGS.REG(eax)) : "d" (CONTEXT_REGS.REG(edx))); } static void em_outbl(unsigned char literal) { asm volatile ("outb %b0, %w1" : : "a" (CONTEXT_REGS.REG(eax)), "d" (literal)); } static void em_outb(void) { asm volatile ("outb %b0, %w1" : : "a" (CONTEXT_REGS.REG(eax)), "d" (CONTEXT_REGS.REG(edx))); } static void em_outw(void) { asm volatile ("outw %w0, %w1" : : "a" (CONTEXT_REGS.REG(eax)), "d" (CONTEXT_REGS.REG(edx))); } static void em_outl(void) { asm volatile ("outl %0, %w1" : : "a" (CONTEXT_REGS.REG(eax)), "d" (CONTEXT_REGS.REG(edx))); } static int emulate(void) { unsigned char *insn; struct { unsigned char seg; unsigned int size : 1; unsigned int rep : 1; } prefix = { DSEG, 0, 0 }; int i = 0; insn = (unsigned char *)((unsigned int)CONTEXT_REGS.REG(cs) << 4); insn += CONTEXT_REGS.REG(eip); while (1) { if (insn[i] == 0x66) { prefix.size = 1 - prefix.size; i++; } else if (insn[i] == 0xf3) { prefix.rep = 1; i++; } else if (insn[i] == CSEG || insn[i] == SSEG || insn[i] == DSEG || insn[i] == ESEG || insn[i] == FSEG || insn[i] == GSEG) { prefix.seg = insn[i]; i++; } else if (insn[i] == 0xf0 || insn[i] == 0xf2 || insn[i] == 0x67) { /* these prefixes are just ignored */ i++; } else if (insn[i] == 0x6c) { if (prefix.rep) em_rep_ins(1); else em_ins(1); i++; break; } else if (insn[i] == 0x6d) { if (prefix.rep) { if (prefix.size) em_rep_ins(4); else em_rep_ins(2); } else { if (prefix.size) em_ins(4); else em_ins(2); } i++; break; } else if (insn[i] == 0x6e) { if (prefix.rep) em_rep_outs(1, prefix.seg); else em_outs(1, prefix.seg); i++; break; } else if (insn[i] == 0x6f) { if (prefix.rep) { if (prefix.size) em_rep_outs(4, prefix.seg); else em_rep_outs(2, prefix.seg); } else { if (prefix.size) em_outs(4, prefix.seg); else em_outs(2, prefix.seg); } i++; break; } else if (insn[i] == 0xe4) { em_inbl(insn[i + 1]); i += 2; break; } else if (insn[i] == 0xec) { em_inb(); i++; break; } else if (insn[i] == 0xed) { if (prefix.size) em_inl(); else em_inw(); i++; break; } else if (insn[i] == 0xe6) { em_outbl(insn[i + 1]); i += 2; break; } else if (insn[i] == 0xee) { em_outb(); i++; break; } else if (insn[i] == 0xef) { if (prefix.size) em_outl(); else em_outw(); i++; break; } else return 0; } CONTEXT_REGS.REG(eip) += i; return 1; } #if defined(__linux__) /* I don't know how to make sure I get the right vm86() from libc. The one I want is syscall # 113 (vm86old() in libc 5, vm86() in glibc) which should be declared as "int vm86(struct vm86_struct *);" in . This just does syscall 113 with inline asm, which should work for both libc's (I hope). */ #if !defined(USE_LIBC_VM86) static int lrmi_vm86(struct vm86_struct *vm) { int r; #ifdef __PIC__ asm volatile ( "pushl %%ebx\n\t" "movl %2, %%ebx\n\t" "int $0x80\n\t" "popl %%ebx" : "=a" (r) : "0" (113), "r" (vm)); #else asm volatile ( "int $0x80" : "=a" (r) : "0" (113), "b" (vm)); #endif return r; } #else #define lrmi_vm86 vm86 #endif #endif /* __linux__ */ static void debug_info(int vret) { #ifdef LRMI_DEBUG int i; unsigned char *p; fputs("vm86() failed\n", stderr); fprintf(stderr, "return = 0x%x\n", vret); fprintf(stderr, "eax = 0x%08x\n", CONTEXT_REGS.REG(eax)); fprintf(stderr, "ebx = 0x%08x\n", CONTEXT_REGS.REG(ebx)); fprintf(stderr, "ecx = 0x%08x\n", CONTEXT_REGS.REG(ecx)); fprintf(stderr, "edx = 0x%08x\n", CONTEXT_REGS.REG(edx)); fprintf(stderr, "esi = 0x%08x\n", CONTEXT_REGS.REG(esi)); fprintf(stderr, "edi = 0x%08x\n", CONTEXT_REGS.REG(edi)); fprintf(stderr, "ebp = 0x%08x\n", CONTEXT_REGS.REG(ebp)); fprintf(stderr, "eip = 0x%08x\n", CONTEXT_REGS.REG(eip)); fprintf(stderr, "cs = 0x%04x\n", CONTEXT_REGS.REG(cs)); fprintf(stderr, "esp = 0x%08x\n", CONTEXT_REGS.REG(esp)); fprintf(stderr, "ss = 0x%04x\n", CONTEXT_REGS.REG(ss)); fprintf(stderr, "ds = 0x%04x\n", CONTEXT_REGS.REG(ds)); fprintf(stderr, "es = 0x%04x\n", CONTEXT_REGS.REG(es)); fprintf(stderr, "fs = 0x%04x\n", CONTEXT_REGS.REG(fs)); fprintf(stderr, "gs = 0x%04x\n", CONTEXT_REGS.REG(gs)); fprintf(stderr, "eflags = 0x%08x\n", CONTEXT_REGS.REG(eflags)); fputs("cs:ip = [ ", stderr); p = (unsigned char *)((CONTEXT_REGS.REG(cs) << 4) + (CONTEXT_REGS.REG(eip) & 0xffff)); for (i = 0; i < 16; ++i) fprintf(stderr, "%02x ", (unsigned int)p[i]); fputs("]\n", stderr); #endif } #if defined(__linux__) static int run_vm86(void) { unsigned int vret; sigset_t all_sigs, old_sigs; unsigned long old_gs, old_fs; while (1) { // FIXME: may apply this to BSD equivalents? sigfillset(&all_sigs); sigprocmask(SIG_SETMASK, &all_sigs, &old_sigs); asm volatile ("mov %%gs, %0" : "=rm" (old_gs)); asm volatile ("mov %%fs, %0" : "=rm" (old_fs)); vret = lrmi_vm86(&context.vm); asm volatile ("mov %0, %%gs" :: "rm" (old_gs)); asm volatile ("mov %0, %%fs" :: "rm" (old_fs)); sigprocmask(SIG_SETMASK, &old_sigs, NULL); if (VM86_TYPE(vret) == VM86_INTx) { unsigned int v = VM86_ARG(vret); if (v == RETURN_TO_32_INT) return 1; pushw(CONTEXT_REGS.REG(eflags)); pushw(CONTEXT_REGS.REG(cs)); pushw(CONTEXT_REGS.REG(eip)); CONTEXT_REGS.REG(cs) = get_int_seg(v); CONTEXT_REGS.REG(eip) = get_int_off(v); CONTEXT_REGS.REG(eflags) &= ~(VIF_MASK | TF_MASK); continue; } if (VM86_TYPE(vret) != VM86_UNKNOWN) break; if (!emulate()) break; } debug_info(vret); return 0; } #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) #if defined(__NetBSD__) || defined(__OpenBSD__) static void vm86_callback(int sig, int code, struct sigcontext *sc) { /* Sync our context with what the kernel develivered to us. */ memcpy(&CONTEXT_REGS, sc, sizeof(*sc)); switch (VM86_TYPE(code)) { case VM86_INTx: { unsigned int v = VM86_ARG(code); if (v == RETURN_TO_32_INT) { context.success = 1; longjmp(context.env, 1); } pushw(CONTEXT_REGS.REG(eflags)); pushw(CONTEXT_REGS.REG(cs)); pushw(CONTEXT_REGS.REG(eip)); CONTEXT_REGS.REG(cs) = get_int_seg(v); CONTEXT_REGS.REG(eip) = get_int_off(v); CONTEXT_REGS.REG(eflags) &= ~(VIF_MASK | TF_MASK); break; } case VM86_UNKNOWN: if (emulate() == 0) { context.success = 0; context.vret = code; longjmp(context.env, 1); } break; default: context.success = 0; context.vret = code; longjmp(context.env, 1); return; } /* ...and sync our context back to the kernel. */ memcpy(sc, &CONTEXT_REGS, sizeof(*sc)); } #elif defined(__FreeBSD__) static void vm86_callback(int sig, int code, struct sigcontext *sc) { unsigned char *addr; /* Sync our context with what the kernel develivered to us. */ memcpy(&CONTEXT_REGS, sc, sizeof(*sc)); if (code) { /* XXX probably need to call original signal handler here */ context.success = 0; context.vret = code; longjmp(context.env, 1); } addr = (unsigned char *)((CONTEXT_REGS.REG(cs) << 4) + CONTEXT_REGS.REG(eip)); if (addr[0] == 0xcd) { /* int opcode */ if (addr[1] == RETURN_TO_32_INT) { context.success = 1; longjmp(context.env, 1); } pushw(CONTEXT_REGS.REG(eflags)); pushw(CONTEXT_REGS.REG(cs)); pushw(CONTEXT_REGS.REG(eip)); CONTEXT_REGS.REG(cs) = get_int_seg(addr[1]); CONTEXT_REGS.REG(eip) = get_int_off(addr[1]); CONTEXT_REGS.REG(eflags) &= ~(VIF_MASK | TF_MASK); } else { if (emulate() == 0) { context.success = 0; longjmp(context.env, 1); } } /* ...and sync our context back to the kernel. */ memcpy(sc, &CONTEXT_REGS, sizeof(*sc)); } #endif /* __FreeBSD__ */ static int run_vm86(void) { if (context.old_sighandler) { #ifdef LRMI_DEBUG fprintf(stderr, "run_vm86: callback already installed\n"); #endif return (0); } #if defined(__NetBSD__) || defined(__OpenBSD__) context.old_sighandler = signal(SIGURG, (void (*)(int))vm86_callback); #elif defined(__FreeBSD__) context.old_sighandler = signal(SIGBUS, (void (*)(int))vm86_callback); #endif if (context.old_sighandler == (void *)-1) { context.old_sighandler = NULL; #ifdef LRMI_DEBUG fprintf(stderr, "run_vm86: cannot install callback\n"); #endif return (0); } if (setjmp(context.env)) { #if defined(__NetBSD__) || defined(__OpenBSD__) (void) signal(SIGURG, context.old_sighandler); #elif defined(__FreeBSD__) (void) signal(SIGBUS, context.old_sighandler); #endif context.old_sighandler = NULL; if (context.success) return (1); debug_info(context.vret); return (0); } #if defined(__NetBSD__) || defined(__OpenBSD__) if (i386_vm86(&context.vm) == -1) return (0); #elif defined(__FreeBSD__) if (i386_vm86(VM86_INIT, &context.vm.init)) return 0; CONTEXT_REGS.REG(eflags) |= PSL_VM | PSL_VIF; sigreturn(&context.vm.uc); #endif /* __FreeBSD__ */ /* NOTREACHED */ return (0); } #endif /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */ int LRMI_call(struct LRMI_regs *r) { unsigned int vret; memset(&CONTEXT_REGS, 0, sizeof(CONTEXT_REGS)); set_regs(r); CONTEXT_REGS.REG(cs) = r->cs; CONTEXT_REGS.REG(eip) = r->ip; if (r->ss == 0 && r->sp == 0) { CONTEXT_REGS.REG(ss) = context.stack_seg; CONTEXT_REGS.REG(esp) = context.stack_off; } else { CONTEXT_REGS.REG(ss) = r->ss; CONTEXT_REGS.REG(esp) = r->sp; } pushw(context.ret_seg); pushw(context.ret_off); vret = run_vm86(); get_regs(r); return vret; } int LRMI_int(int i, struct LRMI_regs *r) { unsigned int vret; unsigned int seg, off; seg = get_int_seg(i); off = get_int_off(i); /* If the interrupt is in regular memory, it's probably still pointing at a dos TSR (which is now gone). */ if (seg < 0xa000 || (seg << 4) + off >= 0x100000) { #ifdef LRMI_DEBUG fprintf(stderr, "Int 0x%x is not in rom (%04x:%04x)\n", i, seg, off); #endif return 0; } memset(&CONTEXT_REGS, 0, sizeof(CONTEXT_REGS)); set_regs(r); CONTEXT_REGS.REG(cs) = seg; CONTEXT_REGS.REG(eip) = off; if (r->ss == 0 && r->sp == 0) { CONTEXT_REGS.REG(ss) = context.stack_seg; CONTEXT_REGS.REG(esp) = context.stack_off; } else { CONTEXT_REGS.REG(ss) = r->ss; CONTEXT_REGS.REG(esp) = r->sp; } pushw(DEFAULT_VM86_FLAGS); pushw(context.ret_seg); pushw(context.ret_off); vret = run_vm86(); get_regs(r); return vret; } #else /* (__linux__ || __NetBSD__ || __FreeBSD__ || __OpenBSD__) && __i386__ */ #warning "LRMI is not supported on your system!" #endif s3switch/lrmi.h0000664000175000017500000000535611661735345013361 0ustar tormodtormod/* Linux Real Mode Interface - A library of DPMI-like functions for Linux. Copyright (C) 1998 by Josh Vanderhoof Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL JOSH VANDERHOOF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef LRMI_H #define LRMI_H #if defined(__i386__) && (defined(__linux__) || defined(__NetBSD__) \ || defined(__FreeBSD__) || defined(__OpenBSD__)) struct LRMI_regs { unsigned int edi; unsigned int esi; unsigned int ebp; unsigned int reserved; unsigned int ebx; unsigned int edx; unsigned int ecx; unsigned int eax; unsigned short int flags; unsigned short int es; unsigned short int ds; unsigned short int fs; unsigned short int gs; unsigned short int ip; unsigned short int cs; unsigned short int sp; unsigned short int ss; }; #ifndef LRMI_PREFIX #define LRMI_PREFIX LRMI_ #endif #define LRMI_CONCAT2(a, b) a ## b #define LRMI_CONCAT(a, b) LRMI_CONCAT2(a, b) #define LRMI_MAKENAME(a) LRMI_CONCAT(LRMI_PREFIX, a) /* Package version (high 16bit = major, low 16bit minor) */ #define LRMI_version 0x0009 /* 0.9 */ /* Initialize returns 1 if sucessful, 0 for failure */ #define LRMI_init LRMI_MAKENAME(init) int LRMI_init(void); /* Simulate a 16 bit far call returns 1 if sucessful, 0 for failure */ #define LRMI_call LRMI_MAKENAME(call) int LRMI_call(struct LRMI_regs *r); /* Simulate a 16 bit interrupt returns 1 if sucessful, 0 for failure */ #define LRMI_int LRMI_MAKENAME(int) int LRMI_int(int interrupt, struct LRMI_regs *r); /* Allocate real mode memory The returned block is paragraph (16 byte) aligned */ #define LRMI_alloc_real LRMI_MAKENAME(alloc_real) void * LRMI_alloc_real(int size); /* Free real mode memory */ #define LRMI_free_real LRMI_MAKENAME(free_real) void LRMI_free_real(void *m); #else /* (__linux__ || __NetBSD__ || __FreeBSD__) && __i386__ */ #warning "LRMI is not supported on your system!" #endif #endif s3switch/s3switch.1x0000664000175000017500000000331311655557734014264 0ustar tormodtormod.TH S3SWITCH 8 "S3 Savage Utilities" .SH NAME s3switch \- manage the output devices on an S3 Savage chip .SH SYNOPSIS .B s3switch [\-q] [crt] [lcd] [tv] [both] [ntscj|ntsc|pal] .SH DESCRIPTION The S3 Savage chips have the ability to send their output to a variety of different devices. The Savage3D can display to a standard CRT or to a TV. The Savage4 and Savage2000 can display to a standard CRT or through a digital interface to an LCD panel. The Savage/MX and Savage/IX can ship to any two of those three. .PP The .IR s3switch utility is used to select the devices to be used, and the format of the TV signal. When invoked with no parameters, .I s3switch displays the devices currently attached, and which of those devices are currently active. Supplying one or more of the words .I crt, .I lcd, or .I tv makes those devices the active devices. The word .I both is a shortcut for the combination of .I crt and .I lcd. .PP To change the format of the TV signal, specify either .I ntscj, .I ntsc or .I pal. These words will be silently ignored on systems where a TV is not attached or not possible. .PP The case of the keywords is unimportant; .I LCD and .I lcd are equivalent. .PP .IR S3switch always displays the current state of things just before it exits, unless the .I -q option is given, in which case, it operates quietly. Note that only the Savage4 and Savage2000 are able to detect the presence or absence of the various devices; the rest just assume everything is attached. .IR S3switch must be run as root. .PP .I S3switch will not allow you to activate devices which are not connected, on those chips that are able to detect this. .SH AUTHOR Tim Roberts (timr@probo.com) .SH "SEE ALSO" .BR X(1x), .BR Xserver(1x)