问题
在学习串口子系统,在卸载串口模块的时候,引起内核崩溃。具体的崩溃日志如下:
/mnt/uartdeep # rmmod virtual_uart.ko
[ 23.208560] /home/book/bsp/mcu/uartdeep/virtual_uart_ok/virtual_uart.c virtual_uart_exit 301
[ 23.217242] Before uart_remove_one_port
[ 23.221631] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 23.230235] pgd = 94598000
[ 23.233703] [00000000] *pgd=94800831, *pte=00000000, *ppte=00000000
[ 23.243278] Internal error: Oops: 80000007 [#1] PREEMPT SMP ARM
[ 23.249210] Modules linked in: virtual_uart(O-)
[ 23.253784] CPU: 0 PID: 110 Comm: rmmod Tainted: G O 4.1.15 #4
[ 23.260750] Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[ 23.266939] task: 9426aac0 ti: 947ee000 task.ti: 947ee000
[ 23.272345] PC is at 0x0
[ 23.274894] LR is at uart_remove_one_port+0x130/0x150
[ 23.279954] pc : [<00000000>] lr : [<80354ad0>] psr: 200d0013
[ 23.279954] sp : 947efef8 ip : 16fcc000 fp : 00000000
[ 23.291435] r10: 00000000 r9 : 947ee000 r8 : 8000f604
[ 23.296667] r7 : 946fae9c r6 : 00000000 r5 : 946fae00 r4 : 94708010
[ 23.303201] r3 : 00000000 r2 : 00000000 r1 : 600d0013 r0 : 94708010
[ 23.309735] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
[ 23.316876] Control: 10c53c7d Table: 9459806a DAC: 00000015
[ 23.322628] Process rmmod (pid: 110, stack limit = 0x947ee210)
[ 23.328466] Stack: (0x947efef8 to 0x947f0000)
[ 23.332830] fee0: 7f000ad8 7f00093c
[ 23.341017] ff00: 94102244 00000081 8000f604 7f000154 94102210 80385834 94102210 7f00093c
[ 23.349203] ff20: 94102244 80383c80 94102210 7f00093c 94102244 80384410 7f00093c 7e883bf0
[ 23.357392] ff40: 5f6c6175 803839e0 7f000904 7f0004ec 7f000980 800938e4 00001000 74726976
[ 23.365578] ff60: 5f6c6175 74726175 7e883c00 00000001 947ee000 00000000 76f23568 800d5eb8
[ 23.373766] ff80: 00100871 00000000 ffffffff 948025d8 76f23568 00027d38 76dcf5e4 00028a89
[ 23.381952] ffa0: 74726976 8000f480 00028a89 74726976 7e883bf0 00000880 00000000 7e883e88
[ 23.390138] ffc0: 00028a89 74726976 5f6c6175 00000081 00000002 00000000 76f23000 00000000
[ 23.398324] ffe0: 7e883be8 7e883bd8 000289d1 76e08ad2 800d0030 7e883bf0 97fbe821 97fbec21
[ 23.406535] [<80354ad0>] (uart_remove_one_port) from [<7f000154>] (virtual_uart_remove+0x64/0x9c [virtual_uart])
[ 23.416731] [<7f000154>] (virtual_uart_remove [virtual_uart]) from [<80385834>] (platform_drv_remove+0x18/0x30)
[ 23.426838] [<80385834>] (platform_drv_remove) from [<80383c80>] (__device_release_driver+0x70/0xe4)
[ 23.435987] [<80383c80>] (__device_release_driver) from [<80384410>] (driver_detach+0xac/0xb0)
[ 23.444612] [<80384410>] (driver_detach) from [<803839e0>] (bus_remove_driver+0x4c/0xa0)
[ 23.452720] [<803839e0>] (bus_remove_driver) from [<7f0004ec>] (virtual_uart_exit+0x30/0x40 [virtual_uart])
[ 23.462480] [<7f0004ec>] (virtual_uart_exit [virtual_uart]) from [<800938e4>] (SyS_delete_module+0x174/0x1b8)
[ 23.472413] [<800938e4>] (SyS_delete_module) from [<8000f480>] (ret_fast_syscall+0x0/0x3c)
[ 23.480692] Code: bad PC value
[ 23.487156] ---[ end trace f7196ca02fb58b98 ]---
Segmentation fault
/mnt/uartdeep # [ 85.962528] random: nonblocking pool is initialized
由上述日志来看,是由访问非法地址(0地址),造成内核崩溃。而且崩溃的函数是uart_remove_one_port,具体的偏移是0x130
uart_remove_one_port具体实现
该函数的具体承载在文件:drivers/tty/serial/serial_core.c
具体的实现是:
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;
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);/** Remove the devices from the tty layer*/tty_unregister_device(drv->tty_driver, uport->line);tty = tty_port_tty_get(port);if (tty) {tty_vhangup(port->tty);tty_kref_put(tty);}/** If the port is used as a console, unregister it*/if (uart_console(uport))unregister_console(uport->cons);/** Free the port IO and memory resources, if any.*/if (uport->type != PORT_UNKNOWN)uport->ops->release_port(uport);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;
}
dump日志文件提示,出错点在偏移为0x130(304)字节处
具体的指令与字符换算关系如下(32位系统):
1条c语言指令(210条汇编指令-840字节)
简单语句:2~4条指令 (8~16字节)
函数调用:48条指令(1632字节)
条件判断+分支:612条指令(2448字节)
累计之后,0x130的位置在uport->ops->release_port(uport)处。
该函数是在uart_port结构体的ops变量中。具体的情况如下:
static const struct uart_ops virt_pops = {
.tx_empty = virt_tx_empty,
.set_mctrl = virt_set_mctrl,
.get_mctrl = virt_get_mctrl,
.stop_tx = virt_stop_tx,
.start_tx = virt_start_tx,
.stop_rx = virt_stop_rx,
.startup = virt_startup,
.shutdown = virt_shutdown,
.set_termios = virt_set_termios,
.type = virt_type,
};
如上所示,在virt_pops中,没有release_port的实现。由此在调用uart_remove_one_port的时候,由于release_port为空,导致内核崩溃
修正
在virt_pops中,增加release_port的实现:具体如下所示:
static void virt_release_port(struct uart_port *port)
{
}
static const struct uart_ops virt_pops = {
.tx_empty = virt_tx_empty,
.set_mctrl = virt_set_mctrl,
.get_mctrl = virt_get_mctrl,
.stop_tx = virt_stop_tx,
.start_tx = virt_start_tx,
.stop_rx = virt_stop_rx,
.startup = virt_startup,
.shutdown = virt_shutdown,
.set_termios = virt_set_termios,
.type = virt_type,
.release_port = virt_release_port,
};
如上图所示,增加一个release_port的空实现。之前的问题得以解决。