linux 虚拟地址转物理地址

一、重点内容

  1. 页表翻译相关的硬件
  2. 通过软件将物理地址转换为虚拟地址

二、相关硬件

#include <stdio.h>
int main(void)
{
  printf("hello world\n");
}

三、物理地址

四、参考源码

#define pr_fmt(fmt) "xxx: " fmt 
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/slab.h>  
#include <linux/mm.h>  
#include <linux/init.h>  
#include <linux/highmem.h>  
#include <linux/io.h>
#include "ckret.h"

static void *vaddr;
#define MAGIC_VALUE			0xAABB

/**************************************************************************************************
 * @brief  : 手动计算物理地址
 * @param  : None
 * @return : 0成功, -1失败
**************************************************************************************************/
static phys_addr_t manu_to_phys(void *addr)
{
    pgd_t *pgd, *m_pgd;  
    p4d_t *p4d, *m_p4d;  
    pud_t *pud, *m_pud;  
    pmd_t *pmd, *m_pmd;  
    pte_t *pte, *m_pte;
	phys_addr_t paddr;

	/* 通过函数计算 */
	pgd = pgd_offset(current->mm, (unsigned long)addr);		// 获取pgd
	p4d = p4d_offset(pgd, (unsigned long)addr);				// 获取p4d
	pud = pud_offset(p4d, (unsigned long)addr);				// 获取pud
	pmd = pmd_offset(pud, (unsigned long)addr);				// 获取pmd
	pte = pte_offset_map(pmd, (unsigned long)addr);			// 获取pte

	/* 通过pte转成物理地址 */
	paddr = (pte_pfn(*pte) << PAGE_SHIFT) | ((unsigned long)vaddr & ~PAGE_MASK);
	pte_unmap(pte);

	/* 手动计算 */
	unsigned long caddr = (unsigned long)addr;
	// long index = (caddr >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1);
	long index = (caddr >> 39) & 0x1FF;
	pgd_t *tpgd = current->mm->pgd;

	LOG_I("index = %ld, func index = %ld", index, pgd_index(caddr));
	LOG_I("my pgd = %p", tpgd + index);

	return paddr;
}

/**************************************************************************************************
 * @brief  : 内存转换测试
 * @param  : None
 * @return : 0成功, -1失败
**************************************************************************************************/
static void mem_transfer_test(void)
{
	int val;
	void __iomem *map_addr;
	phys_addr_t func_paddr, manu_paddr;

	/* 内存地址判断 */
	vaddr = kmalloc(4096, GFP_KERNEL);			// 申请4K大小内存
	func_paddr = virt_to_phys(vaddr);			// 通过函数计算物理地址
	manu_paddr = manu_to_phys(vaddr);			// 通过页表计算物理地址

	LOG_I("vaddr = %p, func = %llx, manu = %llx", vaddr, func_paddr, manu_paddr);

	if(func_paddr == manu_paddr) {
		LOG_I("match addr");
	} else {
		LOG_I("not match addr");
	}

	/* 物理地址检验 */
	*(int *)vaddr = MAGIC_VALUE;					// 向虚拟地址写入一个0xAABB 幻数
	map_addr = ioremap(func_paddr, sizeof(int));	// 把物理地址转为虚拟地址
	val = ioread32(map_addr);
	iounmap(map_addr);

	if(val == MAGIC_VALUE) {
		LOG_I("match value");
	}else {
		LOG_I("not match value");
	}
}


/**************************************************************************************************
 * @brief  : 模块初始化
 * @param  : None
 * @return : 0成功, -1失败
**************************************************************************************************/
static int __init xxx_init(void)
{
	mem_transfer_test();
	LOG_I("xxx init.");
	return 0;
}


/**************************************************************************************************
 * @brief  : 模块退出
 * @param  : None
 * @return : 0成功, -1失败
**************************************************************************************************/
static void __exit xxx_exit(void)
{
	kfree(vaddr);
	LOG_I("xxx exit.");
}

module_init(xxx_init);
module_exit(xxx_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZW");
MODULE_DESCRIPTION("kmod - xxmem");

posted @ 2024-10-23 17:20  子午先森  阅读(111)  评论(0)    收藏  举报