Hi Martin,
On 02/28/2018 04:02 PM, Martin Stein wrote:
Hi Josef, [...] First, I would look at the assembly context at 0x1001bb0 especially what is done with R1/R9 in the subsequent instructions. You might also play around with explicit register assignment:
register unsigned my_var asm("r1") = n;
or inline assembler to get a better understanding what you're actually doing wrong in the perspective of the client.
Maybe the client returns from emulation with a bad IP which you could check by adding an assembler instruction with an observable side effect directly behind the LDR (or using a debugger).
You could even write an inline assembler snippet that writes the whole GPR state after the LDR to RAM when running without emulation. This way, you can compare the GPR states of the emulation case (inside observer) and the non-emulation case.
I experimented a bit and after comparing the actual register contents of R0-R15 inside the child application with the contents of the Thread_state register backup (R0-R15) that is delivered to my fault handler, it seems like Genode (or Fiasco.OC or the glue code) delivers the registers in a strange mapping, where some original regs are mapped to different regs in the backup, and some do not appear at all. The mapping is like this:
Thread_state | Child | Child - Alternative Possibility --------------------------------------------------------- R0 | R9 | R7 R1 | R10 | R4 R2 | R11 | R3 | | R4 | | R5 | | R6 | | R7 | | R8 | R0 | R9 | R1 | R10 | R2 | R11 | R3 | R12 | R12 | R5 R13 | R13 | R14 | R14 | R15 | R15 | | R6 | | R8 |
(Alternative Possibility means I couldn't tell which of the two is actually correct due to equal values. The Thread_state registers that have no mapping contained values that did not match any of the original register contents; some of those values appeared in two distinct Thread_state registers. I assured myself that the child didn't modify the registers before dumping the contents.) While this looks pretty strange, I verified the 'mapping' on a few occasions and after incorporating the mapping into the instruction emulator/the redundant memory writer, it does what it should, at least for a limited test case (which doesn't access one of the unmapped registers).
I should mention that we use a slightly modified Fiasco.OC kernel and kernel interface, since in the unmodified Genode 16.08/Fiasco.OC, calling Cpu_thread_component::state() [1] internally calls Platform_thread::state(), which does not return the contents of all registers. Instead our modified Platform_thread::state() calls our own method all_regs() [2] which does that. The modified Fiasco.OC kernel source files are [3] and [4] with modifications marked with a comment mentioning "rtcr". But no remapping or other strange things are done there.
My guess is that this register magic is not happening in the base-hw version of Genode (since Vinit instruction emulation is not using any remapping and still works), but maybe you still have a clue of what is going on there.
Best regards, Josef
[1] state() repos/base/src/core/cpu_thread_component.cc [2] all_regs(): https://github.com/jmstark/cr-genode/blob/red_mem/repos/base-focnados/src/co... [3] https://github.com/argos-research/foc/blob/checkpointRestore/l4/pkg/l4sys/in... [4] https://github.com/argos-research/foc/blob/checkpointRestore/kernel/fiasco/s...