浅析user用户态程序如何访问kernel空间的物理内存DDR和物理寄存器

本文介绍了Linux下通过/dev/mem和/dev/kmem访问物理内存及内核空间的方法,详细解析了这两种方式的区别及其应用场景,包括访问物理IO设备和查看内核变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

浅析user用户态程序如何访问kernel空间的物理内存DDR和物理寄存器

fs_initcall( chr_dev_init) ;
创建如下的char设备在/ dev/:
/ dev/ mem
/ dev/ kmen
/ dev/ null
/ dev/ port
/ dev/ zero
/ dev/ full
/ dev/ random
/ dev/ urandom
/ dev/ kmsg
/ dev/ oldmem
+ + + + + + + + + + + + + + + + + + + +
因为kernel已经将所有的DDR物理内存和io寄存器线性映射到了内核空间, 同时
为他们建立了mmu三级映射表, 所以这就保证了, 不用执行mmap再建mmu三级表, 直接访问即可【gliethttp.Leith】!
= = = = = = = = = = = = = = = = = = = =
/ dev/ mem的实现:
比如我们的arm物理内存:
0x80000000~ 0x8fffffff共256M
high_memory为DDR物理地址上限0x8fffffff对应的内核虚拟地址,
/ dev/ mem进行读操作时,
传入的有效pos值范围为: 0x80000000~ 0x8fffffff,
然后/ dev/ mem驱动会将0x80000000~ 0x8fffffff范围内的* ppos值, 通过
__phys_to_virt( * ppos) 函数将物理地址转换为内核地址, 然后执行copy_to_user( ) , 将kernel空间的内存数据拷贝到用户空间,
= = = = = = = = = = = = = = = = = = = =
/ dev/ kmem的实现:
/ dev/ mem基本一样, 唯一不同的地方是,
/ dev/ kmem的read函数部不对pos值进行加工, 传入的是什么值, 就直接将该值作为地址执行copy_to_user( ) 操作, 所以/ dev/ kmem没有__phys_to_virt( * ppos) 这一步
= = = = = = = = = = = = = = = = = = = =
/ dev/ port的实现:
/ dev/ mem一样, 不对pos值进行加工, 传入的值就是寄存器的物理地址
= = = = = = = = = = = = = = = = = = = =
/ dev/ null的实现:
static ssize_t read_null( struct file * file , char __user * buf,
             size_t count , loff_t * ppos)
{
    return 0; //没东西
}
static ssize_t write_null( struct file * file , const char __user * buf,
             size_t count , loff_t * ppos)
{
    return count ; //认为写入成功,直接返回成功写入字节数count,所以/dev/null就成了大家所谓的"黑洞
"
}
= = = = = = = = = = = = = = = = = = = = = = = = =
static const struct {
    unsigned int         minor;
    char             * name;
    umode_t            mode;
    const struct file_operations    * fops;
} devlist[ ] = { /* list of minor devices */
# ifdef CONFIG_DEVMEM
    { 1, "mem" , S_IRUSR | S_IWUSR | S_IRGRP, & mem_fops} ,
    { 2, "kmem" , S_IRUSR | S_IWUSR | S_IRGRP, & kmem_fops} ,
# endif
    { 3, "null" , S_IRUGO | S_IWUGO, & null_fops} ,
# ifdef CONFIG_DEVPORT
    { 4, "port" , S_IRUSR | S_IWUSR | S_IRGRP, & port_fops} ,
# endif
    { 5, "zero" , S_IRUGO | S_IWUGO, & zero_fops} ,
    { 7, "full" , S_IRUGO | S_IWUGO, & full_fops} ,
    { 8, "random" , S_IRUGO | S_IWUSR, & random_fops} ,
    { 9, "urandom" , S_IRUGO | S_IWUSR, & urandom_fops} ,
    { 11, "kmsg" , S_IRUGO | S_IWUSR, & kmsg_fops} ,
# ifdef CONFIG_CRASH_DUMP
    { 12, "oldmem" , S_IRUSR | S_IWUSR | S_IRGRP, & oldmem_fops} ,
# endif
} ;

static const struct file_operations memory_fops = {
    . open         = memory_open,     /* just a selector for the real open */
} ;

