• boot.S
.global _start
.section .text.boot, "ax"

_start:
    mrs x0, mpidr_el1
    and x0, x0, #3

    mov x6, x0
    mov x1, #0x0
    add x2, x1, #0x400000
    lsl x6, x6, #18
    sub x3, x2, x6
    mov sp, x3

    bl uart_init

    mrs x0, mpidr_el1
    and x0, x0, #3
    cbz x0, core0
    cmp x0, #1
    b.eq core1
    cmp x0, #2
    b.eq core2
    cmp x0, #3
    b.eq core3

core0:
    bl print0
    b hang

core1:
    bl print1
    b hang

core2:
    bl print2
    b hang

core3:
    bl print3
    b hang

hang:
    wfi
    b hang


  • main.c
extern void delay(unsigned long);
extern void put32(unsigned long, unsigned int);
extern unsigned int get32(unsigned long);
extern int get_el(void);

#define PBASE 0x3F000000
#define GPFSEL1	  (PBASE + 0x00200004)
#define GPSET0	  (PBASE + 0x0020001C)
#define GPCLR0	  (PBASE + 0x00200028)
#define GPPUD	  (PBASE + 0x00200094)
#define GPPUDCLK0 (PBASE + 0x00200098)

#define AUX_ENABLES	(PBASE + 0x00215004)
#define AUX_MU_IO_REG	(PBASE + 0x00215040)
#define AUX_MU_IER_REG	(PBASE + 0x00215044)
#define AUX_MU_IIR_REG	(PBASE + 0x00215048)
#define AUX_MU_LCR_REG	(PBASE + 0x0021504C)
#define AUX_MU_MCR_REG	(PBASE + 0x00215050)
#define AUX_MU_LSR_REG	(PBASE + 0x00215054)
#define AUX_MU_MSR_REG	(PBASE + 0x00215058)
#define AUX_MU_SCRATCH	(PBASE + 0x0021505C)
#define AUX_MU_CNTL_REG (PBASE + 0x00215060)
#define AUX_MU_STAT_REG (PBASE + 0x00215064)
#define AUX_MU_BAUD_REG (PBASE + 0x00215068)

void uart_send_char(char c)
{
	while (1) {
		if (get32(AUX_MU_LSR_REG) & 0x20)
			break;
	}
	put32(AUX_MU_IO_REG, c);
}

void uart_init(void)
{
	unsigned int selector;

	selector = get32(GPFSEL1);
	selector &= ~(7 << 12);
	selector |= 2 << 12;
	selector &= ~(7 << 15);
	selector |= 2 << 15;
	put32(GPFSEL1, selector);

	put32(GPPUD, 0);
	delay(150);
	put32(GPPUDCLK0, (1 << 14) | (1 << 15));
	delay(150);
	put32(GPPUDCLK0, 0);

	put32(AUX_ENABLES, 1);
	put32(AUX_MU_CNTL_REG, 0);
	put32(AUX_MU_IER_REG, 0);
	put32(AUX_MU_LCR_REG, 3);
	put32(AUX_MU_MCR_REG, 0);
	put32(AUX_MU_BAUD_REG, 270);

	put32(AUX_MU_CNTL_REG, 3);
}

void print0() {
	for(int j = 0; j <= 3; j++) {
		for(int i = 0; i <= 200000000; i++) {
			if (i == 1)
				uart_send_char('0');
		}
	}
}

void print1() {
	for(int j = 0; j <= 3; j++) {
		for(int i = 0; i <= 200000000; i++) {
			if (i == 10000000)
				uart_send_char('1');
		}
	}		
}

void print2() {
	for(int j = 0; j <= 3; j++) {
		for(int i = 0; i <= 200000000; i++) {
			if (i == 90000000)
				uart_send_char('2');
		}
	}		
}

void print3() {
	for(int j = 0; j <= 3; j++) {
		for(int i = 0; i <= 200000000; i++) {
			if (i == 80000000)
				uart_send_char('3');
		}
	}		
}


  • utils.S
.globl get_el
get_el:
	mrs x0, CurrentEL
	lsr x0, x0, #2
	ret

.globl put32
put32:
	str w1,[x0]
	ret

.globl get32
get32:
	ldr w0,[x0]
	ret

.globl delay
delay:
	subs x0, x0, #1
	bne delay
	ret


  • Makefile
ARMGNU ?= aarch64-linux-gnu

COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -mgeneral-regs-only
ASMOPS = -Iinclude

BUILD_DIR = build
SRC_DIR = .

all : arm_smp

run : arm_smp
	qemu-system-aarch64 -M raspi3b -nographic -serial null -serial mon:stdio -kernel kernel8.elf

clean :
	rm -rf $(BUILD_DIR) *.img *.bin *.elf

$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c
	mkdir -p $(@D)
	$(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@

$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S
	$(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@

C_FILES = $(wildcard $(SRC_DIR)/*.c)
ASM_FILES = $(wildcard $(SRC_DIR)/*.S)
OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o)
OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o)

DEP_FILES = $(OBJ_FILES:%.o=%.d)
-include $(DEP_FILES)

arm_smp: $(SRC_DIR)/linker.ld $(OBJ_FILES)
	 $(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -o kernel8.elf  $(OBJ_FILES)


  • linker.ld
ENTRY(_start)

SECTIONS {
    . = 0x80000;

    .text : {
        *(.text.boot)
        *(.text)
    }

    .data : {
        *(.data)
    }
}


$ make run
0132013201320132