Does XF1 have dual CPU? - tiredboffin/fffw GitHub Wiki
Example of concurrent code
The kernel loader at address 0x0
boot:00000000 18 f0 9f e5 ldr pc=>FUN_00004000,[DAT_00000020]
comes eventually to
...
boot:00004040 9d 00 00 eb bl exr_get_cpu_n
boot:00004044 9f 01 00 eb bl FUN_000046c8
boot:00004048 13 00 02 f1 cps #19
the function FUN_000046c8 accepts one parameter (that I think is cpu_n, but that does not matter at the moment). It loads parts of code from eeprom and eventually at the very end comes to the following forever loop:
FUN_000046c8(int cpu_n)
{
...
//decompiled code at boot:0000480c
ptr = PTR_boot_sync_cpu_00004858;
ptr[0] = 0;
while (ptr[1] == 0x22b) for (i = 0; i!=100;i++);
return;
}
The function waits for the 32-bit word at boot_sync[1]
to change from 555
(0x22b
) to something else. The variable boot_sync
resides in the .data
segment of the boot section at address 0x21300
(equivalent to non-executable 0x40021300
) and has an initial value of 555
.
boot:40021300 [0] 22Bh, 22Bh
The code above would wait indefinitely unless some other code changes the value. However, this fragment is part of the kernel loader that runs during boot, just before it "jumps" into the kernel after returning from FUN_000046c8
. Therefore, the code that changes the value 555
could only be: 1) an IRQ handler, or 2) concurrent code running on another core.
I believe the latter is the case, and here's my reasoning.
The code at address 0x10000
boot:00010000 18 f0 9f e5 ldr pc=>FUN_00014000,[->FUN_00014000]
and the entry function at 0x14000 look very similar to the code at 0x0 and 0x4000
boot:00014040 9d 00 00 eb bl exr_get_cpu_n_2
boot:00014044 24 01 00 eb bl FUN_000144dc
boot:00014048 13 00 02 f1 cps #19
but this time the function changes boot sync_cpu[1] to 0 and waits for the sync_cpu[0] != 555
FUN_000144dc(int cpu_n)
{
...
//decompiled code at boot:000145ac
boot_sync_cpu[1] = 0;
while (boot_sync_cpu[0] == 0x22b) for (i = 0; i!=100;i++);
}
My interpretation of the code suggests that the functions FUN_000046c8
and FUN_000144dc
are running concurrently and synchronize using the loops above before the boot code jumps to the kernel — in fact, each jumps to its own slightly different copy of the kernel.
How does the code differentiate CPU0 from CPU1?
I believe the function below is used to differentiate between CPU0 and CPU1. It's duplicated with slight variations throughout the code, for example, in the kernel loaders at 0x042bc
and 0x142bc
, and within the kernel itself at 0x00042c88
, among other locations.
/* return 0 if run on the primary CPU0 */
int exr_get_cpu_n(void)
{
uint32_t dw;
/* read DBGDSAR (Debug Self Address Offset Register */
asm("mrc p14, #0, %0, c2, c0, #0 " : "=r" (dw));
return dw != 0x4003;
}
Questions still unanswered
However, there are a couple of points that make me doubt this interpretation:
-
I don't see any code that jumps to the
0x10000
address. As we know, the bootrom starts at0x0
, with no references to0x10000
. This is assuming the bootrom is the same on the XF1 as on the EXRII cameras (XE2, XA2, XM1), though this assumption may be incorrect. It’s possible that CPU1 simply starts at0x10000
after being powered by the bootrom or by kenel loader code starting at0x0
. -
If my assumption is correct that
exr_get_cpu_n()
returns the CPU number, the code is structured to run on any CPU. For e.g., the code at0x14000
can only run on CPU1, yet it still contains a check likeif (exr_get_cpu_n() == 0) {}
which seems unnecessary. -
I searched Google for dual ARM Cortex-R4 designs and found nothing beyond the "lock-step" mode. It's hard to believe that Fujifilm would have used such a unique design.