commit - /dev/null
commit + 77fdb85750d58975361feae858277cc65c1906d5
blob - /dev/null
blob + 8d8ef3c2eba2dcd44f5d2ff4f5832380787b6a47 (mode 644)
--- /dev/null
+++ LICENSE
+BSD 2-Clause License
+
+Copyright (c) 2025, nastonen
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
blob - /dev/null
blob + 50a8320ca074316ca0d0f01836c3890a5b2dfa43 (mode 644)
--- /dev/null
+++ README.md
+# SystemN
+My take on RISC-V UNIX clone.<br />
+<br />
+Install qemu-system-riscv64<br />
+cd kernel<br />
+make clean<br />
+./build_shell.sh<br />
+make run [NODEBUG=1]<br />
blob - /dev/null
blob + 96e84e62e4bcdaa099ff01f9dad5d103b73050a3 (mode 644)
--- /dev/null
+++ kernel/Makefile
+# Toolchain prefix
+CROSS = riscv64-elf-
+
+# Tools
+CC = $(CROSS)gcc
+LD = $(CROSS)ld
+OBJCOPY = $(CROSS)objcopy
+OBJDUMP = $(CROSS)objdump
+
+# Files
+OBJS = entry.o start.o uart.o spinlock.o proc.o trap/trap.o \
+ trap/trap_vec.o string.o sched.o swtch.o shell.o mm/mem.o \
+ mm/snub.o mm/pagetable.o syscall.o fs/vfs.o
+TARGET = kernel
+LINKER = kernel.ld
+
+# Flags
+CFLAGS = -march=rv64g -mabi=lp64 -nostdlib -fno-pic -mno-relax \
+ -mcmodel=medany -Wall -Wextra -O0 $(DEBUG_FLAGS)
+#-g
+
+ifeq ($(NODEBUG), 1)
+DEBUG_FLAGS =
+else
+DEBUG_FLAGS = -DDEBUG
+endif
+
+LDFLAGS = -T $(LINKER)
+
+#all: $(TARGET).bin
+
+# Compile .c and .S files
+%.o: %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $< -o $@
+
+# Link the ELF
+$(TARGET).elf: $(OBJS) $(LINKER)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS)
+
+# Convert to raw binary
+#$(TARGET).bin: $(TARGET).elf
+# $(OBJCOPY) -O binary $< $@
+
+# Disassemble (optional)
+dump: $(TARGET).elf
+ $(OBJDUMP) -D $(TARGET).elf > $(TARGET).asm
+
+# Run in QEMU
+run: $(TARGET).elf
+ qemu-system-riscv64 -machine virt \
+ -smp 4 \
+ -bios none \
+ -nographic \
+ -cpu rv64,zihintpause=true \
+ -monitor none \
+ -serial stdio \
+ -display none \
+ -kernel $(TARGET).elf
+# -d int -D log.txt
+# -d int,cpu -D log.txt
+# -S -gdb tcp::1234
+
+clean:
+ rm -f $(shell find . -name '*.o' -o -name '*.elf' -o -name '*.bin' -o -name '*.asm')
+
+
+.PHONY: all clean dump run
blob - /dev/null
blob + 33070ad973dc761fb7297d6a155f8c18ef66ed5e (mode 644)
--- /dev/null
+++ kernel/TODO
+- 'write' syscall to print whole string at a time
+- schedule on timer interrupts
+- console driver + buffered printk
+- non-linear mm mapping
blob - /dev/null
blob + 9a3ad2fdac2044ddf23e56e538675405cced97a9 (mode 755)
--- /dev/null
+++ kernel/build_shell.sh
+#!/bin/zsh
+
+riscv64-elf-gcc -nostdlib -T user.ld -o shell.elf shell.c
+riscv64-elf-objcopy -O binary -j .text shell.elf shell.bin
+riscv64-elf-ld -r -b binary -o shell.o shell.bin
+
+#riscv64-elf-nm shell.elf | grep ' T '
+#-T user.ld
+#-Ttext=0x80200000 -e _start
blob - /dev/null
blob + 296c044be5939c8f28ea1a638288d826cdbb5668 (mode 644)
--- /dev/null
+++ kernel/entry.S
+.section .text
+.global _entry
+_entry:
+ # Set up a stack for C
+ # sp = _stack + ((hartId + 1) * 4096)
+ csrr t0, mhartid
+ addi t0, t0, 1
+ li t1, 4096
+ mul t0, t0, t1
+ la t1, _stack
+ add sp, t1, t0
+
+ call start
+
+spin:
+ j spin
+
+# Stack section: 4KB × 4 harts = 16KB
+.section .bss
+.align 16
+.global _stack
+_stack:
+ .space 4096 * 4
blob - /dev/null
blob + d59d6cfe8e223c7acc5d7ac01e7ee972982ddfe8 (mode 644)
--- /dev/null
+++ kernel/fs/ramfs.c
+#include "ramfs.h"
+
+struct ramfs_file {
+ char name[MAX_NAME_LEN];
+ char *data;
+ ulong size;
+ uint mode; // VFS_MODE_FILE or VFS_MODE_DIR
+ inode_t *inode;
+} ramfs_file_t;
+
+static ramfs_file_t file_table[MAX_OPEN_FILES];
+static ulong next_inode_no = 1;
+
+static filesystem_t ramfs_fs;
+static inode_t root_inode;
+
+static ramfs_file_t *
+ramfs_find(const char *name)
+{
+ for (int i = 0; i < RAMFS_MAX_FILES; i++)
+ if (file_table[i].inode && strcmp(file_table[i].name, name) == 0)
+ return &file_table[i];
+
+ return NULL;
+}
+
+static inode_t *
+ramfs_lookup(inode_t *dir, const char *name)
+{
+ ramfs_file_t *f = ramfs_find(name);
+ return f ? f->inode : NULL;
+}
+
+static int
+ramfs_read(inode_t *inode, ulong offset, void *buf, ulong len)
+{
+ ramfs_file_t *f = (ramfs_file_t *)inode->internal;
+ if (!f || f->mode != VFS_MODE_FILE)
+ return -1;
+
+ if (offset >= f->size)
+ return 0;
+
+ ulong to_read = (offset + len > f->size) ? (f->size - offset) : len;
+ memcpy(buf, f->data + offset, to_read);
+
+ return to_read;
+}
+
+static int
+ramfs_write(inode_t *inode, ulong offset, const void *buf, ulong len)
+{
+ ramfs_file_t *f = (ramfs_file_t *)inode->internal;
+ if (!f || f->mode != VFS_MODE_FILE)
+ return -1;
+
+ ulong new_size = offset + len;
+ if (new_size > f->size) {
+ char *new_data = realloc(f->data, new_size);
+ if (!new_data)
+ return -1;
+
+ f->data = new_data;
+ f->size = new_size;
+ inode->size = new_size;
+ }
+
+ memcpy(f->data + offset, buf, len);
+
+ return len;
+}
+
+static int
+ramfs_create(inode_t *dir, const char *name, uint mode)
+{
+ for (int i = 0; i < RAMFS_MAX_FILES; i++) {
+ if (!file_table[i].inode) {
+ ramfs_file_t *f = &file_table[i];
+ strncpy(f->name, name, RAMFS_NAME_LEN);
+ f->mode = mode;
+ f->data = NULL;
+ f->size = 0;
+
+ inode_t *inode = malloc(sizeof(inode_t));
+ if (!inode)
+ return -1;
+
+ inode->inode_no = next_inode_no++;
+ inode->mode = mode;
+ inode->size = 0;
+ inode->fs = &ramfs_fs;
+ inode->internal = f;
+
+ f->inode = inode;
+
+ return 0;
+ }
+ }
+
+ return -1; // No space
+}
+
+static int
+ramfs_mkdir(inode_t *dir, const char *name)
+{
+ // For now, don't support nested directories
+ return -1;
+}
+
+static filesystem_ops_t ramfs_ops = {
+ .lookup = ramfs_lookup,
+ .read = ramfs_read,
+ .write = ramfs_write,
+ .create = ramfs_create,
+ .mkdir = ramfs_mkdir
+};
+
+filesystem_t *
+ramfs_create(void)
+{
+ memset(&file_table, 0, sizeof(file_table));
+ memset(&root_inode, 0, sizeof(inode_t));
+
+ root_inode.inode_no = 0;
+ root_inode.mode = VFS_MODE_DIR;
+ root_inode.size = 0;
+ root_inode.fs = &ramfs_fs;
+ root_inode.internal = NULL;
+
+ ramfs_fs.name = "ramfs";
+ ramfs_fs.ops = &ramfs_ops;
+ ramfs_fs.root = &root_inode;
+
+ return &ramfs_fs;
+}
blob - /dev/null
blob + c21e2c2107084ed33dd6e58aa89c9a5d32e1cddd (mode 644)
--- /dev/null
+++ kernel/fs/ramfs.h
+#pragma once
+
+#include "vfs.h"
+
+filesystem_t *ramfs_create(void);
blob - /dev/null
blob + 4ea43ad095fb441e96d52332837c4906668bf68b (mode 644)
--- /dev/null
+++ kernel/fs/vfs.c
+#include "vfs.h"
+#include "../mm/snub.h"
+
+static filesystem_t *root_fs;
+static file_t *open_files[MAX_OPEN_FILES];
+
+int
+vfs_install_fd(file_t *f)
+{
+ for (int i = 0; i < MAX_OPEN_FILES; i++) {
+ if (open_files[i] == NULL) {
+ open_files[i] = f;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+file_t *
+vfs_get_file(int fd)
+{
+ if (fd < 0 || fd >= MAX_OPEN_FILES)
+ return NULL;
+
+ return open_files[fd];
+}
+
+void
+vfs_init(void)
+{
+ // Currently nothing
+}
+
+int
+vfs_mount(filesystem_t *fs)
+{
+ if (!fs || !fs->root)
+ return -1;
+
+ root_fs = fs;
+
+ return 0;
+}
+
+inode_t *
+vfs_lookup(const char *path)
+{
+ if (!path || path[0] != '/' || !root_fs)
+ return NULL;
+
+ inode_t *curr = root_fs->root;
+ char temp[MAX_PATH_LEN];
+ strlcpy(temp, path, MAX_PATH_LEN);
+ char *token = strtok(temp, "/");
+
+ while (token && curr && (curr->mode & VFS_MODE_DIR)) {
+ curr = curr->fs->ops->lookup(curr, token);
+ token = strtok(NULL, "/");
+ }
+
+ return curr;
+}
+
+int
+vfs_open(const char *path)
+{
+ inode_t *node = vfs_lookup(path);
+ if (!node)
+ return -1;
+
+ file_t *f = (file_t *)kmalloc(sizeof(file_t));
+ if (!f)
+ return -1;
+
+ f->inode = node;
+ f->offset = 0;
+ f->flags = 0;
+
+ return vfs_install_fd(f);
+}
+
+int
+vfs_read(int fd, void* buf, ulong len)
+{
+ file_t *f = vfs_get_file(fd);
+ if (!f || !f->inode || !(f->inode->mode & VFS_MODE_FILE))
+ return -1;
+
+ int n = f->inode->fs->ops->read(f->inode, f->offset, buf, len);
+ if (n > 0)
+ f->offset += n;
+
+ return n;
+}
+
+int
+vfs_write(int fd, const void *buf, ulong len)
+{
+ file_t *f = vfs_get_file(fd);
+ if (!f || !f->inode || !(f->inode->mode & VFS_MODE_FILE))
+ return -1;
+
+ int n = f->inode->fs->ops->write(f->inode, f->offset, buf, len);
+ if (n > 0)
+ f->offset += n;
+
+ return n;
+}
+
+int
+vfs_create(const char *path, uint mode)
+{
+ if (!path || !root_fs)
+ return -1;
+
+ const char *name = path + 1; // Assume single-level path for now
+ return root_fs->ops->create(root_fs->root, name, mode);
+}
+
+int
+vfs_mkdir(const char *path)
+{
+ if (!path || !root_fs)
+ return -1;
+
+ const char *name = path + 1;
+ return root_fs->ops->mkdir(root_fs->root, name);
+}
blob - /dev/null
blob + 6e83c0638b3493093cfb91bb7d054e212255855f (mode 644)
--- /dev/null
+++ kernel/fs/vfs.h
+#pragma once
+
+#include "../string.h"
+
+#define MAX_NAME_LEN 64
+#define MAX_PATH_LEN 256
+#define MAX_OPEN_FILES 64
+
+// File types and mode flags
+#define VFS_MODE_DIR 0x4000
+#define VFS_MODE_FILE 0x8000
+
+// Filesystem operation table
+typedef struct filesystem_ops {
+ struct inode *(*lookup)(struct inode *dir, const char *name);
+ int (*read)(struct inode *inode, ulong offset, void *buf, ulong len);
+ int (*write)(struct inode *inode, ulong offset, const void *buf, ulong len);
+ int (*create)(struct inode *dir, const char *name, uint mode);
+ int (*mkdir)(struct inode *dir, const char *name);
+} filesystem_ops_t;
+
+// Core inode structure
+typedef struct inode {
+ ulong inode_no;
+ ulong size;
+ uint mode;
+ struct filesystem *fs;
+ void *internal; // FS-specific pointer
+} inode_t;
+
+// Directory entry
+typedef struct dentry {
+ char name[MAX_NAME_LEN];
+ struct inode *inode;
+} dentry_t;
+
+// Open file handle
+typedef struct file {
+ struct inode *inode;
+ ulong offset;
+ int flags;
+} file_t;
+
+// Filesystem mountpoint
+typedef struct filesystem {
+ const char *name;
+ struct filesystem_ops *ops;
+ struct inode *root;
+} filesystem_t;
+
+void vfs_init(void);
+int vfs_mount(struct filesystem *fs);
+
+struct inode *vfs_lookup(const char *path);
+int vfs_open(const char *path);
+int vfs_read(int fd, void *buf, ulong len);
+int vfs_write(int fd, const void *buf, ulong len);
+int vfs_create(const char *path, uint mode);
+int vfs_mkdir(const char *path);
+
+// FD helpers
+int vfs_install_fd(struct file *f);
+struct file *vfs_get_file(int fd);
blob - /dev/null
blob + 7506650800767704be1adcdaebe47cfdf46b8555 (mode 644)
--- /dev/null
+++ kernel/kernel.ld
+ENTRY(_entry)
+
+SECTIONS
+{
+ . = 0xffffffff80000000; /* Starting address of the kernel */
+
+ .text : AT(0x80000000) {
+ _kernel_text_start = .;
+ *(.text .text.*) /* All code goes here */
+ _kernel_text_end = .;
+ }
+
+ .rodata : ALIGN(0x1000) {
+ _kernel_rodata_start = .;
+ *(.rodata .rodata.*) /* Read-only data goes here */
+ _kernel_rodata_end = .;
+ }
+
+ .data : ALIGN(0x1000) {
+ _kernel_data_start = .;
+ *(.*data .*data.*) /* All initialized data goes here */
+ }
+
+ .bss : ALIGN(0x1000) {
+ *(.*bss .*bss.*) /* All uninitialized data goes here */
+ }
+
+ _kernel_end = .;
+}
blob - /dev/null
blob + 947c539792d3e3a577600c0ad8457c15faaab5e4 (mode 644)
--- /dev/null
+++ kernel/list.h
+#pragma once
+
+typedef struct list_node {
+ struct list_node *prev;
+ struct list_node *next;
+} list_node_t;
+
+#define LIST_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD_INIT(ptr) do { \
+ (ptr)->next = (ptr); \
+ (ptr)->prev = (ptr); \
+} while (0)
+
+// Add node after head
+static inline void
+list_add(list_node_t *new, list_node_t *head)
+{
+ new->next = head->next;
+ new->prev = head;
+ head->next->prev = new;
+ head->next = new;
+}
+
+// Add node after tail
+static inline void
+list_add_tail(list_node_t *new, list_node_t *head)
+{
+ new->next = head;
+ new->prev = head->prev;
+ head->prev->next = new;
+ head->prev = new;
+}
+
+static inline void
+list_del(list_node_t *entry)
+{
+ entry->next->prev = entry->prev;
+ entry->prev->next = entry->next;
+ entry->next = entry->prev = NULL;
+}
+
+static inline int
+list_empty(const list_node_t *head)
+{
+ return head->next == head;
+}
+
+static inline int
+list_in_queue(list_node_t *node)
+{
+ return node->next && node->prev;
+}
+
+
+// Iterate over list
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+// Iterate over list safely during deletion
+#define list_for_each_safe(pos, tmp, head) \
+ for (pos = (head)->next, tmp = pos->next; pos != (head); \
+ pos = tmp, tmp = pos->next)
+
+// Get the containing struct from node
+#define container_of(ptr, type, member) \
+ ((type *)((char *)(ptr) - (char *)(&((type *)NULL)->member)))
blob - /dev/null
blob + b29094bf7efc879c49d53c4ff7b8f8b98a649d6d (mode 644)
--- /dev/null
+++ kernel/mm/mapping.h
+#pragma once
+
+#define KERNEL_START 0x80000000
+#define KERNEL_END 0x80800000 // 8MB for kernel space
+#define USER_START KERNEL_END
+#define USER_END 0x88000000 // 120MB for user space
+
+#define KERNEL_START_VA 0xffffffff80000000 // Map kernel to high virtual memory
+#define KERNEL_END_VA 0xffffffff80800000 // 8MB for kernel space
+#define USER_START_VA 0x0
+#define USER_END_VA 0x7800000 // 120MB for user space
+
+#define VA_OFFSET (KERNEL_START_VA - KERNEL_START)
blob - /dev/null
blob + 97a32a17cfacab9599ad7f31b99d76cb33ee43d6 (mode 644)
--- /dev/null
+++ kernel/mm/mem.c
+#include "mem.h"
+#include "../spinlock.h"
+#include "../uart.h"
+
+extern char _kernel_end[];
+static spinlock_t buddy_lock = SPINLOCK_INIT;
+static ulong num_pages;
+static page_t *page_array;
+static page_t *free_lists[MAX_ORDER + 1];
+ulong va_offset;
+ulong buddy_base_phys;
+pte_t *kernel_pagetable;
+
+static inline page_t *
+get_page_struct(void *pa)
+{
+ ulong idx = ((ulong)pa - buddy_base_phys) / PAGE_SIZE;
+ if (idx >= num_pages) {
+ uart_puts("get_page_struct: INVALID idx: ");
+ uart_putlong(idx);
+ uart_putc('\n');
+ while (1)
+ asm volatile("wfi");
+ }
+ return &page_array[idx];
+}
+
+
+static inline void *
+page_addr(ulong idx)
+{
+ if (idx >= num_pages) {
+ spin_lock(&uart_lock);
+ uart_puts("page_addr: INVALID IDX ");
+ uart_putlong(idx);
+ uart_putc('\n');
+ spin_unlock(&uart_lock);
+ while (1)
+ asm volatile("wfi");
+ }
+ return (void *)(buddy_base_phys + idx * PAGE_SIZE);
+}
+
+static inline void *
+buddy_of(void *addr, int order)
+{
+ ulong block = (ulong)addr - buddy_base_phys;
+ ulong size = PAGE_SIZE << order;
+ ulong buddy = block ^ size;
+
+ return (void *)(buddy + buddy_base_phys);
+}
+
+static inline void *
+early_alloc(ulong size)
+{
+ // Align size
+ size = ALIGN_UP(size);
+
+ if (!buddy_base_phys)
+ buddy_base_phys = ALIGN_UP((ulong)_kernel_end);
+
+ void *addr = (void *)buddy_base_phys;
+ buddy_base_phys += size;
+ return addr;
+}
+
+int
+buddy_allocator_init()
+{
+ /*
+ DEBUG_PRINT(
+ uart_puts("Initializing buddy allocator...\n");
+ );
+ */
+
+ // Allocate kernel pagetable
+ kernel_pagetable = early_alloc(PAGE_SIZE);
+ memset((void *)kernel_pagetable, 0, PAGE_SIZE);
+
+ // Calculate memory range
+ ulong phys_start = buddy_base_phys;
+ ulong phys_end = USER_END;
+
+ // Estimate max pages, accounting for metadata per page
+ ulong per_page_cost = PAGE_SIZE + sizeof(page_t);
+ ulong estimated_pages = (phys_end - phys_start) / per_page_cost;
+
+ // Allocate page_array metadata
+ page_array = early_alloc(estimated_pages * sizeof(page_t));
+ memset(page_array, 0, estimated_pages * sizeof(page_t));
+
+ // Align and update the start of page memory
+ buddy_base_phys = ALIGN_UP(buddy_base_phys);
+ phys_start = buddy_base_phys;
+
+ // Calculate how many pages fit now
+ num_pages = (USER_END - buddy_base_phys) / PAGE_SIZE;
+
+ // Initialize free lists
+ for (int i = 0; i <= MAX_ORDER; i++)
+ free_lists[i] = NULL;
+
+ // Populate initial free blocks
+ ulong curr = phys_start;
+ while (curr + PAGE_SIZE <= phys_end) {
+ int order = MAX_ORDER;
+
+ // Align block and ensure space
+ while (order > 0 &&
+ (curr + (PAGE_SIZE << order) > phys_end ||
+ curr & ((PAGE_SIZE << order) - 1))) {
+ order--;
+ }
+
+ page_t *pg = get_page_struct((void *)curr);
+ pg->order = order;
+ pg->is_free = true;
+ pg->next = free_lists[order];
+ free_lists[order] = pg;
+
+ curr += PAGE_SIZE << order;
+ }
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Buddy allocator ready.\n");
+ );
+ */
+
+ return 0;
+}
+
+void *
+alloc_page(void)
+{
+ return alloc_pages(0);
+}
+
+void *
+alloc_pages(int order)
+{
+ if (order > MAX_ORDER)
+ return NULL;
+
+ // Lock spinlock
+ spin_lock(&buddy_lock);
+
+ for (int o = order; o <= MAX_ORDER; o++) {
+ if (!free_lists[o])
+ continue;
+
+ // Pop from freelist
+ page_t *block = free_lists[o];
+ page_t *block_va = (page_t *)PHYS_TO_VIRT(free_lists[o]);
+ block_va->order = order;
+ block_va->is_free = false;
+ free_lists[o] = (page_t *)block_va->next;
+
+ // Split higher orders
+ int block_idx = block - page_array;
+
+ for (int curr = o; curr > order; curr--) {
+ uint buddy_idx = block_idx + (1 << (curr - 1));
+ if (buddy_idx >= num_pages) {
+ uart_puts("ERROR: buddy_idx out of bounds\n");
+ while (1)
+ asm volatile("wfi");
+ }
+
+ page_t *buddy = (page_t *)PHYS_TO_VIRT(&page_array[buddy_idx]);
+ buddy->order = curr - 1;
+ buddy->is_free = true;
+ buddy->next = free_lists[curr - 1];
+ free_lists[curr - 1] = (page_t *)VIRT_TO_PHYS(buddy);
+ }
+
+ // Get the address
+ void *addr = page_addr(block_idx);
+
+ // Unlock spinlock
+ spin_unlock(&buddy_lock);
+
+ // Zero the memory
+ memset(PHYS_TO_VIRT(addr), 0, PAGE_SIZE << order);
+
+ return addr;
+ }
+
+ // Unlock spinlock
+ spin_unlock(&buddy_lock);
+
+ return NULL;
+}
+
+void
+free_page(void *ptr)
+{
+ free_pages(ptr);
+}
+
+void
+free_pages(void *addr)
+{
+ if (!addr)
+ return;
+
+ // Lock spinlock
+ spin_lock(&buddy_lock);
+
+ page_t *pg = get_page_struct(addr);
+
+ if (pg->is_free) {
+ spin_unlock(&buddy_lock);
+ return;
+
+ /*
+ spin_lock(&uart_lock);
+ uart_puts("Double free detected at: ");
+ uart_puthex((ulong)addr);
+ uart_putc('\n');
+ spin_unlock(&uart_lock);
+ while (1)
+ asm volatile("wfi");
+ */
+ }
+
+ int order = pg->order;
+ pg->is_free = true;
+
+ while (order < MAX_ORDER) {
+ void *buddy_addr = buddy_of(addr, order);
+ page_t *buddy = get_page_struct(buddy_addr);
+
+ if (!buddy->is_free || buddy->order != order)
+ break;
+
+ // Remove buddy from freelist
+ page_t **p = &free_lists[order];
+ while (*p && *p != buddy)
+ p = &(*p)->next;
+
+ if (*p == buddy)
+ *p = buddy->next;
+
+ buddy->is_free = false;
+ if ((ulong)addr > (ulong)buddy_addr)
+ addr = buddy_addr;
+
+ order++;
+ }
+
+ page_t *merged = get_page_struct(addr);
+ merged->order = order;
+ merged->is_free = true;
+ merged->next = free_lists[order];
+ free_lists[order] = merged;
+
+ // Unlock spinlock
+ spin_unlock(&buddy_lock);
+}
blob - /dev/null
blob + 8a8a4dab597b949f1e1073a7a9ea65d31dfb1fb3 (mode 644)
--- /dev/null
+++ kernel/mm/mem.h
+#pragma once
+
+#include "../types.h"
+#include "../string.h"
+#include "mapping.h"
+
+#define PAGE_SIZE 4096
+
+#define VA_OFFSET (KERNEL_START_VA - KERNEL_START)
+
+#define PHYS_TO_VIRT(pa) ((void *)((ulong)(pa) + va_offset))
+#define VIRT_TO_PHYS(va) ((ulong)(va) - va_offset)
+
+#define KSTACK_SIZE PAGE_SIZE
+#define USER_STACK_SIZE PAGE_SIZE
+#define USER_STACK_TOP USER_END_VA
+#define USER_STACK_BASE (USER_END_VA - USER_STACK_SIZE)
+#define USER_HEAP_END (USER_STACK_BASE - PAGE_SIZE) // Leave 4KB guard
+
+#define MAX_ORDER 10 // Max 2^10 pages = 4MB
+#define PAGE_ENTRIES (PAGE_SIZE / sizeof(pte_t)) // 4096 / 8 = 512
+#define ALIGN_UP(addr) (((addr) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
+
+// Extract physical address from a PTE and vice versa
+#define PAGE_SHIFT 12
+#define PTE2PA(pte) (((pte) >> 10) << 12)
+#define PA2PTE(pa) (((ulong)(pa) >> PAGE_SHIFT) << 10)
+
+// Extract permission/flag bits from a PTE
+#define PTE_FLAGS(pte) ((pte) & 0x3FF) // bits [9:0]
+
+typedef struct page {
+ uchar order; // Power-of-two order (0–10)
+ uchar is_free;
+ struct page *next;
+} page_t;
+
+int buddy_allocator_init();
+void *alloc_page(void);
+void *alloc_pages(int order);
+void free_page(void *ptr);
+void free_pages(void *addr);
+
+extern pte_t *kernel_pagetable;
+extern ulong buddy_base_phys;
+extern ulong va_offset;
blob - /dev/null
blob + 7de7cff36c5825c82714ca2d4b2a9cc62ee63baa (mode 644)
--- /dev/null
+++ kernel/mm/pagetable.c
+#include "pagetable.h"
+#include "../riscv.h"
+#include "../uart.h"
+
+static inline int
+vpn_level(ulong va, int level)
+{
+ return (va >> (PAGE_SHIFT + 9 * level)) & 0x1FF;
+}
+
+/*
+ * Virtual Address (Sv39): 39 bits
+ * | 9 bits | 9 bits | 9 bits | 12 bits |
+ * | VPN2 | VPN1 | VPN0 | Offset |
+ *
+ * Levels: L2 -> L1 -> L0
+ * pagetable
+ */
+pte_t *
+walk(pte_t *pagetable, ulong va, int alloc)
+{
+ for (int level = 2; level > 0; level--) {
+ uint idx = vpn_level(va, level);
+ pte_t *pte = &pagetable[idx];
+
+ if (*pte & PTE_V) {
+ pagetable = (pte_t *)PHYS_TO_VIRT(PTE2PA(*pte));
+ } else {
+ //uart_puts("current level not valid!\n");
+ if (!alloc){
+ DEBUG_PRINT(uart_puts("walk(): access to pagetable failed\n"););
+ return NULL;
+ }
+
+ void *new_pg = alloc_page();
+ if (!new_pg) {
+ DEBUG_PRINT(uart_puts("alloc_page failed in walk()\n"););
+ return NULL;
+ }
+
+ *pte = PA2PTE(new_pg) | PTE_V;
+ pagetable = (pte_t *)PHYS_TO_VIRT(new_pg);
+ }
+ }
+
+ return &pagetable[vpn_level(va, 0)];
+}
+
+int
+map_page(pte_t *pagetable, ulong va, ulong pa, int perm)
+{
+ // Convert to virtual address
+ pagetable = PHYS_TO_VIRT(pagetable);
+
+ pte_t *pte = walk(pagetable, va, 1);
+ if (!pte)
+ return -1;
+
+ if (!(*pte & PTE_V))
+ *pte = PA2PTE(pa) | perm | PTE_V | PTE_A | PTE_D;
+
+ return 0;
+}
+
+int
+unmap_page(pte_t *pagetable, ulong va)
+{
+ // Convert to virtual address
+ pagetable = PHYS_TO_VIRT(pagetable);
+
+ pte_t *pte = walk(pagetable, va, 0);
+ if (!pte || !(*pte & PTE_V))
+ return -1;
+
+ ulong pa = PTE2PA(*pte);
+ free_page((void *)pa);
+
+ // Clear the PTE
+ *pte = 0;
+
+ // Flush TLB for this address
+ asm volatile("sfence.vma %0, zero" :: "r"(va) : "memory");
+
+ return 0;
+}
+
+
+pte_t *
+alloc_pagetable(void)
+{
+ return (pte_t *)alloc_page();
+}
+
+void
+load_pagetable(pte_t *pagetable)
+{
+ write_csr(satp, MAKE_SATP(pagetable));
+
+ // Flush TLB
+ asm volatile ("sfence.vma zero, zero");
+}
+
+void
+free_pagetable(pte_t *pagetable)
+{
+ // Convert to virtual address
+ pagetable = PHYS_TO_VIRT(pagetable);
+
+ for (int i = 0; i < PTE_COUNT; i++) {
+ pte_t pte = pagetable[i];
+ if ((pte & PTE_V) && !(pte & (PTE_R | PTE_W | PTE_X))) {
+ pte_t *child = (pte_t *)PTE2PA(pte);
+ free_pagetable(child);
+ }
+ }
+ free_page(pagetable);
+}
blob - /dev/null
blob + cf8f08e90d98e8e6d300ebb67d9518dfa3fd4174 (mode 644)
--- /dev/null
+++ kernel/mm/pagetable.h
+#pragma once
+
+#include "../types.h"
+#include "mem.h"
+
+#define PTE_COUNT 512 // Entries per page table level (9 bits)
+
+#define SATP_MODE_SV39 (8UL << 60)
+#define MAKE_SATP(pgtbl) (SATP_MODE_SV39 | (((ulong)pgtbl >> PAGE_SHIFT) & 0xFFFFFFFFFFFUL))
+
+#define PTE_V (1L << 0)
+#define PTE_R (1L << 1)
+#define PTE_W (1L << 2)
+#define PTE_X (1L << 3)
+#define PTE_U (1L << 4)
+#define PTE_A (1L << 6)
+#define PTE_D (1L << 7)
+
+pte_t *alloc_pagetable(void);
+pte_t *walk(pte_t *pagetable, ulong va, int alloc);
+int map_page(pte_t *pagetable, ulong va, ulong pa, int perm);
+int unmap_page(pte_t *pagetable, ulong va);
+void load_pagetable(pte_t *pagetable);
+void free_pagetable(pte_t *pagetable);
blob - /dev/null
blob + 7f8c9f7bb2c2cb705860edd5cd0d47228645d642 (mode 644)
--- /dev/null
+++ kernel/mm/snub.c
+#include "snub.h"
+
+static snub_cache_t caches[SNUB_NUM_CLASSES];
+
+static inline int
+size_to_idx(ulong size)
+{
+ int shift = SNUB_MIN_SHIFT;
+ while ((1UL << shift) < size && shift <= SNUB_MAX_SHIFT)
+ shift++;
+
+ return (shift > SNUB_MAX_SHIFT) ? -1 : (shift - SNUB_MIN_SHIFT);
+}
+
+void
+snub_init(void)
+{
+ for (int i = 0; i < SNUB_NUM_CLASSES; i++) {
+ caches[i].object_size = 1 << (SNUB_MIN_SHIFT + i);
+ caches[i].partial = NULL;
+ spinlock_init(&caches[i].lock);
+ }
+}
+
+void *
+kmalloc(ulong size)
+{
+ int idx = size_to_idx(size);
+ if (idx < 0 || idx >= SNUB_NUM_CLASSES)
+ return NULL;
+
+ snub_cache_t *cache = &caches[idx];
+
+ // Lock spinlock
+ spin_lock(&cache->lock);
+
+ if (!cache->partial) {
+ // Allocate new snub page
+ void *page = PHYS_TO_VIRT(alloc_page());
+ if (!page) {
+ // Unlock spinlock
+ spin_unlock(&cache->lock);
+ return NULL;
+ }
+
+ int objs_per_page = PAGE_SIZE / cache->object_size;
+ void *head = NULL;
+
+ for (int i = 0; i < objs_per_page; i++) {
+ void *obj = (char *)page + i * cache->object_size;
+ *(void **)obj = head;
+ head = obj;
+ }
+
+ cache->partial = head;
+ }
+
+ void *obj = cache->partial;
+ cache->partial = *(void **)obj;
+
+ // Unlock spinlock
+ spin_unlock(&cache->lock);
+
+ return obj;
+}
+
+void *
+kzalloc(ulong size)
+{
+ void *ptr = kmalloc(size);
+ if (ptr)
+ memset(ptr, 0, size);
+
+ return ptr;
+}
+
+void
+kfree(void *ptr)
+{
+ ulong addr = (ulong)ptr;
+ ulong offset = addr - buddy_base_phys;
+ int page_idx = offset / PAGE_SIZE;
+ ulong page_base = buddy_base_phys + (page_idx * PAGE_SIZE);
+ ulong diff = addr - page_base;
+
+ // Guess object size by nearest size class
+ for (int i = 0; i < SNUB_NUM_CLASSES; i++) {
+ int obj_size = caches[i].object_size;
+ if (diff % obj_size == 0 && diff < PAGE_SIZE) {
+ // Lock spinlock
+ spin_lock(&caches[i].lock);
+
+ *(void **)ptr = caches[i].partial;
+ caches[i].partial = ptr;
+
+ // Unlock spinlock
+ spin_unlock(&caches[i].lock);
+
+ return;
+ }
+ }
+
+ // Not a known cache — maybe full page allocation
+ free_pages((void *)VIRT_TO_PHYS(ptr));
+}
blob - /dev/null
blob + 79a8fc8fcaccfa01f61afee13af3da09e4910903 (mode 644)
--- /dev/null
+++ kernel/mm/snub.h
+#pragma once
+
+#include "../spinlock.h"
+#include "../types.h"
+#include "mem.h"
+
+#define SNUB_MIN_SHIFT 4 // 2^4 = 16 bytes
+#define SNUB_MAX_SHIFT 12 // 2^12 = 4096 bytes
+#define SNUB_NUM_CLASSES (SNUB_MAX_SHIFT - SNUB_MIN_SHIFT + 1)
+
+typedef struct snub_cache {
+ int object_size;
+ void *partial; // Freelist of objects
+ spinlock_t lock;
+} snub_cache_t;
+
+void snub_init(void);
+void *kmalloc(ulong size);
+void *kzalloc(ulong size);
+void kfree(void *ptr);
blob - /dev/null
blob + 286c61cd6c3b26aacdc2cef15d8bd1868aed96ad (mode 644)
--- /dev/null
+++ kernel/mm/user_copy.h
+#pragma once
+
+#include "mem.h"
+
+static inline uint
+is_user_addr(ulong va, uint len)
+{
+ return len && (va < USER_END_VA) && (len < (USER_END_VA - va));
+}
+
+static inline int
+copy_from_user(void *kernel_dst, const void *user_src, uint len)
+{
+ if (!is_user_addr((ulong)user_src, len))
+ return -1;
+
+ set_csr(sstatus, SSTATUS_SUM);
+ __builtin_memcpy(kernel_dst, user_src, len);
+ clear_csr(sstatus, SSTATUS_SUM);
+
+ return 0;
+}
+
+static inline int
+copy_to_user(void *user_dst, const void *kernel_src, uint len)
+{
+ if (!is_user_addr((ulong)user_dst, len))
+ return -1;
+
+ set_csr(sstatus, SSTATUS_SUM);
+ __builtin_memcpy(user_dst, kernel_src, len);
+ clear_csr(sstatus, SSTATUS_SUM);
+
+ return 0;
+}
blob - /dev/null
blob + 6f60918b42a67562e3d2e15eb9c775ba6122da64 (mode 644)
--- /dev/null
+++ kernel/proc.c
+#include "proc.h"
+#include "uart.h"
+#include "mm/snub.h"
+#include "list.h"
+#include "mm/pagetable.h"
+#include "trap/trap.h"
+
+static long next_pid;
+
+cpu_t cpus[NCPU];
+proc_t idle_procs[NCPU];
+extern char _kernel_end[];
+
+void
+idle_loop()
+{
+ DEBUG_PRINT(
+ uart_puts("Hart ");
+ uart_putc('0' + curr_cpu()->id);
+ uart_puts(": idle loop\n");
+ );
+
+ // Set simplified trap vector
+ write_csr(stvec, idle_trap_vector);
+
+ // Enable interrupts
+ set_csr(sstatus, SSTATUS_SIE);
+
+ while (1)
+ asm volatile("wfi"); // Wait for interrupt
+}
+
+void
+dump_mappings(pte_t *pagetable, ulong start_va, ulong end_va)
+{
+ for (ulong va = start_va; va < end_va; va += PAGE_SIZE) {
+ pte_t *pte = walk(pagetable, va, 0);
+ if (!pte || !(*pte & PTE_V)) {
+ uart_puts("VA not mapped: ");
+ uart_puthex(va);
+ uart_putc('\n');
+ continue;
+ }
+
+ uart_puts("VA: ");
+ uart_puthex(va);
+ uart_puts(" -> PA: ");
+ uart_puthex(PTE2PA(*pte));
+ uart_puts(" Flags: ");
+ uart_puthex(PTE_FLAGS(*pte));
+ uart_putc('\n');
+ }
+}
+
+void
+dump_pagetable_recursive(pte_t *pagetable, int level, ulong va_base)
+{
+ for (int i = 0; i < 512; i++) {
+ if (pagetable[i] & PTE_V) {
+ ulong va = va_base | ((ulong)i << (12 + 9 * level));
+ ulong pte = pagetable[i];
+
+ uart_puts("L");
+ uart_putc('0' + level);
+ uart_puts(" Entry ");
+ uart_puthex(i);
+ uart_puts(": VA ");
+ uart_puthex(va);
+ uart_puts(" -> PTE ");
+ uart_puthex(pte);
+
+ if (pte & (PTE_R | PTE_W | PTE_X)) {
+ // It's a leaf entry, calculate physical address
+ ulong pa = PTE2PA(pte);
+ uart_puts(" -> PA ");
+ uart_puthex(pa);
+ }
+
+ uart_putc('\n');
+
+ if ((pte & (PTE_R | PTE_W | PTE_X)) == 0) {
+ // This PTE points to a lower-level page table
+ ulong next_pa = PTE2PA(pte);
+ pte_t *next = (pte_t *)PHYS_TO_VIRT(next_pa);
+ dump_pagetable_recursive(next, level - 1, va);
+ }
+ }
+ }
+}
+
+void
+copy_kernel_mappings(pte_t *dst, pte_t *src)
+{
+ for (ulong va = KERNEL_START_VA; va < (ulong)_kernel_end; va += PAGE_SIZE) {
+ pte_t *src_pte = walk(src, va, 0);
+ if (!src_pte || !(*src_pte & PTE_V)) {
+ uart_puts("copy_kernel_mappings: missing PTE at ");
+ uart_puthex(va);
+ uart_putc('\n');
+ continue;
+ }
+
+ ulong pa = PTE2PA(*src_pte);
+ ulong flags = PTE_FLAGS(*src_pte);
+
+ if (map_page(dst, va, pa, flags) != 0) {
+ uart_puts("copy_kernel_mappings: map_page failed at ");
+ uart_puthex(va);
+ uart_putc('\n');
+ }
+ }
+}
+
+proc_t *
+create_proc(void *binary, ulong binary_size)
+{
+ //DEBUG_PRINT(uart_puts("create_proc()\n"););
+
+ proc_t *p = (proc_t *)kzalloc(sizeof(proc_t));
+ if (!p)
+ goto fail;
+
+ p->pid = ATOMIC_INC_AND_FETCH(&next_pid);
+ p->bound_cpu = -1;
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Proc allocated\n");
+ uart_puts("Allocating proc pagetable...\n");
+ );
+ */
+
+ // Allocate user page table
+ p->pagetable = alloc_pagetable();
+ if (!p->pagetable)
+ goto fail1;
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Proc pagetable allocated\n");
+ uart_puts("Allocating kstack for the proc\n");
+ );
+ */
+
+ p->kstack = (char *)kzalloc(KSTACK_SIZE);
+ if (!p->kstack)
+ goto fail1;
+
+ // Map proc_t
+ if (map_page(p->pagetable, (ulong)p, VIRT_TO_PHYS(p), PTE_R | PTE_W) == -1)
+ goto fail1;
+
+ // Map kstack
+ if (map_page(p->pagetable, (ulong)p->kstack, VIRT_TO_PHYS(p->kstack), PTE_R | PTE_W) == -1)
+ goto fail1;
+
+ p->tf = (trap_frame_t *)(p->kstack + KSTACK_SIZE - sizeof(trap_frame_t));
+ p->tf->regs[2] = USER_STACK_TOP;
+ p->tf->sepc = USER_START_VA;
+ p->tf->sstatus = SSTATUS_SPIE | SSTATUS_SUM;
+ p->ctx.sp = (ulong)p->tf;
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Kernel stack allocated and mapped\n");
+ uart_puts("Loading user binary...\n");
+ );
+ */
+
+ // Load user binary
+ ulong va = USER_START_VA;
+ ulong remaining = binary_size;
+ uchar *src = (uchar *)binary;
+
+ while (remaining > 0) {
+ ulong user_bin_pa = (ulong)alloc_page();
+ if (!user_bin_pa)
+ goto fail1;
+
+ ulong to_copy = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
+ memcpy(PHYS_TO_VIRT(user_bin_pa), src, to_copy);
+
+ if (map_page(p->pagetable, va, user_bin_pa, PTE_R | PTE_X | PTE_U) == -1)
+ goto fail1;
+
+ va += PAGE_SIZE;
+ src += to_copy;
+ remaining -= to_copy;
+ }
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("User binary loaded\n");
+ uart_puts("Allocating user heap and stack...\n");
+ );
+ */
+
+ // Set user heap
+ p->heap_start = ALIGN_UP(va);
+ p->heap_end = p->heap_start;
+
+ // User stack (1 page)
+ p->ustack = alloc_page();
+ if (!p->ustack)
+ goto fail1;
+
+ // Map user stack
+ if (map_page(p->pagetable, USER_STACK_TOP - USER_STACK_SIZE, (ulong)p->ustack, PTE_R | PTE_W | PTE_U) == -1)
+ goto fail1;
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("User stack allocated and mapped\n");
+ uart_puts("Mapping kernel pagetable into user pagetable\n");
+ );
+ */
+
+ // Map kernel segments
+ copy_kernel_mappings(p->pagetable, PHYS_TO_VIRT(kernel_pagetable));
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Kernel pagetable into user pagetable mapped\n");
+ uart_puts("Mapping UART to user pagetable\n");
+ );
+ */
+
+ // Map UART
+ if (map_page(p->pagetable, UART0, VIRT_TO_PHYS(UART0), PTE_R | PTE_W) == -1) {
+ uart_puts("map_page() failed for UART, halting...\n");
+ while (1)
+ asm volatile("wfi");
+ }
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("UART mapped into user pagetable\n");
+ );
+ */
+
+ //dump_mappings(p->pagetable, USER_STACK_TOP - USER_STACK_SIZE, USER_STACK_TOP);
+ //dump_mappings(p->pagetable, KERNEL_START_VA, (ulong)_kernel_end);
+ //dump_mappings(p->pagetable, UART0, UART0 + PAGE_SIZE);
+
+ // Put into RUNNABLE queue
+ p->state = RUNNABLE;
+ list_add_tail(&p->q_node, &cpus[curr_cpu()->id].run_queue);
+
+ //dump_pagetable(p->pagetable);
+ //uart_puts("User Page Table Dump:\n");
+ //dump_pagetable_recursive(p->pagetable, 2, KERNEL_START_VA);
+ //dump_pagetable_range(p->pagetable, USER_START_VA, USER_END_VA);
+ //uart_puts("And kernel range:\n");
+ //dump_pagetable_range(p->pagetable, KERNEL_START_VA, (ulong)_kernel_end);
+
+ return p;
+
+fail1:
+ free_proc(p);
+fail:
+ DEBUG_PRINT(uart_puts("Process creation failed. Return NULL.\n"););
+ return NULL;
+}
+
+void
+free_proc(proc_t *p)
+{
+ if (!p)
+ return;
+
+ // If it's in a queue, remove it
+ if (list_in_queue(&p->q_node))
+ list_del(&p->q_node);
+
+ // Free user stack
+ if (p->ustack)
+ free_page(p->ustack);
+
+ // TODO: properly free user binary
+
+ // Free user page table
+ if (p->pagetable)
+ free_pagetable(p->pagetable);
+
+ // Free kernel stack
+ if (p->kstack)
+ kfree(p->kstack);
+
+ // Free the process
+ kfree(p);
+}
blob - /dev/null
blob + aba5d6ed6e12dfb24fae252639c72e7cefaf2fad (mode 644)
--- /dev/null
+++ kernel/proc.h
+#pragma once
+
+#include "riscv.h"
+#include "list.h"
+
+#define NCPU 4 // number of CPUs
+
+typedef struct trap_frame {
+ ulong regs[32]; // x0 - x31
+ ulong sepc; // saved program counter
+ ulong sstatus; // status register
+ ulong scause; // cause of trap
+ ulong stval; // trap value (like faulting addr)
+} trap_frame_t;
+
+typedef struct context {
+ ulong ra;
+ ulong sp;
+ ulong s[12];
+} context_t;
+
+enum proc_state {
+ UNUSED,
+ SLEEPING,
+ RUNNABLE,
+ RUNNING,
+ ZOMBIE
+};
+
+typedef struct proc {
+ int pid;
+ enum proc_state state;
+ int is_idle;
+ int bound_cpu;
+ ulong sleep_until;
+ trap_frame_t *tf;
+ context_t ctx;
+ list_node_t q_node;
+ //list_node_t user_bin;
+ pte_t *pagetable;
+ ulong heap_start;
+ ulong heap_end;
+ char *kstack;
+ char *ustack;
+} proc_t;
+
+typedef struct cpu {
+ uint id; // hard ID
+ uint lock_depth; // depth of nested spinlocks
+ ulong sstatus; // interrupt state before first lock
+ int needs_sched;
+ proc_t *proc; // current process
+ list_node_t run_queue;
+ list_node_t sleep_queue;
+} cpu_t;
+
+static inline cpu_t *
+curr_cpu()
+{
+ return (cpu_t *)read_tp();
+}
+
+extern cpu_t cpus[NCPU];
+extern proc_t idle_procs[NCPU];
+
+void idle_loop();
+proc_t *create_proc(void *binary, ulong binary_size);
+void free_proc(proc_t *p);
blob - /dev/null
blob + 0f4db90794f081faadac9928f446bcb57116871d (mode 755)
--- /dev/null
+++ kernel/rerun.sh
+#!/bin/zsh
+
+make clean
+./build_shell.sh
+make run
blob - /dev/null
blob + 9cb003ef5862deced721e40aae5a613eb3274258 (mode 644)
--- /dev/null
+++ kernel/riscv.h
+#pragma once
+
+#include "types.h"
+
+#define SSTATUS_SIE (1UL << 1)
+#define SSTATUS_SPP (1UL << 8)
+#define SSTATUS_SPIE (1UL << 5)
+#define SSTATUS_UPIE (0UL << 8)
+#define SSTATUS_SUM (1UL << 18)
+
+#define SIE_STIE (1UL << 5)
+#define SIE_SEIE (1UL << 9)
+
+#define MENVCFG_FDT (1UL << 63)
+#define MCOUNTEREN_TIME (1UL << 1)
+#define MSTATUS_MPP_MASK (3UL << 11) // Mask for MPP (bits 11-12)
+#define MSTATUS_MPP_S_MODE (1UL << 11) // MPP set to S-mode (01)
+
+#define read_csr(reg) ({ \
+ ulong __tmp; \
+ asm volatile("csrr %0, " #reg : "=r"(__tmp)); \
+ __tmp; \
+})
+
+#define write_csr(reg, val) ({ \
+ ulong __v = (ulong)(val); \
+ asm volatile("csrw " #reg ", %0" :: "rK"(__v)); \
+})
+
+#define set_csr(reg, bit) ({ \
+ asm volatile("csrs " #reg ", %0" :: "rK"(bit)); \
+})
+
+#define clear_csr(reg, bit) ({ \
+ asm volatile("csrc " #reg ", %0" :: "rK"(bit)); \
+})
+
+#define ATOMIC_FETCH_AND_INC(ptr) ({ \
+ long __old_val; \
+ asm volatile ( \
+ "li t0, 1\n\t" \
+ "amoadd.d.aq %0, t0, (%1)\n\t" \
+ : "=r"(__old_val) \
+ : "r"(ptr) \
+ : "t0", "memory"); \
+ __old_val; \
+})
+
+#define ATOMIC_INC_AND_FETCH(ptr) ({ \
+ long __old_val; \
+ asm volatile ( \
+ "li t0, 1\n\t" \
+ "amoadd.d.aq %0, t0, (%1)\n\t" \
+ : "=r"(__old_val) \
+ : "r"(ptr) \
+ : "t0", "memory"); \
+ __old_val + 1; \
+})
+
+static inline ulong
+read_tp()
+{
+ ulong val;
+ asm volatile("mv %0, tp" : "=r"(val));
+ return val;
+}
+
+static inline void
+write_tp(ulong val)
+{
+ asm volatile("mv tp, %0" :: "r"(val));
+}
blob - /dev/null
blob + a06589801d4ec723085203b39403ab033a1478d9 (mode 644)
--- /dev/null
+++ kernel/sched.c
+#include "sched.h"
+#include "proc.h"
+#include "uart.h"
+#include "list.h"
+#include "mm/mem.h"
+#include "trap/trap.h"
+
+// Forward declaration
+void swtch(context_t *old, context_t *new);
+
+void
+schedule()
+{
+ cpu_t *c = curr_cpu();
+ proc_t *old = c->proc;
+ proc_t *new = NULL;
+
+ DEBUG_PRINT(
+ uart_putc('0' + c->id);
+ uart_puts(" schedule() run...\n");
+ );
+
+ list_node_t *head = &c->run_queue;
+ list_node_t *node = head->next;
+
+ // Next process found
+ if (node != head) {
+ new = container_of(node, proc_t, q_node);
+ list_del(&new->q_node);
+ new->state = RUNNING;
+
+ DEBUG_PRINT(
+ uart_puts("new proc ");
+ uart_putc('0' + new->pid);
+ uart_puts(" found, switching to user code at ");
+ uart_puthex(new->tf->sepc);
+ uart_putc('\n');
+ );
+
+ if (!old->is_idle) {
+ old->state = RUNNABLE;
+ list_add_tail(&old->q_node, &c->run_queue);
+ }
+ c->proc = new;
+
+ // Set normal trap vector
+ write_csr(stvec, trap_vector);
+
+ // Load page table for new process
+ load_pagetable(c->proc->pagetable);
+
+ // Save trap frame
+ write_csr(sscratch, c->proc->tf);
+
+ // Kernel context switch
+ swtch(&old->ctx, &c->proc->ctx);
+ } else if (!old->is_idle) {
+ // No runnable process, run idle
+ DEBUG_PRINT(
+ uart_putc('0' + c->id);
+ uart_puts(" switching to idle\n");
+ );
+
+ // Idle process always stays the same
+ c->proc = &idle_procs[c->id];
+ c->proc->ctx.ra = (ulong)idle_loop;
+ c->proc->ctx.sp = (ulong)(c->proc->kstack + KSTACK_SIZE);
+
+ load_pagetable(kernel_pagetable);
+ swtch(&old->ctx, &c->proc->ctx);
+ }
+}
blob - /dev/null
blob + 9617289c3f977c8db884d70099e5e08de79ba31c (mode 644)
--- /dev/null
+++ kernel/sched.h
+#pragma once
+
+#include "spinlock.h"
+#include "mm/pagetable.h"
+
+extern spinlock_t sched_lock;
+
+void schedule();
blob - /dev/null
blob + 1e401a346c6745aac3ff827f5cbae9f339b09ed9 (mode 644)
--- /dev/null
+++ kernel/shell.c
+#include "user.h"
+
+#define INPUT_BUF_SIZE 128
+
+int
+streq(const char *s1, const char *s2, int n)
+{
+ for (int i = 0; i < n; i++)
+ if (!s1[i] || !s2[i] || s1[i] != s2[i])
+ return 0;
+
+ return 1;
+}
+
+__attribute__((section(".text.user_shell_main"), used))
+void
+user_shell_main()
+{
+ char buf[INPUT_BUF_SIZE] = "$ ";
+ const char cmd_yield[] = "yield";
+
+ while (1) {
+ // Print prompt
+ const char prompt[] = "$ ";
+ write(prompt, 2);
+
+ // Read input (blocking)
+ int n = read(&buf[2], sizeof(buf) - 1);
+ if (n <= 0)
+ continue;
+
+ sleep(1000);
+
+ if (streq(&buf[2], cmd_yield, sizeof(cmd_yield) - 1))
+ yield();
+
+ // Echo back
+ write(buf, n + 2); // \0 and \n
+
+ //yield();
+ //exit();
+ }
+}
blob - /dev/null
blob + ca7bc8013cbfc70e823a0200b19ade1686b51db3 (mode 644)
--- /dev/null
+++ kernel/spinlock.c
+#include "spinlock.h"
+#include "proc.h"
+#include "uart.h"
+
+void
+spin_lock(spinlock_t *lock)
+{
+ cpu_t *c = curr_cpu();
+ if (c->lock_depth == 0) {
+ // Save current interrupt state and disable interrupts
+ c->sstatus = read_csr(sstatus);
+ clear_csr(sstatus, SSTATUS_SIE); // Clear SIE
+
+ }
+ c->lock_depth++;
+
+ while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE))
+ asm volatile("nop");
+}
+
+int
+spin_trylock(spinlock_t *lock)
+{
+ cpu_t *c = curr_cpu();
+ if (c->lock_depth == 0) {
+ // Save current interrupt state and disable interrupts
+ c->sstatus = read_csr(sstatus);
+ clear_csr(sstatus, SSTATUS_SIE); // Clear SIE
+
+ }
+ c->lock_depth++;
+
+ int expected = 0;
+ return __atomic_compare_exchange_n(&lock->locked, &expected, 1,
+ 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
+}
+
+void
+spin_unlock(spinlock_t *lock)
+{
+ cpu_t *c = curr_cpu();
+
+ // Sanity check: lock should not be unlocked if not locked
+ if (c->lock_depth == 0) {
+ uart_puts("[panic] spin_unlock: lock_depth underflow\n");
+ while (1)
+ asm volatile("wfi");
+ }
+
+ // Decrement lock nesting depth
+ c->lock_depth--;
+
+ // Release the lock — __ATOMIC_RELEASE ensures memory ordering
+ __atomic_clear(&lock->locked, __ATOMIC_RELEASE);
+
+ // Only restore interrupt state when outermost lock is released
+ // Restore sstatus (e.g., re-enable interrupts if they were on before)
+ if (c->lock_depth == 0)
+ write_csr(sstatus, c->sstatus);
+}
blob - /dev/null
blob + 6e5a457a1fd626cb5d097c8223e5e18bdf993ab4 (mode 644)
--- /dev/null
+++ kernel/spinlock.h
+#pragma once
+
+#include "types.h"
+
+typedef struct {
+ volatile uint locked;
+} spinlock_t;
+
+#define SPINLOCK_INIT { .locked = 0 }
+
+static inline void spinlock_init(spinlock_t *lock) {
+ lock->locked = 0;
+}
+
+void spin_lock(spinlock_t *lock);
+int spin_trylock(spinlock_t *lock);
+void spin_unlock(spinlock_t *lock);
blob - /dev/null
blob + ea4cd04273f4e453fbd71610f046da034d2a9bf5 (mode 644)
--- /dev/null
+++ kernel/start.c
+#include "uart.h"
+#include "spinlock.h"
+#include "riscv.h"
+#include "trap/trap.h"
+#include "types.h"
+#include "proc.h"
+#include "timer.h"
+#include "syscall.h"
+#include "string.h"
+#include "sched.h"
+#include "mm/snub.h"
+#include "mm/pagetable.h"
+
+volatile int allocator_ready = 0;
+
+extern char _stack[];
+
+extern char _binary_shell_bin_start[];
+extern char _binary_shell_bin_end[];
+
+extern char _kernel_text_start[];
+extern char _kernel_text_end[];
+extern char _kernel_rodata_start[];
+extern char _kernel_rodata_end[];
+extern char _kernel_data_start[];
+
+void
+shell_init()
+{
+ ulong size = _binary_shell_bin_end - _binary_shell_bin_start;
+ proc_t *shell = create_proc(_binary_shell_bin_start, size);
+
+ list_del(&shell->q_node);
+ shell->state = RUNNING;
+ shell->bound_cpu = 0;
+
+ curr_cpu()->proc = shell;
+}
+
+void
+setup_idle_proc()
+{
+ cpu_t *c = curr_cpu();
+ proc_t *idle = &idle_procs[c->id];
+
+ memset(idle, 0, sizeof(proc_t));
+ //idle->pid = 0;
+ idle->is_idle = 1;
+ idle->state = RUNNING;
+ idle->pagetable = kernel_pagetable;
+
+ // Reuse boot stack as idle stack
+ idle->kstack = _stack + (c->id * KSTACK_SIZE);
+
+ // Set context
+ idle->ctx.ra = (ulong)idle_loop;
+ idle->ctx.sp = (ulong)(idle->kstack + KSTACK_SIZE);
+
+ if (c->id)
+ c->proc = idle;
+}
+
+void jump_to_user_shell()
+{
+ DEBUG_PRINT(uart_puts("Jumping to user shell\n"););
+
+ proc_t *p = curr_cpu()->proc;
+
+ // Setup SSTATUS: SPIE=1 (enable interrupts)
+ write_csr(sstatus, SSTATUS_SPIE);
+ // Set return mode = user
+ clear_csr(sstatus, SSTATUS_SPP);
+ // Save trap frame
+ write_csr(sscratch, (ulong)p->tf);
+
+ load_pagetable(p->pagetable);
+
+ // Set up sp (toggle SUM to touch user memory)
+ set_csr(sstatus, SSTATUS_SUM);
+ register ulong sp asm("sp") = (ulong)USER_STACK_TOP;
+ clear_csr(sstatus, SSTATUS_SUM);
+
+ asm volatile(
+ "mv sp, %0\n"
+ "sret\n"
+ :
+ : "r"(sp)
+ : "memory"
+ );
+}
+
+void
+s_mode_main()
+{
+ // Just print hello for now
+ DEBUG_PRINT(
+ uart_puts("Hart ");
+ uart_putc('0' + curr_cpu()->id);
+ uart_puts(": Hello from S-mode!\n");
+ );
+
+ // Create per-CPU process queues
+ LIST_HEAD_INIT(&cpus[curr_cpu()->id].run_queue);
+ LIST_HEAD_INIT(&cpus[curr_cpu()->id].sleep_queue);
+
+ // Enable timer irqs only after queues are set up
+ timer_init();
+
+ // Create idle process for each CPU
+ setup_idle_proc();
+
+ // Jump to shell (future init) on core0
+ if (curr_cpu()->id == 0) {
+ shell_init();
+ jump_to_user_shell();
+ } else {
+ idle_loop();
+ }
+
+ DEBUG_PRINT(uart_puts("End of s_mode_main()\n"););
+ while (1)
+ asm volatile("wfi");
+}
+
+void
+setup_kernel_pagetable()
+{
+ /*
+ DEBUG_PRINT(
+ uart_puts("calling map_page() on KERNEL_START - KERNEL_END\n");
+ );
+ */
+
+ // Map kernel code
+ for (ulong pa = (ulong)_kernel_text_start; pa < (ulong)_kernel_text_end; pa += PAGE_SIZE) {
+ if (map_page(kernel_pagetable, pa + VA_OFFSET, pa, PTE_R | PTE_X) == -1) {
+ uart_puts("map_page() failed for kernel text, halting...\n");
+ while (1)
+ asm volatile("wfi");
+ }
+ }
+
+ // Map rodata
+ for (ulong pa = (ulong)_kernel_rodata_start; pa < (ulong)_kernel_rodata_end; pa += PAGE_SIZE) {
+ if (map_page(kernel_pagetable, pa + VA_OFFSET, pa, PTE_R) == -1) {
+ uart_puts("map_page() failed for kernel rodata, halting...\n");
+ while (1)
+ asm volatile("wfi");
+ }
+ }
+
+ // Map (s)data, (s)bss and heap
+ for (ulong pa = (ulong)_kernel_data_start; pa < (ulong)KERNEL_END; pa += PAGE_SIZE) {
+ if (map_page(kernel_pagetable, pa + VA_OFFSET, pa, PTE_R | PTE_W) == -1) {
+ uart_puts("map_page() failed for kernel data + heap, halting...\n");
+ while (1)
+ asm volatile("wfi");
+ }
+ }
+
+ // Map UART
+ if (map_page(kernel_pagetable, UART0, UART0 - VA_OFFSET, PTE_R | PTE_W) == -1) {
+ uart_puts("map_page() failed for UART, halting...\n");
+ while (1)
+ asm volatile("wfi");
+ }
+
+ // Wait for the changes
+ asm volatile ("sfence.vma zero, zero");
+}
+
+void
+mm_init()
+{
+ // Initialize global memory allocator
+ buddy_allocator_init();
+
+ // Map phys mem to virt in kernel
+ setup_kernel_pagetable();
+
+ // Use kernel page table
+ load_pagetable(kernel_pagetable);
+
+ // SystemN Unified Buddy allocator :)
+ snub_init();
+
+ // Adjust addrs to virtual for S-mode
+ va_offset = VA_OFFSET;
+}
+
+void
+start()
+{
+ // Set struct cpu to tp register
+ uint hart_id = read_csr(mhartid);
+ write_tp((ulong)&cpus[hart_id]);
+
+ // Set hart_id to struct cpu
+ curr_cpu()->id = hart_id;
+
+ // Halt all harts except 0
+ //if (hart_id != 0)
+ // while (1)
+ // asm volatile("wfi");
+
+ // Initialize memory system
+ if (hart_id == 0) {
+ mm_init();
+ __sync_synchronize();
+ allocator_ready = 1;
+ } else {
+ // Wait for hart 0 to finish memory init
+ while (!allocator_ready)
+ asm volatile("nop");
+
+ // Use kernel page table on all cores
+ __sync_synchronize();
+ load_pagetable(kernel_pagetable);
+ }
+
+ // Delegate exceptions and interrupts to S-mode
+ write_csr(medeleg, 0xffff);
+ write_csr(mideleg, 0xffff);
+
+ // Setup PMP to give S-mode access to all memory
+ write_csr(pmpaddr0, -1L);
+ write_csr(pmpcfg0, 0x0f); // R/W/X permissions, TOR mode
+
+ // Set S-mode trap vector
+ write_csr(stvec, trap_vector + VA_OFFSET);
+
+ // Set up mstatus to enter S-mode
+ clear_csr(mstatus, MSTATUS_MPP_MASK); // Clear MPP (bits 12-11)
+ set_csr(mstatus, MSTATUS_MPP_S_MODE); // Set MPP = S-mode (01)
+
+ // Enable interrupts in S-mode
+ set_csr(sstatus, SSTATUS_SIE);
+
+ // Supervisor External Interrupt Enable
+ //set_csr(sie, SIE_SEIE);
+
+ // Init timer interrupts
+ set_csr(sie, SIE_STIE);
+ set_csr(menvcfg, MENVCFG_FDT);
+ set_csr(mcounteren, MCOUNTEREN_TIME);
+
+ // Set mepc to the address of S-mode entry point
+ write_csr(mepc, (ulong)s_mode_main + VA_OFFSET);
+
+ // Adjust registers to virtual for S-mode
+ write_tp((ulong)&cpus[hart_id] + VA_OFFSET);
+ asm volatile("add sp, sp, %0" :: "r"(VA_OFFSET));
+
+ // Drop to S-mode!
+ asm volatile("mret");
+}
blob - /dev/null
blob + bbdef7debda8dedb8c4e2195864962429d6f3ca6 (mode 644)
--- /dev/null
+++ kernel/string.c
+#include "string.h"
+
+void *
+memcpy(void *dest, const void *src, ulong n)
+{
+ char *d = dest;
+ const char *s = src;
+
+ while (n--)
+ *d++ = *s++;
+
+ return dest;
+}
+
+void *
+memset(void *dst, int c, ulong n)
+{
+ uchar *p = (uchar *)dst;
+ for (uint i = 0; i < n; i++)
+ p[i] = (uchar)c;
+
+ return dst;
+}
+
+ulong
+strlen(const char *s)
+{
+ ulong len = 0;
+ while (*s++)
+ len++;
+
+ return len;
+}
+
+ulong
+strnlen(const char *s, ulong maxlen)
+{
+ ulong len = 0;
+ while (len < maxlen && s[len] != '\0')
+ len++;
+
+ return len;
+}
+
+/*
+ * $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
+ */
+ulong
+strlcpy(char *dst, const char *src, ulong size)
+{
+ const char *s = src;
+ ulong n = size;
+
+ if (n != 0)
+ while (--n != 0)
+ if ((*dst++ = *src++) == '\0')
+ break;
+
+ if (n == 0) {
+ if (size != 0)
+ *dst = '\0';
+ while (*src++)
+ ;
+ }
+
+ return src - s - 1;
+}
+
+char *
+strchr(const char *s, int c)
+{
+ while (*s) {
+ if (*s == (char)c)
+ return (char *)s;
+ s++;
+ }
+
+ return NULL;
+}
+
+char *
+strtok(char *str, const char *delim)
+{
+ static char *next;
+ char *start;
+
+ if (str)
+ next = str; // Start new tokenization
+ if (!next)
+ return NULL;
+
+ // Skip leading delimiters
+ while (*next && strchr(delim, *next))
+ next++;
+
+ if (*next == '\0') {
+ next = NULL;
+ return NULL;
+ }
+
+ start = next;
+
+ // Find end of token
+ while (*next && !strchr(delim, *next))
+ next++;
+
+ if (*next) {
+ *next = '\0'; // Null-terminate current token
+ next++; // Advance to next token
+ } else {
+ next = NULL; // No more tokens
+ }
+
+ return start;
+}
blob - /dev/null
blob + 2d328be32509ab6b0891b41f04da0e2b89b8c56b (mode 644)
--- /dev/null
+++ kernel/string.h
+#pragma once
+
+#include "types.h"
+
+#define MAXLEN 64
+
+void *memcpy(void *dest, const void *src, ulong n);
+void *memset(void *dst, int c, ulong n);
+ulong strlen(const char *s);
+ulong strnlen(const char *s, ulong maxlen);
+ulong strlcpy(char *dst, const char *src, ulong size);
+char *strtok(char *str, const char *delim);
blob - /dev/null
blob + 916852f48c681b59bf1ddb4f25ef4f5542541f49 (mode 644)
--- /dev/null
+++ kernel/swtch.S
+.section .text
+.global swtch
+swtch:
+ # Save ra, sp, s0-s11 into old
+ sd ra, 0(a0)
+ sd sp, 8(a0)
+ sd s0, 16(a0)
+ sd s1, 24(a0)
+ sd s2, 32(a0)
+ sd s3, 40(a0)
+ sd s4, 48(a0)
+ sd s5, 56(a0)
+ sd s6, 64(a0)
+ sd s7, 72(a0)
+ sd s8, 80(a0)
+ sd s9, 88(a0)
+ sd s10, 96(a0)
+ sd s11, 104(a0)
+
+ #srli t0, a2, 12 # convert to physical PPN (assuming identity mapping)
+ #li t1, 8
+ #slli t1, t1, 60 # t1 = SATP_MODE_SV39 (8 << 60)
+ #or t0, t0, t1 # t0 = final satp value
+ #sfence.vma zero, zero # flush TLB
+ #csrw satp, t0 # load satp
+ #sfence.vma zero, zero # flush TLB
+
+ # Load ra, sp, s0-s11 from new
+ ld ra, 0(a1)
+ ld sp, 8(a1)
+ ld s0, 16(a1)
+ ld s1, 24(a1)
+ ld s2, 32(a1)
+ ld s3, 40(a1)
+ ld s4, 48(a1)
+ ld s5, 56(a1)
+ ld s6, 64(a1)
+ ld s7, 72(a1)
+ ld s8, 80(a1)
+ ld s9, 88(a1)
+ ld s10, 96(a1)
+ ld s11, 104(a1)
+
+ # return to ra from new context
+ ret
blob - /dev/null
blob + c1b4b0434a0a0765702d2c648ae068103bdde098 (mode 644)
--- /dev/null
+++ kernel/syscall.c
+#include "syscall.h"
+#include "uart.h"
+#include "timer.h"
+#include "mm/mem.h"
+#include "mm/pagetable.h"
+#include "mm/user_copy.h"
+
+long
+sys_write(int fd, void *user_buf, uint len)
+{
+ if (fd != 1 && fd != 2)
+ return -1;
+
+ if (len > MAXLEN)
+ len = MAXLEN;
+
+ char buf[MAXLEN];
+ if (copy_from_user(buf, user_buf, len) < 0)
+ return -1;
+
+ int written = 0;
+ spin_lock(&uart_lock);
+ written = uart_putsn(buf, len);
+ spin_unlock(&uart_lock);
+
+ // return number of bytes written
+ return written;
+}
+
+long
+sys_read(int fd, void *user_buf, uint len)
+{
+ if (fd)
+ return -1;
+
+ if (len > MAXLEN)
+ len = MAXLEN;
+
+ DEBUG_PRINT(uart_puts("waiting for uart_gets()...\n"););
+
+ char buf[MAXLEN];
+ int read = uart_gets(buf, len);
+ if (copy_to_user(user_buf, buf, read) < 0)
+ return -1;
+
+ return read;
+}
+
+long
+sys_sleep_ms(cpu_t *c, proc_t *p, ulong ms)
+{
+ if (ms == 0 || ms > MAX_SLEEP_MS)
+ return -1;
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Proc id ");
+ uart_putc('0' + p->pid);
+ uart_puts(" going to sleep for ");
+ uart_putlong(ms / 1000);
+ uart_puts(" seconds\n");
+ );
+ */
+
+ p->state = SLEEPING;
+ list_add_tail(&p->q_node, &c->sleep_queue);
+ p->sleep_until = read_time() + MS_TO_TIME(ms);
+
+ return 0;
+}
+
+long
+sys_sbrk(proc_t *p, long size)
+{
+ if (!p || !size)
+ return -1L;
+
+ ulong old_brk = ALIGN_UP(p->heap_end);
+ size = ALIGN_UP(size);
+
+ ulong new_brk = old_brk + size;
+ if (new_brk < p->heap_start || new_brk > USER_HEAP_END)
+ return -1L;
+
+ // Map new pages
+ if (size > 0) {
+ for (ulong va = old_brk; va < new_brk; va += PAGE_SIZE) {
+ void *pa = alloc_page();
+ if (!pa)
+ return -1L;
+
+ if (map_page(p->pagetable, va, (ulong)pa, PTE_R | PTE_W | PTE_U) == -1)
+ return -1L;
+ }
+ } else {
+ for (ulong va = new_brk; va < old_brk; va += PAGE_SIZE)
+ unmap_page(p->pagetable, va);
+ }
+
+ p->heap_end = new_brk;
+
+ return old_brk;
+}
+
+long
+sys_munmap(proc_t *p, ulong addr, ulong size)
+{
+ if (!p || !size || addr % PAGE_SIZE != 0)
+ return -1;
+
+ ulong end = ALIGN_UP(addr + size);
+ for (ulong va = addr; va < end; va += PAGE_SIZE)
+ if (unmap_page(p->pagetable, va) == -1)
+ return -1;
+
+ return 0;
+}
blob - /dev/null
blob + bdedd6169e97cad809d05ddab8aef24e1483a56c (mode 644)
--- /dev/null
+++ kernel/syscall.h
+#pragma once
+
+#include "types.h"
+#include "proc.h"
+
+long sys_write(int fd, void *user_buf, uint len);
+long sys_read(int fd, void *user_buf, uint len);
+long sys_sleep_ms(cpu_t *c, proc_t *p, ulong ms);
+long sys_sbrk(proc_t *p, long size);
+long sys_munmap(proc_t *p, ulong addr, ulong size);
blob - /dev/null
blob + bfda256f9a20772472b196933619495f48e79cee (mode 644)
--- /dev/null
+++ kernel/timer.h
+#pragma once
+
+#include "sched.h"
+#include "list.h"
+
+#define TIME_FREQ 10000000UL // 10 MHz
+#define TIMER_INTERVAL (TIME_FREQ / 100) // 100 Hz
+#define MS_TO_TIME(ms) ((ms) * (TIME_FREQ / 1000))
+#define MAX_SLEEP_MS 60 * 1000 // 1 minute
+
+static inline ulong
+read_time()
+{
+ return read_csr(time);
+}
+
+static inline void
+timer_init()
+{
+ write_csr(stimecmp, read_time() + TIMER_INTERVAL);
+}
+
+static inline void
+timer_handle()
+{
+ write_csr(stimecmp, read_time() + TIMER_INTERVAL);
+
+ //DEBUG_PRINT(
+ // uart_puts("timer_handle()\n");
+ //);
+
+ cpu_t *c = curr_cpu();
+ int need_sched = 0;
+ ulong now = read_time();
+
+ list_node_t *pos, *tmp;
+ list_for_each_safe(pos, tmp, &c->sleep_queue) {
+ proc_t *p = container_of(pos, proc_t, q_node);
+ if (now >= p->sleep_until) {
+ list_del(&p->q_node);
+ p->state = RUNNABLE;
+ list_add_tail(&p->q_node, &c->run_queue);
+ need_sched = 1;
+
+ DEBUG_PRINT(
+ uart_puts("CPU ");
+ uart_putc('0' + curr_cpu()->id);
+ uart_puts(": proc id ");
+ uart_putc('0' + p->pid);
+ uart_puts(" became runnable\n");
+ );
+ }
+ }
+
+
+ if (need_sched)
+ curr_cpu()->needs_sched = 1;
+}
blob - /dev/null
blob + 0674f8e94b718b049e16f886ca8234c37929f41a (mode 644)
--- /dev/null
+++ kernel/trap/trap.c
+#include "trap.h"
+#include "../riscv.h"
+#include "../uart.h"
+#include "../types.h"
+#include "../proc.h"
+#include "../timer.h"
+#include "../syscall.h"
+#include "../user_syscall.h"
+#include "../string.h"
+#include "../sched.h"
+
+void
+syscall_handler(proc_t *p)
+{
+ cpu_t *c = curr_cpu();
+ int syscall_num = p->tf->regs[17]; // a7 (syscall number)
+
+ switch (syscall_num) {
+ case SYS_write: {
+ DEBUG_PRINT(uart_puts("SYS_write\n"););
+ p->tf->regs[10] = sys_write(p->tf->regs[10], (void *)p->tf->regs[11], p->tf->regs[12]);
+ break;
+ }
+ case SYS_read: {
+ DEBUG_PRINT(uart_puts("SYS_read\n"););
+ p->tf->regs[10] = sys_read(p->tf->regs[10], (void *)p->tf->regs[11], p->tf->regs[12]);
+ break;
+ }
+ case SYS_exit:
+ DEBUG_PRINT(uart_puts("SYS_exit\n"););
+ p->state = ZOMBIE;
+ c->needs_sched = 1;
+ break;
+ case SYS_getpid:
+ DEBUG_PRINT(uart_puts("SYS_getpid\n"););
+ p->tf->regs[10] = p->pid;
+ break;
+ case SYS_yield:
+ DEBUG_PRINT(uart_puts("SYS_yield\n"););
+ c->needs_sched = 1;
+ break;
+ case SYS_sleep_ms:
+ DEBUG_PRINT(uart_puts("SYS_sleep_ms\n"););
+ p->tf->regs[10] = sys_sleep_ms(c, p, p->tf->regs[10]);
+ c->needs_sched = 1;
+ break;
+ case SYS_sbrk:
+ p->tf->regs[10] = sys_sbrk(p, p->tf->regs[10]);
+ break;
+ case SYS_munmap:
+ p->tf->regs[10] = sys_munmap(p, p->tf->regs[10], p->tf->regs[11]);
+ break;
+ default:
+ DEBUG_PRINT(
+ uart_puts("Unknown syscall number: ");
+ uart_puthex(syscall_num);
+ uart_putc('\n');
+ );
+ p->tf->regs[10] = -1;
+ break;
+ }
+}
+
+void
+trap_handler(trap_frame_t *tf)
+{
+ cpu_t *c = curr_cpu();
+ proc_t *p = c->proc;
+
+ ulong cause = read_csr(scause);
+ ulong code = SCAUSE_CODE(cause);
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Trap: ");
+ uart_puthex(code);
+ uart_putc('\n');
+
+ uart_puts("sepc: ");
+ uart_puthex(read_csr(sepc));
+ uart_puts("\n");
+
+ uart_puts("stval: ");
+ uart_puthex(read_csr(stval));
+ uart_puts("\n");
+ );
+ */
+
+ // Must not access tf on idle process, it is NULL!
+ if (p && p->is_idle) {
+ // Timer interrupt on idle core
+ if ((cause & SCAUSE_IRQ_BIT) && code == SCAUSE_TIMER_INTERRUPT) {
+ timer_handle();
+ goto end;
+ } else {
+ DEBUG_PRINT(
+ uart_puts("Trap on idle core? Should not happen, Halting...\n");
+ );
+ while (1)
+ asm volatile("wfi");
+ }
+ }
+
+ if (SCAUSE_CODE(tf->scause) == 5 && !(tf->scause & SCAUSE_IRQ_BIT)) {
+ DEBUG_PRINT(
+ uart_puts("Trap: ");
+ uart_puthex(SCAUSE_CODE(tf->scause));
+ uart_puts("\n");
+ );
+ while (1)
+ ;
+ }
+
+ // This shouldn't happen: every CPU should have a proc (idle or otherwise)
+ if (!p) {
+ // Timer interrupt
+ if ((cause & SCAUSE_IRQ_BIT) && code == SCAUSE_TIMER_INTERRUPT) {
+ timer_handle();
+ return;
+ }
+
+ // Weird - should not happen
+ DEBUG_PRINT(
+ uart_puts("No current process for CPU ");
+ uart_putc('0' + c->id);
+ uart_puts(" during trap!\n");
+ );
+ while (1)
+ asm volatile("wfi");
+ }
+
+ // Capture original process
+ //proc_t *orig_proc = p;
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Before saving tf: p->tf.sepc = ");
+ uart_puthex(p->tf.sepc);
+ uart_putc('\n');
+ );
+ */
+
+ //p->tf = tf;
+
+ // Enable interrupts (allow preemption)
+ //set_csr(sstatus, SSTATUS_SIE);
+
+ // Debug
+ /*
+ DEBUG_PRINT(
+ uart_puts("Trap frame at: ");
+ uart_puthex((ulong)&p->tf);
+ uart_putc('\n');
+ uart_puts("scause: ");
+ uart_puthex(p->tf.scause);
+ uart_putc('\n');
+ );
+ */
+
+ switch (code) {
+ case SCAUSE_USER_ECALL:
+ // User-mode (U-mode)
+ DEBUG_PRINT(
+ uart_puts("[S] U-mode system call received\n");
+ );
+ tf->sepc += 4;
+ syscall_handler(p);
+ break;
+ case SCAUSE_SUPERVISOR_ECALL:
+ // Supervisor-mode (S-mode)
+ DEBUG_PRINT(
+ uart_puts("[S] S-mode system call received\n");
+ //uart_puts("TF a7 = ");
+ //uart_puthex(p->tf.regs[17]); // a7
+ //uart_putc('\n');
+ );
+ tf->sepc += 4;
+ syscall_handler(p);
+ break;
+ case SCAUSE_TIMER_INTERRUPT:
+ // Timer interrupt
+ if (cause & SCAUSE_IRQ_BIT) {
+ /*
+ DEBUG_PRINT(
+ uart_putc('0' + c->id);
+ uart_puts("Normal timer interrupt\n");
+ );
+ */
+
+ timer_handle();
+ }
+ break;
+ case SCAUSE_SUPERVISOR_IRQ:
+ DEBUG_PRINT(
+ uart_putc('0' + c->id);
+ uart_puts(": [S] Supervisor Software Interrupt\n");
+ );
+ while(1);
+ break;
+ case SCAUSE_ILLEGAL_INSTR:
+ DEBUG_PRINT(
+ uart_puts("CPU ");
+ uart_putc('0' + c->id);
+ uart_puts(": [S] Illegal instruction at ");
+ uart_puthex(tf->sepc);
+ uart_putc('\n');
+ );
+ while(1);
+ break;
+ default:
+ DEBUG_PRINT(
+ uart_puts("[S] Unknown trap! cause=");
+ uart_puthex(cause);
+ uart_puts("\nHalting.\n");
+ );
+ while (1)
+ asm volatile("wfi");
+ }
+
+ // Disable interrupts
+ //clear_csr(sstatus, SSTATUS_SIE);
+
+ /*
+ DEBUG_PRINT(
+ uart_puts("Before restoring tf: p->tf->sepc = ");
+ uart_puthex(p->tf.sepc);
+ uart_putc('\n');
+ );
+ */
+
+end:
+ if (curr_cpu()->needs_sched) {
+ curr_cpu()->needs_sched = 0;
+ schedule();
+ }
+}
blob - /dev/null
blob + 80a4601e9bf172a42c1adcfc13b81360d4c557fe (mode 644)
--- /dev/null
+++ kernel/trap/trap.h
+#pragma once
+
+#include "../types.h"
+#include "../proc.h"
+
+#define SCAUSE_IRQ_BIT (1UL << 63)
+#define SCAUSE_CODE(scause) ((scause) & ~SCAUSE_IRQ_BIT)
+
+#define SCAUSE_SUPERVISOR_IRQ 1 // Supervisor software interrupt
+#define SCAUSE_ILLEGAL_INSTR 2 // Illegal instruction
+#define SCAUSE_TIMER_INTERRUPT 5 // Timer interrupt
+#define SCAUSE_USER_ECALL 8 // U-mode system call
+#define SCAUSE_SUPERVISOR_ECALL 9 // S-mode system call
+
+void trap_handler(trap_frame_t *tf);
+void trap_vector(); // asm entry point
+void idle_trap_vector(); // idle entry point
blob - /dev/null
blob + 108eab0f1001d2fa8f69f96aae6ee87213679f8d (mode 644)
--- /dev/null
+++ kernel/trap/trap_vec.S
+.section .text
+.global trap_vector
+trap_vector:
+ # Save trap cause early
+ csrr t1, scause
+
+ # Check SPP bit in sstatus: 0 = user, 1 = supervisor
+ csrr t0, sstatus
+ andi t0, t0, (1 << 8) # Check SPP bit (bit 8)
+ beqz t0, .from_user # If SPP == 0 -> from user mode
+ j .from_kernel
+
+.from_user:
+ # Load trap frame pointer from sscratch
+ csrr t0, sscratch
+
+ # Save user sp into trap frame (x2), offset 16
+ sd sp, 16(t0)
+
+ # Move trap frame to sp
+ mv sp, t0
+ j .save_regs
+
+.from_kernel:
+ # We're already on kernel stack, use current sp directly
+ # Reserve space for trap frame (36 x 8 = 288 bytes)
+ addi sp, sp, -288
+
+.save_regs:
+ # Save x1–x31 (excluding x0, and x2 already saved if from_user)
+ # sd x0 0(sp) # x0 (hardwired to zero)
+ sd x1, 8(sp) # ra
+ # x2 (sp) was saved manually (at 16) in .from_user
+ sd x3, 24(sp) # gp
+ sd x4, 32(sp) # tp
+ sd x5, 40(sp) # t0
+ sd x6, 48(sp) # t1
+ sd x7, 56(sp) # t2
+ sd x8, 64(sp) # s0/fp
+ sd x9, 72(sp) # s1
+ sd x10, 80(sp) # a0
+ sd x11, 88(sp) # a1
+ sd x12, 96(sp) # a2
+ sd x13, 104(sp) # a3
+ sd x14, 112(sp) # a4
+ sd x15, 120(sp) # a5
+ sd x16, 128(sp) # a6
+ sd x17, 136(sp) # a7
+ sd x18, 144(sp) # s2
+ sd x19, 152(sp) # s3
+ sd x20, 160(sp) # s4
+ sd x21, 168(sp) # s5
+ sd x22, 176(sp) # s6
+ sd x23, 184(sp) # s7
+ sd x24, 192(sp) # s8
+ sd x25, 200(sp) # s9
+ sd x26, 208(sp) # s10
+ sd x27, 216(sp) # s11
+ sd x28, 224(sp) # t3
+ sd x29, 232(sp) # t4
+ sd x30, 240(sp) # t5
+ sd x31, 248(sp) # t6
+
+ # Save CSRs
+ csrr t0, sepc
+ sd t0, 256(sp)
+ csrr t0, sstatus
+ sd t0, 264(sp)
+ sd t1, 272(sp) # scause (already read)
+ csrr t0, stval
+ sd t0, 280(sp)
+
+ # Pass trap_frame_t* in a0
+ mv a0, sp
+
+ call trap_handler
+
+ # Restore registers
+ # sd x0 0(sp) # x0 (hardwired to zero)
+ ld x1, 8(sp) # ra
+ # x2 (sp) handle separately
+ ld x3, 24(sp) # gp
+ ld x4, 32(sp) # tp
+ ld x5, 40(sp) # t0
+ ld x6, 48(sp) # t1
+ ld x7, 56(sp) # t2
+ ld x8, 64(sp) # s0/fp
+ ld x9, 72(sp) # s1
+ ld x10, 80(sp) # a0
+ ld x11, 88(sp) # a1
+ ld x12, 96(sp) # a2
+ ld x13, 104(sp) # a3
+ ld x14, 112(sp) # a4
+ ld x15, 120(sp) # a5
+ ld x16, 128(sp) # a6
+ ld x17, 136(sp) # a7
+ ld x18, 144(sp) # s2
+ ld x19, 152(sp) # s3
+ ld x20, 160(sp) # s4
+ ld x21, 168(sp) # s5
+ ld x22, 176(sp) # s6
+ ld x23, 184(sp) # s7
+ ld x24, 192(sp) # s8
+ ld x25, 200(sp) # s9
+ ld x26, 208(sp) # s10
+ ld x27, 216(sp) # s11
+ ld x28, 224(sp) # t3
+ ld x29, 232(sp) # t4
+ ld x30, 240(sp) # t5
+ ld x31, 248(sp) # t6
+
+ # Restore CSRs
+ ld t0, 256(sp)
+ csrw sepc, t0
+ ld t0, 264(sp)
+ csrw sstatus, t0
+
+ # If we entered from kernel mode, deallocate trap frame
+ csrr t0, sstatus
+ andi t0, t0, (1 << 8)
+ bnez t0, .restore_kernel_stack
+
+ # Restore user sp (x2)
+ ld x2, 16(sp)
+ sret
+
+.restore_kernel_stack:
+ addi sp, sp, 288
+ sret
+
+
+.global idle_trap_vector
+idle_trap_vector:
+ # Minimal context save
+ #addi sp, sp, -16
+ #sd ra, 0(sp)
+
+ csrr a0, scause
+ csrr a1, sepc
+ csrr a2, sstatus
+ csrr a3, stval
+
+ call trap_handler
+
+ #ld ra, 0(sp)
+ #addi sp, sp, 16
+
+ csrw sepc, a1
+ csrw sstatus, a2
+ sret
blob - /dev/null
blob + 5844b873e77732d2d514110a790de7428a6b6798 (mode 644)
--- /dev/null
+++ kernel/types.h
+#pragma once
+
+#define NULL 0
+
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef ulong pte_t;
+
+#ifdef DEBUG
+#define DEBUG_PRINT(...) \
+ do { \
+ spin_lock(&uart_lock); \
+ __VA_ARGS__; \
+ spin_unlock(&uart_lock); \
+ } while (0)
+#else
+#define DEBUG_PRINT(...) do {} while (0)
+#endif
blob - /dev/null
blob + 06019d39d62c24cd95b519bda815c0bc2ba66290 (mode 644)
--- /dev/null
+++ kernel/uart.c
+#include "uart.h"
+
+spinlock_t uart_lock = SPINLOCK_INIT;
+
+void
+uart_putc(char c)
+{
+ *(volatile char *)UART0 = c;
+}
+
+void
+uart_puts(const char *s)
+{
+ while (*s)
+ uart_putc(*s++);
+}
+
+ulong
+uart_putsn(const char *s, ulong len)
+{
+ ulong written = 0;
+ while (len-- && *s) {
+ uart_putc(*s++);
+ written++;
+ }
+
+ return written;
+}
+
+void
+uart_puthex(ulong x)
+{
+ uart_puts("0x");
+ for (int i = (sizeof(x) * 2) - 1; i >= 0; i--) {
+ int nibble = (x >> (i * 4)) & 0xf;
+ uart_putc(nibble < 10 ? '0' + nibble : 'a' + nibble - 10);
+ }
+}
+
+void
+uart_putlong(ulong val)
+{
+ char buf[21]; // Max for 64-bit decimal is 20 digits + null
+ int i = 20;
+ buf[i--] = '\0';
+
+ if (val == 0) {
+ uart_putc('0');
+ return;
+ }
+
+ while (val > 0 && i >= 0) {
+ buf[i--] = '0' + (val % 10);
+ val /= 10;
+ }
+
+ uart_puts(&buf[i + 1]);
+}
+
+char uart_getc(void)
+{
+ volatile char *uart = (volatile char *)UART0;
+
+ /*
+ while (1) {
+ if (uart[UART_LSR] & UART_LSR_RX_READY) {
+ return uart[UART_RHR]; // return valid char
+ }
+
+ // Give up CPU while waiting — cooperative multitasking
+ syscall0(SYS_yield);
+ }
+ */
+
+ // Wait until data is available in receive buffer
+ while ((uart[UART_LSR] & UART_LSR_RX_READY) == 0)
+ ;
+
+ return uart[UART_RHR];
+}
+
+int
+uart_gets(char *buf, int maxlen)
+{
+ int i = 0;
+ while (i < maxlen - 1) {
+ char c = uart_getc();
+ if (c == '\r') // Enter key is usually CR
+ c = '\n';
+ uart_putc(c); // Echo back
+
+ if (c == '\n') {
+ buf[i++] = c;
+ break;
+ }
+ buf[i++] = c;
+ }
+ buf[i] = 0; // Null-terminate
+
+ return i;
+}
blob - /dev/null
blob + d0a9fdb3f60205ef88835e3c2d2dc3e90c8a8ec5 (mode 644)
--- /dev/null
+++ kernel/uart.h
+#pragma once
+
+#include "spinlock.h"
+#include "mm/mapping.h"
+
+#define UART0 (0x10000000 + VA_OFFSET)
+#define UART_RHR 0 // Receive Holding Register (read only)
+#define UART_LSR 5 // Line Status Register
+#define UART_LSR_RX_READY (1 << 0) // Bit 0: Receiver data ready
+
+//extern ulong UART0;
+extern spinlock_t uart_lock;
+
+void uart_putc(char c);
+void uart_puts(const char* s);
+ulong uart_putsn(const char* s, ulong len);
+void uart_puthex(ulong x);
+void uart_putlong(ulong val);
+
+char uart_getc(void);
+int uart_gets(char *buf, int maxlen);
blob - /dev/null
blob + 4eb1d7055db17d4e6466ee0e69c2b029633fa65f (mode 644)
--- /dev/null
+++ kernel/user.h
+#pragma once
+
+#include "user_syscall.h"
+
+static inline long
+write(const void *buf, unsigned long cnt)
+{
+ // only stdout for now
+ return syscall3(SYS_write, 1, (long)buf, cnt);
+}
+
+static inline void
+exit(int status)
+{
+ syscall1(SYS_exit, status);
+ //while (1) {} // Should never return
+}
+
+static inline long
+getpid()
+{
+ return syscall0(SYS_getpid);
+}
+
+static inline void
+yield()
+{
+ syscall0(SYS_yield);
+}
+
+static inline long
+read(void *buf, unsigned long cnt)
+{
+ return syscall3(SYS_read, 0, (long)buf, cnt);
+}
+
+static inline long
+sleep(unsigned long ms)
+{
+ if (ms == 0 || ms > (60 * 1000))
+ return 0;
+
+ return syscall1(SYS_sleep_ms, ms);
+}
+
+static inline long
+sbrk(unsigned long size)
+{
+ return syscall1(SYS_sbrk, size);
+}
+
+static inline long
+munmap(void *addr, unsigned long size)
+{
+ return syscall2(SYS_munmap, (long)addr, size);
+}
+
+// Move to stdlib
+void *
+malloc(unsigned long size)
+{
+ long mem = sbrk(size);
+ if (mem == -1)
+ return 0; //NULL;
+
+ return (void *)mem;
+}
blob - /dev/null
blob + ac61b53e1c52a32f9ba7b43cd6f9b7c8b23ace43 (mode 644)
--- /dev/null
+++ kernel/user.ld
+ENTRY(user_shell_main)
+
+SECTIONS {
+ . = 0x84000000;
+
+ .text : {
+ PROVIDE(user_shell_main = .);
+ *(.text.user_shell_main)
+ *(.text*)
+ }
+
+ .rodata : {
+ *(.rodata*)
+ }
+
+ .data : {
+ *(.data*)
+ }
+
+ .bss : {
+ *(.bss*)
+ *(COMMON)
+ }
+}
blob - /dev/null
blob + 5b4c0faadacf3147c4d5585ad3a17e82a32fcac5 (mode 644)
--- /dev/null
+++ kernel/user_syscall.h
+#pragma once
+
+#define SYS_write 1 //64
+#define SYS_exit 2 //93
+#define SYS_getpid 3 //172
+#define SYS_yield 4 //124
+#define SYS_read 5 //63
+#define SYS_sleep_ms 6
+#define SYS_sbrk 7
+#define SYS_munmap 8
+
+//#define SYS_fork 220
+//#define SYS_open 102
+//#define SYS_close 57
+
+static inline long
+syscall(long num, long arg0, long arg1, long arg2,
+ long arg3, long arg4, long arg5)
+{
+ 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 a7 asm("a7") = num;
+
+ asm volatile (
+ "ecall"
+ : "+r"(a0)
+ : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a7)
+ : "memory"
+ );
+
+ return a0;
+}
+
+static inline long
+syscall0(long num)
+{
+ return syscall(num, 0, 0, 0, 0, 0, 0);
+}
+
+static inline long
+syscall1(long num, long arg0)
+{
+ return syscall(num, arg0, 0, 0, 0, 0, 0);
+}
+
+static inline long
+syscall2(long num, long arg0, long arg1)
+{
+ return syscall(num, arg0, arg1, 0, 0, 0, 0);
+}
+
+static inline long
+syscall3(long num, long arg0, long arg1, long arg2)
+{
+ return syscall(num, arg0, arg1, arg2, 0, 0, 0);
+}