user1225606
user1225606

Reputation: 1153

Why do all ARM bootloaders have assembly code?

Writing in assembler reduces the code size and the execution time. But suppose I have enough memory and enough time. Can I use just C code and boot the device? I mean, from power on, directly run C code. I'm specifically interested in ARM processors.

Upvotes: 4

Views: 3622

Answers (4)

old_timer
old_timer

Reputation: 71606

As mentioned, the cortex-m3 is special and would allow such a thing, okay with one exception, you still need some asm or some other magic to build the vector table to call the C code.

In general the answer is no, some assembly is required. Processors are generic, they dont normally know what address space you have set up for program and data and stack and such, the registers, in particular the stack pointer, are often initialized to some known value like zero. The processor is generally known to have a bootup sequence, either there is an address it starts executing from or an address where it finds an address to start executing from. That table of addresses, one for reset, one for interrupts, etc, is placed there by the programmer of the bootcode, although possible to manage in C, not worth the effort, much easier to write a few lines of asm to build that vector table. (this is the case for the cortex-m)

So at a minimum you need to set a stack pointer and then branch to the entry C code and from there you can probably get away with running C. Now if this an arm and is not a cortex-m then you have multiple stacks to setup (if you want to use interrupts, handle faults, etc) and you must use specific asm instructions in order to perform that setup, so asm is required, real or inline.

If you are in the habit of using .data or expect .bss to be zero, something has to do that, some code has to zero bss and prepare the .data. Typically your system (even your desktop/laptop) will boot from flash, if you have .data that .data will need to be in flash, but will want to be read/write, so you will need to copy that data from flash to its home in ram, well you need ram up and running first (see below) to do that.

Not in the good old days but definitely today with dram you cant just turn on and start using memory, there is a lot of code required to get dram up and running. Yes this code is in C and probably not asm (although for performance reasons, asm may be used to capitalize on specific instructions you cant necessarily make the C compiler generate for you).

So you have a different scenarios in general, not necessarily specific to arm. I will list some but not every nuance

1) you have a system that uses sram or internal ram that does not need to be initialized, you are not using .data and your code does not assume that .bss has been zeroed. The minimum in general would be to initialize the stack pointer and branch to your C entry point (main, or whatever you call it).

2) you have a system that uses sram or internal ram that does not need to be initialized, you are using .data that you expect to be there before your C code starts (pretty typical for C programmers) and .bss to be zero before your C code starts (also typical, but thankfully gcc is starting to complain about code that does this). Since your C code in this case is expecting these things to be prepared before C code is run then the zeroing of .bss and copying of .data from flash to ram happens in asm. This is your most common scenario, go look at many of the crt0.s routines for different processors, this is the common theme, set the stack pointer, zero bss, copy .data branch to main.

3) you have a system that has some internal sram that does not need to be initialized but the main memory that you expect to use is dram. This is a two step, your first bootloader will still need to set the stack pointer, optionally zero .bss and copy .data depending on your preferences then branch to the entry point for the first bootloader. This bootloader will bring up the dram system. This bootloader can now optionally copy .data and zero .bss (this can be in C now) and then branch to the main bootloader entrypoint/function, which expects and uses the larger main memory.

4) you have an x86 processor with microcoding, you need to patch the microcode on boot, I dont have personal knowledge about this but would assume that you have a limited number of instructions you should be using, as you are changing microcoding for some, or perhaps you copy to some ram then flip a bit and it magically switches in the patches and you execute with changes, dont know, but I bet some assembly is required.

Bear in mind that a number of C library calls are in assembly. So C is assembly, esp on an arm. do you avoid all divides, multiplies, modulos, floating point, etc? do you never use any string functions or copy functions. do you use C library calls at all? It is likely that there is asm that you are using and didnt know it. memcpy is very often hand tuned in asm, esp on arm. And if you use structures in your code the compiler can/will thrown in memcpys depending on what and how you use them. divide and modulo definitely asm. multiply sometimes. floating point, often asm, even if there is an fpu. You didnt write this asm yourself, sure, but there is likely asm in there and your bootloader wouldnt exist without that asm (if you use those libraries).

Your title question, implying only assembly for a bootloader, is false, bootloaders are mostly not assembly. Almost always though some assembly is required.

The cortex-m is a unique beast as it was designed so that you didnt have to wrap interrupts with special instructions or asm (or have the compiler do that for you). not typical. the cortex-m has a large to huge vector table though. curious how you are going to build that table in C. I have some ideas how to do it but it is so much easier with asm even though the asm isnt actually asm it is directives to the assembler (.word this_handler, .word that_handler).

The traditional arm's, 32 bit ARM7 like instruction ARM cores, you have to setup the stacks and branch to C code in asm at a minimum, unless you play compiler games the exception handlers also want a little bit of asm, just a few lines.

I have to wonder about the root of this question. Bootloaders are very intimately tied to the processor and perhipherals, programmers writing bootloaders have to be comfortable working with hardware registers, etc. Which implies some level of comfort with the instruction set for that processor. Avoiding all asm in a bootloader is a concern. Even with the cortex-m just because you CAN go straight from the vector table to C code, you still need to validate that the compiler you are using is conforming to the hardwares calling convention, not just some generic arm calling convention, it specifically has to insure that a certain range of registers are preserved and the proper instruction is used on the return, that means disassembly and hand/visual inspection of the compiler generated code, at this level, although just reading, is on a par with writing asm.

Upvotes: 4

Igor Skochinsky
Igor Skochinsky

Reputation: 25318

Actually you can make a C-only firmware for ARM's Cortex-M3 microcontrollers. Because its vector table includes an entry for the stack pointer, its value will be initialized by the processor and you can use compiled code straight away from the reset. You still need to set up the peripherals and initialize the C library environment, but you don't have to do it in assembler. Cortex-M3 also automatically saves volatile registers on interrupt handler entry so they can also be written straight in C.

That said, most compiler vendors still provide startup written in assembler since it offers the most control.

Upvotes: 6

jhonkola
jhonkola

Reputation: 3435

A bootloader is responsible to initialize the device when it is started. At this point there is nothing else than assembler available. The assembler part is often kept as small as possible, and responsible for initializing the system so that parts of the operating system and e.g. minimal C runtime can be loaded into memory and executed, and after this point the other initialization tasks can be done using e.g. C.

Some links that may be helpful:

Wikipedia bootloader entry

U-boot design principles

Hope this is helpful.

Upvotes: 1

nhahtdh
nhahtdh

Reputation: 56829

I also don't know the exact answer. I just propose some of the reasons I can think of (minus memory and efficiency):
- The code is very specific to the device, no need to be compiled and run elsewhere
- Access to special instruction (?)
- The state when the bootloader is run is different from a normal program (?) (stack, heap)

Disclaimer: This answer may contain wrong information. Don't take anything seriously here.

Upvotes: 1

Related Questions