/* * LRBug.c * * Try to work around what seems to be a hardware bug of CyberstormPPC * 604e/233 cards. * * Written by Emmanuel Lesueur lesueur@club-internet.fr * * This code is in the public domain. You can do whatever you want with it. * * * CLI Usage: * LRBug NOREQ/S,QUIET/S,NOWAIT/S * * NOREQ: don't ask before attempting to restart a task. * QUIET: don't write messages with kprinf() when restarting a task. * NOWAIT: don't install a handler, just restart crashed tasks and quit. * * WB Usage: * Tooltypes NOREQ, QUIET, and NOWAIT, with the same meaning. * * To remove the handler, send a CTRL-C. * * * Object: * Install an exception handler to catch exceptions caused by a * corrupted LR (bit 0 or 2 incorrectly set). When one is caught, * clear the bad bit and restart the task. * * Issue: * Unfortunately, I don't know of any way to fall back to the default * exception handler for exceptions that are not of the "bad LR" type. So * in that case, I just display a small message via kprintf() and let the * user run PPCShowTrap to get the full register/MMU/stack dump. * * * To compile with SAS/C 6.58: * sc resetopts cpu=68040 params=registers nostackcheck optimize * optimizesize link stripdebug nostartup lib lib:debug.lib LRBug.c * * To compile with gcc: * gcc -o LRBug -O2 -m68040 -nostdlib LRBug.c -ldebug * * To compile with vbcc: * vc -o LRBug -O2 -cpu=68040 -nostdlib LRBug.c -lamiga -lppc -ldebug * * Others: * I don't know, but: SHOULD BE LINKED WITH NO STARTUP ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __SASC # define SAVEDS __saveds # define ASM __asm # define REG(x,y) register __##x y #elif defined(__GNUC__) # define SAVEDS # define ASM # define REG(x,y) y __asm__(#x) #elif defined(_DCC) # define SAVEDS __geta4 # define ASM # define REG(x,y) __##x y #elif defined(__STORM__) # define SAVEDS __saveds # define ASM # define REG(x,y) register __##x y #elif defined(__VBCC__) # define SAVEDS # define ASM # define REG(x,y) __reg(#x) y #else # error add #defines for your compiler... #endif typedef BOOL ASM hook_func(REG(a0,struct Hook* h), REG(a2,void* t), REG(a1,struct ExceptionMsg* em)); static void* maintask; static struct Hook* oldhook; static BOOL noreq,quiet; struct Library* PPCLibBase; struct ExecBase *SysBase; struct IntuitionBase *IntuitionBase; struct DosLibrary *DOSBase; struct Library *IconBase; void kprintf(const char*,...); void install_handler(void); void scan_tasks_list(void); /* * Program entry point. No startup code needed. */ int SAVEDS start(void) { struct Process *proc; struct WBStartup *wbmsg=NULL; int ret=RETURN_FAIL; SysBase=*(struct ExecBase**)4; proc=(struct Process*)FindTask(NULL); if(!proc->pr_CLI) { WaitPort(&proc->pr_MsgPort); wbmsg=(struct WBStartup*)GetMsg(&proc->pr_MsgPort); } if((DOSBase=(struct DosLibrary*)OpenLibrary("dos.library",36)) && (IntuitionBase=(struct IntuitionBase*)OpenLibrary("intuition.library",39))) { if(PPCLibBase=OpenLibrary("ppc.library",46)) { maintask=FindTask(NULL); if(proc->pr_CLI) { struct RDArgs *rda; LONG args[3]={0,0,0}; if(rda=ReadArgs("NOREQ/S,QUIET/S,NOWAIT/S",args,NULL)) { ret=RETURN_OK; noreq=args[0]; quiet=args[1]; if(args[2]) scan_tasks_list(); else install_handler(); FreeArgs(rda); } else PrintFault(IoErr(),"Error: "); } else { BPTR olddir=CurrentDir(wbmsg->sm_ArgList[0].wa_Lock); struct DiskObject *dob; if(IconBase=OpenLibrary("icon.library",36)) { if(dob=GetDiskObjectNew(wbmsg->sm_ArgList[0].wa_Name)) { ret=RETURN_OK; noreq=dob->do_ToolTypes && FindToolType((UBYTE **)dob->do_ToolTypes, (UBYTE *)"NOREQ"); quiet=dob->do_ToolTypes && FindToolType((UBYTE **)dob->do_ToolTypes, (UBYTE *)"QUIET"); if(dob->do_ToolTypes && FindToolType((UBYTE **)dob->do_ToolTypes, (UBYTE *)"NOWAIT")) scan_tasks_list(); else install_handler(); FreeDiskObject(dob); } CloseLibrary(IconBase); } CurrentDir(olddir); } CloseLibrary((struct Library*)PPCLibBase); } else Printf("Can't open ppc.library V46\n"); } CloseLibrary((struct Library*)IntuitionBase); CloseLibrary((struct Library*)DOSBase); if(wbmsg) { Forbid(); ReplyMsg(&wbmsg->sm_Message); } return ret; } /* * Exception hook: * - If the exception looks like one caused by the LR bug (i.e. * PC==LR and bit 0 or 2 of LR set), signal our main task. * - otherwise, if there was an old exception hook, call it. * - otherwise, if the exception is a real one (not a kernel message), * kprintf a message to signal it to the user. I'd like to * fall back to the default handling (showing regs, stack, ...), * but I don't think it's possible. One can always use * PPCShowTrap to display those informations, anyway. */ BOOL SAVEDS ASM exception_func(REG(a0,struct Hook* h), REG(a2,void* task), REG(a1,struct ExceptionMsg* em)) { if(em->Type==EXCEPTION_DATAACCESS && em->SRR0==em->LR && ((em->LR&0xf0000000)==0x20000000 || (em->LR&0xf0000000)==0x80000000)) { /* * This hook is called with task==NULL, so we don't know * which task to restart. To work around that problem, * the main task will scan the tasks list and retrieve * it by checking LR's. No need to save the exception message. */ Signal(maintask,SIGBREAKF_CTRL_F); return TRUE; } else if(oldhook) { return ((hook_func*)oldhook->h_Entry)(oldhook,task,em); } else { if(!(em->Type&EXCEPTION_MSG)) kprintf("Exception %lx. Use PPCShowTrap for details.\n",em->Type); return FALSE; } } static struct Hook hook={{NULL,NULL},(ULONG (*)())exception_func}; /* * Restart a task. */ void restart(void* task,ULONG lr) { PPCSetTaskAttrsTags(task, PPCTASKINFOTAG_PC,&lr, PPCTASKINFOTAG_LR,&lr, TAG_END); PPCStartTaskTags(task,PPCTASKSTARTTAG_RUN,TRUE,TAG_END); } /* * Task list scanning hook. Look for tasks that have bad PC/LR, * and restart them. */ void SAVEDS ASM scantasks_func(REG(a2,void* task)) { ULONG pc,lr,state; state=PPCGetTaskAttrsTags(task,PPCTASKINFOTAG_STATE,NULL,TAG_END); PPCGetTaskAttrsTags(task,PPCTASKINFOTAG_PC,&pc,TAG_END); PPCGetTaskAttrsTags(task,PPCTASKINFOTAG_LR,&lr,TAG_END); if(state==TS_WAIT && pc==lr && ((lr&0xf0000000)==0x20000000 || (lr&0xf0000000)==0x80000000)) { static struct EasyStruct es={ sizeof(struct EasyStruct),0,"LRBug Exception Handler", "LR bug caught in task\n\"%s\"", "Continue|Suspend", }; const char* name=(const char*)PPCGetTaskAttrsTags(task, PPCTASKINFOTAG_NAME,NULL, TAG_END); if(!quiet) kprintf("LR bug caught in task \"%s\" (%lx).\n",name,task); lr&=~0xf0000000; if(noreq) restart(task,lr); else { if(EasyRequest(NULL,&es,NULL,name)==1) restart(task,lr); } } } void scan_tasks_list(void) { static struct Hook scantasks_hook={{NULL,NULL},(ULONG (*)())scantasks_func}; PPCGetTaskAttrsTags(NULL, PPCTASKINFOTAG_HOOK,&scantasks_hook, PPCTASKINFOTAG_ALLTASK,TRUE, TAG_END); } /* * Install the exception handler and wait for events. */ void install_handler(void) { oldhook=(struct Hook *)PPCGetAttrsTags(PPCINFOTAG_EXCEPTIONHOOK,NULL,TAG_END); PPCSetAttrsTags(PPCINFOTAG_EXCEPTIONHOOK,&hook,TAG_END); while(!(Wait(SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F)&SIGBREAKF_CTRL_C)) scan_tasks_list(); PPCSetAttrsTags(PPCINFOTAG_EXCEPTIONHOOK,oldhook,TAG_END); }