Estoy intentando acceder a la memoria física directamente para un proyecto de Linux incorporado, pero no estoy seguro de cómo puedo designar la mejor memoria para mi uso.Acceso directo a memoria en Linux
Si reinicio mi dispositivo regularmente y accedo a/dev/mem, puedo leer y escribir fácilmente en cualquier lugar que desee. Sin embargo, en esto, estoy accediendo a la memoria que se puede asignar fácilmente a cualquier proceso; cosa que no quiero hacer
Mi código para/dev/mem es (todo comprobación de errores, etc. eliminado):
mem_fd = open("/dev/mem", O_RDWR));
mem_p = malloc(SIZE + (PAGE_SIZE - 1));
if ((unsigned long) mem_p % PAGE_SIZE) {
mem_p += PAGE_SIZE - ((unsigned long) mem_p % PAGE_SIZE);
}
mem_p = (unsigned char *) mmap(mem_p, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, mem_fd, BASE_ADDRESS);
Y esto funciona. Sin embargo, me gustaría usar memoria que nadie más tocará. Intenté limitar la cantidad de memoria que ve el kernel arrancando con mem = XXXm, y luego estableciendo BASE_ADDRESS en algo por encima de eso (pero debajo de la memoria física), pero parece que no está accediendo a la misma memoria de manera consistente.
Basado en lo que he visto en línea, sospecho que puedo necesitar un módulo kernel (que está bien) que utiliza ya sea ioremap() o remap_pfn_range() (o ambos ???), pero no tengo ni idea cómo; ¿Alguien puede ayudar?
EDIT: Lo que quiero es una forma de acceder siempre a la misma memoria física (digamos, 1.5MB vale), y dejar esa memoria a un lado para que el kernel no la asigne a ningún otro proceso.
Estoy tratando de reproducir un sistema que teníamos en otros sistemas operativos (sin gestión de memoria), por lo que pude asignar un espacio en la memoria mediante el engarce, y acceder a ella usando algo como
*(unsigned char *)0x12345678
Edit2: Supongo que debería proporcionar más detalles. Este espacio de memoria se usará para un búfer RAM para una solución de registro de alto rendimiento para una aplicación incorporada. En los sistemas que tenemos, no hay nada que borre o codifique la memoria física durante un reinicio suave. Por lo tanto, si escribo un bit en una dirección física X y reinicio el sistema, el mismo bit se establecerá después del reinicio. Esto ha sido probado en el mismo hardware exacto que ejecuta VxWorks (esta lógica también funciona muy bien en Nucleus RTOS y OS20 en diferentes plataformas, FWIW). Mi idea era probar lo mismo en Linux al abordar directamente la memoria física; por lo tanto, es esencial que obtenga las mismas direcciones en cada arranque.
Probablemente debería aclarar que esto es para kernel 2.6.12 y posteriores.
EDIT3: Aquí está mi código, primero para el módulo kernel, luego para la aplicación del espacio de usuario.
Para usarlo, reinicio con mem = 95m, luego insmod foo-module.ko, luego mknod mknod/dev/foo c 32 0, luego ejecuto foo-user, donde muere. Se ejecuta bajo el BGF muestra que muere a la asignación, aunque dentro de gdb, no puedo eliminar la referencia de la dirección de E obtener de mmap (aunque puede printf)
foo-module.c
#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/io.h>
#define VERSION_STR "1.0.0"
#define FOO_BUFFER_SIZE (1u*1024u*1024u)
#define FOO_BUFFER_OFFSET (95u*1024u*1024u)
#define FOO_MAJOR 32
#define FOO_NAME "foo"
static const char *foo_version = "@(#) foo Support version " VERSION_STR " " __DATE__ " " __TIME__;
static void *pt = NULL;
static int foo_release(struct inode *inode, struct file *file);
static int foo_open(struct inode *inode, struct file *file);
static int foo_mmap(struct file *filp, struct vm_area_struct *vma);
struct file_operations foo_fops = {
.owner = THIS_MODULE,
.llseek = NULL,
.read = NULL,
.write = NULL,
.readdir = NULL,
.poll = NULL,
.ioctl = NULL,
.mmap = foo_mmap,
.open = foo_open,
.flush = NULL,
.release = foo_release,
.fsync = NULL,
.fasync = NULL,
.lock = NULL,
.readv = NULL,
.writev = NULL,
};
static int __init foo_init(void)
{
int i;
printk(KERN_NOTICE "Loading foo support module\n");
printk(KERN_INFO "Version %s\n", foo_version);
printk(KERN_INFO "Preparing device /dev/foo\n");
i = register_chrdev(FOO_MAJOR, FOO_NAME, &foo_fops);
if (i != 0) {
return -EIO;
printk(KERN_ERR "Device couldn't be registered!");
}
printk(KERN_NOTICE "Device ready.\n");
printk(KERN_NOTICE "Make sure to run mknod /dev/foo c %d 0\n", FOO_MAJOR);
printk(KERN_INFO "Allocating memory\n");
pt = ioremap(FOO_BUFFER_OFFSET, FOO_BUFFER_SIZE);
if (pt == NULL) {
printk(KERN_ERR "Unable to remap memory\n");
return 1;
}
printk(KERN_INFO "ioremap returned %p\n", pt);
return 0;
}
static void __exit foo_exit(void)
{
printk(KERN_NOTICE "Unloading foo support module\n");
unregister_chrdev(FOO_MAJOR, FOO_NAME);
if (pt != NULL) {
printk(KERN_INFO "Unmapping memory at %p\n", pt);
iounmap(pt);
} else {
printk(KERN_WARNING "No memory to unmap!\n");
}
return;
}
static int foo_open(struct inode *inode, struct file *file)
{
printk("foo_open\n");
return 0;
}
static int foo_release(struct inode *inode, struct file *file)
{
printk("foo_release\n");
return 0;
}
static int foo_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
if (pt == NULL) {
printk(KERN_ERR "Memory not mapped!\n");
return -EAGAIN;
}
if ((vma->vm_end - vma->vm_start) != FOO_BUFFER_SIZE) {
printk(KERN_ERR "Error: sizes don't match (buffer size = %d, requested size = %lu)\n", FOO_BUFFER_SIZE, vma->vm_end - vma->vm_start);
return -EAGAIN;
}
ret = remap_pfn_range(vma, vma->vm_start, (unsigned long) pt, vma->vm_end - vma->vm_start, PAGE_SHARED);
if (ret != 0) {
printk(KERN_ERR "Error in calling remap_pfn_range: returned %d\n", ret);
return -EAGAIN;
}
return 0;
}
module_init(foo_init);
module_exit(foo_exit);
MODULE_AUTHOR("Mike Miller");
MODULE_LICENSE("NONE");
MODULE_VERSION(VERSION_STR);
MODULE_DESCRIPTION("Provides support for foo to access direct memory");
foo-user.c
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
int main(void)
{
int fd;
char *mptr;
fd = open("/dev/foo", O_RDWR | O_SYNC);
if (fd == -1) {
printf("open error...\n");
return 1;
}
mptr = mmap(0, 1 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 4096);
printf("On start, mptr points to 0x%lX.\n",(unsigned long) mptr);
printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, *mptr);
mptr[0] = 'a';
mptr[1] = 'b';
printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, *mptr);
close(fd);
return 0;
}
Para aclarar, desea (en un módulo) devolver un espacio de direcciones al espacio de usuario adquirido a través de vmalloc(), no kmalloc(), ¿correcto? ¿Cuánta memoria realmente necesitas? –
Esto probablemente se hace más fácil con kmalloc(), lo que haría sería configurar 1,5 MB de espacio del kernel y presentarlo al espacio de usuario. Si eso es lo que quieres hacer, me refrescaré con unas cuantas entrañas de kernel e intentaré responder. –
Nota, hacer esto con vmalloc() puede ser una tarea extremadamente desagradable. La cantidad que realmente necesita asignar influye en la respuesta, por lo que está seguro de que es de 1,5 MB o menos? –