aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojtek Kosior <kwojtus@protonmail.com>2019-10-03 11:00:21 +0200
committerWojtek Kosior <kwojtus@protonmail.com>2019-10-03 11:00:21 +0200
commit4e7666742e6089e7f0ff359c42f726acde7e2a0c (patch)
tree102d0dbf4983a81651d05f0c0ecaa86dc7c0a8e3
parent5f5df2cbe593b8d4365b5a20c2ec240746c9d914 (diff)
downloadrpi-MMU-example-4e7666742e6089e7f0ff359c42f726acde7e2a0c.tar.gz
rpi-MMU-example-4e7666742e6089e7f0ff359c42f726acde7e2a0c.zip
copy-paste broken code from wiki.osdev; fix it; add Makefile
-rw-r--r--Makefile29
-rw-r--r--boot.S52
-rw-r--r--kernel.c143
-rw-r--r--linker.ld44
4 files changed, 268 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e465085
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+all : kernel7.img
+
+kernel.o : kernel.c
+ arm-none-eabi-gcc -mcpu=cortex-a7 -ffreestanding -std=gnu99 -c -O2 -Wall -Wextra $^ -o $@
+
+boot.o : boot.S
+ arm-none-eabi-as -mcpu=cortex-a7 boot.S -o boot.o
+
+kernel.elf : boot.o kernel.o
+ arm-none-eabi-gcc -T linker.ld -o $@ -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc
+
+kernel_padded.img : kernel.elf
+ arm-none-eabi-objcopy $^ -O binary $@
+
+# objcopy pads 0x0000 to 0x8000 with zeros, we need to get rid of them
+kernel7.img : kernel_padded.img
+ -rm $@
+ dd bs=4096 skip=8 if=$^ of=$@
+
+qemu-elf : kernel.elf
+ qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^
+
+qemu-bin : kernel7.img
+ qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^
+
+clean :
+ -rm kernel7.img kernel_padded.img kernel.elf boot.o kernel.o
+
+.PHONY: all qemu-elf qemu-bin clean
diff --git a/boot.S b/boot.S
new file mode 100644
index 0000000..da8aca7
--- /dev/null
+++ b/boot.S
@@ -0,0 +1,52 @@
+// AArch32 mode
+
+// To keep this in the first portion of the binary.
+.section ".text.boot"
+
+// Make _start global.
+.globl _start
+
+.org 0x8000
+// Entry point for the kernel.
+// r15 -> should begin execution at 0x8000.
+// r0 -> 0x00000000
+// r1 -> 0x00000C42
+// r2 -> 0x00000100 - start of ATAGS
+// preserve these registers as argument for kernel_main
+_start:
+ // Shut off extra cores
+ mrc p15, 0, r5, c0, c0, 5
+ and r5, r5, #3
+ cmp r5, #0
+ bne halt
+
+ // Setup the stack.
+ ldr r5, =_start
+ mov sp, r5
+
+ // 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
diff --git a/kernel.c b/kernel.c
new file mode 100644
index 0000000..6d09ce7
--- /dev/null
+++ b/kernel.c
@@ -0,0 +1,143 @@
+#include <stddef.h>
+#include <stdint.h>
+
+// board type, raspi2
+#define RASPI 2
+
+// 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");
+}
+
+#if RASPI == 4
+#define GPIO_BASE 0xFE200000
+#else
+#if RASPI == 3 || RASPI == 2
+#define GPIO_BASE 0x3F200000
+#else
+#define GPIO_BASE 0x20200000
+#endif // RASPI == 3 || RASPI == 2
+#endif // RASPI == 4
+
+enum
+ {
+ // 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 = 0x3F201000, // for raspi2 & 3, 0x20201000 for raspi1
+
+ // 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 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]);
+}
+
+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());
+}
diff --git a/linker.ld b/linker.ld
new file mode 100644
index 0000000..7f3ee57
--- /dev/null
+++ b/linker.ld
@@ -0,0 +1,44 @@
+ENTRY(_start)
+
+SECTIONS
+{
+ /* Starts at LOADER_ADDR. */
+ . = 0x8000;
+ /* For AArch64, use . = 0x80000; */
+ __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 = .;
+}