searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

ARM64架构下Linux内核Fixmap机制解析

2025-03-13 07:55:43
3
0

ARM64架构下Linux内核Fixmap机制解析

1. Fixmap概述

在Linux内核启动的早期阶段,完整的页表机制尚未建立,动态内存分配器也未初始化。此时,内核需要通过**Fixmap(固定映射)**机制访问特定的物理内存或硬件寄存器。Fixmap是一块预定义的虚拟地址区域,允许内核将物理地址静态映射到固定虚拟地址,解决了早期内存管理受限的问题。

2. Fixmap的核心作用

  • 静态映射保证:在启动早期,固定虚拟地址确保关键硬件(如串口、设备树)的可访问性。
  • 绕过动态分配:避免依赖vmalloc()kmalloc()等动态机制,直接通过编译时确定的虚拟地址操作硬件。
  • 类型多样性:支持映射物理内存(FIX_PMD)、设备寄存器(FIX_EARLYCON_MEM_BASE)等多种资源。

3. ARM64架构下的Fixmap实现

3.1 虚拟地址空间布局

在ARM64中,内核虚拟地址空间划分如下(以48位地址为例):

0xffff000000000000 - 0xffff7fffffffffff : vmalloc区域
0xffff800000000000 - 0xffffffffffffffff : 内核空间
  |- 0xffff800000000000 - 0xffff800007ffffff : 线性映射区(直接映射物理内存)
  |- 0xffffb7ffffee0000 - 0xffffb7ffffef0000 : Fixmap区域

Fixmap的起始地址通常由FIXADDR_START定义,大小由__end_of_permanent_fixed_addresses决定。

3.2 Fixmap索引与映射类型

Fixmap区域按功能划分为多个子区域,每个区域通过枚举索引标识。例如:

// arch/arm64/include/asm/fixmap.h
enum fixed_addresses {
    FIX_HOLE,
    FIX_FDT_END,
    FIX_FDT,
    FIX_EARLYCON_MEM_BASE,
    FIX_TEXT_POKE0,
    __end_of_permanent_fixed_addresses,
    // 临时映射区域
    NR_FIX_BTMAPS = 32,
    FIX_BTMAP_END = __end_of_permanent_fixed_addresses + NR_FIX_BTMAPS,
    __end_of_fixed_addresses
};
  • 永久映射(如FIX_FDT):用于设备树等长期存在的资源。
  • 临时映射(如FIX_BTMAP):短时映射,用后立即释放。
3.3 关键操作函数
  • 设置映射:通过set_fixmap(idx, phys_addr)将物理地址映射到Fixmap的指定索引位置。
    void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot)
    {
        unsigned long vaddr = __fix_to_virt(idx);
        create_pgd_mapping(swapper_pg_dir, vaddr, phys, FIXMAP_PAGE_SIZE, prot);
    }
    
  • 清除映射:使用clear_fixmap(idx)解除映射。

4. Fixmap使用场景分析

4.1 设备树(FDT)解析

启动早期,设备树物理地址需映射到Fixmap区域以便解析:

// drivers/of/fdt.c
void *initial_boot_params;

void __init early_init_dt_setup(void *params)
{
    initial_boot_params = fixmap_remap_fdt(params, &size, PAGE_KERNEL);
}
4.2 早期控制台输出

初始化串口控制台前,需映射UART寄存器:

// drivers/tty/serial/earlycon.c
static int __init earlycon_init(struct earlycon_device *device)
{
    device->membase = earlycon_map(addr, size);
    // 写入UART寄存器进行初始化
}
4.3 内核代码热补丁

使用FIX_TEXT_POKE0映射临时可写页,修改运行中的内核代码:

// arch/arm64/kernel/insn.c
void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
{
    set_fixmap(FIX_TEXT_POKE0, __pa_symbol(addr));
    memcpy((void *)fix_to_virt(FIX_TEXT_POKE0), opcode, len);
    clear_fixmap(FIX_TEXT_POKE0);
}

5. 使用Fixmap的示例代码

映射物理内存并访问:
#include <asm/fixmap.h>

void access_phys_mem(phys_addr_t phys)
{
    void __iomem *vaddr;
    
    // 设置映射
    set_fixmap(FIX_EARLYCON_MEM_BASE, phys);
    vaddr = (void __iomem *)fix_to_virt(FIX_EARLYCON_MEM_BASE);
    
    // 读写操作
    iowrite32(0x1234, vaddr);
    u32 val = ioread32(vaddr);
    
    // 清除映射
    clear_fixmap(FIX_EARLYCON_MEM_BASE);
}

6. 注意事项

  1. 索引冲突:Fixmap区域有限(通常约64项),需确保索引使用不越界。
  2. 并发安全:Fixmap非原子操作,需在单线程环境(如启动初期)使用。
  3. 调试支持:启用CONFIG_DEBUG_VIRTUAL时,非法地址访问会触发内核警告。

7. 总结

Fixmap机制是ARM64内核早期初始化的基石,通过预编译的虚拟地址映射,解决了硬件访问的关键问题。理解其实现细节和适用场景,有助于在内核开发中高效处理内存映射需求,规避潜在的启动错误。随着内核启动流程的推进,Fixmap的临时映射逐渐被动态机制取代,但其在系统启动阶段的核心地位不可替代。

