当前位置: 技术问答>linux和unix
请问驱动程序里怎么得到用户空间地址对应的物理地址?
来源: 互联网 发布时间:2015-07-20
本文导语: read 函数的原型如下: ssize_t read (struct file *filp, char *buf, size_t count, loff_t *f_pos); 其中buf是一个用户空间的指针,请问我怎么得到这指针的物理地址呢? 谢谢! | 试试下面这段代码: unsign...
read 函数的原型如下:
ssize_t read (struct file *filp, char *buf, size_t count, loff_t *f_pos);
其中buf是一个用户空间的指针,请问我怎么得到这指针的物理地址呢?
谢谢!
ssize_t read (struct file *filp, char *buf, size_t count, loff_t *f_pos);
其中buf是一个用户空间的指针,请问我怎么得到这指针的物理地址呢?
谢谢!
|
试试下面这段代码:
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
}
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
}
|
就是个地址映射的问题吧. 按照linux的内存管理机制,
用pgd_offset(), pmd_offset(), pte_offset()就可以实现这种转换吧.
用pgd_offset(), pmd_offset(), pte_offset()就可以实现这种转换吧.
|
你得到物理地址有用吗?在用户空间无论如何都不能访问物理地址的吧
|
如果你的代码运行在内核态,那可以用virt_to_bus()或__pa()
如果在用户态,我也不知道怎么办了.
驱动程序是在内核态的.所以可以用.
如果在用户态,我也不知道怎么办了.
驱动程序是在内核态的.所以可以用.
|
在内核模块编程中想要使用封装好的系统调用,需要作相应的处理.因为系统调用是供用户空间使用的,它首先会检查自己的地址是否是用户空间,如果不是则出错.楼主的问题就在这里,怎么让系统调用不检查使用的空间是否为用户空间.我当初也遇到这个问题,你看看下面的文档,是我当初在论坛上与别人讨论后总结的,你看看就很清楚了:
在内核模块编程中使用系统调用有他的特殊性,如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
#define __KERNEL_SYSCALLS__
#ifdef MODVERSIONS
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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("Hello,worldn");
BEGIN_KMEM;
err=open("/usr/include/asm/uaccess.h",O_RDONLY,0);
if(err>0)
printk("open file successfuln");
END_KMEM;
return 0;
}
void cleanup_module(void){
printk("Goodbye cruel wordn");
}
在2.4.20和2.4.26上均能成功使用!
但是,这是针对I/O系统调用的特殊性提出的解决办法,而对于其他的系统调用,如socketcall等,还需要进一步研究它的实现机制(我目前作的内核模块最核心的问题就是通过LKM实现网络通信),有谁在LKM编程中使用过SOCKET API,并且能成功insmod,能不能说一下如何实现?
谢谢.
在内核模块编程中使用系统调用有他的特殊性,如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
#define __KERNEL_SYSCALLS__
#ifdef MODVERSIONS
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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("Hello,worldn");
BEGIN_KMEM;
err=open("/usr/include/asm/uaccess.h",O_RDONLY,0);
if(err>0)
printk("open file successfuln");
END_KMEM;
return 0;
}
void cleanup_module(void){
printk("Goodbye cruel wordn");
}
在2.4.20和2.4.26上均能成功使用!
但是,这是针对I/O系统调用的特殊性提出的解决办法,而对于其他的系统调用,如socketcall等,还需要进一步研究它的实现机制(我目前作的内核模块最核心的问题就是通过LKM实现网络通信),有谁在LKM编程中使用过SOCKET API,并且能成功insmod,能不能说一下如何实现?
谢谢.
|
xuexi