星期四, 4月 14, 2011

ARM based 的 __pa() 與 __va()

Linux 的 memory 分為:

1. user space virtual address
2. physical address (hardware)
3. bus address (IO)
4. kernel logical address
5. kernel virtual address


__pa(x) 與 __va(x) 是用來轉換 kernel logical address 與 <-> physical address 用的 macro, 由 <asm/page.h> 定義。
<->
通常 logical 跟 physical 只差在一個固定量的常數,以 ARM 為例為 PAGE_OFFSET,往下追蹤這兩個 macro 可找到:
 
<-><arch/arm/include/asm/memory.h>


#define __pa(x)   __virt_to_phys((unsigned long)(x))
#define __va(x)   ((void *)__phys_to_virt((unsigned long)(x)))


可知:
  __pa(x) ==> __virt_to_phys,  __va(x) ==> __phys_to_virt.

繼續追蹤,可以在同一個檔案看到如下這些定義:


#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)

在較舊版本 Linux 中, PAGE_OFFSET 可能會直接指定, 但在較新版本 Linux 如 2.6.36 中可能會看到如下定義:

#define PAGE_OFFSET  UL(CONFIG_PAGE_OFFSET)

看到 "CONFIG_" 開頭就不用想了, 這表示會在 autoconf.h 裡, 以 2.6.36 版為例可在 include/generated/autoconf.h 找到如下定義:
(舊版本 autoconf.h 路徑會有所不同)

#define CONFIG_PAGE_OFFSET 0xC0000000

而 PHYS_OFFSET 則依照平台不同而有所變化, 以 PXA 系列為例可以在下面檔案找到:

arch/arm/mach-pxa/include/mach/memory.h

定義內容如下:

#define PHYS_OFFSET UL(0xa0000000)

所以我們可以由此推算 logical 轉 physical 公式為:

physical = logical - 0xc0000000 + 0xa0000000

以 linux kernel 進入點為 0xc0008000 來說, 其對應的 physical address 就是 0xa0008000

然後我們就可以對照 System.map 這檔案的列表, 把想要的東西 dump 出來看,
如 Embedded Linux Primer 裡提到的利用 __log_buf 來除錯.

實際上來說, 0xa0000000 是 PXA270 的 physical address (Partitoin 0),
在 Marvell PXA310 EVM 中定義的 physical address 是 0x80000000,
Marvell PXA3xx Datasheet 的定義:

1.6.2.2 Dynamic Memory Controller Memory Map

Chip Select Address Space
nSDCS<1> (PXA32x processor only) 0xC000_0000 – 0xFFFF_FFFF
nSDCS<1> (PXA30x and PXA31x Only) 0xC000_0000 – 0xDFFF_FFFF
nSDCS<0> (PXA32x processor only) 0x8000_0000 – 0xBFFF_FFFF
nSDCS<0> (PXA30x and PXA31x only) 0x8000_0000 – 0x9FFF_FFFF

也因此如果自己拿新版 kernel 來 build 放到 EVM 上, 基本上只會看到
Uncompressing Linux... done, booting the kernel. 然後就當了.
知道原因之後, 只要把 0xa0000000 改成 0x80000000 就好了, 如下:

修改
#define PHYS_OFFSET UL(0xa0000000)

變成
#ifdef CONFIG_PXA3xx
#define PHYS_OFFSET UL(0x80000000)
#else
#define PHYS_OFFSET UL(0xa0000000)
#endif

沒有留言: