现象
- 开发平台: IMX6ULL正点原子开发板
- 学习韦东山 串口驱动相关代码
- 使用自己/老师提供的代码,在卸载模块的时候出现内核崩溃。崩溃内容如下
/home/constant_z/c_code/UART_CODE/02_virtual_uart/virtual_uart.c virtual_uart_init 950
virtual_uart_probe 100ask
/lib/modules/4.1.15 # rmmod virtual_uart.ko
/home/constant_z/c_code/UART_CODE/02_virtual_uart/virtual_uart.c virtual_uart_exit 965
virtual_uart_remove 100ask
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = 887c8000
[00000000] *pgd=886f1831, *pte=00000000, *ppte=00000000
Internal error: Oops: 80000007 [#1] PREEMPT SMP ARM
Modules linked in: virtual_uart(O-)
CPU: 0 PID: 84 Comm: rmmod Tainted: G O 4.1.15 #1
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
task: 886aaac0 ti: 887d0000 task.ti: 887d0000
PC is at 0x0
LR is at uart_remove_one_port+0x130/0x150
pc : [<00000000>] lr : [<8031b204>] psr: 200d0013
sp : 887d1ef8 ip : 00000000 fp : 00000000
r10: 00000000 r9 : 887d0000 r8 : 8000f604
r7 : 886d109c r6 : 00000000 r5 : 886d1000 r4 : 886bee10
r3 : 00000000 r2 : 00000000 r1 : 600d0013 r0 : 886bee10
Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
Control: 10c5387d Table: 887c806a DAC: 00000015
Process rmmod (pid: 84, stack limit = 0x887d0210)
Stack: (0x887d1ef8 to 0x887d2000)
1ee0: 7f000a74 7f0008d8
1f00: 88107644 00000081 8000f604 7f000118 88107610 8034a2a4 88107610 7f0008d8
1f20: 88107644 803486f0 88107610 7f0008d8 88107644 80348e80 7f0008d8 7ec75930
1f40: 5f6c6175 80348450 7f0008a0 7f000494 7f00091c 80093bfc 00001000 74726976
1f60: 5f6c6175 74726175 7ec75900 00000001 887d0000 00000000 76fb8568 800d61a0
1f80: 00100871 00000000 ffffffff 88596108 76fb8568 00027d38 76e515e4 00028a89
1fa0: 74726976 8000f480 00028a89 74726976 7ec75930 00000880 00000000 7ec75bc8
1fc0: 00028a89 74726976 5f6c6175 00000081 00000002 00000000 76fb8000 00000000
1fe0: 7ec75928 7ec75918 000289d1 76e8aad2 800d0030 7ec75930 8bf59811 8bf59c11
[<8031b204>] (uart_remove_one_port) from [<7f000118>] (virtual_uart_remove+0x28/0x38 [virtual_uart])
[<7f000118>] (virtual_uart_remove [virtual_uart]) from [<8034a2a4>] (platform_drv_remove+0x18/0x30)
[<8034a2a4>] (platform_drv_remove) from [<803486f0>] (__device_release_driver+0x70/0xe4)
[<803486f0>] (__device_release_driver) from [<80348e80>] (driver_detach+0xac/0xb0)
[<80348e80>] (driver_detach) from [<80348450>] (bus_remove_driver+0x4c/0xa0)
[<80348450>] (bus_remove_driver) from [<7f000494>] (virtual_uart_exit+0x30/0x40 [virtual_uart])
[<7f000494>] (virtual_uart_exit [virtual_uart]) from [<80093bfc>] (SyS_delete_module+0x174/0x1b8)
[<80093bfc>] (SyS_delete_module) from [<8000f480>] (ret_fast_syscall+0x0/0x3c)
Code: bad PC value
---[ end trace 7250232a078a0492 ]---
Segmentation fault
-
内核版本
学习正点原子驱动时的LINUX内核版本是4.1.15 -
排查流程:
- 通过打印信息发现,初步定位在
uart_remove_one_port这个函数中出现问题。 - 根据nxp的流程,及它的
imx.c,它没有定义全局静态变量uart_port,而是动态申请份分配- 于使我使用nxp一样的构造方法,定义私有数据变量结构体,在结构体里面定义
uart_port
不要在私有数据里面定义指针变量,否则,使用devm_kzalloc会报错。
- 于使我使用nxp一样的构造方法,定义私有数据变量结构体,在结构体里面定义
- 我做了个基本的测试驱动程序,叫
test_base_uart.c,用来一步一步搭建串口驱动- 4.1 发现只要填充
uart_port结构体后,卸载驱动就会出现错误。 - 于使我进入内核源码里面,在下面添加内核中添加如下打印信息
- 4.1 发现只要填充
- 通过打印信息发现,初步定位在
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state = drv->state + uport->line;
struct tty_port *port = &state->port;
struct tty_struct *tty;
int ret = 0;
printk("-----1\n");
BUG_ON(in_interrupt());
if (state->uart_port != uport)
dev_alert(uport->dev, "Removing wrong port: %p != %p\n",
state->uart_port, uport);
mutex_lock(&port_mutex);
/*
* Mark the port "dead" - this prevents any opens from
* succeeding while we shut down the port.
*/
mutex_lock(&port->mutex);
if (!state->uart_port) {
mutex_unlock(&port->mutex);
ret = -EINVAL;
goto out;
}
uport->flags |= UPF_DEAD;
mutex_unlock(&port->mutex);
printk("-----2\n");
/*
* Remove the devices from the tty layer
*/
tty_unregister_device(drv->tty_driver, uport->line);
printk("-----3\n");
tty = tty_port_tty_get(port);
if (tty) {
tty_vhangup(port->tty);
tty_kref_put(tty);
}
printk("-----4\n");
/*
* If the port is used as a console, unregister it
*/
if (uart_console(uport))
unregister_console(uport->cons);
printk("-----5\n");
/*
* Free the port IO and memory resources, if any.
*/
if (uport->type != PORT_UNKNOWN)
uport->ops->release_port(uport);
printk("-----6\n");
kfree(uport->tty_groups);
/*
* Indicate that there isn't a port here anymore.
*/
uport->type = PORT_UNKNOWN;
state->uart_port = NULL;
out:
mutex_unlock(&port_mutex);
return ret;
}
printk均是后续添加进入内核的,重新复现错误,输出如下打印信息

那问题就简单了,即
if (uport->type != PORT_UNKNOWN)
uport->ops->release_port(uport);
printk("-----6\n");
出现问题,结合我们驱动程序中
virt_uart_port->type = PORT_8250;//必须设置,不然会报错
当初,韦老师视频中随意设置了一项,所以会进入uart_port的我们提供的release_port这个函数,但我们并没有这个函数,所以释放的时候就会指向空指针,而且NXP的原版驱动中imx.c也没有提供对应的项,算是一个bug吧。我们仿照别人有的驱动代码,填充这个操作函数
static void virtual_uart_release_port(struct uart_port *port){
return;
}
再次加载运行,问题就解决了。

最后,别忘了复原内核的代码。推荐使用git命令管理自己的内核版本
1411

被折叠的 条评论
为什么被折叠?



