Re: Raspberry PI Bare Bones OS
Posted: Sun May 30, 2021 7:09 pm
Thanks for the input, I looked a bit into it, if it becomes a better option and GCC doesn't work for me I might give it a shot.if gcc is so messy, byzantine and just f&cked up with this respect, maybe try clang as already suggested? for example, I got it on my Windows machine, messed around a bit with its (pretty sucky, admittedly) documentation and after a short time - was able to compile an aarch64 PE image of my UEFI OS Loader for ARM boards. and it worked just like the MSVC build. these two look like being crosscompilers by design and they let you compile/link even freestanding things pretty easy. ethin gave you a useful link. it's incomplete unfortunately, because, well lines like "target maybe A, B, C, etc" is not what one expects from a reference. should I guess, what that "etc" hides? if you don't like the idea of having a dozen of gcc builds for everything, and want to do your work on RPi, then your answer is clang.
---
So, after doing some tinkering around I decided to attempt using local (not cross-compiler) gcc, see if that works.
This is my boot.S file:
Code: Select all
// 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 - machine id
// 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
Code: Select all
#include <stddef.h>
#include <stdint.h>
static uint32_t MMIO_BASE;
// The MMIO area base address, depends on board type
static inline void mmio_init(int raspi)
{
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.
}
}
// Memory-Mapped I/O output
static inline void mmio_write(uint32_t reg, uint32_t data)
{
*(volatile uint32_t*)(MMIO_BASE + reg) = data;
}
// Memory-Mapped I/O input
static inline uint32_t mmio_read(uint32_t reg)
{
return *(volatile uint32_t*)(MMIO_BASE + 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");
}
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),
// 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
volatile unsigned int __attribute__((aligned(16))) mbox[9] = {
9*4, 0, 0x38002, 12, 8, 2, 3000000, 0 ,0
};
void uart_init(int raspi)
{
mmio_init(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)(&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
{
// initialize UART for Raspi2
uart_init(3);
uart_puts("Hello, kernel World!\r\n");
while (1)
uart_putc(uart_getc());
}
Code: Select all
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 = .;
}
Code: Select all
gcc -mcpu=cortex-a53 -fpic -ffreestanding -c boot.S -o boot.o
gcc -mcpu=cortex-a53 -fpic -ffreestanding -std=gnu99 -c kernel.c -o kernel.o -O2 -Wall -Wextra
gcc -T linker.ld -o myos.elf -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc
objcopy myos.elf -O binary kernel7.img
any idea what the problem could be?