arm linux debug notes (error note)

1.  forgot __func__ and pointer is judged too earlily.

	ret = sprdfb_panel_constructor(pan_node, fb_dev);
	if (ret) {
		pr_err("%s: failed to construct panel device\n"); // lose __func__
		return -EFAULT;
	}

%s, __func__, forgot __func__

	if (!fb_dev || fb_dev->panel)
		return -ENODEV; // Actually, panel pointer is NULL...

	pan_node = sprdfb_panel_search(fb_dev);
	if (!pan_node) {
		pr_warn("can't find panel specified by cmdline, try reading id\n");
	}
	ret = sprdfb_panel_constructor(pan_node, fb_dev);
	if (ret) {
		pr_err("%s: failed to construct panel device\n", __func__);
		return -EFAULT;
	}
	panel = fb_dev->panel;

The error log is as following:

7sprdfb_panel: ---- enter: sprdfb_panel_device_register ----

1Unable to handle kernel paging request at virtual address 60000113

1pgd = c0004000

1[60000113] *pgd=00000000

0Internal error: Oops: 5 [#1] PREEMPT SMP ARM

dModules linked in:

dCPU: 1 PID: 1 Comm: swapper/0 Tainted: G        W    3.10.17-00026-g6d61c92-dirty #29

dtask: d7098000 ti: d70a0000 task.ti: d70a0000

PC is at strnlen+0x1c/0x30

LR is at string.isra.3+0x34/0xc8

pc : []    lr : []    psr: 20000193 // stopped at printk, actually is pr_err.

sp : d70a1c78  ip : 00000020  fp : d70a1d58

r10: c055827b  r9 : c084b0e0  r8 : 00000000

r7 : 60000113  r6 : ffffffff  r5 : c084b0e0  r4 : c084ad0a

r3 : 60000113  r2 : 60000113  r1 : 60000112  r0 : 60000113 // 6000113 is a abnormal address!!!

Flags: nzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment kernel

Control: 10c53c7d  Table: 8000406a  DAC: 00000015

2.  pointer should not NULL

	porch = kzalloc(sizeof(struct timing_rgb), GFP_KERNEL);
	panel = kzalloc(sizeof(struct panel_spec), GFP_KERNEL);
	if (!panel || porch) // judge porch not right, should !porch
		goto alloc_panel_mem_error;

3. The field name doesn't not match

	name = (char *)of_get_property(pan_node,
			"sprdfb,dispc-dsi-panel-name", NULL);
	if (!name) {
		strlcpy(panel->panel_name, "unspecified", PANEL_NAME_LEN);
		ret = -ENXIO;
		pr_err("%s:%d, Panel name not specified\n",
				__func__, __LINE__);
		goto parse_dt_error;
	} else {
		strlcpy(panel->panel_name, name, PANEL_NAME_LEN);
		pr_info("%s: Panel: %s is specified\n",
				__func__, panel->panel_name);
	}

The name in dtsi is "dispc-panel-name", but in source code, the name is "dispc-dsi-panel-name".

4. NULL Pointer

static int sprdfb_panel_parse_dcs_cmds(const struct device_node *np,
				struct dsi_cmds_list *pcmds, char *cmd_key)
{
	const char *data;
	int dt_len = 0, len;
	char *buf, *bp;
	struct dsi_cmd_desc *d;
	int cnt = 0;

	pr_debug("---- enter: %s ----\n", __func__);
	data = of_get_property(np, cmd_key, &dt_len);
	if (!data) {
		pr_err("%s: failed, key=%s\n", __func__, cmd_key);
		return -ENOMEM;
	}

	buf = kzalloc(sizeof(char) * dt_len, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
	memcpy(buf, data, dt_len);
	/* scan dcs commands */
	bp = buf;
	len = dt_len;
	d = (struct dsi_cmd_desc *)bp;
	d->c_len = ntohs(d->c_len); // If d is NULL, now dereference it, kernel will be dumped. 
	while (len > (sizeof(*d) + d->c_len)) {
		if (d->c_len > len) {
			pr_err("%s: dtsi cmd=%x error, len=%d",
				__func__, d->data_type, d->c_len);
			goto exit_free;
		}
		pr_debug("data type is %d, cmd len is %d\n",
				d->data_type, d->c_len);
		bp += sizeof(*d) + d->c_len;
		len -= sizeof(*d) + d->c_len;
		d = (struct dsi_cmd_desc *)bp;
		d->c_len = ntohs(d->c_len);
		cnt++;
	}
	pcmds->cmds_cnt = cnt;
	pcmds->cmds = (struct dsi_cmd_desc *)buf;
	return 0;

exit_free:
	kfree(buf);
	return -EFAULT;
}


5. Init sequence for using dts

1) platform_bus_init 

2) iomap

3) register all platform_driver to platform_driver bus

4) hwspinlock init
   4.1 find the hwspinlock node in DTS
   4.2 register the hwspinlock device to platform_device bus
   4.3 hwspinlock probe() and init the hwspinlock data struct
   4.4 early_init_hwspinlock

5) ADI_init and ADC_init

