The BAQ/Memo/Linux C/仮想アドレス<->物理アドレス変換

Japanese English
Nuclear Physics Data Acquisition Orchestra Gallery Memo Diary Link Home
Linux C Fortran Postscript Home

もどる

最近のLinux(HIGHMEMを使うようなやつ?)では、phys_to_virtで変換したアドレスを直接アクセスすると怒られるので、きっちりioremapする必要があるっぽい。
以下は126MBから1MBマッピングする場合。

#define ADDRESS (126*0x1000000)
#define MAPSIZE (0x1000000)
char *ptr;

ptr = ioremap(ADDRESS, MAPSIZE)

以下は古い情報で、使えない場合があります。

DMA使用時などプログラムからデバイスにメモリアドレスを渡すときには仮想アドレスから物理アドレスに変換する必要がある。また、プログラム内でメモリアドレスを直接指定する場合物理アドレスを仮想アドレスに変換しなければならない。

仮想アドレス -> 物理アドレス 物理アドレス -> 仮想アドレス
virt_to_phys(unsigned long *address)
又は、__pa(address)
phys_to_virt(unsigned long address)
又は、__va(address)


これらの関数又はマクロは引数のaddressを仮想もしくは物理アドレスに変換して値を返す。深く考えずに

#define ADDRESS (126*0x1000000)
char *ptr;

ptr = phys_to_virt(ADDRESS);
とか
ptr = __va(ADDRESS);

のように使えばよい。
virt_to_phys、phys_to_virtは<asm/io.h>、__pa、__vaは<asm/page.h>に記述されている。

<asm/io.h>より

.
.
省略
.
.

#include <linux/vmalloc.h>
#include <asm/page.h>

#define __io_virt(x)            ((void *)(PAGE_OFFSET | (unsigned long)(x)))
#define __io_phys(x)            ((unsigned long)(x) & ~PAGE_OFFSET)
/*
 * Change virtual addresses to physical addresses and vv.
 * These are pretty trivial
 */
extern inline unsigned long virt_to_phys(volatile void * address)
{
        return __io_phys(address);
}

extern inline void * phys_to_virt(unsigned long address)
{
        return __io_virt(address);
}

.
.
省略
.
.

/*
 * IO bus memory addresses are also 1:1 with the physical address
 */
#define virt_to_bus virt_to_phys
#define bus_to_virt phys_to_virt

.
.
以下省略
.
.

これによると、virt_to_physとphys_to_physはそれぞれ本質的に__io_phys(x)、__io_virt(x)らしい。
またvirt_to_bus(= virt_to_phys)、bus_to_virt(= phys_to_virt)というのも用意されているようです。

<asm/page.h>より

.
.
省略
.
.

#include <asm/page_offset.h>

#define __PAGE_OFFSET		(PAGE_OFFSET_RAW)

#define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
#define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
#define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))

.
.
以下省略
.
.

これを見て解るとおりvirt_to_physやphys_to_virtと__pa、__vaは本質的に違いはありません。
違いと言えば面白いことにビット演算か加減をしていることである。
ちなみにPAGE_OFFSETはMemoryサイズに依存するということが<asm/page_offset.h>に記述されている。

<asm/io.h>より

#include <linux/config.h>
#ifdef CONFIG_1GB
#define PAGE_OFFSET_RAW 0xC0000000
#elif defined(CONFIG_2GB)
#define PAGE_OFFSET_RAW 0x80000000
#elif defined(CONFIG_3GB)
#define PAGE_OFFSET_RAW 0x40000000
#endif

Last Update: 2008/5/20
Hidetada Baba
baba ribf.riken.jp