Compare commits

..

2 Commits

Author SHA1 Message Date
2c70ec98d8 Disk I/O 2026-02-15 16:14:57 +04:00
c94866f688 User mode 2026-02-15 15:34:42 +04:00
11 changed files with 308 additions and 39 deletions

View File

@@ -1,5 +1,11 @@
CC = clang CC = clang
OBJCOPY=llvm-objcopy
CFLAGS = -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib CFLAGS = -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib
kernel: kernel: shell
$(CC) $(CFLAGS) -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf kernel.c common.c $(CC) $(CFLAGS) -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf kernel.c common.c shell.bin.o
shell:
$(CC) $(CFLAGS) -Wl,-Tuser.ld -Wl,-Map=shell.map -o shell.elf shell.c user.c common.c
$(OBJCOPY) --set-section-flags .bss=alloc,contents -O binary shell.elf shell.bin
$(OBJCOPY) -Ibinary -Oelf32-littleriscv shell.bin shell.bin.o

View File

@@ -28,6 +28,10 @@ typedef uint32_t vaddr_t;
#define PAGE_SIZE 4096 #define PAGE_SIZE 4096
#define USER_BASE 0x1000000
#define SSTATUS_SPIE (1 << 5)
void *memset(void *buf, char c, size_t n); void *memset(void *buf, char c, size_t n);
void *memcpy(void *dst, const void *src, size_t n); void *memcpy(void *dst, const void *src, size_t n);

View File

