Permalink
Browse files

Milestone's DSP Bridge failure signalization via android property

dsprecovery: under some conditions, the dsp bridge implementation
in Milestone's kernel may end up in an endless loop ("Mailbox full"
msgs repeated forever in dmesg). In such case, this simple module
will set hw.dspbridge.needs_recovery property that can be used
to trigger the dsp bridge reinitialization by init.
  • Loading branch information...
nadlabak committed Apr 16, 2012
1 parent 5654b85 commit 4b1128bdfb8fd18cd9ef8bc6fa2193f4e2e8cf7e
Showing with 480 additions and 0 deletions.
  1. +42 −0 dsp/bridge/Kbuild
  2. +170 −0 dsp/bridge/wmd/dsprecovery.c
  3. +108 −0 dsp/bridge/wmd/hook.c
  4. +49 −0 dsp/bridge/wmd/hook.h
  5. +111 −0 dsp/bridge/wmd/symsearch.h
View
@@ -0,0 +1,42 @@
+obj-m += dsprecovery.o
+dsprecovery-objs := wmd/dsprecovery.o wmd/hook.o
+
+obj-$(CONFIG_MPU_BRIDGE) += bridgedriver.o
+
+libgen = gen/gb.o gen/gt.o gen/gs.o gen/gh.o gen/_gt_para.o gen/uuidutil.o
+libservices = services/csl.o services/mem.o services/list.o services/dpc.o \
+ services/kfile.o services/sync.o \
+ services/clk.o services/cfg.o services/reg.o \
+ services/regsup.o services/ntfy.o \
+ services/dbg.o services/services.o
+libwmd = wmd/chnl_sm.o wmd/msg_sm.o wmd/io_sm.o wmd/tiomap3430.o \
+ wmd/tiomap3430_pwr.o wmd/tiomap_sm.o wmd/tiomap_io.o \
+ wmd/mmu_fault.o wmd/ue_deh.o
+libpmgr = pmgr/chnl.o pmgr/io.o pmgr/msg.o pmgr/cod.o pmgr/dev.o pmgr/wcd.o \
+ pmgr/dmm.o pmgr/cmm.o pmgr/dbll.o
+librmgr = rmgr/dbdcd.o rmgr/disp.o rmgr/drv.o rmgr/mgr.o rmgr/node.o \
+ rmgr/proc.o rmgr/pwr.o rmgr/rmm.o rmgr/strm.o rmgr/dspdrv.o \
+ rmgr/nldr.o rmgr/drv_interface.o
+libdload = dynload/cload.o dynload/getsection.o dynload/reloc.o dynload/tramp.o
+libhw = hw/hw_prcm.o hw/hw_dspssC64P.o hw/hw_mmu.o hw/hw_mbox.o
+
+bridgedriver-objs = $(libgen) $(libservices) $(libwmd) $(libpmgr) $(librmgr) \
+ $(libdload) $(libhw)
+
+# Debug
+ifeq ($(CONFIG_BRIDGE_DEBUG),y)
+ccflags-y += -DGT_TRACE -DDEBUG
+endif
+
+#Machine dependent
+ccflags-y += -D_TI_ -D_DB_TIOMAP -DTMS32060 \
+ -DTICFG_PROC_VER -DTICFG_EVM_TYPE -DCHNL_SMCLASS \
+ -DCHNL_MESSAGES -DUSE_LEVEL_1_MACROS
+
+#Header files
+ccflags-y += -Idrivers/dsp/bridge/services
+ccflags-y += -Idrivers/dsp/bridge/wmd
+ccflags-y += -Idrivers/dsp/bridge/pmgr
+ccflags-y += -Idrivers/dsp/bridge/rmgr
+ccflags-y += -Idrivers/dsp/bridge/hw
+ccflags-y += -Iarch/arm
@@ -0,0 +1,170 @@
+/*
+ * dsprecovery: under some conditions, the dsp bridge implementation in Milestone's
+ * kernel may end up in an endless loop ("Mailbox full" msgs repeated forever in dmesg).
+ * In such case, this simple module will set hw.dspbridge.needs_recovery property that
+ * can be used to trigger dsp bridge reinitialization by init.
+ *
+ * hooking taken from "n - for testing kernel function hooking" by Nothize.
+ *
+ * Copyright (C) 2012 Nadlabak
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include "hook.h"
+#include "symsearch.h"
+
+#include <dspbridge/dbdefs.h>
+#include <dspbridge/errbase.h>
+
+#include <dspbridge/cfg.h>
+#include <dspbridge/drv.h>
+#include <dspbridge/dev.h>
+
+#include <dspbridge/dbg.h>
+
+#include "_tiomap.h"
+#include "_tiomap_pwr.h"
+
+#define MAILBOX_FIFOSTATUS(m) (0x80 + 4 * (m))
+
+SYMSEARCH_DECLARE_FUNCTION_STATIC(DSP_STATUS, ss_tiomap3430_bump_dsp_opp_level);
+SYMSEARCH_DECLARE_FUNCTION_STATIC(DSP_STATUS, ss_DSP_PeripheralClocks_Enable, struct WMD_DEV_CONTEXT *, IN void *);
+SYMSEARCH_DECLARE_FUNCTION_STATIC(HW_STATUS, ss_HW_MBOX_restoreSettings, void __iomem *);
+SYMSEARCH_DECLARE_FUNCTION_STATIC(HW_STATUS, ss_HW_MBOX_MsgWrite, const void __iomem *, const HW_MBOX_Id_t, const u32);
+
+static inline unsigned int fifo_full(void __iomem *mbox_base, int mbox_id)
+{
+ return __raw_readl(mbox_base + MAILBOX_FIFOSTATUS(mbox_id)) & 0x1;
+}
+
+static int dsp_recovery( void )
+{
+ char *argv[] = { "/system/bin/setprop", "hw.dspbridge.needs_recovery", "1", NULL };
+ static char *envp[] = {
+ "HOME=/",
+ "TERM=linux",
+ "PATH=/sbin:/system/bin:/system/xbin", NULL };
+
+ return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_EXEC );
+}
+
+DSP_STATUS CHNLSM_InterruptDSP2(struct WMD_DEV_CONTEXT *pDevContext,
+ u16 wMbVal)
+{
+ DSP_STATUS status = DSP_SOK;
+ unsigned long timeout;
+ u32 temp;
+ u32 cnt = 0;
+ bool time_expired = true;
+
+
+ if (DSP_FAILED(status))
+ return DSP_EFAIL;
+#ifdef CONFIG_BRIDGE_DVFS
+ if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION ||
+ pDevContext->dwBrdState == BRD_HIBERNATION) {
+ if (!in_atomic())
+ ss_tiomap3430_bump_dsp_opp_level();
+ }
+#endif
+
+ if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION ||
+ pDevContext->dwBrdState == BRD_HIBERNATION) {
+ /* Restart the peripheral clocks */
+ ss_DSP_PeripheralClocks_Enable(pDevContext, NULL);
+
+ /* Restore mailbox settings */
+ /* Enabling Dpll in lock mode*/
+ temp = (u32) *((REG_UWORD32 *)
+ ((u32) (pDevContext->cmbase) + 0x34));
+ temp = (temp & 0xFFFFFFFE) | 0x1;
+ *((REG_UWORD32 *) ((u32) (pDevContext->cmbase) + 0x34)) =
+ (u32) temp;
+ temp = (u32) *((REG_UWORD32 *)
+ ((u32) (pDevContext->cmbase) + 0x4));
+ temp = (temp & 0xFFFFFC8) | 0x37;
+
+ *((REG_UWORD32 *) ((u32) (pDevContext->cmbase) + 0x4)) =
+ (u32) temp;
+ ss_HW_MBOX_restoreSettings(pDevContext->dwMailBoxBase);
+
+ /* Access MMU SYS CONFIG register to generate a short wakeup */
+ temp = (u32) *((REG_UWORD32 *) ((u32)
+ (pDevContext->dwDSPMmuBase) + 0x10));
+
+ pDevContext->dwBrdState = BRD_RUNNING;
+ } else if (pDevContext->dwBrdState == BRD_RETENTION)
+ /* Restart the peripheral clocks */
+ ss_DSP_PeripheralClocks_Enable(pDevContext, NULL);
+
+ /* Check the mailbox every 1 msec 10 times before giving up */
+ for (cnt = 0; (cnt < 10) && (time_expired); cnt++) {
+ timeout = jiffies + msecs_to_jiffies(1);
+ time_expired = false;
+ while (fifo_full((void __iomem *)pDevContext->dwMailBoxBase, 0)) {
+ if (time_after(jiffies, timeout)) {
+ time_expired = true;
+ printk(KERN_WARNING "Mailbox full trying again count %d \n", cnt);
+ break;
+ }
+ }
+ }
+ if ((cnt == 10) && (time_expired)) {
+ printk(KERN_ERR "dspbridge: Mailbox was not empty after %d trials , no message written !!!\n", cnt);
+ dsp_recovery();
+ return WMD_E_TIMEOUT;
+ }
+ DBG_Trace(DBG_LEVEL3, "writing %x to Mailbox\n",
+ wMbVal);
+
+ ss_HW_MBOX_MsgWrite(pDevContext->dwMailBoxBase, MBOX_ARM2DSP,
+ wMbVal);
+ if (0) HOOK_INVOKE(CHNLSM_InterruptDSP2, pDevContext, wMbVal);
+ return DSP_SOK;
+}
+
+struct hook_info g_hi[] = {
+ HOOK_INIT(CHNLSM_InterruptDSP2),
+ HOOK_INIT_END
+};
+
+static int __init dsprecovery_init( void )
+{
+ printk(KERN_INFO "DSP Bridge recovery helper: init\n");
+ SYMSEARCH_BIND_FUNCTION_TO(dsprecovery, tiomap3430_bump_dsp_opp_level, ss_tiomap3430_bump_dsp_opp_level);
+ SYMSEARCH_BIND_FUNCTION_TO(dsprecovery, DSP_PeripheralClocks_Enable, ss_DSP_PeripheralClocks_Enable);
+ SYMSEARCH_BIND_FUNCTION_TO(dsprecovery, HW_MBOX_restoreSettings, ss_HW_MBOX_restoreSettings);
+ SYMSEARCH_BIND_FUNCTION_TO(dsprecovery, HW_MBOX_MsgWrite, ss_HW_MBOX_MsgWrite);
+ hook_init();
+ return 0;
+}
+
+static void __exit dsprecovery_exit( void )
+{
+ hook_exit();
+}
+
+module_init( dsprecovery_init );
+module_exit( dsprecovery_exit );
+
+MODULE_ALIAS("DSP Bridge recovery helper");
+MODULE_DESCRIPTION("Milestone's DSP Bridge failure signalization via android property");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nadlabak");
+
View
@@ -0,0 +1,108 @@
+/*
+ * hook - Hook utilities.
+ * use of Skrilax's symsearch added by Nadlabak
+ *
+ * Copyright (C) 2010 Nothize
+ *
+ * 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 "hook.h"
+#include "symsearch.h"
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/smp_lock.h>
+
+//#define DEBUG_HOOK
+#ifdef DEBUG_HOOK
+#define P(format, ...) printk(KERN_INFO "hook: " format, ## __VA_ARGS__)
+#else
+#define P(format, ...)
+#endif
+
+#define INFO(format, ...) (printk(KERN_INFO "hook:%s:%d " format, __FUNCTION__, __LINE__, ## __VA_ARGS__))
+
+SYMSEARCH_DECLARE_FUNCTION_STATIC(unsigned long, pkallsyms_lookup_name, const char *);
+SYMSEARCH_DECLARE_FUNCTION_STATIC(const char *, pkallsyms_lookup, unsigned long, unsigned long *, unsigned long *, char **, char *);
+
+/* Only ARM is supported and the target will crash if there involves
+ PC related addressing in the first instruction. Because that
+ instruction will be moved to hook_info for execution.
+*/
+int hook(struct hook_info *hi) {
+ char targetName[KSYM_NAME_LEN];
+ char *ptargetName;
+ if ( !hi->target ) {
+ if ( hi->targetName ) {
+ hi->target = (unsigned int*)pkallsyms_lookup_name(hi->targetName);
+ }
+ if ( !hi->target ) {
+ P("Target address is not defined and targetName(%s) cannot be found.\n", hi->targetName ?
+ hi->targetName : "");
+ return -1;
+ }
+ ptargetName = hi->targetName;
+ } else {
+ pkallsyms_lookup((unsigned int)hi->target, NULL, NULL, NULL, targetName);
+ ptargetName = targetName;
+ }
+
+ // Save the first 2 instructions from target.
+ P("target = %p(%s), newf = %x\n", hi->target, ptargetName, hi->newfunc);
+ P("*target = %x\n", hi->target[0]);
+ hi->asm0 = hi->target[0];
+
+ // Use 1 instruction static replacement.
+ hi->target[0] = 0xea000000 + (0xffffff & (hi->newfunc - ((unsigned int)hi->target + 8)) / 4);
+ P("*target = %x\n", hi->target[0]);
+
+ // Setup jmp table to first non-overwritten offset.
+ hi->jmp = 0xe51ff004;
+ hi->target_cont = hi->target+1;
+ P("&invoke = %p, target_cont = %p\n", &hi->asm0, hi->target_cont);
+
+ INFO("hooked %s\n", ptargetName);
+ return 0;
+}
+
+int unhook(struct hook_info *hi) {
+ if ( hi->target ) {
+ // Restore the first 2 instructions to target.
+ hi->target[0] = hi->asm0;
+ INFO("unhooked %p\n", hi->target);
+ }
+ return 0;
+}
+
+void hook_init(void) {
+ int i;
+ SYMSEARCH_BIND_FUNCTION_TO(dsifix, kallsyms_lookup_name, pkallsyms_lookup_name);
+ SYMSEARCH_BIND_FUNCTION_TO(dsifix, kallsyms_lookup, pkallsyms_lookup);
+ lock_kernel();
+ for (i = 0; g_hi[i].newfunc; ++i) {
+ hook(&g_hi[i]);
+ }
+ unlock_kernel();
+}
+
+void hook_exit(void) {
+ int i;
+ lock_kernel();
+ for (i = 0; g_hi[i].newfunc; ++i) {
+ unhook(&g_hi[i]);
+ }
+ unlock_kernel();
+}
View
@@ -0,0 +1,49 @@
+/*
+ * n
+ *
+ * Copyright (C) 2010 Nothize
+ *
+ * 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
+ *
+ */
+
+#ifndef _HOOK_H_
+#define _HOOK_H_
+
+struct hook_info {
+ unsigned int asm0;
+ unsigned int jmp;
+ unsigned int *target_cont;
+ unsigned int *target;
+ char *targetName;
+ unsigned int newfunc;
+};
+
+int hook(struct hook_info *);
+
+int unhook(struct hook_info *);
+
+void hook_init(void);
+void hook_exit(void);
+
+extern struct hook_info g_hi[];
+
+#define HOOK_INVOKE(_f, ...) ((typeof(&_f))&g_hi[__COUNTER__].asm0)(__VA_ARGS__)
+
+#define HOOK_INIT(f) { .targetName = #f, .newfunc = (unsigned int)f }
+
+#define HOOK_INIT_END { .newfunc = 0 }
+
+#endif
Oops, something went wrong.

0 comments on commit 4b1128b

Please sign in to comment.