3

I am learning the ARM architecture via bare metal programming on the Raspberry Pi 3. I have a booting "kernel" that prints out some info. I have reached a point that I don't understand what is happening. I have two pieces of boot code below, one works and one doesn't. I don't understand the difference. My gut says there is some sort of alignment thing I've missed in the documentation, but I can't put my finger on it.

I'm PXE booting my Pi 3, and have kernel_old=1 set in the config.txt so that it runs cores from 0x0, if this matters.

This boot.S works and I get into kernel_main and the other cores get into aux_core_main:

// File: boot.S
// Platform: Raspberry Pi 3
// Date: 05/20/2018
//
//
//
// To keep this in the first portion of the binary.
.section ".text.boot"

#define __BOOT_CODE__
#include "identifiers.h"

// GLOBALS



// Make _start global.
.globl _start


_start:
///
// all cores setup code
//
// setup the VM midr/mipdr
    mrs x0, mpidr_el1                       // Read MIPDR
    mrs x1, midr_el1                        // Read MIDR
    msr vpidr_el2, x1                       // set the VM versions of these
    msr vmpidr_el2, x0                      // same


// start core specific code
    and x0, x0, #0x03                       // mask off cpu ID bits
    cbz x0, _core0_start                    // start cpu0
    cmp x0, #0x1                            // compare and branch
    beq _core1_start
    cmp x0, #0x2                            // compare and branch
    beq _core2_start
    b   _core3_start                        // last core, just go there


_core0_start:
// get out of EL3 and into EL2
//
    mrs     x0, CurrentEL                   // read current EL
    and     x0, x0, #0x0C                   // clear reserved bits
    cmp     x0, 0x0C                        // are the two bits set (0b11 = EL3)
    bne     _exit_el3

// chaging from el3 to el2
    mov     x2, #0x5b1                      // Secure Configuration Register
    msr     scr_el3, x2                     //
    mov     x2, #0x3c9
    msr     spsr_el3, x2                    // Program Status Registers
    adr     x2, _exit_el3
    msr     elr_el3, x2
    eret                                    // go!
// So we're now in EL2, proceed with setup
_exit_el3:
    mov x0, #0
    mov x1, #ARCH_AARCH64
    mov x2, #PLATFORM_RASPI3
    ldr x3, =__dtb_start
    ldr x4, =__core_0_el2_stack_end
    mov sp, x4
    ldr x4, =__core_0_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_0_el0_stack_end
    msr sp_el0, x4
    bl  kernel_main
    b __halt__

_core1_start:
    ldr x4, =__core_1_el2_stack_end
    mov sp, x4
    ldr x4, =__core_1_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_1_el0_stack_end
    msr sp_el0, x4
    bl aux_core_main
    b __halt__

_core2_start:
    ldr x4, =__core_2_el2_stack_end
    mov sp, x4
    ldr x4, =__core_2_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_2_el0_stack_end
    msr sp_el0, x4
    bl aux_core_main
    b __halt__

_core3_start:
    ldr x4, =__core_3_el2_stack_end
    mov sp, x4
    ldr x4, =__core_3_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_3_el0_stack_end
    msr sp_el0, x4
    bl aux_core_main
    b __halt__




.globl  __halt__
__halt__:
    wfe
    b __halt__

While adding a nop before the very first instruction breaks the whole thing:

// File: boot.S
// Platform: Raspberry Pi 3
// Date: 05/20/2018
//
//
//
// To keep this in the first portion of the binary.
.section ".text.boot"

#define __BOOT_CODE__
#include "identifiers.h"

// GLOBALS



// Make _start global.
.globl _start


_start:
///
// all cores setup code
//
// setup the VM midr/mipdr
    nop
    mrs x0, mpidr_el1                       // Read MIPDR
    mrs x1, midr_el1                        // Read MIDR
    msr vpidr_el2, x1                       // set the VM versions of these
    msr vmpidr_el2, x0                      // same


// start core specific code
    and x0, x0, #0x03                       // mask off cpu ID bits
    cbz x0, _core0_start                    // start cpu0
    cmp x0, #0x1                            // compare and branch
    beq _core1_start
    cmp x0, #0x2                            // compare and branch
    beq _core2_start
    b   _core3_start                        // last core, just go there


_core0_start:
// get out of EL3 and into EL2
//
    mrs     x0, CurrentEL                   // read current EL
    and     x0, x0, #0x0C                   // clear reserved bits
    cmp     x0, 0x0C                        // are the two bits set (0b11 = EL3)
    bne     _exit_el3

// chaging from el3 to el2
    mov     x2, #0x5b1                      // Secure Configuration Register
    msr     scr_el3, x2                     //
    mov     x2, #0x3c9
    msr     spsr_el3, x2                    // Program Status Registers
    adr     x2, _exit_el3
    msr     elr_el3, x2
    eret                                    // go!
// So we're now in EL2, proceed with setup
_exit_el3:
    mov x0, #0
    mov x1, #ARCH_AARCH64
    mov x2, #PLATFORM_RASPI3
    ldr x3, =__dtb_start
    ldr x4, =__core_0_el2_stack_end
    mov sp, x4
    ldr x4, =__core_0_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_0_el0_stack_end
    msr sp_el0, x4
    bl  kernel_main
    b __halt__

_core1_start:
    ldr x4, =__core_1_el2_stack_end
    mov sp, x4
    ldr x4, =__core_1_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_1_el0_stack_end
    msr sp_el0, x4
    bl aux_core_main
    b __halt__

_core2_start:
    ldr x4, =__core_2_el2_stack_end
    mov sp, x4
    ldr x4, =__core_2_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_2_el0_stack_end
    msr sp_el0, x4
    bl aux_core_main
    b __halt__

_core3_start:
    ldr x4, =__core_3_el2_stack_end
    mov sp, x4
    ldr x4, =__core_3_el1_stack_end
    msr sp_el1, x4
    ldr x4, =__core_3_el0_stack_end
    msr sp_el0, x4
    bl aux_core_main
    b __halt__




.globl  __halt__
__halt__:
    wfe
    b __halt__
goldilocks
  • 60,325
  • 17
  • 117
  • 234

1 Answers1

1

The solution lies in the config.txt.

After reading many forums and the RPi site, there is a config option called disable_commandline_tags.

If it is not set or is set to 0 in config.txt, ATAGS is loaded by start.elf starting at 0x100. My kernel was also using kernel_old=1, which starts loading at 0x0.

The ATAGs entry was overwriting a piece of the kernel. Setting disable_commandline_tags=1, no ATAGs are populated, and the problem is solved.