/*
 *  linux/kernel/panic.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/*
 * This function is used through-out the kernel (including mm and fs)
 * to indicate a major problem.
 */
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/sysrq.h>
#include <linux/interrupt.h>
#include <linux/vt_kern.h>
#include <linux/pc_keyb.h>
#include <linux/console.h>


#if defined(__i386__) && defined(CONFIG_KMSGDUMP)
extern void machine_dump(int);
#endif

asmlinkage void sys_sync(void);	/* it's really int */

int panic_timeout;

struct notifier_block *panic_notifier_list;

static int __init panic_setup(char *str)
{
	panic_timeout = simple_strtoul(str, NULL, 0);
	return 1;
}

__setup("panic=", panic_setup);

int machine_paniced; 

#if (defined(CONFIG_X86) && defined(CONFIG_VT)) || defined(CONFIG_PC_KEYB)
#define do_blink(x) pckbd_blink(x)
#else
#define do_blink(x) 0
#endif

#ifdef CONFIG_PANIC_MORSE

static int blink_setting = 1;

static const unsigned char morsetable[] = {
	0122, 0, 0310, 0, 0, 0163,			/* "#$%&' */
	055, 0155, 0, 0, 0163, 0141, 0152, 0051, 	/* ()*+,-./ */
	077, 076, 074, 070, 060, 040, 041, 043, 047, 057, /* 0-9 */
	0107, 0125, 0, 0061, 0, 0114, 0, 		/* :;<=>?@ */
	006, 021, 025, 011, 002, 024, 013, 020, 004,	/* A-I */
	036, 015, 022, 007, 005, 017, 026, 033, 012,	/* J-R */
	010, 003, 014, 030, 016, 031, 035, 023,		/* S-Z */
	0, 0, 0, 0, 0154				/* [\]^_ */
};

__inline__ unsigned char tomorse(char c) {
	if (c >= 'a' && c <= 'z')
		c = c - 'a' + 'A';
	if (c >= '"' && c <= '_') {
		return morsetable[c - '"'];
	} else
		return 0;
}


#define DITLEN (HZ / 5)
#define DAHLEN 3 * DITLEN
#define SPACELEN 7 * DITLEN

#define FREQ 844

/* Tell the user who may be running in X and not see the console that we have 
   panic'ed. This is to distingush panics from "real" lockups. 
   Could in theory send the panic message as morse, but that is left as an
   exercise for the reader.  
   And now it's done! LED and speaker morse code by Andrew Rodland 
   <arodland@noln.com>, with improvements based on suggestions from
   linux@horizon.com and a host of others.
*/ 

void panic_blink(char *buf)
{ 
	static unsigned long next_jiffie = 0;
	static char * bufpos = 0;
	static unsigned char morse = 0;
	static char state = 1;
	
	if (!blink_setting) 
		return;

	if (!buf)
		buf="Panic lost?";


	if (bufpos && time_after (next_jiffie, jiffies)) {
		return; /* Waiting for something. */
	}

	if (state) { /* Coming off of a blink. */
		if (blink_setting & 0x01)
			do_blink(0);

		state = 0;

		if(morse > 1) { /* Not done yet, just a one-dit pause. */
			next_jiffie = jiffies + DITLEN;
		} else { /* Get a new char, and figure out how much space. */
			
			if(!bufpos)
				bufpos = (char *)buf; /* First time through */

			if(!*bufpos) {
				bufpos = (char *)buf; /* Repeating */
				next_jiffie = jiffies + SPACELEN;
			} else {
				/* Inter-letter space */
				next_jiffie = jiffies + DAHLEN; 
			}

			if (! (morse = tomorse(*bufpos) )) {
				next_jiffie = jiffies + SPACELEN;
				state = 1; /* And get us back here */
			}
			bufpos ++;
		}
	} else { /* Starting a new blink. We have valid code in morse. */
		int len;

		len = (morse & 001) ? DAHLEN : DITLEN;

		if (blink_setting & 0x02)
			kd_mksound(FREQ, len);
		
		next_jiffie = jiffies + len;

		if (blink_setting & 0x01)
			do_blink(1);
		state = 1;
		morse >>= 1;
	}
}  

#else /* CONFIG_PANIC_MORSE */

static int blink_setting = HZ / 2; /* Over here, it's jiffies between blinks. */

/* This is the "original" 2.4-ac panic_blink, rewritten to use my
 * sorta-arch-independent do_blink stuff.
 */
void panic_blink(char *buf) {
	static char state = 0;
	static unsigned long next_jiffie = 0;

	if (!blink_setting)
		return;

	if (jiffies >= next_jiffie) {
		state ^= 1;
		do_blink(state);
		next_jiffie = jiffies + blink_setting;
	}

	return;
}

