7

メモリ マップド デバイスが接続され、ARM CPU が Linux を実行する組み込みシステムがあります。デバイスは address0x40400000にあり、1 メガバイトを占有します (そのほとんどは実際のメモリに支えられていませんが、アドレス空間はとにかくデバイスにマップされています)。現在、このデバイス用のデバイス ドライバーはありません。

デバイスには、アドレス に特別な読み取り専用レジスタ (CID と呼ばれる) があります0x404f0704。このレジスタには値が含まれますCID = 0x404。ARM で実行されているプログラムからこのレジスタを読み取ろうとしています。

mmap()ネットを検索すると、ユーザー空間から物理アドレスにアクセスできると思われる機能について知りました。それで、私が見つけたいくつかの例に従おうとして、次のテストを書きました:


#include <sys/mman.h>
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    void          *pdev = (void *) 0x40400000;
    size_t         ldev = (1024*1024);
    int           *pu;
    int  volatile *pcid;
    int  volatile  cid;

    pu = mmap(pdev, ldev, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
    if (pu == MAP_FAILED)
        errx(1, "mmap failure");

    pcid = (int *) (((void *) pu) + 0xf0704);

    printf("pu    = %08p\n", pu);
    printf("pcid  = %08p\n", pcid);

    cid = *pcid;
    printf("CID   = %x\n", cid);

    munmap(pu, ldev);

    return (EXIT_SUCCESS);
}

ARM クロスコンパイラを使用したコンパイル:

a-gcc -O0 -g3 -o mmap-test.elf mmap-test.c

期待した結果が得られません。私が見ているのは次のとおりです。

pu   = 0x40400000
pcid = 0x404f0704
CID  = 0

予想の代わりに

CID  = 404

ここで何が欠けていますか/間違っていますか?


アップデート:

別のデモ プログラムを見つけ、そのコードに従ってコードを動作させることができました。


int main(void)
{
    off_t          dev_base = 0x40400000;
    size_t         ldev = (1024 * 1024);
    unsigned long  mask = (1024 * 1024)-1;
    int           *pu;
    void          *mapped_base;
    void          *mapped_dev_base;
    int  volatile *pcid;
    int  volatile  cid;
    int            memfd;

    memfd = open("/dev/mem", O_RDWR | O_SYNC);
    mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, dev_base & ~MAP_MASK);
    if (mapped_base == MAP_FAILED)
        errx(1, "mmap failure");
    mapped_dev_base = mapped_base + (dev_base & MAP_MASK);
    pu = mapped_dev_base;

    pcid = (int *) (((void *) pu) + 0xf0704);

    printf("pu    = %08p\n", pu);
    printf("pcid  = %08p\n", pcid);

    cid = *pcid;
    printf("CID   = %x\n", cid);

    munmap(mapped_base, ldev);
    close(memfd);

    return (EXIT_SUCCESS);
}

それでも、最初のバージョンが機能しなかった理由はよくわかりません。私の理解では、一度使用MAP_ANONYMOUSすると、マッピング用のファイル ハンドルは必要ありません。また、私は明らかにaddr引数 (pepi私の最初のバージョン) を物理アドレスと間違えました。私が今いる場合、これは実際には仮想アドレスです。

4

1 に答える 1

7

Mmap は通常、仮想アドレスで機能する関数です。mmap(... MAP_ANONYMOUS)(またはファイルmmapを)呼び出す/dev/zeroと、ゼロで満たされた新しい仮想メモリがいくらか提供されます。返されるアドレスは仮想メモリのアドレスになります。

一部のファイルを (MAP_ANONYMOUS なしで) mmap すると、mmap はファイルの内容を仮想メモリ範囲にマップします。

デバイスはアドレス 0x40400000 にあります

デバイス MMIO は物理メモリにあります。どのプロセスでも仮想アドレス 0x40400000 を使用できます。ただし、MMU (メモリ管理ユニット) によって空き物理ページにマップ (変換) されます。OSに仮想メモリを要求するだけで、それがデバイス範囲にmmapされると期待することはできません(地獄の変種になります)。

ただし、すべての物理メモリを含むファイルとして使用できる特別なデバイス /dev/mem があります。/dev/mem を実行mmapすると、実際には、要求された物理範囲への仮想メモリの新しいマッピングを作成するよう OS に要求しています。

mmap の呼び出しで:

 mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, 
   MAP_SHARED, memfd, dev_base & ~MAP_MASK);

物理メモリ範囲[0x40400000 .. 0x4050000-1](1 メガバイト。バイト 0x40500000 を含まない) を数メガバイトの仮想メモリにマップするように要求します (その開始アドレスは mmap によって返されます)。

于 2012-10-10T23:44:00.167 に答える