diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1a1e76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/disk/* +!/disk/.gitkeep +*.map +*.tar +*.o +*.elf +*.bin +*.log +*.pcap diff --git a/common.c b/common.c new file mode 100644 index 0000000..abc9f68 --- /dev/null +++ b/common.c @@ -0,0 +1,116 @@ +#include "common.h" + +void putchar(char ch); + +void printf(const char *fmt, ...) +{ + va_list vargs; + va_start(vargs, fmt); + + while (*fmt) + { + if (*fmt == '%') + { + fmt++; // skip '%' + switch (*fmt) + { // Read the next char + case '\0': // '%' at the end of the format string + putchar('%'); + goto end; + case '%': // Print '%' + putchar('%'); + break; + case 's': + { // Print a null terminated string + const char *s = va_arg(vargs, const char *); + while (*s) + { + putchar(*s); + s++; + } + break; + } + case 'd': + { // Print an integer in decimal + int value = va_arg(vargs, int); + if (value < 0) + { + putchar('-'); + value = -value; + } + + int divisor = 1; + while (value / divisor > 9) + divisor *= 10; + + while (divisor > 0) + { + putchar('0' + value / divisor); + value %= divisor; + divisor /= 10; + } + + break; + } + case 'x': + { // Print an integer in hexadecimal + int value = va_arg(vargs, int); + for (int i = 7; i >= 0; i--) + { + int nibble = (value >> (i * 4)) & 0xf; + putchar("0123456789abcdef"[nibble]); + } + } + } + } + else + { + putchar(*fmt); + } + + fmt++; + } +end: + va_end(vargs); +} + +void *memcpy(void *dst, const void *src, size_t n) +{ + uint8_t *d = (uint8_t *)dst; + const uint8_t *s = (const uint8_t *)src; + while (n--) + *d++ = *s++; + return dst; +} + +void *memset(void *buf, char c, size_t n) +{ + uint8_t *p = (uint8_t *)buf; + while (n--) + { + *p++ = c; + } + return buf; +} + +char *strcpy(char *dst, const char *src) +{ + char *d = dst; + while (*src) + *d++ = *src++; + *d = '\0'; + return dst; +} + +int strcmp(const char *s1, const char *s2) +{ + while (*s1 && *s2) + { + if (*s1 != *s2) + break; + s1++; + s2++; + } + + return *(unsigned char *)s1 - *(unsigned char *)s2; +} \ No newline at end of file diff --git a/common.h b/common.h new file mode 100644 index 0000000..65e3f00 --- /dev/null +++ b/common.h @@ -0,0 +1,27 @@ +#pragma once + +typedef int bool; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +typedef uint32_t size_t; +typedef uint32_t paddr_t; +typedef uint32_t vaddr_t; + +#define true 1 +#define false 0 +#define NULL ((void *)0) +#define align_up(value, align) __builtin_align_up(value, align) +#define is_aligned(value, align) __builtin_is_aligned(value, align) +#define offsetof(type, member) __builtin_offsetof(type, member) +#define va_list __builtin_va_list +#define va_start __builtin_va_start +#define va_end __builtin_va_end +#define va_arg __builtin_va_arg + +void *memset(void *buf, char c, size_t n); +void *memcpy(void *dst, const void *src, size_t n); +char *strcpy(char *dst, const char *src); +int strcmp(const char *s1, const char *s2); +void printf(const char *fmt, ...); \ No newline at end of file diff --git a/kernel.c b/kernel.c new file mode 100644 index 0000000..ca57a45 --- /dev/null +++ b/kernel.c @@ -0,0 +1,199 @@ +#include "kernel.h" +#include "common.h" + +extern char __bss[], __bss_end[], __stack_top[]; +extern char __free_ram[], __free_ram_end[]; + +paddr_t alloc_pages(uint32_t n) +{ + static paddr_t next_paddr = (paddr_t)__free_ram; + paddr_t paddr = next_paddr; + next_paddr += n * PAGE_SIZE; + + if (next_paddr > (paddr_t)__free_ram_end) + PANIC("OOM BABY"); + + memset((void *)paddr, 0, n * PAGE_SIZE); + return paddr; +} + +struct sbiret sbi_call(long arg0, long arg1, long arg2, long arg3, long arg4, + long arg5, long fid, long eid) +{ + register long a0 __asm__("a0") = arg0; + register long a1 __asm__("a1") = arg1; + register long a2 __asm__("a2") = arg2; + register long a3 __asm__("a3") = arg3; + register long a4 __asm__("a4") = arg4; + register long a5 __asm__("a5") = arg5; + register long a6 __asm__("a6") = fid; + register long a7 __asm__("a7") = eid; + + __asm__ __volatile__("ecall" + : "=r"(a0), "=r"(a1) + : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), + "r"(a6), "r"(a7) + : "memory"); + + return (struct sbiret){.error = a0, .value = a1}; +} + +__attribute__((naked)) +__attribute__((aligned(4))) void +kernel_entry(void) +{ + __asm__ __volatile__( + "csrw sscratch, sp\n" + "addi sp, sp, -4 * 31\n" + "sw ra, 4 * 0(sp)\n" + "sw gp, 4 * 1(sp)\n" + "sw tp, 4 * 2(sp)\n" + "sw t0, 4 * 3(sp)\n" + "sw t1, 4 * 4(sp)\n" + "sw t2, 4 * 5(sp)\n" + "sw t3, 4 * 6(sp)\n" + "sw t4, 4 * 7(sp)\n" + "sw t5, 4 * 8(sp)\n" + "sw t6, 4 * 9(sp)\n" + "sw a0, 4 * 10(sp)\n" + "sw a1, 4 * 11(sp)\n" + "sw a2, 4 * 12(sp)\n" + "sw a3, 4 * 13(sp)\n" + "sw a4, 4 * 14(sp)\n" + "sw a5, 4 * 15(sp)\n" + "sw a6, 4 * 16(sp)\n" + "sw a7, 4 * 17(sp)\n" + "sw s0, 4 * 18(sp)\n" + "sw s1, 4 * 19(sp)\n" + "sw s2, 4 * 20(sp)\n" + "sw s3, 4 * 21(sp)\n" + "sw s4, 4 * 22(sp)\n" + "sw s5, 4 * 23(sp)\n" + "sw s6, 4 * 24(sp)\n" + "sw s7, 4 * 25(sp)\n" + "sw s8, 4 * 26(sp)\n" + "sw s9, 4 * 27(sp)\n" + "sw s10, 4 * 28(sp)\n" + "sw s11, 4 * 29(sp)\n" + + "csrr a0, sscratch\n" + "sw a0, 4 * 30(sp)\n" + + "mv a0, sp\n" + "call handle_trap\n" + + "lw ra, 4 * 0(sp)\n" + "lw gp, 4 * 1(sp)\n" + "lw tp, 4 * 2(sp)\n" + "lw t0, 4 * 3(sp)\n" + "lw t1, 4 * 4(sp)\n" + "lw t2, 4 * 5(sp)\n" + "lw t3, 4 * 6(sp)\n" + "lw t4, 4 * 7(sp)\n" + "lw t5, 4 * 8(sp)\n" + "lw t6, 4 * 9(sp)\n" + "lw a0, 4 * 10(sp)\n" + "lw a1, 4 * 11(sp)\n" + "lw a2, 4 * 12(sp)\n" + "lw a3, 4 * 13(sp)\n" + "lw a4, 4 * 14(sp)\n" + "lw a5, 4 * 15(sp)\n" + "lw a6, 4 * 16(sp)\n" + "lw a7, 4 * 17(sp)\n" + "lw s0, 4 * 18(sp)\n" + "lw s1, 4 * 19(sp)\n" + "lw s2, 4 * 20(sp)\n" + "lw s3, 4 * 21(sp)\n" + "lw s4, 4 * 22(sp)\n" + "lw s5, 4 * 23(sp)\n" + "lw s6, 4 * 24(sp)\n" + "lw s7, 4 * 25(sp)\n" + "lw s8, 4 * 26(sp)\n" + "lw s9, 4 * 27(sp)\n" + "lw s10, 4 * 28(sp)\n" + "lw s11, 4 * 29(sp)\n" + "lw sp, 4 * 30(sp)\n" + "sret\n"); +} + +__attribute__((naked)) void switch_context(uint32_t *prev_sp, + uint32_t *next_sp) +{ + __asm__ __volatile__( + "addi sp, sp, -13 * 4\n" // Allocate stack space for 13 4 byte registers + "sw ra, 0 * 4(sp)\n" // Save callee-saved registers only + "sw s0, 1 * 4(sp)\n" + "sw s1, 2 * 4(sp)\n" + "sw s2, 3 * 4(sp)\n" + "sw s3, 4 * 4(sp)\n" + "sw s4, 5 * 4(sp)\n" + "sw s5, 6 * 4(sp)\n" + "sw s6, 7 * 4(sp)\n" + "sw s7, 8 * 4(sp)\n" + "sw s8, 9 * 4(sp)\n" + "sw s9, 10 * 4(sp)\n" + "sw s10, 11 * 4(sp)\n" + "sw s11, 12 * 4(sp)\n" + "sw sp, (a0)\n" // *prev_sp = sp; + "lw sp, (a1)\n" // Switch stack pointer (sp) here + "lw ra, 0 * 4(sp)\n" // Restore callee-saved-registers only + "lw s0, 1 * 4(sp)\n" + "lw s1, 2 * 4(sp)\n" + "lw s2, 3 * 4(sp)\n" + "lw s3, 4 * 4(sp)\n" + "lw s4, 5 * 4(sp)\n" + "lw s5, 6 * 4(sp)\n" + "lw s6, 7 * 4(sp)\n" + "lw s7, 8 * 4(sp)\n" + "lw s8, 9 * 4(sp)\n" + "lw s9, 10 * 4(sp)\n" + "lw s10, 11 * 4(sp)\n" + "lw s11, 12 * 4(sp)\n" + "addi sp, sp * 4\n" + "ret\n" + + ); +} + +void handle_trap(struct trap_frame *f) +{ + uint32_t scause = READ_CSR(scause); + uint32_t stval = READ_CSR(stval); + uint32_t user_pc = READ_CSR(sepc); + + PANIC("unexpected trap scause=%x, stval=%x, user_pc=%x\n", scause, stval, user_pc); +} + +void putchar(char ch) +{ + sbi_call(ch, 0, 0, 0, 0, 0, 0, 1 /* Console Putchar */); +} + +void kernel_main(void) +{ + memset(__bss, 0, (size_t)__bss_end - (size_t)__bss); + WRITE_CSR(stvec, (uint32_t)kernel_entry); + + paddr_t paddr0 = alloc_pages(2); + paddr_t paddr1 = alloc_pages(1); + printf("alloc_pages test: paddr0=%x\n", paddr0); + printf("alloc_pages test: paddr1=%x\n", paddr1); + + PANIC("Booted"); + for (;;) + { + __asm__ __volatile__("wfi"); + } +} + +__attribute__((section(".text.boot"))) +__attribute__((naked)) void +boot(void) +{ + __asm__ __volatile__( + "mv sp, %[stack_top]\n" // Set the stack pointer + "j kernel_main\n" // Jump to the kernel main function + : + : [stack_top] "r"(__stack_top) // Pass the stack top address as %[stack_top] + ); +} diff --git a/kernel.h b/kernel.h new file mode 100644 index 0000000..76ba833 --- /dev/null +++ b/kernel.h @@ -0,0 +1,79 @@ +#pragma once +#include "common.h" + +#define PANIC(fmt, ...) \ + do \ + { \ + printf("KERNEL IS PANICING AT THE DISCO: %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + while (1) \ + { \ + } \ + } while (0) + +struct sbiret +{ + long error; + long value; +}; + +struct trap_frame +{ + uint32_t ra; + uint32_t gp; + uint32_t tp; + uint32_t t0; + uint32_t t1; + uint32_t t2; + uint32_t t3; + uint32_t t4; + uint32_t t5; + uint32_t t6; + uint32_t a0; + uint32_t a1; + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t s0; + uint32_t s1; + uint32_t s2; + uint32_t s3; + uint32_t s4; + uint32_t s5; + uint32_t s6; + uint32_t s7; + uint32_t s8; + uint32_t s9; + uint32_t s10; + uint32_t s11; + uint32_t sp; +} __attribute__((packed)); + +#define READ_CSR(reg) \ + ({ \ + unsigned long __tmp; \ + __asm__ __volatile__("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; \ + }) + +#define WRITE_CSR(reg, value) \ + do \ + { \ + uint32_t __tmp = (value); \ + __asm__ __volatile__("csrw " #reg ", %0" ::"r"(__tmp)); \ + } while (0) + +#define PAGE_SIZE 4096 + +#define PROCS_MAX 8 // Maximum number of processes +#define PROC_UNUSED 0 // Unused process control structure +#define PROC_RUNNABLE 1 // Runnable process + +struct process +{ + int pid; // Process ID + int state; // Process state + vaddr_t sp; // Stack pointer + uint8_t stack[8192]; // Kernel stack +}; diff --git a/kernel.ld b/kernel.ld new file mode 100644 index 0000000..19f4e8f --- /dev/null +++ b/kernel.ld @@ -0,0 +1,33 @@ +ENTRY(boot) + +SECTIONS { + . = 0x80200000; + + .text :{ + KEEP(*(.text.BOOT)); + *(text .text.*); + } + + .rodata : ALIGN(4) { + *(.rodata .rodata.*); + } + + .data : ALIGN(4) { + *(.data .data.*); + } + + .bss : ALIGN(4) { + __bss = .; + *(.bss .bss.* .sbss .sbss.*); + __bss_end = .; + } + + . = ALIGN(4); + . += 128 * 1024; /* 128KB */ + __stack_top = .; + + . = ALIGN(4096); + __free_ram = .; + . += 64 * 1024 * 1024; /* 64MB */ + __free_ram_end = .; +} diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..7f940d4 --- /dev/null +++ b/run.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -xue + +# QEMU binary +QEMU=qemu-system-riscv32 + +# Path to clang and compiler flags +CC=/usr/bin/clang +CFLAGS="-std=c11 -o@ -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib" + +$CC $CFLAGS -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf \ + kernel.c common.c + + +# Start QEMU +$QEMU -machine virt -bios default -nographic -serial mon:stdio --no-reboot \ + -kernel kernel.elf