6) populate the DT, register all devices to platform_device bus

 

6. name is defined wrongly

	data = (char *)of_get_property(node,
			"sprdfb,dispc-dsi-color-mode-polarity", NULL);
	if (data) {
		if (!strcmp(data, "negative"))
			panel->info.mipi->color_mode_pol = SPRDFB_POLARITY_NEG;
		else
			panel->info.mipi->color_mode_pol = SPRDFB_POLARITY_POS;
	} else
		panel->info.mipi->color_mode_pol = SPRDFB_POLARITY_POS;

The field "sprdfb,dispc-dsi-color-mode-polarity" is right, but previously, it is wrong, "-polarity" is missed.
In dtsi, the field is as following:
	sprdfb,dispc-dsi-h-sync-polarity = "positive";
	sprdfb,dispc-dsi-v-sync-polarity = "positive";
	sprdfb,dispc-dsi-de-polarity = "positive";
	sprdfb,dispc-dsi-te-polarity = "positive";
	sprdfb,dispc-dsi-color-mode-polarity = "negative";
	sprdfb,dispc-dsi-shut-down-polarity = "negative";

7. pointer to pointers and dsi commands

static int sprdfb_dsi_tx_cmds(struct panel_spec *panel,
		struct dsi_cmd_desc **cmds, u32 cmds_len, bool force_lp)
{
	int i, time, len;
	struct ops_mipi *ops = panel->info.mipi->ops;
	u8 *data;
	u16 work_mode = panel->info.mipi->work_mode;

	pr_info("%s, len: %d\n", __func__, cmds_len);

	if (!ops->mipi_set_cmd_mode ||
			!ops->mipi_dcs_write ||
			!ops->mipi_set_hs_mode ||
			!ops->mipi_set_lp_mode ||
			!ops->mipi_force_write) {
		pr_err("%s: Expected functions are NULL.\n", __func__);
		return -EFAULT;
	}

	if (force_lp) {
		if (work_mode == SPRDFB_MIPI_MODE_CMD)
			ops->mipi_set_lp_mode();
		else
			ops->mipi_set_data_lp_mode();
	}

	ops->mipi_set_cmd_mode();

	for (i = 0; i < cmds_len; i++) {
		if (cmds[i]->data_type != DCS_SHORT_WRITE_0 &&
				cmds[i]->data_type != DCS_SHORT_WRITE_1) {
			len = cmds[i]->c_len + sizeof(cmds[i]->c_len);
			data = cmds[i]->payload - sizeof(cmds[i]->c_len);
		} else {
			len = cmds[i]->c_len;
			data = cmds[i]->payload;
		}
		pr_debug("dsi write cmd type: %d, len: %d\n",
				cmds[i]->data_type, len);
		if (cmds[i]->data_type == GENERIC_LONG_WRITE)
			ops->mipi_force_write(GENERIC_LONG_WRITE, data, len);
		else
			ops->mipi_dcs_write(data, len);

		if (cmds[i]->wait_time) {
			time = cmds[i]->wait_time * 1000;
			usleep_range(time, time);
		}
	}

	if (force_lp) {
		if (work_mode == SPRDFB_MIPI_MODE_CMD)
			ops->mipi_set_hs_mode();
		else
			ops->mipi_set_data_hs_mode();
	}
	return 0;
}

struct dsi_cmds_list {
	struct dsi_cmd_desc *cmds[PANEL_MAX_CMDS_LEN];
	u32 cmds_cnt;
};

struct dsi_cmd_desc {
	u8 data_type; /* BYTE[0] */
	u8 reserved; /* BYTE[1]: reserved */
	u8 wait_time; /* BYTE[2] */
	u16 c_len; /* BYTE[3...4]: cmd lenght */
	u8 payload[0]; /* BYTE[5] */
} __packed;

/* 05: DCS short Wr; 39: DCS long Wr; 06: DCS read; 15: DCS short Wr */
#define GENERIC_SHORT_WRITE_0		0x03
#define GENERIC_SHORT_WRITE_1		0x13
#define GENERIC_SHORT_WRITE_2		0x23
#define GENERIC_LONG_WRITE			0x29

#define DCS_SHORT_WRITE_0			0x05
#define DCS_SHORT_WRITE_1			0x15
#define DCS_LONG_WRITE				0x39

#define GENERIC_SHORT_READ_0			0x04
#define GENERIC_SHORT_READ_1			0x14
#define GENERIC_SHORT_READ_2			0x24

#define DCS_READ_0					0x06

#define SET_MAX_RET_PKT_SIZE		0x37

Parsing and handling commands of dsi are not easy. They should be paid a lot of attention to.
static int sprdfb_panel_parse_dcs_cmds(const struct device_node *np,
				struct dsi_cmds_list *pcmds, char *cmd_key)
{
	const char *data;
	int dt_len = 0, len, header;
	char *buf, *bp;
	int cnt = 0;

	pr_debug("---- enter: %s ----\n", __func__);
	data = of_get_property(np, cmd_key, &dt_len);
	if (!data) {
		pr_err("%s: failed, key=%s\n", __func__, cmd_key);
		return -ENOMEM;
	}

