¿Cómo acceder a mmaped /dev/mem sin bloquear el kernel de Linux?

5 minutos de lectura

avatar de usuario
Vinay

Tengo un programa simple que intenta acceder a la memoria física en el espacio del usuario, donde el núcleo almacena la primera página de estructura. En una máquina de 64 bits, esta dirección es:

  • dirección virtual del núcleo: ffffea0000000000
  • dirección física: 0000620000000000

Estoy tratando de acceder a esta dirección física a través de mmap en el espacio del usuario. Pero el siguiente código bloquea el kernel.

int *addr;
if ((fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0 ) {
    printf("Error opening file. \n");
    close(fd);
    return (-1);
}
/* mmap.  address of first struct page for 64 bit architectures 
 * is 0x0000620000000000.
 */
addr = (int *)mmap(0, num*STRUCT_PAGE_SIZE, PROT_READ, MAP_PRIVATE,
            fd, 0x0000620000000000);
printf("addr: %p \n",addr);
printf("addr: %d \n",*addr); /* CRASH. */

  • ¿Cuál es el valor que mmap() devuelve en addr?

    – BjoernD

    9 de agosto de 2012 a las 21:18

  • @BjoernD: probé lo anterior en un x86 de 32 bits (reemplazando mmap offset como 0x01000000); dirección = 0xffffffff. Y sí, se bloquea, por supuesto, en la desreferencia. ¿Cual es la solución?

    – Kaiwán

    10 de agosto de 2012 a las 6:19

  • 0xffffffff == -1 -> mmap() está devolviendo un error. Según la página del manual, el motivo del error se indica en la variable ‘errno’. Así que es posible que desee comprobar tat.

    – BjoernD

    10 de agosto de 2012 a las 7:43

  • Consulte también ¿Cómo acceder a /dev/mem mmaped sin bloquear el kernel de Linux?

    – jww

    11 de julio de 2017 a las 3:24

avatar de usuario
Kaiwán

Creo que he encontrado el problema: tiene que ver con la protección de mapeo de memoria /dev/mem en el x86.

Consulte este artículo de LWN: “x86: introduzca restricciones /dev/mem con una opción de configuración”
http://lwn.net/Artículos/267427/

CONFIG_NONPROMISC_DEVMEM

Ahora (probé esto en un kernel 3.2.21 reciente), la opción de configuración parece llamarse CONFIG_STRICT_DEVMEM.

Cambié la configuración de mi kernel:

$ grep DEVMEM .config
# CONFIG_STRICT_DEVMEM is not set
$ 

Cuando el prg anterior se ejecutó con el anterior kernel, con CONFIG_STRICT_DEVMEM SET: dmesg muestra:

[29537.565599] Program a.out tried to access /dev/mem between 1000000->1001000.
[29537.565663] a.out[13575]: segfault at ffffffff ip 080485bd sp bfb8d640 error 4 in a.out[8048000+1000]

Esto se debe a la protección del núcleo.

Cuando se reconstruyó el kernel (con CONFIG_STRICT_DEVMEM DESCONFIGURAR) y se ejecutó el prg anterior:

# ./a.out 
mmap failed: Invalid argument
# 

Esto se debe a que el parámetro ‘compensación’ es > 1 MB (no válido en x86) (eran 16 MB).

Después de hacer que el desplazamiento de mmap esté dentro de 1 MB:

# ./a.out 
addr: 0xb7758000
*addr: 138293760 
# 

¡Funciona! Consulte el artículo de LWN anterior para obtener más información.

En las arquitecturas x86 con compatibilidad con PAT (tabla de atributos de página), el kernel sigue impidiendo la asignación de regiones DRAM. La razón de esto como se menciona en el fuente del núcleo es:

This check is nedded to avoid cache aliasing when PAT is enabled

Esta comprobación provocará un error similar al mencionado anteriormente. Por ejemplo:

Program a.out tried to access /dev/mem between [mem 68200000-68201000].

Esta restricción se puede eliminar desactivando PAT. PAT se puede deshabilitar agregando el argumento “nopat” a la línea de comando del kernel en el momento del arranque.

  • Hola Kaiwan, gracias por señalar la interesante variable de configuración. ‘CONFIG_STRICT_DEVMEM no está configurado’ en mi caso. Núcleo (3.4.6 y 3.1.0). Después de hacer el cambio de compensación, el programa funciona. Pero estaba interesado en acceder a esa dirección ya que contiene la primera página de estructura. ¿es posible?

    – Vinay

    10 de agosto de 2012 a las 18:54

  • También establecí el desplazamiento en 0x0000000000000000 y obtengo una dirección de retorno válida. Pero si configuro el desplazamiento en alguna dirección aleatoria, digamos 0x00000000000000ff, no obtengo una dirección válida. ¿Tengo que establecer la dirección en el límite de la página?

    – Vinay

    10 de agosto de 2012 a las 19:14

  • ARM requiere el uso de un límite de página para mmap(). A partir de una pequeña prueba en IA32, parece ser el caso allí también … (y lo has probado en el x86_64, supongo). Además, escriba su comentario sobre el acceso a la primera página de estructura, con CONFIG_STRICT_DEVMEM desactivado, pensé que funcionaría (en un límite de página). No estoy seguro de esto.

    – Kaiwán

    12 de agosto de 2012 a las 3:11


  • @Vinay, puede deshabilitar la verificación. Por ejemplo, siempre haga devmem_is_allowed volver 1. Sé que es un truco. 😉

    – chenwj

    6 de septiembre de 2012 a las 8:13


  • @trblnc ¡Gracias! su implementación parece simple: hacer mmap a especial va codificada de forma rígida Mantengo un software más generalizado que se puede usar para obtener acceso r/w a cualquier memoria (MMIO/registros/RAM) en modo de usuario; Por favor echa un vistazo: github.com/kaiwan/device-memory-readwrite

    – Kaiwán

    16 de febrero de 2017 a las 12:37

En arquitecturas x86 con compatibilidad con PAT (tabla de atributos de página), el kernel puede evitar la asignación de regiones DRAM (incluso si se compila sin configurar CONFIG_NONPROMISC_DEVMEM).

La razón de esto como se menciona en el fuente del núcleo es:

This check is nedded to avoid cache aliasing when PAT is enabled

Esta verificación hará que aparezca un error similar en dmesg como el mencionado en la respuesta de kaiwan arriba arriba. Por ejemplo:

Program a.out tried to access /dev/mem between [mem 68200000-68201000].

Esta restricción se puede eliminar desactivando PAT.

PAT se puede deshabilitar agregando el nopat argumento a la línea de comandos del núcleo en el momento del arranque.

  • Esta respuesta (¡correcta!) parece haber sido copiada y pegada en la respuesta anterior por un usuario anónimo 8 minutos después de su respuesta. Si desea que se elimine, hágamelo saber y lo editaré, ya que lo considero injusto si se hace sin su consentimiento.

    – Ciro Santilli Путлер Капут 六四事

    16 de julio de 2017 a las 6:25

  • Fui yo. Yo fui quien lo agregó a la respuesta anterior y luego lo agregó como una respuesta separada porque (aparentemente) no sé cómo usar la herramienta: D.

    – Safayet Ahmed

    17 de julio de 2017 a las 12:15

  • ¡OK! Avíseme si desea que se elimine de la respuesta anterior. Salud.

    – Ciro Santilli Путлер Капут 六四事

    17 de julio de 2017 a las 12:38


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad