From 1b5d7cd40eb1c1f55deedf34d3d6324498b5f000 Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Sat, 8 Feb 2025 17:50:57 -0800
Subject: Hello world.

---
 run.sh       |   4 +-
 src/gpio.h   |  12 ++++++
 src/kernel.c |  10 +++++
 src/link.ld  |  53 ++++++++++++++++++-------
 src/mmio.c   |  23 +++++++++++
 src/mmio.h   |   8 ++++
 src/raspi.c  |  27 +++++++++++++
 src/raspi.h  |   4 ++
 src/uart.c   | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/uart.h   |  10 +++++
 10 files changed, 258 insertions(+), 18 deletions(-)
 create mode 100644 src/gpio.h
 create mode 100644 src/mmio.c
 create mode 100644 src/mmio.h
 create mode 100644 src/raspi.c
 create mode 100644 src/raspi.h
 create mode 100644 src/uart.c
 create mode 100644 src/uart.h

diff --git a/run.sh b/run.sh
index 3b2c6f2..ffe4732 100755
--- a/run.sh
+++ b/run.sh
@@ -2,9 +2,7 @@
 
 make
 qemu-system-aarch64 \
-	-cpu cortex-a53 \
-	-m 1024 \
 	-M raspi3b \
 	-serial stdio \
-	-kernel build/bin/kernel8.elf
+	-kernel build/bin/kernel8.img
 
diff --git a/src/gpio.h b/src/gpio.h
new file mode 100644
index 0000000..9bb442b
--- /dev/null
+++ b/src/gpio.h
@@ -0,0 +1,12 @@
+#pragma once
+
+enum
+{
+    // The offsets for reach register.
+    GPIO_BASE = 0x200000,
+    // 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),
+};
+
diff --git a/src/kernel.c b/src/kernel.c
index eb7d832..f1150be 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -1,4 +1,14 @@
+#include <mmio.h>
+#include <raspi.h>
+#include <uart.h>
+
 void main() {
+  const int raspi = raspi_init();
+  mmio_init(raspi);
+  uart_init(raspi);
+  
+  uart_print("Hello world!\n");
+  
   while (1);
 }
 
diff --git a/src/link.ld b/src/link.ld
index f1d1730..1f51675 100644
--- a/src/link.ld
+++ b/src/link.ld
@@ -1,20 +1,43 @@
+ENTRY(_start)
+
 SECTIONS
 {
-    . = 0x80000;     /* Kernel load address for AArch64 */
-    .text (READONLY) : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
-    .rodata (READONLY) : { *(.rodata .rodata.* .gnu.linkonce.r*) }
-    PROVIDE(_data = .);
-    .data : { *(.data .data.* .gnu.linkonce.d*) }
-    .bss (NOLOAD) : {
-        . = ALIGN(16);
-        __bss_start = .;
-        *(.bss .bss.*)
-        *(COMMON)
-        __bss_end = .;
+    /* Starts at LOADER_ADDR. */
+    . = 0x80000;
+    __start = .;
+    __text_start = .;
+    .text :
+    {
+        KEEP(*(.text.boot))
+        *(.text)
     }
-    _end = .;
+    . = ALIGN(4096); /* align to page size */
+    __text_end = .;
 
-   /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
-}
-__bss_size = (__bss_end - __bss_start);
+    __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 = .;
+}
diff --git a/src/mmio.c b/src/mmio.c
new file mode 100644
index 0000000..e545517
--- /dev/null
+++ b/src/mmio.c
@@ -0,0 +1,23 @@
+#include <mmio.h>
+
+void* MMIO_BASE;
+
+void mmio_init(int raspi) {
+  switch (raspi) {
+    case 2:
+    case 3:  MMIO_BASE = (void*)0x3F000000; break; // raspi2 & 3
+    case 4:  MMIO_BASE = (void*)0xFE000000; break; // raspi4
+    default: MMIO_BASE = (void*)0x20000000; break; // raspi1, raspi zero, etc.
+  }
+}
+
+#define REG_ADDR(reg) ((volatile uint32_t*)(MMIO_BASE + reg))
+
+uint32_t mmio_read(uint32_t reg) {
+  return *REG_ADDR(reg);
+}
+
+void mmio_write(uint32_t reg, uint32_t val) {
+  *REG_ADDR(reg) = val;
+}
+
diff --git a/src/mmio.h b/src/mmio.h
new file mode 100644
index 0000000..b566bbe
--- /dev/null
+++ b/src/mmio.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stdint.h>
+
+void mmio_init(int raspi);
+uint32_t mmio_read(uint32_t reg);
+void mmio_write(uint32_t reg, uint32_t val);
+
diff --git a/src/raspi.c b/src/raspi.c
new file mode 100644
index 0000000..bc76f89
--- /dev/null
+++ b/src/raspi.c
@@ -0,0 +1,27 @@
+#include <raspi.h>
+
+#include <stdint.h>
+
+int raspi_init() {
+  int raspi;
+  uint32_t reg;
+
+  // Read the system register.
+#if __aarch64__
+  asm volatile ("mrs %x0, midr_el1" : "=r" (reg));
+#else
+  asm volatile ("mrc p15,0,%0,c0,c0,0" : "=r" (reg));
+#endif
+
+  // Get the PartNum and detect the board.
+  switch ((reg >> 4) & 0xFFF) {
+      case 0xB76: raspi = 1; break;
+      case 0xC07: raspi = 2; break;
+      case 0xD03: raspi = 3; break;
+      case 0xD08: raspi = 4; break;
+      default:    raspi = 0; break;
+  }
+  
+  return raspi;
+}
+
diff --git a/src/raspi.h b/src/raspi.h
new file mode 100644
index 0000000..13f73e2
--- /dev/null
+++ b/src/raspi.h
@@ -0,0 +1,4 @@
+#pragma once
+
+int raspi_init();
+
diff --git a/src/uart.c b/src/uart.c
new file mode 100644
index 0000000..c5823e8
--- /dev/null
+++ b/src/uart.c
@@ -0,0 +1,125 @@
+#include <uart.h>
+
+#include <gpio.h>
+#include <mmio.h>
+
+enum
+{
+    // The base address for UART.
+    UART0_BASE = (GPIO_BASE + 0x1000), // for raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 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),
+    // The offsets for Mailbox registers.
+    MBOX_BASE    = 0xB880,
+    MBOX_READ    = (MBOX_BASE + 0x00),
+    MBOX_STATUS  = (MBOX_BASE + 0x18),
+    MBOX_WRITE   = (MBOX_BASE + 0x20)
+};
+
+// A mailbox message with set clock rate of PL011 to 3MHz tag.
+static volatile unsigned int  __attribute__((aligned(16))) mbox[9] = {
+  9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0
+};
+
+// 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");
+}
+
+void uart_init(int raspi) {
+  // 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) (((uint64_t)(&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_write(const void* buffer, size_t length) {
+  const uint8_t* bytes = buffer;
+  for (size_t i = 0; i < length; ++i) {
+    uart_putc(*bytes++);
+  }
+}
+
+void uart_print(const char* string) {
+  while (*string) {
+    uart_putc(*string++);
+  }
+}
+
diff --git a/src/uart.h b/src/uart.h
new file mode 100644
index 0000000..bc4669e
--- /dev/null
+++ b/src/uart.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stddef.h>
+
+void uart_init(int raspi);
+void uart_putc(unsigned char c);
+unsigned char uart_getc();
+void uart_write(const void* buffer, size_t length);
+void uart_print(const char* string);
+
-- 
cgit v1.2.3