@@ -54,7 +54,7 @@ void map_page(uint32_t *table1, uint32_t vaddr, paddr_t paddr, uint32_t flags) {
} }
// ===== Exception handler ===== // ===== Exception handler =====
void kernel_entry(void) { __attribute__((naked)) __attribute__((aligned(4))) void kernel_entry(void) {
__asm__ __volatile__("csrrw sp, sscratch, sp\n" __asm__ __volatile__("csrrw sp, sscratch, sp\n"
"addi sp, sp, -4 * 31\n" "addi sp, sp, -4 * 31\n"
@@ -89,9 +89,11 @@ void kernel_entry(void) {
"sw s10, 4 * 28(sp)\n" "sw s10, 4 * 28(sp)\n"
"sw s11, 4 * 29(sp)\n" "sw s11, 4 * 29(sp)\n"
// Извлечение и сохранение sp в момент исключения.
"csrr a0, sscratch\n" "csrr a0, sscratch\n"
"sw a0, 4 * 30(sp)\n" "sw a0, 4 * 30(sp)\n"
// Сброс стека ядра.
"addi a0, sp, 4 * 31\n" "addi a0, sp, 4 * 31\n"
"csrw sscratch, a0\n" "csrw sscratch, a0\n"
@@ -182,7 +184,15 @@ __attribute__((naked)) void switch_context(uint32_t *prev_sp,
"ret\n"); "ret\n");
} }
struct process *create_process(uint32_t pc) { __attribute__((naked)) void user_entry(void) {
__asm__ __volatile__("csrw sepc, %[sepc] \n"
"csrw sstatus, %[sstatus] \n"
"sret \n"
:
: [sepc] "r"(USER_BASE), [sstatus] "r"(SSTATUS_SPIE));
}
struct process *create_process(const void *image, size_t image_size) {
struct process *proc = NULL; struct process *proc = NULL;
int i; int i;
for (i = 0; i < PROCS_MAX; i++) { for (i = 0; i < PROCS_MAX; i++) {
@@ -208,13 +218,26 @@ struct process *create_process(uint32_t pc) {
*--sp = 0; // s2 *--sp = 0; // s2
*--sp = 0; // s1 *--sp = 0; // s1
*--sp = 0; // s0 *--sp = 0; // s0
*--sp = (uint32_t)pc; // ra *--sp = (uint32_t)user_entry; // ra
uint32_t *page_table = (uint32_t *)alloc_pages(1); uint32_t *page_table = (uint32_t *)alloc_pages(1);
for (paddr_t paddr = (paddr_t)__kernel_base; for (paddr_t paddr = (paddr_t)__kernel_base;
paddr < (paddr_t)__free_ram_end; paddr += PAGE_SIZE) paddr < (paddr_t)__free_ram_end; paddr += PAGE_SIZE)
map_page(page_table, paddr, paddr, PAGE_R | PAGE_W | PAGE_X); map_page(page_table, paddr, paddr, PAGE_R | PAGE_W | PAGE_X);
map_page(page_table, VIRTIO_BLK_PADDR, VIRTIO_BLK_PADDR, PAGE_R | PAGE_W);
for (uint32_t off = 0; off < image_size; off += PAGE_SIZE) {
paddr_t page = alloc_pages(1);
size_t remaining = image_size - off;
size_t copy_size = PAGE_SIZE <= remaining ? PAGE_SIZE : remaining;
memcpy((void *)page, image + off, copy_size);
map_page(page_table, USER_BASE + off, page,
PAGE_U | PAGE_R | PAGE_W | PAGE_X);
}
proc->pid = i + 1; proc->pid = i + 1;
proc->state = PROC_RUNNABLE; proc->state = PROC_RUNNABLE;
proc->sp = (uint32_t)sp; proc->sp = (uint32_t)sp;
@@ -248,35 +271,137 @@ void yield(void) {
switch_context(&prev->sp, &next->sp); switch_context(&prev->sp, &next->sp);
} }
// ===== Disk I/O =====
uint32_t virtio_reg_read32(unsigned offset) {
return *((volatile uint32_t *)(VIRTIO_BLK_PADDR + offset));
}
uint64_t virtio_reg_read64(unsigned offset) {
return *((volatile uint64_t *)(VIRTIO_BLK_PADDR + offset));
}
void virtio_reg_write32(unsigned offset, uint32_t value) {
*((volatile uint32_t *)(VIRTIO_BLK_PADDR + offset)) = value;
}
void virtio_reg_fetch_and_or32(unsigned offset, uint32_t value) {
virtio_reg_write32(offset, virtio_reg_read32(offset) | value);
}
struct virtio_virtq *virtq_init(unsigned index) {
paddr_t virtq_paddr = alloc_pages(
align_up(sizeof(struct virtio_virtq), PAGE_SIZE) / PAGE_SIZE);
struct virtio_virtq *vq = (struct virtio_virtq *)virtq_paddr;
vq->queue_index = index;
vq->used_index = (volatile uint16_t *)&vq->used.index;
virtio_reg_write32(VIRTIO_REG_QUEUE_SEL, index);
virtio_reg_write32(VIRTIO_REG_QUEUE_NUM, VIRTQ_ENTRY_NUM);
virtio_reg_write32(VIRTIO_REG_QUEUE_ALIGN, 0);
virtio_reg_write32(VIRTIO_REG_QUEUE_PFN, virtq_paddr);
return vq;
}
void virtio_blk_init(void) {
if (virtio_reg_read32(VIRTIO_REG_MAGIC) != 0x74726976)
PANIC("virtio: invalid magic value");
if (virtio_reg_read32(VIRTIO_REG_VERSION) != 1)
PANIC("virtio: invalid version");
if (virtio_reg_read32(VIRTIO_REG_DEVICE_ID) != VIRTIO_DEVICE_BLK)
PANIC("virtio: invalid device id");
virtio_reg_write32(VIRTIO_REG_DEVICE_STATUS, 0);
virtio_reg_fetch_and_or32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_ACK);
virtio_reg_fetch_and_or32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_DRIVER);
virtio_reg_fetch_and_or32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_FEAT_OK);
blk_request_vq = virtq_init(0);
virtio_reg_write32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_DRIVER_OK);
blk_capacity =
virtio_reg_read64(VIRTIO_REG_DEVICE_CONFIG + 0) * SECTOR_SIZE;
printf("virtio-blk: capacity is %d bytes\n", blk_capacity);
blk_req_paddr =
alloc_pages(align_up(sizeof(*blk_req), PAGE_SIZE) / PAGE_SIZE);
blk_req = (struct virtio_blk_req *)blk_req_paddr;
}
void virtq_kick(struct virtio_virtq *vq, int desc_index) {
vq->avail.ring[vq->avail.index % VIRTQ_ENTRY_NUM] = desc_index;
vq->avail.index++;
__sync_synchronize();
virtio_reg_write32(VIRTIO_REG_QUEUE_NOTIFY, vq->queue_index);
vq->last_used_index++;
}
bool virtq_is_busy(struct virtio_virtq *vq) {
return vq->last_used_index != *vq->used_index;
}
void read_write_disk(void *buf, unsigned sector, int is_write) {
if (sector >= blk_capacity / SECTOR_SIZE) {
printf("virtio: tried to read/write sector=%d, but capacity is %d\n",
sector, blk_capacity / SECTOR_SIZE);
return;
}
blk_req->sector = sector;
blk_req->type = is_write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
if (is_write)
memcpy(blk_req->data, buf, SECTOR_SIZE);
struct virtio_virtq *vq = blk_request_vq;
vq->descs[0].addr = blk_req_paddr;
vq->descs[0].len = sizeof(uint32_t) * 2 + sizeof(uint64_t);
vq->descs[0].flags = VIRTQ_DESC_F_NEXT;
vq->descs[0].next = 1;
vq->descs[1].addr = blk_req_paddr + offsetof(struct virtio_blk_req, data);
vq->descs[1].len = SECTOR_SIZE;
vq->descs[1].flags =
VIRTQ_DESC_F_NEXT | (is_write ? 0 : VIRTQ_DESC_F_WRITE);
vq->descs[1].next = 2;
vq->descs[2].addr = blk_req_paddr + offsetof(struct virtio_blk_req, status);
vq->descs[2].len = sizeof(uint8_t);
vq->descs[2].flags = VIRTQ_DESC_F_WRITE;
virtq_kick(vq, 0);
while (virtq_is_busy(vq))
;
if (blk_req->status != 0) {
printf("virtio: warn: failed to read/write sector=%d status=%d\n",
sector, blk_req->status);
return;
}
if (!is_write)
memcpy(buf, blk_req->data, SECTOR_SIZE);
}
// ===== ENTRY POINT ===== // ===== ENTRY POINT =====
struct process *proc_a;
struct process *proc_b;
void proc_a_entry(void) {
while (1) {
putchar('A');
yield();
}
}
void proc_b_entry(void) {
while (1) {
putchar('B');
yield();
}
}
void kernel_main(void) { void kernel_main(void) {
memset(__bss, 0, (size_t)__bss_end - (size_t)__bss); memset(__bss, 0, (size_t)__bss_end - (size_t)__bss);
printf("\n\n");
WRITE_CSR(stvec, (uint32_t)kernel_entry); WRITE_CSR(stvec, (uint32_t)kernel_entry);
idle_proc = create_process((uint32_t)NULL); virtio_blk_init();
char buf[SECTOR_SIZE];
read_write_disk(buf, 0, false);
printf("first sector: %s\n", buf);
strcpy(buf, "hello from kernel!!!\n");
read_write_disk(buf, 0, true);
idle_proc = create_process(NULL, 0);
idle_proc->pid = -1; idle_proc->pid = -1;
current_proc = idle_proc; current_proc = idle_proc;
proc_a = create_process((uint32_t)proc_a_entry); create_process(_binary_shell_bin_start, (size_t)_binary_shell_bin_size);
proc_b = create_process((uint32_t)proc_b_entry);
yield(); yield();
PANIC("switched to idle process"); PANIC("switched to idle process");

View File

@@ -7,6 +7,7 @@ extern char __bss[], __bss_end[];
extern char __stack_top[]; extern char __stack_top[];
extern char __free_ram[], __free_ram_end[]; extern char __free_ram[], __free_ram_end[];
extern char __kernel_base[]; extern char __kernel_base[];
extern char _binary_shell_bin_start[], _binary_shell_bin_size[];
// ===== SBI data ====== // ===== SBI data ======
struct sbiret { struct sbiret {
@@ -74,6 +75,79 @@ struct process *idle_proc;
#define PAGE_X (1 << 3) #define PAGE_X (1 << 3)
#define PAGE_U (1 << 4) #define PAGE_U (1 << 4)
// ===== Disk I/O =====
#define SECTOR_SIZE 512
#define VIRTQ_ENTRY_NUM 16
#define VIRTIO_DEVICE_BLK 2
#define VIRTIO_BLK_PADDR 0x10001000
#define VIRTIO_REG_MAGIC 0x00
#define VIRTIO_REG_VERSION 0x04
#define VIRTIO_REG_DEVICE_ID 0x08
#define VIRTIO_REG_QUEUE_SEL 0x30
#define VIRTIO_REG_QUEUE_NUM_MAX 0x34
#define VIRTIO_REG_QUEUE_NUM 0x38
#define VIRTIO_REG_QUEUE_ALIGN 0x3c
#define VIRTIO_REG_QUEUE_PFN 0x40
#define VIRTIO_REG_QUEUE_READY 0x44
#define VIRTIO_REG_QUEUE_NOTIFY 0x50
#define VIRTIO_REG_DEVICE_STATUS 0x70
#define VIRTIO_REG_DEVICE_CONFIG 0x100
#define VIRTIO_STATUS_ACK 1
#define VIRTIO_STATUS_DRIVER 2
#define VIRTIO_STATUS_DRIVER_OK 4
#define VIRTIO_STATUS_FEAT_OK 8
#define VIRTQ_DESC_F_NEXT 1
#define VIRTQ_DESC_F_WRITE 2
#define VIRTQ_AVAIL_F_NO_INTERRUPT 1
#define VIRTIO_BLK_T_IN 0
#define VIRTIO_BLK_T_OUT 1
struct virtq_desc {
uint64_t addr;
uint32_t len;
uint16_t flags;
uint16_t next;
} __attribute__((packed));
struct virtq_avail {
uint16_t flags;
uint16_t index;
uint16_t ring[VIRTQ_ENTRY_NUM];
} __attribute__((packed));
struct virtq_used_elem {
uint32_t id;
uint32_t len;
} __attribute__((packed));
struct virtq_used {
uint16_t flags;
uint16_t index;
struct virtq_used_elem ring[VIRTQ_ENTRY_NUM];
} __attribute__((packed));
struct virtio_virtq {
struct virtq_desc descs[VIRTQ_ENTRY_NUM];
struct virtq_avail avail;
struct virtq_used used __attribute__((aligned(PAGE_SIZE)));
int queue_index;
volatile uint16_t *used_index;
uint16_t last_used_index;
} __attribute__((packed));
struct virtio_blk_req {
uint32_t type;
uint32_t reserved;
uint64_t sector;
uint8_t data[512];
uint8_t status;
} __attribute__((packed));
struct virtio_virtq *blk_request_vq;
struct virtio_blk_req *blk_req;
paddr_t blk_req_paddr;
unsigned blk_capacity;
// ===== Macros ===== // ===== Macros =====
#define READ_CSR(reg) \ #define READ_CSR(reg) \
({ \ ({ \

BIN
src/lorem.txt Normal file

Binary file not shown.

View File

@@ -1,4 +1,6 @@
@echo off @echo off
make kernel make kernel
cd /d "C:\Program Files\qemu" cd /d "C:\Program Files\qemu"
qemu-system-riscv32.exe -machine virt -bios default -nographic -serial mon:stdio --no-reboot -kernel %~dp0kernel.elf qemu-system-riscv32.exe -machine virt -bios default -nographic -serial mon:stdio --no-reboot -drive id=drive0,file=lorem.txt,format=raw,if=none -device virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0 -kernel %~dp0kernel.elf

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash
set -xue set -xue
make kernel && qemu-system-riscv32 -machine virt -bios default -nographic -serial mon:stdio --no-reboot -kernel kernel.elf make kernel && qemu-system-riscv32 -machine virt -bios default -nographic -serial mon:stdio --no-reboot -drive id=drive0,file=lorem.txt,format=raw,if=none -device virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0 -kernel kernel.elf

6
src/shell.c Normal file
View File

@@ -0,0 +1,6 @@
#include "user.h"
void main(void) {
for (;;)
;
}

18
src/user.c Normal file
View File

@@ -0,0 +1,18 @@
#include "user.h"
extern char __stack_top[];
__attribute__((noreturn)) void exit(void) {
for (;;)
;
}
void putchar(char c) { /* Доделать*/ }
__attribute__((section(".text.start"))) __attribute__((naked)) void
start(void) {
__asm__ __volatile__(
"mv sp, %[stack_top] \n"
"call main \n"
"call exit \n" ::[stack_top] "r"(__stack_top));
}

6
src/user.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include "common.h"
__attribute__((noreturn)) void exit(void);
void putchar(char ch);

28
src/user.ld Normal file
View File

@@ -0,0 +1,28 @@
ENTRY(start)
SECTIONS {
. = 0x1000000;
.text :{
KEEP(*(.text.start));
*(.text .text.*);
}
.rodata : ALIGN(4) {
*(.rodata .rodata.*);
}
.data : ALIGN(4) {
*(.data .data.*);
}
.bss : ALIGN(4) {
*(.bss .bss.* .sbss .sbss.*);
. = ALIGN(16);
. += 64 * 1024; /* 64KB */
__stack_top = .;
ASSERT(. < 0x1800000, "too large executable");
}
}