仮想アドレス <-> 物理アドレス変換

Back


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 
#include 

#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 

#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 
#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

August 21,2000
Hidetada Baba <baba@daq.rikkyo.ac.jp>