Makogan
Makogan

Reputation: 9540

What am I doing wrong when compiling C code to bare metal (raspberry pi)?

I have spent multiple days trying to figure this out and I just can't. I have some C code. I have made the assembly code for this C program, copy pasted the assembly to someone else's project (that only contains a single assembly file) and assembled that. In these case things work. But if I try to compile from C directly to generate the binaries, it doesn't work. Even though everything else should be identical. This is my C code:

 #include <stdint.h>

#define REGISTERS_BASE 0x3F000000
#define MAIL_BASE 0xB880  // Base address for the mailbox registers
// This bit is set in the status register if there is no space to write into the mailbox
#define MAIL_FULL 0x80000000
// This bit is set in the status register if there is nothing to read from the mailbox
#define MAIL_EMPTY 0x40000000

struct Message
{
  uint32_t messageSize;
  uint32_t requestCode;
  uint32_t tagID;
  uint32_t bufferSize;
  uint32_t requestSize;
  uint32_t pinNum;
  uint32_t on_off_switch;
  uint32_t end;
};

struct Message m =
{
  .messageSize = sizeof(struct Message),
  .requestCode =0,
  .tagID = 0x00038041,
  .bufferSize = 8,
  .requestSize =0,
  .pinNum = 130,
  .on_off_switch = 1,
  .end = 0,
};

/** Main function - we'll never return from here */
int _start(void)
{
  uint32_t mailbox = MAIL_BASE + REGISTERS_BASE + 0x18;
  volatile uint32_t status;

  do
  {
    status = *(volatile uint32_t *)(mailbox);
  }
  while((status & 0x80000000));

  *(volatile uint32_t *)(MAIL_BASE + REGISTERS_BASE + 0x20) = ((uint32_t)(&m) & 0xfffffff0) | (uint32_t)(8);

  while(1);
}

This is a linker file I copied from the successful method:

/*
 * Very simple linker script, combing the text and data sections
 * and putting them starting at address 0x800.
 */
SECTIONS {
  /* Put the code at 0x80000, leaving room for ARM and
   * the stack. It also conforms to the standard expecations.
   */
  .init 0x8000 : {
    *(.init)
  }

  .text : {
    *(.text)
  }

  /* Put the data after the code */
  .data : {
    *(.data)
  }
}

And these is how I am compiling and linking everything:

arm-none-eabi-gcc -O0 -march=armv8-a PiTest.c -nostartfiles -o kernel.o
arm-none-eabi-ld kernel.o -o kernel.elf -T kernel.ld
arm-none-eabi-objcopy kernel.elf -O binary kernel.img

My target architecture is armv8 since that's what the pi model 3 uses. I have no idea how the generated assembly works, but the C code directly does not. Please help I am on the verge of madness.

EDIT: The expected behaviour is for the pi's light to turn on. which it does with the first method I described. With the second method the light remains off.

EDIT4: Made some changes to files, deleted previous edits with outdated info to reduce post size

  kernel.elf:     file format elf32-littlearm


Disassembly of section .init:

00008000 <_start>:
    8000:   e3a0dd7d    mov sp, #8000   ; 0x1f40
    8004:   eaffffff    b   8008 <kernel_main>

Disassembly of section .text:

