Skip to content

Commit

Permalink
compile and run hello world
Browse files Browse the repository at this point in the history
  • Loading branch information
goodstuff20 committed Nov 30, 2020
1 parent a1d7faf commit aae265a
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 0 deletions.
38 changes: 38 additions & 0 deletions qemu-IO-test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
CROSS_COMPILE = aarch64-elf-
CC = $(CROSS_COMPILE)gcc
CFLAGS = -Wall -fno-common -O0 -g \
-nostdlib -nostartfiles -ffreestanding \
-march=armv8-a

kernel8.img: linker.ld boot.o hello.o
aarch64-elf-ld boot.o hello.o -T linker.ld -o kernel8.img
aarch64-elf-objdump -d kernel8.img > kernel.list
aarch64-elf-objdump -t kernel8.img | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym

boot.o: boot.S
aarch64-elf-gcc -c boot.S -o boot.o

basicfct:
aarch64-linux-gnu-as -o basicfct.o basicfct.S
aarch64-linux-gnu-ld -s -o basicfct basicfct.o

clean:
rm -f kernel8.img *.o *.list *.sym basicfct

.PHONY: all qemu clean


# assembler compile:
# aarch64-elf-as -c boot.S -o boot.o

# c compile:
# aarch64-elf-gcc -ffreestanding -c hello.c -o hello.o -O2 -Wall -Wextra

# link kernel (doesn't work properly though?)
# aarch64-elf-gcc -T linker.ld -o myos.elf -ffreestanding -O2 -nostdlib boot.o hello.o -lgcc
# aarch64-elf-objcopy myos.elf -O binary kernel8.img

# run emulator
# qemu-system-aarch64 -M raspi3 -serial mon:stdio -kernel kernel8.img --nographic
# extra / removable flags:
# -serial mon:stdio (input works buggy)
23 changes: 23 additions & 0 deletions qemu-IO-test/basicfct.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.data

/* from: https://peterdn.com/post/2020/08/22/hello-world-in-arm64-assembly/ */
msg:
.ascii "Hello, ARM64!\n"
len = . - msg

.text
.type myadd,%function

.globl printh
printh:
/* syscall write(int fd, const void *buf, size_t count) */
mov x0, #1 /* fd := STDOUT_FILENO */
ldr x1, =msg /* buf := msg */
ldr x2, =len /* count := len */
mov w8, #64 /* write is syscall #64 */
svc #0 /* invoke syscall */

/* syscall exit(int status) */
mov x0, #0 /* status := 0 */
mov w8, #93 /* exit is syscall #1 */
svc #0 /* invoke syscall */
32 changes: 32 additions & 0 deletions qemu-IO-test/boot.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// AArch64 mode

// To keep this in the first portion of the binary.
.section ".text.boot"

// Make _start global.
.globl _start

.org 0x80000
// Entry point for the kernel. Registers:
// x0 -> 32 bit pointer to DTB in memory (primary core only) / 0 (secondary cores)
// x1 -> 0
// x2 -> 0
// x3 -> 0
// x4 -> 32 bit kernel entry point, _start location
_start:
// set stack before our code
ldr x5, =_start
mov sp, x5

// clear bss
ldr x5, =__bss_start
ldr w6, =__bss_size
3: cbz w6, 4f
str xzr, [x5], #8
sub w6, w6, #1
cbnz w6, 3b

// jump to C code, should not return
4: bl kernel_main
// for failsafe, halt this core too
// b not_here
176 changes: 176 additions & 0 deletions qemu-IO-test/hello.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

// board type
int raspi = 3; // raspi 4 wird noch nicht unterstuetzt

// extern void printh(); // is a system call and not printf or so at the moment so won't work I assume

// 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");
}

//-----------
// The MMIO area base address.
/*switch (raspi) {
case 2:
case 3: MMIO_BASE = 0x3F000000; break; // for raspi2 & 3
case 4: MMIO_BASE = 0xFE000000; break; // for raspi4
default: MMIO_BASE = 0x20000000; break; // for raspi1, raspi zero etc.
}*/
#define MMIO_BASE 0x3F000000

