2010-03-23 20 views
5

Estoy utilizando la versión del kernel de Linux 2.6.26 y estoy tratando de cambiar la tabla de descriptores de interrupción utilizando un módulo kernel. Solo estoy intentando cambiar la entrada de la tabla de fallas de página aquí. Así que hago una copia del IDT original y realizo cambios en la entrada de la tabla de fallas de la página solamente. El objetivo del ISR es imprimir información del error de página antes de llamar al controlador de error de página original. Pero el kernel simplemente falla 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.Modificación del descriptor de interrupción Tabla

He pegado el código de abajo

#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); 
+0

Ver también http://stackoverflow.com/questions/5302392 - y la respuesta de amrzar a continuación. El kernel ya proporciona funciones de utilidad para reemplazar entradas específicas en las tablas de descriptores y/o en las tablas completas. El pirateo de bajo nivel no suele ser una buena idea. –

+0

¿por qué cuando uso printk en mi función C, el sistema genera seg fault? – Jianchen

Respuesta

1

Su selector de segmento en su descriptor trampa puerta parece estar codificada para 0x0010, cuando debería ser __KERNEL_CS (que es 0x0060 en las fuentes del kernel 2.6.26 que tengo)

Por cierto, esto es bastante barroca:

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 

Se podría simplificar hasta que (con el arreglo __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; 
+0

Tiene razón al respecto ... Gracias ... Pero luego el código se cuelga dentro de la función my_intrept. No estoy seguro de cómo seguir una pregunta aquí ... –

+0

Solo publique una nueva pregunta. – caf

+0

¿por qué cuando uso printk en mi función C, el sistema genera seg fault? – Jianchen

5

¿Por qué no utilizar la función de núcleo en lugar de toqueteando bits manualmente comprobarlo (que es el módulo de inicialización 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; 

He probado que funciona!

Cuestiones relacionadas