是否可以映射内核模块内存?

时间:2011-06-21 15:01:04

标签: memory-management linux-kernel

通过vmap对映射内核模块的内存有任何约束吗?在我的系统上,我编写了一个简单的KMOD,它映射了一个内核函数(printk)和一个模块函数(printx),并检查映射是否等于。结果显示映射模块的printx存在问题 - 映射和函数的代码不相等。有人可以解释一下我做错了什么吗?这是代码:

// 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

欢迎任何建议:)谢谢。

1 个答案:

答案 0 :(得分: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。我会检查它是否有帮助并稍后发布结果。