When Linux kernel panic, what can we do ?

本文介绍如何在Freescale iMX515硬件上,使用Linux内核2.6.31,在LCD显示“boot failed!”信息,以响应内核panic情况。通过修改fbcon.c和panic.c文件实现。
注意: 版权所有, 转载请注明出处.

Caution: All rights reserved, Please indicate the source if reproduce.


Hardware: Freescale iMX515
Software: Linux kernel 2.6.31
Target requirement: show "boot failed !" in LCD while Linux kernel panic.
Technology methods: use Notifier chains of kernel to do.
Reference to:
[1]. http://bbs.chinaunix.net/thread-2011776-1-1.html
[2]. Linux kernel source code tree: /include/linux/notifier.h, kernel/notifier.c
Modification souce code:
drivers/video/console/fbcon.c
kernel/panic.c

1. modify drivers/video/console/fbcon.c

1.1 modify function fbcon_init()

static void fbcon_init(struct vc_data *vc, int init)
{
	/* omit ... */

	/* show boot information in LCD */
	#if defined(CONFIG_LOGO_LINUX_CLUT224)
	show_boot.info = registered_fb[con2fb_map[vc->vc_num]];
	show_boot.ops = show_boot.info->fbcon_par;
	show_boot.vc = vc;
	show_boot.p = &fb_display[vc->vc_num];
	ret = kernel_thread(show_boot_info_thread, (void *) &show_boot, CLONE_KERNEL);
	if(ret < 0)
	{
		pr_err("Failed to start show_boot_info_thread daemon\n");
	}
	else
	{
		pr_notice("Successful to start show_boot_info_thread daemon\n");
	}	
	#endif /* CONFIG_LOGO_LINUX_CLUT224 */
}

1.2 add below codes in this file

#if defined(CONFIG_LOGO_LINUX_CLUT224)
/* Wenxy add begin, 20111014 */
struct show_boot_in_lcd
{
	struct fbcon_ops *ops;
	struct vc_data *vc;
	struct fb_info *info;
	struct display *p;	
};

static struct show_boot_in_lcd show_boot; 

static RAW_NOTIFIER_HEAD(lcd_show_chain); 	/* show error information in LCD */

/* LCD show error information */
static int lcd_tip_error(struct notifier_block *this, unsigned long event, void *ptr)
{
	struct vc_data *vc;
	struct fb_info *info;
	struct fbcon_ops *ops;
	struct display *p;
	int yy; 
	int xx;
	int fg;
	int bg;
	unsigned int len;
	unsigned char tips[]="b o o t   f a i l e d   ! ";
	
	if(!show_boot.ops)
	{
		return -EINVAL;
	}
	
	vc = show_boot.vc;
	info = show_boot.info;
	ops = show_boot.ops;
	p = show_boot.p;	

	xx = 5; 	/* 62 */
	yy = 28;
	fg = 0x04; 	/* red: 0x04 */
	bg = 0x7; 	/* vc->vc_halfcolor; */
	len = strlen(tips)/2;
	ops->putcs(vc, info, (unsigned short *)tips, len, real_y(p, yy), real_y(p, xx), fg, bg);
	
	return 0; 
}


static struct notifier_block lcd_notifier_error =
{        
	.notifier_call = lcd_tip_error,
};


int lcd_notifier_call_chain(unsigned long val, void *v)
{
	return raw_notifier_call_chain(&lcd_show_chain, 1, NULL);

}
EXPORT_SYMBOL(lcd_notifier_call_chain);


