diff options
Diffstat (limited to 'lume-legacy/src/drivers')
| -rw-r--r-- | lume-legacy/src/drivers/plic.cc | 77 | ||||
| -rw-r--r-- | lume-legacy/src/drivers/uart.cc | 204 | ||||
| -rw-r--r-- | lume-legacy/src/drivers/virtio_dist.cc | 382 |
3 files changed, 663 insertions, 0 deletions
diff --git a/lume-legacy/src/drivers/plic.cc b/lume-legacy/src/drivers/plic.cc new file mode 100644 index 0000000..a975c44 --- /dev/null +++ b/lume-legacy/src/drivers/plic.cc @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Richard Qin + */ +#include "common/types.h" +#include "kernel/riscv.h" +#include "drivers/plic.h" +#include "drivers/uart.h" +#include "kernel/fdt.h" + +// Helper Macros — all relative to PLIC base from FDT +#define PLIC_BASE (g_devices.plic.base_addr) +#define PLIC_PRIORITY (PLIC_BASE + 0x0) +#define PLIC_PENDING (PLIC_BASE + 0x1000) + +#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 + if (g_devices.uart.irq > 0) + *REG32(PLIC_PRIORITY + g_devices.uart.irq * 4) = 1; + + // Set priority for all VirtIO IRQs + for (int i = 0; i < g_devices.virtio_count; i++) + { + if (g_devices.virtio[i].irq > 0) + *REG32(PLIC_PRIORITY + g_devices.virtio[i].irq * 4) = 1; + } + } + + void inithart() + { + int hart = static_cast<int>(r_tp()); + + // Build enable bitmask from discovered devices + uint32 enable = 0; + + if (g_devices.uart.irq > 0 && g_devices.uart.irq < 32) + enable |= (1 << g_devices.uart.irq); + + for (int i = 0; i < g_devices.virtio_count; i++) + { + if (g_devices.virtio[i].irq > 0 && g_devices.virtio[i].irq < 32) + enable |= (1 << g_devices.virtio[i].irq); + } + + *REG32(PLIC_SENABLE_ADDR(hart)) = enable; + + // 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/lume-legacy/src/drivers/uart.cc b/lume-legacy/src/drivers/uart.cc new file mode 100644 index 0000000..397a402 --- /dev/null +++ b/lume-legacy/src/drivers/uart.cc @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Richard QIn + */ +#include "drivers/uart.h" +#include "kernel/riscv.h" +#include "kernel/spinlock.h" +#include "kernel/proc.h" +#include "kernel/mm.h" +#include "lib/string.h" + +namespace Drivers +{ + struct + { + Spinlock lock; + char buf[128]; // Line buffer + uint32 r; // Read pointer (position taken by the user) + uint32 w; // Write pointer (position confirmed by Enter) + uint32 e; // Editing cursor (current typing position) + } cons; + + // Circular buffer structure + struct + { + 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() + { + uart_rx.lock.init("uart_rx"); + cons.lock.init("console"); + uart_tx_lock.init("uart_tx"); + uart_rx.r = 0; + uart_rx.w = 0; + + // Disable interrupts and configure + *(volatile uint8 *)(uart_base() + IER) = 0x00; + + // Enter baud rate setting mode (LCR_DLAB) = 0x80; + *(volatile uint8 *)(uart_base() + LCR) = 0x80; + *(volatile uint8 *)(uart_base() + 0) = 0x03; // DLL: 38.4K baud + *(volatile uint8 *)(uart_base() + 1) = 0x00; // DLM + + // Exit baud rate setting, set 8-bit word length, no parity + *(volatile uint8 *)(uart_base() + LCR) = 0x03; + + // Reset and enable FIFO + *(volatile uint8 *)(uart_base() + FCR) = 0x07; + + // Enable Receive Data Available Interrupt + *(volatile uint8 *)(uart_base() + IER) = 0x01; + } + + void uart_putc(char c) + { + uart_tx_lock.acquire(); + while ((*(volatile uint8 *)(uart_base() + LSR) & LSR_TX_IDLE) == 0) + ; + *(volatile uint8 *)(uart_base() + 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_puts("0"); + return; + } + if (val < 0) + { + uart_puts("-"); + val = -val; + } + while (val > 0) + { + buf[i++] = (val % 10) + '0'; + val /= 10; + } + while (i > 0) + { + uart_putc(buf[--i]); + } + } + + 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_intr() + { + uart_rx.lock.acquire(); + + while (1) + { + if ((*(volatile uint8 *)(uart_base() + LSR) & LSR_RX_READY) == 0) + break; + + char c = *(volatile uint8 *)(uart_base() + RHR); + + uart_rx.buf[uart_rx.w % UART_RX_BUF_SIZE] = c; + uart_rx.w++; + + ProcManager::wakeup(&uart_rx); + } + + uart_rx.lock.release(); + } + + // Modern console read: Line buffering + Editing Keys + Echo + int console_read(uint64 target, int len) + { + struct Proc *p = myproc(); + int target_idx = 0; + + cons.lock.acquire(); + + while (target_idx < len) + { + while (cons.r < cons.w) + { + char c = cons.buf[cons.r++ % sizeof(cons.buf)]; + cons.lock.release(); + + if (VM::copyout(p->pagetable, target + target_idx, &c, 1) < 0) + return -1; + + target_idx++; + if (c == '\n') + return target_idx; + if (target_idx == len) + return target_idx; + + cons.lock.acquire(); + } + + uart_rx.lock.acquire(); + while (uart_rx.r == uart_rx.w) + { + if (p->xstate) + { + uart_rx.lock.release(); + cons.lock.release(); + return -1; + } + cons.lock.release(); + ProcManager::sleep(&uart_rx, &uart_rx.lock); + cons.lock.acquire(); + } + + char c = uart_rx.buf[uart_rx.r++ % UART_RX_BUF_SIZE]; + uart_rx.lock.release(); + + if (c == 127 || c == '\b') + { + if (cons.e != cons.w) + { + cons.e--; + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + } + } + else + { + if (c == '\r') + c = '\n'; + + uart_putc(c); + + cons.buf[cons.e++ % sizeof(cons.buf)] = c; + + if (c == '\n' || cons.e == cons.w + sizeof(cons.buf)) + { + cons.w = cons.e; + ProcManager::wakeup(&cons); + } + } + } + + cons.lock.release(); + return target_idx; + } +}
\ No newline at end of file diff --git a/lume-legacy/src/drivers/virtio_dist.cc b/lume-legacy/src/drivers/virtio_dist.cc new file mode 100644 index 0000000..8292874 --- /dev/null +++ b/lume-legacy/src/drivers/virtio_dist.cc @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Richard QIn + */ +#include "drivers/virtio.h" +#include "kernel/fdt.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. Probe VirtIO devices discovered by FDT + bool found = false; + for (int i = 0; i < g_devices.virtio_count; i++) + { + uint64 addr = g_devices.virtio[i].base_addr; + if (addr == 0) + continue; + + 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 |
