Soubor zařízení můžete namapovat do paměti uživatelského procesu pomocí mmap(2)
systémové volání. Soubory zařízení jsou obvykle mapování fyzické paměti na systém souborů. V opačném případě musíte napsat modul jádra, který takový soubor vytvoří nebo poskytne způsob, jak namapovat potřebnou paměť na uživatelský proces.
Dalším způsobem je přemapování částí /dev/mem do uživatelské paměti.
Edit:Příklad mmapování /dev/mem (tento program musí mít přístup k /dev/mem, např. mít práva 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
je malý nástroj CLI, který mmapuje /dev/mem
.
Můžete jej získat v Ubuntu s:sudo apt-get install busybox
Použití:čtení 4 bajtů z fyzické adresy 0x12345678
:
sudo busybox devmem 0x12345678
Napište 0x9abcdef0
na tuto adresu:
sudo busybox devmem 0x12345678 w 0x9abcdef0
Zdroj:https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85
mmap MAP_SHARED
Při mmapování /dev/mem
, pravděpodobně budete chtít použít:
open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)
MAP_SHARED
umožňuje zápisy do fyzické paměti okamžitě, což usnadňuje pozorování a dává větší smysl pro zápisy do hardwarových registrů.
CONFIG_STRICT_DEVMEM
a nopat
Chcete-li použít /dev/mem
Chcete-li zobrazit a upravit běžnou RAM na jádře v4.9, musíte nejprve:
- zakázat
CONFIG_STRICT_DEVMEM
(výchozí nastavení na Ubuntu 17.04) - předejte
nopat
možnost příkazového řádku jádra pro x86
IO porty bez nich stále fungují.
Viz také:mmap /dev/mem selže s neplatným argumentem pro adresu virt_to_phys, ale adresa je zarovnána na stránku
Vyprázdnění mezipaměti
Pokud se pokusíte zapisovat do RAM místo do registru, může být paměť ukládána do mezipaměti CPU:Jak vyprázdnit mezipaměť CPU pro oblast adresního prostoru v Linuxu? a nevidím příliš přenosný/snadný způsob, jak to spláchnout nebo označit oblast jako necachovatelnou:
- Jak zapsat paměť prostoru jádra (fyzickou adresu) do souboru pomocí O_DIRECT?
- Jak vyprázdnit mezipaměť CPU pro oblast adresního prostoru v systému Linux?
- Je možné alokovat v uživatelském prostoru blok paměti, který nelze uložit do mezipaměti v systému Linux?
Takže možná /dev/mem
nelze spolehlivě použít k předávání vyrovnávacích pamětí zařízením?
To bohužel nelze v QEMU pozorovat, protože QEMU nesimuluje mezipaměti.
Jak to otestovat
Nyní k té zábavnější části. Zde je několik skvělých nastavení:
- Paměť uživatele
- přidělit
volatile
proměnná v uživatelském procesu - získáte fyzickou adresu pomocí
/proc/<pid>/maps
+/proc/<pid>/pagemap
- upravte hodnotu na fyzické adrese pomocí
devmem
a sledujte reakci uživatelského procesu
- přidělit
- Paměť Kernelland
- přidělte paměť jádra pomocí
kmalloc
- získáte fyzickou adresu pomocí
virt_to_phys
a předat jej zpět uživatelské zemi - upravte fyzickou adresu pomocí
devmem
- dotaz na hodnotu z modulu jádra
- přidělte paměť jádra pomocí
- IO paměť a zařízení virtuální platformy QEMU
- vytvořte platformové zařízení se známými adresami fyzického registru
- použijte
devmem
zapsat do registru - sledujte
printf
s vycházejí z virtuálního zařízení jako odpověď
Bonus:určení fyzické adresy pro virtuální adresu
Existuje nějaké API pro určení fyzické adresy z virtuální adresy v Linuxu?