mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-19 02:34:01 +08:00
[AVR32] Clean up exception handling code
* Use generic BUG() handling * Remove some useless debug statements * Use a common function _exception() to send signals or oops when an exception can't be handled. This makes sure init doesn't enter an infinite exception loop as well. Borrowed from powerpc. * Add some basic exception tracing support to the page fault code. * Rework dump_stack(), show_regs() and friends and move everything into process.c * Print information about configuration options and chip type when oopsing Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
This commit is contained in:
parent
3b328c9809
commit
623b0355d5
@ -68,6 +68,11 @@ config GENERIC_CALIBRATE_DELAY
|
||||
bool
|
||||
default y
|
||||
|
||||
config GENERIC_BUG
|
||||
bool
|
||||
default y
|
||||
depends on BUG
|
||||
|
||||
source "init/Kconfig"
|
||||
|
||||
menu "System Type and features"
|
||||
|
@ -12,10 +12,11 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
void *module_alloc(unsigned long size)
|
||||
@ -315,10 +316,10 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
||||
vfree(module->arch.syminfo);
|
||||
module->arch.syminfo = NULL;
|
||||
|
||||
return 0;
|
||||
return module_bug_finalize(hdr, sechdrs, module);
|
||||
}
|
||||
|
||||
void module_arch_cleanup(struct module *module)
|
||||
{
|
||||
|
||||
module_bug_cleanup(module);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <asm/sysreg.h>
|
||||
@ -115,39 +116,178 @@ void release_thread(struct task_struct *dead_task)
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
static void dump_mem(const char *str, const char *log_lvl,
|
||||
unsigned long bottom, unsigned long top)
|
||||
{
|
||||
unsigned long p;
|
||||
int i;
|
||||
|
||||
printk("%s%s(0x%08lx to 0x%08lx)\n", log_lvl, str, bottom, top);
|
||||
|
||||
for (p = bottom & ~31; p < top; ) {
|
||||
printk("%s%04lx: ", log_lvl, p & 0xffff);
|
||||
|
||||
for (i = 0; i < 8; i++, p += 4) {
|
||||
unsigned int val;
|
||||
|
||||
if (p < bottom || p >= top)
|
||||
printk(" ");
|
||||
else {
|
||||
if (__get_user(val, (unsigned int __user *)p)) {
|
||||
printk("\n");
|
||||
goto out;
|
||||
}
|
||||
printk("%08x ", val);
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
|
||||
{
|
||||
return (p > (unsigned long)tinfo)
|
||||
&& (p < (unsigned long)tinfo + THREAD_SIZE - 3);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs, const char *log_lvl)
|
||||
{
|
||||
unsigned long lr, fp;
|
||||
struct thread_info *tinfo;
|
||||
|
||||
if (regs)
|
||||
fp = regs->r7;
|
||||
else if (tsk == current)
|
||||
asm("mov %0, r7" : "=r"(fp));
|
||||
else
|
||||
fp = tsk->thread.cpu_context.r7;
|
||||
|
||||
/*
|
||||
* Walk the stack as long as the frame pointer (a) is within
|
||||
* the kernel stack of the task, and (b) it doesn't move
|
||||
* downwards.
|
||||
*/
|
||||
tinfo = task_thread_info(tsk);
|
||||
printk("%sCall trace:\n", log_lvl);
|
||||
while (valid_stack_ptr(tinfo, fp)) {
|
||||
unsigned long new_fp;
|
||||
|
||||
lr = *(unsigned long *)fp;
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
printk("%s [<%08lx>] ", log_lvl, lr);
|
||||
#else
|
||||
printk(" [<%08lx>] ", lr);
|
||||
#endif
|
||||
print_symbol("%s\n", lr);
|
||||
|
||||
new_fp = *(unsigned long *)(fp + 4);
|
||||
if (new_fp <= fp)
|
||||
break;
|
||||
fp = new_fp;
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#else
|
||||
static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs, const char *log_lvl)
|
||||
{
|
||||
unsigned long addr;
|
||||
|
||||
printk("%sCall trace:\n", log_lvl);
|
||||
|
||||
while (!kstack_end(sp)) {
|
||||
addr = *sp++;
|
||||
if (kernel_text_address(addr)) {
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
printk("%s [<%08lx>] ", log_lvl, addr);
|
||||
#else
|
||||
printk(" [<%08lx>] ", addr);
|
||||
#endif
|
||||
print_symbol("%s\n", addr);
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
|
||||
struct pt_regs *regs, const char *log_lvl)
|
||||
{
|
||||
struct thread_info *tinfo;
|
||||
|
||||
if (sp == 0) {
|
||||
if (tsk)
|
||||
sp = tsk->thread.cpu_context.ksp;
|
||||
else
|
||||
sp = (unsigned long)&tinfo;
|
||||
}
|
||||
if (!tsk)
|
||||
tsk = current;
|
||||
|
||||
tinfo = task_thread_info(tsk);
|
||||
|
||||
if (valid_stack_ptr(tinfo, sp)) {
|
||||
dump_mem("Stack: ", log_lvl, sp,
|
||||
THREAD_SIZE + (unsigned long)tinfo);
|
||||
show_trace_log_lvl(tsk, (unsigned long *)sp, regs, log_lvl);
|
||||
}
|
||||
}
|
||||
|
||||
void show_stack(struct task_struct *tsk, unsigned long *stack)
|
||||
{
|
||||
show_stack_log_lvl(tsk, (unsigned long)stack, NULL, "");
|
||||
}
|
||||
|
||||
void dump_stack(void)
|
||||
{
|
||||
unsigned long stack;
|
||||
|
||||
show_trace_log_lvl(current, &stack, NULL, "");
|
||||
}
|
||||
EXPORT_SYMBOL(dump_stack);
|
||||
|
||||
static const char *cpu_modes[] = {
|
||||
"Application", "Supervisor", "Interrupt level 0", "Interrupt level 1",
|
||||
"Interrupt level 2", "Interrupt level 3", "Exception", "NMI"
|
||||
};
|
||||
|
||||
void show_regs(struct pt_regs *regs)
|
||||
void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl)
|
||||
{
|
||||
unsigned long sp = regs->sp;
|
||||
unsigned long lr = regs->lr;
|
||||
unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT;
|
||||
|
||||
if (!user_mode(regs))
|
||||
if (!user_mode(regs)) {
|
||||
sp = (unsigned long)regs + FRAME_SIZE_FULL;
|
||||
|
||||
print_symbol("PC is at %s\n", instruction_pointer(regs));
|
||||
print_symbol("LR is at %s\n", lr);
|
||||
printk("pc : [<%08lx>] lr : [<%08lx>] %s\n"
|
||||
"sp : %08lx r12: %08lx r11: %08lx\n",
|
||||
instruction_pointer(regs),
|
||||
lr, print_tainted(), sp, regs->r12, regs->r11);
|
||||
printk("r10: %08lx r9 : %08lx r8 : %08lx\n",
|
||||
regs->r10, regs->r9, regs->r8);
|
||||
printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
|
||||
regs->r7, regs->r6, regs->r5, regs->r4);
|
||||
printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
|
||||
regs->r3, regs->r2, regs->r1, regs->r0);
|
||||
printk("Flags: %c%c%c%c%c\n",
|
||||
printk("%s", log_lvl);
|
||||
print_symbol("PC is at %s\n", instruction_pointer(regs));
|
||||
printk("%s", log_lvl);
|
||||
print_symbol("LR is at %s\n", lr);
|
||||
}
|
||||
|
||||
printk("%spc : [<%08lx>] lr : [<%08lx>] %s\n"
|
||||
"%ssp : %08lx r12: %08lx r11: %08lx\n",
|
||||
log_lvl, instruction_pointer(regs), lr, print_tainted(),
|
||||
log_lvl, sp, regs->r12, regs->r11);
|
||||
printk("%sr10: %08lx r9 : %08lx r8 : %08lx\n",
|
||||
log_lvl, regs->r10, regs->r9, regs->r8);
|
||||
printk("%sr7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
|
||||
log_lvl, regs->r7, regs->r6, regs->r5, regs->r4);
|
||||
printk("%sr3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
|
||||
log_lvl, regs->r3, regs->r2, regs->r1, regs->r0);
|
||||
printk("%sFlags: %c%c%c%c%c\n", log_lvl,
|
||||
regs->sr & SR_Q ? 'Q' : 'q',
|
||||
regs->sr & SR_V ? 'V' : 'v',
|
||||
regs->sr & SR_N ? 'N' : 'n',
|
||||
regs->sr & SR_Z ? 'Z' : 'z',
|
||||
regs->sr & SR_C ? 'C' : 'c');
|
||||
printk("Mode bits: %c%c%c%c%c%c%c%c%c\n",
|
||||
printk("%sMode bits: %c%c%c%c%c%c%c%c%c\n", log_lvl,
|
||||
regs->sr & SR_H ? 'H' : 'h',
|
||||
regs->sr & SR_R ? 'R' : 'r',
|
||||
regs->sr & SR_J ? 'J' : 'j',
|
||||
@ -157,9 +297,21 @@ void show_regs(struct pt_regs *regs)
|
||||
regs->sr & SR_I1M ? '1' : '.',
|
||||
regs->sr & SR_I0M ? '0' : '.',
|
||||
regs->sr & SR_GM ? 'G' : 'g');
|
||||
printk("CPU Mode: %s\n", cpu_modes[mode]);
|
||||
printk("%sCPU Mode: %s\n", log_lvl, cpu_modes[mode]);
|
||||
printk("%sProcess: %s [%d] (task: %p thread: %p)\n",
|
||||
log_lvl, current->comm, current->pid, current,
|
||||
task_thread_info(current));
|
||||
}
|
||||
|
||||
show_trace(NULL, (unsigned long *)sp, regs);
|
||||
void show_regs(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long sp = regs->sp;
|
||||
|
||||
if (!user_mode(regs))
|
||||
sp = (unsigned long)regs + FRAME_SIZE_FULL;
|
||||
|
||||
show_regs_log_lvl(regs, "");
|
||||
show_trace_log_lvl(current, (unsigned long *)sp, regs, "");
|
||||
}
|
||||
EXPORT_SYMBOL(show_regs);
|
||||
|
||||
|
@ -5,158 +5,25 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#undef DEBUG
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/traps.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/addrspace.h>
|
||||
#include <asm/ocd.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
|
||||
{
|
||||
unsigned long p;
|
||||
int i;
|
||||
|
||||
printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
|
||||
|
||||
for (p = bottom & ~31; p < top; ) {
|
||||
printk("%04lx: ", p & 0xffff);
|
||||
|
||||
for (i = 0; i < 8; i++, p += 4) {
|
||||
unsigned int val;
|
||||
|
||||
if (p < bottom || p >= top)
|
||||
printk(" ");
|
||||
else {
|
||||
if (__get_user(val, (unsigned int __user *)p)) {
|
||||
printk("\n");
|
||||
goto out;
|
||||
}
|
||||
printk("%08x ", val);
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
|
||||
{
|
||||
return (p > (unsigned long)tinfo)
|
||||
&& (p < (unsigned long)tinfo + THREAD_SIZE - 3);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long lr, fp;
|
||||
struct thread_info *tinfo;
|
||||
|
||||
tinfo = (struct thread_info *)
|
||||
((unsigned long)sp & ~(THREAD_SIZE - 1));
|
||||
|
||||
if (regs)
|
||||
fp = regs->r7;
|
||||
else if (tsk == current)
|
||||
asm("mov %0, r7" : "=r"(fp));
|
||||
else
|
||||
fp = tsk->thread.cpu_context.r7;
|
||||
|
||||
/*
|
||||
* Walk the stack as long as the frame pointer (a) is within
|
||||
* the kernel stack of the task, and (b) it doesn't move
|
||||
* downwards.
|
||||
*/
|
||||
while (valid_stack_ptr(tinfo, fp)) {
|
||||
unsigned long new_fp;
|
||||
|
||||
lr = *(unsigned long *)fp;
|
||||
printk(" [<%08lx>] ", lr);
|
||||
print_symbol("%s\n", lr);
|
||||
|
||||
new_fp = *(unsigned long *)(fp + 4);
|
||||
if (new_fp <= fp)
|
||||
break;
|
||||
fp = new_fp;
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#else
|
||||
static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long addr;
|
||||
|
||||
while (!kstack_end(sp)) {
|
||||
addr = *sp++;
|
||||
if (kernel_text_address(addr)) {
|
||||
printk(" [<%08lx>] ", addr);
|
||||
print_symbol("%s\n", addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void show_trace(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (regs &&
|
||||
(((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
|
||||
((regs->sr & MODE_MASK) == MODE_USER)))
|
||||
return;
|
||||
|
||||
printk ("Call trace:");
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
printk("\n");
|
||||
#endif
|
||||
|
||||
__show_trace(tsk, sp, regs);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
void show_stack(struct task_struct *tsk, unsigned long *sp)
|
||||
{
|
||||
unsigned long stack;
|
||||
|
||||
if (!tsk)
|
||||
tsk = current;
|
||||
if (sp == 0) {
|
||||
if (tsk == current) {
|
||||
register unsigned long *real_sp __asm__("sp");
|
||||
sp = real_sp;
|
||||
} else {
|
||||
sp = (unsigned long *)tsk->thread.cpu_context.ksp;
|
||||
}
|
||||
}
|
||||
|
||||
stack = (unsigned long)sp;
|
||||
dump_mem("Stack: ", stack,
|
||||
THREAD_SIZE + (unsigned long)tsk->thread_info);
|
||||
show_trace(tsk, sp, NULL);
|
||||
}
|
||||
|
||||
void dump_stack(void)
|
||||
{
|
||||
show_stack(NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(dump_stack);
|
||||
#include <asm/ocd.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
|
||||
|
||||
int register_die_notifier(struct notifier_block *nb)
|
||||
{
|
||||
pr_debug("register_die_notifier: %p\n", nb);
|
||||
|
||||
return atomic_notifier_chain_register(&avr32_die_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(register_die_notifier);
|
||||
@ -169,93 +36,103 @@ EXPORT_SYMBOL(unregister_die_notifier);
|
||||
|
||||
static DEFINE_SPINLOCK(die_lock);
|
||||
|
||||
void __die(const char *str, struct pt_regs *regs, unsigned long err,
|
||||
const char *file, const char *func, unsigned long line)
|
||||
void NORET_TYPE die(const char *str, struct pt_regs *regs, long err)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
static int die_counter;
|
||||
|
||||
console_verbose();
|
||||
spin_lock_irq(&die_lock);
|
||||
bust_spinlocks(1);
|
||||
|
||||
printk(KERN_ALERT "%s", str);
|
||||
if (file && func)
|
||||
printk(" in %s:%s, line %ld", file, func, line);
|
||||
printk("[#%d]:\n", ++die_counter);
|
||||
print_modules();
|
||||
show_regs(regs);
|
||||
printk("Process %s (pid: %d, stack limit = 0x%p)\n",
|
||||
tsk->comm, tsk->pid, tsk->thread_info + 1);
|
||||
|
||||
if (!user_mode(regs) || in_interrupt()) {
|
||||
dump_mem("Stack: ", regs->sp,
|
||||
THREAD_SIZE + (unsigned long)tsk->thread_info);
|
||||
printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n" KERN_EMERG,
|
||||
str, err, ++die_counter);
|
||||
#ifdef CONFIG_PREEMPT
|
||||
printk("PREEMPT ");
|
||||
#endif
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
printk("FRAME_POINTER ");
|
||||
#endif
|
||||
if (current_cpu_data.features & AVR32_FEATURE_OCD) {
|
||||
unsigned long did = __mfdr(DBGREG_DID);
|
||||
printk("chip: 0x%03lx:0x%04lx rev %lu\n",
|
||||
(did >> 1) & 0x7ff,
|
||||
(did >> 12) & 0x7fff,
|
||||
(did >> 28) & 0xf);
|
||||
} else {
|
||||
printk("cpu: arch %u r%u / core %u r%u\n",
|
||||
current_cpu_data.arch_type,
|
||||
current_cpu_data.arch_revision,
|
||||
current_cpu_data.cpu_type,
|
||||
current_cpu_data.cpu_revision);
|
||||
}
|
||||
|
||||
print_modules();
|
||||
show_regs_log_lvl(regs, KERN_EMERG);
|
||||
show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG);
|
||||
bust_spinlocks(0);
|
||||
spin_unlock_irq(&die_lock);
|
||||
do_exit(SIGSEGV);
|
||||
|
||||
if (in_interrupt())
|
||||
panic("Fatal exception in interrupt");
|
||||
|
||||
if (panic_on_oops)
|
||||
panic("Fatal exception");
|
||||
|
||||
do_exit(err);
|
||||
}
|
||||
|
||||
void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
|
||||
const char *file, const char *func, unsigned long line)
|
||||
void _exception(long signr, struct pt_regs *regs, int code,
|
||||
unsigned long addr)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
if (!user_mode(regs))
|
||||
__die(str, regs, err, file, func, line);
|
||||
die("Unhandled exception in kernel mode", regs, signr);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.si_signo = signr;
|
||||
info.si_code = code;
|
||||
info.si_addr = (void __user *)addr;
|
||||
force_sig_info(signr, &info, current);
|
||||
|
||||
/*
|
||||
* Init gets no signals that it doesn't have a handler for.
|
||||
* That's all very well, but if it has caused a synchronous
|
||||
* exception and we ignore the resulting signal, it will just
|
||||
* generate the same exception over and over again and we get
|
||||
* nowhere. Better to kill it and let the kernel panic.
|
||||
*/
|
||||
if (is_init(current)) {
|
||||
__sighandler_t handler;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
handler = current->sighand->action[signr-1].sa.sa_handler;
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
if (handler == SIG_DFL) {
|
||||
/* init has generated a synchronous exception
|
||||
and it doesn't have a handler for the signal */
|
||||
printk(KERN_CRIT "init has generated signal %ld "
|
||||
"but has no handler for it\n", signr);
|
||||
do_exit(signr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_SUBARCH_AVR32B
|
||||
/*
|
||||
* The exception entry always saves RSR_EX. For NMI, this is
|
||||
* wrong; it should be RSR_NMI
|
||||
*/
|
||||
regs->sr = sysreg_read(RSR_NMI);
|
||||
#endif
|
||||
|
||||
printk("NMI taken!!!!\n");
|
||||
die("NMI", regs, ecr);
|
||||
BUG();
|
||||
printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n");
|
||||
show_regs_log_lvl(regs, KERN_ALERT);
|
||||
show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT);
|
||||
}
|
||||
|
||||
asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
printk("Unable to handle critical exception %lu at pc = %08lx!\n",
|
||||
ecr, regs->pc);
|
||||
die("Oops", regs, ecr);
|
||||
BUG();
|
||||
die("Critical exception", regs, SIGKILL);
|
||||
}
|
||||
|
||||
asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (ecr == ECR_ADDR_ALIGN_X)
|
||||
pr_debug("Instruction Address Exception at pc = %08lx\n",
|
||||
regs->pc);
|
||||
else if (ecr == ECR_ADDR_ALIGN_R)
|
||||
pr_debug("Data Address Exception (Read) at pc = %08lx\n",
|
||||
regs->pc);
|
||||
else if (ecr == ECR_ADDR_ALIGN_W)
|
||||
pr_debug("Data Address Exception (Write) at pc = %08lx\n",
|
||||
regs->pc);
|
||||
else
|
||||
BUG();
|
||||
|
||||
show_regs(regs);
|
||||
#endif
|
||||
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = BUS_ADRALN;
|
||||
info.si_addr = (void __user *)regs->pc;
|
||||
|
||||
force_sig_info(SIGBUS, &info, current);
|
||||
_exception(SIGBUS, regs, BUS_ADRALN, regs->pc);
|
||||
}
|
||||
|
||||
/* This way of handling undefined instructions is stolen from ARM */
|
||||
@ -280,7 +157,8 @@ static int do_cop_absent(u32 insn)
|
||||
{
|
||||
int cop_nr;
|
||||
u32 cpucr;
|
||||
if ( (insn & 0xfdf00000) == 0xf1900000 )
|
||||
|
||||
if ((insn & 0xfdf00000) == 0xf1900000)
|
||||
/* LDC0 */
|
||||
cop_nr = 0;
|
||||
else
|
||||
@ -292,136 +170,91 @@ static int do_cop_absent(u32 insn)
|
||||
sysreg_write(CPUCR, cpucr);
|
||||
|
||||
cpucr = sysreg_read(CPUCR);
|
||||
if ( !(cpucr & (1 << (24 + cop_nr))) ){
|
||||
printk("Coprocessor #%i not found!\n", cop_nr);
|
||||
return -1;
|
||||
}
|
||||
if (!(cpucr & (1 << (24 + cop_nr))))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BUG
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
|
||||
int is_valid_bugaddr(unsigned long pc)
|
||||
{
|
||||
char *file;
|
||||
u16 line;
|
||||
char c;
|
||||
unsigned short opcode;
|
||||
|
||||
if (__get_user(line, (u16 __user *)(regs->pc + 2)))
|
||||
return;
|
||||
if (__get_user(file, (char * __user *)(regs->pc + 4))
|
||||
|| (unsigned long)file < PAGE_OFFSET
|
||||
|| __get_user(c, file))
|
||||
file = "<bad filename>";
|
||||
if (pc < PAGE_OFFSET)
|
||||
return 0;
|
||||
if (probe_kernel_address((u16 *)pc, opcode))
|
||||
return 0;
|
||||
|
||||
printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
|
||||
return opcode == AVR32_BUG_OPCODE;
|
||||
}
|
||||
#else
|
||||
static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
u32 insn;
|
||||
struct undef_hook *hook;
|
||||
siginfo_t info;
|
||||
void __user *pc;
|
||||
long code;
|
||||
|
||||
if (!user_mode(regs))
|
||||
goto kernel_trap;
|
||||
if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) {
|
||||
enum bug_trap_type type;
|
||||
|
||||
type = report_bug(regs->pc);
|
||||
switch (type) {
|
||||
case BUG_TRAP_TYPE_NONE:
|
||||
break;
|
||||
case BUG_TRAP_TYPE_WARN:
|
||||
regs->pc += 2;
|
||||
return;
|
||||
case BUG_TRAP_TYPE_BUG:
|
||||
die("Kernel BUG", regs, SIGKILL);
|
||||
}
|
||||
}
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
pc = (void __user *)instruction_pointer(regs);
|
||||
if (__get_user(insn, (u32 __user *)pc))
|
||||
goto invalid_area;
|
||||
if (user_mode(regs)) {
|
||||
pc = (void __user *)instruction_pointer(regs);
|
||||
if (get_user(insn, (u32 __user *)pc))
|
||||
goto invalid_area;
|
||||
|
||||
if (ecr == ECR_COPROC_ABSENT) {
|
||||
if (do_cop_absent(insn) == 0)
|
||||
if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn))
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irq(&undef_lock);
|
||||
list_for_each_entry(hook, &undef_hook, node) {
|
||||
if ((insn & hook->insn_mask) == hook->insn_val) {
|
||||
if (hook->fn(regs, insn) == 0) {
|
||||
spin_unlock_irq(&undef_lock);
|
||||
return;
|
||||
spin_lock_irq(&undef_lock);
|
||||
list_for_each_entry(hook, &undef_hook, node) {
|
||||
if ((insn & hook->insn_mask) == hook->insn_val) {
|
||||
if (hook->fn(regs, insn) == 0) {
|
||||
spin_unlock_irq(&undef_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&undef_lock);
|
||||
}
|
||||
spin_unlock_irq(&undef_lock);
|
||||
|
||||
invalid_area:
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Illegal instruction at pc = %08lx\n", regs->pc);
|
||||
if (regs->pc < TASK_SIZE) {
|
||||
unsigned long ptbr, pgd, pte, *p;
|
||||
|
||||
ptbr = sysreg_read(PTBR);
|
||||
p = (unsigned long *)ptbr;
|
||||
pgd = p[regs->pc >> 22];
|
||||
p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
|
||||
pte = p[(regs->pc >> 12) & 0x3ff];
|
||||
printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
|
||||
}
|
||||
#endif
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_addr = (void __user *)regs->pc;
|
||||
switch (ecr) {
|
||||
case ECR_ILLEGAL_OPCODE:
|
||||
case ECR_UNIMPL_INSTRUCTION:
|
||||
info.si_code = ILL_ILLOPC;
|
||||
break;
|
||||
case ECR_PRIVILEGE_VIOLATION:
|
||||
info.si_code = ILL_PRVOPC;
|
||||
code = ILL_PRVOPC;
|
||||
break;
|
||||
case ECR_COPROC_ABSENT:
|
||||
info.si_code = ILL_COPROC;
|
||||
code = ILL_COPROC;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
code = ILL_ILLOPC;
|
||||
break;
|
||||
}
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
_exception(SIGILL, regs, code, regs->pc);
|
||||
return;
|
||||
|
||||
kernel_trap:
|
||||
#ifdef CONFIG_BUG
|
||||
if (__kernel_text_address(instruction_pointer(regs))) {
|
||||
insn = *(u16 *)instruction_pointer(regs);
|
||||
if (insn == AVR32_BUG_OPCODE) {
|
||||
do_bug_verbose(regs, insn);
|
||||
die("Kernel BUG", regs, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
die("Oops: Illegal instruction in kernel code", regs, ecr);
|
||||
invalid_area:
|
||||
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc);
|
||||
}
|
||||
|
||||
asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
printk("Floating-point exception at pc = %08lx\n", regs->pc);
|
||||
|
||||
/* We have no FPU... */
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_addr = (void __user *)regs->pc;
|
||||
info.si_code = ILL_COPROC;
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
/* We have no FPU yet */
|
||||
_exception(SIGILL, regs, ILL_COPROC, regs->pc);
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,6 +26,12 @@ SECTIONS
|
||||
_sinittext = .;
|
||||
*(.text.reset)
|
||||
*(.init.text)
|
||||
/*
|
||||
* .exit.text is discarded at runtime, not
|
||||
* link time, to deal with references from
|
||||
* __bug_table
|
||||
*/
|
||||
*(.exit.text)
|
||||
_einittext = .;
|
||||
. = ALIGN(4);
|
||||
__tagtable_begin = .;
|
||||
@ -86,6 +92,8 @@ SECTIONS
|
||||
__stop___ex_table = .;
|
||||
}
|
||||
|
||||
BUG_TABLE
|
||||
|
||||
RODATA
|
||||
|
||||
. = ALIGN(8192);
|
||||
@ -126,7 +134,6 @@ SECTIONS
|
||||
* thrown away, as cleanup code is never called unless it's a module.
|
||||
*/
|
||||
/DISCARD/ : {
|
||||
*(.exit.text)
|
||||
*(.exit.data)
|
||||
*(.exitcall.exit)
|
||||
}
|
||||
|
@ -16,26 +16,8 @@
|
||||
#include <asm/kdebug.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/tlb.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
static void dump_code(unsigned long pc)
|
||||
{
|
||||
char *p = (char *)pc;
|
||||
char val;
|
||||
int i;
|
||||
|
||||
|
||||
printk(KERN_DEBUG "Code:");
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (__get_user(val, p + i))
|
||||
break;
|
||||
printk(" %02x", val);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#endif
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
|
||||
@ -68,17 +50,19 @@ static inline int notify_page_fault(enum die_val val, struct pt_regs *regs,
|
||||
}
|
||||
#endif
|
||||
|
||||
int exception_trace = 1;
|
||||
|
||||
/*
|
||||
* This routine handles page faults. It determines the address and the
|
||||
* problem, and then passes it off to one of the appropriate routines.
|
||||
*
|
||||
* ecr is the Exception Cause Register. Possible values are:
|
||||
* 5: Page not found (instruction access)
|
||||
* 6: Protection fault (instruction access)
|
||||
* 12: Page not found (read access)
|
||||
* 13: Page not found (write access)
|
||||
* 14: Protection fault (read access)
|
||||
* 15: Protection fault (write access)
|
||||
* 15: Protection fault (read access)
|
||||
* 16: Protection fault (write access)
|
||||
* 20: Page not found (instruction access)
|
||||
* 24: Page not found (read access)
|
||||
* 28: Page not found (write access)
|
||||
*/
|
||||
asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
@ -88,7 +72,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||
const struct exception_table_entry *fixup;
|
||||
unsigned long address;
|
||||
unsigned long page;
|
||||
int writeaccess = 0;
|
||||
int writeaccess;
|
||||
long signr;
|
||||
int code;
|
||||
|
||||
if (notify_page_fault(DIE_PAGE_FAULT, regs,
|
||||
ecr, SIGSEGV) == NOTIFY_STOP)
|
||||
@ -99,6 +85,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||
tsk = current;
|
||||
mm = tsk->mm;
|
||||
|
||||
signr = SIGSEGV;
|
||||
code = SEGV_MAPERR;
|
||||
|
||||
/*
|
||||
* If we're in an interrupt or have no user context, we must
|
||||
* not take the fault...
|
||||
@ -125,7 +114,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||
* can handle it...
|
||||
*/
|
||||
good_area:
|
||||
//pr_debug("good area: vm_flags = 0x%lx\n", vma->vm_flags);
|
||||
code = SEGV_ACCERR;
|
||||
writeaccess = 0;
|
||||
|
||||
switch (ecr) {
|
||||
case ECR_PROTECTION_X:
|
||||
case ECR_TLB_MISS_X:
|
||||
@ -176,46 +167,24 @@ survive:
|
||||
* map. Fix it, but check if it's kernel or user first...
|
||||
*/
|
||||
bad_area:
|
||||
pr_debug("Bad area [%s:%u]: addr %08lx, ecr %lu\n",
|
||||
tsk->comm, tsk->pid, address, ecr);
|
||||
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
if (user_mode(regs)) {
|
||||
/* Hmm...we have to pass address and ecr somehow... */
|
||||
/* tsk->thread.address = address;
|
||||
tsk->thread.error_code = ecr; */
|
||||
#ifdef DEBUG
|
||||
show_regs(regs);
|
||||
dump_code(regs->pc);
|
||||
|
||||
page = sysreg_read(PTBR);
|
||||
printk("ptbr = %08lx", page);
|
||||
if (page) {
|
||||
page = ((unsigned long *)page)[address >> 22];
|
||||
printk(" pgd = %08lx", page);
|
||||
if (page & _PAGE_PRESENT) {
|
||||
page &= PAGE_MASK;
|
||||
address &= 0x003ff000;
|
||||
page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
|
||||
printk(" pte = %08lx\n", page);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pr_debug("Sending SIGSEGV to PID %d...\n",
|
||||
tsk->pid);
|
||||
force_sig(SIGSEGV, tsk);
|
||||
if (exception_trace)
|
||||
printk("%s%s[%d]: segfault at %08lx pc %08lx "
|
||||
"sp %08lx ecr %lu\n",
|
||||
is_init(tsk) ? KERN_EMERG : KERN_INFO,
|
||||
tsk->comm, tsk->pid, address, regs->pc,
|
||||
regs->sp, ecr);
|
||||
_exception(SIGSEGV, regs, code, address);
|
||||
return;
|
||||
}
|
||||
|
||||
no_context:
|
||||
pr_debug("No context\n");
|
||||
|
||||
/* Are we prepared to handle this kernel fault? */
|
||||
fixup = search_exception_tables(regs->pc);
|
||||
if (fixup) {
|
||||
regs->pc = fixup->fixup;
|
||||
pr_debug("Found fixup at %08lx\n", fixup->fixup);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -230,7 +199,6 @@ no_context:
|
||||
printk(KERN_ALERT
|
||||
"Unable to handle kernel paging request");
|
||||
printk(" at virtual address %08lx\n", address);
|
||||
printk(KERN_ALERT "pc = %08lx\n", regs->pc);
|
||||
|
||||
page = sysreg_read(PTBR);
|
||||
printk(KERN_ALERT "ptbr = %08lx", page);
|
||||
@ -241,20 +209,20 @@ no_context:
|
||||
page &= PAGE_MASK;
|
||||
address &= 0x003ff000;
|
||||
page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
|
||||
printk(" pte = %08lx\n", page);
|
||||
printk(" pte = %08lx", page);
|
||||
}
|
||||
}
|
||||
die("\nOops", regs, ecr);
|
||||
do_exit(SIGKILL);
|
||||
printk("\n");
|
||||
die("Kernel access of bad area", regs, signr);
|
||||
return;
|
||||
|
||||
/*
|
||||
* We ran out of memory, or some other thing happened to us
|
||||
* that made us unable to handle the page fault gracefully.
|
||||
*/
|
||||
out_of_memory:
|
||||
printk("Out of memory\n");
|
||||
up_read(&mm->mmap_sem);
|
||||
if (current->pid == 1) {
|
||||
if (is_init(current)) {
|
||||
yield();
|
||||
down_read(&mm->mmap_sem);
|
||||
goto survive;
|
||||
@ -267,21 +235,20 @@ out_of_memory:
|
||||
do_sigbus:
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
/*
|
||||
* Send a sigbus, regardless of whether we were in kernel or
|
||||
* user mode.
|
||||
*/
|
||||
/* address, error_code, trap_no, ... */
|
||||
#ifdef DEBUG
|
||||
show_regs(regs);
|
||||
dump_code(regs->pc);
|
||||
#endif
|
||||
pr_debug("Sending SIGBUS to PID %d...\n", tsk->pid);
|
||||
force_sig(SIGBUS, tsk);
|
||||
|
||||
/* Kernel mode? Handle exceptions or die */
|
||||
signr = SIGBUS;
|
||||
code = BUS_ADRERR;
|
||||
if (!user_mode(regs))
|
||||
goto no_context;
|
||||
|
||||
if (exception_trace)
|
||||
printk("%s%s[%d]: bus error at %08lx pc %08lx "
|
||||
"sp %08lx ecr %lu\n",
|
||||
is_init(tsk) ? KERN_EMERG : KERN_INFO,
|
||||
tsk->comm, tsk->pid, address, regs->pc,
|
||||
regs->sp, ecr);
|
||||
|
||||
_exception(SIGBUS, regs, BUS_ADRERR, address);
|
||||
}
|
||||
|
||||
asmlinkage void do_bus_error(unsigned long addr, int write_access,
|
||||
@ -292,8 +259,7 @@ asmlinkage void do_bus_error(unsigned long addr, int write_access,
|
||||
addr, write_access ? "write" : "read");
|
||||
printk(KERN_INFO "DTLB dump:\n");
|
||||
dump_dtlb();
|
||||
die("Bus Error", regs, write_access);
|
||||
do_exit(SIGKILL);
|
||||
die("Bus Error", regs, SIGKILL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -18,27 +18,53 @@
|
||||
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
|
||||
#define BUG() \
|
||||
do { \
|
||||
asm volatile(".hword %0\n\t" \
|
||||
".hword %1\n\t" \
|
||||
".long %2" \
|
||||
: \
|
||||
: "n"(AVR32_BUG_OPCODE), \
|
||||
"i"(__LINE__), "X"(__FILE__)); \
|
||||
} while (0)
|
||||
#define _BUG_OR_WARN(flags) \
|
||||
asm volatile( \
|
||||
"1: .hword %0\n" \
|
||||
" .section __bug_table,\"a\",@progbits\n" \
|
||||
"2: .long 1b\n" \
|
||||
" .long %1\n" \
|
||||
" .short %2\n" \
|
||||
" .short %3\n" \
|
||||
" .org 2b + %4\n" \
|
||||
" .previous" \
|
||||
: \
|
||||
: "i"(AVR32_BUG_OPCODE), "i"(__FILE__), \
|
||||
"i"(__LINE__), "i"(flags), \
|
||||
"i"(sizeof(struct bug_entry)))
|
||||
|
||||
#else
|
||||
|
||||
#define BUG() \
|
||||
do { \
|
||||
asm volatile(".hword %0\n\t" \
|
||||
: : "n"(AVR32_BUG_OPCODE)); \
|
||||
} while (0)
|
||||
#define _BUG_OR_WARN(flags) \
|
||||
asm volatile( \
|
||||
"1: .hword %0\n" \
|
||||
" .section __bug_table,\"a\",@progbits\n" \
|
||||
"2: .long 1b\n" \
|
||||
" .short %1\n" \
|
||||
" .org 2b + %2\n" \
|
||||
" .previous" \
|
||||
: \
|
||||
: "i"(AVR32_BUG_OPCODE), "i"(flags), \
|
||||
"i"(sizeof(struct bug_entry)))
|
||||
|
||||
#endif /* CONFIG_DEBUG_BUGVERBOSE */
|
||||
|
||||
#define BUG() \
|
||||
do { \
|
||||
_BUG_OR_WARN(0); \
|
||||
for (;;); \
|
||||
} while (0)
|
||||
|
||||
#define WARN_ON(condition) \
|
||||
({ \
|
||||
typeof(condition) __ret_warn_on = (condition); \
|
||||
if (unlikely(__ret_warn_on)) \
|
||||
_BUG_OR_WARN(BUGFLAG_WARNING); \
|
||||
unlikely(__ret_warn_on); \
|
||||
})
|
||||
|
||||
#define HAVE_ARCH_BUG
|
||||
#define HAVE_ARCH_WARN_ON
|
||||
|
||||
#endif /* CONFIG_BUG */
|
||||
|
||||
|
@ -134,10 +134,10 @@ extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
|
||||
#define thread_saved_pc(tsk) ((tsk)->thread.cpu_context.pc)
|
||||
|
||||
struct pt_regs;
|
||||
void show_trace(struct task_struct *task, unsigned long *stack,
|
||||
struct pt_regs *regs);
|
||||
|
||||
extern unsigned long get_wchan(struct task_struct *p);
|
||||
extern void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl);
|
||||
extern void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
|
||||
struct pt_regs *regs, const char *log_lvl);
|
||||
|
||||
#define KSTK_EIP(tsk) ((tsk)->thread.cpu_context.pc)
|
||||
#define KSTK_ESP(tsk) ((tsk)->thread.cpu_context.ksp)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define __ASM_AVR32_SYSTEM_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
@ -140,15 +141,9 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
|
||||
sizeof(*(ptr))))
|
||||
|
||||
struct pt_regs;
|
||||
extern void __die(const char *, struct pt_regs *, unsigned long,
|
||||
const char *, const char *, unsigned long);
|
||||
extern void __die_if_kernel(const char *, struct pt_regs *, unsigned long,
|
||||
const char *, const char *, unsigned long);
|
||||
|
||||
#define die(msg, regs, err) \
|
||||
__die(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
|
||||
#define die_if_kernel(msg, regs, err) \
|
||||
__die_if_kernel(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
|
||||
void NORET_TYPE die(const char *str, struct pt_regs *regs, long err);
|
||||
void _exception(long signr, struct pt_regs *regs, int code,
|
||||
unsigned long addr);
|
||||
|
||||
#define arch_align_stack(x) (x)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user