mirror of
https://github.com/StepanovPlaton/OSin1000Lines.git
synced 2026-04-03 12:20:46 +04:00
Disk I/O
This commit is contained in:
137
src/kernel.c
137
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;
|
||||
|
||||
73
src/kernel.h
73
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) \
|
||||
({ \
|
||||
|
||||
BIN
src/lorem.txt
Normal file
BIN
src/lorem.txt
Normal file
Binary file not shown.
@@ -1,4 +1,6 @@
|
||||
@echo off
|
||||
make kernel
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
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
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "user.h"
|
||||
|
||||
void main(void) {
|
||||
*((volatile int *)0x80200000) = 0x1234; // new!
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user