Linuxを実行しているARMベースのシステムでは、メモリが物理アドレスにマップされているデバイスがあります。すべてのアドレスが仮想であるユーザー空間プログラムから、このアドレスからコンテンツを読み取るにはどうすればよいですか?
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;
}
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)を表示および変更するには、次のことを行う必要があります。
CONFIG_STRICT_DEVMEM
(Ubuntu 17.04でデフォルトで設定)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
を使用して物理アドレスを変更しますdevmem
を使用してレジスタに書き込みますprintf
sが応答して仮想デバイスから出てくるのを見る