Anda dapat memetakan file perangkat ke memori proses pengguna menggunakan mmap(2)
panggilan sistem. Biasanya, file perangkat memetakan memori fisik ke sistem file. Jika tidak, Anda harus menulis modul kernel yang membuat file tersebut atau menyediakan cara untuk memetakan memori yang diperlukan ke proses pengguna.
Cara lain adalah memetakan ulang bagian /dev/mem ke memori pengguna.
Sunting:Contoh mmaping /dev/mem (program ini harus memiliki akses ke /dev/mem, misalnya memiliki hak 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
adalah utilitas CLI mungil yang mmaps /dev/mem
.
Anda bisa mendapatkannya di Ubuntu dengan:sudo apt-get install busybox
Penggunaan:baca 4 byte dari alamat fisik 0x12345678
:
sudo busybox devmem 0x12345678
Tulis 0x9abcdef0
ke alamat itu:
sudo busybox devmem 0x12345678 w 0x9abcdef0
Sumber:https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85
mmap MAP_SHARED
Saat memetakan /dev/mem
, Anda mungkin ingin menggunakan:
open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)
MAP_SHARED
membuat penulisan langsung masuk ke memori fisik, yang membuatnya lebih mudah untuk diamati, dan lebih masuk akal untuk penulisan register perangkat keras.
CONFIG_STRICT_DEVMEM
dan nopat
Untuk menggunakan /dev/mem
untuk melihat dan memodifikasi RAM biasa pada kernel v4.9, Anda harus mengepal:
- nonaktifkan
CONFIG_STRICT_DEVMEM
(ditetapkan secara default di Ubuntu 17.04) - teruskan
nopat
opsi baris perintah kernel untuk x86
Port IO masih berfungsi tanpa itu.
Lihat juga:mmap dari /dev/mem gagal dengan argumen yang tidak valid untuk alamat virt_to_phys, tetapi alamatnya selaras dengan halaman
Pembersihan cache
Jika Anda mencoba menulis ke RAM alih-alih register, memori mungkin di-cache oleh CPU:Bagaimana cara mengosongkan cache CPU untuk wilayah ruang alamat di Linux? dan saya tidak melihat cara yang sangat portabel/mudah untuk membersihkannya atau menandai wilayah sebagai tidak dapat di-cache:
- Bagaimana cara menulis memori ruang kernel (alamat fisik) ke file menggunakan O_DIRECT?
- Bagaimana cara mengosongkan cache CPU untuk wilayah ruang alamat di Linux?
- Apakah mungkin mengalokasikan, di ruang pengguna, blok memori yang tidak dapat di-cache di Linux?
Jadi mungkin /dev/mem
tidak dapat digunakan dengan andal untuk meneruskan buffer memori ke perangkat?
Sayangnya, hal ini tidak dapat diamati di QEMU, karena QEMU tidak mensimulasikan cache.
Cara mengujinya
Sekarang untuk bagian yang menyenangkan. Berikut ini beberapa penyiapan keren:
- Memori pengguna
- alokasikan
volatile
variabel pada proses userland - dapatkan alamat fisik dengan
/proc/<pid>/maps
+/proc/<pid>/pagemap
- ubah nilai di alamat fisik dengan
devmem
, dan saksikan proses userland bereaksi
- alokasikan
- Memori kernelland
- alokasikan memori kernel dengan
kmalloc
- dapatkan alamat fisik dengan
virt_to_phys
dan meneruskannya kembali ke userland - ubah alamat fisik dengan
devmem
- kueri nilai dari modul kernel
- alokasikan memori kernel dengan
- IO mem dan perangkat platform virtual QEMU
- buat perangkat platform dengan alamat register fisik yang diketahui
- gunakan
devmem
untuk menulis ke register - tonton
printf
s keluar dari perangkat virtual sebagai tanggapan
Bonus:tentukan alamat fisik untuk alamat virtual
Apakah ada API untuk menentukan alamat fisik dari alamat virtual di Linux?