Why does second stage bootloader not set VTOR and SP? #6
Labels
No labels
bazel
bluetooth
board settings
boot_stage2
bug
build
c++
can't reproduce
clang
clib-interface
cmsis
code cleanliness
code size
compiler version issues
cyw43_driver
debug
discussion
documentation
duplicate
dynamic_clock
elf2uf2
enhancement
errata
filesystem
flash layout
floating point
FreeRTOS
hardware_adc
hardware_claim
hardware_clocks
hardware_dma
hardware_exception
hardware_flash
hardware_gpio
hardware_i2c
hardware_irq
hardware_pio
hardware_pwm
hardware_rcp
hardware_rtc
hardware_spi
hardware_sync
hardware_uart
hardware_vreg
hardware_watchdog
hardware_xosc
help wanted
host
IAR
installation
invalid
linker scripts
lto
lwip
mbedtls
missing API
needs reporter detail
networking
newlib
newlib-multicore
non-c
non-cmake
non-gcc
openocd
orphan PR
OS integration
pico_async_context
pico_base
pico_binary_info
pico_flash
picolibc
pico_lwip
pico_malloc
pico_multicore
pico_printf
pico_runtime
pico_stdio
pico_stdio_usb
pico_sync
pico_time
pico_util
pioasm
port
question
review
review2
rtos interop
runtime init
static library
svd
test
tinyusb
vscode
wifi integration
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-raspberrypi/pico-sdk#6
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Having to make 0x100 in flash a function as opposed to a vector table complicates the use of other programming languages and SDKs, which assume flash starts with a vector table.
Is there a reason why the second stage bootloader doesn't set VTOR to 0x1000_0100, SP to [0x1000_0100] and then jump to [0x1000_0104]?
I'm not sure that is an assumption they should make (unless flash is at 0x00000000), Still if they don't reference the stack pointer you could replace it with something like
0x47184b00, // ldr r3, [pc, #0]; bx r3
to jump to the reset vector, and actually put a stack pointer in there (though all your bootstrap code need to be in the first 256 bytes of flash somewhere)
Seems like the second stage bootloader already breaks this assumption, so I guess the other issue is the alignment requirement of VTOR, and not wanting to hoist the reset handler over the vector table to fill the alignment hole?
P.S. yay, rust i'm guessing - i wanted to give it a try myself, but i had zero time, and my Rust is not very idiomatic yet!
Purely a size consideration, in case we needed to squeeze more in there at a later time (as you can see it is already quite tight), and want to keep the interface stable. No strong objections to that mechanism but it looks like it would add a few bytes. Then again I guess now would be the time to change it, if ever.
(I'm guessing one of the unstated priors here is that you would rather not ship your own second stages with those SDKs)
So I understand, you're suggesting a patch like this? (edit: and also setting VTOR as well as vectoring through the table)
oh i had entirely misread the original topic - ignore everything i said!
Thought about this a bit longer, and it makes sense to put vector table at the start in flash images, but we also have a NO_FLASH build flag, where binaries are built to be loaded into RAM. The issue is that when loaded over UF2->USB, the bootrom enters these images in the same way that boot2 currently enters flash crt0.
This means we'll have vector table first on flash builds (which by default we then copy out and relocate into RAM anyway), and second on RAM-only builds. I can't see this causing any particular trouble other than making people's eyes bulge, so I'll raise a PR to make these changes.
Thanks!
https://GitHub.com/thejpster/pico-blink-rs contains my terrible hacks.
And to be clear, yes I would prefer not to ship either ASM or blobs with a Rust application. ASM is preferable, but it involves pulling in big dependencies to kick off gcc / as automatically.
Shipping ASM shims is much easier if you take the approach that I've used in the cortex-m crate, it works without any external dependencies: https://github.com/rust-embedded/cortex-m/blob/master/asm/lib.rs
Ok, so the ideal situation from a dependency point of view is to reimplement boot2 in rust, if you are able? In other words, you would be free to change the handover between that second stage and the reset handler of the rust program?
I think it still makes sense to make this change to the boot2 files shipped with the SDK.
I think the change would help with supporting other OSes too.
We'll look at doing boot2 in Rust. It'll be an interesting challenge!
Note the other thing we have in the first 256 bytes is the root of our machine walkable "binary info" tree (used by picotool)
This is a bunch of (semi) self-describing information embedded in the binary; we have a 20 byte header that is only looked for in 10000100 to 10000200 in flash binaries (though we can obviously change that if it is going to be an issue for other languages, so goot to get your opinion)
Our hope is to have other languages include other information that might be useful to users (you can run this against a BIN/UF2/ELF file or against a device to see what is on it!)
The information is designed to be compact, but if nothing else included the end address of the binary is good because it allows you to do
picotool saveto save your program off your RP2040 device into a UF2/BINe.g.
or
Off the top of my head, 256 bytes minus 20 bytes leaves room for 57 interrupt vectors, which is more than enough I think. Configuring an SDK to put a special section after the vector table is usually much easier than trying to put it in front of the vector table.
I'm not that sure moving the vectors to 0x10000100 is that advantageous. If we're going to copy the vectors to RAM anyway, they can be anywhere in flash really. They could even be first thing in .data, so they'll be initialized by the .data copy.
I'm not aware of the build system or language context here. It makes sense to me that existing projects might assume their image starts with a vector table, as they may expect the hardware to be doing the vectoring. It seems a bit cleaner for boot2 (whose contents ought to be language-agnostic, and whose size is fixed) to be the only thing bumped on the front of that.
Yes FYI the original reason for this was that we support the very non standard idea of RAM UF2s, and the bootrom has no idea what the entry point is in the binary, so we go with the lowest address. Also RAM binaries means that you can't just expect to always download code in debugger, then reboot, you should also support "file" and "run".
Anyway I think we support all that with the new layout (which I agree is more sensible) without needing to change the bootrom :-)
Yeah definitely worth considering the RAM binary use case. (RP2040 with no flash, running something on the device without disturbing the flash) use cases, since you do need code at the beginning for that (although clearly you could just trade out the boot stage 2 for a 256 byte indirect thru boot vector at 0x100)
Agreed; more of an FYI than a concern over space
Traditionally the main app will relocate VTOR if necessary, which is why it doesn't make sense to do it in boot stage2. You can have a look at SystemInit function in system_XXX.c files in CMSIS libraries. This concept makes sense, you can have multiple implementations of system_XXXX.c, one which will copy VTOR to RAM and relocate address there, another while will use FLASH for this purpose. I don't think that that it's necessary to do it in boot_stage2.
On the other hand, I think that you could use C directly writing boot_stage2 code. It's more readable for most people and the size of the output is pretty much the same, or very close (it fits 252bytes). I've verified this by rewriting this in C. I'm porting this ASM to C and the results look ok. I think I should get my pico tomorrow and will check if everything works. If it's ok then I will post a like to my repo.
Yeah, the question here is not about installing the VTOR, but whether to allow the real binary to have its vector table at the beginning of the binary (which we currently don't as we jump there). The change would just be to pull initial PC,SP from 0x100-0x108 instead of jumping to 0x100
Oh damn, yeah missed the point :D I've looked at the linker file again, I get what you mean. That looks really weird indeed... What was the initial idea behind placing reset section in front of vector?
Keeping the handover between boot2 and crt0 absolutely minimal in case there was any critical hardware setup we needed to use that space for. We've released now, things seem to be basically working, so it makes sense to spend those bytes to bring our binary layout in line with other codebases' assumptions
Please do share -- right now I'm keen on just getting the binary layout change merged before people go too much further with bare metal language bringup stuff, but it would be interesting to look at rewriting in C in the future (and might let us have a single, more configurable boot2)
I added a comment to the PR, but I don't think we should actually install the VTOR. we should just redirect thru the first 8 bytes, and leave it to the language runtime to decide how they setup vector table. Thoughts?
I think the issue with not installing VTOR is that a lot of existing runtime will just turn on IRQs without modifying VTOR (as VTOR is not modifiable on some systems anyway), under the assumption that VTOR points at the vector table that you came into the image through, which is true on almost every system apart from ours.
If we didn't install VTOR, this would send all such code through the bootrom vector table.
This doesn't affect our runtime anyway because we relocate VTOR before enabling IRQs, so it wouldn't affect our code, and would give other crt0s an experience that's more consistent with other Cortex-M platforms.
Yes, you are right, my brain was out to lunch
The answer to the question in this thread's title is now "it does" :)