I have a system with an MPC8548 processor. My job is to send a second (external) program to the running firmware and execute it.
I have a cross compiler set up to build the base firmware and the external code.
I was able to send a separate program in runtime (I'm not sure if it's correct, but it seems to be working). Here's how I did it: in the original firmware I made room for the external code by inserting a lot of nop
instructions starting at a specific memory address in the .text
section. After that, in runtime, I copied the received external code (the compiled code using gcc, the .o
file) to that memory address, using memcpy
. Then in the original firmware I made a function pointer, pointing to that memory location and called it.
This is the external, simple code:
int external_main(void)
{
int x = 11;
return x;
}
And this is how I called it in the original firmware:
int (*extFuncPtr)(void);
extFuncPtr = &external_start;
int x = extFuncPtr();
external_start
is specified in the linker scrip's .text
section (it is the starting address of the nop
instructions), and in the code declared as extern unsigned char external_start;
I've printed out that memory region and the external code is there and it runs successfully (if I download some garbage there, it crashes).
My problem is, that I cannot get any return data from the external code. The value of x
will be garbage. I tried passing an int*
as function parameter, but it's still empty. I also tried writing something on a fix address (in the external code), and reading the same address in the firmware, but that didn't work either.
My question is that is it possible to get any data from the external code? I'm probably doing something wrong, or missing something.
Are there other possibilities to solve this problem?
EDIT 1 This is the generated assembly of the external code (calling objdump):
00000000 <external_main>:
0: 94 21 ff e0 stwu r1,-32(r1)
4: 93 e1 00 1c stw r31,28(r1)
8: 7c 3f 0b 78 mr r31,r1
c: 39 20 00 0b li r9,11
10: 91 3f 00 08 stw r9,8(r31)
14: 81 3f 00 08 lwz r9,8(r31)
18: 7d 23 4b 78 mr r3,r9
1c: 39 7f 00 20 addi r11,r31,32
20: 83 eb ff fc lwz r31,-4(r11)
24: 7d 61 5b 78 mr r1,r11
28: 4e 80 00 20 blr
If I put that external_main
function in the firmware, it generates the same instructions. And when I call it, it look like this:
6dbdc: 4b ff 74 b5 bl 65090 <external_main>
6dbe0: 90 7f 00 3c stw r3,60(r31)
And this is the generated instructions of the function pointer part:
6dbb8: 3d 20 00 15 lis r9,21
6dbbc: 39 29 18 ec addi r9,r9,6380
6dbc0: 91 3f 00 34 stw r9,52(r31)
6dbc4: 81 3f 00 34 lwz r9,52(r31)
6dbc8: 7d 29 03 a6 mtctr r9
6dbcc: 4e 80 04 21 bctrl
6dbd0: 90 7f 00 38 stw r3,56(r31)
EDIT 2
So I've tried a lot of things to see what the problem might be, including downloading the whole main firmware as the external program (aka injecting the firmware into the firmware in runtime). I called a function which returns a number and it worked. So I started clearing out the secondary firmware (throwing out everything unnecessary), constantly checking if it is still working or not. And it worked until the binary size became less than 16 Kilobytes.
When the external program's size is less then 16 KB, it just doesn't work (can't get any return values), but when it's 16 or higher, it works. It returns values, also if I pass a pointer to an array as input parameter (allocated in the base firmware) and populate it in the external code, when it returns the array will be there, populated.
Don't know why this happens, but it works. The external code won't be smaller than 16 KB anyway, so, it's a win in my book. If anyone has some idea why it behaves like this, I'll be glad to hear that.