内核ioremap接口报Warning问题

当我们写一些外设驱动,或者内核模块,经常会使用ioremap函数来映射寄存器io地址,来访问io空间,在ioremap函数的实现内部会对传入的参数进行判断,如果地址不属于io空间,则会内核会报Warnig。

从函数名字我们也可以看得出来,既然函数叫io remap,那么映射的地址空间必须是io的物理地址,所以如果映射的地址落在了ram地址空间,内核就会报warning!如下面的log

内核报warning的日志:

[    2.536156] ------------[ cut here ]------------
[    2.540766] WARNING: CPU: 0 PID: 1 at __ioremap_caller+0xd0/0xd8
[    2.546759] Modules linked in:
[    2.549804] 
[    2.551286] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.9.30-rt19-EMBSYS-CGEL-6.1.R3-g51292b3-dirty #26
[    2.560664] Hardware name: MCS2 (DT)
[    2.564227] task: ffffffc2d7c98d00 task.stack: ffffffc2d7c9c000
[    2.570134] PC is at __ioremap_caller+0xd0/0xd8
[    2.574652] LR is at __ioremap_caller+0x54/0xd8
[    2.579170] pc : [<ffffff80080964e8>] lr : [<ffffff800809646c>] pstate: 80000045
[    2.586551] sp : ffffffc2d7c9fd40
[    2.589853] x29: ffffffc2d7c9fd40 x28: 0000000000000000 
[    2.595156] x27: ffffff80085bad50 x26: ffffff80085b5da8 
[    2.600459] x25: ffffff8008588aa0 x24: ffffff800864e950 
[    2.605762] x23: 00e8000000000707 x22: ffffff80085a3ab8 
[    2.611066] x21: 0000000000000000 x20: 0000000100200000 
[    2.616369] x19: 0000000001400000 x18: 0000000000000010 
[    2.621672] x17: 0000000000000001 x16: 0000000000000019 
[    2.626975] x15: 0000000000000006 x14: ffffff8088623bb7 
[    2.632278] x13: ffffff8008623bc5 x12: 0000000000000030 
[    2.637581] x11: 0101010101010101 x10: 7f7f7f7f7f7f7f7f 
[    2.642884] x9 : 6c646c616b69feff x8 : 0000000020000000 
[    2.648187] x7 : 0000000000000018 x6 : ffffff800863fb28 
[    2.653490] x5 : 0000000000000002 x4 : 0000000000000002 
[    2.658793] x3 : 0000000120000000 x2 : ffffff800863fb40 
[    2.664096] x1 : 0000000000000001 x0 : 0000000000000001 
[    2.669398] 
[    2.670880] ---[ end trace 52179c7400ddff0c ]---
[    2.675485] Call trace:
[    2.677921] Exception stack(0xffffffc2d7c9fb70 to 0xffffffc2d7c9fca0)
[    2.684349] fb60:                                   0000000001400000 0000008000000000
[    2.692166] fb80: ffffffc2d7c9fd40 ffffff80080964e8 ffffffc2d7c05100 ffffffc2d174c240
[    2.699983] fba0: ffffff80082e724c 00000000000000c0 00000000000003c0 000000007fffffff
[    2.707800] fbc0: 000000000000007f ffffff80086497c8 ffffffc2d7c1c608 0000000000000001
[    2.715617] fbe0: 00000000000003c0 000000007fffffff ffffff8008588aa0 ffffff80086497c8
[    2.723433] fc00: ffffffc2d7c9fc30 ffffff80082e724c 0000000000000001 0000000000000001
[    2.731250] fc20: ffffff800863fb40 0000000120000000 0000000000000002 0000000000000002
[    2.739067] fc40: ffffff800863fb28 0000000000000018 0000000020000000 6c646c616b69feff
[    2.746884] fc60: 7f7f7f7f7f7f7f7f 0101010101010101 0000000000000030 ffffff8008623bc5
[    2.754701] fc80: ffffff8088623bb7 0000000000000006 0000000000000019 0000000000000001
[    2.762518] [<ffffff80080964e8>] __ioremap_caller+0xd0/0xd8
[    2.768077] [<ffffff8008096500>] __ioremap+0x10/0x18
[    2.773032] [<ffffff80085a3ab8>] memblk_init+0x98/0x240
[    2.778246] [<ffffff8008082938>] do_one_initcall+0x38/0x120
[    2.783808] [<ffffff8008590cd0>] kernel_init_freeable+0x18c/0x228
[    2.789892] [<ffffff800847d658>] kernel_init+0x10/0x100
[    2.795105] [<ffffff8008082700>] ret_from_fork+0x10/0x50
[    2.801629] thread-exit capture initialized!

追溯一下代码,让我们找到真正的原因,先找到ioremap的定义,ioremap是一条宏定义:(arch/arm64/include/asm/io.h)

#define ioremap(addr, size)        __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))

__ioremap函数的定义如下,最终会调用__ioremap_caller函数:(arch/arm64/mm/ioremap.c)

void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot)
{
    return __ioremap_caller(phys_addr, size, prot,
                __builtin_return_address(0));
}

__ioremap_caller函数实现很短,注意其中的WARN_ON()函数,WARN_ON是内核常用的断言,当条件为真时,会调用dump_stack打印堆栈,就会看到上面的一长串warning打印,相应的断言还有BUG_ON。如果触发了BUG_ON,内核一般会panic。但WARN_ON不会使内核崩溃。

下面函数中调用了WARN_ON来进行物理地址检查,并且,由注释“Don't allow RAM to be mapped.”也可以看出,ioremap不允许映射ram地址。

static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size,
				      pgprot_t prot, void *caller)
{
	unsigned long last_addr;
	unsigned long offset = phys_addr & ~PAGE_MASK;
	int err;
	unsigned long addr;
	struct vm_struct *area;

	/*
	 * Page align the mapping address and size, taking account of any
	 * offset.
	 */
	phys_addr &= PAGE_MASK;
	size = PAGE_ALIGN(size + offset);

	/*
	 * Don't allow wraparound, zero size or outside PHYS_MASK.
	 */
	last_addr = phys_addr + size - 1;
	if (!size || last_addr < phys_addr || (last_addr & ~PHYS_MASK))
		return NULL;

	/*
	 * Don't allow RAM to be mapped.
	 */
	WARN_ON(pfn_valid(__phys_to_pfn(phys_addr))); /* 检查phys_addr是否是一个有效的物理地址 */

	area = get_vm_area_caller(size, VM_IOREMAP, caller);
	if (!area)
		return NULL;
	addr = (unsigned long)area->addr;
	area->phys_addr = phys_addr;

	err = ioremap_page_range(addr, addr + size, phys_addr, prot);
	if (err) {
		vunmap((void *)addr);
		return NULL;
	}

	return (void __iomem *)(offset + addr);
}

kernel用pfn_valid(__phys_to_pfn(phys_addr))来判断该地址是否为一个有效的系统ram地址,因为物理地址可以转换成一个有效的页帧号。

---以上代码摘自linux-4.9.30版本内核。

猜你喜欢

转载自blog.csdn.net/y33988979/article/details/81870091