General Notes - By: Coutts[]
These are my notes I have created while looking into all of the tasks created from DryOS on the Rebel T1i / 500d. This page will contain my findings related to task sequencers and calls to CreateSequencer.
A sequencer appears to create a message queue and task using a name passed in arg2. This may be used to create multiple related tasks at the same time, and manage their message queues and events using a single handler function.
CreateSequencer[]
Located at 0xFF029C38 in v1.1.0 firmware of the 500d.
Here is my breakdown of the function, with some help from the arm-console.
- arg0 - passed as arg1 to task_create
- arg1 - passed as arg2 to task_create
- arg2 - passed as arg0 to task_create
- arg3 - stored at off_0x20 of AllocateMemory struct.
- arg4 - in first debug message, this is the Num value. also stored at off_0x18
DryosDebugMsg(0x0, 0x3, "[SEQ] CreateSequencer (%s, Num = %d)", arg2, [SP beforebranch]); AllocateMemory(0x24); if (ret_AllocateMemory == 0) RETURN 0x5; KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe(arg2, 0xA); ret_AllocateMemory->off_0xC = ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe if NE((ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe_FF029418 & 0x1)): DryosDebugMsg(0x0, 0x16, '[SEQ ERROR] CreateMessageQueue (%#x)', ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe) FreeMemory(ret_AllocateMemory) return 5 *(ret_AllocateMemory->off_0x0) = 'Sequencer' *(ret_AllocateMemory->off_0x4) = ret_AllocateMemory->off_0x10 *(ret_AllocateMemory->off_0x8) = "[SEQ] CreateSequencer (%s, Num = %d)" *(ret_AllocateMemory->off_0xC) = ret_KernelDry_KerQueue.c__create_message_queue_R0.name__R1.size__maybe *(ret_AllocateMemory->off_0x10) = 1 *(ret_AllocateMemory->off_0x14) = arg3 *(ret_AllocateMemory->off_0x18) = arg2 *(ret_AllocateMemory->off_0x1C) = 0 *(ret_AllocateMemory->off_0x20) = *(arg3) task_create(arg2, arg0, arg1, 0xFF0292CC, ret_AllocateMemory) if EQ((ret_task_create & 0x1)): *(ret_AllocateMemory->off_0x8) = ret_task_create return ret_AllocateMemory DryosDebugMsg(0x0, 0x16, '[SEQ ERROR] CreateTaskClass (%#x)', ret_task_create) KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue(ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe) FreeMemory(ret_AllocateMemory) return 5
How It's Called[]
In the 500d v1.1.0 firmware, it is called in 4 spots:
- 0xFF013330 - CreateSequencer(0x19, 0x2800, "Startup", 0x19C0, 0x6);
- 0xFF01FC08 - CreateSequencer(0x15, 0x1000, "Terminate", 0x1BE8, 0x3);
- 0xFF01FD80 - CreateSequencer(0x15, 0x1000, "Terminate", 0x1BC4, 0x3);
- 0xFF0200AC - CreateSequencer(0x11, 0x1000, "Terminate", 0x1C0C, 0x2);
task_create[]
When CreateSequencer is called, one of the things it does is it creates a new task. It's called like this:
task_create(arg2, arg0, arg1, 0xFF0292CC, ret_AllocateMemory)
So it looks like anything that calls CreateSequencer will have the same task? Odd. Let's look at that task. This is the output straight from the console:
In [40]: dec SEQ_seqEventDispatch found 5 code paths code has loops => expect bad results emulating code path 1 of 5 emulating code path 2 of 5 emulating code path 3 of 5 emulating code path 4 of 5 emulating code path 5 of 5 merging * * * * rebuilding *(-4 + sp0) = lr0 *(-8 + sp0) = unk_R4 *(-12 + sp0) = arg3 *(-16 + sp0) = arg2 if -arg0->off_0x18 < -arg0->off_0x1C /*CC*/: msg_queue_receive(arg0->off_0xC, -12 + sp0, 0x0, arg3) => ret_msg_queue_receive_FF0292E4 if ret_msg_queue_receive_FF0292E4 == 0 /*EQ*/: !!! Stack not restored !!! !end if arg0->off_0x10 != 0 /*NE*/: *(-16 + sp0) = arg3 DryosDebugMsg(0x0, 0x5, '[SEQ] seqEventDispatch (%s, %d)', arg0->off_0x4) => ret_DryosDebugMsg_FF029314 j_IRQ_disable() => ret_j_IRQ_disable_FF029318 arg0->off_0x1C = 1 + arg0->off_0x1C arg0->off_0x20 = *(12 + 12*arg0->off_0x1C + arg0->off_0x14) j_IRQ_restore() => ret_j_IRQ_restore_FF029338 FUNC(*(4 + 12*arg3 + arg0->off_0x14))(*(4 + 12*arg3 + arg0->off_0x14), 0x0, 0x0, *(4 + 12*arg3 + arg0->off_0x14)) => ret_FUNC(*(4 + 12*arg3 + arg0->off_0x14))_FF02935C if arg0->off_0x10 == 0 /*EQ*/: DryosDebugMsg(0x0, 0x5, '[SEQ] seqEventDispatch (%s) : Canceled', arg0->off_0x4) => ret_DryosDebugMsg_FF029374 DryosDebugMsg(0x0, 0x5, '[SEQ] seqEventDispatch (%s) : End', arg0->off_0x4) => ret_DryosDebugMsg_FF0293B0 KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue(arg0->off_0xC) => ret_KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue_FF0293B8 FreeMemory(arg0) => ret_FreeMemory_FF0293C0 return ret_FreeMemory_FF0293C0 !end if ret_msg_queue_receive_FF0292E4 != 0 /*NE*/: DryosDebugMsg(0x0, 0x16, '[SEQ ERROR] ReceiveMessageQueue (%#x)', ret_msg_queue_receive_FF0292E4) => ret_DryosDebugMsg_FF02938C if CS(-arg0->off_0x18 + arg0->off_0x1C): DryosDebugMsg(0x0, 0x5, '[SEQ] seqEventDispatch (%s) : End', arg0->off_0x4) => ret_DryosDebugMsg_FF0293B0 KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue(arg0->off_0xC) => ret_KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue_FF0293B8 FreeMemory(arg0) => ret_FreeMemory_FF0293C0 return ret_FreeMemory_FF0293C0 !!! Stack not restored !!! !end if CS(-arg0->off_0x18 + arg0->off_0x1C): DryosDebugMsg(0x0, 0x5, '[SEQ] seqEventDispatch (%s) : End', arg0->off_0x4) => ret_DryosDebugMsg_FF0293B0 KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue(arg0->off_0xC) => ret_KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue_FF0293B8 FreeMemory(arg0) => ret_FreeMemory_FF0293C0 return ret_FreeMemory_FF0293C0 !end
Other Forms of Sequencers[]
In LV_Initialize - 0xFF033F84, there is a call to a function similar to CreateSequencer, for what appears to be a live view task (possibly). There is some kind of event system here. Here's the decompiler output for a backwards decompile to the call to this function, I have bolded the function we are looking into (this doesn't look to be very accurate, never trust the console completely***):
In [44]: bd 0xFF034030 *(-4 + sp0) = lr0 *(-8 + sp0) = unk_R11 *(-12 + sp0) = unk_R10 *(-16 + sp0) = unk_R9 *(-20 + sp0) = unk_R8 *(-24 + sp0) = unk_R7 *(-28 + sp0) = unk_R6 *(-32 + sp0) = unk_R5 *(-36 + sp0) = unk_R4 *(-40 + sp0) = arg3 *(-44 + sp0) = arg2 *(-48 + sp0) = arg1 *(-52 + sp0) = arg0 DryosDebugMsg(0x9b, 0x3, 'LV_Initialize %s', 'Mar 11 2009') => ret_DryosDebugMsg_FF033FA8 if *0x1D78 == 0 /*EQ*/: AllocateMemory(9956) => ret_AllocateMemory_FF033FC0 if ret_AllocateMemory_FF033FC0 != 0 /*NE*/: ret_AllocateMemory_FF033FC0->off_0x8C = 1 *(9776 + ret_AllocateMemory_FF033FC0) = 1185 *(9792 + ret_AllocateMemory_FF033FC0) = 0 *(9948 + ret_AllocateMemory_FF033FC0) = 1 *(9796 + ret_AllocateMemory_FF033FC0) = HALFWORD(0) *(9784 + ret_AllocateMemory_FF033FC0) = -1 *(ret_AllocateMemory_FF033FC0) = 'LiveViewMgr' *(-136 + sp0) = @sub_FF033F1C sub_FF1A6790('LiveViewMgr', arg1, 0xc00, 0xc8) => ret_sub_FF1A6790_FF034030 !!! Stack not restored !!! !end if TRUE(ret_AllocateMemory_FF033FC0): *0x1D78 = ret_AllocateMemory_FF033FC0
Now this function decompiled:
In [45]: dec 0xFF1A6790 found 4 code paths emulating code path 1 of 4 emulating code path 2 of 4 emulating code path 3 of 4 emulating code path 4 of 4 merging * * * rebuilding *(-4 + sp0) = lr0 *(-8 + sp0) = unk_R9 *(-12 + sp0) = unk_R8 *(-16 + sp0) = unk_R7 *(-20 + sp0) = unk_R6 *(-24 + sp0) = unk_R5 *(-28 + sp0) = unk_R4 *(-32 + sp0) = arg3 AllocateMemory(24) => ret_AllocateMemory_FF1A67AC if ret_AllocateMemory_FF1A67AC == 0 /*EQ*/: return 5 *(ret_AllocateMemory_FF1A67AC) = 'TaskClass' ret_AllocateMemory_FF1A67AC->off_0x4 = arg0 ret_AllocateMemory_FF1A67AC->off_0x8 = 1 ret_AllocateMemory_FF1A67AC->off_0x14 = arg4 KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe(arg0, arg3) => ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe_FF1A67E0 ret_AllocateMemory_FF1A67AC->off_0x10 = ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe_FF1A67E0 if NE((ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe_FF1A67E0 & 0x1)): FreeMemory(ret_AllocateMemory_FF1A67AC) => ret_FreeMemory_FF1A67F4 return 5 *(-32 + sp0) = ret_AllocateMemory_FF1A67AC task_create(arg0, arg1, arg2, @sub_FF1A671C, ret_AllocateMemory_FF1A67AC) ret_AllocateMemory_FF1A67AC->off_0xC = ret_task_create_FF1A6810 if EQ((ret_task_create_FF1A6810 & 0x1)): return ret_AllocateMemory_FF1A67AC KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue(ret_KernelDry_KerQueue.c__create_message_queue___R0.name__R1.size__maybe_FF1A67E0) => ret_KernelDry_KerQueue.c_or_AJ_guess_USB_CreateMessageQueue_FF1A6828 FreeMemory(ret_AllocateMemory_FF1A67AC) => ret_FreeMemory_FF1A67F4 return 5 !end
In this function, task_create is called as follows (i traced the value of arg1 back to where LV_Initialize is called from):
task_create("LiveViewMgr", 0x11, 0xCC0, 0xFF1A671C, ret_AllocateMemory);
So, every function that calls this function (0xFF1A6790) will all use the same task function at 0xFF1A671C. There are quite a few callers. Here are their call locations, and the name of their task created:
- 0xFF0261C4 -- EventMgr
- 0xFF027110 -- FileCache
- 0xFF029F80 -- RscMgr
- 0xFF034030 -- LiveViewMgr
- 0xFF035118 -- LVC_AE
- 0xFF0355B0 -- LVC_AF
- 0xFF035F1C -- LVC_DEV
- 0xFF036A9C -- LVC_MD
- 0xFF0387B0 -- ReDevelop
- 0xFF03BFA8 -- Ceres
- 0xFF040184 -- FileMgr
- 0xFF04CC38 -- MovWriter
- 0xFF04E598 -- MovieRecorder
- 0xFF0594D8 -- PropMgr
- 0xFF0668E4 -- DbgMgr
- 0xFF0C4068 -- DpMgr
- 0xFF0D9A1C -- DpImgEditMgr
- 0xFF0E8460 -- LiveViewAngelMgr
- 0xFF11F12C -- SdioDrv
- 0xFF13C998 -- Mrk
- 0xFF19F4C4 -- IPCTask
- 0xFF19F6A8 -- IPCTask
- 0xFF21934C -- SdioTsk
- 0xFF22ED88 -- Decrypto
Now to take a look at the task function for all of these (at 0xFF1A671C):
The Task Function - located at 0xFF1A671C[]
A C interpretation from Trammell Hudson (I have modified it slightly):
struct arg { uint32_t off_0x00 uint32_t off_0x04; uint32_t enabled; // off_0x08 uint32_t off_0x0c; uint32_t off_0x10; void (*handler)(uint32_t, uint32_t, uint32_t, uint32_t); // off_0x14 }; void sub_FF1A671C( struct arg * arg ) { uint32_t x, y, z, w; while (arg->enabled) { int rc = sub_FF1A6690(arg, &x, &y, &z, &w); if (rc != 0) continue; if (!arg->enabled) break; arg->handler(x, y, z, w); } return 0; }
This seems very accurate to me. The question though is where do x, y, z, and w come from?
Here everything broken down for our LiveViewMgr example, starting from LV_Initialize:
- LV_Initialize calls sub_FF1A6790 (function that creates a msg_queue and task), the location of lvEventDispatch (sub_FF033F1C) is passed as arg4 to it.
- after jumping to sub_FF1A6790 (function that creates a msg_queue and task), lvEventDispatch's location is stored at arg->handler.
- msg_queue is created [haven't looked at that part yet]
- return of msg_queue function is stored at arg->enabled
- task is created
- [now in task loop] sub_FF1A6690 (function that checks msg_queue) is called.
- If it returns 0, then the value of arg->enabled is checked.
- If arg->enabled is 0 as well, then the task ends.
- If arg->enabled isn't 0, then arg->handler(x, y, z, w) is called.
- If it doesn't return 0, then the value of arg->enabled is checked.
- If arg->enabled is 0, then the task ends.
- If arg->enabled isn't 0, then start the loop over, calling sub_FF1A6690 again.
- If it returns 0, then the value of arg->enabled is checked.
sub_FF1A6690 (function that checks msg_queue) decompiled:
In [52]: dec FF1A6690 found 3 code paths emulating code path 1 of 3 emulating code path 2 of 3 emulating code path 3 of 3 merging * * rebuilding *(-4 + sp0) = lr0 *(-8 + sp0) = unk_R9 *(-12 + sp0) = unk_R8 *(-16 + sp0) = unk_R7 *(-20 + sp0) = unk_R6 *(-24 + sp0) = unk_R5 *(-28 + sp0) = unk_R4 *(-32 + sp0) = arg3 if NE(12111470 + *(arg0)): return 7 *(-32 + sp0) = 0 msg_queue_receive(arg0->off_0x10, -32 + sp0, arg5, 'TaskClass') => ret_msg_queue_receive_FF1A66D0 if ret_msg_queue_receive_FF1A66D0 == 0 /*EQ*/: *(arg1) = *0x0 *(arg2) = *0x4 *(arg3) = *0x8 *(arg4) = *0xC FreeMemory(0x0) => ret_FreeMemory_FF1A6710 return ret_msg_queue_receive_FF1A66D0 !end ==arg->handler()==
From our example with LiveViewMgr, arg->handler's value is lvEventDispatch (sub_FF033F1C). Here it is decompiled:
In [53]: dec FF033F1C found 2 code paths emulating code path 1 of 2 emulating code path 2 of 2 merging * rebuilding *(-4 + sp0) = lr0 *(-8 + sp0) = unk_R6 *(-12 + sp0) = unk_R5 *(-16 + sp0) = unk_R4 *(-20 + sp0) = arg3 *(-24 + sp0) = arg2 *(-24 + sp0) = arg3 sub_FF1A61D4((*0x1D78)->off_0xC, arg0, arg1, arg2) => ret_sub_FF1A61D4_FF033F48 if ret_sub_FF1A61D4_FF033F48 != 0 /*NE*/: (*0x1D78)->off_0xC = ret_sub_FF1A61D4_FF033F48 return ret_sub_FF1A61D4_FF033F48 sub_FF1A6390((*0x1D78)->off_0xC) => ret_sub_FF1A6390_FF033F64 *(-24 + sp0) = arg1 *(-20 + sp0) = arg2 DryosDebugMsg(0x98, 0x6, 'lvEventDispatch : Current = %d, dwEventID = %d, dwParam = %#x', ret_sub_FF1A6390_FF033F64) => ret_DryosDebugMsg_FF033F7C return ret_DryosDebugMsg_FF033F7C !end
sub_FF1A61D4 and sub_FF1A6390 are related to state objects:
In [54]: dec FF1A61D4 found 2 code paths emulating code path 1 of 2 emulating code path 2 of 2 merging * rebuilding *(-4 + sp0) = lr0 *(-8 + sp0) = unk_R5 *(-12 + sp0) = unk_R4 *(-16 + sp0) = arg3 REGWRITE(LR, arg3) if (): return 7 *(-16 + sp0) = arg4 FUNC(arg0->off_0xC)(arg0, arg1, arg2, arg3) => ret_FUNC(arg0->off_0xC)_FF1A6204 return ret_MEM(12 + arg0)_FF1A6204 !end
In [64]: dec sub_FF1A6390 found 2 code paths emulating code path 1 of 2 emulating code path 2 of 2 merging * rebuilding if *(arg0) == 'StateObject' /*EQ*/: return arg0->off_0x1C return -1 !end
I'm done digging for now. As I finsih this up, I have been working on this for 14 hours straight now, I can't think about it much more.