我想在我的驱动程序中分配页面并从用户空间写入这个内存。
我正在使用函数 get_user_pages_fast 来固定地址。
我从文档中了解到,流程应该是:
问题是当我想从用户空间写入虚拟地址时,出现了段错误。
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;
}