请问驱动程序里怎么得到用户空间地址对应的物理地址?

hillhero789 2004-08-22 01:23:50
read 函数的原型如下:
ssize_t read (struct file *filp, char *buf, size_t count, loff_t *f_pos);

其中buf是一个用户空间的指针,请问我怎么得到这指针的物理地址呢?


谢谢!
...全文
321 点赞 收藏 13
写回复
13 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
constantine 2004-08-28
xuexi
回复
lysliberty 2004-08-28
试试下面这段代码:
unsigned char * GetPhyAddr(struct vm_area_struct *vma, unsigned long vaddr)
{
pgd_t *pgdir; pmd_t *pmdir; pte_t *pte;
pgdir=pgd_offset(vma->vm_mm, vaddr);
if(pgd_none(*pgdir)||pgd_bad(*pgdir))
return -EINVAL;
pmdir=pmd_offset(pgdir,vaddr);
if(pmd_none(*pmdir)||pmd_bad(*pmdir))
return -EINVAL;
pte=pte_offset(pmdir, vaddr);
if(pte_present(*pte))
{
addr=(unsigned char)pte_page(*pte);
return addr;
}
return -EINVAL
}
回复
hillhero789 2004-08-28
请问struct vm_area_struct *vma这个参数我怎么传递进去那?
谢谢
回复
hillhero789 2004-08-28
感谢lysliberty(liberty), 我马上试试
成功了马上结贴给分
回复
hillhero789 2004-08-27
谢谢lysliberty的回答

请问能不能举个具体点的例子呢? (我刚开始学linux驱动编程, 很多东西都很陌生)
回复
lysliberty 2004-08-25
就是个地址映射的问题吧. 按照linux的内存管理机制,
用pgd_offset(), pmd_offset(), pte_offset()就可以实现这种转换吧.

回复
hillhero789 2004-08-24
help
回复
shyi 2004-08-23
在内核模块编程中想要使用封装好的系统调用,需要作相应的处理.因为系统调用是供用户空间使用的,它首先会检查自己的地址是否是用户空间,如果不是则出错.楼主的问题就在这里,怎么让系统调用不检查使用的空间是否为用户空间.我当初也遇到这个问题,你看看下面的文档,是我当初在论坛上与别人讨论后总结的,你看看就很清楚了:

   在内核模块编程中使用系统调用有他的特殊性,如I/O操作,OPEN,READ,WRITE等,它会核对缓冲区是否是0-3GB(用户空间),如果不是,则报错,所以应该显示地更改这种限制,如何更改大家可以参考(来自:http://www.linux.it/kerneldocs/ksys/ksys.html)
Before calling the function, however, a preparing step must be performed. Like any other function that transfers data to/from user space using a user-provided pointer, the system call checks whether the provided buffer is a valid address or not. During normal operation, an address that lies in the user address range (0-3GB for standard kernel configuration) is considered valid, and an address that lies in kernel address space (3GB-4GB) is not.

If the system call is invoked from kernel space, though, we must prevent the usual check to fail, because the virtual address of our destination buffer will be in kernel space, above the 3GB mark.

The field addr_limit in the task_struct structure is used to define the highest virtual address that is to be considered valid; the macros get_fs and set_fs can be used to read and write the value. The limit that must be used when invoking system calls from kernel space (in practice, the "no limit" case) is returned by the get_ds macro. See the box in this page for an explanation of the names and meanings of the macro calls.

For this reasong, kernel system calls, must be wrapped by the following code:

mm_segment_t fs;

fs = get_fs(); /* save previous value */
set_fs (get_ds()); /* use kernel limit */

/* system calls can be invoked */

set_fs(fs); /* restore before returning to user space */

There's no need to wrap each individual system call, and group of them can occur in a row. It's important, however, that the original ``fs'' is restored before returning to user space. Otherwise, the user program that executed this code will retain permission to overwrite kernel memory by passing bogus pointers to further read (or ioctl) system calls.

下面是我在LKM编程中使用open系统调用的一段源代码
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#define __KERNEL_SYSCALLS__
#ifdef MODVERSIONS
#include <linux/modversion.h>
#endif

#include <linux/smp_lock.h>
#include <linux/sched.h>

#include <linux/file.h>
#include <linux/unistd.h>
#include <asm/io.h>

#include <linux/kernel.h>
#include <asm/fcntl.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
#define END_KMEM set_fs(old_fs); }

int errno;
int init_module(void){
int err;
printk("<1>Hello,world\n");
BEGIN_KMEM;
err=open("/usr/include/asm/uaccess.h",O_RDONLY,0);
if(err>0)
printk("open file successful\n");
END_KMEM;
return 0;
}
void cleanup_module(void){
printk("<1>Goodbye cruel word\n");
}
在2.4.20和2.4.26上均能成功使用!
但是,这是针对I/O系统调用的特殊性提出的解决办法,而对于其他的系统调用,如socketcall等,还需要进一步研究它的实现机制(我目前作的内核模块最核心的问题就是通过LKM实现网络通信),有谁在LKM编程中使用过SOCKET API,并且能成功insmod,能不能说一下如何实现?
谢谢.
回复
hillhero789 2004-08-23
谢谢两位指教

我就是不知道virt_to_bus或者__pa能不能正确的得到用户空间传过来的指针对应的物理地址

其实我想得到它的物理地址的原因是为了可以使用dma实现把数据从内核空间传送到用户空间

不知道这样可行不?
回复
zxm927 2004-08-23
如果你的代码运行在内核态,那可以用virt_to_bus()<asm/io.h>或__pa()<page.h>
如果在用户态,我也不知道怎么办了.
驱动程序是在内核态的.所以可以用.
回复
沙沙的吹 2004-08-23
你得到物理地址有用吗?在用户空间无论如何都不能访问物理地址的吧
回复
hillhero789 2004-08-23
非常感谢shyi
通过看你的回复,我了解到原来在内核空间也可以使用为用户空间提供的函数调用

不过可能是我表达的不好,我的问题是怎么通过把一个用户空间的地址转换成一个真是的物理地址,那个read函数我只是用来举例的.

(不知道是不是我理解错了你的意思呢? 希望指教)
回复
hillhero789 2004-08-22
大家帮帮忙吧
回复
相关推荐
发帖
Linux_Kernel
创建于2007-08-27

4156

社区成员

Linux/Unix社区 内核源代码研究区
申请成为版主
帖子事件
创建了帖子
2004-08-22 01:23
社区公告
暂无公告