0条评论
作者已关闭评论
李****锋
5文章数
0粉丝数
李****锋
5 文章 | 0 粉丝
原创

ARM64架构下Linux内核Fixmap机制解析

2025-03-13 07:55:43
3
0

ARM64架构下Linux内核Fixmap机制解析

1. Fixmap概述

在Linux内核启动的早期阶段,完整的页表机制尚未建立,动态内存分配器也未初始化。此时,内核需要通过**Fixmap(固定映射)**机制访问特定的物理内存或硬件寄存器。Fixmap是一块预定义的虚拟地址区域,允许内核将物理地址静态映射到固定虚拟地址,解决了早期内存管理受限的问题。

2. Fixmap的核心作用

  • 静态映射保证:在启动早期,固定虚拟地址确保关键硬件(如串口、设备树)的可访问性。
  • 绕过动态分配:避免依赖vmalloc()kmalloc()等动态机制,直接通过编译时确定的虚拟地址操作硬件。
  • 类型多样性:支持映射物理内存(FIX_PMD)、设备寄存器(FIX_EARLYCON_MEM_BASE)等多种资源。

3. ARM64架构下的Fixmap实现

3.1 虚拟地址空间布局

在ARM64中,内核虚拟地址空间划分如下(以48位地址为例):

0xffff000000000000 - 0xffff7fffffffffff : vmalloc区域
0xffff800000000000 - 0xffffffffffffffff : 内核空间
  |- 0xffff800000000000 - 0xffff800007ffffff : 线性映射区(直接映射物理内存)
  |- 0xffffb7ffffee0000 - 0xffffb7ffffef0000 : Fixmap区域

Fixmap的起始地址通常由FIXADDR_START定义,大小由__end_of_permanent_fixed_addresses决定。

3.2 Fixmap索引与映射类型

Fixmap区域按功能划分为多个子区域,每个区域通过枚举索引标识。例如:

// arch/arm64/include/asm/fixmap.h
enum fixed_addresses {
    FIX_HOLE,
    FIX_FDT_END,
    FIX_FDT,
    FIX_EARLYCON_MEM_BASE,
    FIX_TEXT_POKE0,
    __end_of_permanent_fixed_addresses,
    // 临时映射区域
    NR_FIX_BTMAPS = 32,
    FIX_BTMAP_END = __end_of_permanent_fixed_addresses + NR_FIX_BTMAPS,
    __end_of_fixed_addresses
};
  • 永久映射(如FIX_FDT):用于设备树等长期存在的资源。
  • 临时映射(如FIX_BTMAP):短时映射,用后立即释放。
3.3 关键操作函数
  • 设置映射:通过set_fixmap(idx, phys_addr)将物理地址映射到Fixmap的指定索引位置。
    void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot)
    {
        unsigned long vaddr = __fix_to_virt(idx);
        create_pgd_mapping(swapper_pg_dir, vaddr, phys, FIXMAP_PAGE_SIZE, prot);
    }
    
  • 清除映射:使用clear_fixmap(idx)解除映射。

4. Fixmap使用场景分析

4.1 设备树(FDT)解析

启动早期,设备树物理地址需映射到Fixmap区域以便解析:

// drivers/of/fdt.c
void *initial_boot_params;

void __init early_init_dt_setup(void *params)
{
    initial_boot_params = fixmap_remap_fdt(params, &size, PAGE_KERNEL);
}
4.2 早期控制台输出

初始化串口控制台前,需映射UART寄存器:

// drivers/tty/serial/earlycon.c
static int __init earlycon_init(struct earlycon_device *device)
{
    device->membase = earlycon_map(addr, size);
    // 写入UART寄存器进行初始化
}
4.3 内核代码热补丁

使用FIX_TEXT_POKE0映射临时可写页,修改运行中的内核代码:

// arch/arm64/kernel/insn.c
void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
{
    set_fixmap(FIX_TEXT_POKE0, __pa_symbol(addr));
    memcpy((void *)fix_to_virt(FIX_TEXT_POKE0), opcode, len);
    clear_fixmap(FIX_TEXT_POKE0);
}

5. 使用Fixmap的示例代码

映射物理内存并访问:
#include <asm/fixmap.h>

void access_phys_mem(phys_addr_t phys)
{
    void __iomem *vaddr;
    
    // 设置映射
    set_fixmap(FIX_EARLYCON_MEM_BASE, phys);
    vaddr = (void __iomem *)fix_to_virt(FIX_EARLYCON_MEM_BASE);
    
    // 读写操作
    iowrite32(0x1234, vaddr);
    u32 val = ioread32(vaddr);
    
    // 清除映射
    clear_fixmap(FIX_EARLYCON_MEM_BASE);
}

6. 注意事项

  1. 索引冲突:Fixmap区域有限(通常约64项),需确保索引使用不越界。
  2. 并发安全:Fixmap非原子操作,需在单线程环境(如启动初期)使用。
  3. 调试支持:启用CONFIG_DEBUG_VIRTUAL时,非法地址访问会触发内核警告。

7. 总结

Fixmap机制是ARM64内核早期初始化的基石,通过预编译的虚拟地址映射,解决了硬件访问的关键问题。理解其实现细节和适用场景,有助于在内核开发中高效处理内存映射需求,规避潜在的启动错误。随着内核启动流程的推进,Fixmap的临时映射逐渐被动态机制取代,但其在系统启动阶段的核心地位不可替代。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0