1.参数解析
early_param(“earlycon”, param_setup_earlycon);
#define early_param(str, fn) \ __setup_param(str, fn, fn, 1)
#define __setup_param(str, unique_id, fn, early) \
static const char __setup_str_##unique_id[] __initconst \
__aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
1,所有的系统启动参数都是由形如 static int __init foo(char *str);的函数来支持的
注:#define __init attribute ((section (".init.text")))
申明所有的启动参数支持函数都放入.init.text段
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};
static const char __setup_str_param_setup_earlycon __initconst __aligned(1) = earlycon
static struct obs_kernel_param __setup_param_setup_earlycon __used __section(.init.setup)
__attribute__((aligned((sizeof(long))))) = {
__setup_str_param_setup_earlycon,
param_setup_earlycon,
0
}
也就是说,启动参数(函数指针)被封装到obs_kernel_param结构中,
所有的内核启动参数形成内核映像.init.setup段中的一个
obs_kernel_param数组
__setup与early_param不同的是,early_param 宏注册的内核选项必须要在其他内核选项之前被处理。
在函数start_kernel中,parse_early_param处理early_param定义的参数,parse_args处理__setup定义的参数
内核对启动参数的解析:下面函数历遍obs_kernel_param数组,调用
支持函数
asmlinkage __visible void __init start_kernel(void)—>
parse_early_param();—>
parse_early_options(tmp_cmdline);—>
parse_args(“early options”, cmdline, NULL, 0, 0, 0, NULL, do_early_param);---->
do_early_param------>
循环遍历自己setup中填充的函数
2 函数的调用
经过kernel的层层的调用最后回到了 match->setup(&early_console_dev,buf)
这个就调用我们注册的setup函数。
/tty/serial/8250/8250_uniphier.c:61:OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart",
./tty/serial/imx.c:2122:OF_EARLYCON_DECLARE(ec_imx6q, "fsl,imx6q-uart", imx_console_early_setup);
./tty/serial/imx.c:2123:OF_EARLYCON_DECLARE(ec_imx21, "fsl,imx21-uart", imx_console_early_setup);
./tty/serial/altera_uart.c:532:OF_EARLYCON_DECLARE(uart, "altr,uart-1.0", altera_uart_earlycon_setup);
./tty/serial/omap-serial.c:1282:OF_EARLYCON_DECLARE(omapserial, "ti,omap2-uart", early_omap_serial_setup);
./tty/serial/omap-serial.c:1283:OF_EARLYCON_DECLARE(omapserial, "ti,omap3-uart", early_omap_serial_setup);
./tty/serial/omap-serial.c:1284:OF_EARLYCON_DECLARE(omapserial, "ti,omap4-uart", early_omap_serial_setup);
./tty/serial/meson_uart.c:567:OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart",
./tty/serial/meson_uart.c:570:OF_EARLYCON_DECLARE(meson, "amlogic,meson-ao-uart",
./tty/serial/sh-sci.c:3461:OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup);
./tty/serial/sh-sci.c:3462:OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup);
./tty/serial/sh-sci.c:3463:OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup);
./tty/serial/sh-sci.c:3464:OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup);
./tty/serial/sh-sci.c:3465:OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup);
./tty/serial/samsung.c:2512:OF_EARLYCON_DECLARE(s3c2410, "samsung,s3c2410-uart",
./tty/serial/samsung.c:2526:OF_EARLYCON_DECLARE(s3c2412, "samsung,s3c2412-uart",
./tty/serial/samsung.c:2528:OF_EARLYCON_DECLARE(s3c2440, "samsung,s3c2440-uart",
./tty/serial/samsung.c:2530:OF_EARLYCON_DECLARE(s3c6400, "samsung,s3c6400-uart",
./tty/serial/samsung.c:2544:OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart",
./tty/serial/samsung.c:2546:OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart",
./tty/serial/mps2-uart.c:477:OF_EARLYCON_DECLARE(mps2, "arm,mps2-uart", mps2_early_console_setup);
./tty/serial/xilinx_uartps.c:1168:OF_EARLYCON_DECLARE(cdns, "xlnx,xuartps", cdns_early_console_setup);
./tty/serial/xilinx_uartps.c:1169:OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p8", cdns_early_console_setup);
./tty/serial/xilinx_uartps.c:1170:OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p12", cdns_early_console_setup);
./tty/serial/xilinx_uartps.c:1171:OF_EARLYCON_DECLARE(cdns, "xlnx,zynqmp-uart", cdns_early_console_setup);
./tty/serial/amba-pl011.c:2471:OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
./tty/serial/amba-pl011.c:2472:OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup);
#define _OF_EARLYCON_DECLARE(_name, compat, fn, unique_id) \
static const struct earlycon_id unique_id \
EARLYCON_USED_OR_UNUSED __initconst \
= { .name = __stringify(_name), \
.compatible = compat, \
.setup = fn }; \
static const struct earlycon_id EARLYCON_USED_OR_UNUSED \
__section(__earlycon_table) \
* const __PASTE(__p, unique_id) = &unique_id
#define OF_EARLYCON_DECLARE(_name, compat, fn) \
_OF_EARLYCON_DECLARE(_name, compat, fn, \
__UNIQUE_ID(__earlycon_##_name))
#define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn)
3关于串口的几个重要知识点
uart_port 是关于硬件部分的填充,同时关注uart_add_one_port这个函数的功能,一个tty 可以对应多个uart_port
4 回到最开始的问题
log的出处:kernel/driver/tty/serial/serial_core.c
本章的主要目的是找到log的出处,然后确定io类型跟io地址的由来,来源bios经过设备扫描后,全部进行映射,通过acpi table传给os。
我们的驱动程序加载了两个驱动设备 :
Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
一个pnp bus上的8250 一个是自己创造的16550设备:
先看16550:
serial8250_isa_devs = platform_device_alloc(“serial8250”,
PLAT8250_DEV_LEGACY);
ret = platform_device_add(serial8250_isa_devs);
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
ret = platform_driver_register(&serial8250_isa_driver);
serial8250_init---->
serial8250_isa_init_ports();—>
serial8250_init_port(up);—>
for (i = 0, up = serial8250_ports;
i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
i++, up++) {
struct uart_port *port = &up->port;
port->iobase = old_serial_port[i].port;
port->irq = irq_canonicalize(old_serial_port[i].irq);
port->irqflags = 0;
port->uartclk = old_serial_port[i].baud_base * 16;
port->flags = old_serial_port[i].flags;
port->hub6 = 0;
port->membase = old_serial_port[i].iomem_base;
port->iotype = old_serial_port[i].io_type;
port->regshift = old_serial_port[i].iomem_reg_shift;
serial8250_set_defaults(up);
port->irqflags |= irqflag;
if (serial8250_isa_config != NULL)
serial8250_isa_config(i, &up->port, &up->capabilities);
}
static const struct old_serial_port old_serial_port[] = {
SERIAL_PORT_DFNS /* defined in asm/serial.h */
};
#define SERIAL_PORT_DFNS \
/* UART CLK PORT IRQ FLAGS */ \
{ .uart = 0, BASE_BAUD, 0x3F8, 4, STD_COMX_FLAGS }, /* ttyS0 */ \
{ .uart = 0, BASE_BAUD, 0x2F8, 3, STD_COMX_FLAGS }, /* ttyS1 */ \
{ .uart = 0, BASE_BAUD, 0x3E8, 4, STD_COMX_FLAGS }, /* ttyS2 */ \
{ .uart = 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
#endif /* _ASM_X86_SERIAL_H */
然后在pnp bus上面的这个:
首先这个pnp bus节点之前没有遇到过,看了大概猜测是从acpi table进行匹配,获取设备的私有函数。
serial_pnp_probe—>
if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) {
uart.port.iobase = pnp_port_start(dev, 2);--->
uart.port.iotype = UPIO_PORT;
struct resource *res = pnp_get_resource(dev, IORESOURCE_IO, bar);—>
list_for_each_entry(pnp_res, &dev->resources, list) {
res = &pnp_res->res;
if (pnp_resource_type(res) == type && num-- == 0)---->
return res;
}
从上面函数的接口看到获取端口资源,如果是内存型的,就是获取内存资源,设备资源是怎么加载进去的,和内核是怎么遍历pcie设备树,并重新分配映射地址的 留到下一个问题探索中。
最后猜测:从最后的资源获取中,可以感觉到最上面的log打印出的信息,是通过pcie table中获取的。
后面发现port不是这个,有一个pcie串口驱动,把那个port加载到tty路径中。