1-使用MTRR:事实证明这是不可行的,因为MTRR允许对物理地址范围禁用缓存。我使用的是64位Tinycore Linux,这意味着内核空间可以映射到整个RAM(与32位系统的空间映射到低物理内存不同)。
我试图通过在相应的页表项中设置一个标志,手动将用户空间进程的某个内存区域标记为不可缓存(出于教育目的,不打算在生产代码中使用)。>
我有一个在x86_64 Intel Skylake处理器上运行的具有4.4 Linux内核的Ubuntu 14.04(已禁用ASLR)。>>
在我的内核模块中,我具有以下功能:
/* * Set memory region [start,end], excluding 'addr', of process with PID 'pid' as uncacheable. */ ssize_t set_uncachable(uint32_t pid, uint64_t start, uint64_t end, uint64_t addr) { struct task_struct* ts = NULL; struct vm_area_struct *curr, *first = NULL; struct mm_struct* mm; pgd_t * pgd; pte_t * pte; uint64_t numpages, curr_addr; uint32_t level, j, i = 0; printk(KERN_INFO "set_unacheable called\n"); ts = pid_task(find_vpid(pid), PIDTYPE_PID); //find task from PID pgd = ts->mm->pgd; //page table root of the task first = ts->mm->mmap; curr = first; if(first == NULL) return -1; do { printk(KERN_INFO "Region %3u [0x%016llx - 0x%016llx]", i, curr->vm_start, curr->vm_end); numpages = (curr->vm_end - curr->vm_start) / PAGE_SIZE; //PAGE_SIZE is 4K for now if(curr->vm_start > curr->vm_end) numpages = 0; for(j = 0; j < numpages; j++) { curr_addr = curr->vm_start + (PAGE_SIZE*j); pte = lookup_address_in_pgd(pgd, curr_addr, &level); if((pte != NULL) && (level == 1)) { printk(KERN_INFO "PTE for 0x%016x - 0x%016x (level %u)\n", curr_addr, pte->pte, level); if(curr_addr >= start && curr_addr < end && curr_addr != addr) { //setting page entry to PAT#3 pte->pte |= PWT_BIT | PCD_BIT; pte->pte &= ~PAT_BIT; printk(KERN_INFO "PTE for 0x%016x - 0x%016x (level %u) -- UPDATED\n", curr_addr, pte->pte, level); } } } curr = curr->vm_next; if(curr == NULL) return -1; i++; } while (curr != first); return 0; }
为了测试以上代码,我运行了一个在内存中分配特定区域的应用程序:
//#define BUF_ADDR_START 0x0000000008400000LL /* works */ #define BUF_ADDR_START 0x00007ffff0000000LL /* does not work */ [...] buffer = mmap((void *)BUF_ADDR, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_POPULATE, 0, 0); if ( buffer == MAP_FAILED ) { printf("Failed to map buffer\n"); exit(-1); } memset(buffer, 0, BUF_SIZE); printf("Buffer at %p\n", buffer);
我想使用内核模块将缓冲区标记为不可缓存。我的内核模块中的代码适用于0x8400000,但适用于0x7ffff0000000,找不到页面表条目(即lookup_address_in_pgd返回NULL)。不过,缓冲区肯定是在测试程序中分配的。
似乎我的内核模块适用于低地址(代码,数据和堆节),但不适用于映射到较高地址(堆栈,共享库等)的内存。
有人知道为什么较大的地址会失败吗?也欢迎提出关于如何更优雅地实现set_uncachable的建议;-)
谢谢!
我试图通过在相应的...中设置一个标志,手动将用户空间进程的某个内存区域标记为不可缓存(出于教育目的,不打算在生产代码中使用)。]
1-使用MTRR:事实证明这是不可行的,因为MTRR允许对物理地址范围禁用缓存。我使用的是64位Tinycore Linux,这意味着内核空间可以映射到整个RAM(与32位系统的空间映射到低物理内存不同)。
2-使用PAT:这似乎是更好的选择,因为它适用于虚拟页面。但是,我不确定是否应该使用IA32_PAT寄存器以及如何使用?或修改内核页面的页面表条目,以及如何?
您是否修复过此代码?是否可以将整个内核空间设置为不可缓存?您可以提供完整的代码吗?
1-使用MTRR:事实证明这是不可行的,因为MTRR允许对物理地址范围禁用缓存。我使用的是64位Tinycore Linux,这意味着内核空间可以映射到整个RAM(与32位系统的空间映射到低物理内存不同)。