无法从内核获取虚拟地址到用户空间并写入内存

时间:2021-03-04 23:23:17

标签: c linux linux-kernel

我想在我的驱动程序中分配页面并从用户空间写入这个内存。

我正在使用函数 get_user_pages_fast 来固定地址。

我从文档中了解到,流程应该是:

  1. 在用户空间分配内存。
  2. 将内存地址发送到内核(假设使用 ioctl)。
  3. 驱动程序中的 get_user_pages_fast 函数将固定此内存。
  4. 调用 page_address 以获取虚拟地址以从用户空间写入数据。

问题是当我想从用户空间写入虚拟地址时,出现了段错误。

        struct page_info_st page_info;
        struct page** page_list = {NULL};
        unsigned long lock_limit;
        unsigned long new_pinned;
        struct mm_struct *mm;
        unsigned int gup_flags = FOLL_WRITE;
        int page_counter;
        int pinned = 0;
        int ret;

        // Copy the page info structure from the user space.
        if (copy_from_user(&page_info, udata, sizeof(struct page_info_st))) {
            ret = -EFAULT;
            goto fin;
        }

        /*
        * If the combination of the addr and size requested for this memory
        * region causes an integer overflow, return error.
        */
        if (((page_info.page_pointer_start + page_info.page_size) < page_info.page_pointer_start) ||
                PAGE_ALIGN(page_info.page_pointer_start + page_info.page_size) < (page_info.page_pointer_start + page_info.page_size) ||
                page_info.page_amount <= 0)
        {
            ret = -EINVAL;
            goto fin;
        }

        if (!can_do_mlock())
        {
            ret = -EPERM;
            goto fin;
        }

        mm = current->mm;
        mmgrab(mm);

        page_list = kcalloc(page_info.page_amount, sizeof(struct page *), GFP_KERNEL);
        if (!page_list)
        {
            ret = -ENOMEM;
            goto fin;
        }

        lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
        new_pinned = atomic64_add_return(page_info.page_amount, &mm->pinned_vm);
        if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {
            atomic64_sub(page_info.page_amount, &mm->pinned_vm);
            ret = -ENOMEM;
            goto fin;
        }
            
        while (pinned < page_info.page_amount)
        {
            int num_pages = page_info.page_amount - pinned;

            uint64_t current_ptr = page_info.page_pointer_start + (pinned * PAGE_SIZE);

            struct page** current_pages = page_list + pinned;
            
            ret = get_user_pages_fast(current_ptr, num_pages,
                                      gup_flags, current_pages);
            if (ret < 0)
            {
                unpin_user_pages(page_list, pinned);
                kfree(page_list);
                ret = -EFAULT;
                goto fin;
            }

            pinned += ret;
        }
 
        if (ret != page_info.page_amount)
        {
            ret = -EFAULT;
            goto fin;
        }

        // Initialize the pages.
        for (page_counter = 0; 
                page_counter < page_info.page_amount;
                page_counter++)
        {
            // Save the physical and virtual address for the user space purpose.
            page_info.page_address_array[page_counter].physical_address = 
                page_to_phys(page_list[page_counter]);

            page_info.page_address_array[page_counter].virtual_address = 
                *(uint64_t*)page_address(page_list[page_counter]);
        }

        ret = copy_to_user(udata, &page_info, sizeof(struct page_info_st));        
        
        break;

用户空间代码:

int allocate_kernel_memory_page(file* file_t, struct page_info* page_info,
                                int page_amount)
{
    int return_value = 0;
    int page_size = getpagesize();


    if(!file_t|| !page_info)
    {
        return_value = -1;
    }

    page_info->page_amount = page_amount;
    
    // Set the requested page size.
    page_info->page_size = page_amount * page_size;

    // Allocate user buffer.
    file_t->pin_pages = memalign(page_size, page_info->page_size);

    // Save the start buffer pointer as an integer.
    page_info->page_pointer_start = (unsigned long)file_t->pin_pages;

    // Pin the memory in the kernel space.
    return_value = ioctl(file_t->fd, PCICONF_PAGE_ALLOCATION, page_info);

    **HERE IS THE SEG FAULT**

    u_int32_t* data_ptr = (u_int32_t*)page_info->page_address_array.virtual_address;
    for (int i = 0; i < accessData->size / 4; i++) {
        **data_ptr = ___my_swab32(data[(currentOffset) / 4 + i]);*
        data_ptr++;
    }

    return return_value;
}

0 个答案:

没有答案