aarch64页表管理[1] 宏定义与头文件概览

devtools/2025/1/16 9:30:50/

文章目录

  • 头文件与宏定义概览
    • aarch64架构特定头文件
    • 通用头文件
    • 和页表无关的公共定义
    • kernel config
  • 头文件内容说明
    • arch/arm64/include/asm/memory.h
    • include/asm-generic/page.h
    • arch/arm64/include/asm/pgtable-types.h
    • arch/arm64/include/asm/pgtable.h
    • pgtable-hwdef.h
    • page-def.h
    • include/asm-generic/pgtable-nop4d.h
    • goto pgtable-hwdef.h
    • include/linux/pgtable.h
    • arch/arm64/mm/mmu.c
    • arch/arm64/include/asm/memory.h

头文件与宏定义概览

aarch64架构特定头文件

arch/arm64/include/asm/memory.h
arch/arm64/include/asm/pgtable.h
arch/arm64/include/asm/pgtable-types.h
arch/arm64/include/asm/pgtable-hwdef.h
arch/arm64/include/asm/page-def.h

通用头文件

include/linux/pgtable.h
include/asm-generic/page.h
include/asm-generic/pgtable-nop4d.h

和页表无关的公共定义

#define __AC(X,Y)	(X##Y)
#define _AC(X,Y)	__AC(X,Y)
#define _UL(x)		(_AC(x, UL))
#define _AT(T,X)	((T)(X)) //作用是强制类型转换

kernel config

CONFIG_PGTABLE_LEVELS=4     # 页表级别为4
CONFIG_ARM64_PAGE_SHIFT=12  # 4k页是这样的
CONFIG_ARM64_VA_BITS=48     # 物理地址位宽
# CONFIG_DEBUG_VIRTUAL      # 不知道是啥

头文件内容说明

arch/arm64/include/asm/memory.h

#define VA_BITS			(CONFIG_ARM64_VA_BITS)
#define _PAGE_OFFSET(va)	(-(UL(1) << (va)))
#define PAGE_OFFSET		(_PAGE_OFFSET(VA_BITS))
#define KIMAGE_VADDR		(MODULES_END)
#define MODULES_END		(MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR		(_PAGE_END(VA_BITS_MIN))
#define MODULES_VSIZE		(SZ_2G)
#define VMEMMAP_START		(-(UL(1) << (VA_BITS - VMEMMAP_SHIFT)))
#define VMEMMAP_END		(VMEMMAP_START + VMEMMAP_SIZE)
#define PCI_IO_END		(VMEMMAP_START - SZ_8M)
#define PCI_IO_START		(PCI_IO_END - PCI_IO_SIZE)
#define FIXADDR_TOP		(VMEMMAP_START - SZ_32M)

其中
VA_BITS = 48
PAGE_OFFSET = 0xffff-0000-0000-0000

include/asm-generic/page.h

#define pte_val(x)	((x).pte)
#define pmd_val(x)	((&x)->pmd[0])
#define pgd_val(x)	((x).pgd)
#define pgprot_val(x)	((x).pgprot)#define __pte(x)	((pte_t) { (x) } )
#define __pmd(x)	((pmd_t) { (x) } )
#define __pgd(x)	((pgd_t) { (x) } )
#define __pgprot(x)	((pgprot_t) { (x) } )

arch/arm64/include/asm/pgtable-types.h

typedef u64 pteval_t;
typedef u64 pmdval_t;
typedef u64 pudval_t;
typedef u64 p4dval_t;
typedef u64 pgdval_t;typedef struct { pteval_t pte; } pte_t;
#define pte_val(x)	((x).pte)
#define __pte(x)	((pte_t) { (x) } )typedef struct { pgdval_t pgd; } pgd_t; // 声明了一个匿名结构体, 只有pgd_t这个类型, 没有struct pgd_t
#define pgd_val(x)	((x).pgd)
#define __pgd(x)	((pgd_t) { (x) } )

小结
__pgd就是把一个值强转成pgd_t类型
pgd_val宏就是获取 pgd_t类型的结构体的pgdval_t

arch/arm64/include/asm/pgtable.h

#define __pte_to_phys(pte)	(pte_val(pte) & PTE_ADDR_MASK)
#define __p4d_to_phys(p4d)	__pte_to_phys(p4d_pte(p4d))#define pte_valid(pte)		(!!(pte_val(pte) & PTE_VALID))

__pte_to_phys(pte)
pte_val是获取pte_t结构体的pteval_t(u64)的值
__pte_to_phys(pte.pteval_t & 0xffff-ffff-f000)

