devmem读写物理内存和devkmem读取内核虚拟内存

在日常工作中常有直接操作寄存器或者某一物理地址的需求,busybox中提供了devmem。通过它可以读写物理内存。它的实现借助mmap和/dev/mem,通过mmap将/dev/mem物理地址映射到用户空间,devmem就可以像操作虚拟地址一样进行读写。

hexdump同样也可以类似devmem的功能。

如果需要在用户空间获取内核某个变量值,可以使用devkmem通过/dev/kmem进行。

下面分别介绍这三种工具。

1. devmem操作物理地址,它是如何做到的?

用户空间是无法直接操作物理地址的;但是日常工作中常需要对某一物理地址进行读写,尤其是寄存器。

devmem可以实现这个功能。那么devmem做了什么?/dev/mem在内核中优势如何实现的呢?

1.1 devmem工具使用

devmem使用介绍如下:

BusyBox v1.27.2 (2019-04-16 17:00:28 CST) multi-call binary.

Usage: devmem ADDRESS [WIDTH [VALUE]]

Read/write from physical address

ADDRESS Address to act upon
        WIDTH  Width (8/16/...)
        VALUE  Data to be written

devmem的能力有限,只能处理最大64字节的数目。

下面向0xfc20700这个地址写入32位数据0x12345678:

devmem 0xfc20700 32 0x12345678

然后从0xfc20700读取进行验证。

devmem 0xfc20700 32
0x12345678

1.2 devmem工具分析

从下面的代码可知,devmem解析参数,然后将地址转换成页面对齐的地址。mmap将/dev/mem的输入地址偏移的页面映射到用户空间,然后读取数值。

int devmem_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int devmem_main(int argc UNUSED_PARAM, char **argv)
{
    void *map_base, *virt_addr;
    uint64_t read_result;
    uint64_t writeval = writeval; /* for compiler */
    off_t target;
    unsigned page_size, mapped_size, offset_in_page;
    int fd;
    unsigned width = 8 * sizeof(int);

/* ADDRESS */
    if (!argv[1])
        bb_show_usage();
    errno = 0;
    target = bb_strtoull(argv[1], NULL, 0); /* allows hex, oct etc */---------------第一个参数是地址

/* WIDTH */
    if (argv[2]) {------------------------------------------------------------------第二个参数,在写的情况下,需要知道写数据的位宽。
        if (isdigit(argv[2][0]) || argv[2][1])
            width = xatou(argv[2]);
        else {
            static const char bhwl[] ALIGN1 = "bhwl";
            static const uint8_t sizes[] ALIGN1 = {
                8 * sizeof(char),
                8 * sizeof(short),
                8 * sizeof(int),
                8 * sizeof(long),
                0 /* bad */
            };
            width = strchrnul(bhwl, (argv[2][0] | 0x20)) - bhwl;
            width = sizes[width];
        }
        /* VALUE */
        if (argv[3])-----------------------------------------------------------------第三个参数,待写入数值。
            writeval = bb_strtoull(argv[3], NULL, 0);
    } else { /* argv[2] == NULL */
        /* make argv[3] to be a valid thing to fetch */
        argv--;
    }
    if (errno)
        bb_show_usage(); /* one of bb_strtouXX failed */

fd = xopen("/dev/mem", argv[3] ? (O_RDWR | O_SYNC) : (O_RDONLY | O_SYNC));-------根据第三个参数确定是以只读形式打开,还是以读写形式打开。/dev/mem代表整个内核空间。
    mapped_size = page_size = getpagesize();
    offset_in_page = (unsigned)target & (page_size - 1);-----------------------------对地址进行也对齐。
    if (offset_in_page + width > page_size) {----------------------------------------如果跨页,则mapped_size编程两个页面。
        /* This access spans pages.
        * Must map two pages to make it possible: */
        mapped_size *= 2;
    }
    map_base = mmap(NULL,
            mapped_size,
            argv[3] ? (PROT_READ | PROT_WRITE) : PROT_READ,
            MAP_SHARED,
            fd,
            target & ~(off_t)(page_size - 1));---------------------------------------将/dev/mem文件的从target的页对齐偏移开始,映射mapped_size块大小内存。映射结果是map_base。
    if (map_base == MAP_FAILED)
        bb_perror_msg_and_die("mmap");

//    printf("Memory mapped at address %p.\n", map_base);

virt_addr = (char*)map_base + offset_in_page;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/12046.html