#endif /* CONFIG_PANIC_MORSE */

static int __init panicblink_setup(char *str)
{
	int par;
	if (get_option(&str,&par)) 
		blink_setting = par;
	return 1;
}

/* panicblink=0 disables the blinking as it caused problems with some console
   switches. */
__setup("panicblink=", panicblink_setup);


/**
 *	panic - halt the system
 *	@fmt: The text string to print
 *
 *	Display a message, then perform cleanups. Functions in the panic
 *	notifier list are called after the filesystem cache is flushed (when possible).
 *
 *	This function never returns.
 */
 
NORET_TYPE void panic(const char * fmt, ...)
{
	static char buf[1024];
	va_list args;
#if defined(CONFIG_ARCH_S390)
        unsigned long caller = (unsigned long) __builtin_return_address(0);
#endif

#ifdef CONFIG_VT
	disable_console_blank();
#endif
	machine_paniced = 1;
	
	bust_spinlocks(1);
	va_start(args, fmt);
	vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);
	sys_sync();
	printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);
	bust_spinlocks(0);

#ifdef CONFIG_SMP
	smp_send_stop();
#endif

#if defined(__i386__) && defined(CONFIG_KMSGDUMP)
	/* We allow SysRq to be used for <panic_timeout> seconds, and
	   then the dump is forced. This way, if IRQs are frozen and
	   keyboard doesn't respond anymore, the system can dump messages
	   and reboot itself.
	   This code has been voluntarily inserted before notifier_call_chain
	   to ensure proper execution in case of crash. Please tell me if this
	   can be wrong.
	*/
	printk(KERN_EMERG "Dumping messages in %d seconds : last chance for Alt-SysRq...",
	       panic_timeout);
	sti();
	for(panic_timeout*=10; panic_timeout>0; panic_timeout--) {
		CHECK_EMERGENCY_SYNC;
		mdelay(100);
	}
	machine_dump(0);	/* 0 tells machine_dump that it's called from panic() */
#endif
	notifier_call_chain(&panic_notifier_list, 0, buf);

	if (panic_timeout > 0)
	{
		/*
	 	 * Delay timeout seconds before rebooting the machine. 
		 * We can't use the "normal" timers since we just panicked..
	 	 */
		printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout);

#ifdef CONFIG_FBCON_SPLASHSCREEN
		{
			extern int splash_verbose(void);
			(void)splash_verbose();
		}
#endif
		mdelay(panic_timeout*1000);
		/*
		 *	Should we run the reboot notifier. For the moment Im
		 *	choosing not too. It might crash, be corrupt or do
		 *	more harm than good for other reasons.
		 */
		machine_restart(NULL);
	}
#ifdef __sparc__
	{
		extern int stop_a_enabled;
		/* Make sure the user can actually press L1-A */
		stop_a_enabled = 1;
		printk("Press L1-A to return to the boot prom\n");
	}
#endif
#if defined(CONFIG_ARCH_S390)
        disabled_wait(caller);
#endif
	sti();
#ifdef CONFIG_FBCON_SPLASHSCREEN
	{
		extern int splash_verbose(void);
		(void)splash_verbose();
	}
#endif
	for(;;) {
		panic_blink(buf);
		CHECK_EMERGENCY_SYNC
	}
}

/**
 *	print_tainted - return a string to represent the kernel taint state.
 *
 *  'P' - Proprietory module has been loaded.
 *  'F' - Module has been forcibly loaded.
 *  'S' - SMP with CPUs not designed for SMP.
 *
 *	The string is overwritten by the next call to print_taint().
 */
 
const char *print_tainted()
{
	static char buf[20];
	if (tainted) {
		snprintf(buf, sizeof(buf), "Tainted: %c%c%c",
			tainted & TAINT_PROPRIETORY_MODULE ? 'P' : 'G',
			tainted & TAINT_FORCED_MODULE ? 'F' : ' ',
			tainted & TAINT_UNSAFE_SMP ? 'S' : ' ');
	}
	else
		snprintf(buf, sizeof(buf), "Not tainted");
	return(buf);
}

int tainted = 0;

/*
 * A BUG() call in an inline function in a header should be avoided,
 * because it can seriously bloat the kernel.  So here we have
 * helper functions.
 * We lose the BUG()-time file-and-line info this way, but it's
 * usually not very useful from an inline anyway.  The backtrace
 * tells us what we want to know.
 */

void __out_of_line_bug(int line)
{
	printk("kernel BUG in header file at line %d\n", line);

	BUG();

	/* Satisfy __attribute__((noreturn)) */
	for ( ; ; )
		;
}
