Cambio de la tabla del descriptor de interrupción

Estoy usando la versión del kernel de Linux 2.6.26 y estoy tratando de cambiar la tabla del descriptor de interrupciones usando un módulo del kernel. Solo estoy tratando de cambiar la entrada de la tabla de fallas de página aquí. Así que hago una copia del IDT original y hago cambios solo en la entrada de la tabla de fallas de la página. El objetivo del ISR es imprimir información de la falla de la página antes de llamar al manejador de fallas de la página original. Pero el núcleo simplemente se bloquea una vez que lo cargo con insmod, es decir, se bloquea específicamente con la función "loadIDTR". Con una mayor depuración, descubrí que al no cambiar ninguna entrada si cargo el IDTR, funciona bien. Se me acabaron las ideas.

He pegado el siguiente código

#include <linux/module.h>   // for init_module() 
#include <linux/init.h>
#include <linux/mm.h>       // for get_free_page()
#include <linux/sched.h>
#include <linux/spinlock.h>

#define SUCCESS 0
#define PGFAULT_INT 0x0E

static char modname[] = "pgfaults";
static unsigned short oldidtr[3], newidtr[3];
static unsigned long long *oldidt, *newidt;
static unsigned long isr_orig, kpage;
static char *why[]={ "sra", "srp", "swa", "swp", "ura", "urp", "uwa", "uwp" };

unsigned long long gate_desc_orig,gate_desc_orig1;

static void my_intrept( unsigned long *tos )
{
    // stack-layout:
    // es,ds,edi,esi,ebp,esp,ebx,edx,ecx,eax,err,eip,cs,efl
    //  0  1   2   3   4   5   6   7   8   9  10  11 12  13  
    volatile unsigned long  vaddr;
    struct task_struct *task = current;
    unsigned long   err = tos[ 10 ];    
    unsigned long   eip = tos[ 11 ];
    static int  count = 0;
    int     exe, len = 0;
    char        msg[80]="";

    // get the faulting virtual address from register CR2
    asm(" mov %%cr2, %%eax ; movl %%eax, %0 " : "=m" (vaddr) ); 

    // construct the diagnostic message
    len += sprintf( msg+len, "#%-6d ", ++count );
    len += sprintf( msg+len, "%16s  ", task->comm );
    len += sprintf( msg+len, "pid=%-5d  ", task->pid );
    len += sprintf( msg+len, "CR2=%08X  ", (unsigned int) vaddr );
    len += sprintf( msg+len, "EIP=%08X  ", (unsigned int) eip );
    len += sprintf( msg+len, "%s ", why[ err ] );
    // note if an instruction-fetch caused the page-fault
    if ( vaddr == eip ) exe = 'x'; else exe = ' ';
    len += sprintf( msg+len, "%c ", exe );
    // print this diagnostic message to the kernel log
    printk( "<1>  %s \n", msg );
}




//----------  NEW PAGE-FAULT EXCEPTION-HANDLER  ---------//
asmlinkage void isr0x0E( void );
asm("   .text                       ");
asm("   .type   isr0x0E, @function          ");
asm("isr0x0E:                       ");
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    my_intrept              ");
asm("   addl    $4, %esp                ");
//
asm("   popl    %es                 "); 
asm("   popl    %ds                 ");
asm("   popal                       ");
asm("   jmp *isr_orig               ");
//-------------------------------------------------------//



static void load_IDTR( void *regimage )
{
    asm(" lidt %0 " : : "m" (*(unsigned short*)regimage) );
}



int pgfault_init( void )
{
    int         i;
    unsigned long long gate_desc,gate_desc1,gate_desc2;

    spinlock_t lock =SPIN_LOCK_UNLOCKED;
    unsigned long flags;
    unsigned short selector1;

    // allocate a mapped kernel page for our new IDT
    kpage =__get_free_page( GFP_KERNEL);
    if ( !kpage ) return -ENOMEM;


    // initialize our other global variables

    asm(" sidt oldidtr ; sidt newidtr ");

    memcpy( newidtr+1, &kpage, sizeof( kpage ) );
    oldidt = (unsigned long long *)(*(unsigned long*)(oldidtr+1));
    newidt = (unsigned long long *)(*(unsigned long*)(newidtr+1));

    // extract and save entry-point to original page-pault handler
    gate_desc_orig = oldidt[ PGFAULT_INT ];
    gate_desc =gate_desc_orig & 0xFFFF00000000FFFF;

    gate_desc |= ( gate_desc >> 32 );
    isr_orig = (unsigned long)gate_desc;
    // initialize our new Interrupt Descriptor Table
    memcpy( newidt, oldidt, 256*sizeof( unsigned long long ) );

    gate_desc_orig1 = (unsigned long)isr0x0E;
    gate_desc = gate_desc_orig1 & 0x00000000FFFFFFFF;

    gate_desc = gate_desc | ( gate_desc << 32 );
    gate_desc1= 0xFFFF0000;
    gate_desc1= gate_desc1 << 32;
    gate_desc1= gate_desc1 | 0x0000FFFF;
    gate_desc = gate_desc & gate_desc1;
    gate_desc2= 0x0000EF00;
    gate_desc2= gate_desc2 <<32;
    gate_desc2= gate_desc2 | 0x00100000;
    gate_desc = gate_desc | gate_desc2; // trap-gate
    //Part which is most likely creating a fault when loading the idtr
    newidt[ PGFAULT_INT ] = gate_desc;
    //**********************************************
    // activate the new IDT

    spin_lock_irqsave(&lock,flags);
    load_IDTR( newidtr );
    spin_unlock_irqrestore(&lock,flags);

//  smp_call_function( load_IDTR, oldidtr, 1, 1 );
    return  SUCCESS;
}



void pgfault_exit( void )
{

    // reactivate the old IDT
    unsigned long flags;
    spinlock_t lock =SPIN_LOCK_UNLOCKED;
    spin_lock_irqsave(&lock,flags);
    load_IDTR( oldidtr );
    spin_unlock_irqrestore(&lock,flags);
//  smp_call_function( load_IDTR, oldidtr, 1, 1 );

    // release allocated kernel page 
    if ( kpage ) free_page( kpage );
}
EXPORT_SYMBOL_GPL(my_intrept);
MODULE_LICENSE("GPL"); 
module_init( pgfault_init);
module_exit( pgfault_exit);

Respuestas a la pregunta(3)

Su respuesta a la pregunta