静态映射

在做代码移植时,看到一些寄存器操作相关的代码,发现一些宏定义在新版本的代码中已经没有了。这些宏主要是芯片各个IP的寄存器物理地址的宏定义。众所周知,在操作系统启动完,并启用MMU的情况下,是无法直接读写寄存器物理地址来操作芯片的IP的。无论是在用户态还是在内核态,都需要把物理地址映射成虚拟地址,再通过虚拟地址去访问寄存器资源。

arch/arm/include/mach/hardware.h头文件定义了内存资源和I/O资源的物理地址和虚拟地址的映射宏:

/*****************************************************************
                            IO Mapping
 All registers (128MB) and flash (max 128MB) should fit within
 256MB @ 0xF0000000
 Mem address Range
         Virt = 0xF0000000 to 0xFFFFFFFF
         Phys = 0x10000000 to 0x1FFFFFFF
 IO address Range
         Virt = 0xF7000000 to 0xFEFFFFFF
         Phys = 0x16000000 to 0x1EFFFFFF
*****************************************************************/
#define IO_BASE            0xF0000000                 // VA of IO
#define MEM_ADDRESS(x)  ((x&0x00ffffff)+IO_BASE)
#define IO_ADDRESS(x)   ((x&0x0fffffff)+IO_BASE)
#define PHY_ADDRESS(x)  ((x&0x0fffffff)+0x10000000)

相当于操作系统在启动之处就把0x16000000到0x1EFFFFFF的物理地址映射到了0xF7000000到0xFEFFFFFF这个线性区间了。所以可以直接通过IO_ADDRESS这个宏直接读写芯片的I/O资源。而这部分映射的原理(流程)是怎么样的呢?

1 静态映射

首先介绍下静态映射,相比于静态映射,动态映射是直接通过ioremap等函数创建一段I/O外设到内核虚拟地址的映射表。是我们平时使用得比较多的,也比较熟悉的。静态映射是在系统启动过程中在内存中拿出一段内存用于映射I/O资源的物理地址。这种映射方式用map_desc结构体定义了具体的资源映射,可以根据实际来定义。

