commit 549b86adef0de247fd8be896b996ce78a08d1519 Author: uie99917 Date: Mon Apr 7 11:16:01 2025 +0000 Initial commit - full kernel with IRQ, shell and UART diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d9ecf4c --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +CROSS_COMPILE = arm-none-eabi- +AS = $(CROSS_COMPILE)as +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS = -Wall -mcpu=arm926ej-s -nostdlib -ffreestanding + +all: kernel.bin + +kernel.elf: boot.o kernel.o + $(LD) -T boot.ld -o $@ $^ + +kernel.bin: kernel.elf + $(OBJCOPY) -O binary $< $@ + +boot.o: boot.s + $(AS) -mcpu=arm926ej-s $< -o $@ + +kernel.o: kernel.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f *.o *.elf *.bin \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ad7889 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# MiniOS Kernel (ARMv7) + +This is a minimal bare-metal ARM kernel project built for educational and prototyping purposes. +It includes multitasking, a UART shell, a simple task scheduler, and IRQ handling. +Tested and compatible with **Windows 10** using `arm-none-eabi-gcc` and QEMU. + +--- + +## Features + +- ARMv7 Bootloader (`boot.s`, `boot.ld`) +- UART driver (`uart.c`, `uart.h`) +- Task manager with real context switching (`task.c`, `switch.s`) +- Round-robin scheduler triggered by timer IRQ (`irq.s`) +- Interactive shell via UART (`shell.c`) + - Supported commands: + - `ps` – list tasks + - `echo` – simple message + - `halt` – stop execution + +--- + +## Requirements + +- **Windows 10** +- [Git Bash](https://git-scm.com/download/win) +- [GCC ARM Embedded Toolchain](https://developer.arm.com/downloads/-/gnu-rm) +- [QEMU for Windows 64-bit](https://qemu.weilnetz.de/w64/) + +--- + +## How to Build and Run + +```bash +make +qemu-system-arm -M versatilepb -m 128M -nographic -kernel kernel.bin +``` + +Once running, you'll see: + +``` +MiniOS Kernel Booted +MiniOS Shell> +``` + +Type one of the supported commands (`ps`, `echo`, `halt`). + +--- + +## Next Steps (Optional) + +- Virtual File System (VFS) +- `ls`, `cat`, `cd` shell commands +- ELF binary loader +- FAT32 filesystem support +- Advanced multitasking with priorities + +--- + +## Author + +Built by Georgiana using ARM bare-metal architecture. +Powered by QEMU and the GNU ARM Toolchain. \ No newline at end of file diff --git a/boot.ld b/boot.ld new file mode 100644 index 0000000..d2bec33 --- /dev/null +++ b/boot.ld @@ -0,0 +1,20 @@ +ENTRY(_start) + +SECTIONS { + . = 0x8000; + + .text : { + *(.text) + } + + .data : { + *(.data) + } + + .bss : { + *(.bss COMMON) + } + + . = ALIGN(8); + stack_top = . + 0x1000; +} \ No newline at end of file diff --git a/boot.s b/boot.s new file mode 100644 index 0000000..8180aa2 --- /dev/null +++ b/boot.s @@ -0,0 +1,12 @@ +.global _start +.section .text +_start: + // Set up stack pointer + LDR sp, =stack_top + + // Jump to kernel main entry point + BL kernel_main + + // Infinite loop if kernel_main returns +halt: + B halt \ No newline at end of file diff --git a/irq.s b/irq.s new file mode 100644 index 0000000..2820c53 --- /dev/null +++ b/irq.s @@ -0,0 +1,7 @@ +.global irq_handler +.type irq_handler, %function + +irq_handler: + PUSH {r0-r12, lr} + BL task_schedule + POP {r0-r12, pc} \ No newline at end of file diff --git a/kernel.c b/kernel.c new file mode 100644 index 0000000..3703104 --- /dev/null +++ b/kernel.c @@ -0,0 +1,32 @@ +#include "uart.h" +#include "task.h" +#include "shell.h" + +#define TIMER_BASE 0x101E2000 +#define TIMER_LOAD (*(volatile unsigned int *)(TIMER_BASE + 0x00)) +#define TIMER_CTRL (*(volatile unsigned int *)(TIMER_BASE + 0x08)) + +void timer_init() { + TIMER_LOAD = 0x10000; + TIMER_CTRL = (1 << 7) | (1 << 6) | (1 << 5) | 1; +} + +void enable_interrupts() { + __asm__ volatile ( + "mrs r0, cpsr\n" + "bic r0, r0, #0x80\n" + "msr cpsr_c, r0\n" + ); +} + +void kernel_main(void) { + uart_init(); + uart_write("MiniOS Kernel Booted\n"); + + timer_init(); + enable_interrupts(); + + task_init(); + + shell_run(); +} \ No newline at end of file diff --git a/shell.c b/shell.c new file mode 100644 index 0000000..bc657e4 --- /dev/null +++ b/shell.c @@ -0,0 +1,36 @@ +#include "uart.h" +#include "task.h" +#include "shell.h" +#include + +void shell_run() { + char input[64]; + int pos = 0; + + uart_write("MiniOS Shell> "); + + while (1) { + char c = uart_getchar(); + uart_putchar(c); // Echo + if (c == '\r' || c == '\n') { + input[pos] = '\0'; + uart_write("\n"); + + if (strcmp(input, "echo") == 0) { + uart_write("Echo test\n"); + } else if (strcmp(input, "ps") == 0) { + uart_write("Task list: [0] task1, [1] task2\n"); + } else if (strcmp(input, "halt") == 0) { + uart_write("System halted.\n"); + while (1); + } else { + uart_write("Unknown command\n"); + } + + pos = 0; + uart_write("MiniOS Shell> "); + } else { + input[pos++] = c; + } + } +} \ No newline at end of file diff --git a/shell.h b/shell.h new file mode 100644 index 0000000..318202c --- /dev/null +++ b/shell.h @@ -0,0 +1,6 @@ +#ifndef SHELL_H +#define SHELL_H + +void shell_run(void); + +#endif \ No newline at end of file diff --git a/startup.s b/startup.s new file mode 100644 index 0000000..1c75b92 --- /dev/null +++ b/startup.s @@ -0,0 +1,9 @@ +.global _start +.section .text +_start: + LDR sp, =stack_top + BL kernel_main + B . + +.org 0x18 + B irq_handler \ No newline at end of file diff --git a/switch.s b/switch.s new file mode 100644 index 0000000..ee655bd --- /dev/null +++ b/switch.s @@ -0,0 +1,18 @@ +.global context_switch +.type context_switch, %function + +context_switch: + // r0 = current task context + // r1 = next task context + + // Save r4-r12 + STMIA r0!, {r4-r12} + STR sp, [r0] + STR lr, [r0, #4] + + // Restore r4-r12 + LDMIA r1!, {r4-r12} + LDR sp, [r1] + LDR lr, [r1, #4] + + BX lr \ No newline at end of file diff --git a/task.c b/task.c new file mode 100644 index 0000000..a69d4f7 --- /dev/null +++ b/task.c @@ -0,0 +1,26 @@ +#include "task.h" + +task_t tasks[MAX_TASKS]; +int current_task = 0; + +void task1() { + while (1); +} + +void task2() { + while (1); +} + +void task_init() { + tasks[0].context.sp = (unsigned int)&tasks[0].stack[255]; + tasks[0].context.lr = (unsigned int)task1; + + tasks[1].context.sp = (unsigned int)&tasks[1].stack[255]; + tasks[1].context.lr = (unsigned int)task2; + + current_task = 0; +} + +void task_schedule() { + current_task = (current_task + 1) % MAX_TASKS; +} \ No newline at end of file diff --git a/task.h b/task.h new file mode 100644 index 0000000..032293e --- /dev/null +++ b/task.h @@ -0,0 +1,22 @@ +#ifndef TASK_H +#define TASK_H + +#define MAX_TASKS 2 + +typedef struct { + unsigned int regs[13]; // r0-r12 + unsigned int sp; + unsigned int lr; +} context_t; + +typedef struct { + context_t context; + unsigned int stack[256]; +} task_t; + +void task_init(void); +void task_schedule(void); +extern task_t tasks[MAX_TASKS]; +extern int current_task; + +#endif \ No newline at end of file diff --git a/uart.c b/uart.c new file mode 100644 index 0000000..cb17286 --- /dev/null +++ b/uart.c @@ -0,0 +1,19 @@ +#include "uart.h" + +#define UART_BASE ((volatile char *)0x101f1000) + +void uart_init(void) {} + +void uart_write(const char *msg) { + while (*msg) { + *UART_BASE = *msg++; + } +} + +void uart_putchar(char c) { + *UART_BASE = c; +} + +char uart_getchar(void) { + return *UART_BASE; +} \ No newline at end of file diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..cfcdd7e --- /dev/null +++ b/uart.h @@ -0,0 +1,9 @@ +#ifndef UART_H +#define UART_H + +void uart_init(void); +void uart_write(const char *msg); +void uart_putchar(char c); +char uart_getchar(void); + +#endif \ No newline at end of file