codedoo
Posts: 3
Joined: Fri Dec 29, 2017 11:40 pm

Hello world! bare metal boot of PI Zero, ASM + C

Mon Jan 08, 2018 11:03 pm

Hi!
I'm trying to write the classic 'hello world' during a bare metal boot on my pi zero.
I followed this guide : http://wiki.osdev.org/index.php?title=R ... ldid=20596

The code slightly modified for Pi Zero, the assembly boot file : boot.S

Code: Select all

.section ".text.boot"
 
.globl _start
 
_start:
	// Setup the stack.
	mov sp, #0x8000
 
	// Clear out bss.
	ldr r4, =__bss_start
	ldr r9, =__bss_end
	mov r5, #0
	mov r6, #0
	mov r7, #0
	mov r8, #0
	b       2f
 
1:
	// store multiple at r4.
	stmia r4!, {r5-r8}
 
	// If we are still below bss_end, loop.
2:
	cmp r4, r9
	blo 1b
 
	// Call kernel_main
	ldr r3, =kernel_main
	blx r3
 
	// halt
halt:
	wfe
	b halt
	
assembler : arm-none-eabi-as boot.S -o boot.o

The basic kernel : kernel.c

Code: Select all

#include <stddef.h>
#include <stdint.h>
 
// Memory-Mapped I/O output
static inline void mmio_write(uint32_t reg, uint32_t data)
{
	*(volatile uint32_t*)reg = data;
}
 
// Memory-Mapped I/O input
static inline uint32_t mmio_read(uint32_t reg)
{
	return *(volatile uint32_t*)reg;
}
 
// Loop <delay> times in a way that the compiler won't optimize away
static inline void delay(int32_t count)
{
	asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n"
		 : "=r"(count): [count]"0"(count) : "cc");
}
 
enum
{
    // The GPIO registers base address.
    GPIO_BASE = 0x20200000, // for Pi Zero 
 
    // The offsets for reach register.
 
    // Controls actuation of pull up/down to ALL GPIO pins.
    GPPUD = (GPIO_BASE + 0x94),
 
    // Controls actuation of pull up/down for specific GPIO pin.
    GPPUDCLK0 = (GPIO_BASE + 0x98),
 
    // The base address for UART.
    UART0_BASE = 0x20201000, // for Pi Zero
 
    // The offsets for reach register for the UART.
    UART0_DR     = (UART0_BASE + 0x00),
    UART0_RSRECR = (UART0_BASE + 0x04),
    UART0_FR     = (UART0_BASE + 0x18),
    UART0_ILPR   = (UART0_BASE + 0x20),
    UART0_IBRD   = (UART0_BASE + 0x24),
    UART0_FBRD   = (UART0_BASE + 0x28),
    UART0_LCRH   = (UART0_BASE + 0x2C),
    UART0_CR     = (UART0_BASE + 0x30),
    UART0_IFLS   = (UART0_BASE + 0x34),
    UART0_IMSC   = (UART0_BASE + 0x38),
    UART0_RIS    = (UART0_BASE + 0x3C),
    UART0_MIS    = (UART0_BASE + 0x40),
    UART0_ICR    = (UART0_BASE + 0x44),
    UART0_DMACR  = (UART0_BASE + 0x48),
    UART0_ITCR   = (UART0_BASE + 0x80),
    UART0_ITIP   = (UART0_BASE + 0x84),
    UART0_ITOP   = (UART0_BASE + 0x88),
    UART0_TDR    = (UART0_BASE + 0x8C),
};
 
void uart_init()
{
	// Disable UART0.
	mmio_write(UART0_CR, 0x00000000);
	// Setup the GPIO pin 14 && 15.
 
	// Disable pull up/down for all GPIO pins & delay for 150 cycles.
	mmio_write(GPPUD, 0x00000000);
	delay(150);
 
	// Disable pull up/down for pin 14,15 & delay for 150 cycles.
	mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15));
	delay(150);
 
	// Write 0 to GPPUDCLK0 to make it take effect.
	mmio_write(GPPUDCLK0, 0x00000000);
 
	// Clear pending interrupts.
	mmio_write(UART0_ICR, 0x7FF);
 
	// Set integer & fractional part of baud rate.
	// Divider = UART_CLOCK/(16 * Baud)
	// Fraction part register = (Fractional part * 64) + 0.5
	// UART_CLOCK = 3000000; Baud = 115200.
 
	// Divider = 3000000 / (16 * 115200) = 1.627 = ~1.
	mmio_write(UART0_IBRD, 1);
	// Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
	mmio_write(UART0_FBRD, 40);
 
	// Enable FIFO & 8 bit data transmissio (1 stop bit, no parity).
	mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6));
 
	// Mask all interrupts.
	mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) |
	                       (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10));
 
	// Enable UART0, receive & transfer part of UART.
	mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9));
}
 
void uart_putc(unsigned char c)
{
	// Wait for UART to become ready to transmit.
	while ( mmio_read(UART0_FR) & (1 << 5) ) { }
	mmio_write(UART0_DR, c);
}
 
unsigned char uart_getc()
{
    // Wait for UART to have received something.
    while ( mmio_read(UART0_FR) & (1 << 4) ) { }
    return mmio_read(UART0_DR);
}
 