static struct map_desc aspeed_std_desc[] __initdata = 
{
    { AST_AHBC_VA_BASE,            __phys_to_pfn(AST_AHBC_BASE),           SZ_64K,      MT_DEVICE},
    { AST_MAC1_VA_BASE,            __phys_to_pfn(AST_MAC1_BASE),           SZ_64K,      MT_DEVICE},
    { AST_USB20_VA_BASE,           __phys_to_pfn(AST_USB20_BASE),          SZ_4K,       MT_DEVICE},
    { AST_USB11_VA_BASE,           __phys_to_pfn(AST_USB11_BASE),          SZ_64K,      MT_DEVICE},
    { AST_IC_VA_BASE,              __phys_to_pfn(AST_IC_BASE),             SZ_64K,      MT_DEVICE},
    { AST_SDRAMC_VA_BASE,          __phys_to_pfn(AST_SDRAMC_BASE),         SZ_4K,       MT_DEVICE},
    { AST_SCU_VA_BASE,             __phys_to_pfn(AST_SCU_BASE),            SZ_4K,       MT_DEVICE},
    { AST_CRYPTO_VA_BASE,          __phys_to_pfn(AST_CRYPTO_BASE),         SZ_4K,       MT_DEVICE},
    { AST_JTAG_VA_BASE,            __phys_to_pfn(AST_JTAG_BASE),           SZ_4K,       MT_DEVICE},
    { AST_GRAPHIC_VA_BASE,         __phys_to_pfn(AST_GRAPHIC_BASE),        SZ_4K,       MT_DEVICE},
    { AST_ADC_VA_BASE,             __phys_to_pfn(AST_ADC_BASE),            SZ_4K,       MT_DEVICE},
    { AST_AHB_TO_PBUS_VA_BASE,     __phys_to_pfn(AST_AHB_TO_PBUS_BASE),    SZ_64K,      MT_DEVICE},
    { AST_MDMA_VA_BASE,            __phys_to_pfn(AST_MDMA_BASE),           SZ_64K,      MT_DEVICE},
    { AST_2D_VA_BASE,              __phys_to_pfn(AST_2D_BASE),             SZ_64K,      MT_DEVICE},
    { AST_GPIO_VA_BASE,            __phys_to_pfn(AST_GPIO_BASE),           SZ_4K,       MT_DEVICE},
    { AST_RTC_VA_BASE,             __phys_to_pfn(AST_RTC_BASE),            SZ_4K,       MT_DEVICE},
    { AST_TIMER_VA_BASE,           __phys_to_pfn(AST_TIMER_BASE),          SZ_4K,       MT_DEVICE},
    { AST_WDT_VA_BASE,             __phys_to_pfn(AST_WDT_BASE),            SZ_4K,       MT_DEVICE},
    { AST_PWM_VA_BASE,             __phys_to_pfn(AST_PWM_BASE),            SZ_4K,       MT_DEVICE},
    { AST_I2C_VA_BASE,             __phys_to_pfn(AST_I2C_BASE),            SZ_4K,       MT_DEVICE},
    { AST_DMA_VA_BASE,             __phys_to_pfn(AST_DMA_BASE),            SZ_4K,       MT_DEVICE},
    { AST_MCTP_VA_BASE,            __phys_to_pfn(AST_MCTP_BASE),           SZ_4K,       MT_DEVICE},
    { AST_LPC_PLUS_VA_BASE,        __phys_to_pfn(AST_LPC_PLUS_BASE),       SZ_4K,       MT_DEVICE},
    { AST_VIDEO_VA_BASE,           __phys_to_pfn(AST_VIDEO_BASE),          SZ_128K,     MT_DEVICE},
    { AST_PECI_VA_BASE,            __phys_to_pfn(AST_PECI_BASE),           SZ_4K,       MT_DEVICE},
    { AST_VUART0_VA_BASE,          __phys_to_pfn(AST_VUART0_BASE),         SZ_4K,       MT_DEVICE},
    { AST_VUART1_VA_BASE,          __phys_to_pfn(AST_VUART1_BASE),         SZ_4K,       MT_DEVICE},
    { AST_LPC_VA_BASE,             __phys_to_pfn(AST_LPC_BASE),            SZ_4K,       MT_DEVICE},
    { AST_USB1_VA_BASE,            __phys_to_pfn(AST_USB1_BASE),           SZ_4K,       MT_DEVICE},
}

struct machine_desc结构体中有个函数指针:

void			(*map_io)(void);/* IO mapping function	*/

arch/arm/mach-aspeed/setup.c下面的aspeed_map_common_io完成静态映射的动作:

void
__init aspeed_map_common_io(void)
{
	iotable_init(aspeed_std_desc, ARRAY_SIZE(aspeed_std_desc));
 	return;
}

aspeed_map_common_io在加载设备树的时候会赋值给map_io:

const struct machine_desc * __init setup_machine_fdt(void *dt_virt)
{
	const struct machine_desc *mdesc, *mdesc_best = NULL;

#if defined(CONFIG_ARCH_MULTIPLATFORM) || defined(CONFIG_ARM_SINGLE_ARMV7M)
	DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
		.l2c_aux_val = 0x0,
		.l2c_aux_mask = ~0x0,
       #if defined(CONFIG_SOC_ASPEED)
                .map_io     = aspeed_map_common_io,
                .init_machine = aspeed_init,
       #endif

	MACHINE_END

	mdesc_best = &__mach_desc_GENERIC_DT;
#endif

...
...
}

真正调用的地方是在paging_init函数会调用devicemaps_init,而devicemaps_init会调用map_io函数:

static void __init devicemaps_init(const struct machine_desc *mdesc)
{
    ...
    if (mdesc->map_io)
		mdesc->map_io();
	else
		debug_ll_io_init();
    ...
}

调用函数链如下:

Start_kernel --> setup_arch --> paging_init --> devicemaps_init中通过MACHINE_START宏来初始化machine_desc结构体。

2 reference

Linux内核:一文搞懂外设I/O内存资源的静态映射方式

posted @ 2025-08-06 20:09  cockpunctual  阅读(10)  评论(0)    收藏  举报