- 论坛徽章:
- 0
|
本帖最后由 Godbach 于 2011-08-12 11:37 编辑
请教大家一个内核方面的问题:
内核在拷贝用户空间的数据时,使用copy_from_user函数,可以将数据正确的拷贝到内核空间。
我理解该函数的目的为:一旦在拷贝数据缺页时,可以处理缺页异常,然后正确的找到数据地址,然后将它拷贝到内核
我想请教的问题是,在发生缺页异常之前用户空间的数据是存在哪里的,缺页异常程序如何正确的找到该数据或者说数据的物理地址?
我做了一个实验:
1.用户空间定义了一个字符数据为初始化数据:
#define DEVICE "/dev/VM-TEST"
#define VM_TEST_GET_VADDR 0
char str[20]= "123456677777";
int main(int argc, char *argv[])
{
int fd = 0;
char chr = 0;
char str1[30];
fd = open(DEVICE, O_RDWR);
if (fd < 0) {
printf("Can't open the %s\n", DEVICE);
return -1;
}
printf("vaddress: 0x%08x str: %s\n", (unsigned long)str, str);
ioctl(fd, VM_TEST_GET_VADDR, str);
while (1) {
chr = getchar();
if (chr == 'q') {
printf("Byte!!\n");
return 0;
}
}
return 0;
}
2.驱动代码:
使用页表直接查找虚拟地址对应的物理地址时,不能找到物理地址,但在调用copy_from_user之后可以找到物理地址
static int
vm_test_ioctl( struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg )
{
void __user *argp = (void __user *)arg;
unsigned long vaddr;
unsigned long paddr;
unsigned char str[20] = {0};
switch (cmd)
{
case VM_TEST_GET_VADDR:
printk("VM_TEST_GET_VADDR: 0x%08x\n", (unsigned long)argp);
/* output data from user space */
/* copy_from_user(str, argp, 18); //不调用该函数uva_to_pa不能找到物理地址
printk("str:%s\n", str); */
//printk("str:%s\n", (unsigned char *)argp);
/* Get physical address */
struct mm_struct *mm = current->mm;
paddr = uva_to_pa(mm, vaddr);
if (paddr == 0)
printk("Can't find physical address!\n");
else
printk("paddr: 0x%08x\n", paddr);
printk("\n=======Data========\n");
unsigned long vpp = ioremap_nocache(paddr, 18);
int i = 0;
for (i = 0; i < 18; i++) {
printk("%c", *(volatile unsigned char *)(vpp + i));
}
printk("\n=======Data=======\n");
return 0;
default:
return -EINVAL;
}
}
/* 使用进程的虚拟地址查找物理地址 */
static unsigned long uva_to_pa(struct mm_struct *mm, unsigned long addr)
{
unsigned long ret = 0UL;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
pgd = pgd_offset(mm, addr);
if (!pgd_none(*pgd)) {
pud = pud_offset(pgd, addr);
if (!pud_none(*pud)) {
pmd = pmd_offset(pud, addr);
if (!pmd_none(*pmd)) {
pte = pte_offset_map(pmd, addr);
if (!pte_none(*pte) && pte_present(*pte)) {
/* Use hard PTE */
pte = (pte_t *)((u32)pte - 2048);
if(pte)
ret = (*pte & 0xfffff000) | (addr & 0xfff);
} else {
printk("pet is not present:0x%08x\n", (*pte & 0xfffff000) | (addr & 0xfff));
}
}
}
}
return ret;
} |
|