//------------------------------------------------------------------- // activity.c // // This module creates a pseudo-file named '/proc/activity' // that is both readable and writable by user applications. // The file consists of an array of counters (corresponding // to the 256 different possible processor interrupts), and // each counter will be incremented whenever its associated // interrupt service routine gets executed. The array also // gets reinitialized if an application writes to the file. // // NOTE: Developed and tested using kernel version 2.4.18. // // programmer: ALLAN CRUSE // date begun: 23 MAR 2003 // revised on: 05 JUL 2003 -- to employ symbol __KERNEL_CS //------------------------------------------------------------------- #define __KERNEL__ #define MODULE #include // for init_module() #include // for create_proc_entry() #include // for copy_to_user() #define SUCCESS 0 #define PROC_DIR NULL #define PROC_MODE 0666 static char modname[] = "activity"; static unsigned short oldidtr[3], newidtr[3]; static unsigned long long *oldidt, *newidt; static unsigned long kpage; static unsigned long counter[ 256 ]; static const int filesize = sizeof( counter ); static void isr_common( unsigned long *tos ) { unsigned long long oldgate; unsigned long intID, newEIP, newCS; // adjust stack for transfer to old interrupt-handler intID = tos[ 11 ] & 0xFF; // get interrupt-number oldgate = oldidt[ intID ]; // get old descriptor newCS = ( oldgate >> 16 )&0x0000FFFF; newEIP = ( oldgate >> 32 )&0xFFFF0000; newEIP |= ( oldgate >> 0 )&0x0000FFFF; tos[ 10 ] = newEIP; tos[ 11 ] = newCS; // update our counter for this interrupt ++counter[ intID ]; } //-------- INTERRUPT SERVICE ROUTINES ---------// asmlinkage void isr_entry( void ); asm(" .text "); asm(" .type isr_entry, @function "); asm(" .align 16 "); asm("isr_entry: "); asm(" i = 0 "); asm(" .rept 256 "); asm(" pushfl "); asm(" pushl $i "); asm(" call ahead "); asm(" i = i + 1 "); asm(" .align 16 "); asm(" .endr "); asm("ahead: "); asm(" pushal "); asm(" pushl %ds "); asm(" pushl %es "); // asm(" movl %ss, %eax "); asm(" movl %eax, %ds "); asm(" movl %eax, %es "); // asm(" pushl %esp "); asm(" call isr_common "); asm(" addl $4, %esp "); // asm(" popl %es "); asm(" popl %ds "); asm(" popal "); asm(" iret "); //-----------------------------------------------// static void load_IDTR( void *regimage ) { asm(" lidt %0 " : : "m" (*(unsigned short*)regimage) ); } static ssize_t my_write( struct file *file, const char *buf, size_t len, loff_t *pos ), my_read( struct file *file, char *buf, size_t len, loff_t *pos ); static struct file_operations my_fops = { owner: THIS_MODULE, write: my_write, read: my_read, }; void cleanup_module( void ) { // destroy our module's proc file remove_proc_entry( modname, PROC_DIR ); // reactivate original Interrupt Descriptor Table smp_call_function( load_IDTR, oldidtr, 1, 1 ); load_IDTR( oldidtr ); // release allocated kernel memory if ( kpage ) free_page( kpage ); printk( "<1>Removing \'%s\' module\n", modname ); } int init_module( void ) { unsigned long long newgate; struct proc_dir_entry *entry; unsigned long isrlocn; int i; printk( "<1>\nInstalling \'%s\' module\n", modname ); // allocate kernel memory for a new Interrupt Descriptor Table kpage = get_free_page( GFP_KERNEL | GFP_DMA ); if ( !kpage ) return -ENOMEM; // initialize our module's global variables asm(" sidt oldidtr \n sidt newidtr "); memcpy( newidtr+1, &kpage, sizeof( unsigned long ) ); oldidt = (unsigned long long*)(*(unsigned long *)(oldidtr+1)); newidt = (unsigned long long*)(*(unsigned long *)(newidtr+1)); memcpy( newidt, oldidt, 256 * sizeof( unsigned long long ) ); // setup new interrupt descriptors isrlocn = (unsigned long)isr_entry; for (i = 0; i < 256; i++) { newgate = isrlocn; newgate &= 0x00000000FFFFFFFF; newgate |= ( newgate << 32 ); newgate &= 0xFFFF00000000FFFF; newgate |= ( __KERNEL_CS << 16 ); //<--- added 7/5/03 newgate |= 0x0000EE0000000000; //<--- Interrupt-Gate newidt[ i ] = newgate; isrlocn += 16; } // activate the new Interrupt Descriptor Table load_IDTR( newidtr ); smp_call_function( load_IDTR, newidtr, 1, 1 ); // create proc file with read-and-write capabilities entry = create_proc_entry( modname, PROC_MODE, PROC_DIR ); entry->proc_fops = &my_fops; return SUCCESS; } MODULE_LICENSE("GPL"); static ssize_t my_write( struct file *file, const char *buf, size_t len, loff_t *pos ) { int more; if ( *pos >= filesize ) return -EINVAL; if ( *pos + len > filesize ) len = filesize - *pos; more = copy_from_user( counter, buf, len ); if ( more ) return -EFAULT; *pos += len; return len; } static ssize_t my_read( struct file *file, char *buf, size_t len, loff_t *pos ) { int more; if ( *pos >= filesize ) return -EINVAL; if ( *pos + len > filesize ) len = filesize - *pos; more = copy_to_user( buf, counter, len ); if ( more ) return -EFAULT; *pos += len; return len; }