/* Show boot information in LCD */
static int show_boot_info_thread(void * dummy)
{
	struct vc_data *vc;
	struct fb_info *info;
	struct fbcon_ops *ops;
	struct display *p;	
	const unsigned short *s;
	int count; 
	int yy; 
	int xx;
	int fg;
	int bg;
	int softback_lines;
	int mode;
	unsigned int n, index, len1, len2;
	#define FM_VERSION "Firmware version: " 	/* UTS_RELEASE */
	unsigned char version[256], substr1[128], substr2[128];
	unsigned char bar[]="                                                                                                                                                                       ";

	unsigned int time_count, pass_count, bar_count;
	#define SLEEP_MS			100	/* millisecond, 100,500 */
	#define BOOT_KERNEL_TIME	10	/* second, 10 */
	#define BAR_LEN				90	/* boot progress bar length */
	
	if(!dummy)
	{
		return -EINVAL;
	}
	vc = ((struct show_boot_in_lcd *)dummy)->vc;
	info = ((struct show_boot_in_lcd *)dummy)->info;
	ops = ((struct show_boot_in_lcd *)dummy)->ops;
	p = ((struct show_boot_in_lcd *)dummy)->p;

	time_count = (BOOT_KERNEL_TIME*1000)/SLEEP_MS;
	pass_count = 1;
	/*
	time_count = 180;
	pass_count = 1;
	bar_count = BAR_LEN/time_count;
	*/

	/* register notifier block */
	if(raw_notifier_chain_register(&lcd_show_chain, &lcd_notifier_error))
	{
		pr_warning("Warning: into %s, %s, raw_notifier_chain_register call failed\n", __FILE__, __FUNCTION__);
	}
	
	/* show version in LCD */
	memset(substr1, 0, sizeof(substr1));
	memset(substr2, 0, sizeof(substr2));
	memset(version, 0, sizeof(version));
	memcpy(substr1, FM_VERSION, sizeof(FM_VERSION));
	memcpy(substr2, UTS_RELEASE, sizeof(UTS_RELEASE));
	
	len1 = 2*strlen(FM_VERSION);
	index = 0;
	for(n = 0; n < len1; n+=2)
	{
		version[n] = substr1[index++];
		version[n+1] = ' ';
	}
	
	len2 = 2*strlen(UTS_RELEASE);
	index = 0;
	for(n = 0; n < len2; n+=2)
	{
		version[len1 + n] = substr2[index++];
		version[len1 + n+1] = ' ';
	}	
	/*pr_notice("Debug:%s\n", version);*/
	
	xx = 55; 	/* 62 */
	yy = 28;
	fg = 0x00; 	/* 0x00 */
	bg = 0x7; 	/* vc->vc_halfcolor; */
	ops->putcs(vc, info, (unsigned short *)version, (len1+len2)/2, real_y(p, yy), real_y(p, xx), fg, bg);
	
	/* draw progress bar in LCD */
	xx = 5;
	yy = 26;
	fg = 0x7;
	bg = fg;
	ops->putcs(vc, info, (unsigned short *)bar, BAR_LEN, real_y(p, yy), real_y(p, xx), fg, bg);
		
	while(pass_count <= time_count)
	{
		xx = 5;
		yy = 26;
		fg = 0x2;
		bg = 0x2;	
		
		ops->putcs(vc, info, (unsigned short *)bar, pass_count>BAR_LEN?BAR_LEN:pass_count, \
			real_y(p, yy), real_y(p, xx), fg, bg);
		/*
		ops->putcs(vc, info, (unsigned short *)bar, pass_count*bar_count, \
			real_y(p, yy), real_y(p, xx), fg, bg);
		*/
		pass_count++;
		
		#if 0
		ops->cursor_flash = 0; /* stop cursor flash */
		vc->vc_x = 0; 		/* Cursor position */
		vc->vc_y = 0;
		vc->vc_deccm = 0;	/* Cursor cannot Visible */
		#endif	
		
		mdelay(SLEEP_MS);
	}

	pr_notice("Successful to exit show_boot_info_thread daemon\n");
	#if 0
	vc->vc_deccm = 1;
	ops->cursor_flash = 1;
	#endif
	/* unregister notifier block */
	if(raw_notifier_chain_unregister(&lcd_show_chain, &lcd_notifier_error))
	{
		pr_warning("Warning: into %s, %s, raw_notifier_chain_unregister call failed\n", __FILE__, __FUNCTION__);
	}
	
	return 0;
}
/* Wenxy add end */
#endif /* LOGO_LINUX_CLUT224 */

2. modify kernel/panic.c
2.1 add the code in this file

extern int lcd_notifier_call_chain(unsigned long val, void *v);

2.2 modify function panic()

/**
 *	panic - halt the system
 *	@fmt: The text string to print
 *
 *	Display a message, then perform cleanups.
 *
 *	This function never returns.
 */
NORET_TYPE void panic(const char * fmt, ...)
{
	static char buf[1024];
	va_list args;
	long i;
	
	#if defined(CONFIG_LOGO_LINUX_CLUT224)
	/* notification show error information in LCD */
	if(lcd_notifier_call_chain(1, NULL))
	{
		pr_warning("Warning: into %s, %s, lcd_notifier_call_chain call failed\n", __FILE__, __FUNCTION__);
	}
	/*mdelay(1 * 1000);*/
	#endif /* LOGO_LINUX_CLUT224 */
	
	/*
	 * It's possible to come here directly from a panic-assertion and
	 * not have preempt disabled. Some functions called from here want
	 * preempt to be disabled. No point enabling it later though...
	 */
	preempt_disable();

	bust_spinlocks(1);
	va_start(args, fmt);
	vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);
	printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);
