web-dev-qa-db-ja.com

Linuxのユーザー空間から物理アドレスにアクセスする方法は?

Linuxを実行しているARMベースのシステムでは、メモリが物理アドレスにマップされているデバイスがあります。すべてのアドレスが仮想であるユーザー空間プログラムから、このアドレスからコンテンツを読み取るにはどうすればよいですか?

28
lang2

mmap(2)システムコールを使用して、デバイスファイルをユーザープロセスメモリにマップできます。通常、デバイスファイルは、物理メモリからファイルシステムへのマッピングです。それ以外の場合は、そのようなファイルを作成するか、必要なメモリをユーザープロセスにマップする方法を提供するカーネルモジュールを記述する必要があります。

もう1つの方法は、/ dev/memの一部をユーザーメモリに再マッピングすることです。

編集:/ dev/memのmmapingの例(このプログラムには/ dev/memへのアクセス権が必要です。たとえば、root権限が必要です):

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
        return 0;
    }

    off_t offset = strtoul(argv[1], NULL, 0);
    size_t len = strtoul(argv[2], NULL, 0);

    // Truncate offset to a multiple of the page size, or mmap will fail.
    size_t pagesize = sysconf(_SC_PAGE_SIZE);
    off_t page_base = (offset / pagesize) * pagesize;
    off_t page_offset = offset - page_base;

    int fd = open("/dev/mem", O_SYNC);
    unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
    if (mem == MAP_FAILED) {
        perror("Can't map memory");
        return -1;
    }

    size_t i;
    for (i = 0; i < len; ++i)
        printf("%02x ", (int)mem[page_offset + i]);

    return 0;
}
36
Dmytro Sirenko

busybox devmem

busybox devmemは、/dev/memをmmapする小さなCLIユーティリティです。

Sudo apt-get install busyboxを使用してUbuntuで取得できます。

使用法:物理アドレス0x12345678から4バイトを読み取ります:

Sudo busybox devmem 0x12345678

0x9abcdef0をそのアドレスに書き込みます。

Sudo busybox devmem 0x12345678 w 0x9abcdef0

ソース: https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85

mmap MAP_SHARED

/dev/memをmmappingするときは、おそらく以下を使用したいでしょう:

open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)

MAP_SHAREDは、書き込みを物理メモリにすぐに送信します。これにより、監視が容易になり、ハードウェアレジスタの書き込みに適しています。

CONFIG_STRICT_DEVMEMおよびnopat

/dev/memを使用して、カーネルv4.9で通常のRAM)を表示および変更するには、次のことを行う必要があります。

  • disable CONFIG_STRICT_DEVMEM(Ubuntu 17.04でデフォルトで設定)
  • x86のnopatカーネルコマンドラインオプションを渡す

IOポートはそれらなしでも動作します。

参照: / dev/memのmmapは、virt_to_physアドレスの無効な引数で失敗しますが、アドレスはページ境界で整列されます

キャッシュのフラッシュ

レジスタの代わりにRAMに書き込もうとすると、メモリはCPUによってキャッシュされる場合があります: Linux? そして、私はそれをフラッシュしたり、キャッシュ不可としてリージョンをマークするための非常に移植性のある/簡単な方法を見ていません:

それで、おそらく/dev/memを使用してメモリバッファをデバイスに渡すことはできませんか?

QEMUはキャッシュをシミュレートしないため、残念ながらQEMUではこれを確認できません。

それをテストする方法

楽しい部分になりました。いくつかのクールなセットアップを次に示します。

  • ユーザーランドメモリ
    • ユーザーランドプロセスにvolatile変数を割り当てる
    • /proc/<pid>/maps + /proc/<pid>/pagemapで物理アドレスを取得します
    • 物理アドレスの値をdevmemで変更し、ユーザーランドプロセスが反応するのを監視します
  • カーネルランドメモリ
    • kmallocでカーネルメモリを割り当てます
    • virt_to_physで物理アドレスを取得し、ユーザーランドに返します
    • devmemを使用して物理アドレスを変更します
    • カーネルモジュールから値を問い合わせる
  • IO memおよびQEMU仮想プラットフォームデバイス
    • 既知の物理レジスタアドレスを持つプラットフォームデバイスを作成する
    • devmemを使用してレジスタに書き込みます
    • printfsが応答して仮想デバイスから出てくるのを見る