kernel.img placement in memory (not being placed at 0x0)


6 posts
by pmeyer » Mon Feb 18, 2013 1:29 am
I'm working through the Baking Pi courses and having fun with blinking lights. Very familiar with ARM assembly, not so familiar with ARM11/RPi.

I know ARM resets to 0x0. My kernel.img assumes it is going to be loaded at 0x0. I branch to 0x8000 and start from there. However, all of my non-pc-relative pointers are nonfunctional and don't load the correct data from my image.

Debugging (lots of staring at a blinking LED), I realize that my kernel.img is being loaded at 0x8000, and my main (which should be at 0x8000) is being shifted up to 0x10000. This hoses everything.

What's going on here? How do I get my kernel.img loaded at 0x0? Alternatively, how do I tell the linker that the kernel.img is going to be loaded higher up?
Posts: 3
Joined: Mon Feb 18, 2013 12:30 am
by pmeyer » Mon Feb 18, 2013 1:58 am
Also, if the default kernel.img load is at 32k, does this mean I have to copy my own vector table down to 0x0?
Posts: 3
Joined: Mon Feb 18, 2013 12:30 am
by pmeyer » Mon Feb 18, 2013 2:27 am
Got it! Searching for 32k instead of 0x8000 helped. For anyone else looking:

It appears that the firmware folks modified things a bit over the last year. Specifically, because the space below 0x8000 (32k) tends to get used for configuration stuff (atags? configuration info supplied by the system for the OS to use?) the standard image had the vector table at 0x0, 32k of zeroes, then the real code at 0x8000.

To simplify things, they decided change the default to load the kernel.img at 0x8000. . Works great except for all the tutorials and documentation that say kernel.img loads at 0x0 is now wrong.

You can go back to the old style by adding "kernel_old=1" in your config.txt on the SD card. (Make sure to remove that if you swap back your raspian kernel).

I'll do that for now, and figure out how to adjust my ld file later.
Posts: 3
Joined: Mon Feb 18, 2013 12:30 am
by mrvn » Mon Feb 18, 2013 8:28 am
As you discovered the kernel is loaded at 0x8000 because before that the bootloader (on the GPU) places configuration information. You do want those because they tell you about the amount of memory you have (minus whatever the gpu is configured to use) and the command line. Also tells you about an initrd if there is one.

To link your kernel to the right address you simply need to set the address in your linker script like this:
Code: Select all
ENTRY(Start)
SECTIONS
{
    . = 0x8000;
...

As to the exception vector table you have 3 choices:
  • copy the table to 0x0 yourself. Works bet when using ldr. You can also write the table from scratch but I find copying simpler.
  • Set the flag for a high vector, which will put the table at 0xffff000 iirc.
  • Set the exception vector base address register to point to your own table
    Code: Select all
    .balign 32
    .globl exception_table
    exception_table:
            ldr     pc, addr_exception_reset
            ldr     pc, addr_exception_undefined
            ldr     pc, addr_exception_syscall
            ldr     pc, addr_exception_prefetch_abort
            ldr     pc, addr_exception_data_abort
            ldr     pc, addr_exception_reserved
            ldr     pc, addr_exception_irq
            ldr     pc, addr_exception_fiq

    addr_exception_reset:           .word exception_reset
    addr_exception_undefined:       .word exception_undefined
    addr_exception_syscall:         .word exception_syscall
    addr_exception_prefetch_abort:  .word exception_prefetch_abort
    addr_exception_data_abort:      .word exception_data_abort
    addr_exception_reserved:        .word exception_reserved
    addr_exception_irq:             .word exception_irq
    addr_exception_fiq:             .word exception_fiq
    Code: Select all
    extern uin32_t exception_table[];
    void set_exception_table(void) {
        asm volatile("mcr p15, 0, %[addr], c12, c0, 0"
                         : : [addr]"r"(&exception_table));
    }
The last option is the most flexible and is what I use. The table can be anywhere as long as it is 32 byte aligned.

MfG
Mrvn
Posts: 27
Joined: Wed Jan 09, 2013 6:50 pm
by DavidS » Mon Feb 18, 2013 1:34 pm
@mvrn:
Yes using the base pointer is more flexable, though also more limiting. If you end up working on system with an older ARM it may not support this. For this reason I would recomend copying your table down to 0x00 or using kernel_old=1, this gives more flexability as to the target. Of cource not so important for Baking Pi as the device code is built into the kernel and it is desgned only for the RPi, though still a good habit to form before you write a proper kernel.
ARM Assembly Language: For those that want: Simple, Powerful, Easy to learn, and Easy to debug.
User avatar
Posts: 1251
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by mrvn » Mon Feb 18, 2013 5:06 pm
DavidS wrote:@mvrn:
Yes using the base pointer is more flexable, though also more limiting. If you end up working on system with an older ARM it may not support this. For this reason I would recomend copying your table down to 0x00 or using kernel_old=1, this gives more flexability as to the target. Of cource not so important for Baking Pi as the device code is built into the kernel and it is desgned only for the RPi, though still a good habit to form before you write a proper kernel.


There is another choice: Align the exception vector to a page and map it to 0 with the MMU.
Posts: 27
Joined: Wed Jan 09, 2013 6:50 pm