Compare commits

..

No commits in common. "main" and "master" have entirely different histories.
main ... master

36 changed files with 595 additions and 129 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build/
*.o
*.elf

View file

@ -1,36 +1,20 @@
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 -I. CC=aarch64-linux-gnu-gcc
LD=aarch64-linux-gnu-ld
OBJCOPY=aarch64-linux-gnu-objcopy
.SUFFIXES: .s .o .c CFLAGS=-ffreestanding -O2 -nostdlib -Wall
LDFLAGS=-T link.ld
OBJS=startup.o kernel_main.o uart.o scheduler.o smp.o vfs.o shell.o emmc.o mmc_cmds.o profiler.o proc.o virtio_net_dma.o
all: kernel.elf
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
%.o: %.s kernel.elf: $(OBJS)
$(AS) -mcpu=arm926ej-s $< -o $@ $(LD) $(LDFLAGS) -o $@ $(OBJS)
ASM_S_FILES = $(wildcard system/asm/*.s)
ASM_O_FILES = $(ASM_S_FILES:.s=.o)
CFILES = $(wildcard devices/*.c) $(wildcard devices/*/*.c) $(wildcard system/*.c) $(wildcard system/*/*.c)
COFILES = $(CFILES:.c=.o)
OFILES = $(ASM_O_FILES) $(COFILES)
all: kernel.bin
kernel.elf: ${OFILES}
$(LD) -T boot.ld -o $@ $^
kernel.bin: kernel.elf
$(OBJCOPY) -O binary $< $@
clean: clean:
rm -f *.o */*.o */*/*.o *.elf *.bin rm -f *.o kernel.elf
run: kernel.bin
qemu-system-arm -M versatilepb -m 128M -nographic -kernel kernel.bin

BIN
MiniOS.7z

Binary file not shown.

View file

@ -1,63 +1,27 @@
# MiniOS Kernel (ARMv7)
This is a minimal bare-metal ARM kernel project built for educational and prototyping purposes. # MiniOS
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.
--- MiniOS is a simple educational operating system designed for ARM64 (QEMU and Luckfox Lyra B M).
It includes:
## Features - Preemptive multitasking with SMP
- UART console with shell
- MMU and memory protection
- IPC and basic VFS with /proc
- Profiler and task inspector
- Networking (VirtIO + UDP)
- eMMC boot support
- ELF loader
- ARMv7 Bootloader (`boot.s`, `boot.ld`) ## Build
- 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 ```bash
make make
qemu-system-arm -M versatilepb -m 128M -nographic -kernel kernel.bin
``` ```
Once running, you'll see: ## Boot on Luckfox
```bash
load mmc 0:1 0x40200000 kernel.elf
bootelf 0x40200000
``` ```
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.

20
boot.ld
View file

@ -1,20 +0,0 @@
ENTRY(_start)
SECTIONS {
. = 0x8000;
.text : {
*(.text)
}
.data : {
*(.data)
}
.bss : {
*(.bss COMMON)
}
. = ALIGN(8);
stack_top = . + 0x1000;
}

0
boot/boot.its Normal file
View file

5
boot/load_boot.sh Normal file
View file

@ -0,0 +1,5 @@
#!/bin/sh
echo "Loading kernel to 0x40200000..."
load mmc 0:1 0x40200000 kernel.elf
bootelf 0x40200000

View file

@ -1,19 +0,0 @@
#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;
}

View file

@ -1,9 +0,0 @@
#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

8
src/elf.c Normal file
View file

@ -0,0 +1,8 @@
#include "elf.h"
#include "uart.h"
int elf_load(const void *elf_data) {
uart_puts("[ELF] Loading ELF...\n");
// Stub - parse headers, map segments
return 0;
}

6
src/elf.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef ELF_H
#define ELF_H
int elf_load(const void *elf_data);
#endif

43
src/emmc.c Normal file
View file

@ -0,0 +1,43 @@
#include "emmc.h"
#include "uart.h"
#include "mmio.h"
#include "delay.h"
#define EMMC_BASE 0x10005000
#define EMMC_ARG2 (EMMC_BASE + 0x00)
#define EMMC_BLKSZCNT (EMMC_BASE + 0x04)
#define EMMC_CMDTM (EMMC_BASE + 0x08)
#define EMMC_RESP0 (EMMC_BASE + 0x0C)
#define EMMC_DATA (EMMC_BASE + 0x20)
#define EMMC_STATUS (EMMC_BASE + 0x24)
void emmc_init(void) {
uart_print("[emmc] Init sequence...\n");
mmio_write(EMMC_CMDTM, 0x00000000); // Reset command
delay(1000);
uart_print("[emmc] Init done\n");
}
int emmc_read_block(uint32_t lba, uint8_t *buffer) {
mmio_write(EMMC_ARG2, lba);
mmio_write(EMMC_BLKSZCNT, (1 << 16) | 512); // 1 block
mmio_write(EMMC_CMDTM, 0x11200010); // CMD17 read single
while (mmio_read(EMMC_STATUS) & 0x2); // wait for ready
for (int i = 0; i < 128; i++) {
((uint32_t*)buffer)[i] = mmio_read(EMMC_DATA);
}
return 0;
}
int emmc_boot(void) {
uart_print("[emmc] Booting...\n");
uint8_t *image = (uint8_t *)0x8000000;
emmc_read_block(2048, image); // Read first boot block
void (*entry)(void) = (void (*)(void))(image);
entry();
return 0;
}

11
src/emmc.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef EMMC_H
#define EMMC_H
#include <stdint.h>
void emmc_init(void);
int emmc_read_block(uint32_t lba, uint8_t *buffer);
int emmc_boot(void);
#endif

38
src/ipc.c Normal file
View file

@ -0,0 +1,38 @@
#include "ipc.h"
#include "scheduler.h"
#include <string.h>
#define MAX_MSGS 8
#define MAX_TASKS 16
typedef struct {
ipc_msg_t queue[MAX_MSGS];
int head, tail, count;
} ipc_queue_t;
static ipc_queue_t ipc_queues[MAX_TASKS];
void ipc_init(void) {
memset(ipc_queues, 0, sizeof(ipc_queues));
}
int ipc_send(uint64_t to_id, ipc_msg_t msg) {
if (to_id >= MAX_TASKS) return -1;
ipc_queue_t *q = &ipc_queues[to_id];
if (q->count >= MAX_MSGS) return -2;
q->queue[q->tail] = msg;
q->tail = (q->tail + 1) % MAX_MSGS;
q->count++;
return 0;
}
int ipc_recv(ipc_msg_t *msg_out) {
uint64_t id = scheduler_get_current()->id;
ipc_queue_t *q = &ipc_queues[id];
if (q->count == 0) return -3;
*msg_out = q->queue[q->head];
q->head = (q->head + 1) % MAX_MSGS;
q->count--;
return 0;
}

16
src/ipc.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef IPC_H
#define IPC_H
#include <stdint.h>
typedef struct {
uint64_t from;
uint32_t type;
uint64_t data;
} ipc_msg_t;
int ipc_send(uint64_t to_task_id, ipc_msg_t msg);
int ipc_recv(ipc_msg_t *msg_out);
void ipc_init(void);
#endif

13
src/kernel_main.c Normal file
View file

@ -0,0 +1,13 @@
#include "uart.h"
#include "smp.h"
#include "scheduler.h"
void kernel_main(void) {
uart_init();
smp_init();
scheduler_init();
while (1) {
scheduler_schedule();
}
}

9
src/mmc_cmds.c Normal file
View file

@ -0,0 +1,9 @@
#include <stdint.h>
// Placeholder for MMC commands implementation
// To be implemented depending on hardware or simulation platform
int mmc_send_cmd(uint32_t cmd, uint32_t arg) {
return 0;
}

24
src/mmu.c Normal file
View file

@ -0,0 +1,24 @@
#include <stdint.h>
#define PAGE_SIZE 4096
#define PAGE_TABLE_SIZE 4096
#define MAIR_VALUE 0xFF
static uint64_t __attribute__((aligned(PAGE_SIZE))) l1_table[512];
void mmu_init(void) {
for (int i = 0; i < 512; i++) {
l1_table[i] = (i << 30) | (0b11 << 0) | (0b01 << 2);
}
asm volatile("msr mair_el1, %0" :: "r"(MAIR_VALUE));
uint64_t tcr = (16ULL << 0) | (16ULL << 16);
asm volatile("msr tcr_el1, %0" :: "r"(tcr));
uint64_t ttbr = (uint64_t)l1_table;
asm volatile("msr ttbr0_el1, %0" :: "r"(ttbr));
uint64_t sctlr;
asm volatile("mrs %0, sctlr_el1" : "=r"(sctlr));
sctlr |= (1 << 0) | (1 << 2) | (1 << 12);
asm volatile("msr sctlr_el1, %0" :: "r"(sctlr));
asm volatile("isb");
}

6
src/mmu.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef MMU_H
#define MMU_H
void mmu_init(void);
#endif

18
src/proc.c Normal file
View file

@ -0,0 +1,18 @@
#include "uart.h"
#include "scheduler.h"
#include "profiler.h"
void proc_uptime(void) {
extern int global_ticks;
uart_print("Uptime: ");
uart_print_int(global_ticks / 100);
uart_print(" seconds\n");
}
void proc_meminfo(void) {
extern int free_pages;
uart_print("Free memory pages: ");
uart_print_int(free_pages);
uart_print("\n");
}

28
src/profiler.c Normal file
View file

@ -0,0 +1,28 @@
#include "profiler.h"
#include "uart.h"
#define MAX_TASKS 16
static int task_ticks[MAX_TASKS];
void profiler_init(void) {
for (int i = 0; i < MAX_TASKS; i++) task_ticks[i] = 0;
}
void profiler_tick(void) {
extern int current_task_id;
task_ticks[current_task_id]++;
}
void profiler_report(void) {
uart_print("CPU Time per Task:\n");
for (int i = 0; i < MAX_TASKS; i++) {
if (task_ticks[i]) {
uart_print("Task ");
uart_print_int(i);
uart_print(": ");
uart_print_int(task_ticks[i]);
uart_print(" ticks\n");
}
}
}

9
src/profiler.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef PROFILER_H
#define PROFILER_H
void profiler_init(void);
void profiler_tick(void);
void profiler_report(void);
#endif

30
src/scheduler.c Normal file
View file

@ -0,0 +1,30 @@
#include "scheduler.h"
#include <string.h>
#define MAX_TASKS 16
static task_t tasks[MAX_TASKS];
static int task_count = 0;
static task_t *current = NULL;
void scheduler_init(void) {
memset(tasks, 0, sizeof(tasks));
task_count = 0;
current = NULL;
}
void scheduler_add(task_t *t) {
if (task_count < MAX_TASKS) {
tasks[task_count++] = *t;
}
}
task_t *scheduler_get_current(void) {
return current;
}
void scheduler_schedule(void) {
static int i = 0;
current = &tasks[i];
i = (i + 1) % task_count;
}

17
src/scheduler.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef SCHEDULER_H
#define SCHEDULER_H
typedef struct task {
int id;
int state;
void *stack;
void *stack_top;
} task_t;
void scheduler_init(void);
void scheduler_add(task_t *t);
void scheduler_schedule(void);
task_t *scheduler_get_current(void);
#endif

38
src/shell.c Normal file
View file

@ -0,0 +1,38 @@
#include "uart.h"
#include "vfs.h"
#include "scheduler.h"
#include "proc.h"
void shell_loop(void) {
char buf[128];
while (1) {
uart_print("> ");
int i = 0;
while (1) {
char c = uart_getc();
if (c == '\r' || c == '\n') break;
if (i < 127) buf[i++] = c;
uart_putc(c);
}
buf[i] = 0;
uart_print("\n");
if (strcmp(buf, "ps") == 0) {
extern void scheduler_list_tasks(void);
scheduler_list_tasks();
} else if (strcmp(buf, "uptime") == 0) {
proc_uptime();
} else if (strcmp(buf, "meminfo") == 0) {
proc_meminfo();
} else if (strncmp(buf, "cat ", 4) == 0) {
const char *data = vfs_read(buf + 4);
if (data)
uart_print(data);
else
uart_print("File not found\n");
} else {
uart_print("Unknown command\n");
}
}
}

26
src/smp.c Normal file
View file

@ -0,0 +1,26 @@
#include "uart.h"
volatile int core_ready[4];
int get_core_id(void) {
uint64_t mpidr;
asm volatile("mrs %0, mpidr_el1" : "=r"(mpidr));
return mpidr & 0b11;
}
void core_entry(void) {
int cid = get_core_id();
uart_puts("Core ");
uart_putc('0' + cid);
uart_puts(" ready\n");
core_ready[cid] = 1;
while (1);
}
void smp_init(void) {
for (int i = 1; i < 4; i++) {
*(volatile uint64_t *)(0x4000008 + i * 8) = (uint64_t)core_entry;
asm volatile("sev");
}
}

6
src/startup.s Normal file
View file

@ -0,0 +1,6 @@
.section .text
.global _start
_start:
bl kernel_main
b .

26
src/switch.S Normal file
View file

@ -0,0 +1,26 @@
.global context_switch
.type context_switch, %function
context_switch:
// Save callee-saved registers
stp x19, x20, [x0]
stp x21, x22, [x0, #16]
stp x23, x24, [x0, #32]
stp x25, x26, [x0, #48]
stp x27, x28, [x0, #64]
str x29, [x0, #80]
str x30, [x0, #88]
str sp, [x0, #96]
// Load new task context
ldp x19, x20, [x1]
ldp x21, x22, [x1, #16]
ldp x23, x24, [x1, #32]
ldp x25, x26, [x1, #48]
ldp x27, x28, [x1, #64]
ldr x29, [x1, #80]
ldr x30, [x1, #88]
ldr sp, [x1, #96]
ret

32
src/task.c Normal file
View file

@ -0,0 +1,32 @@
#include "scheduler.h"
#include "uart.h"
#include <stdlib.h>
#include <string.h>
#define MAX_TASKS 16
static task_t tasks[MAX_TASKS];
static int task_count = 0;
static task_t *current = NULL;
void scheduler_init(void) {
memset(tasks, 0, sizeof(tasks));
task_count = 0;
current = NULL;
}
void scheduler_add(task_t *t) {
if (task_count < MAX_TASKS) {
tasks[task_count++] = *t;
}
}
task_t *scheduler_get_current(void) {
return current;
}
void scheduler_schedule(void) {
static int i = 0;
current = &tasks[i];
i = (i + 1) % task_count;
}

35
src/uart.c Normal file
View file

@ -0,0 +1,35 @@
#include <stdint.h>
#define UART0_BASE 0x09000000
#define UARTFR (*(volatile uint32_t *)(UART0_BASE + 0x18))
#define UARTDR (*(volatile uint32_t *)(UART0_BASE + 0x00))
void uart_init(void) {}
void uart_putc(char c) {
while (UARTFR & (1 << 5));
UARTDR = c;
}
void uart_puts(const char *s) {
while (*s) uart_putc(*s++);
}
void uart_print(const char *s) {
uart_puts(s);
}
void uart_print_int(int x) {
char buf[16];
int i = 0;
if (x == 0) {
uart_putc('0');
return;
}
while (x > 0) {
buf[i++] = '0' + (x % 10);
x /= 10;
}
while (i--) uart_putc(buf[i]);
}

11
src/uart.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef UART_H
#define UART_H
void uart_init(void);
void uart_putc(char c);
void uart_puts(const char *s);
void uart_print(const char *s);
void uart_print_int(int x);
#endif

12
src/usb.c Normal file
View file

@ -0,0 +1,12 @@
#include "usb.h"
#include "uart.h"
void usb_init(void) {
uart_puts("[USB] Initializing USB Host...\n");
// Placeholder logic - real implementation would probe EHCI/OHCI/XHCI
}
int usb_read_bulk(int device, void *buffer, int length) {
uart_puts("[USB] Reading bulk data...\n");
return -1; // Not implemented
}

7
src/usb.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef USB_H
#define USB_H
void usb_init(void);
int usb_read_bulk(int device, void *buffer, int length);
#endif

29
src/vfs.c Normal file
View file

@ -0,0 +1,29 @@
#include "vfs.h"
#include "uart.h"
#include <string.h>
#include <stdbool.h>
static vfs_file_t files[] = {
{ "/dev/info.txt", "MiniOS v1.0 - Built July 2025\n" },
{ "/proc/version", "MiniOS kernel 1.0\n" },
{ NULL, NULL }
};
void vfs_init(void) {}
const char *vfs_read(const char *path) {
for (int i = 0; files[i].path; i++) {
if (strcmp(files[i].path, path) == 0)
return files[i].content;
}
return NULL;
}
void vfs_list(const char *prefix) {
size_t len = strlen(prefix);
for (int i = 0; files[i].path; i++) {
if (strncmp(files[i].path, prefix, len) == 0)
uart_puts(files[i].path + len + 1), uart_puts("\n");
}
}

14
src/vfs.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef VFS_H
#define VFS_H
typedef struct vfs_file {
const char *path;
const char *content;
} vfs_file_t;
const char *vfs_read(const char *path);
void vfs_list(const char *prefix);
void vfs_init(void);
#endif

46
src/virtio_net_dma.c Normal file
View file

@ -0,0 +1,46 @@
#include "uart.h"
#include "pmm.h"
#include <stdint.h>
#include <string.h>
#define VIRTIO_MMIO_BASE 0x10001000
#define VIRTIO_NET_Q_TX 1
typedef struct {
uint64_t addr;
uint32_t len;
uint16_t flags;
uint16_t next;
} __attribute__((packed)) virtq_desc_t;
typedef struct {
uint16_t flags;
uint16_t idx;
struct {
uint16_t id;
uint16_t len;
} ring[8];
} __attribute__((packed)) virtq_used_t;
static void *desc_area;
void virtio_net_dma_send(const void *data, int len) {
volatile uint32_t *mmio = (volatile uint32_t *)VIRTIO_MMIO_BASE;
virtq_desc_t *desc = (virtq_desc_t *)desc_area;
void *packet = pmm_alloc_page();
memcpy(packet, data, len);
desc[0].addr = (uint64_t)(uintptr_t)packet;
desc[0].len = len;
desc[0].flags = 0;
desc[0].next = 0;
virtq_used_t *used = (virtq_used_t *)((uint8_t *)desc_area + 4096);
used->ring[0].id = 0;
used->ring[0].len = len;
used->idx++;
mmio[0x070 / 4] |= 0x4; // notify
}