//------------------------------------------------------------------- // pgfaults.c // // This module installs a kernel function which intercepts all // page-fault exceptions in order to record statistics that an // application program would be able to access (by reading the // pseudo-file named '/proc/pgfaults'). // // programmer: ALLAN CRUSE // written on: 14 OCT 2004 //------------------------------------------------------------------- #include // for init_module() #include // for create_proc_read_entry() #define PGF_ID 14 static char modname[] = "pgfaults"; static unsigned long reason[ 8 ]; static unsigned short oldidtr[3], newidtr[3]; static unsigned long long *oldidt, *newidt; static unsigned long kpage, isr_orig, selector_cs; static void pgf_intercept( unsigned long *tos ) { int error_code = tos[ 10 ]; reason[ error_code & 7 ] += 1; } //------ PAGE_FAULT EXCEPTION-HANDLER --------// asmlinkage void isr_page_fault( void ); asm(" .text "); asm(" .type isr_page_fault, @function "); asm("isr_page_fault: "); 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 pgf_intercept "); asm(" addl $4, %esp "); // asm(" popl %es "); asm(" popl %ds "); asm(" popal "); asm(" jmp *isr_orig "); //----------------------------------------------// static int my_get_info( char *buf, char **start, off_t off, int count ) { int i, len; len = 0; len += sprintf( buf+len, "\nPage-Faults Summary\n" ); len += sprintf( buf+len, "\nReason Occurrences\n" ); for (i = 0; i < 8; i++) { len += sprintf( buf+len, " %d%d%d:", (i>>2)&1, (i>>1)&1, i&1 ); len += sprintf( buf+len, " %8d ", reason[ i ] ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n" ); return len; } MODULE_LICENSE("GPL"); static void load_IDTR( void *regimage ) { asm(" lidt %0 " : : "m" (*(unsigned short *)regimage) ); } void cleanup_module( void ) { // delete the pseudo-file remove_proc_entry( modname, NULL ); // deactivate our altered Interrupt Descriptor Table smp_call_function( load_IDTR, oldidtr, 1, 1 ); load_IDTR( oldidtr ); // release the allocated page of kernel memory if ( kpage ) free_page( kpage ); printk( "<1>Removing \'%s\' module\n", modname ); } int init_module( void ) { unsigned long long gate_desc; printk( "<1>\nInstalling \'%s\' module\n", modname ); // allocate a page of kernel memory to hold our new IDT kpage = get_free_page( GFP_KERNEL ); if ( !kpage ) return -ENOMEM; // copy the Interrupt Descriptor Table to this 'writable' page asm(" sidt oldidtr \n sidt newidtr \n movw %cs, selector_cs "); memcpy( newidtr+1, &kpage, sizeof( kpage ) ); oldidt = (unsigned long long*)(*(unsigned long*)(oldidtr+1)); newidt = (unsigned long long*)(*(unsigned long*)(newidtr+1)); memcpy( newidt, oldidt, 256 * sizeof( unsigned long long ) ); // initialize our exception-handler's indirect jump address gate_desc = oldidt[ PGF_ID ]; gate_desc &= 0xFFFF00000000FFFFLL; gate_desc |= ( gate_desc >> 32 ); isr_orig = (unsigned long)gate_desc; // modify our copy of the Interrupt Descriptor Table gate_desc = (unsigned long long)isr_page_fault; gate_desc &= 0x00000000FFFFFFFFLL; gate_desc |= ( gate_desc << 32 ); gate_desc &= 0xFFFF00000000FFFFLL; gate_desc |= 0x00008E0000000000LL; gate_desc |= ( selector_cs << 16 ); newidt[ PGF_ID ] = gate_desc; // now activate the new Interrupt Descriptor Table load_IDTR( newidtr ); smp_call_function( load_IDTR, newidtr, 1, 1 ); // create the pseudo-file that lets users see page-fault counts create_proc_info_entry( modname, 0, NULL, my_get_info ); return 0; //SUCCESS }