summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/drivers/virtio_dist.cc36
-rw-r--r--src/fs/fat32.cc325
-rw-r--r--src/fs/file.cc140
-rw-r--r--src/fs/fs.cc190
-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
-rw-r--r--src/lib/string.cc13
-rw-r--r--src/user/init.cc36
12 files changed, 1162 insertions, 34 deletions
diff --git a/src/drivers/virtio_dist.cc b/src/drivers/virtio_dist.cc
index 99d100f..9c981d4 100644
--- a/src/drivers/virtio_dist.cc
+++ b/src/drivers/virtio_dist.cc
@@ -5,6 +5,7 @@
#include "lib/string.h"
#include "drivers/uart.h"
#include "kernel/proc.h"
+#include "kernel/buf.h"
static uint64 virtio_base = 0x10001000;
@@ -335,4 +336,39 @@ namespace VirtIO
PMM::free_page(buf);
}
+
+ void test_bio()
+ {
+ Drivers::uart_puts(ANSI_BLUE "[Bio] Starting Buffer Cache Test...\n" ANSI_RESET);
+
+ struct Buf *b = BufferCache::bread(0, 100);
+
+ Drivers::uart_puts("[Bio] Bread block 100 (1st time).\n");
+
+ b->data[0] = 'L';
+ b->data[1] = 'u';
+ b->data[2] = 'm';
+ b->data[3] = 'e';
+ b->data[4] = '\0';
+
+ BufferCache::bwrite(b);
+ Drivers::uart_puts("[Bio] Bwrite block 100.\n");
+
+ BufferCache::brelse(b);
+ Drivers::uart_puts("[Bio] Brelease block 100.\n");
+
+ struct Buf *b2 = BufferCache::bread(0, 100);
+ Drivers::uart_puts("[Bio] Bread block 100 (2nd time).\n");
+
+ if (b2->data[0] == 'L' && b2->data[1] == 'u' && b2->data[3] == 'e')
+ {
+ Drivers::uart_puts(ANSI_GREEN "[Bio] Data verification PASSED.\n" ANSI_RESET);
+ }
+ else
+ {
+ Drivers::uart_puts(ANSI_RED "[Bio] Data verification FAILED.\n" ANSI_RESET);
+ }
+
+ BufferCache::brelse(b2);
+ }
} \ No newline at end of file
diff --git a/src/fs/fat32.cc b/src/fs/fat32.cc
new file mode 100644
index 0000000..7ef829a
--- /dev/null
+++ b/src/fs/fat32.cc
@@ -0,0 +1,325 @@
+#include "fs/fat32.h"
+#include "lib/string.h"
+#include "drivers/uart.h"
+#include "kernel/mm.h"
+#include "kernel/proc.h"
+
+FAT32FileSystem fat32_fs;
+
+// FAT32 FileSystem Implementation
+
+void FAT32FileSystem::init()
+{
+ struct Buf *b = BufferCache::bread(0, 0);
+ uint8 *data = (uint8 *)b->data;
+ struct FAT32_BPB *bpb = (struct FAT32_BPB *)data;
+
+ uint32 partition_lba = 0;
+
+ bool is_valid_bpb = (bpb->bytesPerSector == 512) && (bpb->rootClus != 0);
+
+ if (!is_valid_bpb)
+ {
+ Drivers::uart_puts("[FAT32] Sector 0 is not a valid BPB. Checking MBR...\n");
+
+ if (data[510] != 0x55 || data[511] != 0xAA)
+ {
+ Drivers::uart_puts("[FAT32] Panic: Invalid Disk Signature!\n");
+ while (1)
+ ;
+ }
+
+ uint32 lba = *(uint32 *)(data + 446 + 8);
+
+ if (lba > 0)
+ {
+ Drivers::uart_puts("[FAT32] MBR Partition 1 found @ Sector ");
+ Drivers::uart_put_int(lba);
+ Drivers::uart_puts("\n");
+ partition_lba = lba;
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] Panic: No partition found.\n");
+ while (1)
+ ;
+ }
+
+ BufferCache::brelse(b);
+
+ b = BufferCache::bread(0, partition_lba / 2);
+ bpb = (struct FAT32_BPB *)b->data;
+ }
+
+ if (bpb->bytesPerSector != 512)
+ {
+ Drivers::uart_puts("[FAT32] Panic: Invalid FAT32 BPB in partition.\n");
+ while (1)
+ ;
+ }
+
+ uint32 fat_sz = bpb->FATSz32;
+ uint32 reserved_sec = bpb->reservedSecCnt;
+ uint8 num_fats = bpb->numFATs;
+
+ sectors_per_cluster = bpb->secPerClus;
+ bytes_per_cluster = sectors_per_cluster * 512;
+ root_cluster = bpb->rootClus;
+
+ fat_start_sector = partition_lba + reserved_sec;
+ data_start_sector = partition_lba + reserved_sec + (fat_sz * num_fats);
+
+ Drivers::uart_puts("[FAT32] Init Success: RootClus=");
+ Drivers::uart_put_int(root_cluster);
+ Drivers::uart_puts(", DataStart=");
+ Drivers::uart_put_int(data_start_sector);
+ Drivers::uart_puts("\n");
+
+ BufferCache::brelse(b);
+}
+
+Inode *FAT32FileSystem::root()
+{
+ FAT32Inode *ip = new FAT32Inode();
+
+ ip->fs = this;
+ ip->dev = 0;
+ ip->inum = root_cluster;
+ ip->ref_cnt = 1;
+ ip->valid = 1;
+ ip->type = T_DIR;
+ ip->size = 0;
+ ip->nlink = 1;
+
+ ip->first_clus = root_cluster;
+ ip->parent_clus = 0;
+
+ return ip;
+}
+
+uint32 FAT32FileSystem::cluster_to_sector(uint32 cluster)
+{
+ return data_start_sector + (cluster - 2) * sectors_per_cluster;
+}
+
+uint32 FAT32FileSystem::next_cluster(uint32 cluster)
+{
+ uint32 fat_offset = cluster * 4;
+ uint32 fat_sector = fat_start_sector + (fat_offset / 512);
+ uint32 ent_offset = fat_offset % 512;
+
+ // block = sector / 2
+ // offset in block = (sector % 2) * 512 + ent_offset
+
+ struct Buf *b = BufferCache::bread(0, fat_sector / 2);
+
+ uint32 block_offset = (fat_sector % 2) * 512 + ent_offset;
+ uint32 next = *(uint32 *)(b->data + block_offset);
+
+ BufferCache::brelse(b);
+
+ return next & 0x0FFFFFFF;
+}
+
+// FAT32 Inode Implementation
+
+static bool match_name(const char *disk_name, const char *target)
+{
+ char temp[13];
+ int i, j;
+
+ for (i = 0; i < 8 && disk_name[i] != ' '; i++)
+ temp[i] = disk_name[i];
+ if (disk_name[8] != ' ')
+ {
+ temp[i++] = '.';
+ for (j = 0; j < 3 && disk_name[8 + j] != ' '; j++)
+ temp[i++] = disk_name[8 + j];
+ }
+ temp[i] = 0;
+ return strcasecmp(temp, target) == 0;
+}
+
+int FAT32Inode::read(char *dst, uint32 off, uint32 n, int isUser)
+{
+ if (first_clus == 0)
+ return 0;
+
+ uint32 tot = 0;
+ uint32 clus = first_clus;
+ uint32 clus_sz = fs->bytes_per_cluster;
+
+ uint32 skip = off / clus_sz;
+ uint32 current_off = off % clus_sz;
+
+ while (skip > 0)
+ {
+ clus = fs->next_cluster(clus);
+ if (clus >= 0x0FFFFFF8)
+ return 0;
+ skip--;
+ }
+
+ while (tot < n)
+ {
+ uint32 sec_in_clus = current_off / 512;
+
+ if (sec_in_clus >= fs->sectors_per_cluster)
+ {
+ clus = fs->next_cluster(clus);
+ if (clus >= 0x0FFFFFF8)
+ break;
+ current_off = 0;
+ continue;
+ }
+
+ uint32 sec = fs->cluster_to_sector(clus) + sec_in_clus;
+ uint32 off_in_sec = current_off % 512;
+
+ uint32 m = 512 - off_in_sec;
+ if (n - tot < m)
+ m = n - tot;
+
+ struct Buf *b = BufferCache::bread(dev, sec / 2);
+ uint32 block_offset = (sec % 2) * 512 + off_in_sec;
+
+ if (isUser)
+ {
+ if (VM::copyout(myproc()->pagetable, (uint64)dst, (char *)(b->data + block_offset), m) < 0)
+ {
+ BufferCache::brelse(b);
+ return -1;
+ }
+ }
+ else
+ {
+ memmove(dst, b->data + block_offset, m);
+ }
+ BufferCache::brelse(b);
+
+ tot += m;
+ dst += m;
+ current_off += m;
+ }
+ return tot;
+}
+
+Inode *FAT32Inode::lookup(const char *name)
+{
+ if (type != T_DIR)
+ return nullptr;
+
+ uint32 clus = first_clus;
+ struct FAT32_DirEnt de;
+
+ while (clus < 0x0FFFFFF8)
+ {
+ uint32 sec = fs->cluster_to_sector(clus);
+
+ for (int i = 0; i < fs->sectors_per_cluster; i++)
+ {
+ uint32 current_sec = sec + i;
+ struct Buf *b = BufferCache::bread(dev, current_sec / 2);
+ uint32 block_base = (current_sec % 2) * 512;
+
+ for (int off = 0; off < 512; off += 32)
+ {
+ memmove(&de, b->data + block_base + off, 32);
+
+ if (de.name[0] == 0x00)
+ {
+ BufferCache::brelse(b);
+ return nullptr;
+ }
+ if (de.name[0] == 0xE5)
+ continue;
+ if (de.attr & ATTR_LONG_NAME)
+ continue;
+
+ if (match_name(de.name, name))
+ {
+ FAT32Inode *ip = new FAT32Inode();
+ ip->fs = fs;
+ ip->dev = dev;
+ ip->type = (de.attr & ATTR_DIRECTORY) ? T_DIR : T_FILE;
+ ip->size = de.fileSize;
+ ip->first_clus = (de.fstClusHI << 16) | de.fstClusLO;
+ ip->inum = ip->first_clus;
+ ip->nlink = 1;
+ ip->ref_cnt = 1;
+ ip->valid = 1;
+
+ BufferCache::brelse(b);
+ return ip;
+ }
+ }
+ BufferCache::brelse(b);
+ }
+ clus = fs->next_cluster(clus);
+ }
+ return nullptr;
+}
+
+// Placeholder
+void FAT32Inode::update() {}
+int FAT32Inode::write(const char *src, uint32 off, uint32 n, int isUser) { return -1; }
+Inode *FAT32Inode::create(const char *name, short type, short major, short minor) { return nullptr; }
+void FAT32Inode::truncate() {}
+uint32 FAT32FileSystem::alloc_cluster() { return 0; }
+
+// Test Function
+void fat32_test()
+{
+ static bool inited = false;
+ if (!inited)
+ {
+ fat32_fs.init();
+ inited = true;
+ }
+
+ Drivers::uart_puts("\n[FAT32] Starting Test via Syscall...\n");
+
+ Inode *root = fat32_fs.root();
+ if (root)
+ {
+ Drivers::uart_puts("[FAT32] Root Inode retrieved. Inum: ");
+ Drivers::uart_put_int(root->inum);
+ Drivers::uart_puts("\n");
+
+ Drivers::uart_puts("[FAT32] Looking for TEST.TXT...\n");
+ Inode *node = root->lookup("test.txt");
+
+ if (node)
+ {
+ Drivers::uart_puts("[FAT32] Found TEST.TXT! Size: ");
+ Drivers::uart_put_int(node->size);
+ Drivers::uart_puts("\n");
+
+ char buf[128];
+ int n = node->read(buf, 0, 127, 0);
+ if (n > 0)
+ {
+ buf[n] = 0;
+ Drivers::uart_puts("[FAT32] Content:\n>> ");
+ Drivers::uart_puts(buf);
+ Drivers::uart_puts("\n");
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] Read returned 0.\n");
+ }
+ VFS::iput(node);
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] TEST.TXT not found.\n");
+ }
+ VFS::iput(root);
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] Failed to get root inode.\n");
+ }
+
+ Drivers::uart_puts("[FAT32] Test Finished.\n");
+} \ No newline at end of file
diff --git a/src/fs/file.cc b/src/fs/file.cc
new file mode 100644
index 0000000..48af49e
--- /dev/null
+++ b/src/fs/file.cc
@@ -0,0 +1,140 @@
+#include "fs/file.h"
+#include "kernel/spinlock.h"
+#include "kernel/mm.h"
+#include "drivers/uart.h"
+#include "kernel/proc.h"
+
+#define NFILE 100
+
+namespace FileTable
+{
+ struct
+ {
+ Spinlock lock;
+ struct file files[NFILE];
+ } ftable;
+
+ void init()
+ {
+ ftable.lock.init("file_table");
+ }
+
+ struct file *alloc()
+ {
+ struct file *f;
+ ftable.lock.acquire();
+ for (f = ftable.files; f < ftable.files + NFILE; f++)
+ {
+ if (f->ref == 0)
+ {
+ f->ref = 1;
+ ftable.lock.release();
+ return f;
+ }
+ }
+ ftable.lock.release();
+ return nullptr;
+ }
+
+ struct file *dup(struct file *f)
+ {
+ ftable.lock.acquire();
+ if (f->ref < 1)
+ {
+ Drivers::uart_puts("panic: file dup\n");
+ while (1)
+ ;
+ }
+ f->ref++;
+ ftable.lock.release();
+ return f;
+ }
+
+ void close(struct file *f)
+ {
+ struct file ff;
+
+ ftable.lock.acquire();
+ if(f->ref < 1)
+ {
+ Drivers::uart_puts("panic: file close\n");
+ while (1)
+ ;
+ }
+ f->ref--;
+ if (f->ref > 0)
+ {
+ ftable.lock.release();
+ return;
+ }
+
+ ff = *f;
+ f->ref = 0;
+ f->type = FD_NONE;
+ ftable.lock.release();
+ if (ff.type == FD_INODE)
+ {
+ // Release the corresponding Inode
+ VFS::iput(ff.ip);
+ }
+ else if (ff.type == FD_PIPE)
+ {
+ // pipeclose(ff.pipe, ff.writable);
+ }
+ }
+
+ int read(struct file *f, uint64 addr, int n)
+ {
+ if (f->readable == 0)
+ return -1;
+ if (f->type == FD_INODE)
+ {
+ VFS::ilock(f->ip);
+ int r = f->ip->read((char*)addr, f->off, n, 1);
+ if(r > 0)
+ {
+ f->off += r;
+ }
+ VFS::iunlock(f->ip);
+ return r;
+ }
+ return -1;
+ }
+
+ int write(struct file *f, uint64 addr, int n)
+ {
+ if (f->writable == 0)
+ return -1;
+ if (f->type == FD_INODE)
+ {
+ VFS::ilock(f->ip);
+ int r = f->ip->write((char *)addr, f->off, n, 1);
+ if (r > 0)
+ {
+ f->off += r;
+ }
+ VFS::iunlock(f->ip);
+ return r;
+ }
+ // else if (f->type == FD_PIPE) ...
+
+ return -1;
+ }
+
+ int stat(struct file *f, uint64 addr)
+ {
+ if (f->type == FD_INODE)
+ {
+ struct kstat st;
+ VFS::ilock(f->ip);
+ f->ip->getattr(&st);
+ VFS::iunlock(f->ip);
+
+ struct Proc *p = myproc();
+ if (VM::copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)
+ return -1;
+ return 0;
+ }
+ return -1;
+ }
+} // namespace FileTable
diff --git a/src/fs/fs.cc b/src/fs/fs.cc
new file mode 100644
index 0000000..b2b91c2
--- /dev/null
+++ b/src/fs/fs.cc
@@ -0,0 +1,190 @@
+#include "fs/fs.h"
+#include "kernel/riscv.h"
+#include "drivers/uart.h"
+#include "kernel/spinlock.h"
+#include "kernel/proc.h"
+#include "fs/fat32.h"
+#include "lib/string.h"
+
+static Inode *global_root_inode = nullptr;
+
+static const char *skipelem(const char *path, char *name)
+{
+ const char *s;
+ int len;
+
+ while (*path == '/')
+ path++;
+ if (*path == 0)
+ return nullptr;
+
+ s = path;
+ while (*path != '/' && *path != 0)
+ path++;
+
+ len = path - s;
+ if (len >= 128)
+ len = 127;
+
+ memmove(name, s, len);
+ name[len] = 0;
+
+ while (*path == '/')
+ path++;
+ return path;
+}
+
+void Inode::getattr(struct kstat *st)
+{
+ st->dev = dev;
+ st->ino = inum;
+ st->type = type;
+ st->nlink = nlink;
+ st->size = size;
+
+ st->uid = 0;
+ st->gid = 0;
+ st->mode = 0;
+}
+
+namespace VFS
+{
+ void init()
+ {
+ Drivers::uart_puts("[VFS] Inode subsystem initialized.\n");
+ }
+
+ void iput(Inode *ip)
+ {
+ if (ip == nullptr)
+ return;
+
+ ip->lock.acquire();
+ if(ip->ref_cnt < 1)
+ {
+ Drivers::uart_puts("panic: iput ref\n");
+ while (1)
+ ;
+ }
+ ip->ref_cnt--;
+ int ref = ip->ref_cnt;
+ ip->lock.release();
+
+ if (ref == 0)
+ {
+ delete ip;
+ }
+ }
+
+ Inode* idup(Inode *ip)
+ {
+ if (ip == nullptr)
+ return nullptr;
+
+ ip->lock.acquire();
+ ip->ref_cnt++;
+ ip->lock.release();
+ return ip;
+ }
+
+ void ilock(Inode *ip)
+ {
+ if (ip == 0 || ip->ref_cnt < 1)
+ {
+ Drivers::uart_puts("panic: ilock\n");
+ while (1)
+ ;
+ }
+
+ ip->lock.acquire();
+
+ }
+
+ void iunlock(Inode *ip)
+ {
+ if (ip == 0 || !ip->lock.holding() || ip->ref_cnt < 1)
+ {
+ Drivers::uart_puts("panic: iunlock\n");
+ while (1)
+ ;
+ }
+ ip->lock.release();
+ }
+
+ static void iunlockput(Inode *ip)
+ {
+ iunlock(ip);
+ iput(ip);
+ }
+
+ void mount_root(Inode *root)
+ {
+ if (global_root_inode)
+ {
+ Drivers::uart_puts("VFS: Root already mounted!\n");
+ return;
+ }
+ global_root_inode = root;
+ Drivers::uart_puts("[VFS] Root filesystem mounted.\n");
+ }
+
+ Inode *get_root()
+ {
+ if (global_root_inode == nullptr)
+ {
+ Drivers::uart_puts("[VFS] Lazy init: Mounting FAT32...\n");
+ fat32_fs.init();
+ mount_root(fat32_fs.root());
+ }
+ if (!global_root_inode)
+ return nullptr;
+ return idup(global_root_inode);
+ }
+
+ Inode *namei(const char *path)
+ {
+ char name[128];
+ Inode *ip, *next;
+ struct Proc *p = myproc();
+
+ if (*path == '/')
+ {
+ ip = get_root();
+ }
+ else
+ {
+ if (p->cwd == nullptr)
+ {
+ ip = get_root();
+ }
+ else
+ {
+ ip = idup(p->cwd);
+ }
+ }
+
+ if (ip == nullptr)
+ return nullptr;
+
+ while ((path = skipelem(path, name)) != nullptr)
+ {
+ ilock(ip);
+
+ if (ip->type != T_DIR)
+ {
+ iunlockput(ip);
+ return nullptr;
+ }
+
+ next = ip->lookup(name);
+ iunlockput(ip);
+
+ ip = next;
+ if (ip == nullptr)
+ return nullptr;
+ }
+
+ return ip;
+ }
+
+} // namespace VFS
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;
}
diff --git a/src/lib/string.cc b/src/lib/string.cc
index bbd079d..a302fc6 100644
--- a/src/lib/string.cc
+++ b/src/lib/string.cc
@@ -69,3 +69,16 @@ int strcmp(const char *s1, const char *s2)
}
return *(const unsigned char *)s1 - *(const unsigned char *)s2;
}
+
+int strcasecmp(const char *s1, const char *s2)
+{
+ while (*s1)
+ {
+ if(*s1 == *s2 || *s1 + 32 == *s2 || *s1 == *s2 + 32){
+ s1++;
+ s2++;
+ }
+ else break;
+ }
+ return *(const unsigned char *)s1 - *(const unsigned char *)s2;
+} \ No newline at end of file
diff --git a/src/user/init.cc b/src/user/init.cc
index 1646f6b..27c1e2f 100644
--- a/src/user/init.cc
+++ b/src/user/init.cc
@@ -1,4 +1,5 @@
#include "user/user.h"
+#include "common/fcntl.h"
int strlen(const char *s)
{
@@ -149,7 +150,40 @@ void test_syscalls()
int main()
{
- print("\n[Lume Shell] Ready.\n");
+ print("\n[Lume Shell] Init process started.\n");
+ print("[Test] Opening test.txt...\n");
+ int fd = open("test.txt", O_RDONLY);
+ if (fd < 0)
+ {
+ print("[Test] Open failed (Is test.txt in fs.img?)\n");
+ }
+ else
+ {
+ print("[Test] Open success! fd=");
+ print_int(fd);
+ print("\n");
+
+ char buf[64];
+ int n = read(fd, buf, 63);
+ if (n > 0)
+ {
+ buf[n] = 0;
+ print("[Test] Read content:\n---\n");
+ print(buf);
+ print("\n---\n");
+ }
+ else
+ {
+ print("[Test] Read returned ");
+ print_int(n);
+ print("\n");
+ }
+ close(fd);
+ print("[Test] File closed.\n");
+ }
+
+
+
print("Commands:\n");
print(" s: Run System Call Tests (fork, wait, mm)\n");
print(" t: Run Disk Driver Test (VirtIO)\n");