summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-01-02 00:47:16 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-01-02 00:58:45 +0800
commitd8eaa13f2efc1eeca4e5f230d5a2d535aaf27338 (patch)
tree6ca02e2a2d1b1121620431059e322724a3c94a33 /src/kernel
parent2dde56422dcfb94affee2baffc8d240ad16ad4b2 (diff)
downloadlume-d8eaa13f2efc1eeca4e5f230d5a2d535aaf27338.tar.gz
lume-d8eaa13f2efc1eeca4e5f230d5a2d535aaf27338.tar.xz
dev(file-system): establish VFS and Read-only FAT32 File System
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/bio.cc135
-rw-r--r--src/kernel/cxx.cc68
-rw-r--r--src/kernel/main.cc8
-rw-r--r--src/kernel/mm/vm.cc47
-rw-r--r--src/kernel/proc/proc.cc33
-rw-r--r--src/kernel/syscall.cc165
6 files changed, 423 insertions, 33 deletions
diff --git a/src/kernel/bio.cc b/src/kernel/bio.cc
new file mode 100644
index 0000000..6e63d60
--- /dev/null
+++ b/src/kernel/bio.cc
@@ -0,0 +1,135 @@
+#include "kernel/buf.h"
+#include "kernel/spinlock.h"
+#include "kernel/riscv.h"
+#include "drivers/virtio.h"
+#include "drivers/uart.h"
+
+namespace BufferCache
+{
+ struct
+ {
+ Spinlock lock;
+ struct Buf buf[NBUF];
+ struct Buf head;
+ } bcache;
+
+ void init()
+ {
+ bcache.lock.init("bcache");
+ // Initialize LRU doubly circular linked list
+ bcache.head.prev = &bcache.head;
+ bcache.head.next = &bcache.head;
+ for (struct Buf *b = bcache.buf; b < bcache.buf + NBUF; ++b)
+ {
+ b->lock.init("buffer");
+ b->next = bcache.head.next;
+ b->prev = &bcache.head;
+ bcache.head.next->prev = b;
+ bcache.head.next = b;
+ }
+ Drivers::uart_puts("[Bio] Buffer cache initialized.\n");
+ }
+
+ static struct Buf* bget(uint32 dev, uint32 blockno)
+ {
+ struct Buf* b;
+ bcache.lock.acquire();
+
+ // Attempt to hit the cache
+ for (b = bcache.head.next; b != &bcache.head; b = b->next)
+ {
+ if (b->dev == dev && b->blockno == blockno)
+ {
+ b->refcnt++;
+ bcache.lock.release();
+ b->lock.acquire(); // Acquire Sleep Lock
+ return b;
+ }
+ }
+
+ // Cache miss, evicting LRU
+ for (b = bcache.head.prev; b != &bcache.head; b = b->prev)
+ {
+ if (b->refcnt == 0)
+ {
+ b->dev = dev;
+ b->blockno = blockno;
+ b->valid = 0;
+ b->refcnt = 1;
+ bcache.lock.release();
+ b->lock.acquire();
+ return b;
+ }
+ }
+
+ Drivers::uart_puts("panic: bget: no buffers\n");
+ while (1)
+ ;
+ }
+
+ struct Buf* bread(uint32 dev, uint32 blockno)
+ {
+ struct Buf *b = bget(dev, blockno);
+
+ if (!b->valid)
+ {
+ VirtIO::rw(0, b->blockno, (char *)b->data);
+ b->valid = 1;
+ }
+
+ return b;
+ }
+
+ void bwrite(struct Buf *b)
+ {
+ if (!b->lock.holding())
+ {
+ Drivers::uart_puts("panic: bwrite\n");
+ while (1)
+ ;
+ }
+ VirtIO::rw(1, b->blockno, (char *)b->data);
+ }
+
+ void brelse(struct Buf *b)
+ {
+ if (!b->lock.holding())
+ {
+ Drivers::uart_puts("panic: brelse\n");
+ while (1)
+ ;
+ }
+ b->lock.release();
+
+ bcache.lock.acquire();
+ b->refcnt--;
+
+ if (b->refcnt == 0)
+ {
+ b->next->prev = b->prev;
+ b->prev->next = b->next;
+
+ b->next = bcache.head.next;
+ b->prev = &bcache.head;
+ bcache.head.next->prev = b;
+ bcache.head.next = b;
+ }
+
+ bcache.lock.release();
+ }
+
+ void bpin(struct Buf *b)
+ {
+ bcache.lock.acquire();
+ b->refcnt++;
+ bcache.lock.release();
+ }
+
+ void bunpin(struct Buf* b)
+ {
+ bcache.lock.acquire();
+ b->refcnt --;
+ bcache.lock.release();
+ }
+
+} // namespace BufferCache
diff --git a/src/kernel/cxx.cc b/src/kernel/cxx.cc
new file mode 100644
index 0000000..f9541b5
--- /dev/null
+++ b/src/kernel/cxx.cc
@@ -0,0 +1,68 @@
+#include "common/types.h"
+#include "kernel/pmm.h"
+#include "drivers/uart.h"
+
+/*
+ * C++ Kernel Runtime Support
+ *
+ * * Strategy: Map directly to PMM::alloc_page().
+ * Pros: Simple, works immediately for VFS.
+ * Cons: Allocates 4KB for every object (wasteful for small objects),
+ * but acceptable for current stage.
+ */
+
+void *operator new(unsigned long size)
+{
+ // Safety check: PMM allocates 4096 bytes max per call
+ if (size > 4096)
+ {
+ Drivers::uart_puts("panic: new allocation too big (max 4096)\n");
+ while (1)
+ ;
+ }
+
+ void *p = PMM::alloc_page();
+ if (!p)
+ {
+ Drivers::uart_puts("panic: new out of memory\n");
+ while (1)
+ ;
+ }
+ // PMM::alloc_page returns zeroed memory usually,
+ // but standard 'new' doesn't guarantee it.
+ // The constructor will handle initialization.
+ return p;
+}
+
+void *operator new[](unsigned long size)
+{
+ return operator new(size);
+}
+
+void operator delete(void *p)
+{
+ if (p)
+ PMM::free_page(p);
+}
+
+void operator delete[](void *p)
+{
+ if (p)
+ PMM::free_page(p);
+}
+
+// C++14 Sized Deallocation (Required by modern compilers)
+// The compiler optimizes 'delete' calls to pass the size of the object
+void operator delete(void *p, unsigned long size)
+{
+ (void)size; // We ignore size because PMM always frees a full page
+ if (p)
+ PMM::free_page(p);
+}
+
+void operator delete[](void *p, unsigned long size)
+{
+ (void)size;
+ if (p)
+ PMM::free_page(p);
+} \ No newline at end of file
diff --git a/src/kernel/main.cc b/src/kernel/main.cc
index 6e3bdde..9654c18 100644
--- a/src/kernel/main.cc
+++ b/src/kernel/main.cc
@@ -9,6 +9,11 @@
#include "kernel/trap.h"
#include "kernel/timer.h"
#include "kernel/cpu.h"
+#include "kernel/buf.h"
+#include "fs/fs.h"
+#include "fs/file.h"
+#include "fs/fat32.h"
+#include "lib/string.h"
extern void fdt_init(uint64 dtb);
extern uint64 g_dtb_addr;
@@ -34,6 +39,9 @@ extern "C" void kernel_main(uint64 hartid, uint64 dtb)
PLIC::init();
PLIC::inithart();
VirtIO::init();
+ BufferCache::init();
+ VFS::init();
+ FileTable::init();
Timer::init();
ProcManager::init(); // Process Management
ProcManager::user_init(); // Initialize first user process
diff --git a/src/kernel/mm/vm.cc b/src/kernel/mm/vm.cc
index 7057d88..29e9eb7 100644
--- a/src/kernel/mm/vm.cc
+++ b/src/kernel/mm/vm.cc
@@ -313,6 +313,53 @@ namespace VM
return 0;
}
+ int copyinstr(uint64 *pagetable, char *dst, uint64 srcva, uint64 max)
+ {
+ uint64 n, va0, pa0;
+ uint64 *pte;
+ int got_null = 0;
+ uint64 len = 0;
+
+ while (len < max)
+ {
+ va0 = PGROUNDDOWN(srcva);
+ pte = walk(pagetable, va0, 0);
+
+ if (pte == nullptr || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
+ return -1;
+
+ pa0 = PTE2PA(*pte);
+
+ n = PGSIZE - (srcva - va0);
+ if (n > max - len)
+ n = max - len;
+
+ char *p = (char *)(pa0 + (srcva - va0));
+ while (n > 0)
+ {
+ if (*p == '\0')
+ {
+ *dst = '\0';
+ got_null = 1;
+ break;
+ }
+ *dst = *p;
+ n--;
+ srcva++;
+ dst++;
+ p++;
+ len++;
+ }
+ if (got_null)
+ break;
+ }
+
+ if (got_null)
+ return 0;
+
+ return -1;
+ }
+
int handle_cow_fault(uint64 *pagetable, uint64 va)
{
if (va >= MAXVA)
diff --git a/src/kernel/proc/proc.cc b/src/kernel/proc/proc.cc
index c8b6d1d..f1f1c1d 100644
--- a/src/kernel/proc/proc.cc
+++ b/src/kernel/proc/proc.cc
@@ -8,6 +8,8 @@
#include "kernel/trap.h"
#include "kernel/riscv.h"
#include "drivers/uart.h"
+#include "fs/fs.h"
+#include "fs/file.h"
#include "lib/string.h"
#define NPROC 64
@@ -172,6 +174,9 @@ found:
p->pid = nextpid++;
p->priority = 0;
p->cpu = mycpu();
+ p->cwd = nullptr;
+ for (int i = 0; i < NOFILE; i++)
+ p->ofile[i] = nullptr;
if ((p->tf = (struct Trapframe *)PMM::alloc_page()) == 0)
{
@@ -206,6 +211,19 @@ static void freeproc(struct Proc *p)
p->kstack = 0;
if (p->pagetable)
VM::uvmfree(p->pagetable, p->sz);
+ for (int i = 0; i < NOFILE; i++)
+ {
+ if (p->ofile[i])
+ {
+ FileTable::close(p->ofile[i]);
+ p->ofile[i] = nullptr;
+ }
+ }
+ if (p->cwd)
+ {
+ VFS::iput(p->cwd);
+ p->cwd = nullptr;
+ }
p->pagetable = 0;
p->sz = 0;
p->pid = 0;
@@ -245,6 +263,19 @@ int fork()
np->parent = p;
np->priority = p->priority;
+ for (int i = 0; i < NOFILE; i++)
+ {
+ if (p->ofile[i])
+ {
+ np->ofile[i] = FileTable::dup(p->ofile[i]);
+ }
+ }
+
+ if (p->cwd)
+ {
+ np->cwd = VFS::idup(p->cwd);
+ }
+
proc_mem_lock.release();
struct cpu *c = mycpu();
@@ -394,6 +425,8 @@ namespace ProcManager
p->sz = stack_va + PGSIZE;
strcpy(p->name, "initcode");
+ p->cwd = nullptr;
+
proc_mem_lock.release();
struct cpu *c = &cpus[0];
diff --git a/src/kernel/syscall.cc b/src/kernel/syscall.cc
index 837ac2c..afa20a1 100644
--- a/src/kernel/syscall.cc
+++ b/src/kernel/syscall.cc
@@ -1,5 +1,6 @@
#include "kernel/syscall.h"
#include "common/types.h"
+#include "common/fcntl.h"
#include "kernel/proc.h"
#include "kernel/riscv.h"
#include "drivers/uart.h"
@@ -7,6 +8,8 @@
#include "kernel/timer.h"
#include "kernel/mm.h"
#include "kernel/trap.h"
+#include "fs/fat32.h"
+#include "fs/file.h"
extern "C++" int fork();
@@ -45,47 +48,126 @@ static int argint(int n, int *ip)
return 0;
}
+static int argstr(int n, char *buf, int max)
+{
+ uint64 addr = argraw(n);
+ return VM::copyinstr(myproc()->pagetable, buf, addr, max);
+}
+
+static int fdalloc(struct file *f)
+{
+ struct Proc *p = myproc();
+ for (int i = 0; i < NOFILE; i++)
+ {
+ if (p->ofile[i] == 0)
+ {
+ p->ofile[i] = f;
+ return i;
+ }
+ }
+ return -1;
+}
+
+static uint64 sys_open()
+{
+ char path[128];
+ int omode;
+ int fd;
+ struct file *f;
+ Inode *ip;
+
+ if (argstr(0, path, 128) < 0 || argint(1, &omode) < 0)
+ return -1;
+
+ ip = VFS::namei(path);
+ if (ip == nullptr)
+ {
+ return -1;
+ }
+
+ f = FileTable::alloc();
+ if (f == nullptr)
+ {
+ VFS::iput(ip);
+ return -1;
+ }
+
+ fd = fdalloc(f);
+ if (fd < 0)
+ {
+ f->ref = 0;
+ f->type = FD_NONE;
+ VFS::iput(ip);
+ return -1;
+ }
+
+ f->type = FD_INODE;
+ f->ip = ip;
+ f->off = 0;
+ f->readable = !(omode & O_WRONLY);
+ f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
-static uint64_t sys_exit()
+ return fd;
+}
+
+static uint64 sys_close()
+{
+ int fd;
+ if (argint(0, &fd) < 0)
+ return -1;
+
+ struct Proc *p = myproc();
+ if (fd < 0 || fd >= NOFILE || p->ofile[fd] == 0)
+ return -1;
+
+ FileTable::close(p->ofile[fd]);
+ p->ofile[fd] = 0;
+ return 0;
+}
+
+static uint64 sys_exit()
{
int n;
if (argint(0, &n) < 0)
- return static_cast<uint64_t>(-1);
+ return static_cast<uint64>(-1);
ProcManager::exit(n);
return 0;
}
-static uint64_t sys_fork()
+static uint64 sys_fork()
{
- return static_cast<uint64_t>(fork());
+ return static_cast<uint64>(fork());
}
-static uint64_t sys_wait()
+static uint64 sys_wait()
{
- uint64_t p = argraw(0);
- return static_cast<uint64_t>(ProcManager::wait(p));
+ uint64 p = argraw(0);
+ return static_cast<uint64>(ProcManager::wait(p));
}
-static uint64_t sys_getpid()
+static uint64 sys_getpid()
{
- return static_cast<uint64_t>(myproc()->pid);
+ return static_cast<uint64>(myproc()->pid);
}
-static uint64_t sys_putc()
+static uint64 sys_putc()
{
char c = static_cast<char>(argraw(0));
Drivers::uart_putc(c);
return 0;
}
-static uint64_t sys_write()
+static uint64 sys_write()
{
- int fd;
+ struct file *f;
int n;
- uint64_t p;
+ uint64 p;
+ int fd;
- if (argint(0, &fd) < 0 || argint(2, &n) < 0)
- return static_cast<uint64_t>(-1);
+ if (argint(0, &fd) < 0 || argint(2, &n) < 0 || argint(1, (int *)&p) < 0)
+ return -1;
+
+ struct Proc *proc = myproc();
p = argraw(1);
@@ -95,9 +177,8 @@ static uint64_t sys_write()
// Drivers::uart_put_int(n);
// Drivers::uart_puts("\n");
- if (fd == 1) // stdout
+ if (fd == 1 && proc->ofile[fd] == nullptr) // stdout
{
- Proc *proc = myproc();
constexpr int MAX_WRITE_BUF = 128;
char buf[MAX_WRITE_BUF];
int i = 0;
@@ -108,10 +189,10 @@ static uint64_t sys_write()
if (len > MAX_WRITE_BUF)
len = MAX_WRITE_BUF;
- if (VM::copyin(proc->pagetable, buf, p + i, static_cast<uint64_t>(len)) < 0)
+ if (VM::copyin(proc->pagetable, buf, p + i, static_cast<uint64>(len)) < 0)
{
Drivers::uart_puts("sys_write: copyin failed\n");
- return static_cast<uint64_t>(-1);
+ return static_cast<uint64>(-1);
}
for (int j = 0; j < len; j++)
@@ -120,45 +201,57 @@ static uint64_t sys_write()
}
i += len;
}
- return static_cast<uint64_t>(n);
+ return static_cast<uint64>(n);
}
- return static_cast<uint64_t>(-1);
+ if (fd < 0 || fd >= NOFILE || (f = proc->ofile[fd]) == 0)
+ return static_cast<uint64>(-1);
+
+ return FileTable::write(f, p, n);
}
static uint64_t sys_read()
{
- int fd;
+ struct file *f;
int n;
- uint64_t p;
+ uint64 p;
+ int fd;
// get param: fd, buf, len
- if (argint(0, &fd) < 0 || argint(2, &n) < 0)
+ if (argint(0, &fd) < 0 || argint(2, &n) < 0 || argint(1, (int *)&p) < 0)
return -1;
- p = argraw(1);
-
- if (fd == 0) // stdin
+
+ struct Proc* proc = myproc();
+ if (fd == 0 && proc->ofile[fd] == nullptr)
{
return Drivers::console_read(p, n);
}
+
+ if (fd < 0 || fd >= NOFILE || (f = proc->ofile[fd]) == 0)
+ return -1;
+
+ return FileTable::read(f, p, n);
+
return -1;
}
-static uint64_t sys_sbrk()
+static uint64 sys_sbrk()
{
int n;
if (argint(0, &n) < 0)
return -1;
- uint64_t addr = myproc()->sz;
+ uint64 addr = myproc()->sz;
if (ProcManager::growproc(n) < 0)
return -1;
return addr;
}
-static uint64_t sys_disk_test()
+static uint64 sys_disk_test()
{
- VirtIO::test_rw();
+ // VirtIO::test_rw();
+ // VirtIO::test_bio();
+ fat32_test();
return 0;
}
@@ -166,7 +259,7 @@ void syscall()
{
Proc *p = myproc();
int num = p->tf->a7;
- uint64_t ret = static_cast<uint64_t>(-1);
+ uint64 ret = static_cast<uint64>(-1);
/* Just For Debug */
// Drivers::uart_puts("Syscall: ");
@@ -175,6 +268,12 @@ void syscall()
switch (num)
{
+ case SYS_open:
+ ret = sys_open();
+ break;
+ case SYS_close:
+ ret = sys_close();
+ break;
case SYS_write:
ret = sys_write();
break;
@@ -206,7 +305,7 @@ void syscall()
Drivers::uart_puts("Unknown Syscall ID: ");
Drivers::print_hex(num);
Drivers::uart_puts("\n");
- ret = static_cast<uint64_t>(-1);
+ ret = static_cast<uint64>(-1);
break;
}