pgtable-hwdef.h:
// 这是个标签,等会要goto回来

pgtable-hwdef.h

#define ARM64_HW_PGTABLE_LEVELS(va_bits) (((va_bits) - 4) / (PAGE_SHIFT - 3))
#define ARM64_HW_PGTABLE_LEVEL_SHIFT(n)	((PAGE_SHIFT - 3) * (4 - (n)) + 3)#define PGDIR_SHIFT		ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS)
#define PGDIR_SIZE		(_AC(1, UL) << PGDIR_SHIFT)
#define PGDIR_MASK		(~(PGDIR_SIZE-1))
#define PTRS_PER_PGD		(1 << (VA_BITS - PGDIR_SHIFT))#define PUD_SHIFT		ARM64_HW_PGTABLE_LEVEL_SHIFT(1)
#define PUD_SIZE		(_AC(1, UL) << PUD_SHIFT)
#define PUD_MASK		(~(PUD_SIZE-1))
#define PTRS_PER_PUD		(1 << (PAGE_SHIFT - 3))#define PTRS_PER_PTE		(1 << (PAGE_SHIFT - 3))// 没有配置CONFIG_ARM64_PA_BITS_52的情况下(48位地址时)
#define PTE_ADDR_LOW		(((_AT(pteval_t, 1) << (48 - PAGE_SHIFT)) - 1) << PAGE_SHIFT)
#define PTE_ADDR_MASK		PTE_ADDR_LOW

对于4k页来说,PAGE_SHIFT就是12
PTE_ADDR_MASK = PTE_ADDR_LOW = (1 << 36 - 1) << 12 = 0xffff-ffff-f000

page-def.h

#define PAGE_SHIFT		CONFIG_ARM64_PAGE_SHIFT
#define PAGE_SIZE		(_AC(1, UL) << PAGE_SHIFT)
#define PAGE_MASK		(~(PAGE_SIZE-1))

include/asm-generic/pgtable-nop4d.h

static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{ return (p4d_t *)pgd;}

aarch64没有实现5级页表,linux内核也没有相应的支持,所以配到4级页表之后会引入pgtable-nop4d.h来消掉p4d的影响

goto pgtable-hwdef.h

回看到前面标签那里

PAGE_SHIFT = CONFIG_ARM64_PAGE_SHIFT = 12
CONFIG_PGTABLE_LEVELS=4
所以
PGD_SHIFT = ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - 4) = 9 * 4 + 3
这个公式的含义是:
页大小/目录描述符大小 * 目录级别 + 34级页表-4k页-PGD为例:页大小4k(12) / 目录描述符大小8字节(3) * 目录级别(4级页表中PGD是4) + 3 = 9 * 4 + 3 = 393级页表-64k页-PGD为例:64k(16) / 8(3) * 3 + 3 = 42

PGD_SHIFT的含义:PGD最低的那个bit到0的位数, 就是PUD(如果有的话) + PMD + PTE + PAGE的宽度和
在aarch64的4级页表中:
PGDIR_SHIFT = 39
P4D_SHIFT = PGDIR_SHIFT = 39
PTRS_PER_PGD = (1 << (VA_BITS - PGDIR_SHIFT)) = 1 << 9 = 512

linuxpgtableh_170">include/linux/pgtable.h

#define pgd_offset(mm, address)		pgd_offset_pgd((mm)->pgd, (address))
= mm->pgd + pgd_index(address);#define pgd_index(a)  (((a) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))= mm->pgd + (address >> PGDIR_SHIFT) & (512 - 1)= mm-pgd + (address >> 39) & 0x1ff
pud_offsetp4d_pgtable(*p4d) + pud_index(address)
pud_index
= (address >> PUD_SHIFT) & (PTRS_PER_PUD - 1)pmd_offsetpud_pgtable(*pud) + pmd_index(address)
pmd_index
= (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1)pte_offset_kernel(pte_t *)pmd_page_vaddr(*pmd) + pte_index(address)
pte_index
= (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)

小结
pte_index就是取出地址中第13-21位的数据(1-12是PAGE)
pgd_index就是取出第40-48位的数据

那么
pgd_offset就是mm->pgd + pgd所在的那9个bit位

arch/arm64/mm/mmu.c

u64 kimage_vaddr __ro_after_init = (u64)&_text;
EXPORT_SYMBOL(kimage_vaddr);u64 kimage_voffset __ro_after_init;
EXPORT_SYMBOL(kimage_voffset);

arch/arm64/include/asm/memory.h

