44

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

4

2 に答える 2

54

busybox devmem

busybox devmemmmapsを使用する小さなCLIユーティリティです/dev/mem

あなたはUbuntuでそれを得ることができます:sudo apt-get install busybox

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

sudo busybox devmem 0x12345678

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

sudo busybox devmem 0x12345678 w 0x9abcdef0

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

mmapMAP_SHARED

mmappingするときは/dev/mem、次を使用する可能性があります。

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

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

CONFIG_STRICT_DEVMEMnopat

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

  • 無効にするCONFIG_STRICT_DEVMEM(Ubuntu 17.04ではデフォルトで設定されています)
  • nopatx86のカーネルコマンドラインオプションを渡す

IOポートはそれらがなくても機能します。

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

キャッシュフラッシング

レジスタの代わりにRAMに書き込もうとすると、メモリがCPUによってキャッシュされる可能性があります。Linuxのアドレス空間の領域のCPUキャッシュをフラッシュする方法は?そして、それをフラッシュしたり、リージョンをキャッシュ不可としてマークしたりするための非常にポータブルで簡単な方法がわかりません。

では/dev/mem、メモリバッファをデバイスに渡すために確実に使用できないのではないでしょうか。

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

それをテストする方法

さて、楽しい部分です。ここにいくつかのクールなセットアップがあります:

  • ユーザーランドメモリ
    • volatileユーザーランドプロセスに変数を割り当てる
    • /proc/<pid>/maps+で物理アドレスを取得します/proc/<pid>/pagemap
    • 物理アドレスの値をで変更しdevmem、ユーザーランドプロセスが反応するのを監視します
  • カーネルランドメモリ
    • カーネルメモリをkmalloc
    • で物理アドレスを取得し、virt_to_physそれをユーザーランドに返します
    • で物理アドレスを変更しますdevmem
    • カーネルモジュールから値を照会する
  • IOmemおよびQEMU仮想プラットフォームデバイス
    • 既知の物理レジスタアドレスを使用してプラットフォームデバイスを作成する
    • devmemレジスタへの書き込みに使用
    • それに応じて時計printfが仮想デバイスから出てきます

ボーナス:仮想アドレスの物理アドレスを決定する

Linuxの仮想アドレスから物理アドレスを決定するためのAPIはありますか?

于 2017-07-16T11:12:12.827 に答える
46

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

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

編集:mmaping / dev / memの例(このプログラムは/ dev / memにアクセスできる必要があります。たとえば、ルート権限があります):

#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;
}
于 2012-08-20T16:18:11.123 に答える