Try   HackMD

Lab 1: Multicore C

Assembly

Now we have to distinguish the cores. For that, we read the mpidr_el1 system register. If it's not zero, we'll do the former infinite loop. If it's zero, then we'll call a C function. But for that, we need a proper stack, and a zerod out bss segment in memory before the call instruction. I've added some more code to the Assembly to do all of that. In case the C code returns (shouldn't), we also jump to the same infinite loop the other CPU cores running.

NOTE: depending on your firmware version, it is possible that application cores are stopped. If that's the case then our code only runs on Core 0, but there's no harm in checking the core number. To start the other cores, you have to write an address of a function to execute at 0xE0, 0xE8, 0xF0 (one address for each core, in order).

.section ".text.boot" .global _start _start: // read cpu id, stop slave cores mrs x1, mpidr_el1 and x1, x1, #3 cbz x1, 2f //if CPU id = 0, jump to 2f // cpu id > 0, stop 1: wfe b 1b 2: // cpu id == 0 // set stack before our code ldr x1, =_start mov sp, x1 // clear bss ldr x1, =__bss_start ldr w2, =__bss_size 3: cbz w2, 4f str xzr, [x1], #8 sub w2, w2, #1 cbnz w2, 3b // jump to C code, should not return 4: bl main // for failsafe, halt this core too b 1b

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Arm Cortex-A53 MPCore Processor Technical Reference Manual

Makefile

It became a bit trickier. I've added commands for compiling C sources, but in a comform way. From now on, we can use the same Makefile for every tutorial, regardless of the number of C sources, and I won't discuss it any further.

SRCS = $(wildcard *.c) OBJS = $(SRCS:.c=.o) CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles all: clean kernel8.img start.o: start.S aarch64-linux-gnu-gcc $(CFLAGS) -c start.S -o start.o %.o: %.c aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ kernel8.img: start.o $(OBJS) aarch64-linux-gnu-ld -nostdlib -nostartfiles start.o $(OBJS) -T link.ld -o kernel8.elf aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img clean: rm kernel8.elf *.o >/dev/null 2>/dev/null || true run: qemu-system-aarch64 -M raspi3 -kernel kernel8.img -d in_asm

Linker Script

Likewise, the linker script became more complex too, as C needs data and bss sections. I've also added a calculation for the bss size, so that we can refer to it from the Assembly without any further hassle.

It is important to start the text segment with the Assembly code, because we set the stack right before it, hence the KEEP(). This way our load address is 0x80000, the same as _start label and stack top.

SECTIONS { . = 0x80000; .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } PROVIDE(_data = .); .data : { *(.data .data.* .gnu.linkonce.d*) } .bss (NOLOAD) : { . = ALIGN(16); __bss_start = .; *(.bss .bss.*) *(COMMON) __bss_end = .; } _end = .; /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } } __bss_size = (__bss_end - __bss_start)>>3;

Main.c

Just an empty loop.

void main() { while(1); }

How to run

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →


Previous Lab : Bare Minimum

Next Lab : UART1


Project Source Code: Bare-Metal-Mini-PiOS
Author : andykuo8766