static int memory_open( struct inode * inode, struct file * filp)
{
    switch ( iminor( inode) ) {
# ifdef CONFIG_DEVMEM
        case 1:
            filp- > f_op = & mem_fops;
            filp- > f_mapping- > backing_dev_info =
                & directly_mappable_cdev_bdi;
            break ;
        case 2:
            filp- > f_op = & kmem_fops;
            filp- > f_mapping- > backing_dev_info =
                & directly_mappable_cdev_bdi;
            break ;
# endif
        case 3:
            filp- > f_op = & null_fops;
            break ;
# ifdef CONFIG_DEVPORT
        case 4:
            filp- > f_op = & port_fops;
            break ;
# endif
        case 5:
            filp- > f_mapping- > backing_dev_info = & zero_bdi;
            filp- > f_op = & zero_fops;
            break ;
        case 7:
            filp- > f_op = & full_fops;
            break ;
        case 8:
            filp- > f_op = & random_fops;
            break ;
        case 9:
            filp- > f_op = & urandom_fops;
            break ;
        case 11:
            filp- > f_op = & kmsg_fops;
            break ;
# ifdef CONFIG_CRASH_DUMP
        case 12:
            filp- > f_op = & oldmem_fops;
            break ;
# endif
        default :
            return - ENXIO;
    }
    if ( filp- > f_op & & filp- > f_op- > open )
        return filp- > f_op- > open ( inode, filp) ;
    return 0;
}

Linux下/ dev/ mem和/ dev/ kmem的区别
    原创内容,转载请标明来自http: //lixings.cublog.cn

区别:

   1. / dev/ mem: 物理内存的全镜像。可以用来访问物理内存。
   2. / dev/ kmem: kernel看到的虚拟内存的全镜像。可以用来访问kernel的内容。


作用:

   1. 前者用来访问物理IO设备,比如X用来访问显卡的物理内存,或嵌入式中访问GPIO。用法一般就是open,然后mmap,接着可以使用map之后的地址来访问物理内存。这其实就是实现用户空间驱动的一种方法。
   2. 后者一般可以用来查看kernel的变量,或者用作rootkit之类的。参考1和2描述了用来查看kernel变量这个问题。


参考:

   1. http: //lwn.net/Articles/147902/
   2. http: //lkml.org/lkml/2005/8/11/301


On Thu, 2005- 08- 11 at 17: 36 - 0400, Steven Rostedt wrote:
> OK, I thought I use to know this . But what is the difference
> between / dev/ kmem and / dev/ mem. I thought that with / dev/ kmem you could
> use the actual kernel addresses to read from.
>
> For example, if I wanted to read the current variable X in the kernel, I
> could look up the address of X in System . map , then mmaping to / dev/ kmem
> I could get to that variable using the address that I got from
> System . map . But this doesn't seem to work.
>
> I'
m getting an IO error on read . And looking at this I see:
>
>
> static int mmap_kmem( struct file * file , struct vm_area_struct * vma)
> {
> unsigned long long val;
>     /*
>      * RED-PEN: on some architectures there is more mapped memory
>      * than available in mem_map which pfn_valid checks
>      * for. Perhaps should add a new macro here.
>      *
>      * RED-PEN: vmalloc is not supported right now.
>      */

>     if ( ! pfn_valid( vma- > vm_pgoff) )
>         return - EIO;
>     val = ( u64) vma- > vm_pgoff < < PAGE_SHIFT;
>     vma- > vm_pgoff = __pa( val) > > PAGE_SHIFT;
>     return mmap_mem( file , vma) ;
> }
>
> I printed out the value in vma- > vm_pgoff, and it still has the
> 0xc0000000 ( but shifted > > 12) . Isn't this suppose to also remove the
> 0xc? Or am I just totally off here?
>
> Thanks,
>
> -- Steve
>


Found the problem. It is a bug with mmap_kmem. The order of checks is
wrong, so here'
s the patch. Attached is a little program that reads the
System map looking for the variable modprobe_path. If it finds it, then
it opens / dev/ kmem for read only and mmaping it to read the contents of
modprobe_path.

Without this fix I get:

# . / tmap / boot/ System . map
found modprobe_path at ( 0xc03647e0) c03647e0
mmap: Input/ output error

On a machine with the patch, I now get:

# . / tmap / boot/ System . map
found modprobe_path at ( 0xc03aa900) c03aa900
/ sbin/ modprobe

Note that the attached program does not handle the case of the string
crossing over a page.

- - Steve

Here

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值