rust-embedded / cortex-m-rt

Minimal startup / runtime for Cortex-M microcontrollers
https://rust-embedded.github.io/cortex-m-rt/
Apache License 2.0
359 stars 85 forks source link

Set SP and VTOR in Reset vector #338

Open adamgreig opened 3 years ago

adamgreig commented 3 years ago

I'm opening this PR for discussion after today's meeting, I'm not really sure I want it to be merged but I thought it might be helpful to see what it could look like.

VTOR

The VTOR register offsets the vector table from the default 0x0000_0000 to a user-provided address, which is required if the user's vector table is not at the start of memory (for example, because a bootloader is present).

We occasionally get people asking for help because none of their interrupts are working and it turns out their bootloader does not set VTOR, so interrupts were not taking the right vector. Typically this is on platforms with manufacturer or vendor bootloaders rather than ones the user has installed, so they may not even be aware the bootloader is running.

It's a hard problem to debug and we could resolve it by having cortex-m-rt always write VTOR at startup, since it knows what the vector table address is. On ARMv6-M the VTOR register is optional, but if not present it must be RAZ/WI, which means it's safe to write to it even though it won't have any effect. On ARMv7-M and ARMv8-M VTOR is always implemented. In practice no Cortex-M0 core has VTOR, but many Cortex-M0+ cores do.

However, this write is useless for the vast majority of applications, using up flash space and boot time without any benefit. I think bootloaders should always be responsible for setting VTOR, and for example this document from Arm agrees, but clearly not all bootloader authors read it.

SP

This seems even rarer, but we have also had reports of debuggers resetting the chip by jumping to the reset vector without setting SP (the stack pointer, which is normally set to the first word of the vector table by the CPU at boot). I don't know what else those debuggers aren't setting, but from reports it sounds like setting SP is enough to get some basic functionality. This seems even more cursed (what else should we set, if we can't rely even on this?) but again is pretty easy to include if we wanted to. It's possible some bootloaders also fail to set SP before jumping to user code, although I don't recall hearing about that and it seems even more egregious. It would probably be helpful to have some more specific references to situations where this is necessary (see related issues).

Making this optional

At the moment the reset vector is pre-built and distributed as a binary, so there's no simple way to control this behaviour with feature gates (except for shipping a lot of different binaries, one for each architecture for each set of features). In the future when global_asm! becomes stable, it will probably be easy to feature-gate this functionality, and then it could be pretty easily enabled by users who need it and not others. Since many of the common use cases are for specific platforms which typically have a commonly-used HAL, those HALs could enable the feature for their users, so many users would not need to know about VTOR.

So, one (good?) option is to wait for global_asm! and then revisit this.

Before or after pre_init?

I've put the new instructions before pre_init so that user code calling pre_init could choose to override them. I don't think it's likely any user code would need to run before these instructions: there's only 5 of them, so it's unlikely e.g. a watchdog would fire, and they don't access RAM, so it's unlikely RAM would need enabling, and they only set up the chip to a state it should normally be in after boot (well, VTOR is usually 0, but what we write should be equivalent).

Related issues

These topics have been requested/discussed before:

And besides those issues, it comes up occasionally on the Matrix chat.

Dirbaio commented 3 years ago

One drawback is this will break firmwares using Nordic's nRF softdevices.

The softdevice lives at the start of flash, the application firmware is after it.. The softdevice handles some irqs, then forwards the rest to the application. Setting VTOR will break it, because all irqs go straight to the application so the softdevice won't be able to handle theirs.

I'd be OK with this being optional, opt-in though!