summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/boot/entry.S26
-rw-r--r--src/drivers/plic.cc59
-rw-r--r--src/drivers/uart.cc221
-rw-r--r--src/drivers/virtio_dist.cc374
-rw-r--r--src/fs/fat32.cc325
-rw-r--r--src/fs/file.cc180
-rw-r--r--src/fs/fs.cc192
-rw-r--r--src/kernel/bio.cc135
-rw-r--r--src/kernel/cxx.cc68
-rw-r--r--src/kernel/fdt.cc11
-rw-r--r--src/kernel/main.cc107
-rw-r--r--src/kernel/mm/pmm.cc286
-rw-r--r--src/kernel/mm/slab.cc255
-rw-r--r--src/kernel/mm/vm.cc462
-rw-r--r--src/kernel/proc/exec.cc214
-rw-r--r--src/kernel/proc/proc.cc553
-rw-r--r--src/kernel/spinlock.cc230
-rw-r--r--src/kernel/syscall.cc348
-rw-r--r--src/kernel/trap/handler.cc270
-rw-r--r--src/lib/string.cc13
-rw-r--r--src/ulib/assert.c8
-rw-r--r--src/ulib/cxx.cc31
-rw-r--r--src/ulib/iostream.cpp8
-rw-r--r--src/ulib/stdio.c1052
-rw-r--r--src/ulib/stdlib.c539
-rw-r--r--src/ulib/string.c331
-rw-r--r--src/user/entry.S12
-rw-r--r--src/user/init.cc72
-rw-r--r--src/user/sh.cc418
-rw-r--r--src/user/user.ld12
-rw-r--r--src/user/usys.S14
31 files changed, 6083 insertions, 743 deletions
diff --git a/src/boot/entry.S b/src/boot/entry.S
index 924d7d6..6d75bcf 100644
--- a/src/boot/entry.S
+++ b/src/boot/entry.S
@@ -2,13 +2,29 @@
.global _start
_start:
- la t0, g_dtb_addr
- sd a1, 0(t0)
-
mv tp, a0
- la sp, stack_top
+ la t0, stack_base
+ li t1, 4096
+ addi t2, a0, 1
+ mul t1, t1, t2
+ add sp, t0, t1
+
+ bnez a0, .L_jump_main
+
+ la t0, sbss
+ la t1, ebss
+.L_clear_bss:
+ bge t0, t1, .L_save_dtb
+ sd zero, 0(t0)
+ addi t0, t0, 8
+ j .L_clear_bss
+
+.L_save_dtb:
+ la t0, g_dtb_addr
+ sd a1, 0(t0)
+.L_jump_main:
call kernel_main
.L_park:
@@ -19,7 +35,7 @@ _start:
.align 4
.global stack_top
stack_base:
- .space 4096 * 4
+ .space 4096 * 8
stack_top:
.align 8
diff --git a/src/drivers/plic.cc b/src/drivers/plic.cc
new file mode 100644
index 0000000..a759597
--- /dev/null
+++ b/src/drivers/plic.cc
@@ -0,0 +1,59 @@
+#include "common/types.h"
+#include "kernel/riscv.h"
+#include "drivers/plic.h"
+#include "drivers/uart.h"
+
+// Base Constants
+constexpr uint64 PLIC_BASE = 0x0c000000L;
+constexpr uint64 PLIC_PRIORITY = PLIC_BASE + 0x0;
+constexpr uint64 PLIC_PENDING = PLIC_BASE + 0x1000;
+
+// Helper Macros to calculate addresses
+#define PLIC_SENABLE_ADDR(hart) (PLIC_BASE + 0x2080 + (hart) * 0x100)
+#define PLIC_STHRESHOLD_ADDR(hart) (PLIC_BASE + 0x201000 + (hart) * 0x2000)
+#define PLIC_SCLAIM_ADDR(hart) (PLIC_BASE + 0x201004 + (hart) * 0x2000)
+
+// Helper for Volatile Access
+static inline volatile uint32 *REG32(uint64 addr)
+{
+ return reinterpret_cast<volatile uint32 *>(addr);
+}
+
+namespace PLIC
+{
+ void init()
+ {
+ // Set Priority for UART (IRQ 10) and VirtIO (IRQ 1)
+ // Must use volatile write
+ *REG32(PLIC_PRIORITY + 10 * 4) = 1;
+ for (int i = 1; i <= 8; i++)
+ {
+ *REG32(PLIC_PRIORITY + i * 4) = 1;
+ }
+ }
+
+ void inithart()
+ {
+ int hart = static_cast<int>(r_tp());
+
+ // Enable UART(10) and VirtIO(1) for this Hart (S-Mode)
+ *REG32(PLIC_SENABLE_ADDR(hart)) = (1 << 10) | 0x1FE;
+
+ // Set Priority Threshold to 0 (Allow all)
+ *REG32(PLIC_STHRESHOLD_ADDR(hart)) = 0;
+
+ Drivers::uart_puts("[PLIC] Hart Init Done.\n");
+ }
+
+ int claim()
+ {
+ int hart = static_cast<int>(r_tp());
+ return *REG32(PLIC_SCLAIM_ADDR(hart));
+ }
+
+ void complete(int irq)
+ {
+ int hart = static_cast<int>(r_tp());
+ *REG32(PLIC_SCLAIM_ADDR(hart)) = static_cast<uint32>(irq);
+ }
+} \ No newline at end of file
diff --git a/src/drivers/uart.cc b/src/drivers/uart.cc
index 367f442..4064b16 100644
--- a/src/drivers/uart.cc
+++ b/src/drivers/uart.cc
@@ -1,77 +1,206 @@
#include "drivers/uart.h"
-#include "common/types.h"
-#include "kernel/config.h"
+#include "kernel/riscv.h"
+#include "kernel/spinlock.h"
+#include "kernel/proc.h"
+#include "kernel/mm.h"
+#include "lib/string.h"
-// #define UART0_BASE 0x10000000L
-
-#define UART_RBR 0x00 // Receiver Buffer Register (Read)
-#define UART_THR 0x00 // Transmitter Holding Register (Write)
-#define UART_IER 0x01 // Interrupt Enable Register
-#define UART_FCR 0x02 // FIFO Control Register
-#define UART_LCR 0x03 // Line Control Register
-#define UART_LSR 0x05 // Line Status Register
-
-#define UART_LSR_RX_READY 0x01
-#define UART_LSR_TX_EMPTY 0x20
-
-namespace Drivers{
- inline volatile uint8_t &reg(uint32_t offset)
+namespace Drivers
+{
+ // Circular buffer structure
+ struct
{
- return *reinterpret_cast<volatile uint8_t *>(g_uart_base + offset);
- }
+ Spinlock lock;
+ char buf[UART_RX_BUF_SIZE];
+ uint32 r; // read index
+ uint32 w; // write index
+ } uart_rx;
+
+ Spinlock uart_tx_lock;
void uart_init()
{
-
- if (g_uart_base == 0)
- return;
+ uart_rx.lock.init("uart_rx");
+ uart_tx_lock.init("uart_tx");
+ uart_rx.r = 0;
+ uart_rx.w = 0;
+
+ // Disable interrupts and configure
+ *(volatile uint8 *)(UART0 + IER) = 0x00;
- reg(UART_IER) = 0x00;
+ // Enter baud rate setting mode (LCR_DLAB) = 0x80;
+ *(volatile uint8 *)(UART0 + LCR) = 0x80;
+ *(volatile uint8 *)(UART0 + 0) = 0x03; // DLL: 38.4K baud
+ *(volatile uint8 *)(UART0 + 1) = 0x00; // DLM
- reg(UART_LCR) = 0x80;
- reg(0x00) = 0x03;
- reg(0x01) = 0x00;
+ // Exit baud rate setting, set 8-bit word length, no parity
+ *(volatile uint8 *)(UART0 + LCR) = 0x03;
- reg(UART_LCR) = 0x03;
+ // Reset and enable FIFO
+ *(volatile uint8 *)(UART0 + FCR) = 0x07;
- reg(UART_FCR) = 0x07; // Enable FIFO, clear TX/RX queues
+ // Enable Receive Data Available Interrupt
+ *(volatile uint8 *)(UART0 + IER) = 0x01;
}
void uart_putc(char c)
{
- if (c == '\n')
+ uart_tx_lock.acquire();
+ while ((*(volatile uint8 *)(UART0 + LSR) & LSR_TX_IDLE) == 0)
+ ;
+ *(volatile uint8 *)(UART0 + THR) = c;
+ uart_tx_lock.release();
+ }
+
+ void uart_puts(const char *s)
+ {
+ while (*s)
+ uart_putc(*s++);
+ }
+
+ void uart_put_int(int val)
+ {
+ char buf[16];
+ int i = 0;
+ if (val == 0)
{
- uart_putc('\r');
+ uart_puts("0");
+ return;
}
-
- while ((reg(UART_LSR) & UART_LSR_TX_EMPTY) == 0)
+ if (val < 0)
{
-
+ uart_puts("-");
+ val = -val;
}
+ while (val > 0)
+ {
+ buf[i++] = (val % 10) + '0';
+ val /= 10;
+ }
+ while (i > 0)
+ {
+ uart_putc(buf[--i]);
+ }
+ }
- reg(UART_THR) = c;
+ void print_hex(uint64 val)
+ {
+ char hex[] = "0123456789ABCDEF";
+ uart_puts("0x");
+ for (int i = 60; i >= 0; i -= 4)
+ {
+ uart_putc(hex[(val >> i) & 0xF]);
+ }
}
- void uart_puts(const char *s)
+ void uart_intr()
{
- while (*s)
+ uart_rx.lock.acquire();
+
+ while (1)
{
- uart_putc(*s++);
+ if ((*(volatile uint8 *)(UART0 + LSR) & LSR_RX_READY) == 0)
+ break;
+
+ char c = *(volatile uint8 *)(UART0 + RHR);
+
+ uart_rx.buf[uart_rx.w % UART_RX_BUF_SIZE] = c;
+ uart_rx.w++;
+
+ ProcManager::wakeup(&uart_rx);
}
+
+ uart_rx.lock.release();
}
- void print_hex(uint64_t val)
+ // Modern console read: Line buffering + Editing Keys + Echo
+ int console_read(uint64 target, int len)
{
- char buf[20];
- const char *digits = "0123456789ABCDEF";
- buf[0] = '0';
- buf[1] = 'x';
- for (int i = 0; i < 16; i++)
+ struct Proc *p = myproc();
+ int target_idx = 0;
+
+ uart_rx.lock.acquire();
+
+ while (target_idx < len)
{
- buf[17 - i] = digits[val & 0xF];
- val >>= 4;
+ while (uart_rx.r == uart_rx.w)
+ {
+ if (p->xstate != 0)
+ {
+ uart_rx.lock.release();
+ return -1;
+ }
+ ProcManager::sleep(&uart_rx, &uart_rx.lock);
+ }
+
+ char c = uart_rx.buf[uart_rx.r % UART_RX_BUF_SIZE];
+ uart_rx.r++;
+
+ uart_rx.lock.release();
+
+ // 1. Handle Backspace (127 or \b)
+ if (c == 127 || c == '\b')
+ {
+ if (target_idx > 0)
+ {
+ // Erase character from screen: Backspace, Space, Backspace
+ uart_putc('\b');
+ uart_putc(' ');
+ uart_putc('\b');
+ target_idx--;
+ }
+ uart_rx.lock.acquire();
+ continue;
+ }
+
+ // 2. Handle Ctrl+U (Kill Line)
+ if (c == 0x15)
+ {
+ while (target_idx > 0)
+ {
+ uart_putc('\b');
+ uart_putc(' ');
+ uart_putc('\b');
+ target_idx--;
+ }
+ uart_rx.lock.acquire();
+ continue;
+ }
+
+ // 3. Handle Ctrl+L (Clear Screen)
+ if (c == 0x0C)
+ {
+ uart_puts(ANSI_CLEAR);
+ uart_puts("$ ");
+ uart_rx.lock.acquire();
+ continue;
+ }
+
+ // 4. Handle Return (CR -> LF)
+ if (c == '\r')
+ {
+ c = '\n';
+ }
+
+ // Echo back to user
+ // uart_putc(c);
+
+ if (VM::copyout(p->pagetable, target + target_idx, &c, 1) < 0)
+ {
+ return -1;
+ }
+
+ target_idx++;
+
+ if (c == '\n')
+ {
+ return target_idx;
+ }
+
+ uart_rx.lock.acquire();
}
- buf[18] = 0;
- Drivers::uart_puts(buf);
+
+ uart_rx.lock.release();
+ return target_idx;
}
} \ No newline at end of file
diff --git a/src/drivers/virtio_dist.cc b/src/drivers/virtio_dist.cc
new file mode 100644
index 0000000..9c981d4
--- /dev/null
+++ b/src/drivers/virtio_dist.cc
@@ -0,0 +1,374 @@
+#include "drivers/virtio.h"
+#include "kernel/riscv.h"
+#include "kernel/pmm.h"
+#include "kernel/spinlock.h"
+#include "lib/string.h"
+#include "drivers/uart.h"
+#include "kernel/proc.h"
+#include "kernel/buf.h"
+
+static uint64 virtio_base = 0x10001000;
+
+// VirtIO Block Device Config Structure
+struct virtio_blk_config
+{
+ uint64 capacity;
+ uint32 size_max;
+ uint32 seg_max;
+ struct virtio_blk_geometry
+ {
+ uint16 cylinders;
+ uint8 heads;
+ uint8 sectors;
+ } geometry;
+ uint32 blk_size;
+ uint8 physical_block_exp;
+ uint8 alignment_offset;
+ uint16 min_io_size;
+ uint32 opt_io_size;
+} __attribute__((packed));
+
+static inline volatile uint32 *REG(uint32 offset)
+{
+ return reinterpret_cast<volatile uint32 *>(virtio_base + offset);
+}
+
+namespace VirtIO
+{
+ struct Disk
+ {
+ struct VRingDesc *desc;
+ struct VRingAvail *avail;
+ struct VRingUsed *used;
+
+ int free_head;
+ int num_free;
+ int free_chain[NUM_DESCS];
+
+ uint16 used_idx;
+
+ struct TraceInfo
+ {
+ int busy;
+ int status;
+ } info[NUM_DESCS];
+
+ struct BlockRequest reqs[NUM_DESCS];
+ uint8 req_status[NUM_DESCS];
+
+ Spinlock lock;
+ } disk;
+
+ void init()
+ {
+ disk.lock.init("virtio_disk");
+
+ // 1. Probing for VirtIO-MMIO device
+ bool found = false;
+ // Probe common VirtIO addresses in QEMU virt machine
+ for (uint64 addr = 0x10001000; addr < 0x10009000; addr += 0x1000)
+ {
+ volatile uint32 *magic = (volatile uint32 *)(addr + VIRTIO_MMIO_MAGIC_VALUE);
+ volatile uint32 *dev_id = (volatile uint32 *)(addr + VIRTIO_MMIO_DEVICE_ID);
+
+ if (*magic == 0x74726976 && *dev_id == 2) // ID 2 = Block Device
+ {
+ virtio_base = addr;
+ found = true;
+ Drivers::uart_puts(ANSI_GREEN "[VirtIO] Found Block Device @ ");
+ Drivers::print_hex(addr);
+ Drivers::uart_puts(ANSI_RESET "\n");
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ Drivers::uart_puts(ANSI_RED "[VirtIO] Fatal - No Block Device found.\n" ANSI_RESET);
+ return;
+ }
+
+ // 2. Check Device Version (We want Modern v2)
+ if (*REG(VIRTIO_MMIO_VERSION) != 2)
+ {
+ Drivers::uart_puts(ANSI_YELLOW "[VirtIO] Warning: Legacy device detected (expected v2).\n" ANSI_RESET);
+ }
+
+ // 3. Reset Device
+ *REG(VIRTIO_MMIO_STATUS) = 0;
+
+ // 4. Set ACKNOWLEDGE & DRIVER status
+ uint32 status = *REG(VIRTIO_MMIO_STATUS);
+ status |= VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER;
+ *REG(VIRTIO_MMIO_STATUS) = status;
+
+ // 5. Feature Negotiation
+ *REG(VIRTIO_MMIO_DEVICE_FEATURES_SEL) = 0;
+ uint32 device_features_lo = *REG(VIRTIO_MMIO_DEVICE_FEATURES);
+ (void)device_features_lo;
+
+ *REG(VIRTIO_MMIO_DEVICE_FEATURES_SEL) = 1;
+ uint32 device_features_hi = *REG(VIRTIO_MMIO_DEVICE_FEATURES);
+ (void)device_features_hi;
+
+ *REG(VIRTIO_MMIO_DRIVER_FEATURES_SEL) = 0;
+ *REG(VIRTIO_MMIO_DRIVER_FEATURES) = 0;
+
+ *REG(VIRTIO_MMIO_DRIVER_FEATURES_SEL) = 1;
+ // VIRTIO_F_VERSION_1 is usually bit 32.
+ // If not defined in header, we hardcode logic here: (1ULL << 0) for high bits
+ *REG(VIRTIO_MMIO_DRIVER_FEATURES) = 1;
+
+ status |= VIRTIO_CONFIG_S_FEATURES_OK;
+ *REG(VIRTIO_MMIO_STATUS) = status;
+
+ if (!(*REG(VIRTIO_MMIO_STATUS) & VIRTIO_CONFIG_S_FEATURES_OK))
+ {
+ Drivers::uart_puts("VirtIO: Feature negotiation failed.\n");
+ return;
+ }
+
+ // 6. Queue Configuration
+ *REG(VIRTIO_MMIO_QUEUE_SEL) = 0;
+ if (*REG(VIRTIO_MMIO_QUEUE_READY))
+ {
+ Drivers::uart_puts("VirtIO: Queue 0 not ready.\n");
+ }
+
+ uint32 max_queue = *REG(VIRTIO_MMIO_QUEUE_NUM_MAX);
+ if (max_queue < NUM_DESCS)
+ {
+ Drivers::uart_puts("VirtIO: Queue size too small.\n");
+ return;
+ }
+
+ *REG(VIRTIO_MMIO_QUEUE_NUM) = NUM_DESCS;
+
+ // Allocate Memory for VirtQueues
+ void *ptr = PMM::alloc_pages(1); // 4KB is enough for small rings
+ memset(ptr, 0, PGSIZE);
+
+ uint64 base_addr = (uint64)ptr;
+ disk.desc = reinterpret_cast<struct VRingDesc *>(base_addr);
+ disk.avail = reinterpret_cast<struct VRingAvail *>(base_addr + NUM_DESCS * 16);
+ disk.used = reinterpret_cast<struct VRingUsed *>(base_addr + PGSIZE / 2);
+
+ *REG(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint32)base_addr;
+ *REG(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint32)(base_addr >> 32);
+
+ uint64 avail_addr = (uint64)disk.avail;
+ *REG(VIRTIO_MMIO_QUEUE_DRIVER_LOW) = (uint32)avail_addr;
+ *REG(VIRTIO_MMIO_QUEUE_DRIVER_HIGH) = (uint32)(avail_addr >> 32);
+
+ uint64 used_addr = (uint64)disk.used;
+ *REG(VIRTIO_MMIO_QUEUE_DEVICE_LOW) = (uint32)used_addr;
+ *REG(VIRTIO_MMIO_QUEUE_DEVICE_HIGH) = (uint32)(used_addr >> 32);
+
+ *REG(VIRTIO_MMIO_QUEUE_READY) = 1;
+
+ // 7. Initialize Internal Structures
+ disk.free_head = 0;
+ disk.num_free = NUM_DESCS;
+ disk.used_idx = 0;
+ for (int i = 0; i < NUM_DESCS; i++)
+ {
+ disk.free_chain[i] = i + 1;
+ disk.info[i].busy = 0;
+ }
+
+ // 8. Finalize Setup
+ status |= VIRTIO_CONFIG_S_DRIVER_OK;
+ *REG(VIRTIO_MMIO_STATUS) = status;
+
+ // === [Feature] Read Capacity ===
+ volatile struct virtio_blk_config *config = (volatile struct virtio_blk_config *)(virtio_base + VIRTIO_MMIO_CONFIG);
+ uint64 capacity_sectors = config->capacity;
+ uint64 size_mb = (capacity_sectors * 512) / (1024 * 1024);
+
+ Drivers::uart_puts(ANSI_GREEN "[VirtIO] Initialized. Capacity: ");
+ Drivers::uart_put_int(size_mb);
+ Drivers::uart_puts(" MB\n" ANSI_RESET);
+ }
+
+ static int alloc_desc()
+ {
+ if (disk.num_free == 0)
+ return -1;
+ int id = disk.free_head;
+ disk.free_head = disk.free_chain[id];
+ disk.num_free--;
+ return id;
+ }
+
+ static void free_desc(int id)
+ {
+ disk.free_chain[id] = disk.free_head;
+ disk.free_head = id;
+ disk.num_free++;
+ ProcManager::wakeup(&disk.free_head);
+ }
+
+ static void free_chain_soft(int head_idx)
+ {
+ int curr = head_idx;
+ while (1)
+ {
+ int flag = disk.desc[curr].flags;
+ int next = disk.desc[curr].next;
+ free_desc(curr);
+ if (flag & VRING_DESC_F_NEXT)
+ curr = next;
+ else
+ break;
+ }
+ }
+
+ void rw(int write, uint64 blockno, char *buf)
+ {
+ uint64 sector = blockno * (1024 / 512); // Assume 1KB blocks
+
+ disk.lock.acquire();
+
+ // Wait for enough descriptors
+ while (disk.num_free < 3)
+ {
+ ProcManager::sleep(&disk.free_head, &disk.lock);
+ }
+
+ int idx[3];
+ idx[0] = alloc_desc();
+ idx[1] = alloc_desc();
+ idx[2] = alloc_desc();
+
+ // 1. Request Header
+ struct BlockRequest *req = &disk.reqs[idx[0]];
+ req->type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
+ req->reserved = 0;
+ req->sector = sector;
+
+ disk.desc[idx[0]].addr = (uint64)req;
+ disk.desc[idx[0]].len = sizeof(struct BlockRequest);
+ disk.desc[idx[0]].flags = VRING_DESC_F_NEXT;
+ disk.desc[idx[0]].next = idx[1];
+
+ // 2. Data Buffer
+ disk.desc[idx[1]].addr = (uint64)buf;
+ disk.desc[idx[1]].len = 1024; // Fixed 1KB for this test
+ disk.desc[idx[1]].flags = VRING_DESC_F_NEXT;
+ if (!write)
+ disk.desc[idx[1]].flags |= VRING_DESC_F_WRITE;
+ disk.desc[idx[1]].next = idx[2];
+
+ // 3. Status Byte
+ disk.desc[idx[2]].addr = (uint64)&disk.req_status[idx[0]];
+ disk.desc[idx[2]].len = 1;
+ disk.desc[idx[2]].flags = VRING_DESC_F_WRITE;
+ disk.desc[idx[2]].next = 0;
+
+ disk.info[idx[0]].busy = 1;
+
+ // Submit to Avail Ring
+ disk.avail->ring[disk.avail->idx % NUM_DESCS] = idx[0];
+ __sync_synchronize();
+ disk.avail->idx++;
+ __sync_synchronize();
+
+ // Notify Device
+ *REG(VIRTIO_MMIO_QUEUE_NOTIFY) = 0;
+
+ // Wait for Completion
+ while (disk.info[idx[0]].busy)
+ {
+ ProcManager::sleep(&disk.info[idx[0]], &disk.lock);
+ }
+
+ free_chain_soft(idx[0]);
+ disk.lock.release();
+ }
+
+ void intr()
+ {
+ disk.lock.acquire();
+
+ uint32 status = *REG(VIRTIO_MMIO_INTERRUPT_STATUS);
+ *REG(VIRTIO_MMIO_INTERRUPT_ACK) = status & 0x3;
+
+ volatile struct VRingUsed *used_ring = (volatile struct VRingUsed *)disk.used;
+
+ while (disk.used_idx != used_ring->idx)
+ {
+ int id = used_ring->ring[disk.used_idx % NUM_DESCS].id;
+
+ if (disk.info[id].busy)
+ {
+ disk.info[id].busy = 0;
+ ProcManager::wakeup(&disk.info[id]);
+ }
+ disk.used_idx++;
+ }
+
+ disk.lock.release();
+ }
+
+ void test_rw()
+ {
+ Drivers::uart_puts(ANSI_BLUE "[VirtIO] Disk Test Start...\n" ANSI_RESET);
+
+ char *buf = (char *)PMM::alloc_page();
+ if (!buf)
+ return;
+
+ const char *msg = "Lume OS Disk Driver Test Pattern";
+ strcpy(buf, msg);
+
+ rw(1, 0, buf); // Write
+ memset(buf, 0, PGSIZE);
+ rw(0, 0, buf); // Read
+
+ if (strcmp(buf, msg) == 0)
+ {
+ Drivers::uart_puts(ANSI_GREEN "[VirtIO] Test PASSED (R/W Verified).\n" ANSI_RESET);
+ }
+ else
+ {
+ Drivers::uart_puts(ANSI_RED "[VirtIO] Test FAILED.\n" ANSI_RESET);
+ }
+
+ 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..99bb65d
--- /dev/null
+++ b/src/fs/file.cc
@@ -0,0 +1,180 @@
+#include "fs/file.h"
+#include "kernel/spinlock.h"
+#include "kernel/mm.h"
+#include "drivers/uart.h"
+#include "kernel/proc.h"
+
+#define NFILE 100
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+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;
+ }
+
+ int lseek(struct file *f, int offset, int whence)
+ {
+ if (f->type != FD_INODE)
+ return -1;
+
+ VFS::ilock(f->ip);
+
+ int new_off = f->off;
+ int size = f->ip->size;
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ new_off = offset;
+ break;
+ case SEEK_CUR:
+ new_off += offset;
+ break;
+ case SEEK_END:
+ new_off = size + offset;
+ break;
+ default:
+ VFS::iunlock(f->ip);
+ return -1;
+ }
+
+ if (new_off < 0)
+ {
+ VFS::iunlock(f->ip);
+ return -1;
+ }
+
+ f->off = new_off;
+ VFS::iunlock(f->ip);
+ return new_off;
+ }
+} // namespace FileTable
diff --git a/src/fs/fs.cc b/src/fs/fs.cc
new file mode 100644
index 0000000..c5a34ee
--- /dev/null
+++ b/src/fs/fs.cc
@@ -0,0 +1,192 @@
+#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();
+ }
+
+ void iunlockput(Inode *ip)
+ {
+ if (!ip)
+ return;
+ 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/fdt.cc b/src/kernel/fdt.cc
index 1863086..5334845 100644
--- a/src/kernel/fdt.cc
+++ b/src/kernel/fdt.cc
@@ -96,17 +96,6 @@ void fdt_init(uint64 dtb)
}
}
- // if (g_clint_base == 0)
- // {
- // for (uint32 i = 0; i < len; i++)
- // {
- // if (data[i] == 'c' && data[i + 1] == 'l' && data[i + 2] == 'i' && data[i + 3] == 'n')
- // {
- // g_clint_base = current_reg_addr;
- // break;
- // }
- // }
- // }
}
}
}
diff --git a/src/kernel/main.cc b/src/kernel/main.cc
index c6d794d..9a24028 100644
--- a/src/kernel/main.cc
+++ b/src/kernel/main.cc
@@ -1,75 +1,68 @@
+#include "common/types.h"
+#include "kernel/riscv.h"
#include "drivers/uart.h"
+#include "drivers/plic.h"
+#include "drivers/virtio.h"
#include "kernel/pmm.h"
#include "kernel/mm.h"
-#include "kernel/trap.h"
+#include "kernel/slab.h"
#include "kernel/proc.h"
+#include "kernel/trap.h"
#include "kernel/timer.h"
-#include "kernel/config.h"
-
-void fdt_init(uint64 dtb);
+#include "kernel/cpu.h"
+#include "kernel/buf.h"
+#include "fs/fs.h"
+#include "fs/file.h"
+#include "fs/fat32.h"
+#include "lib/string.h"
-void delay()
-{
- for(volatile int i = 0; i < 1000000; i++)
- {
+extern void fdt_init(uint64 dtb);
+extern uint64 g_dtb_addr;
- }
-}
-
-void task_a()
-{
- while(1)
- {
- Drivers::uart_putc('A');
- delay();
- // ProcManager::yield();
- }
-}
+volatile static int started = 0;
-void task_b()
+extern "C" void kernel_main(uint64 hartid, uint64 dtb)
{
- while (1)
+ if (hartid == 0)
{
- Drivers::uart_putc('B');
- delay();
- // ProcManager::yield();
- }
-
-}
-
-extern "C" void kernel_main()
-{
-
- fdt_init(g_dtb_addr);
- Drivers::uart_init();
- Drivers::uart_puts("\n[Lume OS Kernel] Booted in S-Mode.\n");
- Drivers::uart_puts("2025 Richard Qin\n");
- Drivers::uart_puts("\n");
- Drivers::uart_puts("\n[Boot] Kernel started.\n");
+ fdt_init(dtb);
- PMM::init();
- Drivers::uart_puts("[Boot] PMM initialized.\n");
+ Drivers::uart_init();
+ Drivers::uart_puts("\n[Lume OS] Booting...\n");
- VM::kvminit();
- Drivers::uart_puts("[Boot] Kernel page table created.\n");
+ Drivers::uart_puts("[Boot] PMP configured.\n");
- VM::kvminithart();
- Drivers::uart_puts("[Boot] MMU ENABLED! We are now using virtual addresses.\n");
+ PMM::init(); // Physical Memory Management
+ VM::kvminit(); // Kernel Pagetable
+ VM::kvminithart(); // Enable MMU
+ Slab::init();
+ Trap::init(); // Trap Management
+ Trap::inithart();
+ 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
- Trap::init();
- Drivers::uart_puts("[Boot] Trap Handler Installed.\n");
+ __atomic_store_n(&started, 1, __ATOMIC_RELEASE);
- Timer::init();
- Drivers::uart_puts("[Boot] Timer Interrupt Enabled.\n");
-
- ProcManager::init();
- Drivers::uart_puts("[Boot] ProcManager Initialized.\n");
+ Drivers::uart_puts("[Boot] System initialized. Entering scheduler...\n");
+ }
+ else
+ {
+ while (__atomic_load_n(&started, __ATOMIC_ACQUIRE) == 0)
+ ;
- void task_a();
- void task_b();
- // ProcManager::create_kernel_thread(task_a, "Task A");
- // ProcManager::create_kernel_thread(task_b, "Task B");
- ProcManager::user_init();
+ VM::kvminithart();
+ Trap::inithart();
+ PLIC::inithart();
+ Timer::init();
+ Drivers::uart_puts("[Boot] Hart started.\n");
+ }
- ProcManager::scheduler();
+ ProcManager::scheduler(); // Enable Scheduler
} \ No newline at end of file
diff --git a/src/kernel/mm/pmm.cc b/src/kernel/mm/pmm.cc
index 9b68fcd..e871d8a 100644
--- a/src/kernel/mm/pmm.cc
+++ b/src/kernel/mm/pmm.cc
@@ -1,71 +1,289 @@
+#include "common/types.h"
+#include "kernel/riscv.h"
#include "kernel/pmm.h"
+#include "kernel/mm.h"
+// #include "kernel/spinlock.h"
#include "drivers/uart.h"
+#include "lib/string.h"
extern "C" char kernel_end[];
+extern uint64 g_dtb_addr;
namespace PMM
{
- struct Run
- {
- struct Run *next;
- };
+ // Global metadata array pointer
+ struct Page *mem_map = nullptr;
+ // Total number of physical memory pages
+ uint64 total_pages = 0;
+ // Free list array (free_area[k] stores the head of the free block list of order=k)
+ struct Page *free_area[MAX_ORDER];
+ // Protective lock
+ Spinlock pmm_lock;
- struct
+ struct Page *pa_to_page(uint64 pa)
{
- struct Run *freelist;
- } kmem;
+ if (pa < KERNBASE || pa >= PHYSTOP)
+ return nullptr;
+ uint64 pfn = (pa - KERNBASE) >> PGSHIFT;
+ if (pfn >= total_pages)
+ return nullptr;
+ return &mem_map[pfn];
+ }
- inline uint64_t PGROUNDUP(uint64_t a)
+ uint64 page_to_pa(struct Page *page)
{
- return (a + PGSIZE - 1) & ~(PGSIZE - 1);
+ uint64 pfn = page - mem_map;
+ return KERNBASE + (pfn << PGSHIFT);
}
- void init()
+ void list_add(struct Page *page, int order)
{
- uint64_t start = PGROUNDUP((uint64_t)kernel_end);
+ page->order = order;
+ page->flags = 0; // Mark as available
+ page->next = free_area[order];
+ page->prev = nullptr;
- Drivers::uart_puts("PMM: Initializing from");
+ if (free_area[order])
+ {
+ free_area[order]->prev = page;
+ }
+ free_area[order] = page;
+ }
- for (uint64_t p = start; p + PGSIZE <= PHYSTOP; p += PGSIZE)
+ void list_del(struct Page* page, int order)
+ {
+ if (page->prev)
+ {
+ page->prev->next = page->next;
+ }
+ else
{
- free_page((void *)p);
+ free_area[order] = page->next;
}
+ if (page->next)
+ {
+ page->next->prev = page->prev;
+ }
+ page->next = nullptr;
+ page->prev = nullptr;
}
- void free_page(void* pa)
+ void init()
{
- struct Run *r;
- if (((uint64_t)pa % PGSIZE) != 0 || (uint64_t)pa < (uint64_t)kernel_end || (uint64_t)pa >= PHYSTOP)
+ pmm_lock.init("pmm");
+
+ uint64 kern_end_pa = PGROUNDUP((uint64)kernel_end);
+ total_pages = (PHYSTOP - KERNBASE) / PGSIZE;
+
+ Drivers::uart_puts(ANSI_GREEN "[PMM] Total RAM size: 128MB\n" ANSI_RESET);
+ // Place the mem_map array
+ // mem_map is located immediately after kernel_end
+ mem_map = (struct Page*)kern_end_pa;
+
+ uint64 mem_map_size = total_pages * sizeof(struct Page);
+ uint64 free_mem_start = PGROUNDUP(kern_end_pa + mem_map_size);
+
+ Drivers::uart_puts(ANSI_GREEN "[PMM] MemMap placed at: " );
+ Drivers::print_hex((uint64)mem_map);
+ Drivers::uart_puts("\n" ANSI_RESET);
+
+ Drivers::uart_puts(ANSI_GREEN "[PMM] Free memory starts at: ");
+ Drivers::print_hex(free_mem_start);
+ Drivers::uart_puts("\n" ANSI_RESET);
+
+ if (free_mem_start >= PHYSTOP)
{
- Drivers::uart_puts("PMM: Error - Invalid free address\n");
- return;
+ Drivers::uart_puts(ANSI_RED "[PMM] Error - Kernel too large, no memory left!\n" ANSI_RESET);
+ while (1)
+ ;
}
- r = (struct Run*)pa;
- r->next = kmem.freelist;
- kmem.freelist = r;
+ // Initialize all page states
+ for (uint64 i = 0; i < total_pages; i++)
+ {
+ mem_map[i].flags = PG_reserved;
+ mem_map[i].order = 0;
+ mem_map[i].refcnt = 0;
+ mem_map[i].next = nullptr;
+ mem_map[i].prev = nullptr;
+ }
+
+ for (int i = 0; i < MAX_ORDER; i++)
+ {
+ free_area[i] = nullptr;
+ }
+
+ // Release actual free memory to the allocator
+ // Range: [free_mem_start, PHYSTOP)
+ // Note: need to avoid the page where the DTB is located
+ uint64 dtb_pa_start = PGROUNDDOWN(g_dtb_addr);
+ uint64 dtb_pa_end = PGROUNDUP(g_dtb_addr + 0x10000);
+
+ uint64 p = free_mem_start;
+ while (p < PHYSTOP)
+ {
+ if (p >= dtb_pa_start && p < dtb_pa_end)
+ {
+ p += PGSIZE;
+ continue;
+ }
+
+ // Mark the page as Used, then call free_pages to release it
+ // This allows free_pages to handle the merging logic automatically
+ struct Page *page = pa_to_page(p);
+ if (page)
+ {
+ page->flags = PG_used;
+ free_pages((void *)p, 0); // Release page by page, let Buddy merge automatically
+ }
+ p += PGSIZE;
+
+
+ }
}
- void* alloc_page()
+ void *alloc_pages(int order)
{
- struct Run* r;
+ if (order < 0 || order >= MAX_ORDER)
+ return nullptr;
+
+ pmm_lock.acquire();
- r = kmem.freelist;
- if (r)
+ int current_order = order;
+ while (current_order < MAX_ORDER)
{
- kmem.freelist = r->next;
+ if (free_area[current_order])
+ {
+ // Find free block
+ struct Page *page = free_area[current_order];
+ list_del(page, current_order);
+
+ // If the block is too large, split it.
+ while (current_order > order)
+ {
+ current_order--;
+ // page is the left part (Buddy A)
+ // buddy is the right part (Buddy B)
+ // Pointer addition: Page* + n is equivalent to address offset n * sizeof(Page)
+ // Since mem_map is linearly mapped, mem_map[i + (1<<k)] exactly corresponds to physical address + (1<<k)*PGSIZE
+ struct Page *buddy = page + (1 << current_order);
+
+ // Add the buddy to a lower-level linked list
+ list_add(buddy, current_order);
+ }
+
+ // Mark as Occupied
+ page->flags = PG_used;
+ page->order = order; // Record the orders that have been assigned
+ page->refcnt = 1;
- uint64_t *p = (uint64_t *)r;
- for (int i = 0; i < PGSIZE / 8; i++)
+ pmm_lock.release();
+ return (void *)page_to_pa(page);
+ }
+ current_order++;
+ }
+ pmm_lock.release();
+ Drivers::uart_puts("PMM: OOM alloc_pages\n");
+ return nullptr;
+ }
+
+ void free_pages(void *pa, int order)
+ {
+ if (!pa)
+ return;
+ struct Page *page = pa_to_page((uint64)pa);
+ if (!page)
+ return;
+ pmm_lock.acquire();
+
+ if (!(page->flags & PG_used))
+ {
+ Drivers::uart_puts("PMM: Double free warning\n");
+ pmm_lock.release();
+ return;
+ }
+
+ // Mark as basic status
+ page->flags = 0;
+ page->refcnt = 0;
+
+ // try merge
+ uint64 page_idx = page - mem_map;
+
+ while (order < MAX_ORDER - 1)
+ {
+ // calculate Buddy index: index ^ (1 << order)
+ uint64 buddy_idx = page_idx ^ (1 << order);
+ struct Page *buddy = &mem_map[buddy_idx];
+
+ // Check if the Buddy can be merged:
+ // 1. Must be within the memory range
+ // 2. Must be free (flags == 0)
+ // 3. Must be at the current order (to prevent merging already split small blocks)
+ if (buddy_idx >= total_pages || (buddy->flags & PG_used) || (buddy->flags & PG_reserved) || buddy->order != order)
+ {
+ break;
+ }
+
+ // Can be merged!
+ // Remove Buddy from the free list
+ list_del(buddy, order);
+
+ // Update the current block to the merged block
+ // The starting address of the merged block is the smaller one between index and buddy_index
+ if (buddy_idx < page_idx)
{
- p[i] = 0;
+ page = buddy;
+ page_idx = buddy_idx;
}
+
+ order++;
}
- else
+
+ // Insert into the final order linked list
+ list_add(page, order);
+
+ pmm_lock.release();
+ }
+
+ void *alloc_page()
+ {
+ void *p = alloc_pages(0);
+ if (p)
{
- Drivers::uart_puts("PMM: Out of memory!\n");
+ memset(p, 0, PGSIZE);
+ }
+ return p;
+ }
+
+ void free_page(void *pa)
+ {
+ free_pages(pa, 0);
+ }
+
+ void print_free_lists()
+ {
+ // Debug print the number of free blocks at each level
+ Drivers::uart_puts("PMM Stats: [ ");
+ for (int i = 0; i < MAX_ORDER; i++)
+ {
+ int count = 0;
+ struct Page *p = free_area[i];
+ while (p)
+ {
+ count++;
+ p = p->next;
+ }
+ if (count > 0)
+ {
+ Drivers::uart_puts("Order");
+ // Drivers::uart_put_int(i);
+ Drivers::uart_puts(":");
+ // Drivers::uart_put_int(count);
+ Drivers::uart_puts(" ");
+ }
}
- return (void *)r;
+ Drivers::uart_puts("]\n");
}
-} \ No newline at end of file
+} // namespace PMM
diff --git a/src/kernel/mm/slab.cc b/src/kernel/mm/slab.cc
new file mode 100644
index 0000000..702dc5c
--- /dev/null
+++ b/src/kernel/mm/slab.cc
@@ -0,0 +1,255 @@
+#include "kernel/slab.h"
+#include "kernel/pmm.h"
+#include "kernel/spinlock.h"
+#include "kernel/riscv.h"
+#include "drivers/uart.h"
+#include "lib/string.h"
+
+#define PG_slab 0x04
+
+// Supported object size (must be a multiple of 8 and <= 2048)
+const uint64 kmalloc_sizes[] = {
+ 16, 32, 64, 128, 256, 512, 1024, 2048};
+const int NUM_CACHES = 8;
+
+struct SlabHeader
+{
+ struct SlabHeader *next; // Next slab in the linked list
+ uint16 free_idx; // Index of the head of the free list (0xFFFF means none)
+ uint16 used_count; // Number of allocated objects
+ // Followed immediately by object data...
+ // The free list of objects is embedded in the first 2 bytes of the object itself (stores the index of the next object)
+ uint32 padding;
+};
+
+struct KmemCache
+{
+ Spinlock lock;
+ uint64 obj_size;
+ uint64 obj_count_per_slab;
+ struct SlabHeader *slab_list; // Slab list with free objects
+ const char *name;
+};
+
+struct KmemCache caches[NUM_CACHES];
+
+// Virtual address -> struct Page*
+static struct Page *virt_to_page(void *ptr)
+{
+ uint64 pa = (uint64)ptr;
+ return PMM::pa_to_page(pa);
+}
+
+// Slab Implementation
+
+namespace Slab
+{
+
+ void init()
+ {
+ for (int i = 0; i < NUM_CACHES; i++)
+ {
+ caches[i].lock.init("slab_lock");
+ caches[i].obj_size = kmalloc_sizes[i];
+ caches[i].name = "kmalloc";
+
+ // Calculate how many objects can fit on a page
+ // Excluding the overhead of the SlabHeader
+ uint64 available = PGSIZE - sizeof(struct SlabHeader);
+ caches[i].obj_count_per_slab = available / caches[i].obj_size;
+ caches[i].slab_list = nullptr;
+ }
+ Drivers::uart_puts("[Slab] Initialized kmalloc caches.\n");
+ }
+
+ // Allocate a new slab page for the cache
+ // Must hold cache->lock
+ static struct SlabHeader *grow_cache(struct KmemCache *c)
+ {
+ void *p = PMM::alloc_page();
+ if (!p)
+ return nullptr;
+
+ // Mark this page as a Slab page
+ struct Page *page = virt_to_page(p);
+ if (page)
+ {
+ page->flags |= PG_slab;
+ int cache_idx = c - caches;
+ page->order = (uint8)cache_idx;
+ }
+
+ struct SlabHeader *slab = (struct SlabHeader *)p;
+ slab->next = nullptr;
+ slab->used_count = 0;
+ slab->free_idx = 0;
+ slab->padding = 0;
+
+ // Initialize the free list of objects
+ // The starting position of each object stores the index of the next object
+ // Starting position of the object data area
+ char *base = (char *)(slab + 1);
+
+ for (uint i = 0; i < c->obj_count_per_slab - 1; i++)
+ {
+ // The content of the i-th object is i+1
+ uint16 *ptr = (uint16 *)(base + i * c->obj_size);
+ *ptr = i + 1;
+ }
+ // The last object points to 0xFFFF
+ uint16 *ptr = (uint16 *)(base + (c->obj_count_per_slab - 1) * c->obj_size);
+ *ptr = 0xFFFF;
+
+ return slab;
+ }
+
+ void *kmalloc(uint64 size)
+ {
+ if (size == 0)
+ return nullptr;
+
+ // Large objects go directly through PMM
+ if (size > 2048)
+ {
+ // calculate order
+ int order = 0;
+ uint64 req = size;
+ uint64 pages = (size + PGSIZE - 1) / PGSIZE;
+ (void)req;
+
+ // calculate order: 2^order >= pages
+ while ((1UL << order) < pages)
+ {
+ order++;
+ }
+ if (order > MAX_ORDER)
+ return nullptr;
+
+ void *p = PMM::alloc_pages(order);
+ // Here, page->flags defaults to PG_used, not PG_slab
+ // kfree needs to be able to recognize it
+ return p;
+ }
+
+ int idx = 0;
+ while (idx < NUM_CACHES && kmalloc_sizes[idx] < size)
+ {
+ idx++;
+ }
+
+ struct KmemCache *c = &caches[idx];
+ c->lock.acquire();
+
+ if (!c->slab_list)
+ {
+ c->slab_list = grow_cache(c);
+ if (!c->slab_list)
+ {
+ c->lock.release();
+ return nullptr;
+ }
+ }
+
+ struct SlabHeader *slab = c->slab_list;
+
+ int obj_idx = slab->free_idx;
+ char *base = (char *)(slab + 1);
+ void *ptr = base + obj_idx * c->obj_size;
+
+ uint16 *link = (uint16 *)ptr;
+ slab->free_idx = *link;
+ slab->used_count++;
+
+ if (slab->free_idx == 0xFFFF)
+ {
+ if (slab->next)
+ {
+ c->slab_list = slab->next;
+ slab->next = nullptr;
+ }
+ else
+ {
+ c->slab_list = nullptr;
+ }
+ }
+
+ c->lock.release();
+
+ memset(ptr, 0, size);
+ return ptr;
+ }
+
+ void kfree(void *ptr)
+ {
+ if (!ptr)
+ return;
+
+ struct Page *page = virt_to_page(ptr);
+ if (!page)
+ return;
+
+ if (!page || !(page->flags & PG_slab))
+ {
+ PMM::free_pages(ptr, page->order);
+ return;
+ }
+
+ int cache_idx = page->order;
+ if (cache_idx >= NUM_CACHES)
+ {
+ Drivers::uart_puts("Slab: Corrupted page metadata\n");
+ return;
+ }
+
+ struct KmemCache *c = &caches[cache_idx];
+ c->lock.acquire();
+
+ struct SlabHeader *slab = (struct SlabHeader *)PGROUNDDOWN((uint64)ptr);
+
+ if ((uint64)ptr < (uint64)(slab + 1) || (uint64)ptr >= (uint64)slab + PGSIZE)
+ {
+ Drivers::uart_puts("Slab: kfree invalid pointer\n");
+ c->lock.release();
+ return;
+ }
+
+ char *base = (char *)(slab + 1);
+ int obj_idx = ((uint64)ptr - (uint64)base) / c->obj_size;
+
+ uint16 *link = (uint16 *)ptr;
+ *link = slab->free_idx;
+ slab->free_idx = (uint16)obj_idx;
+
+ slab->used_count--;
+
+ if (slab->used_count == 0)
+ {
+ struct SlabHeader **pp = &c->slab_list;
+ // bool found = false;
+ while (*pp)
+ {
+ if (*pp == slab)
+ {
+ *pp = slab->next;
+ // found = true;
+ break;
+ }
+ pp = &(*pp)->next;
+ }
+
+ page->flags &= ~PG_slab;
+ PMM::free_page(slab);
+ }
+ else
+ {
+
+ if (slab->used_count == c->obj_count_per_slab - 1)
+ {
+ slab->next = c->slab_list;
+ c->slab_list = slab;
+ }
+ }
+
+ c->lock.release();
+ }
+} \ No newline at end of file
diff --git a/src/kernel/mm/vm.cc b/src/kernel/mm/vm.cc
index 650fcf1..1155d2c 100644
--- a/src/kernel/mm/vm.cc
+++ b/src/kernel/mm/vm.cc
@@ -1,75 +1,85 @@
#include "kernel/mm.h"
#include "kernel/pmm.h"
#include "kernel/riscv.h"
-#include "drivers/uart.h"
#include "common/types.h"
-#include "kernel/config.h"
#include "lib/string.h"
+#include "drivers/uart.h"
+extern "C"
+{
+ extern char kernel_end[];
+ extern char trampoline[];
+}
-extern char text_start[];
-extern char text_end[];
-extern char rodata_start[];
-extern char rodata_end[];
-extern char data_start[];
-extern char data_end[];
-extern char bss_start[];
-extern char bss_end[];
-extern char kernel_end[];
+namespace VM
+{
-extern char uservec[];
-extern char userret[];
-extern char trampoline[];
+ uint64 *kernel_pagetable;
-VM::pagetable_t kernel_pagetable;
+ static void page_ref_inc(uint64 pa)
+ {
+ struct Page *p = PMM::pa_to_page(pa);
+ if (p)
+ {
+ __atomic_add_fetch(&p->refcnt, 1, __ATOMIC_RELAXED);
+ }
+ }
-namespace VM
-{
- // pagetable_t kernel_pagetable;
+ static bool page_ref_dec(uint64 pa)
+ {
+ struct Page *p = PMM::pa_to_page(pa);
+ if (p)
+ {
+ if (__atomic_sub_fetch(&p->refcnt, 1, __ATOMIC_RELEASE) == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
- pte_t *walk(pagetable_t pagetable, uint64 va, int alloc)
+ static uint64 *walk(uint64 *pagetable, uint64 va, int alloc)
{
if (va >= MAXVA)
- return 0;
-
+ return nullptr;
+
for (int level = 2; level > 0; level--)
{
- pte_t *pte = &pagetable[PX(level, va)];
-
+ uint64 *pte = &pagetable[PX(level, va)];
if (*pte & PTE_V)
{
- pagetable = (pagetable_t)PTE2PA(*pte);
+ pagetable = (uint64 *)PTE2PA(*pte);
}
else
{
- if (!alloc || (pagetable = (pagetable_t)PMM::alloc_page()) == 0)
- return 0;
+ if (!alloc || (pagetable = (uint64 *)PMM::alloc_page()) == nullptr)
+ return nullptr;
+
memset(pagetable, 0, PGSIZE);
- // pagetable = (pagetable_t)PMM::alloc_page();
*pte = PA2PTE(pagetable) | PTE_V;
}
}
return &pagetable[PX(0, va)];
}
- int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
+ int mappages(uint64 *pagetable, uint64 va, uint64 size, uint64 pa, int perm)
{
uint64 a, last;
- pte_t *pte;
+ uint64 *pte;
+
+ if (size == 0)
+ return -1;
a = PGROUNDDOWN(va);
last = PGROUNDDOWN(va + size - 1);
- for(;;)
+ for (;;)
{
- if((pte = walk(pagetable, a, 1)) == 0)
+ if ((pte = walk(pagetable, a, 1)) == nullptr)
return -1;
if (*pte & PTE_V)
{
- Drivers::uart_puts("Remap panic: ");
- Drivers::print_hex(a);
- Drivers::uart_puts("\n");
return -1;
}
@@ -83,228 +93,356 @@ namespace VM
return 0;
}
- void kvmmap(uint64 va, uint64 pa, uint64 sz, int perm)
+ uint64 *uvmcreate()
{
- if (mappages(kernel_pagetable, va, sz, pa, perm) != 0)
+ uint64 *pagetable = (uint64 *)PMM::alloc_page();
+ if (pagetable)
{
- Drivers::uart_puts("kvmmap: failed to map\n");
+ memset(pagetable, 0, PGSIZE);
+
+ if (mappages(pagetable, TRAMPOLINE, PGSIZE, (uint64)trampoline, PTE_R | PTE_X) < 0)
+ {
+ PMM::free_page(pagetable);
+ return nullptr;
+ }
}
+ return pagetable;
}
- void kvminit()
+ void uvminit(uint64 *pagetable, uchar *src, uint64 sz)
{
- kernel_pagetable = (pagetable_t)PMM::alloc_page();
- memset(kernel_pagetable, 0, PGSIZE);
-
- kvmmap(g_uart_base, g_uart_base, PGSIZE, PTE_R | PTE_W);
-
- kvmmap(g_plic_base, g_plic_base, 0x400000, PTE_R | PTE_W);
-
- // kvmmap(g_clint_base, g_clint_base, 0x10000, PTE_R | PTE_W);
-
- kvmmap((uint64)text_start, (uint64)text_start,
- (uint64)text_end - (uint64)text_start, PTE_R | PTE_X);
-
- kvmmap((uint64)rodata_start, (uint64)rodata_start,
- (uint64)rodata_end - (uint64)rodata_start, PTE_R);
+ char *mem;
- kvmmap((uint64)data_start, (uint64)data_start,
- (uint64)kernel_end - (uint64)data_start, PTE_R | PTE_W);
+ if (sz >= PGSIZE)
+ {
+ Drivers::uart_puts("uvminit: initcode too big!\n");
+ return;
+ }
- kvmmap((uint64)kernel_end, (uint64)kernel_end,
- (uint64)PHYSTOP - (uint64)kernel_end, PTE_R | PTE_W);
+ mem = (char *)PMM::alloc_page();
+ memset(mem, 0, PGSIZE);
- kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
- }
- void kvminithart()
- {
- w_satp(MAKE_SATP(kernel_pagetable));
- sfence_vma();
+ mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W | PTE_R | PTE_X | PTE_U);
+ memmove(mem, src, sz);
}
-
- pagetable_t uvmcreate()
+ uint64 uvmalloc(uint64 *pagetable, uint64 oldsz, uint64 newsz, int xperm)
{
- pagetable_t pagetable = (pagetable_t)PMM::alloc_page();
- if (pagetable == 0)
- return 0;
- memset(pagetable, 0, PGSIZE);
+ char *mem;
+ uint64 a;
+
+ if (newsz < oldsz)
+ return oldsz;
- if (mappages(pagetable, TRAMPOLINE, PGSIZE, (uint64)trampoline, PTE_R | PTE_X) < 0)
+ oldsz = PGROUNDUP(oldsz);
+ for (a = oldsz; a < newsz; a += PGSIZE)
{
- return 0;
+ mem = (char *)PMM::alloc_page();
+ if (mem == nullptr)
+ {
+ uvmdealloc(pagetable, a, oldsz);
+ return 0;
+ }
+ memset(mem, 0, PGSIZE);
+ if (mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R | PTE_U | xperm) != 0)
+ {
+ PMM::free_page(mem);
+ uvmdealloc(pagetable, a, oldsz);
+ return 0;
+ }
}
-
- return pagetable;
+ return newsz;
}
- void uvminit(pagetable_t pagetable, uchar *src, uint sz)
+ uint64 uvmdealloc(uint64 *pagetable, uint64 oldsz, uint64 newsz)
{
- char *mem;
+ if (newsz >= oldsz)
+ return oldsz;
- if (sz >= PGSIZE)
+ if (PGROUNDUP(newsz) < PGROUNDUP(oldsz))
{
- Drivers::uart_puts("uvminit: initcode too big\n");
- return;
+ int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
+ uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
}
- mem = (char*)PMM::alloc_page();
- memset(mem, 0, PGSIZE);
-
- mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
-
- memmove(mem, src, sz);
-
- uint64 trap_pa = (uint64)uservec;
- uint64 trap_page_start = PGROUNDDOWN(trap_pa);
-
- mappages(pagetable, trap_page_start, PGSIZE, trap_page_start, PTE_R | PTE_X);
-
- mappages(pagetable, trap_page_start + PGSIZE, PGSIZE, trap_page_start + PGSIZE, PTE_R | PTE_X);
+ return newsz;
}
- void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
+ void uvmunmap(uint64 *pagetable, uint64 va, uint64 npages, int do_free)
{
uint64 a;
- pte_t *pte;
+ uint64 *pte;
if ((va % PGSIZE) != 0)
- {
- Drivers::uart_puts("uvmunmap: not aligned\n");
return;
- }
- for(a = va; a < va + npages * PGSIZE; a += PGSIZE)
+ for (a = va; a < va + npages * PGSIZE; a += PGSIZE)
{
- if ((pte = walk(pagetable, a, 0)) == 0)
+ if ((pte = walk(pagetable, a, 0)) == nullptr)
continue;
- if((*pte & PTE_V) == 0)
+ if ((*pte & PTE_V) == 0)
continue;
- if (PTE_FLAGS(*pte) == PTE_V)
+ if ((PTE_FLAGS(*pte) & (PTE_R | PTE_W | PTE_X)) == 0)
{
- Drivers::uart_puts("uvmunmap: not a leaf\n");
+ continue;
}
if (do_free)
{
uint64 pa = PTE2PA(*pte);
- PMM::free_page((void *)pa);
+ if (page_ref_dec(pa))
+ {
+ PMM::free_page((void *)pa);
+ }
}
*pte = 0;
}
}
- void uvmfree(pagetable_t pagetable, uint64 sz)
+ static void freewalk(uint64 *pagetable)
{
- if(sz > 0)
- uvmunmap(pagetable, 0, PGROUNDDOWN(sz)/PGSIZE, 1);
+ for (int i = 0; i < 512; i++)
+ {
+ uint64 pte = pagetable[i];
+ if ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0)
+ {
+ uint64 *child = (uint64 *)PTE2PA(pte);
+ freewalk(child);
+ pagetable[i] = 0;
+ }
+ }
+ PMM::free_page(pagetable);
+ }
- PMM::free_page((void *)pagetable);
+ void uvmfree(uint64 *pagetable, uint64 sz)
+ {
+ if (sz > 0)
+ uvmunmap(pagetable, 0, PGROUNDUP(sz) / PGSIZE, 1);
+ freewalk(pagetable);
}
- int uvmcopy(pagetable_t oldpt, pagetable_t newpt, uint64 sz)
+ int uvmcopy(uint64 *oldpt, uint64 *newpt, uint64 sz)
{
- pte_t *pte;
+ uint64 *pte;
uint64 pa, i;
uint flags;
- char *mem;
- for(i = 0; i < sz; i += PGSIZE)
+ for (i = 0; i < sz; i += PGSIZE)
{
- if((pte = walk(oldpt, i, 0)) == 0)
+ if ((pte = walk(oldpt, i, 0)) == nullptr)
continue;
if ((*pte & PTE_V) == 0)
continue;
-
+
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte);
- if ((mem = (char *)PMM::alloc_page()) == 0)
- goto err;
-
- memmove(mem, (char *)pa, PGSIZE);
+ if (flags & PTE_W)
+ {
+ flags &= ~PTE_W;
+ flags |= PTE_COW;
+ *pte = PA2PTE(pa) | flags;
+ }
- if (mappages(newpt, i, PGSIZE, (uint64)mem, flags) != 0)
+ if (mappages(newpt, i, PGSIZE, pa, flags) != 0)
{
- PMM::free_page(mem);
- goto err;
+ return -1;
}
+
+ page_ref_inc(pa);
}
+
+ sfence_vma();
return 0;
- err:
- uvmunmap(newpt, 0, i / PGSIZE, 1);
- return -1;
}
- uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
- {
- char *mem;
- uint64 a;
+ int handle_cow_fault(uint64 *pagetable, uint64 va);
- if (newsz < oldsz)
- return oldsz;
+ int copyout(uint64 *pagetable, uint64 dstva, char *src, uint64 len)
+ {
+ uint64 n, va0, pa0;
+ uint64 *pte;
- oldsz = PGROUNDUP(oldsz);
- for (a = oldsz; a < newsz; a += PGSIZE)
+ while (len > 0)
{
- mem = (char *)PMM::alloc_page();
- if (mem == 0)
- {
- uvmdealloc(pagetable, a, oldsz);
- return 0;
- }
- memset(mem, 0, PGSIZE);
- if (mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W | PTE_X | PTE_R | PTE_U) != 0)
+ va0 = PGROUNDDOWN(dstva);
+ pte = walk(pagetable, va0, 0);
+ if (pte == nullptr || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
+ return -1;
+
+ // Note: Maybe a bug
+ if (*pte & PTE_COW)
{
- PMM::free_page(mem);
- uvmdealloc(pagetable, a, oldsz);
- return 0;
+ if (handle_cow_fault(pagetable, va0) < 0)
+ return -1;
+ // Re-walk because handle_cow_fault replaced the page mapping
+ pte = walk(pagetable, va0, 0);
+ if (pte == nullptr || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
+ return -1;
}
- }
- return newsz;
- }
- uint64 uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
- {
- if (newsz >= oldsz)
- return oldsz;
+ pa0 = PTE2PA(*pte);
+ n = PGSIZE - (dstva - va0);
+ if (n > len)
+ n = len;
- if (PGROUNDUP(newsz) < PGROUNDUP(oldsz))
- {
- int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
- uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
- }
+ memmove((void *)(pa0 + (dstva - va0)), src, n);
- return newsz;
+ len -= n;
+ src += n;
+ dstva = va0 + PGSIZE;
+ }
+ return 0;
}
- int copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
+ int copyin(uint64 *pagetable, char *dst, uint64 srcva, uint64 len)
{
uint64 n, va0, pa0;
- pte_t *pte;
+ uint64 *pte;
while (len > 0)
{
va0 = PGROUNDDOWN(srcva);
pte = walk(pagetable, va0, 0);
- if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
- {
+ if (pte == nullptr || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
return -1;
- }
+
pa0 = PTE2PA(*pte);
n = PGSIZE - (srcva - va0);
- if(n > len)
+ if (n > len)
n = len;
-
- memmove(dst, (void*)(pa0 + (srcva - va0)), n);
+
+ memmove(dst, (void *)(pa0 + (srcva - va0)), n);
len -= n;
dst += n;
srcva = va0 + PGSIZE;
}
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)
+ return -1;
+
+ uint64 *pte = walk(pagetable, va, 0);
+ if (pte == nullptr || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
+ return -1;
+
+ if ((*pte & PTE_COW) == 0)
+ return -1;
+
+ uint64 pa = PTE2PA(*pte);
+ uint64 flags = PTE_FLAGS(*pte);
+
+ char *mem = (char *)PMM::alloc_page();
+ if (mem == nullptr)
+ return -1;
+
+ memmove(mem, (char *)pa, PGSIZE);
+
+ uint64 new_flags = (flags | PTE_W) & ~PTE_COW;
+ *pte = PA2PTE((uint64)mem) | new_flags | PTE_V;
+
+ if (page_ref_dec(pa))
+ {
+ PMM::free_page((void *)pa);
+ }
+
+ sfence_vma();
+ return 0;
+ }
+
+ static void kvmmap(uint64 va, uint64 pa, uint64 sz, int perm)
+ {
+ if (mappages(kernel_pagetable, va, sz, pa, perm) != 0)
+ {
+ Drivers::uart_puts("kvmmap: failed\n");
+ while (1)
+ ;
+ }
+ }
+
+ void kvminit()
+ {
+ kernel_pagetable = uvmcreate();
+
+ kvmmap(0x10000000, 0x10000000, 0x10000, PTE_R | PTE_W);
+ kvmmap(0x0c000000, 0x0c000000, 0x400000, PTE_R | PTE_W);
+ kvmmap(0x02000000, 0x02000000, 0x10000, PTE_R | PTE_W); // CLINT
+ kvmmap(KERNBASE, KERNBASE, PHYSTOP - KERNBASE, PTE_R | PTE_W | PTE_X);
+ }
+
+ void kvminithart()
+ {
+ w_satp(MAKE_SATP(kernel_pagetable));
+ sfence_vma();
+ Drivers::uart_puts("[Boot] MMU ENABLED! Using virtual addresses.\n");
+ }
+
+ uint64 walkaddr(uint64 *pagetable, uint64 va)
+ {
+ uint64 *pte;
+ uint64 pa;
+
+ if (va >= MAXVA)
+ return 0;
+
+ pte = walk(pagetable, va, 0);
+ if (pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
+ return 0;
+
+ pa = PTE2PA(*pte);
+ return pa;
}
} \ No newline at end of file
diff --git a/src/kernel/proc/exec.cc b/src/kernel/proc/exec.cc
new file mode 100644
index 0000000..22e4842
--- /dev/null
+++ b/src/kernel/proc/exec.cc
@@ -0,0 +1,214 @@
+#include "common/types.h"
+#include "kernel/riscv.h"
+#include "kernel/proc.h"
+#include "kernel/mm.h"
+#include "kernel/elf.h"
+#include "fs/fs.h"
+#include "lib/string.h"
+#include "drivers/uart.h"
+#include "kernel/trap.h"
+#include "drivers/uart.h"
+
+extern char trampoline[];
+
+// RAII: Page Table Resource Management
+class VmGuard
+{
+public:
+ uint64 *pagetable = nullptr;
+ uint64 sz = 0;
+
+ VmGuard() : pagetable(VM::uvmcreate()) {}
+
+ ~VmGuard()
+ {
+ if (pagetable)
+ {
+ VM::uvmfree(pagetable, sz);
+ }
+ }
+
+ // Copying is prohibited
+ VmGuard(const VmGuard &) = delete;
+ VmGuard &operator=(const VmGuard &) = delete;
+
+ // Submit resources: Ownership transfer, destructor no longer releases
+ uint64 *commit()
+ {
+ uint64 *pt = pagetable;
+ pagetable = nullptr;
+ return pt;
+ }
+};
+
+// RAII: Inode Lock Management
+class InodeGuard
+{
+public:
+ Inode *ip;
+ InodeGuard(Inode *i) : ip(i)
+ {
+ if (ip)
+ VFS::ilock(ip);
+ }
+ ~InodeGuard()
+ {
+ if (ip)
+ {
+ VFS::iunlockput(ip); // Automatically unlock and release references
+ }
+ }
+ // Copying is prohibited
+ InodeGuard(const InodeGuard &) = delete;
+ InodeGuard &operator=(const InodeGuard &) = delete;
+};
+
+// laod segment
+static int loadseg(uint64 *pagetable, uint64 va, Inode *ip, uint64 offset, uint64 sz)
+{
+ for (uint64 i = 0; i < sz; i += PGSIZE)
+ {
+ uint64 pa = VM::walkaddr(pagetable, va + i);
+ if (pa == 0)
+ return -1;
+
+ uint64 n = PGSIZE - (va + i) % PGSIZE;
+ if (i + n > sz)
+ n = sz - i;
+
+ // Read physical address (isUser=0)
+ if (ip->read((char *)(pa + (va + i) % PGSIZE), offset + i, n, 0) != (int)n)
+ return -1;
+ }
+ return 0;
+}
+
+namespace Exec
+{
+ int exec(char *path, char **argv)
+ {
+ // Get Inode
+ Inode *raw_ip = VFS::namei(path);
+ if (!raw_ip)
+ return -1;
+ InodeGuard iguard(raw_ip);
+
+ // Get ELF head
+ struct elfhdr elf;
+ if (raw_ip->read((char *)&elf, 0, sizeof(elf), 0) != sizeof(elf))
+ return -1;
+ if (elf.magic != ELF_MAGIC)
+ return -1;
+ // Create new pagetable
+ VmGuard vm_guard;
+ if (!vm_guard.pagetable)
+ return -1;
+ // Load Program Segment
+ struct proghdr ph;
+ for (int i = 0; i < elf.phnum; i++)
+ {
+ uint64 off = elf.phoff + i * sizeof(ph);
+ if (raw_ip->read((char *)&ph, off, sizeof(ph), 0) != sizeof(ph))
+ return -1;
+
+ if (ph.type != ELF_PROG_LOAD)
+ continue;
+ if (ph.memsz < ph.filesz)
+ return -1;
+ if (ph.vaddr + ph.memsz < ph.vaddr)
+ return -1;
+
+ uint64 sz1 = VM::uvmalloc(vm_guard.pagetable, vm_guard.sz, ph.vaddr + ph.memsz, PTE_W | PTE_X | PTE_R | PTE_U);
+ if (sz1 == 0)
+ return -1;
+ vm_guard.sz = sz1;
+
+ if (loadseg(vm_guard.pagetable, ph.vaddr, raw_ip, ph.off, ph.filesz) < 0)
+ return -1;
+ }
+
+ // Prepare user stack
+ uint64 sz = PGROUNDUP(vm_guard.sz);
+
+ const int stack_data_pages = 16;
+ const int stack_total_pages = stack_data_pages + 1;
+
+ uint64 sz1 = VM::uvmalloc(vm_guard.pagetable, sz, sz + stack_total_pages * PGSIZE, PTE_W | PTE_R | PTE_U);
+ if (sz1 == 0)
+ return -1;
+ vm_guard.sz = sz1;
+
+ VM::uvmunmap(vm_guard.pagetable, sz, 1, 0); // protected pagetable
+
+ uint64 sp = sz1;
+ uint64 stackbase = sp - stack_data_pages * PGSIZE;
+ uint64 ustack[32];
+ uint64 argc = 0;
+
+ // Push parameter string
+ for (; argv[argc]; argc++)
+ {
+ if (argc >= 31)
+ return -1;
+ sp -= strlen(argv[argc]) + 1;
+ sp -= sp % 16;
+ if (sp < stackbase)
+ return -1;
+
+ if (VM::copyout(vm_guard.pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
+ return -1;
+ ustack[argc] = sp;
+ }
+ ustack[argc] = 0;
+
+ // Push into parameter array
+ sp -= (argc + 1) * sizeof(uint64);
+ sp -= sp % 16;
+ if (sp < stackbase)
+ return -1;
+ if (VM::copyout(vm_guard.pagetable, sp, (char *)ustack, (argc + 1) * sizeof(uint64)) < 0)
+ return -1;
+
+ // Commit Changes
+ struct Proc *p = myproc();
+ if (VM::mappages(vm_guard.pagetable, TRAPFRAME, PGSIZE, (uint64)p->tf, PTE_R | PTE_W) < 0)
+ return -1;
+
+ uint64 oldsz = p->sz;
+ uint64 *oldpagetable = p->pagetable;
+
+ // setup Trapframe
+ p->tf->a0 = argc;
+ p->tf->a1 = sp;
+ p->tf->sp = sp;
+ p->tf->epc = elf.entry;
+
+ // update process name
+ char *s = path, *last = path;
+ while (*s)
+ {
+ if (*s == '/')
+ last = s + 1;
+ s++;
+ }
+ // memmove(p->name, last, sizeof(p->name));
+ size_t len = 0;
+ while (last[len] && len < sizeof(p->name) - 1)
+ {
+ p->name[len] = last[len];
+ len++;
+ }
+ p->name[len] = '\0';
+
+ // change pagetable
+ p->pagetable = vm_guard.commit();
+ p->sz = vm_guard.sz;
+
+ // release old pagetable
+ VM::uvmfree(oldpagetable, oldsz);
+
+ asm volatile("fence.i");
+
+ return 0; // iguard unlocks and releases IP upon destruction
+ }
+} \ No newline at end of file
diff --git a/src/kernel/proc/proc.cc b/src/kernel/proc/proc.cc
index b0ab370..5db5f7a 100644
--- a/src/kernel/proc/proc.cc
+++ b/src/kernel/proc/proc.cc
@@ -1,112 +1,231 @@
#include "kernel/proc.h"
+#include "kernel/proc_def.h"
+#include "kernel/runqueue.h"
+#include "kernel/cpu.h"
+#include "kernel/spinlock.h"
#include "kernel/pmm.h"
#include "kernel/mm.h"
-#include "drivers/uart.h"
#include "kernel/trap.h"
-#include "common/types.h"
#include "kernel/riscv.h"
-#include "kernel/config.h"
-#include "kernel/spinlock.h"
+#include "kernel/slab.h"
+#include "drivers/uart.h"
+#include "fs/fs.h"
+#include "fs/file.h"
#include "lib/string.h"
-#define NPROC 10
+// #define NPROC 64
+#define NCPU 8
extern "C" void swtch(struct Context *old_ctx, struct Context *new_ctx);
-extern char uservec[];
+extern char uservec[], trampoline[];
+extern "C" void usertrap();
extern "C" void userret(uint64, uint64);
-extern char trampoline[];
+extern char initcode_start[], initcode_end[];
+
+// struct Proc procs[NPROC];
+struct Proc *proc_list_head = nullptr;
+struct Proc *initproc = nullptr;
+struct Spinlock proc_mem_lock;
+struct cpu cpus[NCPU];
+struct RunQueue runqueues[NCPU];
+
-struct Proc procs[NPROC];
-struct Context main_context;
-struct Proc *current_proc = nullptr;
-struct spinlock proc_lock;
+static int nextpid = 1;
-int nextpid = 1;
-extern char initcode_start[];
-extern char initcode_end[];
+struct cpu *mycpu()
+{
+ int id = r_tp();
+ return &cpus[id];
+}
+
-struct Proc *myproc()
+void RunQueue::init(const char *name)
{
- // 简化处理
- return current_proc;
+ lock.init(name);
+ count = 0;
+ for (int i = 0; i < N_PRIO; i++)
+ {
+ heads[i] = nullptr;
+ tails[i] = nullptr;
+ }
}
+void RunQueue::enqueue(struct Proc *p)
+{
+ if (p->priority < 0 || p->priority >= N_PRIO)
+ p->priority = PRIO_MIN;
+ p->next = nullptr;
+ if (heads[p->priority] == nullptr)
+ {
+ heads[p->priority] = p;
+ tails[p->priority] = p;
+ }
+ else
+ {
+ tails[p->priority]->next = p;
+ tails[p->priority] = p;
+ }
+ count++;
+}
+
+struct Proc *RunQueue::dequeue()
+{
+ for (int i = 0; i < N_PRIO; i++)
+ {
+ if (heads[i])
+ {
+ struct Proc *p = heads[i];
+ heads[i] = p->next;
+ if (!heads[i])
+ tails[i] = nullptr;
+ p->next = nullptr;
+ count--;
+ return p;
+ }
+ }
+ return nullptr;
+}
+
+struct Proc *RunQueue::steal()
+{
+ for (int i = N_PRIO - 1; i >= 0; i--)
+ {
+ if (heads[i])
+ {
+ struct Proc *p = heads[i];
+ heads[i] = p->next;
+ if (!heads[i])
+ tails[i] = nullptr;
+ p->next = nullptr;
+ count--;
+ return p;
+ }
+ }
+ return nullptr;
+}
+
+// Trap Return Functions
+
void usertrapret()
{
struct Proc *p = myproc();
intr_off();
+ // Set stvec to point to uservec in the trampoline
+ // When an interrupt occurs in user mode, the CPU will jump here
uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
w_stvec(trampoline_uservec);
+ // Fill in the Trapframe, used when uservec saves the kernel state
p->tf->kernel_satp = r_satp();
p->tf->kernel_sp = (uint64)p->kstack + PGSIZE;
p->tf->kernel_trap = (uint64)usertrap;
p->tf->kernel_hartid = r_tp();
+ // Set SSTATUS
+ // SPP = 0 (return to User Mode)
+ // SPIE = 1 (enable interrupts)
unsigned long x = r_sstatus();
x &= ~SSTATUS_SPP;
x |= SSTATUS_SPIE;
w_sstatus(x);
+ // Set SEPC to the user program counter
w_sepc(p->tf->epc);
+ // Prepare user page table SATP
uint64 satp = MAKE_SATP(p->pagetable);
+ // Calculate the virtual address of userret in the trampoline
uint64 fn = TRAMPOLINE + ((uint64)userret - (uint64)trampoline);
+
+ // Jump to userret, with parameters being the TRAPFRAME address and the user page table SATP
((void (*)(uint64, uint64))fn)(TRAPFRAME, satp);
}
+
void forkret()
{
- //....
- Spinlock::release(&proc_lock);
+
+ struct cpu *c = mycpu();
+ c->runqueue->lock.release();
Drivers::uart_puts("[Proc] forkret: jumping to user.\n");
+
+ // Enter user mode
usertrapret();
}
+// ProcManager Implementation
+
static Proc *allocproc()
{
- struct Proc* p;
- Spinlock::acquire(&proc_lock);
- for (p = procs; p < &procs[NPROC]; p++)
+ struct Proc *p;
+ proc_mem_lock.acquire();
+
+ p = (struct Proc *)Slab::kmalloc(sizeof(struct Proc));
+
+ if (!p)
{
- if (p->state == UNUSED)
- {
- goto found;
- }
+ proc_mem_lock.release();
+ return 0;
}
- Spinlock::release(&proc_lock);
- return 0;
+ memset(p, 0, sizeof(struct Proc));
+
+ p->all_next = proc_list_head;
+ p->all_prev = nullptr;
+ if (proc_list_head)
+ proc_list_head->all_prev = p;
+ proc_list_head = p;
+
-found:
p->state = USED;
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)
{
p->state = UNUSED;
- Spinlock::release(&proc_lock);
+ if (p->all_next)
+ p->all_next->all_prev = p->all_prev;
+ if (p->all_prev)
+ p->all_prev->all_next = p->all_next;
+ if (p == proc_list_head)
+ proc_list_head = p->all_next;
+ Slab::kfree(p);
+ proc_mem_lock.release();
return 0;
}
+
if ((p->kstack = PMM::alloc_page()) == 0)
{
PMM::free_page(p->tf);
p->state = UNUSED;
- Spinlock::release(&proc_lock);
+ if (p->all_next)
+ p->all_next->all_prev = p->all_prev;
+ if (p->all_prev)
+ p->all_prev->all_next = p->all_next;
+ if (p == proc_list_head)
+ proc_list_head = p->all_next;
+ Slab::kfree(p);
+
+ proc_mem_lock.release();
return 0;
}
memset(p->kstack, 0, PGSIZE);
memset(&p->context, 0, sizeof(p->context));
+
+ // Set the context so that the next time this process is scheduled, it starts executing directly from forkret
p->context.ra = (uint64)forkret;
p->context.sp = (uint64)p->kstack + PGSIZE;
-
- Spinlock::release(&proc_lock);
return p;
}
@@ -115,218 +234,275 @@ static void freeproc(struct Proc *p)
if (p->tf)
PMM::free_page(p->tf);
p->tf = 0;
-
if (p->kstack)
PMM::free_page(p->kstack);
p->kstack = 0;
-
if (p->pagetable)
VM::uvmfree(p->pagetable, p->sz);
- p->pagetable = 0;
-
- p->sz = 0;
- p->pid = 0;
- p->parent = 0;
- p->name[0] = 0;
- p->chan = 0;
- p->state = UNUSED;
+ 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;
+ }
+
+ if (p->all_prev)
+ p->all_prev->all_next = p->all_next;
+ else
+ proc_list_head = p->all_next;
+
+ if (p->all_next)
+ p->all_next->all_prev = p->all_prev;
+
+ Slab::kfree(p);
}
int fork()
{
- int pid;
struct Proc *np;
struct Proc *p = myproc();
if ((np = allocproc()) == 0)
- {
return -1;
- }
np->pagetable = VM::uvmcreate();
-
+ np->sz = p->sz;
if (VM::uvmcopy(p->pagetable, np->pagetable, p->sz) < 0)
{
freeproc(np);
- Spinlock::release(&proc_lock);
+ proc_mem_lock.release();
return -1;
}
- np->sz = p->sz;
+
- if (VM::mappages(np->pagetable, TRAPFRAME, PGSIZE,
- (uint64)np->tf, PTE_R | PTE_W) < 0)
+ if (VM::mappages(np->pagetable, TRAPFRAME, PGSIZE, (uint64)np->tf, PTE_R | PTE_W) < 0)
{
freeproc(np);
+ proc_mem_lock.release();
return -1;
}
*(np->tf) = *(p->tf);
-
np->tf->a0 = 0;
-
strcpy(np->name, p->name);
np->parent = p;
+ np->priority = p->priority;
- Spinlock::acquire(&proc_lock);
+ 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();
+ c->runqueue->lock.acquire();
np->state = RUNNABLE;
- pid = np->pid;
- Spinlock::release(&proc_lock);
+ c->runqueue->enqueue(np);
+ c->runqueue->lock.release();
- return pid;
+ return np->pid;
}
namespace ProcManager
{
+
void init()
{
- Spinlock::init(&proc_lock, "proc");
+ proc_mem_lock.init("proc_mem");
+
+ proc_list_head = nullptr;
- for (int i = 0; i < NPROC; i++)
+ for (int i = 0; i < NCPU; i++)
{
- procs[i].state = UNUSED;
- procs[i].kstack = 0;
+ cpus[i].runqueue = &runqueues[i];
+ cpus[i].runqueue->init("runqueue");
}
}
- void create_kernel_thread(void (*func)(), const char *name)
+ void scheduler()
{
- struct Proc *p = nullptr;
- for(int i = 0; i < NPROC; i++)
- {
- if(procs[i].state == UNUSED)
- {
- p = &procs[i];
- p->pid = i + 1;
- break;
- }
- }
+ struct Proc *p;
+ struct cpu *c = mycpu();
+ c->proc = 0;
+ Drivers::uart_puts("[Proc] Scheduler started (Per-CPU).\n");
- if (!p)
+ for (;;)
{
- Drivers::uart_puts("Error: No free process slots!\n");
- return;
- }
+ intr_on();
- p->kstack = PMM::alloc_page();
- if (!p->kstack)
- {
- Drivers::uart_puts("Error: Alloc kstack failed!\n");
- return;
- }
+ // Get from the local queue
+ c->runqueue->lock.acquire();
+ p = c->runqueue->dequeue();
+ c->runqueue->lock.release();
- p->context.ra = (uint64)func;
+ // Work Stealing
+ if (!p)
+ {
+ for (int i = 0; i < NCPU; i++)
+ {
+ if ((uint64)i == r_tp())
+ continue;
+
+ struct cpu *victim = &cpus[i];
+ victim->runqueue->lock.acquire();
+ if (victim->runqueue->count > 0)
+ p = victim->runqueue->steal();
+ victim->runqueue->lock.release();
+ if (p)
+ break;
+ }
+ }
- p->context.sp = (uint64)p->kstack + 4096;
+ if (p)
+ {
+ p->state = RUNNING;
+ c->proc = p;
- p->state = RUNNABLE;
- strcpy(p->name, name);
+ c->runqueue->lock.acquire();
+ swtch(&c->context, &p->context);
+ c->runqueue->lock.release();
- Drivers::uart_puts("[Proc] Created thread: ");
- Drivers::uart_puts(name);
- Drivers::uart_puts("\n");
+ c->proc = 0;
+ }
+ }
}
void yield()
{
struct Proc *p = myproc();
- Spinlock::acquire(&proc_lock);
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
if (p && p->state == RUNNING)
{
p->state = RUNNABLE;
- swtch(&p->context, &main_context);
+ if (p->priority < PRIO_MIN)
+ p->priority++;
+ c->runqueue->enqueue(p);
+ swtch(&p->context, &c->context);
}
- Spinlock::release(&proc_lock);
+ c->runqueue->lock.release();
}
- void scheduler()
+ void user_init()
{
- struct Proc *p = nullptr;
- Drivers::uart_puts("[Proc] Scheduler started.\n");
+ struct Proc *p = allocproc();
+ if (!p)
+ return;
- for (;;)
- {
- intr_on();
+ initproc = p;
- Spinlock::acquire(&proc_lock);
+ p->pagetable = VM::uvmcreate();
- for (p = procs; p < &procs[NPROC]; p++)
+ uint64 initcode_sz = initcode_end - initcode_start;
+ Drivers::uart_puts("[Proc] initcode size: ");
+ Drivers::uart_put_int(initcode_sz);
+ Drivers::uart_puts(" bytes\n");
+
+ uint64 code_pages = PGROUNDUP(initcode_sz) / PGSIZE;
+
+ for (uint64 i = 0; i < code_pages; i++)
+ {
+ void *mem = PMM::alloc_page();
+ if (!mem)
{
- if (p->state == RUNNABLE)
- {
- p->state = RUNNING;
- current_proc = p;
+ Drivers::uart_puts("user_init: out of memory for code\n");
+ return;
+ }
+ memset(mem, 0, PGSIZE);
- swtch(&main_context, &p->context);
+ uint64 src_offset = i * PGSIZE;
+ uint64 copy_len = PGSIZE;
+ if (src_offset + copy_len > initcode_sz)
+ {
+ copy_len = initcode_sz - src_offset;
+ }
+ memmove(mem, (void *)(initcode_start + src_offset), copy_len);
- current_proc = nullptr;
- }
+ if (VM::mappages(p->pagetable, i * PGSIZE, PGSIZE, (uint64)mem, PTE_R | PTE_W | PTE_X | PTE_U) < 0)
+ {
+ Drivers::uart_puts("user_init: mappages code failed\n");
+ return;
}
- Spinlock::release(&proc_lock);
}
- }
- void user_init()
- {
- struct Proc *p;
+ uint64 stack_va = (code_pages + 1) * PGSIZE;
- p = allocproc();
- if (!p)
- {
- Drivers::uart_puts("user_init: failed\n");
+ void *stack = PMM::alloc_page();
+ if (!stack)
return;
- }
-
- p->pagetable = VM::uvmcreate();
+ memset(stack, 0, PGSIZE);
- uint64 initcode_sz = initcode_end - initcode_start;
- VM::uvminit(p->pagetable, (uchar *)initcode_start, initcode_sz);
- p->sz = PGSIZE;
+ if (VM::mappages(p->pagetable, stack_va, PGSIZE, (uint64)stack, PTE_R | PTE_W | PTE_U) < 0)
+ return;
- if (VM::mappages(p->pagetable, TRAPFRAME, PGSIZE,
- (uint64)p->tf, PTE_R | PTE_W) < 0)
- {
- Drivers::uart_puts("user_init: map trapframe failed\n");
+ if (VM::mappages(p->pagetable, TRAPFRAME, PGSIZE, (uint64)p->tf, PTE_R | PTE_W) < 0)
return;
- }
-
p->tf->epc = 0;
- p->tf->sp = PGSIZE;
-
+ p->tf->sp = stack_va + PGSIZE;
+ p->sz = stack_va + PGSIZE;
strcpy(p->name, "initcode");
-
- Spinlock::acquire(&proc_lock);
+ p->cwd = nullptr;
+
+ proc_mem_lock.release();
+
+ struct cpu *c = &cpus[0];
+ c->runqueue->lock.acquire();
p->state = RUNNABLE;
- Spinlock::release(&proc_lock);
+ c->runqueue->enqueue(p);
+ c->runqueue->lock.release();
Drivers::uart_puts("[Proc] user_init success. PID: 1\n");
+ asm volatile("fence.i");
}
- void sleep(void *chan, struct spinlock *lk)
+ void sleep(void *chan, Spinlock *lk)
{
struct Proc *p = myproc();
- if(lk != &proc_lock)
- {
- Drivers::uart_puts("sleep: lock mismatch\n");
- while (1)
- ;
- }
- p->chan = chan;
- p->state = SLEEPING;
+ struct cpu *c = mycpu();
- swtch(&p->context, &main_context);
+ if (lk && lk != &proc_mem_lock)
+ lk->release();
+ c->runqueue->lock.acquire();
+ p->chan = chan;
+ p->state = SLEEPING;
+ swtch(&p->context, &c->context);
p->chan = 0;
+ c->runqueue->lock.release();
+
+ if (lk && lk != &proc_mem_lock)
+ lk->acquire();
}
void wakeup(void *chan)
{
- for(struct Proc *p = procs; p < &procs[NPROC]; p++)
+ for (struct Proc *p = proc_list_head; p != nullptr; p = p->all_next)
{
if (p->state == SLEEPING && p->chan == chan)
{
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
p->state = RUNNABLE;
+ c->runqueue->enqueue(p);
+ c->runqueue->lock.release();
}
}
}
@@ -334,18 +510,15 @@ namespace ProcManager
void exit(int status)
{
struct Proc *p = myproc();
- struct Proc *initproc = &procs[0];
-
if (p == initproc)
- {
- Drivers::uart_puts("init exiting!\n");
while (1)
;
- }
- Spinlock::acquire(&proc_lock);
+ wakeup(p->parent);
- for (struct Proc *pp = procs; pp < &procs[NPROC]; pp++)
+ proc_mem_lock.acquire();
+
+ for (struct Proc *pp = proc_list_head; pp != nullptr; pp = pp->all_next)
{
if (pp->parent == p)
{
@@ -354,56 +527,94 @@ namespace ProcManager
}
}
- wakeup(p->parent);
+ proc_mem_lock.release();
p->xstate = status;
- p->state = ZOMBIE;
- swtch(&p->context, &main_context);
-
- while(1);
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
+ p->state = ZOMBIE;
+ swtch(&p->context, &c->context);
+ while (1)
+ ;
}
int wait(uint64 addr)
{
- struct Proc* np;
+
+ struct Proc *np;
int havekids, pid;
struct Proc *p = myproc();
-
- Spinlock::acquire(&proc_lock);
-
- for(;;)
+ for (;;)
{
havekids = 0;
- for (np = procs; np < &procs[NPROC]; np++)
+ proc_mem_lock.acquire();
+
+ for (np = proc_list_head; np != nullptr; np = np->all_next)
{
if (np->parent == p)
{
havekids = 1;
if (np->state == ZOMBIE)
{
- // Found one
+
+ struct cpu *child_cpu = np->cpu;
+ if (child_cpu)
+ {
+ proc_mem_lock.release();
+ child_cpu->runqueue->lock.acquire();
+ child_cpu->runqueue->lock.release();
+ proc_mem_lock.acquire();
+ }
+
pid = np->pid;
- if (addr != 0)
+ if (addr != 0 && VM::copyout(p->pagetable, addr, (char *)&np->xstate, sizeof(np->xstate)) < 0)
{
- // copyout(p->pagetable, addr, (char*)&np->xstate, sizeof(np->xstate));
- // 暂时不做 copyout
+ proc_mem_lock.release();
+ return -1;
}
freeproc(np);
- Spinlock::release(&proc_lock);
+ proc_mem_lock.release();
return pid;
}
}
}
+ proc_mem_lock.release();
+
if (!havekids || p->state == ZOMBIE)
- {
- Spinlock::release(&proc_lock);
return -1;
- }
- sleep(p, &proc_lock);
+ sleep(p, &proc_mem_lock);
}
}
+ int growproc(int n)
+ {
+ uint64 sz;
+ struct Proc *p = myproc();
+
+ sz = p->sz;
+
+ if (n > 0)
+ {
+ // Call VM::uvmalloc to allocate memory
+ // Parameters: page table, old size, new size, permissions (PTE_W)
+ if ((sz = VM::uvmalloc(p->pagetable, sz, sz + n, PTE_W | PTE_R | PTE_U)) == 0)
+ {
+ return -1;
+ }
+ }
+ else if (n < 0)
+ {
+ sz = VM::uvmdealloc(p->pagetable, sz, sz + n);
+ }
+ p->sz = sz;
+ return 0;
+ }
+ void create_kernel_thread(void (*func)(), const char *name)
+ {
+ (void)func;
+ (void)name;
+ }
} \ No newline at end of file
diff --git a/src/kernel/spinlock.cc b/src/kernel/spinlock.cc
index 210e0b9..55dfc6b 100644
--- a/src/kernel/spinlock.cc
+++ b/src/kernel/spinlock.cc
@@ -1,107 +1,195 @@
#include "kernel/spinlock.h"
#include "kernel/riscv.h"
-#include "kernel/proc.h"
+#include "kernel/cpu.h"
+#include "kernel/proc_def.h"
#include "drivers/uart.h"
-#include "common/types.h"
-#define NCPU 8
+extern struct cpu cpus[];
-namespace Spinlock
+namespace ProcManager
{
- int g_noff[NCPU];
+ void sleep(void *chan, Spinlock *lk);
+ void wakeup(void *chan);
+}
+
+// Interrupt Depth Management
+void push_off(void);
+void pop_off(void);
+
+// // Get the pointer of the current CPU
+// // Depends on r_tp() in riscv.h to read the tp register
+// struct cpu *mycpu()
+// {
+// int id = r_tp();
+// return &cpus[id];
+// }
+
+// Get the current process
+// Must be called with interrupts disabled, or read atomically in a safe manner
+struct Proc *myproc()
+{
+ push_off();
+ struct cpu *c = mycpu();
+ struct Proc *p = c->proc;
+ pop_off();
+ return p;
+}
- int g_intena[NCPU];
+// Spinlock Implementation
- int cpuid()
- {
- return r_tp();
- }
+void Spinlock::init(const char *lock_name)
+{
+ name = lock_name;
+ locked = 0;
+ cpu = nullptr;
+}
+
+void Spinlock::acquire()
+{
+ push_off(); // Disable interrupts: prevent deadlocks (interrupt handler requests a lock that is already held)
- void init(struct spinlock *lk, char* name)
+ if (holding())
{
- lk->name = name;
- lk->locked = 0;
- lk->cpu_id = -1;
+ Drivers::uart_puts("panic: recursive acquire ");
+ Drivers::uart_puts(name);
+ Drivers::uart_puts("\n");
+ while (1)
+ ;
}
- void push_off()
+ // Atomic Exchange
+ // __ATOMIC_ACQUIRE: Ensures that reads and writes after the acquire are not reordered before the acquire
+ while (__atomic_exchange_n(&locked, 1, __ATOMIC_ACQUIRE))
{
- int old = intr_get();
- intr_off();
-
- int id = cpuid();
- if (g_noff[id] == 0)
+ // Busy-wait (Spinning)
+ // Optimization: Read-only, no writes, reducing cache coherence traffic
+ while (__atomic_load_n(&locked, __ATOMIC_RELAXED) == 1)
{
- g_intena[id] = old;
+ asm volatile("nop"); // RISC-V indicates that the CPU is in a spin state
}
- g_noff[id] += 1;
}
- void pop_off()
+ // Record holder
+ cpu = mycpu();
+}
+
+void Spinlock::release()
+{
+ if (!holding())
{
- int id = cpuid();
- if (intr_get())
- Drivers::uart_puts("pop_off - interruptible\n");
+ Drivers::uart_puts("panic: release not holding\n");
+ while (1)
+ ;
+ }
- if (g_noff[id] < 1)
- Drivers::uart_puts("pop_off - count error\n");
+ cpu = nullptr;
- g_noff[id] -= 1;
+ // Atomic Store
+ // __ATOMIC_RELEASE: Ensures all reads and writes before the Release are completed
+ __atomic_store_n(&locked, 0, __ATOMIC_RELEASE);
- if (g_noff[id] == 0 && g_intena[id])
- {
- intr_on();
- }
- }
+ pop_off();
+}
+
+// Check if the current CPU holds the lock
+bool Spinlock::holding()
+{
+ // Interrupt checks must be disabled, otherwise a process switch may occur causing CPU fluctuations.
+ int r;
+ push_off();
+ r = (locked && cpu == mycpu());
+ pop_off();
+ return r;
+}
+
+
+// Mutex Implementation (Sleep Locks)
+
+void Mutex::init(const char *lock_name)
+{
+ name = lock_name;
+ locked = 0;
+ pid = 0;
+ lk.init("mutex_internal");
+}
+
+void Mutex::acquire()
+{
+ lk.acquire(); // Acquire the internal lock to protect the locked field
- int holding(struct spinlock *lk)
+ while (locked)
{
- int r;
- push_off();
- r = (lk->locked && lk->cpu_id == cpuid());
- pop_off();
- return r;
+ // Lock is held, preparing to sleep
+ // Atomicity contract of sleep():
+ // 1. Mark the current process as SLEEPING
+ // 2. Release lk (so others can operate on locked)
+ // 3. Switch CPU (context switch)
+ // 4. After being woken up, reacquire the lk lock and return
+ ProcManager::sleep(this, &lk);
}
- void acquire(struct spinlock *lk)
- {
- push_off();
+ locked = 1;
+ pid = myproc()->pid;
- if(holding(lk))
- {
- Drivers::uart_puts("acquire: panic - recursive lock ");
- Drivers::uart_puts(lk->name);
- Drivers::uart_puts("\n");
- while (1)
- ;
- }
+ lk.release();
+}
- while (__sync_lock_test_and_set(&lk->locked, 1) != 0)
- {
- // 自旋等待
- }
+void Mutex::release()
+{
+ lk.acquire();
- __sync_synchronize();
+ locked = 0;
+ pid = 0;
- lk->cpu_id = cpuid();
- }
+ // Wake up all processes sleeping on this lock (chan = this)
+ ProcManager::wakeup(this);
- void release(struct spinlock *lk)
- {
- if (!holding(lk))
- {
- Drivers::uart_puts("release: panic - not holding\n");
- while (1)
- ;
- }
+ lk.release();
+}
- lk->cpu_id = -1;
+bool Mutex::holding()
+{
+ lk.acquire(); // Must use a lock when reading to prevent race conditions.
+ bool r = locked && (pid == myproc()->pid);
+ lk.release();
+ return r;
+}
- __sync_synchronize();
+// Interrupt Depth Management
- __sync_lock_release(&lk->locked);
+void push_off(void)
+{
+ int old = intr_get();
+ intr_off();
+
+ struct cpu *c = mycpu();
+
+ if (c->noff == 0)
+ c->intena = old;
+
+ c->noff += 1;
+}
+
+void pop_off(void)
+{
+ struct cpu *c = mycpu();
- pop_off();
+ if (intr_get())
+ {
+ Drivers::uart_puts("panic: pop_off - interruptible\n");
+ while (1)
+ ;
}
+ if (c->noff < 1)
+ {
+ Drivers::uart_puts("panic: pop_off count\n");
+ while (1)
+ ;
+ }
+
+ c->noff -= 1;
-} // namespace Spinlock
+ // An interrupt is only truly enabled when the nesting level returns to zero and the outermost layer allows interrupts.
+ if (c->noff == 0 && c->intena)
+ intr_on();
+} \ No newline at end of file
diff --git a/src/kernel/syscall.cc b/src/kernel/syscall.cc
index 92b1a1b..6af7730 100644
--- a/src/kernel/syscall.cc
+++ b/src/kernel/syscall.cc
@@ -1,22 +1,34 @@
#include "kernel/syscall.h"
#include "common/types.h"
+#include "common/fcntl.h"
#include "kernel/proc.h"
#include "kernel/riscv.h"
#include "drivers/uart.h"
+#include "drivers/virtio.h"
#include "kernel/timer.h"
#include "kernel/mm.h"
+#include "kernel/trap.h"
+#include "kernel/slab.h"
+#include "fs/fat32.h"
+#include "fs/file.h"
+#include "lib/string.h"
-extern int fork();
+extern "C++" int fork();
+namespace Exec
+{
+ int exec(char *path, char **argv);
+}
namespace ProcManager
{
void exit(int status);
- int wait(uint64 addr);
+ int wait(uint64_t addr);
}
-uint64 argraw(int n)
+
+static uint64_t argraw(int n)
{
- struct Proc *p = myproc();
+ Proc *p = myproc();
switch (n)
{
case 0:
@@ -31,76 +43,237 @@ uint64 argraw(int n)
return p->tf->a4;
case 5:
return p->tf->a5;
+ default:
+ return static_cast<uint64_t>(-1);
+ }
+}
+
+static int argint(int n, int *ip)
+{
+ *ip = static_cast<int>(argraw(n));
+ return 0;
+}
+
+static int argstr(int n, char *buf, int max)
+{
+ uint64 addr = argraw(n);
+ return VM::copyinstr(myproc()->pagetable, buf, addr, max);
+}
+
+struct KernelArgv
+{
+ static const int MAX_ARGS = 32;
+ static const int MAX_ARG_LEN = 128;
+
+ char *args[MAX_ARGS];
+
+ KernelArgv()
+ {
+ memset(args, 0, sizeof(args));
+ }
+
+ ~KernelArgv()
+ {
+ for (int i = 0; i < MAX_ARGS; i++)
+ {
+ if (args[i])
+ {
+ Slab::kfree(args[i]);
+ args[i] = nullptr;
+ }
+ }
+ }
+
+ KernelArgv(const KernelArgv &) = delete;
+ KernelArgv &operator=(const KernelArgv &) = delete;
+};
+
+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;
}
-int argint(int n, int *ip)
+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);
+
+ return fd;
+}
+
+static uint64 sys_close()
{
- *ip = argraw(n);
+ 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;
}
-uint64 sys_exit()
+static uint64 sys_exec()
+{
+ char path[128];
+ uint64 uargv_ptr;
+
+ if (argstr(0, path, sizeof(path)) < 0)
+ {
+ return -1;
+ }
+
+ if (argint(1, (int *)&uargv_ptr) < 0)
+ {
+ return -1;
+ }
+
+ KernelArgv kargv;
+
+ for (int i = 0; i < KernelArgv::MAX_ARGS; ++i)
+ {
+ uint64 uarg_str_ptr;
+
+ if (VM::copyin(myproc()->pagetable, (char *)&uarg_str_ptr, uargv_ptr + i * sizeof(uint64), sizeof(uint64)) < 0)
+ {
+ return -1;
+ }
+
+ if (uarg_str_ptr == 0)
+ {
+ kargv.args[i] = nullptr;
+ break;
+ }
+
+ kargv.args[i] = (char *)Slab::kmalloc(KernelArgv::MAX_ARG_LEN);
+ if (kargv.args[i] == nullptr)
+ {
+ return -1;
+ }
+
+ if (VM::copyinstr(myproc()->pagetable, kargv.args[i], uarg_str_ptr, KernelArgv::MAX_ARG_LEN) < 0)
+ {
+ return -1;
+ }
+ }
+
+ return Exec::exec(path, kargv.args);
+}
+
+static uint64 sys_exit()
{
int n;
if (argint(0, &n) < 0)
- return -1;
+ return static_cast<uint64>(-1);
ProcManager::exit(n);
return 0;
}
-uint64 sys_fork()
+static uint64 sys_fork()
{
- return fork();
+ return static_cast<uint64>(fork());
}
-uint64 sys_wait()
+static uint64 sys_wait()
{
- uint64 p;
- p = argraw(0);
- return ProcManager::wait(p);
+ uint64 p = argraw(0);
+ return static_cast<uint64>(ProcManager::wait(p));
}
-uint64 sys_getpid()
+static uint64 sys_getpid()
{
- return myproc()->pid;
+ return static_cast<uint64>(myproc()->pid);
}
-uint64 sys_putc()
+static uint64 sys_putc()
{
- char c = (char)argraw(0);
+ char c = static_cast<char>(argraw(0));
Drivers::uart_putc(c);
return 0;
}
-uint64 sys_write()
+static uint64 sys_write()
{
- int fd;
- uint64 p;
+ struct file *f;
int n;
+ uint64 p;
+ int fd;
- if (argint(0, &fd) < 0 || argint(2, &n) < 0 || argraw(1) == 0)
+ if (argint(0, &fd) < 0 || argint(2, &n) < 0 || argint(1, (int *)&p) < 0)
return -1;
+
+ struct Proc *proc = myproc();
+
p = argraw(1);
- if (fd == 1)
+ // Drivers::uart_puts("DEBUG: sys_write fd=");
+ // Drivers::uart_put_int(fd);
+ // Drivers::uart_puts(" len=");
+ // Drivers::uart_put_int(n);
+ // Drivers::uart_puts("\n");
+
+ if (fd == 1 && proc->ofile[fd] == nullptr) // stdout
{
- struct Proc *proc = myproc();
- const int max = 128;
- char buf[max];
+ constexpr int MAX_WRITE_BUF = 128;
+ char buf[MAX_WRITE_BUF];
int i = 0;
while (i < n)
{
int len = n - i;
- if (len > max)
- len = max;
+ if (len > MAX_WRITE_BUF)
+ len = MAX_WRITE_BUF;
- if (VM::copyin(proc->pagetable, buf, p + i, len) < 0)
+ if (VM::copyin(proc->pagetable, buf, p + i, static_cast<uint64>(len)) < 0)
{
Drivers::uart_puts("sys_write: copyin failed\n");
- return -1;
+ return static_cast<uint64>(-1);
}
for (int j = 0; j < len; j++)
@@ -109,25 +282,111 @@ uint64 sys_write()
}
i += len;
}
- return n;
+ return static_cast<uint64>(n);
+ }
+ 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()
+{
+ struct file *f;
+ int n;
+ uint64 p;
+ int fd;
+
+ // get param: fd, buf, len
+ if (argint(0, &fd) < 0 || argint(2, &n) < 0 || argint(1, (int *)&p) < 0)
+ return -1;
+
+ 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;
}
-void syscall()
+static uint64 sys_sbrk()
+{
+ int n;
+ if (argint(0, &n) < 0)
+ return -1;
+
+ uint64 addr = myproc()->sz;
+ if (ProcManager::growproc(n) < 0)
+ return -1;
+
+ return addr;
+}
+
+static uint64 sys_disk_test()
{
+ // VirtIO::test_rw();
+ // VirtIO::test_bio();
+ fat32_test();
+ return 0;
+}
+
+static uint64 sys_lseek()
+{
+ int fd, offset, whence;
+ if (argint(0, &fd) < 0 || argint(1, &offset) < 0 || argint(2, &whence) < 0)
+ return -1;
+
struct Proc *p = myproc();
+ if (fd < 0 || fd >= NOFILE || p->ofile[fd] == 0)
+ return -1;
+
+ return FileTable::lseek(p->ofile[fd], offset, whence);
+}
+
+void syscall()
+{
+ Proc *p = myproc();
int num = p->tf->a7;
- uint64 ret = -1;
+ uint64 ret = static_cast<uint64>(-1);
+
+ /* Just For Debug */
+ // Drivers::uart_puts("Syscall: ");
+ // Drivers::print_hex(num);
+ // Drivers::uart_puts("\n");
switch (num)
{
+ case SYS_open:
+ ret = sys_open();
+ break;
+ case SYS_close:
+ ret = sys_close();
+ break;
+ case SYS_write:
+ ret = sys_write();
+ break;
+ case SYS_read:
+ ret = sys_read();
+ break;
case SYS_putc:
ret = sys_putc();
break;
case SYS_fork:
ret = sys_fork();
break;
+ case SYS_exec:
+ ret = sys_exec();
+ if (ret != static_cast<uint64>(-1))
+ {
+ return;
+ }
+ break;
case SYS_exit:
ret = sys_exit();
break;
@@ -137,14 +396,31 @@ void syscall()
case SYS_getpid:
ret = sys_getpid();
break;
- case SYS_write:
- ret = sys_write();
+ case SYS_sbrk:
+ ret = sys_sbrk();
+ break;
+ case SYS_disk_test:
+ ret = sys_disk_test();
+ break;
+ case SYS_lseek:
+ ret = sys_lseek();
+ break;
+ case SYS_pipe:
+ case SYS_dup:
+ case SYS_chdir:
+ case SYS_fstat:
+ case SYS_mkdir:
+ case SYS_mknod:
+ case SYS_unlink:
+ case SYS_link:
+ Drivers::uart_puts("Unimplemented syscall\n");
+ ret = static_cast<uint64>(-1);
break;
default:
Drivers::uart_puts("Unknown Syscall ID: ");
Drivers::print_hex(num);
Drivers::uart_puts("\n");
- ret = -1;
+ ret = static_cast<uint64>(-1);
break;
}
diff --git a/src/kernel/trap/handler.cc b/src/kernel/trap/handler.cc
index 14ef4c5..3fb772e 100644
--- a/src/kernel/trap/handler.cc
+++ b/src/kernel/trap/handler.cc
@@ -1,13 +1,18 @@
#include "kernel/trap.h"
-#include "common/types.h"
-#include "drivers/uart.h"
+#include "kernel/riscv.h"
#include "kernel/proc.h"
-#include "kernel/timer.h"
+#include "drivers/uart.h"
#include "kernel/syscall.h"
-#include "kernel/riscv.h"
+#include "kernel/mm.h"
+#include "kernel/timer.h"
+#include "drivers/plic.h"
+#include "drivers/virtio.h"
+#include "lib/string.h"
+
+
+extern "C" void kernelvec();
-extern void usertrapret();
-extern void forkret();
+// Handle Function
void panic(const char *s)
{
@@ -18,160 +23,183 @@ void panic(const char *s)
;
}
+// Trap Implementation
+
namespace Trap
{
void init()
{
- uint64 stvec_val = (uint64)kernelvec;
- asm volatile("csrw stvec, %0" : : "r"(stvec_val));
+ Drivers::uart_puts("[Trap] Global init done.\n");
+ }
- uint64 sstatus;
- asm volatile("csrr %0, sstatus" : "=r"(sstatus));
- sstatus &= ~(1L << 1);
- asm volatile("csrw sstatus, %0" : : "r"(sstatus));
+ // Each CPU core needs to call this function when starting to set its own stvec
+ void inithart()
+ {
+ w_stvec((uint64)kernelvec);
- Drivers::uart_puts("[Trap] Initialized stvec to: ");
- Drivers::print_hex(stvec_val);
- Drivers::uart_puts("\n");
+ // Ensure that the S-Mode interrupt enable bit (SIE) is cleared during initialization
+ // Interrupt enabling will be done by the scheduler when the first process runs
+ w_sstatus(r_sstatus() & ~SSTATUS_SIE);
+
+ // Enable the external interrupt, clock interrupt, and software interrupt bits in S-Mode (in SIE register)
+ uint64 sie = r_sie();
+ sie |= SIE_SEIE | SIE_STIE | SIE_SSIE;
+ w_sie(sie);
+
+ Drivers::uart_puts("[Trap] Hart initialized (stvec set).\n");
}
- extern "C" void kerneltrap(struct Trapframe *tf)
+ // Device Interrupt Check
+ int devintr()
{
- uint64 scause, stval, sepc;
+ uint64 scause = r_scause();
- asm volatile("csrr %0, scause" : "=r"(scause));
- asm volatile("csrr %0, stval" : "=r"(stval));
- asm volatile("csrr %0, sepc" : "=r"(sepc));
+ bool is_interrupt = (scause & 0x8000000000000000L) != 0;
+ uint64 code = scause & 0xff;
- if (scause & (1L << 63))
+ if (is_interrupt && code == 9)
{
- uint64 cause_code = scause & 0x7FFFFFFFFFFFFFFFL;
- switch (cause_code)
+ // Supervisor External Interrupt (IRQ 9)
+ int irq = PLIC::claim();
+
+ if (irq == 10)
{
- case 1:
- Drivers::uart_puts("[IRQ] Software Interrupt (Unexpected)\n");
- break;
-
- case 5:
- Drivers::uart_puts("[IRQ] Timer Interrupt\n");
- Timer::set_next_trigger();
- ProcManager::yield();
- break;
-
- case 9:
- Drivers::uart_puts("[IRQ] External Interrupt\n");
- break;
-
- default:
- Drivers::uart_puts("[IRQ] Unknown Interrupt: ");
- Drivers::print_hex(cause_code);
- Drivers::uart_puts("\n");
- break;
+ Drivers::uart_intr();
+ // Debug
+ // Drivers::uart_puts("[Interrupt] UART Input detected!\n");
}
- }
- else
- {
- switch (scause)
+ else if (irq >= 1 && irq <= 8)
+ {
+ VirtIO::intr();
+ // Debug
+ // Drivers::uart_puts("[Interrupt] VirtIO disk finished!\n");
+ }
+
+ if (irq > 0)
{
- case 8:
- Drivers::uart_puts("[Syscall] ecall from Kernel Mode? Panic.\n");
- panic("Kernel Ecall");
- break;
-
- case 12:
- case 13:
- case 15:
- Drivers::uart_puts("\n--- PAGE FAULT ---\n");
- Drivers::uart_puts("Cause Code: ");
- Drivers::print_hex(scause);
- Drivers::uart_puts("\n");
- Drivers::uart_puts("Fault Addr (stval): ");
- Drivers::print_hex(stval);
- Drivers::uart_puts("\n");
- Drivers::uart_puts("Instruction (sepc): ");
- Drivers::print_hex(sepc);
- Drivers::uart_puts("\n");
-
- if (stval == 0)
- {
- panic("Null Pointer Dereference!");
- }
- else if (stval < 0x80000000)
- {
- panic("Invalid Low Memory Access!");
- }
- else
- {
- panic("Segmentation Fault (Unmapped Area)");
- }
- break;
-
- case 2:
- Drivers::uart_puts("\n--- ILLEGAL INSTRUCTION ---\n");
- Drivers::uart_puts("Instruction (sepc): ");
- Drivers::print_hex(sepc);
- Drivers::uart_puts("\n");
- panic("CPU encountered an unknown opcode");
- break;
-
- default:
- Drivers::uart_puts("\n--- UNHANDLED EXCEPTION ---\n");
- Drivers::uart_puts("Cause: ");
- Drivers::print_hex(scause);
- Drivers::uart_puts("\n");
- Drivers::uart_puts("sepc: ");
- Drivers::print_hex(sepc);
- Drivers::uart_puts("\n");
- panic("System Halt");
- break;
+ PLIC::complete(irq);
}
+
+ return 1;
+ }
+ else if (is_interrupt && code == 5)
+ {
+ // Supervisor Timer Interrupt (IRQ 5)
+ Timer::set_next_trigger();
+ // w_sip(r_sip() & ~2);
+ return 2;
}
+
+ return 0;
+ }
+}
+
+// Exception Handling Entry
+
+extern "C" void kerneltrap(struct Trapframe *tf)
+{
+ uint64 sepc = r_sepc();
+ uint64 sstatus = r_sstatus();
+ uint64 scause = r_scause();
+
+ if ((sstatus & SSTATUS_SPP) == 0)
+ panic("kerneltrap: not from supervisor mode");
+
+ if (intr_get() != 0)
+ panic("kerneltrap: interrupts enabled");
+
+ if (Trap::devintr() == 0)
+ {
+ Drivers::uart_puts("\n--- KERNEL EXCEPTION ---\n");
+ Drivers::uart_puts("scause: ");
+ Drivers::print_hex(scause);
+ Drivers::uart_puts("\n");
+ Drivers::uart_puts("sepc: ");
+ Drivers::print_hex(sepc);
+ Drivers::uart_puts("\n");
+ Drivers::uart_puts("stval: ");
+ Drivers::print_hex(r_stval());
+ Drivers::uart_puts("\n");
+ panic("kerneltrap exception");
}
+
+ w_sepc(sepc);
+ w_sstatus(sstatus);
}
extern "C" void usertrap()
{
- asm volatile("csrw stvec, %0" : : "r"(Trap::kernelvec));
+ // Drivers::uart_puts("[Debug] Entered usertrap!\n");
+ int which_dev = 0;
- uint64 scause;
- asm volatile("csrr %0, scause" : "=r"(scause));
+ if ((r_sstatus() & SSTATUS_SPP) != 0)
+ panic("usertrap: not from user mode");
+
+ w_stvec((uint64)kernelvec);
struct Proc *p = myproc();
-
- extern struct Proc procs[];
if (!p)
- {
- for (int i = 0; i < 10; ++i)
- if (procs[i].state == RUNNING)
- p = &procs[i];
- }
+ panic("usertrap: no process");
+
+ p->tf->epc = r_sepc();
- uint64 sepc;
- asm volatile("csrr %0, sepc" : "=r"(sepc));
- p->tf->epc = sepc;
+ uint64 scause = r_scause();
+ uint64 stval = r_stval();
- if(scause == 8)
+ if (scause == 8) // Syscall
{
- intr_on();
+ if (p->state == ZOMBIE)
+ ProcManager::exit(-1);
p->tf->epc += 4;
-
+ intr_on();
syscall();
-
intr_off();
}
- else if ((scause & (1L << 63)) && (scause & 0xF) == 5)
+ else if (scause == 15) // Store Page Fault
{
- Drivers::uart_puts("\nTick\n");
- Timer::set_next_trigger();
- ProcManager::yield();
+ if (VM::handle_cow_fault(p->pagetable, PGROUNDDOWN(stval)) != 0)
+ {
+ uint64 pa = VM::walkaddr(p->pagetable, stval);
+
+ if (pa == 0)
+ {
+ Drivers::uart_puts("usertrap: Segmentation Fault (unmapped) at ");
+ }
+ else
+ {
+ Drivers::uart_puts("usertrap: Protection Fault (or OOM) at ");
+ }
+
+ Drivers::print_hex(stval);
+ Drivers::uart_puts("\n");
+ p->state = ZOMBIE;
+ ProcManager::exit(-1);
+ }
+ }
+ else if ((which_dev = Trap::devintr()) != 0)
+ {
+ if (which_dev == 2)
+ {
+ ProcManager::yield();
+ }
}
else
{
- Drivers::uart_puts("Unexpected User Trap!\n");
+ Drivers::uart_puts("\nusertrap(): unexpected scause ");
Drivers::print_hex(scause);
- while (1)
- ;
+ Drivers::uart_puts(" pid=");
+ Drivers::uart_put_int(p->pid);
+ Drivers::uart_puts(" sepc=");
+ Drivers::print_hex(p->tf->epc);
+ Drivers::uart_puts(" stval=");
+ Drivers::print_hex(stval);
+ Drivers::uart_puts("\n");
+ p->state = ZOMBIE;
+ ProcManager::exit(-1);
}
+ if (p->state == ZOMBIE)
+ ProcManager::exit(-1);
+
usertrapret();
} \ No newline at end of file
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/ulib/assert.c b/src/ulib/assert.c
new file mode 100644
index 0000000..ca2da84
--- /dev/null
+++ b/src/ulib/assert.c
@@ -0,0 +1,8 @@
+#include "stdio.h"
+#include "stdlib.h"
+
+void __assert_fail(const char *expr, const char *file, int line, const char *func)
+{
+ fprintf(stderr, "Assertion failed: %s (%s: %s: %d)\n", expr, file, func, line);
+ abort();
+} \ No newline at end of file
diff --git a/src/ulib/cxx.cc b/src/ulib/cxx.cc
new file mode 100644
index 0000000..6c892ab
--- /dev/null
+++ b/src/ulib/cxx.cc
@@ -0,0 +1,31 @@
+#include "ulib/stdlib.h"
+
+void *operator new(unsigned long n)
+{
+ return malloc(n);
+}
+
+void *operator new[](unsigned long n)
+{
+ return malloc(n);
+}
+
+void operator delete(void *p)
+{
+ free(p);
+}
+
+void operator delete[](void *p)
+{
+ free(p);
+}
+
+void operator delete(void *p, unsigned long)
+{
+ free(p);
+}
+
+void operator delete[](void *p, unsigned long)
+{
+ free(p);
+} \ No newline at end of file
diff --git a/src/ulib/iostream.cpp b/src/ulib/iostream.cpp
new file mode 100644
index 0000000..d5394f9
--- /dev/null
+++ b/src/ulib/iostream.cpp
@@ -0,0 +1,8 @@
+#include "ulib/iostream"
+
+namespace std
+{
+ istream cin(stdin);
+ ostream cout(stdout);
+ ostream cerr(stderr);
+} // namespace std
diff --git a/src/ulib/stdio.c b/src/ulib/stdio.c
new file mode 100644
index 0000000..f8e7a9a
--- /dev/null
+++ b/src/ulib/stdio.c
@@ -0,0 +1,1052 @@
+#include "ulib/stdio.h"
+#include "ulib/string.h"
+#include "ulib/stdlib.h"
+
+/* Include STB Sprintf Implementation */
+#define STB_SPRINTF_IMPLEMENTATION
+#include "ulib/stb_sprintf.h"
+
+const int STDIN_FD = 0;
+const int STDOUT_FD = 1;
+const int STDERR_FD = 2;
+
+
+
+extern int open(const char *path, int flags);
+extern int close(int fd);
+extern int read(int fd, void *buf, uint64 count);
+extern int write(int fd, const void *buf, uint64 count);
+extern int lseek(int fd, int offset, int whence);
+
+static int isspace(int c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
+}
+
+/* Core Formatting Implementation */
+
+static int _ul_to_str_invert(char *buf, uint64 val, int base, int uppercase)
+{
+ int i = 0;
+ const char *digits = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
+ if (val == 0)
+ {
+ buf[i++] = '0';
+ return i;
+ }
+ while (val != 0)
+ {
+ buf[i++] = digits[val % base];
+ val /= base;
+ }
+ return i;
+}
+
+typedef struct
+{
+ char *buf;
+ uint64 limit;
+ uint64 written;
+ FILE *stream;
+} _OutContext;
+
+static void _out_char(_OutContext *ctx, char c)
+{
+ if (ctx->stream)
+ {
+ fputc(c, ctx->stream);
+ ctx->written++;
+ }
+ else
+ {
+ if (ctx->limit > 0 && ctx->written < ctx->limit - 1)
+ {
+ ctx->buf[ctx->written] = c;
+ }
+ ctx->written++;
+ }
+}
+
+static int _vprintf_core(_OutContext *ctx, const char *format, va_list ap)
+{
+ const char *p = format;
+ char num_buf[32];
+
+ while (*p)
+ {
+ if (*p != '%')
+ {
+ _out_char(ctx, *p++);
+ continue;
+ }
+ p++;
+
+ int flag_left = 0, flag_zero = 0, flag_plus = 0, flag_space = 0, flag_hash = 0;
+ while (1)
+ {
+ if (*p == '-')
+ flag_left = 1;
+ else if (*p == '0')
+ flag_zero = 1;
+ else if (*p == '+')
+ flag_plus = 1;
+ else if (*p == ' ')
+ flag_space = 1;
+ else if (*p == '#')
+ flag_hash = 1;
+ else
+ break;
+ p++;
+ }
+
+ int width = 0;
+ if (*p == '*')
+ {
+ width = va_arg(ap, int);
+ if (width < 0)
+ {
+ flag_left = 1;
+ width = -width;
+ }
+ p++;
+ }
+ else
+ {
+ while (*p >= '0' && *p <= '9')
+ {
+ width = width * 10 + (*p - '0');
+ p++;
+ }
+ }
+
+ int precision = -1;
+ if (*p == '.')
+ {
+ p++;
+ if (*p == '*')
+ {
+ precision = va_arg(ap, int);
+ p++;
+ }
+ else
+ {
+ precision = 0;
+ while (*p >= '0' && *p <= '9')
+ {
+ precision = precision * 10 + (*p - '0');
+ p++;
+ }
+ }
+ }
+
+ int len_mod = 0;
+ if (*p == 'l')
+ {
+ len_mod = 1;
+ p++;
+ if (*p == 'l')
+ {
+ len_mod = 2;
+ p++;
+ }
+ }
+ else if (*p == 'z')
+ {
+ len_mod = 2;
+ p++;
+ }
+
+ char spec = *p++;
+ if (!spec)
+ break;
+
+ if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' || spec == 'p')
+ {
+ uint64 val;
+ int is_signed = (spec == 'd' || spec == 'i');
+ int base = (spec == 'x' || spec == 'X' || spec == 'p') ? 16 : 10;
+
+ if (spec == 'p')
+ val = (uint64)va_arg(ap, void *);
+ else if (len_mod == 2)
+ val = is_signed ? (uint64)va_arg(ap, int64) : va_arg(ap, uint64);
+ else if (len_mod == 1)
+ val = is_signed ? (uint64)va_arg(ap, long) : (uint64)va_arg(ap, unsigned long);
+ else
+ val = is_signed ? (uint64)((int64)va_arg(ap, int)) : (uint64)va_arg(ap, uint32);
+
+ char sign_char = 0;
+ if (is_signed)
+ {
+ int64 sval = (int64)val;
+ if (sval < 0)
+ {
+ sign_char = '-';
+ val = (uint64)(-sval);
+ }
+ else if (flag_plus)
+ sign_char = '+';
+ else if (flag_space)
+ sign_char = ' ';
+ }
+
+ int digit_len = _ul_to_str_invert(num_buf, val, base, (spec == 'X'));
+ if (precision >= 0)
+ flag_zero = 0;
+ int prec_zeros = (precision > digit_len) ? precision - digit_len : 0;
+ if (precision == 0 && val == 0)
+ digit_len = 0;
+
+ int prefix_len = sign_char ? 1 : 0;
+ char *prefix_str = NULL;
+ if (spec == 'p' || (flag_hash && val != 0 && (spec == 'x' || spec == 'X')))
+ {
+ prefix_str = (spec == 'X') ? "0X" : "0x";
+ prefix_len += 2;
+ }
+
+ int padding = (width > prefix_len + prec_zeros + digit_len) ? width - (prefix_len + prec_zeros + digit_len) : 0;
+
+ if (!flag_left && !flag_zero)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ if (sign_char)
+ _out_char(ctx, sign_char);
+ if (prefix_str)
+ {
+ _out_char(ctx, prefix_str[0]);
+ _out_char(ctx, prefix_str[1]);
+ }
+ int total_zeros = prec_zeros + ((flag_zero && !flag_left) ? padding : 0);
+ for (int i = 0; i < total_zeros; i++)
+ _out_char(ctx, '0');
+ for (int i = digit_len - 1; i >= 0; i--)
+ _out_char(ctx, num_buf[i]);
+ if (flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ }
+ else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' || spec == 'g' || spec == 'G')
+ {
+ char fmt_tmp[32];
+ int f_idx = 0;
+ fmt_tmp[f_idx++] = '%';
+ if (flag_left)
+ fmt_tmp[f_idx++] = '-';
+ if (flag_zero)
+ fmt_tmp[f_idx++] = '0';
+ if (flag_plus)
+ fmt_tmp[f_idx++] = '+';
+ if (flag_space)
+ fmt_tmp[f_idx++] = ' ';
+ if (flag_hash)
+ fmt_tmp[f_idx++] = '#';
+ if (width > 0)
+ f_idx += sprintf(&fmt_tmp[f_idx], "%d", width);
+ if (precision >= 0)
+ f_idx += sprintf(&fmt_tmp[f_idx], ".%d", precision);
+ fmt_tmp[f_idx++] = spec;
+ fmt_tmp[f_idx] = '\0';
+ double f_val = va_arg(ap, double);
+ char f_buf[128];
+ stbsp_snprintf(f_buf, sizeof(f_buf), fmt_tmp, f_val);
+ for (int i = 0; f_buf[i] != '\0'; i++)
+ _out_char(ctx, f_buf[i]);
+ }
+ else if (spec == 's')
+ {
+ const char *s = va_arg(ap, char *);
+ if (!s)
+ s = "(null)";
+ int len = 0;
+ const char *tmp = s;
+ while (*tmp && (precision < 0 || len < precision))
+ {
+ tmp++;
+ len++;
+ }
+ int padding = (width > len) ? width - len : 0;
+ if (!flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ for (int i = 0; i < len; i++)
+ _out_char(ctx, s[i]);
+ if (flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ }
+ else if (spec == 'c')
+ {
+ char c = (char)va_arg(ap, int);
+ int padding = (width > 1) ? width - 1 : 0;
+ if (!flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ _out_char(ctx, c);
+ if (flag_left)
+ for (int i = 0; i < padding; i++)
+ _out_char(ctx, ' ');
+ }
+ else
+ {
+ _out_char(ctx, '%');
+ if (spec != '%')
+ _out_char(ctx, spec);
+ }
+ }
+ return (int)ctx->written;
+}
+
+int vsnprintf(char *str, uint64 size, const char *format, va_list ap)
+{
+ _OutContext ctx;
+ ctx.buf = str;
+ ctx.limit = size;
+ ctx.written = 0;
+ ctx.stream = NULL;
+ _vprintf_core(&ctx, format, ap);
+ if (size > 0)
+ {
+ if (ctx.written < size)
+ str[ctx.written] = '\0';
+ else
+ str[size - 1] = '\0';
+ }
+ return (int)ctx.written;
+}
+int snprintf(char *str, uint64 size, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return ret;
+}
+int sprintf(char *str, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int ret = vsnprintf(str, (uint64)-1, format, ap);
+ va_end(ap);
+ return ret;
+}
+int printf(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _OutContext ctx;
+ ctx.buf = NULL;
+ ctx.limit = 0;
+ ctx.written = 0;
+ ctx.stream = stdout;
+ _vprintf_core(&ctx, format, ap);
+ va_end(ap);
+ return (int)ctx.written;
+}
+
+/* Core Function Implementation */
+
+typedef struct
+{
+ const char *buf;
+ int fd;
+ uint64 pos;
+ int ungotten;
+} _InContext;
+
+static int _in_char(_InContext *ctx)
+{
+ if (ctx->ungotten >= 0)
+ {
+ int c = ctx->ungotten;
+ ctx->ungotten = -1;
+ return c;
+ }
+ if (ctx->fd == -1)
+ {
+ if (ctx->buf[ctx->pos] == '\0')
+ return -1;
+ return (unsigned char)ctx->buf[ctx->pos++];
+ }
+ char c;
+ if (read(ctx->fd, &c, 1) == 1)
+ return (unsigned char)c;
+ return -1;
+}
+
+static void _unget_char(_InContext *ctx, int c)
+{
+ if (c != -1)
+ ctx->ungotten = c;
+}
+static void _skip_space(_InContext *ctx)
+{
+ int c;
+ while ((c = _in_char(ctx)) != -1)
+ {
+ if (!isspace(c))
+ {
+ _unget_char(ctx, c);
+ break;
+ }
+ }
+}
+
+static double _simple_strtod(const char *str, char **endptr)
+{
+ double val = 0.0;
+ int sign = 1;
+ if (*str == '-')
+ {
+ sign = -1;
+ str++;
+ }
+ else if (*str == '+')
+ str++;
+ while (*str >= '0' && *str <= '9')
+ {
+ val = val * 10.0 + (*str - '0');
+ str++;
+ }
+ if (*str == '.')
+ {
+ str++;
+ double div = 10.0;
+ while (*str >= '0' && *str <= '9')
+ {
+ val += (*str - '0') / div;
+ div *= 10.0;
+ str++;
+ }
+ }
+ if (*str == 'e' || *str == 'E')
+ {
+ str++;
+ int esign = 1;
+ if (*str == '-')
+ {
+ esign = -1;
+ str++;
+ }
+ else if (*str == '+')
+ str++;
+ int eval = 0;
+ while (*str >= '0' && *str <= '9')
+ {
+ eval = eval * 10 + (*str - '0');
+ str++;
+ }
+ double p = 1.0;
+ while (eval--)
+ p *= 10.0;
+ if (esign == 1)
+ val *= p;
+ else
+ val /= p;
+ }
+ if (endptr)
+ *endptr = (char *)str;
+ return sign * val;
+}
+
+static int _vscanf_core(_InContext *ctx, const char *format, va_list ap)
+{
+ int assigned = 0;
+ const char *p = format;
+ while (*p)
+ {
+ if (isspace(*p))
+ {
+ _skip_space(ctx);
+ while (isspace(*p))
+ p++;
+ continue;
+ }
+ if (*p != '%')
+ {
+ _skip_space(ctx);
+ int c = _in_char(ctx);
+ if (c != *p)
+ {
+ _unget_char(ctx, c);
+ return assigned;
+ }
+ p++;
+ continue;
+ }
+ p++;
+ int suppress = 0;
+ if (*p == '*')
+ {
+ suppress = 1;
+ p++;
+ }
+ int width = -1;
+ while (*p >= '0' && *p <= '9')
+ {
+ if (width == -1)
+ width = 0;
+ width = width * 10 + (*p - '0');
+ p++;
+ }
+ int len_mod = 0;
+ if (*p == 'l')
+ {
+ len_mod = 1;
+ p++;
+ if (*p == 'l')
+ {
+ len_mod = 2;
+ p++;
+ }
+ }
+ else if (*p == 'h')
+ {
+ p++;
+ if (*p == 'h')
+ p++;
+ }
+ char spec = *p++;
+ if (!spec)
+ break;
+
+ if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x')
+ {
+ _skip_space(ctx);
+ char num_buf[64];
+ int i = 0, c = _in_char(ctx);
+ if (c == '-' || c == '+')
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ int base = (spec == 'x') ? 16 : (spec == 'i' ? 0 : 10);
+ while (c != -1 && i < 63)
+ {
+ int digit = (c >= '0' && c <= '9');
+ int hex = (base == 16 || base == 0) && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+ int x = (c == 'x' || c == 'X');
+ if (base == 0 && i == 1 && num_buf[0] == '0' && x)
+ base = 16;
+ if (width > 0 && i >= width)
+ break;
+ if (digit || hex || x)
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ else
+ break;
+ }
+ num_buf[i] = '\0';
+ _unget_char(ctx, c);
+ if (i == 0)
+ return assigned;
+ if (!suppress)
+ {
+ int64 val = strtoll(num_buf, NULL, base);
+ if (len_mod == 2)
+ *va_arg(ap, int64 *) = val;
+ else if (len_mod == 1)
+ *va_arg(ap, long *) = (long)val;
+ else
+ *va_arg(ap, int *) = (int)val;
+ assigned++;
+ }
+ }
+ else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' || spec == 'g' || spec == 'G')
+ {
+ _skip_space(ctx);
+ char num_buf[64];
+ int i = 0, c = _in_char(ctx);
+ if (c == '-' || c == '+')
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ while (c != -1 && i < 63)
+ {
+ if ((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-')
+ {
+ num_buf[i++] = (char)c;
+ c = _in_char(ctx);
+ }
+ else
+ break;
+ }
+ num_buf[i] = '\0';
+ _unget_char(ctx, c);
+ if (i == 0)
+ return assigned;
+ if (!suppress)
+ {
+ double val = _simple_strtod(num_buf, NULL);
+ if (len_mod == 1)
+ *va_arg(ap, double *) = val;
+ else
+ *va_arg(ap, float *) = (float)val;
+ assigned++;
+ }
+ }
+ else if (spec == 's')
+ {
+ _skip_space(ctx);
+ char *buf = suppress ? NULL : va_arg(ap, char *);
+ int i = 0, c = _in_char(ctx);
+ while (c != -1 && !isspace(c))
+ {
+ if (width > 0 && i >= width)
+ break;
+ if (!suppress)
+ buf[i] = (char)c;
+ i++;
+ c = _in_char(ctx);
+ }
+ if (!suppress)
+ buf[i] = '\0';
+ _unget_char(ctx, c);
+ if (i > 0 && !suppress)
+ assigned++;
+ }
+ else if (spec == 'c')
+ {
+ int count = (width > 0) ? width : 1;
+ char *buf = suppress ? NULL : va_arg(ap, char *);
+ for (int i = 0; i < count; i++)
+ {
+ int c = _in_char(ctx);
+ if (c == -1)
+ return assigned;
+ if (!suppress)
+ buf[i] = (char)c;
+ }
+ if (!suppress)
+ assigned++;
+ }
+ }
+ return assigned;
+}
+
+int sscanf(const char *str, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _InContext ctx;
+ ctx.buf = str;
+ ctx.fd = -1;
+ ctx.pos = 0;
+ ctx.ungotten = -1;
+ int ret = _vscanf_core(&ctx, format, ap);
+ va_end(ap);
+ return ret;
+}
+int scanf(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _InContext ctx;
+ ctx.buf = NULL;
+ ctx.fd = STDIN_FD;
+ ctx.pos = 0;
+ ctx.ungotten = -1;
+ int ret = _vscanf_core(&ctx, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+/* FILE Stream Implementation */
+
+#define O_RDONLY 00
+#define O_WRONLY 01
+#define O_RDWR 02
+#define O_CREAT 0100
+#define O_TRUNC 01000
+#define O_APPEND 02000
+
+
+static FILE _stdin = {.fd = 0, .buf_mode = 1};
+static FILE _stdout = {.fd = 1, .buf_mode = 1};
+static FILE _stderr = {.fd = 2, .buf_mode = 2};
+
+FILE *stdin = &_stdin;
+FILE *stdout = &_stdout;
+FILE *stderr = &_stderr;
+
+FILE *fopen(const char *filename, const char *mode)
+{
+ int flags = 0;
+ if (strcmp(mode, "r") == 0)
+ flags = O_RDONLY;
+ else if (strcmp(mode, "w") == 0)
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+ else if (strcmp(mode, "a") == 0)
+ flags = O_WRONLY | O_CREAT | O_APPEND;
+ else if (strcmp(mode, "r+") == 0)
+ flags = O_RDWR;
+ else
+ return NULL;
+
+ int64 fd = open(filename, flags);
+ if (fd < 0)
+ return NULL;
+
+ FILE *f = (FILE *)malloc(sizeof(FILE));
+ if (!f)
+ {
+ close(fd);
+ return NULL;
+ }
+
+ memset(f, 0, sizeof(FILE));
+ f->fd = (int)fd;
+ f->flags = flags;
+
+ if (fd == 1)
+ f->buf_mode = 1;
+ else if (fd == 2)
+ f->buf_mode = 2;
+ else
+ f->buf_mode = 0;
+ return f;
+}
+
+int fclose(FILE *stream)
+{
+ if (!stream)
+ return -1;
+ fflush(stream);
+ if (stream != stdin && stream != stdout && stream != stderr)
+ {
+ close(stream->fd);
+ free(stream);
+ }
+ return 0;
+}
+
+int fflush(FILE *stream)
+{
+ if (!stream)
+ return EOF;
+
+ if (stream->buf_write_pos > 0)
+ {
+ int64 ret = write(stream->fd, stream->buffer, stream->buf_write_pos);
+ if (ret < stream->buf_write_pos)
+ {
+ stream->error = 1;
+ stream->buf_write_pos = 0;
+ return EOF;
+ }
+ stream->buf_write_pos = 0;
+ }
+ return 0;
+}
+
+int fprintf(FILE *stream, const char *format, ...)
+{
+ if (!stream)
+ return -1;
+ va_list ap;
+ va_start(ap, format);
+ _OutContext ctx;
+ ctx.buf = NULL;
+ ctx.limit = 0;
+ ctx.written = 0;
+ ctx.stream = stream;
+ _vprintf_core(&ctx, format, ap);
+ va_end(ap);
+ return (int)ctx.written;
+}
+
+int fscanf(FILE *stream, const char *format, ...)
+{
+ if (!stream)
+ return -1;
+ va_list ap;
+ va_start(ap, format);
+ _InContext ctx;
+ ctx.buf = NULL;
+ ctx.fd = stream->fd;
+ ctx.pos = 0;
+ ctx.ungotten = -1;
+ int ret = _vscanf_core(&ctx, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+int fputc(int c, FILE *stream)
+{
+ if (!stream)
+ return EOF;
+ if (stream->buf_mode == 2)
+ {
+ char ch = (char)c;
+ if (write(stream->fd, &ch, 1) != 1)
+ {
+ stream->error = 1;
+ return EOF;
+ }
+ return c;
+ }
+
+ stream->buffer[stream->buf_write_pos++] = (char)c;
+
+ if (stream->buf_write_pos >= BUFSIZ)
+ {
+ fflush(stream);
+ }
+ else if (stream->buf_mode == 1 && c == '\n')
+ {
+ fflush(stream);
+ }
+
+ return c;
+}
+
+int fputs(const char *s, FILE *stream)
+{
+ if (!stream)
+ return -1;
+ while (*s)
+ {
+ if (fputc(*s++, stream) == EOF)
+ return EOF;
+ }
+ return 0;
+}
+
+int fgetc(FILE *stream)
+{
+ if (!stream)
+ return EOF;
+
+ if (stream->buf_write_pos > 0)
+ {
+ fflush(stream);
+ }
+
+ if (stream->buf_read_pos < stream->buf_read_cnt)
+ {
+ return (unsigned char)stream->buffer[stream->buf_read_pos++];
+ }
+
+ int64 ret = read(stream->fd, stream->buffer, BUFSIZ);
+ if (ret <= 0)
+ {
+ if (ret == 0)
+ stream->eof = 1;
+ else
+ stream->error = 1;
+ return EOF;
+ }
+
+ stream->buf_read_cnt = (int)ret;
+ stream->buf_read_pos = 0;
+ return (unsigned char)stream->buffer[stream->buf_read_pos++];
+}
+
+char *fgets(char *str, int n, FILE *stream)
+{
+ if (!stream || n <= 0)
+ return NULL;
+ int i = 0, c;
+ while (i < n - 1)
+ {
+ c = fgetc(stream);
+ if (c == -1)
+ {
+ if (i == 0)
+ return NULL;
+ break;
+ }
+ str[i++] = (char)c;
+ if (c == '\n')
+ break;
+ }
+ str[i] = '\0';
+ return str;
+}
+
+uint64 fread(void *ptr, uint64 size, uint64 nmemb, FILE *stream)
+{
+ if (!stream || size == 0 || nmemb == 0)
+ return 0;
+
+ uint64 total_bytes = size * nmemb;
+ uint64 bytes_read = 0;
+ char *dst = (char *)ptr;
+
+ if (stream->buf_write_pos > 0)
+ fflush(stream);
+
+ while (bytes_read < total_bytes)
+ {
+ if (stream->buf_read_pos < stream->buf_read_cnt)
+ {
+ uint64 available = stream->buf_read_cnt - stream->buf_read_pos;
+ uint64 remaining = total_bytes - bytes_read;
+ uint64 to_copy = (available < remaining) ? available : remaining;
+
+ memcpy(dst + bytes_read, stream->buffer + stream->buf_read_pos, to_copy);
+
+ stream->buf_read_pos += to_copy;
+ bytes_read += to_copy;
+ }
+ else
+ {
+ uint64 remaining = total_bytes - bytes_read;
+
+ if (remaining >= BUFSIZ)
+ {
+ int64 ret = read(stream->fd, dst + bytes_read, remaining);
+ if (ret <= 0)
+ {
+ if (ret == 0)
+ stream->eof = 1;
+ else
+ stream->error = 1;
+ return bytes_read / size;
+ }
+ bytes_read += ret;
+ }
+ else
+ {
+ stream->buf_read_pos = 0;
+ stream->buf_read_cnt = 0;
+ int64 ret = read(stream->fd, stream->buffer, BUFSIZ);
+
+ if (ret <= 0)
+ {
+ if (ret == 0)
+ stream->eof = 1;
+ else
+ stream->error = 1;
+ return bytes_read / size;
+ }
+ stream->buf_read_cnt = (int)ret;
+ }
+ }
+ }
+ return bytes_read / size;
+}
+
+uint64 fwrite(const void *ptr, uint64 size, uint64 nmemb, FILE *stream)
+{
+ if (!stream || !ptr || size == 0 || nmemb == 0)
+ return 0;
+ uint64 total_bytes = size * nmemb;
+ const char *src = (const char *)ptr;
+ uint64 bytes_written = 0;
+
+ if (stream->buf_mode == 2)
+ {
+ while (bytes_written < total_bytes)
+ {
+ int64 ret = write(stream->fd, src + bytes_written, total_bytes - bytes_written);
+ if (ret <= 0)
+ {
+ stream->error = 1;
+ break;
+ }
+ bytes_written += ret;
+ }
+ return bytes_written / size;
+ }
+
+ while (bytes_written < total_bytes)
+ {
+ uint64 space_left = BUFSIZ - stream->buf_write_pos;
+ uint64 remaining = total_bytes - bytes_written;
+
+ if (remaining <= space_left)
+ {
+ memcpy(stream->buffer + stream->buf_write_pos, src + bytes_written, remaining);
+ stream->buf_write_pos += remaining;
+ bytes_written += remaining;
+ if (stream->buf_write_pos == BUFSIZ)
+ fflush(stream);
+ }
+ else
+ {
+ memcpy(stream->buffer + stream->buf_write_pos, src + bytes_written, space_left);
+ stream->buf_write_pos += space_left;
+ bytes_written += space_left;
+ fflush(stream);
+ }
+ }
+
+ return bytes_written / size;
+}
+
+int fseek(FILE *stream, long offset, int whence)
+{
+ if (!stream)
+ return -1;
+
+ if (stream->buf_write_pos > 0)
+ {
+ fflush(stream);
+ }
+ stream->buf_read_pos = 0;
+ stream->buf_read_cnt = 0;
+ stream->eof = 0;
+
+ if (lseek(stream->fd, offset, whence) < 0)
+ {
+ stream->error = 1;
+ return -1;
+ }
+ return 0;
+}
+long ftell(FILE *stream)
+{
+ if (!stream)
+ return -1L;
+ int64 ret = lseek(stream->fd, 0, SEEK_CUR);
+ if (ret < 0)
+ {
+ stream->error = 1;
+ return -1L;
+ }
+ return (long)ret;
+}
+void rewind(FILE *stream)
+{
+ if (stream)
+ {
+ fseek(stream, 0, SEEK_SET);
+ stream->error = 0;
+ }
+}
+
+int feof(FILE *stream)
+{
+ return (stream) ? stream->eof : 1;
+}
+int ferror(FILE *stream)
+{
+ return (stream) ? stream->error : 1;
+}
+void clearerr(FILE *stream)
+{
+ if (stream)
+ {
+ stream->error = 0;
+ stream->eof = 0;
+ }
+}
+
+int ungetc(int c, FILE *stream)
+{
+ if (!stream || c == EOF)
+ return EOF;
+
+ if (stream->buf_read_pos > 0)
+ {
+ stream->buf_read_pos--;
+ stream->buffer[stream->buf_read_pos] = (char)c;
+ stream->eof = 0;
+ return c;
+ }
+ return EOF;
+} \ No newline at end of file
diff --git a/src/ulib/stdlib.c b/src/ulib/stdlib.c
new file mode 100644
index 0000000..89d5a08
--- /dev/null
+++ b/src/ulib/stdlib.c
@@ -0,0 +1,539 @@
+#include "ulib/stdlib.h"
+#include "ulib/string.h"
+#include "ulib/ctypes.h"
+
+extern int sys_exit(int status);
+extern void *sbrk(long increment);
+
+#undef errno
+int errno = 0;
+
+#define ATEXIT_MAX 32
+static void (*__atexit_funcs[ATEXIT_MAX])(void);
+static int __atexit_func_count = 0;
+
+int atexit(void (*function)(void))
+{
+ if (__atexit_func_count >= ATEXIT_MAX)
+ return -1;
+ __atexit_funcs[__atexit_func_count++] = function;
+ return 0;
+}
+
+int exit(int status)
+{
+ while (__atexit_func_count > 0)
+ {
+ (__atexit_funcs[--__atexit_func_count])();
+ }
+ sys_exit(status);
+
+ while (1)
+ ;
+ return 0;
+}
+
+void abort(void)
+{
+ exit(127);
+}
+
+char *getenv(const char *name)
+{
+ /* None */
+ return NULL;
+}
+
+int system(const char *command)
+{
+ /* None */
+ return -1;
+}
+
+/* Memory Management */
+#define ALIGNMENT 16
+#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))
+
+typedef struct header
+{
+ size_t size;
+ struct header *next;
+} header_t;
+
+static header_t base;
+static header_t *freep = NULL;
+
+
+#define LOCK()
+#define UNLOCK()
+
+void free(void *ptr)
+{
+ header_t *bp, *p;
+
+ if (!ptr)
+ return;
+
+
+ bp = (header_t *)ptr - 1;
+
+ LOCK();
+
+
+ for (p = freep; !(bp > p && bp < p->next); p = p->next)
+ {
+ if (p >= p->next && (bp > p || bp < p->next))
+ break;
+ }
+
+ if ((char *)bp + bp->size == (char *)p->next)
+ {
+ bp->size += p->next->size;
+ bp->next = p->next->next;
+ }
+ else
+ {
+ bp->next = p->next;
+ }
+
+ if ((char *)p + p->size == (char *)bp)
+ {
+ p->size += bp->size;
+ p->next = bp->next;
+ }
+ else
+ {
+ p->next = bp;
+ }
+
+ freep = p;
+ UNLOCK();
+}
+
+static header_t *morecore(size_t nu)
+{
+ char *cp;
+ header_t *up;
+
+ if (nu < 4096)
+ nu = 4096;
+
+ nu = ALIGN(nu);
+
+ cp = (char *)sbrk(nu);
+ if (cp == (void *)-1)
+ return NULL;
+
+ up = (header_t *)cp;
+ up->size = nu;
+
+ free((void *)(up + 1));
+ return freep;
+}
+
+void *malloc(size_t size)
+{
+ header_t *p, *prevp;
+
+ if (size == 0)
+ return NULL;
+
+ size = ALIGN(size + sizeof(header_t));
+
+ LOCK();
+
+ if ((prevp = freep) == NULL)
+ {
+ base.next = freep = prevp = &base;
+ base.size = 0;
+ }
+
+ for (p = prevp->next;; prevp = p, p = p->next)
+ {
+ if (p->size >= size)
+ {
+ if (p->size == size)
+ {
+ prevp->next = p->next;
+ }
+ else
+ {
+ p->size -= size;
+
+ p = (header_t *)((char *)p + p->size);
+
+ p->size = size;
+ }
+ freep = prevp;
+ UNLOCK();
+ return (void *)(p + 1);
+ }
+
+ if (p == freep)
+ {
+ if ((p = morecore(size)) == NULL)
+ {
+ UNLOCK();
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+ }
+}
+
+void *calloc(size_t nmemb, size_t size)
+{
+ size_t total;
+ if (__builtin_mul_overflow(nmemb, size, &total))
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ void *p = malloc(total);
+ if (p)
+ memset(p, 0, total);
+ return p;
+}
+
+void *realloc(void *ptr, size_t size)
+{
+ if (!ptr)
+ return malloc(size);
+ if (size == 0)
+ {
+ free(ptr);
+ return NULL;
+ }
+
+ header_t *bp = (header_t *)ptr - 1;
+ size_t new_size = ALIGN(size + sizeof(header_t));
+
+ if (bp->size >= new_size)
+ return ptr;
+
+ void *new_ptr = malloc(size);
+ if (!new_ptr)
+ return NULL;
+
+ memcpy(new_ptr, ptr, bp->size - sizeof(header_t));
+ free(ptr);
+ return new_ptr;
+}
+
+/* String Conversion*/
+
+unsigned long long strtoull(const char *nptr, char **endptr, int base)
+{
+ const char *s = nptr;
+ unsigned long long acc;
+ unsigned long long cutoff;
+ int c;
+ int neg = 0, any, cutlim;
+
+ if (base < 0 || base == 1 || base > 36)
+ {
+ if (endptr)
+ *endptr = (char *)nptr;
+ errno = 0; /* EINVAL */
+ return 0;
+ }
+
+ do
+ {
+ c = *s++;
+ } while (isspace(c));
+
+ if (c == '-')
+ {
+ neg = 1;
+ c = *s++;
+ }
+ else if (c == '+')
+ {
+ c = *s++;
+ }
+
+ if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X'))
+ {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = (c == '0' ? 8 : 10);
+
+ cutoff = ULLONG_MAX / (unsigned long long)base;
+ cutlim = ULLONG_MAX % (unsigned long long)base;
+
+ for (acc = 0, any = 0;; c = *s++)
+ {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+
+ if (c >= base)
+ break;
+
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else
+ {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+
+ if (any < 0)
+ {
+ acc = ULLONG_MAX;
+ errno = ERANGE;
+ }
+ else if (neg)
+ acc = -acc;
+
+ if (endptr != 0)
+ *endptr = (char *)(any ? s - 1 : nptr);
+
+ return acc;
+}
+
+long long strtoll(const char *nptr, char **endptr, int base)
+{
+ const char *s = nptr;
+ unsigned long long acc;
+ int c, neg = 0;
+
+ do
+ {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-')
+ neg = 1;
+
+ acc = strtoull(nptr, endptr, base);
+
+ if (errno == ERANGE && acc == ULLONG_MAX)
+ return neg ? LLONG_MIN : LLONG_MAX;
+
+ if (!neg && acc > (unsigned long long)LLONG_MAX)
+ {
+ errno = ERANGE;
+ return LLONG_MAX;
+ }
+ if (neg && acc > (unsigned long long)LLONG_MAX + 1)
+ {
+ errno = ERANGE;
+ return LLONG_MIN;
+ }
+
+ return neg ? -(long long)acc : (long long)acc;
+}
+
+unsigned long strtoul(const char *nptr, char **endptr, int base)
+{
+ unsigned long long ret = strtoull(nptr, endptr, base);
+ if (ret > ULONG_MAX)
+ {
+ errno = ERANGE;
+ return ULONG_MAX;
+ }
+ return (unsigned long)ret;
+}
+
+long strtol(const char *nptr, char **endptr, int base)
+{
+ long long ret = strtoll(nptr, endptr, base);
+ if (ret > LONG_MAX)
+ {
+ errno = ERANGE;
+ return LONG_MAX;
+ }
+ if (ret < LONG_MIN)
+ {
+ errno = ERANGE;
+ return LONG_MIN;
+ }
+ return (long)ret;
+}
+
+int atoi(const char *nptr) { return (int)strtol(nptr, NULL, 10); }
+long atol(const char *nptr) { return strtol(nptr, NULL, 10); }
+long long atoll(const char *nptr) { return strtoll(nptr, NULL, 10); }
+
+double strtod(const char *nptr, char **endptr)
+{
+ double val = 0.0;
+ int sign = 1;
+ const char *s = nptr;
+
+ while (isspace(*s))
+ s++;
+ if (*s == '-')
+ {
+ sign = -1;
+ s++;
+ }
+ else if (*s == '+')
+ s++;
+
+ while (isdigit(*s))
+ {
+ val = val * 10.0 + (*s - '0');
+ s++;
+ }
+ if (*s == '.')
+ {
+ double factor = 1.0;
+ s++;
+ while (isdigit(*s))
+ {
+ factor *= 0.1;
+ val += (*s - '0') * factor;
+ s++;
+ }
+ }
+
+ if (endptr)
+ *endptr = (char *)s;
+ return val * sign;
+}
+
+double atof(const char *nptr) { return strtod(nptr, NULL); }
+float strtof(const char *nptr, char **endptr) { return (float)strtod(nptr, endptr); }
+
+/* Sort & Search */
+
+static void swap_bytes(char *a, char *b, size_t size)
+{
+ char temp;
+ while (size--)
+ {
+ temp = *a;
+ *a++ = *b;
+ *b++ = temp;
+ }
+}
+
+void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+ if (nmemb < 2 || size == 0)
+ return;
+
+ char *base_ptr = (char *)base;
+ char *pivot = base_ptr + (nmemb - 1) * size;
+ char *i = base_ptr;
+ char *j = base_ptr;
+
+ /* Partition */
+ for (; j < pivot; j += size)
+ {
+ if (compar(j, pivot) < 0)
+ {
+ if (i != j)
+ swap_bytes(i, j, size);
+ i += size;
+ }
+ }
+ swap_bytes(i, pivot, size);
+
+ /* Recurse */
+ size_t left_count = (i - base_ptr) / size;
+ qsort(base, left_count, size, compar);
+ qsort(i + size, nmemb - left_count - 1, size, compar);
+}
+
+void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+ size_t l = 0, u = nmemb;
+ while (l < u)
+ {
+ size_t idx = (l + u) / 2;
+ const void *p = (void *)((const char *)base + (idx * size));
+ int cmp = compar(key, p);
+ if (cmp < 0)
+ u = idx;
+ else if (cmp > 0)
+ l = idx + 1;
+ else
+ return (void *)p;
+ }
+ return NULL;
+}
+
+/* Math & Mics */
+
+int abs(int j) { return (j < 0) ? -j : j; }
+long labs(long j) { return (j < 0) ? -j : j; }
+long long llabs(long long j) { return (j < 0) ? -j : j; }
+
+div_t div(int numer, int denom)
+{
+ div_t res = {numer / denom, numer % denom};
+ return res;
+}
+ldiv_t ldiv(long numer, long denom)
+{
+ ldiv_t res = {numer / denom, numer % denom};
+ return res;
+}
+lldiv_t lldiv(long long numer, long long denom)
+{
+ lldiv_t res = {numer / denom, numer % denom};
+ return res;
+}
+
+static unsigned long next = 1;
+int rand(void)
+{
+ next = next * 1103515245 + 12345;
+ return (unsigned int)(next / 65536) % 32768;
+}
+void srand(unsigned int seed) { next = seed; }
+
+/* Stub */
+int mblen(const char *s, size_t n) { return (s && *s && n) ? 1 : 0; }
+int mbtowc(wchar_t *pwc, const char *s, size_t n)
+{
+ if (!s)
+ return 0;
+ if (n == 0)
+ return -1;
+ if (pwc)
+ *pwc = (wchar_t)*s;
+ return (*s != 0);
+}
+int wctomb(char *s, wchar_t wchar)
+{
+ if (!s)
+ return 0;
+ *s = (char)wchar;
+ return 1;
+}
+size_t mbstowcs(wchar_t *dest, const char *src, size_t n)
+{
+ size_t i = 0;
+ while (src[i] && i < n)
+ {
+ dest[i] = (wchar_t)src[i];
+ i++;
+ }
+ if (i < n)
+ dest[i] = 0;
+ return i;
+}
+size_t wcstombs(char *dest, const wchar_t *src, size_t n)
+{
+ size_t i = 0;
+ while (src[i] && i < n)
+ {
+ dest[i] = (char)src[i];
+ i++;
+ }
+ if (i < n)
+ dest[i] = 0;
+ return i;
+} \ No newline at end of file
diff --git a/src/ulib/string.c b/src/ulib/string.c
new file mode 100644
index 0000000..5fc1cbd
--- /dev/null
+++ b/src/ulib/string.c
@@ -0,0 +1,331 @@
+#include "ulib/string.h"
+#include "ulib/stdlib.h"
+
+
+/* Memory Manipulation Functions */
+
+void *memset(void *dst, int c, size_t n)
+{
+ char *cdst = (char *)dst;
+ for (size_t i = 0; i < n; i++)
+ cdst[i] = (char)c;
+ return dst;
+}
+
+void *memmove(void *vdst, const void *vsrc, size_t n)
+{
+ char *dst = (char *)vdst;
+ const char *src = (const char *)vsrc;
+ if (dst == src)
+ return vdst;
+
+ if (src > dst)
+ {
+ while (n--)
+ *dst++ = *src++;
+ }
+ else
+ {
+ dst += n;
+ src += n;
+ while (n-- > 0)
+ *--dst = *--src;
+ }
+ return vdst;
+}
+
+void *memcpy(void *vdst, const void *vsrc, size_t n)
+{
+ return memmove(vdst, vsrc, n);
+}
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ const unsigned char *p1 = (const unsigned char *)s1;
+ const unsigned char *p2 = (const unsigned char *)s2;
+ while (n--)
+ {
+ if (*p1 != *p2)
+ return *p1 - *p2;
+ p1++;
+ p2++;
+ }
+ return 0;
+}
+
+void *memchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = (const unsigned char *)s;
+ while (n--)
+ {
+ if (*p == (unsigned char)c)
+ return (void *)p;
+ p++;
+ }
+ return NULL;
+}
+
+/* String Manipulation Functions */
+
+size_t strlen(const char *s)
+{
+ const char *p = s;
+ while (*p)
+ p++;
+ return (size_t)(p - s);
+}
+
+char *strcpy(char *dst, const char *src)
+{
+ char *os = dst;
+ while ((*dst++ = *src++) != 0)
+ ;
+ return os;
+}
+
+char *strncpy(char *dst, const char *src, size_t n)
+{
+ char *os = dst;
+ size_t i;
+
+ for (i = 0; i < n && src[i] != '\0'; i++)
+ dst[i] = src[i];
+
+ for (; i < n; i++)
+ dst[i] = '\0';
+
+ return os;
+}
+
+char *strcat(char *dst, const char *src)
+{
+ char *os = dst;
+ while (*dst)
+ dst++;
+ while ((*dst++ = *src++) != 0)
+ ;
+ return os;
+}
+
+char *strncat(char *dst, const char *src, size_t n)
+{
+ char *os = dst;
+ size_t dest_len = strlen(dst);
+ size_t i;
+
+ dst += dest_len;
+
+ for (i = 0; i < n && src[i] != '\0'; i++)
+ dst[i] = src[i];
+
+ dst[i] = '\0';
+
+ return os;
+}
+
+int strcmp(const char *p, const char *q)
+{
+ while (*p && *p == *q)
+ {
+ p++;
+ q++;
+ }
+ return (int)((unsigned char)*p - (unsigned char)*q);
+}
+
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+ if (n == 0)
+ return 0;
+
+ while (n-- > 0 && *s1 && *s1 == *s2)
+ {
+ if (n == 0 || *s1 == '\0')
+ break;
+ s1++;
+ s2++;
+ }
+ return (int)((unsigned char)*s1 - (unsigned char)*s2);
+}
+
+char *strchr(const char *s, int c)
+{
+ while (*s != (char)c)
+ {
+ if (!*s++)
+ return NULL;
+ }
+ return (char *)s;
+}
+
+char *strrchr(const char *s, int c)
+{
+ const char *last = NULL;
+ do
+ {
+ if (*s == (char)c)
+ last = s;
+ } while (*s++);
+
+ return (char *)last;
+}
+
+
+char *strstr(const char *haystack, const char *needle)
+{
+ size_t n_len = strlen(needle);
+ if (n_len == 0)
+ return (char *)haystack;
+
+ for (; *haystack; haystack++)
+ {
+ if (*haystack != *needle)
+ continue;
+
+ if (strncmp(haystack, needle, n_len) == 0)
+ return (char *)haystack;
+ }
+ return NULL;
+}
+
+char *strdup(const char *s)
+{
+ size_t len = strlen(s) + 1;
+ char *new_str = (char *)malloc(len);
+ if (new_str == NULL)
+ return NULL;
+
+ return (char *)memcpy(new_str, s, len);
+}
+
+size_t strlcpy(char *dst, const char *src, size_t size)
+{
+ size_t src_len = strlen(src);
+
+ if (size > 0)
+ {
+ size_t copy_len = (src_len >= size) ? size - 1 : src_len;
+ memcpy(dst, src, copy_len);
+ dst[copy_len] = '\0';
+ }
+
+ return src_len;
+}
+
+size_t strlcat(char *dst, const char *src, size_t size)
+{
+ size_t dst_len = 0;
+ size_t src_len = strlen(src);
+
+ while (dst_len < size && dst[dst_len] != '\0')
+ dst_len++;
+
+ if (dst_len == size)
+ return size + src_len;
+
+ size_t space_left = size - dst_len;
+ size_t copy_len = (src_len >= space_left) ? space_left - 1 : src_len;
+
+ memcpy(dst + dst_len, src, copy_len);
+ dst[dst_len + copy_len] = '\0';
+
+ return dst_len + src_len;
+}
+
+
+size_t strspn(const char *s, const char *accept)
+{
+ const char *p = s;
+ const char *a;
+
+ while (*p)
+ {
+ for (a = accept; *a; a++)
+ {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return (size_t)(p - s);
+ p++;
+ }
+ return (size_t)(p - s);
+}
+
+size_t strcspn(const char *s, const char *reject)
+{
+ const char *p = s;
+ const char *r;
+
+ while (*p)
+ {
+ for (r = reject; *r; r++)
+ {
+ if (*p == *r)
+ return (size_t)(p - s);
+ }
+ p++;
+ }
+ return (size_t)(p - s);
+}
+
+char *strpbrk(const char *s, const char *accept)
+{
+ const char *a;
+ while (*s)
+ {
+ for (a = accept; *a; a++)
+ {
+ if (*s == *a)
+ return (char *)s;
+ }
+ s++;
+ }
+ return NULL;
+}
+
+char *strtok(char *str, const char *delim)
+{
+ static char *olds;
+ char *token;
+
+ if (str == NULL)
+ str = olds;
+
+ str += strspn(str, delim);
+ if (*str == '\0')
+ {
+ olds = str;
+ return NULL;
+ }
+
+ token = str;
+ str = strpbrk(token, delim);
+
+ if (str == NULL)
+ {
+ olds = strchr(token, '\0');
+ }
+ else
+ {
+ *str = '\0';
+ olds = str + 1;
+ }
+
+ return token;
+}
+
+char *strerror(int errnum)
+{
+
+ switch (errnum)
+ {
+ case 0:
+ return "Success";
+ // case EINVAL: return "Invalid argument";
+ case ENOMEM: return "Out of memory";
+
+ default:
+ return "Unknown error";
+ }
+} \ No newline at end of file
diff --git a/src/user/entry.S b/src/user/entry.S
new file mode 100644
index 0000000..110ebbf
--- /dev/null
+++ b/src/user/entry.S
@@ -0,0 +1,12 @@
+.section .text
+.global _start
+
+_start:
+ li a0, 0
+ li a1, 0
+
+ call main
+
+ call exit
+
+1: j 1b \ No newline at end of file
diff --git a/src/user/init.cc b/src/user/init.cc
index 3c42adf..827a21e 100644
--- a/src/user/init.cc
+++ b/src/user/init.cc
@@ -1,34 +1,58 @@
+#include "ulib/stdio.h"
+#include "ulib/stdlib.h"
+#include "ulib/string.h"
#include "user/user.h"
-int main();
-
-extern "C" void _start()
-{
- exit(main());
-}
-
int main()
{
- int pid = fork();
+ char *argv[] = {(char *)"sh", 0};
- if (pid < 0)
- {
- write(1, "Fork failed\n", 12);
- exit(1);
- }
+ printf("[Init] Starting Lume OS Shell...\n");
- if (pid == 0)
- {
- write(1, "Hello from Child!\n", 18);
- exit(0);
- }
- else
+ while (1)
{
- write(1, "Parent waiting...\n", 18);
- wait(0);
- write(1, "Parent: Child exited.\n", 22);
- }
+ // printf("fork start\n");
+ int pid = fork();
+ // printf("fork suceess, pid=%i\n", pid);
+
+ if (pid < 0)
+ {
+ printf("[Init] fork failed!\n");
+ exit(1);
+ }
+
+ if (pid == 0)
+ {
+
+ char sh_path[] = "/sh";
+ exec(sh_path, argv);
- while (1);
+ printf("[Init] exec sh failed\n");
+ return 0;
+ exit(1);
+ }
+ else
+ {
+ // break;
+ int status;
+ int wpid;
+ while (1)
+ {
+ wpid = wait((uint64)&status);
+
+ if (wpid == pid)
+ {
+ printf("Shell Exit");
+ break;
+ }
+ if (wpid < 0)
+ {
+ printf("[Init] wait error or no child\n");
+
+ break;
+ }
+ }
+ }
+ }
return 0;
} \ No newline at end of file
diff --git a/src/user/sh.cc b/src/user/sh.cc
new file mode 100644
index 0000000..1f2e950
--- /dev/null
+++ b/src/user/sh.cc
@@ -0,0 +1,418 @@
+#include "ulib/stdio.h"
+#include "ulib/stdlib.h"
+#include "ulib/string.h"
+#include "user/user.h"
+#include "common/fcntl.h"
+
+constexpr int MAX_ARGS = 10;
+const char *WHITESPACE = " \t\r\n\v";
+const char *SYMBOLS = "<|>&;()";
+
+void panic(const char *s)
+{
+ printf("shell panic: %s\n", s);
+ exit(1);
+}
+
+int fork_or_panic()
+{
+ int pid = fork();
+ if (pid == -1)
+ panic("fork");
+ return pid;
+}
+
+struct Command
+{
+ virtual ~Command() = default;
+
+ virtual void run() = 0;
+};
+
+struct ExecCommand : public Command
+{
+ char *argv[MAX_ARGS];
+
+ ExecCommand()
+ {
+ memset(argv, 0, sizeof(argv));
+ }
+
+ void run() override
+ {
+ if (argv[0] == nullptr)
+ exit(1);
+
+ exec(argv[0], argv);
+
+ printf("exec %s failed\n", argv[0]);
+ exit(1);
+ }
+};
+
+struct RedirCommand : public Command
+{
+ Command *sub_cmd;
+ char *file;
+ char *efile; // file name end pointer (used by parser)
+ int mode;
+ int fd;
+
+ RedirCommand(Command *c, char *f, char *ef, int m, int fd_in)
+ : sub_cmd(c), file(f), efile(ef), mode(m), fd(fd_in) {}
+
+ ~RedirCommand() { delete sub_cmd; }
+
+ void run() override
+ {
+ close(fd);
+ if (open(file, mode) < 0)
+ {
+ printf("open %s failed\n", file);
+ exit(1);
+ }
+ sub_cmd->run();
+ }
+};
+
+struct PipeCommand : public Command
+{
+ Command *left;
+ Command *right;
+
+ PipeCommand(Command *l, Command *r) : left(l), right(r) {}
+ ~PipeCommand()
+ {
+ delete left;
+ delete right;
+ }
+
+ void run() override
+ {
+ int p[2];
+ if (pipe(p) < 0)
+ panic("pipe");
+
+ if (fork_or_panic() == 0)
+ {
+ // Left child: write to pipe
+ close(1); // close stdout
+ dup(p[1]); // dup write-end to stdout
+ close(p[0]);
+ close(p[1]);
+ left->run();
+ }
+
+ if (fork_or_panic() == 0)
+ {
+ // Right child: read from pipe
+ close(0); // close stdin
+ dup(p[0]); // dup read-end to stdin
+ close(p[0]);
+ close(p[1]);
+ right->run();
+ }
+
+ close(p[0]);
+ close(p[1]);
+ wait(0);
+ wait(0);
+ exit(0);
+ }
+};
+
+struct ListCommand : public Command
+{
+ Command *left;
+ Command *right;
+
+ ListCommand(Command *l, Command *r) : left(l), right(r) {}
+ ~ListCommand()
+ {
+ delete left;
+ delete right;
+ }
+
+ void run() override
+ {
+ if (fork_or_panic() == 0)
+ {
+ left->run();
+ }
+ wait(0);
+ right->run();
+ }
+};
+
+struct BackCommand : public Command
+{
+ Command *sub_cmd;
+
+ BackCommand(Command *c) : sub_cmd(c) {}
+ ~BackCommand() { delete sub_cmd; }
+
+ void run() override
+ {
+ if (fork_or_panic() == 0)
+ {
+ sub_cmd->run();
+ }
+ // Parent doesn't wait
+ exit(0);
+ }
+};
+
+class Parser
+{
+public:
+ static Command *parse(char *s)
+ {
+ char *es = s + strlen(s);
+ Command *cmd = parse_line(&s, es);
+ peek(&s, es, "");
+ if (s != es)
+ {
+ printf("leftovers: %s\n", s);
+ // panic("syntax"); // strict syntax check
+ }
+ terminate_strings(cmd);
+ return cmd;
+ }
+
+private:
+ // Helper: NUL-terminate strings found during parsing
+ static void terminate_strings(Command *cmd)
+ {
+ if (!cmd)
+ return;
+ }
+
+ static char get_token(char **ps, char *es, char **q, char **eq)
+ {
+ char *s;
+ int ret;
+
+ s = *ps;
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ if (q)
+ *q = s;
+ ret = *s;
+ switch (*s)
+ {
+ case 0:
+ break;
+ case '|':
+ case '(':
+ case ')':
+ case ';':
+ case '&':
+ case '<':
+ s++;
+ break;
+ case '>':
+ s++;
+ if (*s == '>')
+ {
+ ret = '+';
+ s++;
+ }
+ break;
+ default:
+ ret = 'a';
+ while (s < es && !strchr(WHITESPACE, *s) && !strchr(SYMBOLS, *s))
+ s++;
+ break;
+ }
+ if (eq)
+ *eq = s;
+
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ *ps = s;
+ return ret;
+ }
+
+ static int peek(char **ps, char *es, const char *toks)
+ {
+ char *s = *ps;
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ *ps = s;
+ return *s && strchr(toks, *s);
+ }
+
+ // Recursive Descent Parsers
+
+ static Command *parse_line(char **ps, char *es)
+ {
+ Command *cmd = parse_pipe(ps, es);
+ while (peek(ps, es, "&"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new BackCommand(cmd);
+ }
+ if (peek(ps, es, ";"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new ListCommand(cmd, parse_line(ps, es));
+ }
+ return cmd;
+ }
+
+ static Command *parse_pipe(char **ps, char *es)
+ {
+ Command *cmd = parse_exec(ps, es);
+ if (peek(ps, es, "|"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new PipeCommand(cmd, parse_pipe(ps, es));
+ }
+ return cmd;
+ }
+
+ static Command *parse_exec(char **ps, char *es)
+ {
+ char *q, *eq;
+ int tok, argc;
+ ExecCommand *cmd = new ExecCommand();
+ Command *ret = cmd;
+
+ argc = 0;
+ ret = parse_redirs(ret, ps, es);
+ while (!peek(ps, es, "|)&;"))
+ {
+ if ((tok = get_token(ps, es, &q, &eq)) == 0)
+ break;
+ if (tok != 'a')
+ panic("syntax");
+
+ cmd->argv[argc] = q;
+ *eq = 0; // Temp
+
+ argc++;
+ if (argc >= MAX_ARGS)
+ panic("too many args");
+ ret = parse_redirs(ret, ps, es);
+ }
+ cmd->argv[argc] = 0;
+ return ret;
+ }
+
+ static Command *parse_redirs(Command *cmd, char **ps, char *es)
+ {
+ int tok;
+ char *q, *eq;
+
+ while (peek(ps, es, "<>"))
+ {
+ tok = get_token(ps, es, 0, 0);
+ if (get_token(ps, es, &q, &eq) != 'a')
+ panic("missing file for redirection");
+
+ *eq = 0; // Null terminate filename immediately
+
+ switch (tok)
+ {
+ case '<':
+ cmd = new RedirCommand(cmd, q, eq, O_RDONLY, 0);
+ break;
+ case '>':
+ cmd = new RedirCommand(cmd, q, eq, O_WRONLY | O_CREATE | O_TRUNC, 1);
+ break;
+ case '+': // >>
+ cmd = new RedirCommand(cmd, q, eq, O_WRONLY | O_CREATE, 1); // Append not fully supported in simple OS yet?
+ break;
+ }
+ }
+ return cmd;
+ }
+};
+
+int getcmd(char *buf, int nbuf)
+{
+ write(1, "$ ", 2);
+ memset(buf, 0, nbuf);
+
+ int i = 0;
+ while (i < nbuf - 1)
+ {
+ char c;
+ int n = read(0, &c, 1);
+ if (n <= 0)
+ return -1;
+
+ if (c == '\b' || c == 127)
+ {
+ if (i > 0)
+ {
+ write(1, "\b \b", 3);
+ i--;
+ }
+ }
+ else if (c == '\n' || c == '\r')
+ {
+ write(1, "\n", 1);
+ buf[i] = 0;
+ return 0;
+ }
+ else
+ {
+ write(1, &c, 1);
+ buf[i++] = c;
+ }
+ }
+ return -1;
+}
+
+int main()
+{
+ static char buf[100];
+
+ // int fd;
+ // while ((fd = open("console", O_RDWR)) >= 0)
+ // {
+ // if (fd >= 3)
+ // {
+ // close(fd);
+ // break;
+ // }
+ // }
+
+ printf("\n[Lume Shell] Initialized! \n");
+
+ while (getcmd(buf, sizeof(buf)) >= 0)
+ {
+ if (buf[0] == 'c' && buf[1] == 'd' && (buf[2] == ' ' || buf[2] == '\n' || buf[2] == 0))
+ {
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = 0;
+
+ char *path = buf + 3;
+ if (buf[2] == '\n' || buf[2] == 0)
+ {
+ printf("cd: argument missing\n");
+ continue;
+ }
+
+ if (chdir(path) < 0)
+ {
+ printf("cannot cd %s\n", path);
+ }
+ continue;
+ }
+
+ if (fork_or_panic() == 0)
+ {
+ Command *cmd = Parser::parse(buf);
+ if (cmd)
+ {
+ cmd->run();
+ delete cmd;
+ }
+ exit(0);
+ }
+ wait(0);
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/src/user/user.ld b/src/user/user.ld
index 2111b14..8c9999a 100644
--- a/src/user/user.ld
+++ b/src/user/user.ld
@@ -1,10 +1,10 @@
-OUTPUT_ARCH(riscv)
-ENTRY(_start)
+OUTPUT_ARCH( "riscv" )
+ENTRY( _start )
SECTIONS
{
. = 0x0;
-
+
.text : {
*(.text.entry)
*(.text .text.*)
@@ -12,16 +12,22 @@ SECTIONS
.rodata : {
. = ALIGN(16);
+ *(.srodata .srodata.*)
+ . = ALIGN(16);
*(.rodata .rodata.*)
}
.data : {
. = ALIGN(16);
+ *(.sdata .sdata.*)
+ . = ALIGN(16);
*(.data .data.*)
}
.bss : {
. = ALIGN(16);
+ *(.sbss .sbss.*)
+ . = ALIGN(16);
*(.bss .bss.*)
}
} \ No newline at end of file
diff --git a/src/user/usys.S b/src/user/usys.S
index e805d82..f85917e 100644
--- a/src/user/usys.S
+++ b/src/user/usys.S
@@ -7,8 +7,16 @@
ecall; \
ret
+#define SYSCALL_NAME(name, sysname) \
+ .global name; \
+ name: \
+ li a7, SYS_ ## sysname; \
+ ecall; \
+ ret
+
+SYSCALL_NAME(sys_exit, exit)
+
SYSCALL(fork)
-SYSCALL(exit)
SYSCALL(wait)
SYSCALL(pipe)
SYSCALL(read)
@@ -28,4 +36,6 @@ SYSCALL(getpid)
SYSCALL(sbrk)
SYSCALL(sleep)
SYSCALL(uptime)
-SYSCALL(putc) \ No newline at end of file
+SYSCALL(putc)
+SYSCALL(disk_test)
+SYSCALL(lseek) \ No newline at end of file