diff --git a/study/kernel/memory/kv2p/1.c b/study/kernel/memory/kv2p/1.c new file mode 100644 index 0000000..207a70e --- /dev/null +++ b/study/kernel/memory/kv2p/1.c @@ -0,0 +1,18 @@ +/* + * Physical vs virtual RAM address space conversion. These are + * private definitions which should NOT be used outside memory.h + * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. + */ +#define __virt_to_phys(x) ({ \ + phys_addr_t __x = (phys_addr_t)(x); \ + __x & BIT(VA_BITS - 1) ? (__x & ~PAGE_OFFSET) + PHYS_OFFSET : \ + (__x - kimage_voffset); }) + +#define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) | PAGE_OFFSET) +#define __phys_to_kimg(x) ((unsigned long)((x) + kimage_voffset)) + +/* + * Convert a page to/from a physical address + */ +#define page_to_phys(page) (__pfn_to_phys(page_to_pfn(page))) +#define phys_to_page(phys) (pfn_to_page(__phys_to_pfn(phys))) \ No newline at end of file diff --git a/study/kernel/memory/kv2p/Makefile b/study/kernel/memory/kv2p/Makefile new file mode 100644 index 0000000..4250a02 --- /dev/null +++ b/study/kernel/memory/kv2p/Makefile @@ -0,0 +1,14 @@ + + +obj-m := kv2p.o + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build + +PWD := $(shell pwd) + +all: + make -C $(KERNELDIR) M=$(PWD) modules + +clean: + make -C $(KERNELDIR) M=$(PWD) clean + diff --git a/study/kernel/memory/kv2p/README.md b/study/kernel/memory/kv2p/README.md new file mode 100644 index 0000000..2b80deb --- /dev/null +++ b/study/kernel/memory/kv2p/README.md @@ -0,0 +1,148 @@ +| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN | +| ------------- |:-------------:|:-------------:|:-------------:|:-------------:|:-------------:| +| 2016-07-21 | [Linux-4.7](http://lxr.free-electrons.com/source/?v=4.7) | X86 & arm | [gatieme](http://blog.csdn.net/gatieme) | [LinuxDeviceDrivers](https://github.com/gatieme/LDD-LinuxDeviceDrivers) | [Linux进程管理与调度](http://blog.csdn.net/gatieme/article/category/6225543) | + + + +#1 内核虚拟地址与物理地址的转换 +------- + +| 函数 | 描述 | 底层函数 | +|:---:|:---:|:--------:| +| phys_addr_t virt_to_phys(volatile void *address) | 将内核的虚拟地址转换为物理地址 | \__pa或者 \__virt_to_phys | +| void *phys_to_virt(phys_addr_t x) | 将物理地址转换为虚拟内核 | \__va或者\__phys_to_virt | + +其中phy_addr_t用以表示一个内存地址, 定义在[include/linux/types.h](http://lxr.free-electrons.com/source/include/linux/types.h#L161)中 + +```c +#ifdef CONFIG_PHYS_ADDR_T_64BIT + typedef u64 phys_addr_t; +#else + typedef u32 phys_addr_t; +#endif +``` + +可以通过搜索我们内核的.config文件搜索CONFIG_PHYS_ADDR_T_64BIT这个配置参数 + +```c +cat /usr/src/linux-headers-4.2.0-36-generic/.config | grep CONFIG_PHYS_ADDR_T_64BIT +``` + +![CONFIG_PHYS_ADDR_T_64BIT参数](./images/CONFIG_PHYS_ADDR_T_64BIT.png) + + +#2 virt_to_phys/phys_to_virt实现 +------- + +##2.1 不同架构的实现 +------- + + +| 架构 | 定义 | +|:-------:|:-------:| +| 标准 | [include/asm-generic/io.h, line 724](http://lxr.free-electrons.com/source/include/asm-generic/io.h?v4.7#L724) +| arm | [arch/arm/include/asm/memory.h, line 272](http://lxr.free-electrons.com/source/arch/arm/include/asm/memory.h?v4.7#L272) | +| arm64 | [arch/arm64/include/asm/memory.h](http://lxr.free-electrons.com/source/arch/arm64/include/asm/memory.h?v4.7#L189) | +| x86 |[arch/x86/include/asm/io.h, line 118](http://lxr.free-electrons.com/source/arch/x86/include/asm/io.h?v4.7#L118) + + + +* virt_to_phys函数在内核中是通过\__pa或者\__virt_to_phys函数实现的 + +* phys_to_virt函数在内核中是通过\__va或者\__phys_to_virt函数实现的 + + + + +##2.2 arm64上的实现 + +不同架构的实现有细微差别, 但是其基本思路是一致的 + + +下面我们就以arm64上的实现为例进行讲解, 定义在[arch/arm64/include/asm/memory.h](http://lxr.free-electrons.com/source/arch/arm64/include/asm/memory.h?v4.7#L184) + +```c +/* + * These are *only* valid on the kernel direct mapped RAM memory. + * Note: Drivers should NOT use these. They are the wrong + * translation for translating DMA addresses. Use the driver + * DMA support - see dma-mapping.h. + */ +#define virt_to_phys virt_to_phys +static inline phys_addr_t virt_to_phys(const volatile void *x) +{ + return __virt_to_phys((unsigned long)(x)); +} + +#define phys_to_virt phys_to_virt +static inline void *phys_to_virt(phys_addr_t x) +{ + return (void *)__phys_to_virt(x); +} + +/* + * Drivers should NOT use these either. + */ +#define __pa(x) __virt_to_phys((unsigned long)(x)) +#define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) +#define pfn_to_kaddr(pfn) __va((phys_addr_t)(pfn) << PAGE_SHIFT) +``` + + +可以看到真是的实现是__virt_to_phys和__phys_to_virt函数实现的 + + + +##2.3 __virt_to_phys函数 +------- + +__virt_to_phys函数定义在[arch/arm64/include/asm/memory.h, line 109](http://lxr.free-electrons.com/source/arch/arm64/include/asm/memory.h?v=4.7#L109) + +```c +/* + * Physical vs virtual RAM address space conversion. These are + * private definitions which should NOT be used outside memory.h + * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. + */ +#define __virt_to_phys(x) ({ \ + phys_addr_t __x = (phys_addr_t)(x); \ + __x & BIT(VA_BITS - 1) ? (__x & ~PAGE_OFFSET) + PHYS_OFFSET : \ + (__x - kimage_voffset); }) + +``` + + +##2.4 __phys_to_virt函数 +------- + +__phys_to_virt函数定义在[arch/arm64/include/asm/memory.h, line 114](http://lxr.free-electrons.com/source/arch/arm64/include/asm/memory.h?v=4.7#L114) + + +```c +#define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) | PAGE_OFFSET) +``` + +##2.5 __phys_to_kimg函数 +------- + +```c +#define __phys_to_kimg(x) ((unsigned long)((x) + kimage_voffset)) +``` + +##2.6 page_to_phys和phys_to_page函数 +------- + +```c +/* + * Convert a page to/from a physical address + */ +#define page_to_phys(page) (__pfn_to_phys(page_to_pfn(page))) +#define phys_to_page(phys) (pfn_to_page(__phys_to_pfn(phys))) +``` + + + + + + + diff --git a/study/kernel/memory/kv2p/images/CONFIG_PHYS_ADDR_T_64BIT.png b/study/kernel/memory/kv2p/images/CONFIG_PHYS_ADDR_T_64BIT.png new file mode 100644 index 0000000..54aec24 Binary files /dev/null and b/study/kernel/memory/kv2p/images/CONFIG_PHYS_ADDR_T_64BIT.png differ diff --git a/study/kernel/memory/kv2p/kv2p.c b/study/kernel/memory/kv2p/kv2p.c new file mode 100644 index 0000000..f378c94 --- /dev/null +++ b/study/kernel/memory/kv2p/kv2p.c @@ -0,0 +1,44 @@ +/***************************************************************** +文件名:mem.c +输入参数: +pid 接收待查询进程的PID +va 接收待查询的虚拟地址 +*****************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + + +static unsigned long VA; +module_param(VA, ulong, 0644); + + + +static int __init kv2p_init( ) +{ + // use virt_to_phys + // http://lxr.free-electrons.com/source/arch/arm64/include/asm/memory.h?v4.7#L189 + // + virt_to_phys(VA); + + + return 0; +} + + + +static void __exit kv2p_exit(void) +{ + printk(KERN_INFO"Goodbye!\n"); +} + +module_init(kv2p_init); +module_exit(kv2p_exit); diff --git a/study/kernel/memory/v2p/v2p.c b/study/kernel/memory/v2p/v2p.c index 0c18cdc..d3ab710 100644 --- a/study/kernel/memory/v2p/v2p.c +++ b/study/kernel/memory/v2p/v2p.c @@ -133,7 +133,6 @@ static int find_pgd_init(void) static void find_pgd_exit(void) { printk(KERN_INFO"Goodbye!\n"); - } module_init(find_pgd_init);