// 判断一个虚拟地址是否位于线性映射区
#define __is_lm_address(addr)	(((u64)(addr) - PAGE_OFFSET) < (PAGE_END - PAGE_OFFSET))
// 将线性映射区的虚拟地址转为物理地址
#define __lm_to_phys(addr)	(((addr) - PAGE_OFFSET) + PHYS_OFFSET)
// 将内核镜像的虚拟地址转为物理地址 (kimage_offset是内核镜像的虚拟地址和物理地址之间的便宜量,这个值在内核启动时计算得出
#define __kimg_to_phys(addr)	((addr) - kimage_voffset)// typedef u64 phys_addr_t;
#define __virt_to_phys_nodebug(x) ({					\phys_addr_t __x = (phys_addr_t)(__tag_reset(x));		\__is_lm_address(__x) ? __lm_to_phys(__x) : __kimg_to_phys(__x);	\
})#define __virt_to_phys(x)	__virt_to_phys_nodebug(x)
#define __phys_to_virt(x)	((unsigned long)((x) - PHYS_OFFSET) | PAGE_OFFSET)
#define __phys_to_kimg(x)	((unsigned long)((x) + kimage_voffset))// pa和va宏,用于获取物理/虚拟地址
#define __pa(x)			__virt_to_phys((unsigned long)(x))
#define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))

小结:__va宏展开后可以看出, 从物理地址到虚拟地址的偏移只是常量
物理地址转虚拟地址只需要加偏移
虚拟地址转物理地址需要判断是线性映射区还是内核镜像的地址


http://www.ppmy.cn/devtools/150907.html

相关文章

VSCode连接远程docker环境

容器内部署操作&#xff0c;进入容器 1、安装 ssh 服务 apt-get install openssh-server /etc/init.d/ssh start 设置 root 用户密码 passwd root 修改 ssh 配置文件 vim /etc/ssh/sshd_config 在末尾“插入模式”添加下面内容&#xff1a; RSAAuthentication yes Pu…

【C++】面试题整理(未完待续)

【C】面试题整理 文章目录 一、概述二、C基础2.1 - 指针在 32 位和 64 位系统中的长度2.2 - 数组和指针2.3 - 结构体对齐补齐2.4 - 头文件包含2.5 - 堆和栈的区别 三、智能指针3.1 - 智能指针是线程安全的吗&#xff1f;3.2 - 线程安全的几种方法 三、参考 一、概述 最近面试&…

STM32F1——CAN驱动代码

一、 CAN.H。 #ifndef __CAN_H #define __CAN_H #include "stm32f10x.h" #include "SysTick.h" //PA11--CANRX PA12--CANTX //CAN接收RX0中断使能 #define CAN_RX0_INT_ENABLE 0 //0,不使能;1,使能.u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 …

源码编译安装httpd 2.4,提供系统服务管理脚本并测试

一.使用 systemd 服务文件 1.在官网找到并且下载安装包 Download - The Apache HTTP Server Project 2.解压 tar xf httpd-2.4.62.tar.gz -C /usr/local/src/ 3.安装依赖 [rootlocalhost ~]# dnf install gcc gcc-c make pcre-devel openssl-devel -y 4.编译安装 cd /u…

1.13 多线程编程

1.思维导图 2.创建两个子进程&#xff0c;父进程负责&#xff1a;向文件中写入数据&#xff1b;两个子进程负责&#xff1a;从文件中读取数据。 要求&#xff1a;一定保证1号子进程先读取&#xff0c;2号子进程后读取&#xff0c;使用文件IO去实现。 1>程序代码 …

easyui datagrid表头和网格错位问题

问题&#xff1a;表头与数据网格错位 解决&#xff1a; 在onLoadSuccess事件中调用fitColumns方法 $(this).datagrid(‘fitColumns’);

在 Go语言中一个字段可以包含多种类型的值的设计与接种解决方案

在 Go 中&#xff0c;如果你希望一个字段可以包含多种类型的值&#xff0c;你可以使用以下几种方式来实现&#xff1a; ### 1. **使用空接口 (interface{})** Go 的空接口 interface{} 可以接受任何类型的值&#xff0c;因此&#xff0c;你可以将字段定义为一个空接口&#x…

论文阅读:Searching for Fast Demosaicking Algorithms

今天介绍一篇有关去马赛克的工作&#xff0c;去马赛克是 ISP 流程里面非常重要的一个模块&#xff0c;可以说是将多姿多彩的大千世界进行色彩还原的重要一步。这篇工作探索的是如何从各种各样的去马赛克算法中&#xff0c;选择最佳的一种。 Abstract 本文提出了一种方法&…