#ifdef CONFIG_DEBUG_BUGVERBOSE
	dump_stack();
#endif

	/* omit ... */
}

3. conifg Kernel
#make menuconfig
Select this menu items
Device Drivers ---> Graphics support ---> [*] Bootup logo ---> [*] Standard 224-color Linux logo (NEW)

4. build Kernel
#make -j 4; make uImage



请告诉我具体的实验步骤: Lab: system calls In the last lab you used system calls to write a few utilities. In this lab you will add some new system calls to xv6, which will help you understand how they work and will expose you to some of the internals of the xv6 kernel. You will add more system calls in later labs. Before you start coding, read Chapter 2 of the xv6 book, and Sections 4.3 and 4.4 of Chapter 4, and related source files: • The user-space "stubs" that route system calls into the kernel are in user/usys.S, which is generated by user/usys.pl when you run make. Declarations are in user/user.h • The kernel-space code that routes a system call to the kernel function that implements it is in kernel/syscall.c and kernel/syscall.h. • Process-related code is kernel/proc.h and kernel/proc.c. To start the lab, switch to the syscall branch: $ git fetch $ git checkout syscall $ make clean If you run make grade you will see that the grading script cannot exec trace and sysinfotest. Your job is to add the necessary system calls and stubs to make them work. Using gdb (easy) In many cases, print statements will be sufficient to debug your kernel, but sometimes being able to single step through some assembly code or inspecting the variables on the stack is helpful. To learn more about how to run GDB and the common issues that can arise when using GDB, check out this page. To help you become familiar with gdb, run and then fire up gdb in another window (see the gdb bullet on the guidance page). Once you have two windows open, type in the gdb window: make qemu-gdb (gdb) b syscall Breakpoint 1 at 0x80002142: file kernel/syscall.c, line 243. (gdb) c Continuing. [Switching to Thread 1.2] Thread 2 hit Breakpoint 1, syscall () at kernel/syscall.c:243 243 { (gdb) layout src (gdb) backtrace The layout command splits the window in two, showing where gdb is in the source code. The backtrace prints out the stack backtrace. See Using the GNU Debugger for helpful GDB commands. Answer the following questions in answers-syscall.txt. Looking at the backtrace output, which function called syscall? Type a few times to step past struct proc *p = myproc(); Once past this statement, type , which prints the current process's proc struct (see kernel/proc.h>) in hex. np /x *p What is the value of p->trapframe->a7 and what does that value represent? (Hint: look user/initcode.S, the first user program xv6 starts.) The processor is running in kernel mode, and we can print privileged registers such as sstatus (see RISC-V privileged instructions for a description): (gdb) p /x $sstatus What was the previous mode that the CPU was in? In the subsequent part of this lab (or in following labs), it may happen that you make a programming error that causes the xv6 kernel to panic. For example, replace the statement num = p->trapframe->a7; with num = * (int *) 0; at the beginning of syscall, run , and you will see something similar to: make qemu xv6 kernel is booting hart 2 starting hart 1 starting scause 0x000000000000000d sepc=0x000000008000215a stval=0x0000000000000000 panic: kerneltrap Quit out of qemu. To track down the source of a kernel page-fault panic, search for the sepc value printed for the panic you just saw in the file kernel/kernel.asm, which contains the assembly for the compiled kernel. Write down the assembly instruction the kernel is panicing at. Which register corresponds to the variable num? To inspect the state of the processor and the kernel at the faulting instruction, fire up gdb, and set a breakpoint at the faulting epc, like this: (gdb) b *0x000000008000215a Breakpoint 1 at 0x8000215a: file kernel/syscall.c, line 247. (gdb) layout asm (gdb) c Continuing. [Switching to Thread 1.3] Thread 3 hit Breakpoint 1, syscall () at kernel/syscall.c:247 Confirm that the faulting assembly instruction is the same as the one you found above. Why does the kernel crash? Hint: look at figure 3-3 in the text; is address 0 mapped in the kernel address space? Is that confirmed by the value in scause above? (See description of scause in RISC-V privileged instructions) Note that scause was printed by the kernel panic above, but often you need to look at additional info to track down the problem that caused the panic. For example, to find out which user process was running when the kernel paniced, you can print out the process's name: (gdb) p p->name What is the name of the binary that was running when the kernel paniced? What is its process id (pid)? This concludes a brief introduction to tracking down bugs with gdb; it is worth your time to revisit Using the GNU Debugger when tracking down kernel bugs. The guidance page also has some other other useful debugging tips.
最新发布
11-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值