diff options
author | 3gg <3gg@shellblade.net> | 2025-02-08 17:50:57 -0800 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-02-08 17:50:57 -0800 |
commit | 1b5d7cd40eb1c1f55deedf34d3d6324498b5f000 (patch) | |
tree | a0bc21168f8270ee5fcb139498131dff884a7450 | |
parent | 0e1595330211351823e68691fca013bb47772aeb (diff) |
Hello world.
-rwxr-xr-x | run.sh | 4 | ||||
-rw-r--r-- | src/gpio.h | 12 | ||||
-rw-r--r-- | src/kernel.c | 10 | ||||
-rw-r--r-- | src/link.ld | 53 | ||||
-rw-r--r-- | src/mmio.c | 23 | ||||
-rw-r--r-- | src/mmio.h | 8 | ||||
-rw-r--r-- | src/raspi.c | 27 | ||||
-rw-r--r-- | src/raspi.h | 4 | ||||
-rw-r--r-- | src/uart.c | 125 | ||||
-rw-r--r-- | src/uart.h | 10 |
10 files changed, 258 insertions, 18 deletions
@@ -2,9 +2,7 @@ | |||
2 | 2 | ||
3 | make | 3 | make |
4 | qemu-system-aarch64 \ | 4 | qemu-system-aarch64 \ |
5 | -cpu cortex-a53 \ | ||
6 | -m 1024 \ | ||
7 | -M raspi3b \ | 5 | -M raspi3b \ |
8 | -serial stdio \ | 6 | -serial stdio \ |
9 | -kernel build/bin/kernel8.elf | 7 | -kernel build/bin/kernel8.img |
10 | 8 | ||
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 @@ | |||
1 | #pragma once | ||
2 | |||
3 | enum | ||
4 | { | ||
5 | // The offsets for reach register. | ||
6 | GPIO_BASE = 0x200000, | ||
7 | // Controls actuation of pull up/down to ALL GPIO pins. | ||
8 | GPPUD = (GPIO_BASE + 0x94), | ||
9 | // Controls actuation of pull up/down for specific GPIO pin. | ||
10 | GPPUDCLK0 = (GPIO_BASE + 0x98), | ||
11 | }; | ||
12 | |||
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 @@ | |||
1 | #include <mmio.h> | ||
2 | #include <raspi.h> | ||
3 | #include <uart.h> | ||
4 | |||
1 | void main() { | 5 | void main() { |
6 | const int raspi = raspi_init(); | ||
7 | mmio_init(raspi); | ||
8 | uart_init(raspi); | ||
9 | |||
10 | uart_print("Hello world!\n"); | ||
11 | |||
2 | while (1); | 12 | while (1); |
3 | } | 13 | } |
4 | 14 | ||
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 @@ | |||
1 | ENTRY(_start) | ||
2 | |||
1 | SECTIONS | 3 | SECTIONS |
2 | { | 4 | { |
3 | . = 0x80000; /* Kernel load address for AArch64 */ | 5 | /* Starts at LOADER_ADDR. */ |
4 | .text (READONLY) : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } | 6 | . = 0x80000; |
5 | .rodata (READONLY) : { *(.rodata .rodata.* .gnu.linkonce.r*) } | 7 | __start = .; |
6 | PROVIDE(_data = .); | 8 | __text_start = .; |
7 | .data : { *(.data .data.* .gnu.linkonce.d*) } | 9 | .text : |
8 | .bss (NOLOAD) : { | 10 | { |
9 | . = ALIGN(16); | 11 | KEEP(*(.text.boot)) |
10 | __bss_start = .; | 12 | *(.text) |
11 | *(.bss .bss.*) | ||
12 | *(COMMON) | ||
13 | __bss_end = .; | ||
14 | } | 13 | } |
15 | _end = .; | 14 | . = ALIGN(4096); /* align to page size */ |
15 | __text_end = .; | ||
16 | 16 | ||
17 | /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } | 17 | __rodata_start = .; |
18 | } | 18 | .rodata : |
19 | __bss_size = (__bss_end - __bss_start); | 19 | { |
20 | *(.rodata) | ||
21 | } | ||
22 | . = ALIGN(4096); /* align to page size */ | ||
23 | __rodata_end = .; | ||
20 | 24 | ||
25 | __data_start = .; | ||
26 | .data : | ||
27 | { | ||
28 | *(.data) | ||
29 | } | ||
30 | . = ALIGN(4096); /* align to page size */ | ||
31 | __data_end = .; | ||
32 | |||
33 | __bss_start = .; | ||
34 | .bss : | ||
35 | { | ||
36 | bss = .; | ||
37 | *(.bss) | ||
38 | } | ||
39 | . = ALIGN(4096); /* align to page size */ | ||
40 | __bss_end = .; | ||
41 | __bss_size = __bss_end - __bss_start; | ||
42 | __end = .; | ||
43 | } | ||
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 @@ | |||
1 | #include <mmio.h> | ||
2 | |||
3 | void* MMIO_BASE; | ||
4 | |||
5 | void mmio_init(int raspi) { | ||
6 | switch (raspi) { | ||
7 | case 2: | ||
8 | case 3: MMIO_BASE = (void*)0x3F000000; break; // raspi2 & 3 | ||
9 | case 4: MMIO_BASE = (void*)0xFE000000; break; // raspi4 | ||
10 | default: MMIO_BASE = (void*)0x20000000; break; // raspi1, raspi zero, etc. | ||
11 | } | ||
12 | } | ||
13 | |||
14 | #define REG_ADDR(reg) ((volatile uint32_t*)(MMIO_BASE + reg)) | ||
15 | |||
16 | uint32_t mmio_read(uint32_t reg) { | ||
17 | return *REG_ADDR(reg); | ||
18 | } | ||
19 | |||
20 | void mmio_write(uint32_t reg, uint32_t val) { | ||
21 | *REG_ADDR(reg) = val; | ||
22 | } | ||
23 | |||
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 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <stdint.h> | ||
4 | |||
5 | void mmio_init(int raspi); | ||
6 | uint32_t mmio_read(uint32_t reg); | ||
7 | void mmio_write(uint32_t reg, uint32_t val); | ||
8 | |||
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 @@ | |||
1 | #include <raspi.h> | ||
2 | |||
3 | #include <stdint.h> | ||
4 | |||
5 | int raspi_init() { | ||
6 | int raspi; | ||
7 | uint32_t reg; | ||
8 | |||
9 | // Read the system register. | ||
10 | #if __aarch64__ | ||
11 | asm volatile ("mrs %x0, midr_el1" : "=r" (reg)); | ||
12 | #else | ||
13 | asm volatile ("mrc p15,0,%0,c0,c0,0" : "=r" (reg)); | ||
14 | #endif | ||
15 | |||
16 | // Get the PartNum and detect the board. | ||
17 | switch ((reg >> 4) & 0xFFF) { | ||
18 | case 0xB76: raspi = 1; break; | ||
19 | case 0xC07: raspi = 2; break; | ||
20 | case 0xD03: raspi = 3; break; | ||
21 | case 0xD08: raspi = 4; break; | ||
22 | default: raspi = 0; break; | ||
23 | } | ||
24 | |||
25 | return raspi; | ||
26 | } | ||
27 | |||
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 @@ | |||
1 | #pragma once | ||
2 | |||
3 | int raspi_init(); | ||
4 | |||
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 @@ | |||
1 | #include <uart.h> | ||
2 | |||
3 | #include <gpio.h> | ||
4 | #include <mmio.h> | ||
5 | |||
6 | enum | ||
7 | { | ||
8 | // The base address for UART. | ||
9 | UART0_BASE = (GPIO_BASE + 0x1000), // for raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 0x20201000 for raspi1 | ||
10 | // The offsets for reach register for the UART. | ||
11 | UART0_DR = (UART0_BASE + 0x00), | ||
12 | UART0_RSRECR = (UART0_BASE + 0x04), | ||
13 | UART0_FR = (UART0_BASE + 0x18), | ||
14 | UART0_ILPR = (UART0_BASE + 0x20), | ||
15 | UART0_IBRD = (UART0_BASE + 0x24), | ||
16 | UART0_FBRD = (UART0_BASE + 0x28), | ||
17 | UART0_LCRH = (UART0_BASE + 0x2C), | ||
18 | UART0_CR = (UART0_BASE + 0x30), | ||
19 | UART0_IFLS = (UART0_BASE + 0x34), | ||
20 | UART0_IMSC = (UART0_BASE + 0x38), | ||
21 | UART0_RIS = (UART0_BASE + 0x3C), | ||
22 | UART0_MIS = (UART0_BASE + 0x40), | ||
23 | UART0_ICR = (UART0_BASE + 0x44), | ||
24 | UART0_DMACR = (UART0_BASE + 0x48), | ||
25 | UART0_ITCR = (UART0_BASE + 0x80), | ||
26 | UART0_ITIP = (UART0_BASE + 0x84), | ||
27 | UART0_ITOP = (UART0_BASE + 0x88), | ||
28 | UART0_TDR = (UART0_BASE + 0x8C), | ||
29 | // The offsets for Mailbox registers. | ||
30 | MBOX_BASE = 0xB880, | ||
31 | MBOX_READ = (MBOX_BASE + 0x00), | ||
32 | MBOX_STATUS = (MBOX_BASE + 0x18), | ||
33 | MBOX_WRITE = (MBOX_BASE + 0x20) | ||
34 | }; | ||
35 | |||
36 | // A mailbox message with set clock rate of PL011 to 3MHz tag. | ||
37 | static volatile unsigned int __attribute__((aligned(16))) mbox[9] = { | ||
38 | 9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0 | ||
39 | }; | ||
40 | |||
41 | // Loop <delay> times in a way that the compiler won't optimize away. | ||
42 | static inline void delay(int32_t count) { | ||
43 | asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n" | ||
44 | : "=r"(count): [count]"0"(count) : "cc"); | ||
45 | } | ||
46 | |||
47 | void uart_init(int raspi) { | ||
48 | // Disable UART0. | ||
49 | mmio_write(UART0_CR, 0x00000000); | ||
50 | |||
51 | // Setup the GPIO pin 14 && 15. | ||
52 | |||
53 | // Disable pull up/down for all GPIO pins & delay for 150 cycles. | ||
54 | mmio_write(GPPUD, 0x00000000); | ||
55 | delay(150); | ||
56 | |||
57 | // Disable pull up/down for pin 14,15 & delay for 150 cycles. | ||
58 | mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); | ||
59 | delay(150); | ||
60 | |||
61 | // Write 0 to GPPUDCLK0 to make it take effect. | ||
62 | mmio_write(GPPUDCLK0, 0x00000000); | ||
63 | |||
64 | // Clear pending interrupts. | ||
65 | mmio_write(UART0_ICR, 0x7FF); | ||
66 | |||
67 | // Set integer & fractional part of baud rate. | ||
68 | // Divider = UART_CLOCK/(16 * Baud) | ||
69 | // Fraction part register = (Fractional part * 64) + 0.5 | ||
70 | // Baud = 115200. | ||
71 | |||
72 | // For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default. | ||
73 | // Set it to 3Mhz so that we can consistently set the baud rate | ||
74 | if (raspi >= 3) { | ||
75 | // UART_CLOCK = 30000000; | ||
76 | unsigned int r = (unsigned int) (((uint64_t)(&mbox) & ~0xF) | 8); | ||
77 | // Wait until we can talk to the VC. | ||
78 | while (mmio_read(MBOX_STATUS) & 0x80000000); | ||
79 | // Send our message to property channel and wait for the response. | ||
80 | mmio_write(MBOX_WRITE, r); | ||
81 | while ((mmio_read(MBOX_STATUS) & 0x40000000) || | ||
82 | (mmio_read(MBOX_READ) != r)); | ||
83 | } | ||
84 | |||
85 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. | ||
86 | mmio_write(UART0_IBRD, 1); | ||
87 | // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. | ||
88 | mmio_write(UART0_FBRD, 40); | ||
89 | |||
90 | // Enable FIFO & 8 bit data transmission (1 stop bit, no parity). | ||
91 | mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); | ||
92 | |||
93 | // Mask all interrupts. | ||
94 | mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | | ||
95 | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)); | ||
96 | |||
97 | // Enable UART0, receive & transfer part of UART. | ||
98 | mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); | ||
99 | } | ||
100 | |||
101 | void uart_putc(unsigned char c) { | ||
102 | // Wait for UART to become ready to transmit. | ||
103 | while (mmio_read(UART0_FR) & (1 << 5)); | ||
104 | mmio_write(UART0_DR, c); | ||
105 | } | ||
106 | |||
107 | unsigned char uart_getc() { | ||
108 | // Wait for UART to have received something. | ||
109 | while (mmio_read(UART0_FR) & (1 << 4)); | ||
110 | return mmio_read(UART0_DR); | ||
111 | } | ||
112 | |||
113 | void uart_write(const void* buffer, size_t length) { | ||
114 | const uint8_t* bytes = buffer; | ||
115 | for (size_t i = 0; i < length; ++i) { | ||
116 | uart_putc(*bytes++); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | void uart_print(const char* string) { | ||
121 | while (*string) { | ||
122 | uart_putc(*string++); | ||
123 | } | ||
124 | } | ||
125 | |||
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 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <stddef.h> | ||
4 | |||
5 | void uart_init(int raspi); | ||
6 | void uart_putc(unsigned char c); | ||
7 | unsigned char uart_getc(); | ||
8 | void uart_write(const void* buffer, size_t length); | ||
9 | void uart_print(const char* string); | ||
10 | |||