Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Tracer bullet for bootloader API #61

Closed
wants to merge 3 commits into from

Conversation

utzig
Copy link
Member

@utzig utzig commented Jun 9, 2017

NOT TO BE MERGED YET

This is an initial implementation of an API call table for MCUBoot. Sample changes to make Mynewt compatible, can be seen by looking at the changes on the following branch:

https://github.com/apache/incubator-mynewt-core/compare/master...utzig:bootapi?expand=1

#if defined(__arm__)
#define MCUBOOT_API_INIT \
extern const struct mcuboot_api_itf mcuboot_api_vt; \
asm("ldr r4, =0xdeadbeef\n\t" \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this a (separate) #define, and pass the constant into the inline assembly.

@@ -65,6 +65,38 @@ int boot_swap_type(void);
int boot_set_pending(int permanent);
int boot_set_confirmed(void);

#define MCUBOOT_REQ_FLASH_MAP_SIZE 0
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@d3zd3z Might be a good idea to group the reqs by type, which could be flash_map, flash_read/write, something else in the future... and increment from a different starting value (0x1000, 0x2000, etc). What do you think?

#define MCUBOOT_API_INIT_(m) \
extern const struct mcuboot_api_itf mcuboot_api_vt; \
asm("mov r4, %0\n\t" \
"mov r5, %1" :: "I" (m), "r" (&mcuboot_api_vt) : "r4", "r5");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the compiler is required to preserve r4 and r5 after this asm block, especially since these registers are used for the calling convention. I'd suggest either moving the jump into this sequence, or at least on arm, not botherer with it, and just doing

((void (*)(void))start)(MCUBOOT_BOOT_MAGIC, ptrtoblock);

The ARM register convention passes the first two arguments in r4 and r5. This will generate a bl or blx instruction, but having a return address in lr doesn't really hurt anything (we're already doing this).

Not sure how this would work on x86, since by convention the args are passed on the stack. 'fastcall' will use registers for the first two, so this might work:

((void __fastcall (*)(uint32_t, uint32_t))start)(MCUBOOT_BOOT_MAGIC, (uint32_t)ptrblock);

(with __fastcall possibly needing to be defined, on gcc it is __attribute__((__fastcall__)).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the clobbers there means the registers will not be reused for any "asm" stuff apart from the explicit usage: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers. Not explicitly stated but it would not make much sense IMO if they were not preserved.

According to http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf r0-r3 are used for parameters and r4-r7 for local variables inside the called function.

Btw, the asm block could be updated to just jump/branch directly instead of having to call the function in C. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I will test the parameter passing stuff to see if it works.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is the "clobbers" won't be used to prep and post the asm, but the compiler would be free to use those registers after, including for the call following it. It turns out that we're ok here because the call has no args, so the compiler feels no need to put anything else in the registers.

But, I'd be more comfortable if it was explicit, either putting the jump in the asm itself (along with the arguments), or just using a regular call with arguments.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is the "clobbers" won't be used to prep and post the asm, but the compiler would be free to use those registers after, including for the call following it. It turns out that we're ok here because the call has no args, so the compiler feels no need to put anything else in the registers.

Well, as the ABI document I pointed before says: r0-r3 are used for passing first 4 parameters, extra parameters go in the stack, so we're technically OK by using r4-r5

But, I'd be more comfortable if it was explicit, either putting the jump in the asm itself (along with the arguments), or just using a regular call with arguments.

Yeah, I agree the calling in assembly is safer, I'll try that.

For completeness I tried to check the generated asm and confirm if it is done as defined in the previously mentioned ABI

int fn(int x0, int x1, int x2, int x3, int x4, int x5, int x6, int x7)
{
    int a = x0;
    int b = x1;
    return a == b;
}

int main(void)
{
    fn(0, 1, 2, 3, 4, 5, 6, 7);
    return 0;
}

$ arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb-interwork -mthumb -S test.c

Relevant assembly (function call)

        ...
        movs	r3, #5
	str	r3, [sp, #4]
	movs	r3, #4
	str	r3, [sp]
	movs	r3, #3
	movs	r2, #2
	movs	r1, #1
	movs	r0, #0
	bl	fn
        ...

And it seems that r4-r5 are never touched!


/* magic added to a register to mark bootloader api is valid */
#define MCUBOOT_BOOT_MAGIC 0xb00710ad
#define MCUBOOT_API_INIT(x) MCUBOOT_API_INIT_(MCUBOOT_BOOT_MAGIC)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this patch make use of this macro?

Given that the do_boot functions are target specific, I'm not sure we need to hide all of this in macros, and just put it in the code in the appropriate function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to export a macro to "register" the boot api stuff before jumping into the app. do_boot is zephyr specific, but exporting the macro enables mynewt to "include" it in hal_system_start before jumping to the app.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it confusing to leave things in registers after this though. It may work now, but I don't think it is guaranteed to work. I think both the register setup as well as the call are arch specific and should really be together, not split like this.

e.g., other processors may need something other than a call to jump into the app, maybe some explicit instructions and a special kind of jump.

@utzig utzig mentioned this pull request Jun 28, 2017
@d3zd3z
Copy link
Member

d3zd3z commented Jun 29, 2017

I'm going to put this comment here as well, so it doesn't get lost:

After a discussion with [redacted] today, I think the things we'll need to communicate are:

  • Partitions, at least the partitions the bootloader cares about (the slots and scratch).
  • Version information.
  • Separate version information about the trailer, so know how it should be marked.
  • How mcuboot is configured (swap, upgrade only, run slot)
  • Which slot was run (needed for OTA with dual slot solution).

Things I'm less sure about:

  • Other partitions
  • sector map and write alignment (the drive really should know these)
  • ???

@aditihilbert aditihilbert changed the title Tracer bullet for bootloader API [RFC] Tracer bullet for bootloader API Dec 7, 2017
@utzig utzig force-pushed the bootapi branch 3 times, most recently from 7766cfe to c3617e9 Compare September 25, 2018 17:15
Signed-off-by: Fabio Utzig <utzig@utzig.org>
Signed-off-by: Fabio Utzig <utzig@apache.org>
Magic numbers were separated, one for the boot loader process and
another to mark the api vt struct in memory and defines were added
for both.

Signed-off-by: Fabio Utzig <utzig@utzig.org>
Signed-off-by: Fabio Utzig <utzig@apache.org>
Signed-off-by: Fabio Utzig <utzig@utzig.org>
Signed-off-by: Fabio Utzig <utzig@apache.org>
@d3zd3z
Copy link
Member

d3zd3z commented Dec 2, 2019

A lot has changed since we've originally reviewed this. At least with Zephyr, we have a fairly solid method of making sure the partitions match between MCUboot and the app (device tree), although it is done at build time, and can still be messed up.

TF-M's fork of MCUboot has implemented a communication mechanism that they plan to upstream, but it has a fairly different purpose. It is intended to communicate measurements that will be part of an attestation token. We could rework these patches to fit those requirements, or just bring in fresh code.

@d3zd3z
Copy link
Member

d3zd3z commented Jan 22, 2020

Closing, as this seems to mostly just be out of date.

@d3zd3z d3zd3z closed this Jan 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants