浅析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