00008008 <kernel_main>:
    8008:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
    800c:   e28db000    add fp, sp, #0
    8010:   e24dd00c    sub sp, sp, #12
    8014:   e30b3898    movw    r3, #47256  ; 0xb898
    8018:   e3433f00    movt    r3, #16128  ; 0x3f00
    801c:   e50b3008    str r3, [fp, #-8]
    8020:   e51b3008    ldr r3, [fp, #-8]
    8024:   e5933000    ldr r3, [r3]
    8028:   e50b300c    str r3, [fp, #-12]
    802c:   e51b300c    ldr r3, [fp, #-12]
    8030:   e3530000    cmp r3, #0
    8034:   bafffff9    blt 8020 <kernel_main+0x18>
    8038:   e30b38a0    movw    r3, #47264  ; 0xb8a0
    803c:   e3433f00    movt    r3, #16128  ; 0x3f00
    8040:   e3082050    movw    r2, #32848  ; 0x8050
    8044:   e3402001    movt    r2, #1
    8048:   e3c2200f    bic r2, r2, #15
    804c:   e3822008    orr r2, r2, #8
    8050:   e5832000    str r2, [r3]
    8054:   eafffffe    b   8054 <kernel_main+0x4c>

Disassembly of section .data:

00008058 <__data_start>:
    8058:   00000020    andeq   r0, r0, r0, lsr #32
    805c:   00000000    andeq   r0, r0, r0
    8060:   00038041    andeq   r8, r3, r1, asr #32
    8064:   00000008    andeq   r0, r0, r8
    8068:   00000000    andeq   r0, r0, r0
    806c:   00000082    andeq   r0, r0, r2, lsl #1
    8070:   00000001    andeq   r0, r0, r1
    8074:   00000000    andeq   r0, r0, r0

Disassembly of section .ARM.attributes:

00000000 <_stack-0x80021>:
   0:   00002e41    andeq   r2, r0, r1, asr #28
   4:   61656100    cmnvs   r5, r0, lsl #2
   8:   01006962    tsteq   r0, r2, ror #18
   c:   00000024    andeq   r0, r0, r4, lsr #32
  10:   412d3805            ; <UNDEFINED> instruction: 0x412d3805
  14:   070e0600    streq   r0, [lr, -r0, lsl #12]
  18:   09010841    stmdbeq r1, {r0, r6, fp}
  1c:   14041202    strne   r1, [r4], #-514 ; 0xfffffdfe
  20:   17011501    strne   r1, [r1, -r1, lsl #10]
  24:   1a011803    bne 46038 <__bss_end__+0x3dfc0>
  28:   2a012201    bcs 48834 <__bss_end__+0x407bc>
  2c:   Address 0x000000000000002c is out of bounds.


Disassembly of section .comment:

00000000 <.comment>:
   0:   3a434347    bcc 10d0d24 <_stack+0x1050d03>
   4:   35312820    ldrcc   r2, [r1, #-2080]!   ; 0xfffff7e0
   8:   392e343a    stmdbcc lr!, {r1, r3, r4, r5, sl, ip, sp}
   c:   732b332e            ; <UNDEFINED> instruction: 0x732b332e
  10:   33326e76    teqcc   r2, #1888   ; 0x760
  14:   37373131            ; <UNDEFINED> instruction: 0x37373131
  18:   2029312d    eorcs   r3, r9, sp, lsr #2
  1c:   2e392e34    mrccs   14, 1, r2, cr9, cr4, {1}
  20:   30322033    eorscc  r2, r2, r3, lsr r0
  24:   35303531    ldrcc   r3, [r0, #-1329]!   ; 0xfffffacf
  28:   28203932    stmdacs r0!, {r1, r4, r5, r8, fp, ip, sp}
  2c:   72657270    rsbvc   r7, r5, #112, 4
  30:   61656c65    cmnvs   r5, r5, ror #24
  34:   00296573    eoreq   r6, r9, r3, ror r5

Upvotes: 1

Views: 405

Answers (3)

Makogan
Makogan

Reputation: 9540

First, cudos to old timer for his patience helping me. The mistakes were:

Wrong entry point for the program, fixed by creating an assembly file with the label _start to set the stack pointer and using the linker to put the init section at address 0x8000

The compilation line itself was also wrong, it was missing a -c argument

Upvotes: 0

old_timer
old_timer

Reputation: 71516

kernel8.img

12345678
00000800
00080264
00000000
12345678

kernel8-32.img

12345678
00008320
00008224
200001DA
12345678

kernel7.img

12345678
00000700
00008224
200001DA
12345678

kernel.img

12345678
00000000
00008224
200001DA
12345678

when I wrote and posted this code this is what I got so if you name your file kernel.img then 0x8000 is your entry point the answer I gave in your other SO question is a complete raspberry pi starting point. You can simply add your mailbox stuff, although if you are struggling with this I thing the mailbox and video are not where you should start IMO.

if you name the file kernel8.img then the entry point is 0x80000 change the linker script to match.

I have a serial port based bootloader you can use to save on the sd card dance, can get a long way with that then simply use the binary version of what you are creating to write to the flash once your application is working.

EDIT

Okay this is incredibly disgusting and by posting it here maybe that means you cant use it in your classwork...you should really do this right and not use inline assembly for your bootstrap...

so.c

asm(
".globl _start\n"
"_start:\n"
"mov sp,#0x8000\n"
"bl centry\n"
"b .\n"
);


unsigned int centry ( void )
{
    return(5);
}

build

arm-none-eabi-gcc -O2 -c so.c -o so.o
arm-none-eabi-ld -Ttext=0x8000 so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf -O binary kernel.img

examine

Disassembly of section .text:

00008000 <_start>:
    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000000    bl  800c <centry>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <centry>:
    800c:   e3a00005    mov r0, #5
    8010:   e12fff1e    bx  lr

A complete raspberry pi C with bootstrap example that will work on any of the flavors of pi (so far as I know they might have changed the GPU bootloader in the last few months but assume the didnt).

Upvotes: 1

user149341
user149341

Reputation:

There are a couple of things I see wrong here. The most obvious ones are:

  • You aren't leaving anything at address 0, so the CPU is left executing blank memory at startup. You need to put something (like a branch instruction!) at 0x0.

  • On ARM Cortex-A, the stack pointer is not initialized at startup. You have to initialize it yourself in _start -- which means you will need to write that function in assembly.

Upvotes: 0

Related Questions