基于linux4.14.79, TI AM5728 SDK。
phys:physical物理地址
pfn:物理页帧号(也有叫页框号的?)
page:描述物理页的结构体
virt:virtual虚拟地址
#define PAGE_SHIFT 12
/* 物理内存起始地址 */
#define PHYS_OFFSET ((phys_addr_t)__pv_phys_pfn_offset << PAGE_SHIFT)
/* 物理内存起始页帧号 */
#define PHYS_PFN_OFFSET (__pv_phys_pfn_offset)
#define ARCH_PFN_OFFSET PHYS_PFN_OFFSET
/* 内核虚拟空间起始地址 */
#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
/* 物理内存page结构体指针,指向第一个page结构体 */
struct page *mem_map;
phys <=> pfn :
#define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT))
#define PFN_PHYS(x) ((phys_addr_t)(x) << PAGE_SHIFT)
pfn <=> page:
#define page_to_pfn __page_to_pfn
#define pfn_to_page __pfn_to_page
#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET))
#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \
ARCH_PFN_OFFSET)
pfn - ARCH_PFN_OFFSET表示物理内存对应的页帧号。mem_map是物理内存的page结构体存放地址,加上物理内存的页帧号得到对应物理页的page结构体存放地址(虚拟)。
page <=> virt:
虚拟地址和page结构体地址的转换需要区分线性映射区和vmalloc区(vmalloc/ioremap动态映射,以及使用iotable_init()的机器相关的静态映射),前者属于低端内存,后者属于高端内存。
关于高端内存和低端内存的介绍:https://mp.csdn.net/console/editor/html/100771734。
线性映射区:
线性映射区page 和 virt的转换需要先转换为pfn, page和pfn的转换前面已经介绍了,下面时 virt 和 pfn的转换(同样只适用于线性映射区),根据不同的
/* virt_to_pfn的一种简单实现 */
#define virt_to_pfn(kaddr) \
((((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT) + \
PHYS_PFN_OFFSET)
/* 下面是常规的 */
#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT)
#define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT)
#define __pa(x) __virt_to_phys((unsigned long)(x))
#define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x)))
#define __virt_to_phys(x) __virt_to_phys_nodebug(x)
/* 这2个函数根据不同的配置,实现不一样 */
static inline unsigned long __phys_to_virt(phys_addr_t x);
static inline phys_addr_t __virt_to_phys_nodebug(unsigned long x);
/* 下面是一种实现 */
static inline phys_addr_t __virt_to_phys_nodebug(unsigned long x)
{
return (phys_addr_t)x - PAGE_OFFSET + PHYS_OFFSET;
}
static inline unsigned long __phys_to_virt(phys_addr_t x)
{
return x - PHYS_OFFSET + PAGE_OFFSET;
}
virt和page的转换:
#define virt_to_page(addr) pfn_to_page(virt_to_pfn(addr))
#define page_to_virt(page) pfn_to_virt(page_to_pfn(page))
vmalloc区:
struct page *vmalloc_to_page(const void *vmalloc_addr);