uso - ¿Cómo acceder a las direcciones físicas desde el espacio de usuario en Linux?
ver memoria ram linux terminal (2)
busybox devmem
busybox devmem
es una pequeña utilidad de CLI que mmaps /dev/mem
.
Puedes conseguirlo en Ubuntu con: sudo apt-get install busybox
Uso: lea 4 bytes desde la dirección física 0x12345678
:
sudo busybox devmem 0x12345678
Escriba 0x9abcdef0
en esa dirección:
sudo busybox devmem 0x12345678 w 0x9abcdef0
Fuente: https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85
MAP_SHARED
Cuando mmapping /dev/mem
, es probable que desee utilizar:
open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)
MAP_SHARED
hace que las escrituras vayan a la memoria física inmediatamente, lo que hace que sea más fácil de observar, y tiene más sentido para las grabaciones de registros de hardware.
CONFIG_STRICT_DEVMEM
y nopat
Para usar /dev/mem
para ver y modificar la memoria RAM regular en kernel v4.9, debe puño:
- deshabilitar
CONFIG_STRICT_DEVMEM
(establecido por defecto en Ubuntu 17.04) - pasar la opción de línea de comando del kernel
nopat
para x86
Los puertos IO todavía funcionan sin esos.
Lavado de caché
Si intenta escribir en RAM en lugar de en un registro, la memoria puede ser almacenada en la memoria caché por la CPU: ¿Cómo vaciar el caché de la CPU para una región de espacio de direcciones en Linux? y no veo una forma muy portátil / fácil de enjuagarlo o marcar la región como incableable:
- ¿Cómo vaciar el caché de la CPU para una región de espacio de direcciones en Linux?
- ¿Es posible asignar, en el espacio de usuario, un bloque de memoria no cacheable en Linux?
¿Entonces /dev/mem
no se puede usar de manera confiable para pasar memorias intermedias de memoria a los dispositivos?
Esto no se puede observar en QEMU lamentablemente, ya que QEMU no simula cachés.
Cómo probarlo
Ahora viene la parte divertida. Aquí hay algunas configuraciones geniales:
- Memoria de usuario
- asignar variable
volatile
en un proceso de usuario - obtener la dirección física con
/proc/<pid>/maps
+/proc/<pid>/pagemap
- modifique el valor en la dirección física con
devmem
, y observe cómo el proceso de userland reacciona
- asignar variable
- Memoria Kernelland
- asignar memoria de kernel con
kmalloc
- obtener la dirección física con
virt_to_phys
y pasarla a userland - modificar la dirección física con
devmem
- consultar el valor del módulo kernel
- asignar memoria de kernel con
- Dispositivo de plataforma virtual IO mem y QEMU
- crear un dispositivo de plataforma con direcciones de registros físicos conocidos
- use
devmem
para escribir en el registro - Ver
printf
s salir del dispositivo virtual en respuesta
En un sistema basado en ARM que ejecuta Linux, tengo un dispositivo cuya memoria está asignada a una dirección física. Desde un programa de espacio de usuario donde todas las direcciones son virtuales, ¿cómo puedo leer el contenido de esta dirección?
Puede asignar un archivo de dispositivo a una memoria de proceso de usuario mediante la llamada al sistema mmap(2)
. Por lo general, los archivos del dispositivo son asignaciones de memoria física al sistema de archivos. De lo contrario, tiene que escribir un módulo kernel que crea dicho archivo o proporciona una forma de asignar la memoria necesaria a un proceso de usuario.
Otra forma es reasignar partes de / dev / mem a la memoria de un usuario.
Editar: Ejemplo de mmaping / dev / mem (este programa debe tener acceso a / dev / mem, por ejemplo, tener derechos de 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;
}