void uart_puts(const char* str)
{
	for (size_t i = 0; str[i] != '\0'; i ++)
		uart_putc((unsigned char)str[i]);
}
 
#if defined(__cplusplus)
extern "C" /* Use C linkage for kernel_main. */
#endif
void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags)
{
	// Declare as unused
	(void) r0;
	(void) r1;
	(void) atags;
 
	uart_init();
	uart_puts("Hello, kernel World!\r\n");
 
	while (1)
		uart_putc(uart_getc());
}
compiling the kernel :
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c kernel.c -o kernel.o

The linker script : linker.ld

Code: Select all

ENTRY(_start)
 
SECTIONS
{
    /* Starts at LOADER_ADDR. */
    . = 0x8000;
    __start = .;
    __text_start = .;
    .text :
    {
        KEEP(*(.text.boot))
        *(.text)
    }
    . = ALIGN(4096); /* align to page size */
    __text_end = .;
 
    __rodata_start = .;
    .rodata :
    {
        *(.rodata)
    }
    . = ALIGN(4096); /* align to page size */
    __rodata_end = .;
 
    __data_start = .;
    .data :
    {
        *(.data)
    }
    . = ALIGN(4096); /* align to page size */
    __data_end = .;
 
    __bss_start = .;
    .bss :
    {
        bss = .;
        *(.bss)
    }
    . = ALIGN(4096); /* align to page size */
    __bss_end = .;
    __end = .;
}
Linking
arm-none-eabi-ld boot.o kernel.o -T linker.ld -o kernel.elf
arm-none-eabi-objcopy kernel.elf -O binary kernel.img


After prepared the SDcard (tested with some distros on the same board), I've simply copied on the FAT root :
kernel.img
start.elf (downloaded from the firmware repo)
bootcode.bin (downloaded from the firmware repo)

It starts but I get the splash color screen, I think it means kernel.img failed.

I've studied bare metal boot code on github from PeterLemon, dwelch67, and Ciro Santilli (but none of them is for Pi zero)
And the following tutorials.
http://www.valvers.com/open-software/ra ... g-in-cpt1/
https://www.cl.cam.ac.uk/projects/raspb ... orials/os/

Someone could help with the code or suggest me a 'Pi zero' bare metal boot code working example?

Thank you

Leonardo

dwelch67
Posts: 944
Joined: Sat May 26, 2012 5:32 pm

Re: Hello world! bare metal boot of PI Zero, ASM + C

Tue Jan 09, 2018 2:59 am

absolutely I have examples for pi-zero look in the boards/pi-zero directory or look at the standalone repo https://github.com/dwelch67/raspberrypi-zero

dwelch67
Posts: 944
Joined: Sat May 26, 2012 5:32 pm

Re: Hello world! bare metal boot of PI Zero, ASM + C

Tue Jan 09, 2018 3:05 am

vectors.s

Code: Select all

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
hang: b hang

.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.globl dummy
dummy:
    bx lr
notmain.c

Code: Select all

extern void PUT32 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
extern void dummy ( unsigned int );

#define GPFSEL3 0x2020000C
#define GPFSEL4 0x20200010
#define GPSET1  0x20200020
#define GPCLR1  0x2020002C

int notmain ( void )
{
    unsigned int ra;

    ra=GET32(GPFSEL4);
    ra&=~(7<<21);
    ra|=1<<21;
    PUT32(GPFSEL4,ra);

    while(1)
    {
        PUT32(GPSET1,1<<(47-32));
        for(ra=0;ra<0x100000;ra++) dummy(ra);
        PUT32(GPCLR1,1<<(47-32));
        for(ra=0;ra<0x100000;ra++) dummy(ra);
    }

    return(0);
}
memmap

Code: Select all

MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 0x10000
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}
build

Code: Select all

arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o
arm-none-eabi-ld vectors.o notmain.o -T memmap -o notmain.elf
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy --srec-forceS3 notmain.elf -O srec notmain.srec
arm-none-eabi-objcopy notmain.elf -O binary kernel.img
s-record of binary in case you want to try to build a bin from it and use it...

Code: Select all

S00F00006E6F746D61696E2E737265631F
S3150000800002D9A0E3050000EBFEFFFFEA001080E5C1
S315000080101EFF2FE1000090E51EFF2FE11EFF2FE15E
S3150000802010402DE95C009FE5F9FFFFEB0E16C0E35B
S31500008030021681E34C009FE5F3FFFFEB48009FE546
S315000080400219A0E3F0FFFFEB0040A0E30400A0E16B
S31500008050014084E2F0FFFFEB010654E3FAFFFF1A4A
S3150000806028009FE50219A0E3E7FFFFEB0040A0E32D
S315000080700400A0E1014084E2E7FFFFEB010654E3C0
S31500008080FAFFFF1AECFFFFEA100020202000202054
S309000080902C0020207A
S705000080007A
copy kernel.img to the sd card root directory along with start.elf and bootcode.bin from

https://github.com/raspberrypi

dont clone, just get the two individual files from the firmware directory. (click on View Raw to download).

David (dwelch67)

Return to “Bare metal, Assembly language”

Who is online

Users browsing this forum: No registered users and 9 guests