2011-06-21 60 views
2

通過vmap對映射內核模塊的內存有任何約束嗎?在我的系統上,我編寫了一個簡單的KMOD,它映射內核函數(printk)和模塊函數(printx),並檢查映射是否相等。結果告訴我映射模塊的printx有問題 - 映射和函數的代碼不相等。有人能解釋我做錯了什麼嗎?這裏是代碼:是否有可能vmap內核模塊的內存?

// VMAP-test.c的

#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/mm.h> 

int printx(void) 
{ 
    return 0; 
} 


void vmap_action(unsigned long address) 
{ 
    void * mapping; 
    struct page * page; 

    page = virt_to_page(address); 
    mapping = vmap(&page, 1, VM_MAP, PAGE_KERNEL); 
    if (mapping) { 
     int i = 0; 
     void * data = mapping + offset_in_page(address); 

     printk("VMAP: src %p -> dst %p\n", (void *)address, data); 
     for (i=0; i<16; i++) { 
      printk("%.02x %.02x\n", ((u8 *)address)[i], ((u8 *)data)[i]); 
     } 
     vunmap(mapping); 
    } 
} 

int my_module_init(void) 
{ 
    vmap_action((unsigned long)printk); 
    vmap_action((unsigned long)printx); 

    return 0; 
} 
module_init(my_module_init); 

void my_module_exit(void) 
{ 
} 
module_exit(my_module_exit); 

,並與dmesg結果:

VMAP(printk的)

[88107.398146] VMAP: src ffffffff813dfaef -> dst ffffc900117ddaef 
[88107.398148] 55 55 
[88107.398149] 48 48 
[88107.398150] 89 89 
[88107.398151] e5 e5 
[88107.398152] 48 48 
[88107.398153] 83 83 
[88107.398154] ec ec 
[88107.398155] 50 50 
[88107.398156] 0f 0f 
[88107.398156] 1f 1f 
[88107.398157] 44 44 
[88107.398158] 00 00 
[88107.398159] 00 00 
[88107.398160] 48 48 
[88107.398161] 8d 8d 
[88107.398162] 45 45 

VMAP(printx)

[88107.398164] VMAP: src ffffffffa009a010 -> dst ffffc900117fd010 
[88107.398166] 55 35 
[88107.398167] 48 fb 
[88107.398168] 89 53 
[88107.398169] e5 d5 
[88107.398170] 0f f7 
[88107.398171] 1f 97 
[88107.398171] 44 ee 
[88107.398172] 00 fd 
[88107.398173] 00 d5 
[88107.398174] 31 2d 
[88107.398175] c0 bf 
[88107.398176] 5d f6 
[88107.398177] c3 2d 
[88107.398178] 0f bd 
[88107.398179] 1f b7 
[88107.398180] 00 99 

歡迎任何建議:)謝謝。

+0

啊,我明白你現在問什麼......有趣。我不知道答案 :-)。我會嘗試打印'page'以及在從'vmap'獲得的地址上調用'virt_to_page'。如果頁面不同,接下來的問題是弄清楚這兩個頁面實際上是什麼...... – Nemo

+0

@Nemo:我找到了一個解決方案,請參閱我的答案。內核似乎以不同於自己的方式管理模塊內存。據我所知,模塊內存是從vmalloc區域分配的,這可能是不同的原因。 –

回答

0

嗯,我發現,類似的功能在KSplice項目實施,這裏是:

/* 
* map_writable creates a shadow page mapping of the range 
* [addr, addr + len) so that we can write to code mapped read-only. 
* 
* It is similar to a generalized version of x86's text_poke. But 
* because one cannot use vmalloc/vfree() inside stop_machine, we use 
* map_writable to map the pages before stop_machine, then use the 
* mapping inside stop_machine, and unmap the pages afterwards. 
*/ 
static void *map_writable(void *addr, size_t len) 
{ 
    void *vaddr; 
    int nr_pages = DIV_ROUND_UP(offset_in_page(addr) + len, PAGE_SIZE); 
    struct page **pages = kmalloc(nr_pages * sizeof(*pages), GFP_KERNEL); 
    void *page_addr = (void *)((unsigned long)addr & PAGE_MASK); 
    int i; 

    if (pages == NULL) 
     return NULL; 

    for (i = 0; i < nr_pages; i++) { 
     if (__module_address((unsigned long)page_addr) == NULL) { 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) || !defined(CONFIG_X86_64) 
      pages[i] = virt_to_page(page_addr); 
#else /* LINUX_VERSION_CODE < && CONFIG_X86_64 */ 
/* e3ebadd95cb621e2c7436f3d3646447ac9d5c16d was after 2.6.21 
* This works around a broken virt_to_page() from the RHEL 5 backport 
* of x86-64 relocatable kernel support. 
*/ 
      pages[i] = 
       pfn_to_page(__pa_symbol(page_addr) >> PAGE_SHIFT); 
#endif /* LINUX_VERSION_CODE || !CONFIG_X86_64 */ 
      WARN_ON(!PageReserved(pages[i])); 
     } else { 
      pages[i] = vmalloc_to_page(addr); 
     } 
     if (pages[i] == NULL) { 
      kfree(pages); 
      return NULL; 
     } 
     page_addr += PAGE_SIZE; 
    } 
    vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL); 
    kfree(pages); 
    if (vaddr == NULL) 
     return NULL; 
    return vaddr + offset_in_page(addr); 
} 

所以,當談到有一個不同的處理內核和模塊的內存。如果頁面不屬於任何模塊,則使用vmalloc_to_page而不是virt_to_phys。我會檢查它是否有幫助,並在稍後發佈結果。

+0

嗯,它的工作原理。但是'vmalloc_to_page(addr)'似乎在這個函數中存在一個錯誤。可能必須有'vmalloc_to_page(page_addr)'。 –