整体思路:找到想要设成uncacheable的内存地址,然后通过set_memory_uc()
这个函数把这个地址所在的page设成uncacheable; 这些操作在一个内核模块中完成。
下面通过两个例子进行说明,一个例子是把GDT和IDT所在的page设成uncacheable; 另一个例子是把apic_timer_interrupt()中段函数所在的page设成uncacheable。
例子1——把GDT和IDT所在的page设成uncacheable:
-
新建一个文件夹
setPageUncacheable
, 然后建两个文件setPageUncacheable.c
和Makefile
:mkdir setPageUncacheable cd setPageUncacheable touch setPageUncacheable.c touch Makefile
-
在
setPageUncacheable.c
里面写入以下内容:#include <linux/init.h> #include <asm/apic.h> #include <asm/set_memory.h> #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> struct desc_ptr1{ unsigned short size; unsigned long address; } __attribute__((packed)); static inline void store_gdt(struct desc_ptr1 *ptr){ asm("sidt %w0":"=m" (*ptr)); //sgdt } static int __init test_init(void) { printk("-------- test start ---------"); //get idtr adress unsigned long addr; int pages,retval; struct desc_ptr1 idt_descr1; store_gdt(&idt_descr1); printk("idtr address:%#lx size:%d\n",idt_descr1.address,idt_descr1.size); //set idt page as uncacheable addr = idt_descr1.address; // addr = (unsigned long)0xffffffff9c602800; //ffffffff9c602800 pages = 1; retval = set_memory_uc(addr, pages); if (retval) { printk("set_memory_uc failed.Error\n"); return retval; } printk("set memory_uc success\n"); printk("---------------------------"); return 0; } static void __exit test_exit(void) { printk("-------- test over ----------"); return; } MODULE_LICENSE("GPL"); module_init(test_init); module_exit(test_exit);
(所有代码的功能大致分成两个部分,第一部分是从
idtr
寄存器取出地址,第二部分是通过set_memory_uc()
设置相应page) -
在
Makefile
里面写入以下内容:obj-m += setPageUncacheable.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
-
在刚刚的目录下,通过make生成内核模块, 并加载该内核模块:
cd setPageUncacheable make sudo insmod ./setPageUncacheable.ko
-
加载完后可通过查看
/var/log/syslog
确认是否加载成功:tail -f /var/log/syslog
成功的话,会看到类似下面的输出:
-
成功设置后,卸载模块:
sudo rmmod setPageUncacheable
-
刚刚的操作只是把idt所在的page设成了uncacheable, 如果要进一步设置gdt所在的table, 需先把
setPageUncacheable.c
里面的sidt
改成sgdt
,然后再重复步骤4-6就好了。
例子2——把apic_timer_interrupt()中段函数所在的page设成uncacheable
- 函数
apic_timer_interrupt()
的地址可以通过查看文件/proc/kallsyms
知道,查看该文件一定要用root权限,不然看到的地址都是0x0:
打开文件后找到sudo vim /proc/kallsyms
apic_timer_interrupt()
对应的地址,比如下图中apic_timer_interrupt()
对应的地址就是0xffffffffb4001d20
- 把找到的地址赋值给
setPageUncacheable.c
里面pages = 1;
上一行的addr
字段。 然后重复例子1中的步骤4-6。 - 完。
进一步验证
进一步验证是否设置成功,可通过加载内核模块dump_pagetables.ko,然后在sysfs里面查看所有page对应的属性flag,U对应uncacheable。