Magic Lantern Firmware Wiki
Advertisement

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.


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.

Advertisement