2026/3/20 1:39:44
网站建设
项目流程
做僾网站,网站怎样做,网站内容建设评估,莱山做网站的公司7. 内存管理与 I/O 访问 基于 Linux 内核源码分析 | 硬件平台:瑞芯微 RK3588 (ARM64) 目录 7.1 ioremap / iounmap 7.2 readl/writel 等寄存器访问宏 7.3 DMA 缓冲区分配 7.4 内存屏障简介 7.1 ioremap / iounmap
7.1.1 概述
在 Linux 驱动开发中,硬件设备的寄存器通常位于…7. 内存管理与 I/O 访问基于 Linux 内核源码分析 | 硬件平台:瑞芯微 RK3588 (ARM64)目录7.1 ioremap / iounmap7.2 readl/writel 等寄存器访问宏7.3 DMA 缓冲区分配7.4 内存屏障简介7.1 ioremap / iounmap7.1.1 概述在 Linux 驱动开发中,硬件设备的寄存器通常位于物理地址空间。内核代码使用的是虚拟地址,因此需要一种机制将物理地址映射到内核虚拟地址空间。ioremap就是为此而设计的接口。核心源码位置:ARM64 架构实现:kernel/arch/arm64/mm/ioremap.c通用页表映射:kernel/mm/ioremap.c接口定义:kernel/arch/arm64/include/asm/io.h7.1.2 ioremap 映射流程地址无效有效是 RAM非 RAM失败成功驱动调用 ioremapioremap phys_addr size__ioremap_caller设置 PROT_DEVICE_nGnRE 属性参数检查返回 NULL页对齐处理是否为 RAM 区域?返回 NULL禁止映射 RAMget_vm_area_caller分配虚拟地址空间ioremap_page_range建立页表映射映射成功?vunmap 释放资源返回虚拟地址7.1.3 核心实现分析__ioremap_caller 函数源码位置:kernel/arch/arm64/mm/ioremap.c:21-63staticvoid__iomem*__ioremap_caller(phys_addr_tphys_addr,size_tsize,pgprot_tprot,void*caller){unsignedlonglast_addr;unsignedlongoffset=phys_addr~PAGE_MASK;interr;unsignedlongaddr;structvm_struct*area;/* 1. 页对齐处理 */phys_addr=PAGE_MASK;size=PAGE_ALIGN(size+offset);/* 2. 检查地址范围有效性 */last_addr=phys_addr+size-1;if(!size||last_addrphys_addr||(last_addr~PHYS_MASK))returnNULL;/* 3. 禁止映射 RAM 区域 */if(WARN_ON(pfn_valid(__phys_to_pfn(phys_addr))))returnNULL;/* 4. 分配内核虚拟地址空间 */area=get_vm_area_caller(size,VM_IOREMAP,caller);if(!area)returnNULL;addr=(unsignedlong)area-addr;area-phys_addr=phys_addr;/* 5. 建立页表映射 */err=ioremap_page_range(addr,addr+size,phys_addr,prot);if(err){vunmap((void*)addr);returnNULL;}/* 6. 返回带偏移的虚拟地址 */return(void__iomem*)(offset+addr);}ioremap_page_range 页表建立源码位置:kernel/mm/ioremap.c:222-250页表层级遍历:PGD (Page Global Directory) ↓ P4D (Page 4th Directory) ↓ PUD (Page Upper Directory) ↓ PMD (Page Middle Directory) ↓ PTE (Page Table Entry) → 物理页面支持巨页映射优化:页表级别映射粒度条件P4D 级别512GB地址和大小都要 512GB 对齐PUD 级别1GB地址和大小都要 1GB 对齐PMD 级别2MB地址和大小都要 2MB 对齐PTE 级别4KB常规页面映射7.1.4 内存类型属性源码位置:kernel/arch/arm64/include/asm/pgtable-prot.h:55-60ARM64 定义了多种设备内存属性:/* 设备内存属性 - 不可缓存 */#definePROT_DEVICE_nGnRnE/* non-Gathering, non-Reordering, No Early acknowledge */#definePROT_DEVICE_nGnRE/* non-Gathering, non-Reordering, Early acknowledge *//* 正常内存属性 */#definePROT_NORMAL_NC/* Normal Non-Cacheable */#definePROT_NORMAL/* Normal Cacheable */属性缩写含义说明nGnon-Gathering读写操作不会合并nRnon-Reordering读写操作不会重排序RnERead No Early读操作不提前返回REEarly写操作可提前返回确认NCNon-Cacheable不使用缓存常用 ioremap 变体:/* 标准 ioremap - 设备内存,不可缓存 */void__iomem*ioremap(phys_addr_taddr,size_tsize);/* 写合并模式 - 正常非缓存内存 */void__iomem*ioremap_wc(phys_addr_taddr,size_tsize);/* 可缓存模式 - 用于正常 RAM 区域 */void__iomem*ioremap_cache(phys_addr_taddr,size_tsize);/* PCI 配置空间 - nGnRnE 属性 */void__iomem*pci_remap_cfgspace(phys_addr_taddr,size_tsize);7.1.5 iounmap 解除映射源码位置:kernel/arch/arm64/mm/ioremap.c:72-83voidiounmap(volatilevoid__iomem*io_addr){unsignedlongaddr=(unsignedlong)io_addrPAGE_MASK;/* * 某些情况下(如 ioremap_cache 复用 RAM 映射), * 地址可能不在 vmalloc 范围内,需要检查。 */if(is_vmalloc_addr((void*)addr))vunmap((void*)addr);}7.1.6 使用示例/* 假设 RK3588 GPIO 控制器物理基址为 0xFD820000 */#defineGPIO_BASE_PHYS0xFD820000void__iomem*gpio_base;/* 1. 映射设备寄存器 */gpio_base=ioremap(GPIO_BASE_PHYS,0x1000);if(!gpio_base){pr_err("Failed to ioremap GPIO registers\n");return-ENOMEM;}/* 2. 访问寄存器 */u32 value=