Pergunta

Estou tentando acessar memória física diretamente para um projeto de Linux embutido, mas eu não sei como eu posso memória melhor designado para meu uso.

Se eu arrancar meu dispositivo regularmente, e acesso / dev / mem, eu posso facilmente ler e gravar em qualquer lugar que eu quero. No entanto, neste, eu estou acessando memória que pode ser facilmente atribuídos a qualquer processo; que eu não quero fazer

Meu código para / dev / mem é (toda a verificação de erros, etc. removido):

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);

E isso funciona. No entanto, eu gostaria de estar usando a memória que ninguém mais vai tocar. Eu tentei limitar a quantidade de memória que o kernel vê por arrancar com mem = xxxM, e em seguida, definindo BASE_ADDRESS a algo acima disso (mas abaixo da memória física), mas não parece estar acessando a mesma memória de forma consistente.

Com base no que tenho visto on-line, eu suspeito que pode precisar de um módulo do kernel (que é OK) que utiliza quer ioremap () ou remap_pfn_range () (ou ambos ???), mas eu não tenho absolutamente nenhuma idéia de como ; alguém pode ajudar?

EDIT: O que eu quero é uma maneira de sempre acessar a mesma memória física (por exemplo, no valor de 1.5MB) e set que a memória de lado para que o kernel não alocá-lo para qualquer outro processo.

Eu estou tentando reproduzir um sistema que tínhamos em outros sistemas operacionais (sem gerenciamento de memória) no qual eu poderia alocar um espaço na memória através do ligante, e acessá-lo usando algo como

*(unsigned char *)0x12345678

EDIT2: Eu acho que deveria fornecer mais alguns detalhes. Este espaço de memória será utilizada para um tampão de RAM para uma solução de log elevado desempenho para uma aplicação incorporado. Nos sistemas que temos, não há nada que apura ou embaralha memória física durante a reinicialização. Assim, se eu escrever um pouco para um endereço físico X, e reiniciar o sistema, o mesmo bit ainda será definido após a reinicialização. Este foi testado em exatamente o mesmo hardware rodando VxWorks (esta lógica também funciona muito bem em Nucleus RTOS e OS20 em diferentes plataformas, FWIW). Minha idéia era tentar a mesma coisa em Linux, abordando memória física diretamente; portanto, é essencial que eu recebo os mesmos endereços cada boot.

Eu provavelmente deveria esclarecer que este é para o kernel 2.6.12 e mais recentes.

EDIT3: Aqui está o meu código, primeiro para o módulo do kernel, em seguida, para a aplicação userspace.

Para usá-lo, eu boot com mem = 95m, então insmod foo-módulo.ko, então mknod mknod / dev / foo c 32 0, em seguida, executar foo-usuário, onde ele morre. Rodando sob mostra GDB que morre na atribuição, embora dentro gdb, eu não posso excluir a referência o endereço que recebo de mmap (embora printf pode)

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;
}
Foi útil?

Solução

Eu acho que você pode encontrar uma grande quantidade de documentação sobre a kmalloc + mmap parte. No entanto, não estou certo de que você pode kmalloc tanta memória de forma contígua, e tê-lo sempre no mesmo lugar. Claro, se tudo é sempre o mesmo, então você pode obter um endereço constante. No entanto, cada vez que você alterar o código do kernel, você vai ter um endereço diferente, então eu não iria com a solução kmalloc.

Eu acho que você deve reservar alguma memória em tempo de boot, ou seja, reservar alguma memória física para que se não é tocado pelo kernel. Então você pode ioremap essa memória que lhe dará um endereço virtual do kernel, e então você pode mmap-lo e escrever um driver de dispositivo legal.

Esta levar-nos de volta para Linux drivers de dispositivo em formato PDF. Dê uma olhada no capítulo 15, ele está descrevendo esta técnica na página 443

Edit: ioremap e mmap. Acho que isso pode ser mais fácil de depuração fazer as coisas em duas etapas: primeiro obter a ioremap direita, e testá-lo usando uma operação de dispositivo de caracteres, ou seja, de leitura / gravação. Uma vez que você sabe que você pode seguramente ter acesso a toda a memória ioremapped usando leitura / escrita, em seguida, tentar mmap toda a gama ioremapped.

E se você ficar em apuros pode ser postar uma outra pergunta sobre mmaping

Edit: remap_pfn_range ioremap retorna um virtual_adress, que você deve converter para um pfn para remap_pfn_ranges. Agora, eu não entendo exatamente o que um pfn (Página Quadro Number) é, mas acho que você pode obter um chamando

virt_to_phys(pt) >> PAGE_SHIFT

