Je suis en train d'accéder à la mémoire physique directement pour un Linux embarqué projet, mais je ne suis pas sûr de savoir comment je peux désigner mémoire pour mon utilisation.
Si je démarre mon appareil régulièrement, et d'accéder à /dev/mem, je peux facilement lire et écrire à peu près n'importe où je veux. Toutefois, dans ce domaine, je suis accès à de la mémoire qui peut facilement être attribuée à n'importe quel procédé; que je ne veux pas faire
Mon code pour /dev/mem est (tout de vérification d'erreur, etc. supprimé):
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);
Et cela fonctionne. Cependant, j'aimerais utiliser de la mémoire que personne ne le touche. J'ai essayé de limiter la quantité de mémoire que le noyau voit en démarrant avec mem=XXXm, puis en définissant BASE_ADDRESS à quelque chose au-dessus, mais en dessous de la mémoire physique), mais il ne semble pas à l'accès à la mémoire même de manière cohérente.
Basé sur ce que j'ai vu en ligne, je pense que je peut avoir besoin d'un module du noyau (qui est OK) qui utilise soit ioremap() ou remap_pfn_range() (ou les deux???), mais je n'ai absolument aucune idée de comment; quelqu'un peut-il aider?
EDIT: Ce que je veux, c'est toujours une façon d'accéder à la même mémoire physique (par exemple, 1.5 MO), et le jeu de mémoire côté de sorte que le noyau ne pas affecter à un autre processus.
Je suis en train de reproduire un système que nous avions dans d'autres Systèmes d'exploitation (sans la gestion de la mémoire) dans lequel j'ai pu allouer de l'espace dans la mémoire via l'éditeur de liens, et d'y accéder en utilisant quelque chose comme
*(unsigned char *)0x12345678
EDIT2: Je suppose que je devrais fournir plus de détails. Cet espace mémoire sera utilisée pour mémoire tampon pour une haute performance de la solution d'enregistrement pour une application embarquée. Dans les systèmes que nous avons, il n'y a rien qui efface ou brouille la mémoire physique lors d'un redémarrage logiciel. Ainsi, si j'écris un peu à une adresse physique X, et redémarrez le système, les mêmes bits seront toujours ensemble après le redémarrage de l'ordinateur. Cela a été testé sur exactement le même matériel en cours d'exécution VxWorks (cette logique fonctionne aussi bien dans le Noyau RTOS et OS20 sur différentes plates-formes, FWIW). Mon idée était d'essayer la même chose sous Linux par l'adressage de la mémoire physique directement; par conséquent, il est essentiel que je reçois la même adresse à chaque démarrage.
Dois-je préciser que c'est pour le noyau 2.6.12 et les plus récents.
EDIT3: Voici mon code, tout d'abord pour le module du noyau, puis de l'application utilisateur.
Pour l'utiliser, je démarre avec mem=95m, puis insmod foo-module.ko, puis mknod mknod /dev/foo c 32 0, puis exécutez foo-utilisateur , où il meurt. Fonctionnant sous gdb montre qu'il meurt à la mission, bien que dans gdb, je ne peux pas déréférencer l'adresse que je reçois de mmap (bien que printf peut)
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-utilisateur.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;
}