/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2007 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include /*--- #include ---*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_KALLSYMS #include #endif/*--- #ifdef CONFIG_KALLSYMS ---*/ #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) #include #endif/*--- #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) ---*/ #include #include #include #include "avm_sammel.h" #include "avm_debug.h" #include #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 19) #define AVM_DEBUG_UDEV #endif/*--- #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) ---*/ #define DEB_ERR(args...) printk(KERN_ERR args) /*--- #define DEB_INFO(args...) printk(KERN_INFO args) ---*/ #define DEB_INFO(args...) /*--- #define DEB_NOTE(args...) printk(KERN_INFO args) ---*/ #define DEB_NOTE(args...) #define MAX_DEBUG_MESSAGE_LEN 1024 /*------------------------------------------------------------------------------------------*\ * Ersetzt printk: Aufruf per cat /dev/debug & * frueher im UBIK2-Treiber \*------------------------------------------------------------------------------------------*/ #define TRUE 1 #define FALSE 0 #define SKIP_SPACES(p) while((p) && *(p) && ((*(p) == ' ') || (*(p) == '\t'))) (p)++; #define SKIP_UNTIL_SPACES(p) while((p) && *(p) && (*(p) != ' ') && (*(p) != '\t')) (p)++; #define AVM_DBG_MODE "AVM_PRINTK" #define PRINTK_DBG_MODE "STD_PRINTK" #define AVM_DBG_EOF "AVMDBG_EOF" #define AVM_DBG_SIGNAL "AVMDBG_SIGNAL" #define AVM_DBG_TORTURE "AVMDBG_TORTURE" /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _debug_client { void *refdata; char *prefix; void (*CallBackDebug)(char *string, void *refdata); struct _debug_client *next; }; #define MAX_DEBUG_STACK 8 /*--- count of max nested printk's ---*/ struct _debugstack { atomic_t used; unsigned char *buf; }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static struct _avm_debug { unsigned int init; spinlock_t con_lock; spinlock_t client_lock; spinlock_t synclock; atomic_t open_flag; unsigned char *buffer; unsigned char tmpbuf[PAGE_SIZE]; volatile unsigned int read; volatile unsigned int write; volatile unsigned int wrap; unsigned int size; unsigned int lost; volatile unsigned int is_open; unsigned int major; unsigned int written; dev_t device; struct cdev *cdev; #if defined(AVM_DEBUG_UDEV) struct class *osclass; #endif/*--- #if defined(AVM_DEBUG_UDEV) ---*/ wait_queue_head_t recvwait; struct _debug_client *dbg_clientAnker; unsigned int eof_sync; /*--- erzwinge EOF bei read ---*/ struct task_struct *kthread; unsigned int signal; struct _debugstack debugstack[MAX_DEBUG_STACK]; wait_queue_head_t wait_queue; struct socket *s_push; char comment[64]; } avm_debug; static int avm_debug_open(struct inode *inode, struct file *filp); static int avm_debug_close(struct inode *inode, struct file *filp); static long avm_debug_ioctl(struct file *, unsigned, unsigned long); static ssize_t avm_debug_read(struct file *filp, char *read_buffer, size_t max_read_length, loff_t *read_pos); static unsigned int avm_debug_poll(struct file *file, poll_table * wait); static ssize_t avm_debug_write(struct file *filp, const char *write_buffer, size_t write_length, loff_t *write_pos); static struct _debug_client *find_dbgclient_by_prefix(char *prefix); static __printf(1, 0) int avm_kernel_vprintk(const char *format, va_list marker); #ifdef CONFIG_PRINTK static void avmdebug_sync(void); extern void (*debug_sync)(void) __attribute__ ((weak)); #endif/*--- #ifdef CONFIG_PRINTK ---*/ static int avmdebug_thread( void *data ); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ avm_debug_write_t avm_debug_write_minor[AVM_DEBUG_MAX_MINOR + 1]; avm_debug_read_t avm_debug_read_minor[AVM_DEBUG_MAX_MINOR + 1]; avm_debug_open_t avm_debug_open_minor[AVM_DEBUG_MAX_MINOR + 1]; avm_debug_close_t avm_debug_close_minor[AVM_DEBUG_MAX_MINOR + 1]; avm_debug_ioctl_t avm_debug_ioctl_minor[AVM_DEBUG_MAX_MINOR + 1]; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static ssize_t avm_debug_write_dummy(struct file *filp __attribute__ ((unused)), const char *buff __attribute__ ((unused)), size_t count __attribute__ ((unused)), loff_t *off __attribute__ ((unused))) { return -ENOENT; } static ssize_t avm_debug_read_dummy(struct file *filp __attribute__ ((unused)), char *buff __attribute__ ((unused)), size_t count __attribute__ ((unused)), loff_t *off __attribute__ ((unused))) { return -ENOENT; } static long avm_debug_ioctl_dummy(struct file *filp __attribute__ ((unused)), unsigned cmd __attribute__ ((unused)), unsigned long arg __attribute__ ((unused))) { return -ENOENT; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct file_operations avm_debug_fops = { owner: THIS_MODULE, open: avm_debug_open, release: avm_debug_close, write: avm_debug_write, read: avm_debug_read, unlocked_ioctl: avm_debug_ioctl, poll: avm_debug_poll, }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline int avmdebug_lock(spinlock_t *lock, unsigned long *flags, unsigned int force) { #if defined(CONFIG_SMP) int try = 0; #if defined(CONFIG_MIPS) if(is_yield_context()) { *flags = 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) __raw_spin_lock(&lock->rlock); #else __raw_spin_lock(&lock->raw_lock); #endif return 1; } #endif/*--- #if defined(CONFIG_MIPS) ---*/ if(force) { spin_lock_irqsave(lock, *flags); return 1; } /*--- if interrupted with a fatal error on this vpe (and the other vpe is already disabled) ---*/ while(!spin_trylock_irqsave(lock, *flags)) { if(try++ > 20) { return 0; } mdelay(1); } #else/*--- #if defined(CONFIG_SMP) ---*/ spin_lock_irqsave(lock, *flags); #endif /*--- #else ---*//*--- #if defined(CONFIG_SMP) ---*/ return 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline void avmdebug_unlock(spinlock_t *lock, unsigned long flags) { #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) if(is_yield_context()) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) __raw_spin_unlock(&lock->rlock); #else __raw_spin_unlock(&lock->raw_lock); #endif return; } #endif/*--- #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) ---*/ spin_unlock_irqrestore(lock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_debug_register_minor(int minor, avm_debug_open_t open, avm_debug_close_t close, avm_debug_write_t write, avm_debug_read_t read, avm_debug_ioctl_t ioctl ) { if((minor < 1) || (minor > AVM_DEBUG_MAX_MINOR)) { return -ENXIO; } if((avm_debug_write_minor[minor] != avm_debug_write_dummy) || (avm_debug_read_minor[minor] != avm_debug_read_dummy) || (avm_debug_ioctl_minor[minor] != avm_debug_ioctl_dummy) || (avm_debug_open_minor[minor] != NULL) || (avm_debug_close_minor[minor] != NULL)) { return -EEXIST; } avm_debug_write_minor[minor] = write ? write : avm_debug_write_dummy; avm_debug_read_minor[minor] = read ? read : avm_debug_read_dummy; avm_debug_ioctl_minor[minor] = ioctl ? ioctl : avm_debug_ioctl_dummy; avm_debug_open_minor[minor] = open; avm_debug_close_minor[minor] = close; return 0; } EXPORT_SYMBOL(avm_debug_register_minor); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_debug_release_minor(int minor) { if((minor < 1) || (minor > AVM_DEBUG_MAX_MINOR)) { return -ENXIO; } avm_debug_write_minor[minor] = avm_debug_write_dummy; avm_debug_read_minor[minor] = avm_debug_read_dummy; avm_debug_ioctl_minor[minor] = avm_debug_ioctl_dummy; avm_debug_open_minor[minor] = NULL; avm_debug_close_minor[minor] = NULL; return 0; } EXPORT_SYMBOL(avm_debug_release_minor); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int __init avm_debug_init(void) { int reason; int minor, i; unsigned char *p; for(minor = 1 ; minor <= AVM_DEBUG_MAX_MINOR ; minor++) { avm_debug_write_minor[minor] = avm_debug_write_dummy; avm_debug_read_minor[minor] = avm_debug_read_dummy; avm_debug_ioctl_minor[minor] = avm_debug_ioctl_dummy; avm_debug_open_minor[minor] = NULL; avm_debug_close_minor[minor] = NULL; } memset((void *)&avm_debug, 0, sizeof(avm_debug)); DEB_INFO("[avm_debug] register_chrdev_region()\n"); #if defined(AVM_DEBUG_UDEV) reason = alloc_chrdev_region(&avm_debug.device, 0, AVM_DEBUG_MINOR_COUNT, "debug"); #else /*--- #if defined(AVM_DEBUG_UDEV) ---*/ avm_debug.device = MKDEV(DEBUG_TRACE_MAJOR, 0); reason = register_chrdev_region(avm_debug.device, AVM_DEBUG_MINOR_COUNT, "debug"); #endif if(reason) { DEB_ERR("[avm_debug] register_chrdev_region failed: reason %d!\n", reason); return -ERESTARTSYS; } avm_debug.cdev = cdev_alloc(); if (!avm_debug.cdev) { unregister_chrdev_region(avm_debug.device, AVM_DEBUG_MINOR_COUNT); DEB_ERR("[avm_debug] cdev_alloc failed!\n"); return -ERESTARTSYS; } spin_lock_init(&avm_debug.client_lock); spin_lock_init(&avm_debug.con_lock); spin_lock_init(&avm_debug.synclock); init_waitqueue_head(&avm_debug.recvwait); avm_debug.cdev->owner = avm_debug_fops.owner; avm_debug.cdev->ops = &avm_debug_fops; kobject_set_name(&(avm_debug.cdev->kobj), "debug"); avm_debug.size = 256 * 1024; /*--- avm_debug.size = 1024; ---*/ avm_debug.buffer = kmalloc(avm_debug.size + (MAX_DEBUG_STACK * MAX_DEBUG_MESSAGE_LEN), GFP_KERNEL); if(avm_debug.buffer == NULL) { DEB_ERR("[avm_debug] Could not allocate debug buffer space!\n"); return -ENOMEM; } DEB_INFO("[avm_debug] major %d (success)\n", MAJOR(avm_debug.device)); p = avm_debug.buffer + avm_debug.size; for(i = 0; i< MAX_DEBUG_STACK; i++) { atomic_set(&avm_debug.debugstack[i].used, 0); avm_debug.debugstack[i].buf = p; p += MAX_DEBUG_MESSAGE_LEN; } if(cdev_add(avm_debug.cdev, avm_debug.device, AVM_DEBUG_MINOR_COUNT)) { kobject_put(&avm_debug.cdev->kobj); unregister_chrdev_region(avm_debug.device, AVM_DEBUG_MINOR_COUNT); DEB_ERR("[avm_debug] cdev_add failed!\n"); return -ERESTARTSYS; } #if defined(AVM_DEBUG_UDEV) /*--- Geraetedatei anlegen: ---*/ avm_debug.osclass = class_create(THIS_MODULE, "debug"); device_create(avm_debug.osclass, NULL, 1, NULL, "%s%d", "debug", 0); #endif/*--- #if defined(AVM_DEBUG_UDEV) ---*/ #ifdef CONFIG_PRINTK if(!IS_ERR(&debug_sync) && &debug_sync) {/*--- Die Adresse der Variable muss auf Fehler und NULL geprueft werden ---*/ debug_sync = avmdebug_sync; } #endif/*--- #ifdef CONFIG_PRINTK ---*/ init_waitqueue_head(&avm_debug.wait_queue); avm_debug.kthread = kthread_run(avmdebug_thread, &avm_debug, "avm_debugd"); BUG_ON((avm_debug.kthread == NULL) || IS_ERR(avm_debug.kthread)); atomic_set(&avm_debug.open_flag, 0); avm_debug.init = 1; return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline int avm_debug_kthread_exit(struct task_struct **_kthread) { struct task_struct *kthread = *_kthread; if (!kthread) { return -EINVAL; } return kthread_stop(kthread); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void avm_debug_cleanup(void) { if(avm_debug.init == 0) { return; } avm_debug.init = 0; avm_debug_kthread_exit(&avm_debug.kthread); DEB_INFO("[avm_debug] unregister_chrdev(%u)\n", MAJOR(avm_debug.device)); #if defined(AVM_DEBUG_UDEV) device_destroy(avm_debug.osclass, 1); class_destroy(avm_debug.osclass); #endif/*--- #if defined(AVM_DEBUG_UDEV) ---*/ cdev_del(avm_debug.cdev); unregister_chrdev_region(avm_debug.device, AVM_DEBUG_MINOR_COUNT); if(avm_debug.buffer != NULL) vfree(avm_debug.buffer); } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int avm_debug_open(struct inode *inode, struct file *filp) { int minor = MINOR(inode->i_rdev); filp->private_data = (void *)minor; if(avm_debug_open_minor[minor]) { return (*avm_debug_open_minor[minor])(inode, filp); } /*--- printk_linux(KERN_ERR"[avm_debug]: avm_debug_open: %x %d\n", filp->f_mode, atomic_read(&avm_debug.open_flag)); ---*/ if(filp->f_mode & FMODE_READ){ if(atomic_add_return(1, &avm_debug.open_flag) > 1) { return -EBUSY; } } DEB_INFO("[avm_debug]: avm_debug_open:\n"); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int avm_debug_close(struct inode *inode, struct file *filp) { unsigned int minor = (unsigned int)filp->private_data; DEB_INFO("[avm_debug]: avm_debug_close:\n"); if(avm_debug_close_minor[minor]) { return (*avm_debug_close_minor[minor])(inode, filp); } if(filp->f_mode & FMODE_READ){ atomic_set(&avm_debug.open_flag, 0); } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static long avm_debug_ioctl(struct file *filp, unsigned cmd, unsigned long args) { unsigned int minor = (unsigned int)filp->private_data; if(avm_debug_ioctl_minor[minor]) return (*avm_debug_ioctl_minor[minor])(filp, cmd, args); return -ENXIO; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline unsigned int inc_idx(unsigned int idx, unsigned int max_idx) { if(++idx >= max_idx) { return 0; } return idx; } void avm_debug_enable_avm_printk(void) { #ifdef CONFIG_PRINTK #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) vprintk_set(vprintk_avm); #else set_printk(printk_avm); if (!IS_ERR(&set_vprintk)) set_vprintk(vprintk_avm); #endif #endif /* CONFIG_PRINTK */ } void avm_debug_disable_avm_printk(void) { #ifdef CONFIG_PRINTK /* ab hier alle Ausgaben über Linux vprintk() */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) vprintk_restore(); #else restore_printk(); #endif #endif /* CONFIG_PRINTK */ } static struct timer_list torture_Timer[NR_CPUS]; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void torture(void *info) { unsigned long timer = (unsigned long)info; unsigned long flags; local_irq_save(flags); /* Always use original Linux printk */ printk_linux(KERN_ERR "\nblock cpu%x for %lu ms\n", raw_smp_processor_id(), timer); while(timer--) { udelay(1000); } printk_linux(KERN_ERR "end block\n"); local_irq_restore(flags); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void torture_function(unsigned long context) { printk_linux(KERN_ERR "\nblock timer on cpu%x for %lu ms\n", raw_smp_processor_id(), context); while (context--) udelay(1000); printk_linux(KERN_ERR "end timerblock\n"); } /*--------------------------------------------------------------------------------*\ * ret: Loglevel \*--------------------------------------------------------------------------------*/ static int skip_loglevel(const char **_format, int old_loglevel) { int loglevel; const char *format = *_format; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) loglevel = printk_get_level(format); format = printk_skip_level(format); if(loglevel) { if(loglevel == (int)'d') { loglevel = default_console_loglevel; /* KERN_DEFAULT */ } else { loglevel -= '0'; } } else { /*--- continue-Mode: lass loglevel auf Mode (damit kein linefeed angehaengt wird) ---*/ if (console_loglevel < old_loglevel) { loglevel = old_loglevel; } } #else/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) ---*/ loglevel = 0; if (format[0] == '<') { unsigned char c = format[1]; if (c && format[2] == '>') { switch (c) { case '0' ... '7': /* loglevel */ loglevel = c - '0'; break; case 'd': /* KERN_DEFAULT */ loglevel = default_console_loglevel; break; case 'c': /* KERN_CONT */ if (console_loglevel < old_loglevel) { loglevel = old_loglevel; } break; } format += 3; } } #endif/*--- #else ---*//*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) ---*/ *_format = format; return loglevel; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static ssize_t avm_debug_write(struct file *filp, const char *write_buffer, size_t write_length, loff_t *write_pos) { char Buffer[512], *p, *pa = NULL; if(filp) { unsigned int minor = (unsigned int)filp->private_data; if(avm_debug_write_minor[minor]) return (*avm_debug_write_minor[minor])(filp, write_buffer, write_length, write_pos); } if(write_pos != NULL) { DEB_INFO("[avm_debug]: write_length = %u *write_pos = 0x%LX\n", write_length, *write_pos); } if(write_length >= sizeof(Buffer)) { write_length = sizeof(Buffer) - 1; DEB_NOTE("[avm_debug] long line reduce to %u bytes\n", write_length); } if(filp == NULL) { memcpy(Buffer, write_buffer, write_length); } else { if(copy_from_user(Buffer, write_buffer, write_length)) { DEB_ERR("[avm_debug]: write: copy_from_user failed\n"); return -EFAULT; } } /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ Buffer[write_length] = '\0'; DEB_NOTE("[avm_debug] len %u = '%s'\n", write_length, Buffer); p = strchr(Buffer, 0x0A); if(p) { *p = '\0'; pa = p + 1; write_length = strlen(Buffer) + 1; DEB_NOTE("[avm_debug] multi line reduce to %u bytes. remain %s\n", write_length, pa); } p = Buffer; /*--------------------------------------------------------------------------------------*\ * cmd extrahieren \*--------------------------------------------------------------------------------------*/ SKIP_SPACES(p); if(!strncmp(AVM_DBG_MODE, p, sizeof(AVM_DBG_MODE) - 1)) { p += sizeof(AVM_DBG_MODE) - 1; SKIP_SPACES(p); printk(KERN_ERR "\n[avm_debug] redirecting kernel-messages (/dev/debug)\n"); avm_debug_enable_avm_printk(); } else if(!strncmp(PRINTK_DBG_MODE, p, sizeof(PRINTK_DBG_MODE) - 1)) { printk_linux("\n[avm_debug] standard kernel-messages\n"); avm_debug_disable_avm_printk(); } else if(!strncmp(AVM_DBG_EOF, p, sizeof(AVM_DBG_EOF) - 1)) { int val; p += sizeof(AVM_DBG_EOF) - 1; SKIP_SPACES(p); val = (*p == '1') ? 1 : 0; avm_debug.eof_sync = val; /*--- printk(KERN_ERR"\n[avm_debug]eofsync %d\n", avm_debug.eof_sync); ---*/ } else if(!strncmp(AVM_DBG_SIGNAL, p, sizeof(AVM_DBG_SIGNAL) - 1)) { int val = -1; p += sizeof(AVM_DBG_SIGNAL) - 1; SKIP_SPACES(p); if(*p) sscanf(p, "%d", &val); if(val != -1) { if(pa) avm_DebugPrintf("avm_DebugSignal: %s\n", pa); avm_DebugSignal(val | 0x80000000); } } else if(!strncmp(AVM_DBG_TORTURE, p, sizeof(AVM_DBG_TORTURE) - 1)) { unsigned int cpu = 0; unsigned int timer = 0; int val = -1; p += sizeof(AVM_DBG_TORTURE) - 1; SKIP_SPACES(p); if(*p) sscanf(p, "%d", &val); SKIP_UNTIL_SPACES(p); SKIP_SPACES(p); if(*p) sscanf(p, "%x", &cpu); SKIP_UNTIL_SPACES(p); SKIP_SPACES(p); if(*p) sscanf(p, "%x", &timer); if(val != -1) { /*--- printk("%d %d\n", val, cpu); ---*/ if(timer && (cpu < NR_CPUS)) { init_timer(&torture_Timer[cpu]); torture_Timer[cpu].data = (unsigned long)val; torture_Timer[cpu].function = torture_function; torture_Timer[cpu].expires = jiffies + HZ * timer; add_timer_on(&torture_Timer[cpu], cpu); } else #if defined(CONFIG_SMP) if(cpu != raw_smp_processor_id()) { smp_call_function_single(cpu, torture, (void *)val, 0); } else #endif /*--- #if defined(CONFIG_SMP) ---*/ torture((void *)val); } } else { struct _debug_client *pdbg = find_dbgclient_by_prefix(p); if(pdbg) { pdbg->CallBackDebug(p + strlen(pdbg->prefix), pdbg->refdata); #ifdef CONFIG_PRINTK } else { printk(KERN_ERR"\n[avm_debug]unknown mode: use: %s, %s or %s \n", PRINTK_DBG_MODE, AVM_DBG_MODE, AVM_DBG_EOF); #endif /*--- #ifdef CONFIG_PRINTK ---*/ } } return write_length; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _debug_client *find_dbgclient_by_prefix(char *prefix){ struct _debug_client *pdbg; unsigned long flags; avmdebug_lock(&avm_debug.client_lock, &flags, 1); pdbg = avm_debug.dbg_clientAnker; while(pdbg) { if(strncmp(prefix, pdbg->prefix, strlen(pdbg->prefix)) == 0) { avmdebug_unlock(&avm_debug.client_lock, flags); return pdbg; } pdbg = pdbg->next; } avmdebug_unlock(&avm_debug.client_lock, flags); return NULL; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _debug_client *add_dbgclient(char *prefix, void (*CallBackDebug)(char *string, void *refdata), void *refdata){ struct _debug_client *pdbg; unsigned long flags; pdbg = kmalloc(sizeof(struct _debug_client) + strlen(prefix) + 1, GFP_KERNEL); if(pdbg == NULL) { return NULL; } pdbg->CallBackDebug = CallBackDebug; pdbg->refdata = refdata; pdbg->prefix = (char *)pdbg + sizeof(struct _debug_client); strcpy(pdbg->prefix, prefix); pdbg->next = NULL; avmdebug_lock(&avm_debug.client_lock, &flags, 1); pdbg->next = avm_debug.dbg_clientAnker; avm_debug.dbg_clientAnker = pdbg; avmdebug_unlock(&avm_debug.client_lock, flags); return pdbg; } /*-------------------------------------------------------------------------------------*\ * Debug-Funktion am Treiber anmelden * prefix: der Inputdaten werden nach diesem Prefix durchsucht, und bei Match * wird die CallbackFkt aufgerufen * um also den string 'blabla=haha' zum Treiber angemeldet mit prefix 'unicate_' zu transportieren * ist einfach ein "echo unicate_blabla=haha >/dev/debug" auf der Konsole auszufuehren * ret: handle (fuer UnRegister) \*-------------------------------------------------------------------------------------*/ void *avm_DebugCallRegister(char *prefix, void (*CallBackDebug)(char *string, void *refdata), void *refdata){ struct _debug_client *client; DEB_INFO("[avm_debug] DebugCallRegister(\"%s\", 0x%p, %p)\n",prefix, CallBackDebug, refdata); if(prefix == NULL || CallBackDebug == NULL) { DEB_ERR("[avm_debug] DebugCallRegister(\"%s\", 0x%p, %p): invalid param\n",prefix, CallBackDebug, refdata); return NULL; } SKIP_SPACES(prefix); client = find_dbgclient_by_prefix(prefix); if(client) { DEB_ERR("[avm_debug]DebugCallRegister: prefix '%s' already exist\n", prefix); return NULL; } return add_dbgclient(prefix, CallBackDebug, refdata); } EXPORT_SYMBOL(avm_DebugCallRegister); /*--------------------------------------------------------------------------------*\ * Debug-Funktion am Treiber abmelden \*--------------------------------------------------------------------------------*/ void avm_DebugCallUnRegister(void *handle){ struct _debug_client *pdbg, *prev = NULL; unsigned long flags; DEB_INFO("[avm_debug]avm_DebugCallUnRegister: %p done\n", handle); avmdebug_lock(&avm_debug.client_lock, &flags, 1); pdbg = avm_debug.dbg_clientAnker; while(pdbg) { if(pdbg == handle) { if(prev == NULL) { avm_debug.dbg_clientAnker = pdbg->next; } else { prev->next = pdbg->next; } avmdebug_unlock(&avm_debug.client_lock, flags); kfree(pdbg); DEB_INFO("[avm_debug]avm_DebugCallUnRegister: %p done\n", pdbg); return; } prev = pdbg; pdbg = pdbg->next; } avmdebug_unlock(&avm_debug.client_lock, flags); DEB_ERR("[avm_debug]avm_DebugCallUnRegister: error: no handle for %p found\n", pdbg); } EXPORT_SYMBOL(avm_DebugCallUnRegister); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int avm_debug_poll(struct file *file, poll_table * wait) { unsigned int mask = POLLOUT; poll_wait(file, &avm_debug.recvwait, wait); if (avm_debug.read != avm_debug.write) { mask |= POLLIN | POLLRDNORM; } return mask; } #define AVM_DBGWRAP_STR "[AVMDBG_OVR]" /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static ssize_t avm_debug_read(struct file *filp, char *read_buffer, size_t max_read_length, loff_t *read_pos) { char *dst; unsigned int copy_length = 0; unsigned int local_read, local_write; unsigned long flags; if(filp) { unsigned int minor = (unsigned int)filp->private_data; if(avm_debug_read_minor[minor]) return (*avm_debug_read_minor[minor])(filp, read_buffer, max_read_length, read_pos); } if(filp) { dst = avm_debug.tmpbuf; if(max_read_length > sizeof(avm_debug.tmpbuf)) { max_read_length = sizeof(avm_debug.tmpbuf); } } else { dst = read_buffer; } for( ;; ) { if(avmdebug_lock(&avm_debug.con_lock, &flags, 0) == 0) { /*--- printk_linux(KERN_ERR"%s: #no lock get\n\n", __func__); ---*/ if(filp == NULL) { return 0; } if (filp->f_flags & O_NONBLOCK) { return -EAGAIN; } interruptible_sleep_on_timeout(&avm_debug.recvwait, HZ / 10); if (signal_pending(current)) { return -ERESTARTNOHAND; } continue; } local_read = avm_debug.read; local_write = avm_debug.write; if(local_read == local_write) { if(filp == NULL) { avmdebug_unlock(&avm_debug.con_lock, flags); return 0; } if(avm_debug.eof_sync == 1) { avm_debug.eof_sync = 3; /*--- Debugbuffer nochmal reaktivieren ---*/ if(avm_debug.written >= avm_debug.size) { avm_debug.read = inc_idx(avm_debug.write, avm_debug.size); } else { avm_debug.read = 0; } printk(KERN_ERR"---> reanimated debugbuffer: read=%d write=%d, written=%d <---\n", avm_debug.read, avm_debug.write, avm_debug.written); } avmdebug_unlock(&avm_debug.con_lock, flags); if(avm_debug.eof_sync) { /*--- erzwinge Beenden von cat etc. ---*/ return -EPIPE; } if (filp->f_flags & O_NONBLOCK) { return -EAGAIN; } interruptible_sleep_on_timeout(&avm_debug.recvwait, HZ / 10); if (signal_pending(current)) { return -ERESTARTNOHAND; } continue; } if(unlikely(avm_debug.wrap)) { if(max_read_length >= sizeof(AVM_DBGWRAP_STR) - 1) { avm_debug.wrap = 0; copy_length = sizeof(AVM_DBGWRAP_STR) - 1; if(dst) memcpy(dst, AVM_DBGWRAP_STR, copy_length); goto wrap_continue; } } if(local_read <= local_write) { copy_length = local_write - local_read; } else { copy_length = avm_debug.size - local_read; } if(copy_length > max_read_length) { copy_length = max_read_length; } if(dst) memcpy(dst, avm_debug.buffer + local_read, copy_length); if(avm_debug.read + copy_length >= avm_debug.size) { avm_debug.read = 0; } else { avm_debug.read += copy_length; } wrap_continue: avmdebug_unlock(&avm_debug.con_lock, flags); /*--- copy_to_user() maybe sleep - tmpbuf needed ---*/ if(filp && copy_to_user(read_buffer, dst, copy_length)) { /*--- DBG_ERR("[avm_debug]: copy_to_user failed (read_pos %llu / copy length %u)\n", read_pos ? *read_pos : 0, copy_length); ---*/ return -EFAULT; } break; } if(read_pos)*read_pos += copy_length; return copy_length; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int avm_debugfill(void) { unsigned long size; if(avm_debug.read > avm_debug.write) { size = avm_debug.size - (avm_debug.read - avm_debug.write); } else { size = avm_debug.write - avm_debug.read; } return size; } /*------------------------------------------------------------------------------------------*\ * ret: letzte Zeichen war \r bzw. \n \*------------------------------------------------------------------------------------------*/ int DebugPrintf_Puts(char *DebugData, unsigned int length) { unsigned int local_read, local_write, wrap = 0; unsigned long flags; int ret = 0; if(unlikely(DebugData == NULL) || unlikely(length == 0)) { return 0; } if(*(DebugData+length-1) == '\r' || *(DebugData+length-1) == '\n' ) { ret = 1; } if(avmdebug_lock(&avm_debug.con_lock, &flags, 0)) { local_write = avm_debug.write; local_read = avm_debug.read; avm_debug.written += length; while(length--) { avm_debug.buffer[local_write] = *DebugData++; local_write = inc_idx(local_write, avm_debug.size); if(local_write == local_read) { wrap++; } } if(unlikely(wrap)) { avm_debug.read = inc_idx(local_write, avm_debug.size); avm_debug.wrap = wrap; } avm_debug.write = local_write; avmdebug_unlock(&avm_debug.con_lock, flags); #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) /*--- somit darf avmDebug_Printf auch im yield-context verwendet werden ---*/ if(!is_yield_context()) #endif/*--- #if defined(CONFIG_SMP) ---*/ wake_up(&avm_debug.recvwait); } return ret; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static char *itoa(unsigned int zahl, char *Buffer, unsigned int base) { char tmp[sizeof(unsigned int) * 8 + 2]; char ch; char *Ptr = Buffer; unsigned int Len = 0; static const char HexTab[] = "0123456789ABCDEF"; if(zahl == 0) { Buffer[0] = '0'; Buffer[1] = '\0'; return Buffer; } Buffer[0] = '\0'; switch(base) { case 16: while(zahl) { tmp[Len] = HexTab[zahl & 0x0F]; zahl >>= 4; Len++; } break; case 8: while(zahl) { tmp[Len] = HexTab[zahl & 0x07]; zahl >>= 3; Len++; } break; case 2: while(zahl) { tmp[Len] = HexTab[zahl & 0x01]; zahl >>= 1; Len++; } break; case 10: while(zahl) { ch = (char)(zahl % 10); zahl /= 10; if(ch <= 9) tmp[Len] = (char)(ch + '0'); else tmp[Len] = (char)(ch + 'A' - 10); Len++; } break; default: while(zahl) { ch = (char)(zahl % base); zahl /= base; if(ch <= 9) tmp[Len] = (char)(ch + '0'); else tmp[Len] = (char)(ch + 'A' - 10); Len++; } } while(Len) { *Ptr++ = tmp[--Len]; } *Ptr = '\0'; return Buffer; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int atoi (const char *nptr) { int num = 0, neg = 0; while(*nptr && (*nptr == ' ' || *nptr == '\t')) if(*nptr == '\0') nptr++; switch(*nptr) { case '\0': return 0; case '-': neg = 1; nptr++; break; case '+': neg = 0; nptr++; break; } while(*nptr && *nptr >= '0' && *nptr <= '9') { num = (10 * num) + (*nptr - '0'); nptr++; } return neg ? -num : num; } #define avm_LimitOut(ActLimit) if((int)pud->Pos >= (int)(ActLimit)) {*(DebugData + pud->Pos) = '\0'; DebugPrintf_Puts(DebugData, pud->Pos); pud->Sum += pud->Pos;pud->Pos = 0;} /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ struct _avmdebug_datahandler { unsigned int Pos; unsigned int Sum; int field_length, field_prec; enum { no_extension = '\0', name_of_text_symbol = 'S', name_of_function_pointer ='F', adress_range_in_struct_resource='R', dump_memory = 'B'} p_ext; unsigned char NextIsLong; unsigned char FillZero; unsigned char Leftjust; char SetSign; }; /*-------------------------------------------------------------------------------------*\ %[0-+ #:*][prec/len][l]d \*-------------------------------------------------------------------------------------*/ static const char *avmdebug_parse_percent(const char *format, struct _avmdebug_datahandler *pud, va_list *marker) { format++; pud->field_length = 0; pud->field_prec = 0; pud->Leftjust = FALSE; pud->SetSign = 0; pud->FillZero = FALSE; while ((*format == '-') || (*format == '+') || (*format == ' ') || (*format == '0') || (*format == '#') || (*format == ':')) { switch(*format) { case '-': pud->Leftjust = TRUE; break; case ' ': if (pud->SetSign == 0) pud->SetSign = ' '; break; case '0': if (pud->Leftjust == FALSE) pud->FillZero = TRUE; break; case '+': case '#': case ':': pud->SetSign = *format; } format++; } if (*format == '*') { format++; pud->field_length = (va_arg(*marker, int)); } else if(*format >= '0' && *format <= '9') { /*--- mindestanzahl der zahllaenge ---*/ pud->field_length = atoi(format); while(*format >= '0' && *format <= '9') { format++; } if(pud->field_length < 0) { pud->field_length = 0; } } if(*format == '.') { /*--- ignorieren ---*/ format++; if (*format == '*') { format++; pud->field_prec = (va_arg(*marker, int)); } else { pud->field_prec = atoi(format); while(*format >= '0' && *format <= '9') { format++; } } if(pud->field_prec < 0) { pud->field_prec = 0; } } switch(*format) { case 'F': case 'N': case 'h': pud->NextIsLong = FALSE; format++; break; case 'l': pud->NextIsLong = FALSE; format++; if((*format) != 'l') { break; } /*--- kein break ---*/ case 'L': pud->NextIsLong = TRUE; format++; break; case 'z': if(sizeof(size_t) == sizeof(unsigned long long)) { pud->NextIsLong = TRUE; } else { pud->NextIsLong = FALSE; } format++; break; case 'Z': if(sizeof(size_t) == sizeof(unsigned long long)) { pud->NextIsLong = TRUE; } else { pud->NextIsLong = FALSE; } format++; break; } if ((pud->field_prec > pud->field_length) && (*format != 's')) { pud->field_length = pud->field_prec; } return format; } /*-------------------------------------------------------------------------------------*\ * auch für bin, octal \*-------------------------------------------------------------------------------------*/ static void avmdebug_set_uint(char *DebugData, struct _avmdebug_datahandler *pud, va_list *marker, unsigned int mode) { int Len; unsigned int Value; char Data[66]; if(pud->NextIsLong == TRUE) { unsigned long long lValue; lValue = va_arg(*marker, long long); snprintf(Data, sizeof(Data), "%llu", lValue); Value = lValue ? 1 : 0; } else { Value = va_arg(*marker, int); itoa(Value, Data, mode); } if((pud->SetSign != 0)) { if(mode == 10) *(DebugData + pud->Pos) = pud->SetSign; else if((mode == 8) && (Value != 0)) *(DebugData + pud->Pos) = '0'; pud->Pos++; } Len = strlen(Data); if(pud->Leftjust == TRUE) { /*--- linksbuendig ---*/ memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)Data, Len); pud->Pos += Len; } while(pud->field_length > Len) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 2); if(pud->FillZero && (pud->Leftjust == FALSE)) *(DebugData + pud->Pos) = '0'; else *(DebugData + pud->Pos) = ' '; pud->Pos++; pud->field_length--; } if(pud->Leftjust == FALSE) { /*--- rechtsbuendig ---*/ memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)Data, Len); pud->Pos += Len; } } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static void avmdebug_set_int(char *DebugData, struct _avmdebug_datahandler *pud, va_list *marker) { int Len; char Data[66]; int Variable; if(pud->NextIsLong == TRUE) { signed long long lValue; lValue = va_arg(*marker, long long); snprintf(Data, sizeof(Data), "%lld", lValue); } else { Variable = va_arg(*marker, int); if((signed int)Variable < 0) { *(DebugData + pud->Pos) = '-'; Variable = (unsigned int)(0 - (signed int)Variable); pud->Pos++; } else { if(pud->SetSign != 0) { *(DebugData + pud->Pos) = pud->SetSign; pud->Pos++; } } itoa((unsigned int)Variable, Data, 10); } Len = strlen(Data); if(pud->Leftjust == TRUE) { /*--- linksbuendig ---*/ memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)Data, Len); pud->Pos += Len; } while(pud->field_length > Len) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 2); if(pud->FillZero && (pud->Leftjust == FALSE)) *(DebugData + pud->Pos) = '0'; else *(DebugData + pud->Pos) = ' '; pud->Pos++; pud->field_length--; } if(pud->Leftjust == FALSE) { /*--- rechtsbuendig ---*/ memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)Data, Len); pud->Pos += Len; } } extern char *module_alloc_find_module_name(char *buff, char *end, unsigned long addr) __attribute__ ((weak)); /*-------------------------------------------------------------------------------------*\ * auch für hex, pointer (mode = 1) \*-------------------------------------------------------------------------------------*/ static void avmdebug_set_hex(char *DebugData, struct _avmdebug_datahandler *pud, va_list *marker, unsigned int mode) { char Data[16 + 1]; /*--- maximale Stellen einer 64 Bit hexzahl + 2 ---*/ int Len; unsigned int Val = 0; if(pud->NextIsLong == TRUE) { signed long long lValue; lValue = va_arg(*marker, long long); snprintf(Data, sizeof(Data), "%llx", lValue); } else { Val = va_arg(*marker, int); if((mode == 1) && (Val == 0)) { strcpy(Data, "(null)"); } else { itoa(Val, Data, 16); } } if(mode == 1) { char *modname; const char *name; unsigned long offset, size; char tmp[256]; switch(pud->p_ext) { case name_of_function_pointer: /*--- ignore: only ia64 und ppc: ---*/ /*--- Val = dereference_function_descriptor(Val); ---*/ /*--- kein break; ---*/ case name_of_text_symbol: #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) if(is_yield_context()) { break; } #endif/*--- #if defined(CONFIG_SMP) ---*/ #ifdef CONFIG_KALLSYMS name = kallsyms_lookup(Val, &size, &offset, &modname, tmp); if(!name) { break; } if(modname) { Len = snprintf(tmp, sizeof(tmp), "%s+%#lx/%#lx [%s]", name, offset, size, modname); }else { Len = snprintf(tmp, sizeof(tmp), "%s+%#lx/%#lx", name, offset, size); } #else if(!IS_ERR(module_alloc_find_module_name)) { name = module_alloc_find_module_name(tmp, tmp + sizeof(tmp), Val); } else { break; } Len = name - tmp; #endif avm_LimitOut(Len + 1); strcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)tmp); pud->Pos += Len; return; case adress_range_in_struct_resource: /*--- ignore ---*/ break; case dump_memory: /* Already done */ return; default: break; } } Len = strlen(Data); if((pud->Leftjust == TRUE) || (pud->field_length <= Len)) { /*--- linksbuendig ---*/ if (pud->SetSign == '#') { *(DebugData + pud->Pos++) = '0'; *(DebugData + pud->Pos++) = 'x'; } memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)Data, Len); pud->Pos += Len; while(pud->field_length > Len) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 2); *(DebugData + pud->Pos++) = ' '; pud->field_length--; } } else { if(pud->FillZero) { if (pud->SetSign == '#') { /*--- field_length -= 2; ---*/ *(DebugData + pud->Pos++) = '0'; *(DebugData + pud->Pos++) = 'x'; } while(pud->field_length > Len) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 2); *(DebugData + pud->Pos++) = '0'; pud->field_length--; } } else { while(pud->field_length > Len) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 2); *(DebugData + pud->Pos++) = ' '; pud->field_length--; } if (pud->SetSign == '#') { *(DebugData + pud->Pos++) = '0'; *(DebugData + pud->Pos++) = 'x'; } } avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - Len - 1); memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)Data, Len); pud->Pos += Len; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int check_memory_pointer(void *addr){ int ret = 0; #if defined(CONFIG_MIPS_UR8) if((((unsigned int)addr < 0x94000000) || ((unsigned int)addr >= 0x98000000)) && (((unsigned int)addr < 0xA4000000) || ((unsigned int)addr >= 0xA4008000))) { if(vmalloc_to_page(addr) == NULL) { ret = 1; } } #elif defined(CONFIG_MIPS) if((((unsigned int)addr < KSEG0)) || (((unsigned int)addr >= KSEG2))) { #if defined(CONFIG_SMP) if(is_yield_context()) { ret = 1; } else #endif/*--- #if defined(CONFIG_SMP) ---*/ if(vmalloc_to_page(addr) == NULL) { ret = 1; } } #elif defined(CONFIG_ARM) /*--- #if defined(CONFIG_MIPS) ---*/ if((unsigned int)addr < TASK_SIZE) { ret = 1; } else if(((unsigned int)addr >= VMALLOC_START) && ((unsigned int)addr < VMALLOC_END)) { if(vmalloc_to_page(addr) == NULL) { ret = 1; } } #endif/*--- #elif defined(CONFIG_ARM) ---*//*--- #if defined(CONFIG_MIPS) ---*/ return ret; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static void avmdebug_set_hexfield(char *DebugData, struct _avmdebug_datahandler *pud, va_list *marker) { unsigned char *B; char Hex[] = "0123456789ABCDEF"; char tmp[32]; B = (va_arg(*marker, unsigned char *)); if (pud->field_length == 0) { memcpy((unsigned char *)(DebugData + pud->Pos), "(0)", sizeof("(0)") - 1); pud->Pos += sizeof("(0)") - 1; return; } if(B == NULL) { memcpy((unsigned char *)(DebugData + pud->Pos), "(null)", sizeof("(null)") - 1); pud->Pos += sizeof("(null)") - 1; return; } if(check_memory_pointer(B)){ snprintf(tmp, sizeof(tmp), "(inval=0x%x)", (unsigned int)B); B = tmp; } if(pud->Leftjust == TRUE) { /*--- reverse ---*/ B += pud->field_length - 1; } while(pud->field_length--) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 10); if(pud->SetSign == '0') { *(DebugData + pud->Pos++) = '0'; *(DebugData + pud->Pos++) = 'x'; } *(DebugData + pud->Pos++) = Hex[*B >> 4]; *(DebugData + pud->Pos++) = Hex[*B & 0x0F]; if(pud->Leftjust == TRUE) { /*--- reverse ---*/ B--; } else { B++; } if(pud->field_length) { if(pud->SetSign == ':') *(DebugData + pud->Pos++) = ':'; else *(DebugData + pud->Pos++) = ' '; } } } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static void avmdebug_set_string(char *DebugData, struct _avmdebug_datahandler *pud, va_list *marker) { char tmp[32]; unsigned int Len = 0; char *pstring = (va_arg(*marker, char *)), *ptmp; if(pstring == NULL) { pstring = "(null)"; } if(check_memory_pointer(pstring)){ snprintf(tmp, sizeof(tmp), "(inval=0x%x)", (unsigned int)pstring); pstring = tmp; } if (pud->field_prec == 0) { pud->field_prec = MAX_DEBUG_MESSAGE_LEN - pud->Pos - 2; } ptmp = pstring; while(*ptmp++ && (Len < (unsigned)pud->field_prec)) { Len++; } if(pud->Leftjust == TRUE) { /*--- linksbuendig ---*/ pud->field_length -= Len; while(Len) { unsigned int LimitLen = MAX_DEBUG_MESSAGE_LEN - 1 - pud->Pos; if(LimitLen > Len) { LimitLen = Len; } memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)pstring, LimitLen); pud->Pos += LimitLen; Len -= LimitLen; avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - Len - 1); } } while((unsigned)pud->field_length > Len) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 2); if(pud->FillZero && (pud->Leftjust == FALSE)) *(DebugData + pud->Pos) = '0'; else *(DebugData + pud->Pos) = ' '; pud->Pos++; pud->field_length--; } if(pud->Leftjust == FALSE) { /*--- rechtsbuendig ---*/ while(Len) { unsigned int LimitLen = MAX_DEBUG_MESSAGE_LEN - 1 - pud->Pos; if(LimitLen > Len) { LimitLen = Len; } memcpy((unsigned char *)(DebugData + pud->Pos), (unsigned char *)pstring, LimitLen); pud->Pos += LimitLen; Len -= LimitLen; avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - Len - 1); } } } #if (MAX_DEBUG_MESSAGE_LEN < 127) #error MAX_DEBUG_MESSAGE_LEN zu klein ( <127 )!!!! #endif /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ unsigned char *alloc_debugstack(atomic_t **used) { unsigned int i; for(i = 0; i < MAX_DEBUG_STACK; i++) { if(atomic_add_return(1, &avm_debug.debugstack[i].used) == 1) { *used = &avm_debug.debugstack[i].used; return avm_debug.debugstack[i].buf; } } return NULL; } #if defined(CONFIG_SMP) #define CPU_ARGUMENT_STRING() "[%x] " #define CPU_ID() ,raw_smp_processor_id() #else #define CPU_ARGUMENT_STRING() " " #define CPU_ID() #endif/*--- #if defined(CONFIG_SMP) ---*/ #if defined(CONFIG_PRINTK_TIME) #define TIME_ARGUMENT_STRING() "[%5lu.%06lu]" #define TIME_ARGUMENT() (unsigned long)(clk), clk_rem / 1000 #else #define TIME_ARGUMENT_STRING() "[%08lu]" #define TIME_ARGUMENT() (unsigned long)(jiffies) #endif /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int debug_prefix(char *DebugData, unsigned int lost) { int len; #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) /*--- somit darf avmDebug_Printf auch im yield-context verwendet werden ---*/ if(is_yield_context()) { if(lost) { len = sprintf(DebugData, "<[%lu][Y]debug_message lost %d>", jiffies, lost); } else { len = sprintf(DebugData, "[%lu][Y]", jiffies); } return len; } #endif/*--- #if defined(CONFIG_SMP) ---*/ { #if defined(CONFIG_PRINTK_TIME) /* Follow the token with the time */ unsigned long clk_rem; unsigned long long clk = cpu_clock(raw_smp_processor_id()); clk_rem = do_div(clk, 1000000000); #endif /*--- #if defined(CONFIG_PRINTK_TIME) ---*/ if(lost) { len = sprintf(DebugData, "<"TIME_ARGUMENT_STRING()"debug-message lost %d >", TIME_ARGUMENT(), lost); } else { len = sprintf(DebugData, TIME_ARGUMENT_STRING() CPU_ARGUMENT_STRING(), TIME_ARGUMENT() CPU_ID()); } } return len; } /*-------------------------------------------------------------------------------------*\ * Mode: in: 0x1 print timestamp * in: 0x2 verodert: Aufruf von AVM_DebugPrintf() -> Support von %*B aber nicht aller Kernel-Formate * out: 0x1 last was \r bzw. \n * Mode>>8: old-loglevel \*-------------------------------------------------------------------------------------*/ int avm_DebugvPrintf(unsigned *Mode, const char *format, va_list marker) { struct _avmdebug_datahandler ud, *pud = &ud; char *DebugData; atomic_t *used; int cr = 0, len; DebugData = alloc_debugstack(&used); if(DebugData == NULL) { avm_debug.lost++; if(Mode) *Mode = cr; return 0; } pud->Sum = 0; if(avm_debug.lost) { len = debug_prefix(DebugData, avm_debug.lost); if(len > 0) { cr = DebugPrintf_Puts(DebugData, len); pud->Sum = len; } avm_debug.lost = 0; } #ifdef CONFIG_KALLSYMS if(Mode && (*Mode & 0x2)) { #else /*--- wir benoetigen Modul-Textaufloesung: ---*/ #endif/*--- #ifdef CONFIG_KALLSYMS ---*/ if(Mode && (*Mode & 0x1)) { len = debug_prefix(DebugData, 0); if(len > 0) { cr = DebugPrintf_Puts(DebugData, len); pud->Sum = len; } } pud->Pos = 0; pud->NextIsLong = FALSE; pud->p_ext = no_extension; while(*format) { avm_LimitOut(MAX_DEBUG_MESSAGE_LEN - 66); switch(*format) { case '\b': if(pud->Pos) pud->Pos--; break; case '\t': /*--- tab size 4 ---*/ while(pud->Pos & 0x03) *(DebugData + pud->Pos++) = ' '; break; case '%': /*---------------------------------------------------------------------*\ %[0]4[l]d \*---------------------------------------------------------------------*/ format = avmdebug_parse_percent(format, pud, &marker); switch(*format) { case '\0': /*--- printk("--- erzeugte Fehler: %x\n", *(format+1)); ---*/ continue; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case '%': *(DebugData + pud->Pos) = '%'; pud->Pos++; break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'n': *((unsigned int *)(va_arg(marker, void *))) = pud->Pos; format++; break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'c': *(DebugData + pud->Pos) = (unsigned char)(va_arg(marker, int)); pud->Pos++; break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'u': avmdebug_set_uint(DebugData, pud, &marker, 10); break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'i': case 'd': avmdebug_set_int(DebugData, pud, &marker); break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'b': avmdebug_set_uint(DebugData, pud, &marker, 2); break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'o': avmdebug_set_uint(DebugData, pud, &marker, 8); break; /*-----------------------------------------------------------------*\ unsigned char Bytes[Count] Count default = 1 \*-----------------------------------------------------------------*/ case 'B': avmdebug_set_hexfield(DebugData, pud, &marker); break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 's': avmdebug_set_string(DebugData, pud, &marker); break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'p': case 'P': switch(*(format+1)) { case 'S': case 'F': case 'f': case 'R': pud->p_ext = *(format+1); format++; break; case 'B': avmdebug_set_hexfield(DebugData, pud, &marker); pud->p_ext = *(format+1); format++; break; default: pud->p_ext = no_extension; } avmdebug_set_hex(DebugData, pud, &marker, 1); break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 'x': case 'X': avmdebug_set_hex(DebugData, pud, &marker, 0); break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ case 't': { unsigned int Time = va_arg(marker, int); *(DebugData + pud->Pos++) = (char)0xAB; *(DebugData + pud->Pos++) = (char)(Time >> 0); *(DebugData + pud->Pos++) = (char)(Time >> 8); *(DebugData + pud->Pos++) = (char)(Time >> 16); *(DebugData + pud->Pos++) = (char)(Time >> 24); *(DebugData + pud->Pos++) = (char)0xBA; } break; /*-----------------------------------------------------------------*\ \*-----------------------------------------------------------------*/ default: *(DebugData + pud->Pos) = *format; pud->Pos++; } pud->NextIsLong = FALSE; break; default: *(DebugData + pud->Pos) = *format; pud->Pos++; } format++; } #ifdef CONFIG_KALLSYMS } else { int level = 0; const char *_DebugData; len = 0; if(Mode) { level = (*Mode >> 8); /*--- old-loglevel ---*/ if(*Mode & 0x1) { len = debug_prefix(DebugData, 0); if(len < 0) { len = 0; } } } pud->Pos = min(vsnprintf(&DebugData[len], MAX_DEBUG_MESSAGE_LEN - len, format, marker), MAX_DEBUG_MESSAGE_LEN - len); /*--- falls Loglevel im Formatstring mittels "%sfoo" ---*/ _DebugData = &DebugData[len]; level = skip_loglevel(&_DebugData, level); if (console_loglevel >= level) { if(len) { cr = DebugPrintf_Puts(DebugData, len); pud->Sum = len; } pud->Pos -= (_DebugData - &DebugData[len]); DebugData = (char *)_DebugData; } else { pud->Pos = 0; } /*--- printk_linux("\n len=%d Pos=%d level=%d, console_loglevel=%d\n", len, pud->Pos, level, console_loglevel); ---*/ } #endif/*--- #ifdef CONFIG_KALLSYMS ---*/ if(pud->Pos == 0) { atomic_set(used,0); if(Mode) *Mode = cr; return pud->Sum; } *(DebugData + pud->Pos) = '\0'; cr = DebugPrintf_Puts(DebugData, pud->Pos); if(Mode) *Mode = cr; atomic_set(used,0); return pud->Pos + pud->Sum; } EXPORT_SYMBOL(avm_DebugvPrintf); /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ void avm_DebugPrintf(const char *format, ...) { int print_time = 0x2; const char *start_format = format; va_list marker; int level = skip_loglevel(&format, 0); if (console_loglevel < level) { return; } if(start_format != format) { print_time |= 0x1; /*--- Loglevel gefunden -> Timestamp davor ---*/ } va_start(marker,format); if(likely(avm_debug.init)) { avm_DebugvPrintf(&print_time, format, marker); } else { #if defined(CONFIG_SMP) && defined(CONFIG_MIPS) /*--- somit darf avmDebug_Printf auch im yield-context verwendet werden ---*/ if(!is_yield_context()) #endif/*--- #if defined(CONFIG_SMP) ---*/ vprintk(format, marker); } va_end(marker); } #ifdef CONFIG_PRINTK static char syncbuf[257]; /*--------------------------------------------------------------------------------*\ * avm_DebugPrintf -> printk \*--------------------------------------------------------------------------------*/ #define OUTPUT_LASTBUFFER (1 << (CONFIG_LOG_BUF_SHIFT - 1)) static void avmdebug_sync(void) { int start = 0; unsigned long flags; unsigned long actlen, waste = 0, mw; if(avm_debug.init == 0) { return; } avmdebug_lock(&avm_debug.synclock, &flags, 1); actlen = avm_debugfill(); if(actlen > OUTPUT_LASTBUFFER) { waste = actlen - OUTPUT_LASTBUFFER; } mw = waste; /* printk_linux("avmdebug_sync: %ld, %ld %ld\n", mw, avm_debug.read, avm_debug.write); */ while((actlen = avm_debug_read(NULL, NULL, waste, NULL))) { waste -= actlen; } for(;;) { int len; len = avm_debug_read(NULL, syncbuf, sizeof(syncbuf) - 1, NULL); if(len <= 0) { break; } if(start == 0) { start = 1; printk_linux("\n---- start avmdebug(suppress %ld bytes) ----\n", mw); } syncbuf[len] = 0; printk_linux("%s", syncbuf); } if(start) { printk_linux("\n---- eof avmdebug ----\n"); } avmdebug_unlock(&avm_debug.synclock, flags); } #endif /*--- #ifdef CONFIG_PRINTK ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ asmlinkage int vprintk_avm(const char *format, va_list args) { int r = 0; #ifdef CONFIG_PRINTK r = avm_kernel_vprintk(format, args); #endif /*--- #ifdef CONFIG_PRINTK ---*/ return r; } EXPORT_SYMBOL(vprintk_avm); /*-------------------------------------------------------------------------------------*\ * Ersatz für printk * inklusive Loglevelauswertung \*-------------------------------------------------------------------------------------*/ asmlinkage int printk_avm(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = avm_kernel_vprintk(format, args); va_end(args); return ret; } EXPORT_SYMBOL(printk_avm); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int avm_kernel_vprintk(const char *format, va_list marker) { #ifdef CONFIG_PRINTK static int loglevel; static int last_was_nl; int print_time = 0; if(oops_in_progress) { int ret; #ifdef CONFIG_PRINTK avm_debug_disable_avm_printk(); /* weitere Ausgaben nur noch über standard-printk */ avmdebug_sync(); #endif /*--- #ifdef CONFIG_PRINTK ---*/ /*--- falls Kernel-OOPs, dann auf Linux-Methode biegen ! ---*/ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) ret = vprintk_linux(format, marker); #else/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) ---*/ ret = vprintk(format, marker); #endif/*--- #else ---*//*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) ---*/ return ret; } loglevel = skip_loglevel(&format, loglevel); if (loglevel) { #if defined(CONFIG_PRINTK_TIME) print_time = 0x1; #endif /*--- #if defined(CONFIG_PRINTK_TIME) ---*/ if (!last_was_nl) { last_was_nl = DebugPrintf_Puts("\n", 1); } } if(loglevel < console_loglevel) { int ret; print_time |= loglevel << 8; ret = avm_DebugvPrintf(&print_time, format, marker); last_was_nl = print_time; if(last_was_nl) { /*--- letzte Zeichen war \r bzw \n -> Loglevel auf Default ---*/ loglevel = default_message_loglevel; } return ret; } #endif /*--- #ifdef CONFIG_PRINTK ---*/ return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ EXPORT_SYMBOL(avm_DebugPrintf); /*--------------------------------------------------------------------------------*\ * signal 0 .. 31 * signal: 0 -> pushmail 2 * signal: 1 -> crashreport \*--------------------------------------------------------------------------------*/ void avm_DebugSignal(unsigned int signal){ if(avm_debug.init) { unsigned long flags; avm_DebugPrintf("%s: %x %s %d\n", __func__, signal & 0x1F, signal & 0x80000000 ? "user pid:" : "kernel info:", (signal & ~0x80000000) >> 8); signal &= 0x1F; avmdebug_lock(&avm_debug.client_lock, &flags, 1); avm_debug.signal |= 0x1 << signal; avmdebug_unlock(&avm_debug.client_lock, flags); wake_up_interruptible(&avm_debug.wait_queue); } } EXPORT_SYMBOL(avm_DebugSignal); /*--------------------------------------------------------------------------------*\ * signal 0 .. 31 \*--------------------------------------------------------------------------------*/ void avm_DebugSignalLog(unsigned int signal, char *fmt, ...){ if(avm_debug.init) { va_list marker; va_start(marker,fmt); avm_DebugPrintf("avm_DebugSignal:"); avm_DebugvPrintf(NULL, fmt, marker); avm_DebugPrintf("\n"); va_end(marker); avm_DebugSignal(signal); } } EXPORT_SYMBOL(avm_DebugSignalLog); /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static int send_buf(struct socket *sock, struct sockaddr_un *paddr, unsigned char *buffer, unsigned length, unsigned wait) { struct msghdr msg; struct iovec iov; mm_segment_t oldfs; if (sock->sk == NULL) { return 0; } iov.iov_base = (void *)buffer; iov.iov_len = length; msg.msg_name = paddr; msg.msg_namelen = sizeof(*paddr); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = wait ? 0 : MSG_DONTWAIT; oldfs = get_fs(); set_fs(KERNEL_DS); length = sock_sendmsg(sock, &msg, length); set_fs(oldfs); return length; } #define ARRAY_EL(a) (sizeof(a) / sizeof((a)[0])) #define PUSHMSG(a) { str: "\x80"a, size: sizeof(a) + 1 } static struct _pushmsg { char *str; int size; } pushmsg[] = { PUSHMSG("pushmail 2"), PUSHMSG("crashreport") }; /*--------------------------------------------------------------------------------*\ * aus dem Thread-Kontext pushmsg an den ctrlmgr \*--------------------------------------------------------------------------------*/ static void push_mail(struct _avm_debug *pdbg, unsigned int mode) { struct sockaddr_un addr; int err; memset(&addr, 0x0, sizeof(addr)); if(pdbg->s_push == NULL) { if((err = sock_create(AF_UNIX, SOCK_DGRAM, 0, &pdbg->s_push)) < 0) { avm_DebugPrintf(KERN_ERR"[avmdebug]%s: error during creation of socket %d\n", __func__, err); return; } addr.sun_family = AF_UNIX; sprintf(addr.sun_path, "/var/tmp/me_avmdebug.ctl"); if((err = pdbg->s_push->ops->bind(pdbg->s_push, (struct sockaddr *)&addr, sizeof(addr))) < 0) { avm_DebugPrintf(KERN_ERR"[avmdebug]%s:bind failed %d\n", __func__, err); sock_release(pdbg->s_push); pdbg->s_push = NULL; return; } } if(mode >= ARRAY_EL(pushmsg)) { mode = 0; } avm_DebugPrintf("[avmdebug] push: %s\n", pushmsg[mode].str + 1); addr.sun_family = AF_UNIX; sprintf(addr.sun_path, "/var/tmp/me_ctlmgr.ctl"); if((err = send_buf(pdbg->s_push, &addr, pushmsg[mode].str, pushmsg[mode].size, 1)) != pushmsg[mode].size) { avm_DebugPrintf("[avmdebug]%s: failed with ret=%d\n", __func__, err); } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void push_mailexit(struct _avm_debug *pdbg) { #if 0 if(pdbg->s_push->file && s_push->file->f_dentry && s_push->file->f_dentry->d_op && s_push->file->f_dentry->d_op->d_release) { avm_DebugPrintf("unlink %s\n", addr.sun_path); s_push->file->f_dentry->d_op->d_release(s_push->file->f_dentry); /*--- s_push->file->f_dentry->d_op->d_delete(s_push->file->f_dentry); ---*/ } err = sys_unlink(addr.sun_path); avm_DebugPrintf("sys_unlink %s %d\n", addr.sun_path, err); #endif if(pdbg->s_push) { sock_release(pdbg->s_push); pdbg->s_push = NULL; } } #define TIME_DIFF(act, old) ((unsigned long)(act) - (unsigned long)(old)) #define PUSHMAIL_RETRIGGER (300 * HZ) /*--- alle 5 Minuten ---*/ #define AVMDEBUGTHREAD_WAKE_UP (60 * 60 * HZ) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int avmdebug_thread(void *data) { struct _avm_debug *pdbg = (struct _avm_debug *)data; int ret; unsigned long flags, sig = 0; unsigned long timeout = AVMDEBUGTHREAD_WAKE_UP; unsigned long pushmail_lastjiffies = jiffies - PUSHMAIL_RETRIGGER; unsigned long timeoutsignal = 0; set_user_nice(current, 19); while (!kthread_should_stop()) { ret = wait_event_interruptible_timeout(pdbg->wait_queue, pdbg->signal, timeout); if (ret == -ERESTARTSYS) /* interrupted by signal -> exit */ return ret; avmdebug_lock(&avm_debug.client_lock, &flags, 1); sig = pdbg->signal | timeoutsignal; pdbg->signal = 0; avmdebug_unlock(&avm_debug.client_lock, flags); /* avm_DebugPrintf("[avmdebug]sig %lx tsig %lx timeout %ld\n", sig, timeoutsignal, timeout); */ while (sig) { switch (sig) { case 0x1 << 0: { unsigned long diff_last_pushmail_jiffies = TIME_DIFF(jiffies, pushmail_lastjiffies); if (diff_last_pushmail_jiffies > PUSHMAIL_RETRIGGER) { push_mail(pdbg, 0); timeout = AVMDEBUGTHREAD_WAKE_UP; timeoutsignal &= ~0x1 << 0; pushmail_lastjiffies = jiffies; } else { timeoutsignal |= 0x1 << 0; timeout = min(timeout, PUSHMAIL_RETRIGGER - diff_last_pushmail_jiffies); /* avm_DebugPrintf("[avmdebug] trigger too " "early: wait %d sec", timeout / HZ); */ } sig &= ~(0x1 << 0); break; } case 0x1 << 1: push_mail(pdbg, 1); /*--- Crashreport ---*/ sig &= ~(0x1 << 1); break; default: sig = 0; break; } } } push_mailexit(pdbg); pdbg->kthread = NULL; return 0; }