The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
my_page_fault.c
1 #include <linux/module.h>
2 #include <linux/kernel.h>
3 #include <linux/errno.h>
4 #include <linux/types.h>
5 #include <linux/mm.h>
6 #include <asm/uaccess.h>
7 //#include <asm/traps.h>
8 #include "traps-dummy.h"
9 #include <asm/desc_defs.h>
10 #include <linux/sched.h>
11 #include <linux/moduleparam.h>
12 
13 #define X86_TRAP_PF 14
14 //PGFAULT_NR is the interrupt number of page fault. It is platform specific.
15 #if defined(CONFIG_X86_64)
16 #define PGFAULT_NR X86_TRAP_PF
17 #else
18 #error This module is only for X86_64 kernel
19 #endif
20 
21 static unsigned long new_idt_table_page;
22 static struct desc_ptr default_idtr;
23 
24 //addresses of some symbols
25 static unsigned long addr_dft_page_fault = 0UL; //address of default 'page_fault'
26 static unsigned long addr_dft_do_page_fault = 0UL; //address of default 'do_page_fault'
27 static unsigned long addr_pv_irq_ops = 0UL; //address of 'pv_irq_ops'
28 static unsigned long addr_adjust_exception_frame; //content of pv_irq_ops.adjust_exception_frame, it's a function
29 static unsigned long addr_error_entry = 0UL;
30 static unsigned long addr_error_exit = 0UL;
31 
32 module_param(addr_dft_page_fault, ulong, S_IRUGO);
33 module_param(addr_dft_do_page_fault, ulong, S_IRUGO);
34 module_param(addr_pv_irq_ops, ulong, S_IRUGO);
35 module_param(addr_error_entry, ulong, S_IRUGO);
36 module_param(addr_error_exit, ulong, S_IRUGO);
37 
38 #define CHECK_PARAM(x) do{\
39  if(!x){\
40  printk(KERN_INFO "my_virt_drv: Error: need to set '%s'\n", #x);\
41  is_any_unset = 1;\
42  }\
43  printk(KERN_INFO "my_virt_drv: %s=0x%lx\n", #x, x);\
44 }while(0)
45 
46 typedef void (*do_page_fault_t)(struct pt_regs*, unsigned long);
47 
48 extern void root_sim_page_fault(struct pt_regs* regs, long error_code, do_page_fault_t kernel_handler);
49 
50 static int check_parameters(void){
51  int is_any_unset = 0;
52  CHECK_PARAM(addr_dft_page_fault);
53  CHECK_PARAM(addr_dft_do_page_fault);
54  CHECK_PARAM(addr_pv_irq_ops);
55  CHECK_PARAM(addr_error_entry);
56  CHECK_PARAM(addr_error_exit);
57  return is_any_unset;
58 }
59 
60 void my_do_page_fault(struct pt_regs* regs, unsigned long error_code){
61  // We call the ROOT-Sim page fault handler. Warning: if we have to call the
62  // original kernel handler, this must be done prior to returning!!!!
63  root_sim_page_fault(regs, error_code, (do_page_fault_t)addr_dft_do_page_fault);
64 }
65 
66 asmlinkage void my_page_fault(void);
67 asm(" .text");
68 asm(" .type my_page_fault,@function");
69 asm("my_page_fault:");
70 asm(" .byte 0x66");
71 asm(" xchg %ax, %ax");
72 asm(" callq *addr_adjust_exception_frame");
73 asm(" sub $0x78, %rsp");
74 asm(" callq *addr_error_entry");
75 asm(" mov %rsp, %rdi");
76 asm(" mov 0x78(%rsp), %rsi");
77 asm(" movq $0xffffffffffffffff, 0x78(%rsp)");
78 asm(" callq my_do_page_fault");
79 asm(" jmpq *addr_error_exit");
80 asm(" nopl (%rax)");
81 
82 //this function is copied from kernel source
83 static inline void pack_gate(gate_desc *gate, unsigned type, unsigned long func,
84  unsigned dpl, unsigned ist, unsigned seg){
85  gate->offset_low = PTR_LOW(func);
86  gate->segment = __KERNEL_CS;
87  gate->ist = ist;
88  gate->p = 1;
89  gate->dpl = dpl;
90  gate->zero0 = 0;
91  gate->zero1 = 0;
92  gate->type = type;
93  gate->offset_middle = PTR_MIDDLE(func);
94  gate->offset_high = PTR_HIGH(func);
95 }
96 
97 static void my_load_idt(void *info){
98  struct desc_ptr *idtr_ptr = (struct desc_ptr *)info;
99  load_idt(idtr_ptr);
100 }
101 
102 static int my_fault_init(void){
103  //check all the module_parameters are set properly
104  if(check_parameters())
105  return -1;
106  //get the address of 'adjust_exception_frame' from pv_irq_ops struct
107  addr_adjust_exception_frame = *(unsigned long *)(addr_pv_irq_ops + 0x30);
108  return 0;
109 }
110 
111 int register_my_page_fault_handler(void){
112  struct desc_ptr idtr;
113  gate_desc *old_idt, *new_idt;
114  int retval;
115 
116  //first, do some initialization work.
117  retval = my_fault_init();
118  if(retval)
119  return retval;
120 
121  //record the default idtr
122  store_idt(&default_idtr);
123 
124  //read the content of idtr register and get the address of old IDT table
125  old_idt = (gate_desc *)default_idtr.address; //'default_idtr' is initialized in 'my_virt_drv_init'
126 
127  //allocate a page to store the new IDT table
128  printk(KERN_INFO "my_virt_drv: alloc a page to store new idt table.\n");
129  new_idt_table_page = __get_free_page(GFP_KERNEL);
130  if(!new_idt_table_page)
131  return -ENOMEM;
132 
133  idtr.address = new_idt_table_page;
134  idtr.size = default_idtr.size;
135 
136  //copy the old idt table to the new one
137  new_idt = (gate_desc *)idtr.address;
138  memcpy(new_idt, old_idt, idtr.size);
139  pack_gate(&new_idt[PGFAULT_NR], GATE_INTERRUPT, (unsigned long)my_page_fault, 0, 0, __KERNEL_CS);
140 
141  //load idt for all the processors
142  printk(KERN_INFO "my_virt_drv: load the new idt table.\n");
143  load_idt(&idtr);
144  printk(KERN_INFO "my_virt_drv: new idt table loaded.\n");
145  smp_call_function(my_load_idt, (void *)&idtr, 1); //wait till all are finished
146  printk(KERN_INFO "my_virt_drv: all CPUs have loaded the new idt table.\n");
147  return 0;
148 }
149 
150 void unregister_my_page_fault_handler(void){
151  struct desc_ptr idtr;
152  store_idt(&idtr);
153  //if the current idt is not the default one, restore the default one
154  if(idtr.address != default_idtr.address || idtr.size != default_idtr.size){
155  load_idt(&default_idtr);
156  smp_call_function(my_load_idt, (void *)&default_idtr, 1);
157  free_page(new_idt_table_page);
158  }
159 }
160 
161 MODULE_LICENSE("Dual BSD/GPL");