Domanda

Sto usando Linux 2.6.26 versione del kernel e sto cercando di modificare la tabella descrittore di interrupt utilizzando un modulo del kernel. Sto solo cercando di cambiare la voce della tabella errore di pagina qui. Così faccio una copia dell'originale IDT e modificare solo la voce della tabella errore di pagina. L'obiettivo della ISR è quello di stampare le informazioni dell'errore di pagina prima di chiamare il gestore degli errori pagina originale. Ma il kernel si blocca solo una volta che carico con insmod cioè specificamente si è schiantato con la funzione "loadIDTR". Con un ulteriore debug, ho scoperto che, non cambiando una voce se carico l'IDTR funziona benissimo. Io sono a corto di idee.

Ho incollato il codice seguente

#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);
È stato utile?

Soluzione

Il selettore di segmento nel descrittore cancello trappola sembra essere hardcoded a 0x0010, quando dovrebbe essere __KERNEL_CS (che è 0x0060 nei 2.6.26 sorgenti del kernel che ho).

A proposito, questo è piuttosto barocco:

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

Si potrebbe semplificare che fino a (con la correzione __KERNEL_CS):

gate_desc = (unsigned long long)isr0x0E * 0x100000001ULL;
gate_desc &= 0xFFFF00000000FFFFULL;
gate_desc |= 0x0000EF0000000000ULL; // trap-gate
gate_desc |= (unsigned long long)__KERNEL_CS << 16;

Altri suggerimenti

Perché non si utilizza la funzione del kernel, invece di giocherellare con i bit manualmente! controllare (è modulo inizializzazione func):

struct desc_ptr newidtr;
gate_desc *oldidt, *newidt;

store_idt(&__IDT_register);
oldidt = (gate_desc *)__IDT_register.address;

__IDT_page =__get_free_page(GFP_KERNEL);
if(!__IDT_page)
    return -1;

newidtr.address = __IDT_page;
newidtr.size = __IDT_register.size;
newidt = (gate_desc *)newidtr.address;

memcpy(newidt, oldidt, __IDT_register.size);

pack_gate(&newidt[PGFAULT_NR], GATE_INTERRUPT, (unsigned long)isr0x0E, 0, 0, __KERNEL_CS);

__load_idt((void *)&newidtr);
smp_call_function(__load_idt, &newidtr, 0, 1);

return 0;

L'ho testato funziona!

Per riferimento, ecco un'implementazione di lavoro di un gestore di errore di pagina personalizzata per l'architettura x86_64 Linux. Ho appena provato questo modulo me stesso con il kernel 3.2, funziona perfettamente.

https://github.com/RichardUSTC/intercept-page-fault-handler

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top