	buf = kzalloc(sizeof(char) * dt_len, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
	memcpy(buf, data, dt_len);
	/* scan dcs commands */
	bp = buf;
	len = dt_len;
	header = sizeof(struct dsi_cmd_desc);

	while (len > header) {
		pcmds->cmds[cnt] = (struct dsi_cmd_desc *)bp;
		pcmds->cmds[cnt]->c_len = ntohs(pcmds->cmds[cnt]->c_len);
		if (pcmds->cmds[cnt]->c_len > len) {
			pr_err("%s: data type is 0x%x error, len=%d\n",
				__func__, pcmds->cmds[cnt]->data_type,
				pcmds->cmds[cnt]->c_len);
			goto exit_free;
		}
		pr_debug("data type is 0x%x, cmd len is %d, remaining(%d), "
				"cnt(%d)\n", pcmds->cmds[cnt]->data_type,
				pcmds->cmds[cnt]->c_len, len, cnt + 1);

		bp += header;
		len -= header;
		bp += pcmds->cmds[cnt]->c_len;
		len -= pcmds->cmds[cnt]->c_len;
		cnt++;
	}
	pcmds->cmds_cnt = cnt;
	return 0;

exit_free:
	kfree(buf);
	return -EFAULT;
}

6. virtual address and physical address switch

trace32 -> CPU ->  peripheral -> System Control and Config -> SCTLR_EL1 -> M -> enable (enable virtual address) / disable (physical address)

7 linux flags

__attribute__
        __attribute__(section),用来修饰函数时,可以使你把代码放在image的不同段,如:
                    void   f(void)   __attribute__((section(“new_section”)));
函数f()将被放到只读new_section段中,而不是.text中。
        __attribute((regparm(0))),表示不从寄存器中传递参数,如果是regparm(3)则调用函数的时候参数不是通过栈传递,而是直接放到寄存器里,被调用函数直接从寄存器取参数。gcc编译器在汇编过程中调用c语言函数时传递参数有两种方法:一种是通过堆栈,另一种是通过寄存器,缺省使用寄存器,假如想通过堆栈传递,定义的c函数时要在函数前加上宏asmlinkage
       
asmlinkage
        在看内核源代码start_kernel函数时,第一个遇到的疑问是asmlinkage,原来asmlinkage是一个宏,它定义在文件include/asm-i386/linkage.h中,具体如下:
        #define
        asmlinkage CPP_ASMLINKAGE
        __attribute__((regparm(0))),其中CPP_ASMLINKAGE也是一个宏,定义在include/linux/linkage.h头文件中,具体为:      
        #define   CPP_ASMLINKAGE extern "C",
        extern “C”是用来实现C++和C的混合编程,而 __attribute__是关键字,是gcc的C语言扩展,它可以设置函数属性(Function  Attribute)、变量属性(Variable  Attribute)和类型属性(Type  Attribute),总的来说,asmlinkage是个宏,使用它是为了保持参数在stack中,因为从汇编语言到C语言代码参数的传递是通过stack的,它也可能从stack中得到一些不需要的参数,asmlinkage将要解析那些参数。
       
__init
是一个宏定义,定义在arch/um/include/init.h文件中,具体如:
        #define   __init  __attribute__ ((__section__ (".init.text")))                
        这个标志符和函数声明放在一起,它表示gcc编译器在编译的时候需要把这个函数放在.init.text中,而这个section在内核完成初始化之后会被释放掉。
       
__weak
是一个宏定义,定义在include/asm-mips/linkage.h文件中,具体如:
        #define  __weak __attribute__((weak)),它常用于函数和变量的声明和函数的定义,
        是GNU compiler的扩展,被ARM  compiler支持,可以将一个函数声明为weak,
        当没有其他同名函数声明时调用weak,有其他同名函数时调用其他同名函数。
       
__va(x)        
是个宏,定义在include/asm-alpha/page.h文件中,具体如下:
        #define __va(x)  ((void *)((unsigned long)(x) + PAGE_OFFSET))

__pa(x)       
也是个宏,定义在include/asm-alpha/page.h文件中,具体如下:
        #define __pa(x) ((unsigned  long)(x)-PAGE_OFFSET),
其中的PAGE_OFFSET定义为0xffff800000000000UL或0xfffffc0000000000UL
       
register
可以用来修饰变量,表示寄存器变量,只能用于整型和字符变量,表示变量被存储在CPU的寄存器中, 而不像普通变量被存储在内存中,这样可以提高运算速度,它常用于在同一变量名频繁出现的地方,且 它只适用于局部变量和函数的形式参数,它属于auto型变量,不能作为全局变量
       
EXPORT_SYMBOL
       若内核函数或变量要被内核模块调用,则必须使用EXPORT_SYMBOL宏进行处理,作用之一是将该符        号连接到二进制文件的各个__ksymtab_xx_xx section,内核加载模块时,会先确认该模块调用的各内         核函数是否已export,也就是说EXPORT_SYMBOL可以将一个函数或变量以符号的方式导出给其他模块使用。

你可能感兴趣的:(linux,kernel)