// The offsets for reach register.
#define GPIO_BASE (MMIO_BASE + 0x200000)

// Controls actuation of pull up/down to ALL GPIO pins.
#define GPPUD (GPIO_BASE + 0x94)

// Controls actuation of pull up/down for specific GPIO pin.
#define GPPUDCLK0 (GPIO_BASE + 0x98)

// The base address for UART.
#define UART0_BASE (GPIO_BASE + 0x1000) // for raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 0x20201000 for raspi1

// The offsets for reach register for the UART.
#define UART0_DR (UART0_BASE + 0x00)
#define UART0_RSRECR (UART0_BASE + 0x04)
#define UART0_FR (UART0_BASE + 0x18)
#define UART0_ILPR (UART0_BASE + 0x20)
#define UART0_IBRD (UART0_BASE + 0x24)
#define UART0_FBRD (UART0_BASE + 0x28)
#define UART0_LCRH (UART0_BASE + 0x2C)
#define UART0_CR (UART0_BASE + 0x30)
#define UART0_IFLS (UART0_BASE + 0x34)
#define UART0_IMSC (UART0_BASE + 0x38)
#define UART0_RIS (UART0_BASE + 0x3C)
#define UART0_MIS (UART0_BASE + 0x40)
#define UART0_ICR (UART0_BASE + 0x44)
#define UART0_DMACR (UART0_BASE + 0x48)
#define UART0_ITCR (UART0_BASE + 0x80)
#define UART0_ITIP (UART0_BASE + 0x84)
#define UART0_ITOP (UART0_BASE + 0x88)
#define UART0_TDR (UART0_BASE + 0x8C)

// The offsets for Mailbox registers
#define MBOX_BASE (MMIO_BASE + 0xB880)
#define MBOX_READ (MBOX_BASE + 0x00)
#define MBOX_STATUS (MBOX_BASE + 0x18)
#define MBOX_WRITE (MBOX_BASE + 0x20)
//-----------

// A Mailbox message with set clock rate of PL011 to 3MHz tag
volatile unsigned int __attribute__((aligned(16))) mbox[9] = {
9*4, 0, 0x38002, 12, 8, 2, 3000000, 0 ,0
};

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
// Baud = 115200.

// For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default.
// Set it to 3Mhz so that we can consistently set the baud rate
if (raspi >= 3) {
// UART_CLOCK = 30000000;
unsigned int r = (((unsigned int)(&mbox) & ~0xF) | 8);
// wait until we can talk to the VC
while ( mmio_read(MBOX_STATUS) & 0x80000000 ) { }
// send our message to property channel and wait for the response
mmio_write(MBOX_WRITE, r);
while ( (mmio_read(MBOX_STATUS) & 0x40000000) || mmio_read(MBOX_READ) != r ) { }
}

// 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 transmission (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

#ifdef AARCH64
// arguments for AArch64
void kernel_main(uint64_t dtb_ptr32, uint64_t x1, uint64_t x2, uint64_t x3)
#else
// arguments for AArch32
void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags)
#endif
{
uart_init();
// printh();
uart_puts("Hello, kernel World!\r\n");

while (1)
uart_putc(uart_getc());


}
44 changes: 44 additions & 0 deletions qemu-IO-test/linker.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
ENTRY(_start)

SECTIONS
{
/* Starts at LOADER_ADDR. */
. = 0x80000;
/* For AArch32, use . = 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 = .;
__bss_size = __bss_end - __bss_start;
__end = .;
}
4 changes: 4 additions & 0 deletions qemu-IO-test/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
echo "Press Ctrl-A and then X to exit QEMU"
echo
qemu-system-aarch64 -M raspi3 -kernel kernel8.img --nographic

0 comments on commit aae265a

Please sign in to comment.