From 2c70ec98d810e0c7696e1cd163b95fcb111d0675 Mon Sep 17 00:00:00 2001 From: StepanovPlaton Date: Sun, 15 Feb 2026 16:14:57 +0400 Subject: [PATCH] Disk I/O --- src/kernel.c | 137 +++++++++++++++++++++++++++++++++++++++++++------- src/kernel.h | 73 +++++++++++++++++++++++++++ src/lorem.txt | Bin 0 -> 599 bytes src/run.bat | 4 +- src/run.sh | 2 +- src/shell.c | 1 - 6 files changed, 197 insertions(+), 20 deletions(-) create mode 100644 src/lorem.txt diff --git a/src/kernel.c b/src/kernel.c index 59720eb..cf13f75 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -226,6 +226,8 @@ struct process *create_process(const void *image, size_t image_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, 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); @@ -269,24 +271,116 @@ void yield(void) { 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 ===== -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) { memset(__bss, 0, (size_t)__bss_end - (size_t)__bss); @@ -294,6 +388,15 @@ void kernel_main(void) { WRITE_CSR(stvec, (uint32_t)kernel_entry); + 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; current_proc = idle_proc; diff --git a/src/kernel.h b/src/kernel.h index c0e523a..8e79dc7 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -75,6 +75,79 @@ struct process *idle_proc; #define PAGE_X (1 << 3) #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 ===== #define READ_CSR(reg) \ ({ \ diff --git a/src/lorem.txt b/src/lorem.txt new file mode 100644 index 0000000000000000000000000000000000000000..e872814bd63a1e06052ec2b44ab13b45d888d992 GIT binary patch literal 599 zcmYL`y-vhH41}q93SS@P9S{`-5)Jgu#uqI1C+D?KJU+8I4x|*xX1p`s_)~IjbhcLL zORP#holcJr#6^6f)M}UH;$g(A?%I>qjU=nj^inCD3U8Hf!b|X&3G316Iz+@-aatzo zNwS#sO9-`9+f46HxtSCaYki`##HQ?Y3GKA#B6W>IS9O*NCRuv$%EXD9C7tQbSh`dv z*Gd&x3()(q^k-Ql&@S+Pu_`-vRM*43pI6|ev_AmLKIf;S!uf`)CT5_}| zP$DQ0