Acceder a la memoria en Linux desde un programa de usuario no es algo inmediato, pero tampoco es nada complicado una vez que te lo explican.
En primer lugar es necesario abrir el "fichero" /dev/mem que es una "copia" de la memoria física del ordenador. Ojo, para ello es necesario ser root, por lo que el programa tendrá que ejecutarse teniendo esto en cuenta. La manera de abrirlo es:
fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd<0) {
perror("open(\"/dev/mem\")");
exit(1);
}
}
Una vez abierto el fichero se mapea una página de la memoria a un puntero para poder acceder a la memoria. Para ello se usa la función mmap:
#define MAP_SIZE 4096
#define MAP_MASK ( MAP_SIZE - 1 )
....
map = mmap(0,
MAP_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
addr & ~MAP_MASK
);
if (map == (void*)-1 ) {
perror("mmap()");
exit(1);
}
regaddr = map + (addr & MAP_MASK);
val = *(uint16*) regaddr;
munmap(0,MAP_SIZE);
El primer parámetro de mmap se suele dejar siempre a 0, el segundo indica el tamaño del bloque (que en este caso es una página de 4 kB). El tercero indica que queremos leer y escribir en la memoria. El cuarto indica que este mapeo puede compartirse con otros procesos que soliciten acceder a la misma página física y que la escritura en el bloque mapeado se realiza también en el "fichero" (y por tanto en la memoria física en este caso). El quinto indica el fichero que se mapea y por último se indica el desplazamiento dentro del fichero. Nótese que en dicho desplazamiento se han puesto a cero los bits menos significativos correspondientes a la dirección dentro de la página, pues lo que se quiere mapear es la página que contiene la dirección (addr) a la que queremos acceder.
La siguiente línea calcula la dirección dentro del bloque mapeado en la que se encuentra la dirección física que buscábamos. En la siguiente accedemos a dicha dirección y por último antes de terminar liberamos mapeo pues el número de éstos disponibles es limitado.
Este código se ha obtenido del programa pxaregs.c, disponible como casi todo en google ;)
Dicho programa encapsula todo el proceso de lectura/escritura en memoria física en dos funciones:
static uint16 getmem(uint32 addr);
static void putmem(uint32 addr, uint16 val);
Usando estas funciones hemos creado un programa que barre la memoria de la tarjeta buscando en cada bloque de 16 MB un registro mágico situado en la dirección 0x??F80004. Esto se consigue con este bucle:
for(n=0; n<256; n++){
dir_mag = (n<<24) | TCNMAGIC;
putmem(dir_mag, 0x9A);
valor = getmem(dir_mag);
printf("Dir: %x, valor: %x\n", dir_mag, valor);
}
Al ejecutar el programa resulta que el registro mágico aparece en todos los sitios excepto en donde ya existía memoria; es decir, que la tarjeta en cuanto se intenta acceder a una dirección que no está dada de alta como memoria RAM o como espacio de direcciones del PCI, la envía al bus isa. la ejecución del programa resulta en:
Dir: f80004, valor: 9a
Dir: 1f80004, valor: 9a
...
Dir: 1bf80004, valor: 9a
Dir: 1cf80004, valor: 65
...
Dir: eff80004, valor: 65
Dir: f0f80004, valor: 3568
...
Dir: fef80004, valor: 3568
Dir: fff80004, valor: 3568
Si se hace un cat de /proc/iomem nos sale:
00000000-0009efff : System RAM
000a0000-000bffff : Video RAM area
000c0000-000c7fff : Video ROM
000f0000-000fffff : System ROM
00100000-1bfbffff : System RAM
00100000-002ee668 : Kernel code
002ee669-00385c03 : Kernel data
20000000-2000ffff : 0000:00:09.0
e0000000-e7ffffff : 0000:00:01.1
effa0000-effbffff : 0000:00:09.0
effa0000-effbffff : e100
effe8000-effebfff : 0000:00:01.1
effec000-effeffff : 0000:00:01.1
efff0000-efff3fff : 0000:00:01.1
efff4000-efff7fff : 0000:00:01.2
efff8000-efffbfff : 0000:00:01.1
efffd000-efffdfff : 0000:00:0f.5
efffd000-efffdfff : ehci_hcd
efffe000-efffefff : 0000:00:0f.4
efffe000-efffefff : ohci_hcd
effff000-efffffff : 0000:00:09.0
effff000-efffffff : e100
fffe8000-ffffffff : reserved
No hay comentarios:
Publicar un comentario