Este provavelmente não é o caminho certo (tm) para fazê-lo, mas você deve tentar

Você também deve verificar se FOO_MEM_OFFSET é o endereço físico do seu bloco de RAM. Ou seja, antes de qualquer coisa acontece com o mmu, sua memória está disponível a 0 no mapa de memória do seu processador.

Outras dicas

Desculpe a resposta, mas não completamente responder, eu notei que você já editou a questão. Por favor note que SO não notificar-nos quando você editar a pergunta. Eu estou dando uma resposta genérica aqui, quando você atualizar a questão, por favor deixe um comentário, então eu vou editar a minha resposta.

Sim, você vai precisar para escrever um módulo. O que lhe vem para baixo é o uso de kmalloc() (atribuição de uma região no espaço kernel) ou vmalloc() (atribuição de uma região no espaço do usuário).

A exposição da prévia é fácil, expondo este último pode ser uma dor na parte traseira com o tipo de interface que você está descrevendo, conforme necessário. Você notou 1,5 MB é uma estimativa aproximada de quanto você realmente precisa de reserva, é que vestida de ferro? Ou seja você está tomando confortável que a partir do espaço kernel? você pode lidar adequadamente com ENOMEM ou EIO de userspace (ou dormir rígido mesmo)? IOW, o que está acontecendo nessa região?

Além disso, é concorrência vai ser um problema com isso? Se assim for, você vai estar usando um futex? Se a resposta a qualquer for 'sim' (especialmente o último), é bem provável que você vai ter que morder a bala e ir com vmalloc() (ou semente podre de risco a partir de dentro). Além disso, se você está mesmo pensando em uma interface ioctl() ao dispositivo char (especialmente para alguma idéia ad-hoc locking), você realmente quer ir com vmalloc().

Além disso, você já leu este ? Além disso, não estamos sequer tocar no que grsec / selinux vai pensar deste (se estiver em uso).

/ dev / mem é bom para espreita registo simples e puxões, mas uma vez que você cruzar para interrupções e território DMA, você realmente deve escrever um driver de modo kernel. O que você fez para os SOs seu anterior memória de gestão de menos simplesmente não enxerto bem em um sistema operacional de propósito geral como o Linux.

Você já pensou sobre o problema de alocação de buffer DMA. Agora, pense sobre o "DMA feito" interrupção do seu dispositivo. Como você está indo para instalar uma rotina de interrupção de serviço?

Além disso, / dev / mem normalmente é bloqueado para usuários não-root, por isso não é muito prático para uso geral. Claro, você poderia chmod-lo, mas depois que você abriu um grande buraco de segurança no sistema.

Se você está tentando manter a base de código do driver semelhante entre os sistemas operacionais, você deve considerar refatoração-lo em usuário separado e camadas de modo kernel com um IOCTL-como interface in-between. Se você escrever a parte do modo de usuário como uma biblioteca genérica de código C, que deve ser fácil para a porta entre o Linux e outros sistemas operacionais. A parte específica do OS é o código de modo kernel. (Nós usamos este tipo de abordagem para os nossos motoristas.)

Parece que você já concluiu que é hora de escrever um kernel-piloto, então você está no caminho certo. O único conselho que posso acrescentar é ler esses livros capa a capa.

Linux Device Drivers

Compreender a Linux Kernel

(Tenha em mente que esses livros são circa-2005, assim que a informação é um pouco datado.)

Eu sou, de longe, nenhum perito sobre estas questões, então esta será uma pergunta para você, em vez de uma resposta. Existe algum motivo você não pode simplesmente fazer uma partição do disco ram pequeno e usá-lo apenas para a sua aplicação? Oxalá não lhe dá a garantia de acesso para o mesmo pedaço de memória? Eu não tenho certeza de que haveria quaisquer problemas de desempenho de I / O, ou uma sobrecarga adicional associados fazendo isso. Isso também pressupõe que você pode dizer ao kernel para particionar um intervalo de endereço específico na memória, não tenho certeza se isso é possível.

Peço desculpas pela pergunta newb, mas eu achei a sua pergunta interessante, e estou curioso para saber se o disco de RAM poderia ser usado em tal maneira a.

Você já olhou para o 'memmap' kernel do parâmetro? Em i386 e X64_64, você pode usar o parâmetro memmap para definir como o kernel vai entregar blocos muito específicos de memória (consulte o Linux kernel do parâmetro documentação). No seu caso, você iria querer marca memória como 'reservados' para que o Linux não tocá-lo em tudo. Em seguida, você pode escrever seu código para usar esse endereço absoluto e tamanho (ai de você se você pisar fora desse espaço).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top