mirror of
https://github.com/StepanovPlaton/OSin1000Lines.git
synced 2026-04-03 12:20:46 +04:00
Compare commits
2 Commits
10c456438b
...
2c70ec98d8
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c70ec98d8 | |||
| c94866f688 |
10
src/Makefile
10
src/Makefile
@@ -1,5 +1,11 @@
|
||||
CC = clang
|
||||
OBJCOPY=llvm-objcopy
|
||||
CFLAGS = -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib
|
||||
|
||||
kernel:
|
||||
$(CC) $(CFLAGS) -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf kernel.c common.c
|
||||
kernel: shell
|
||||
$(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
|
||||
|
||||
@@ -28,6 +28,10 @@ typedef uint32_t vaddr_t;
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
#define USER_BASE 0x1000000
|
||||
|
||||
#define SSTATUS_SPIE (1 << 5)
|
||||
|
||||
void *memset(void *buf, char c, size_t n);
|
||||
void *memcpy(void *dst, const void *src, size_t n);
|
||||
|
||||
|
||||
195
src/kernel.c
195
src/kernel.c
@@ -54,7 +54,7 @@ void map_page(uint32_t *table1, uint32_t vaddr, paddr_t paddr, uint32_t flags) {
|
||||
}
|
||||
|
||||
// ===== Exception handler =====
|
||||
void kernel_entry(void) {
|
||||
__attribute__((naked)) __attribute__((aligned(4))) void kernel_entry(void) {
|
||||
__asm__ __volatile__("csrrw sp, sscratch, sp\n"
|
||||
|
||||
"addi sp, sp, -4 * 31\n"
|
||||
@@ -89,9 +89,11 @@ void kernel_entry(void) {
|
||||
"sw s10, 4 * 28(sp)\n"
|
||||
"sw s11, 4 * 29(sp)\n"
|
||||
|
||||
// Извлечение и сохранение sp в момент исключения.
|
||||
"csrr a0, sscratch\n"
|
||||
"sw a0, 4 * 30(sp)\n"
|
||||
|
||||
// Сброс стека ядра.
|
||||
"addi a0, sp, 4 * 31\n"
|
||||
"csrw sscratch, a0\n"
|
||||
|
||||
@@ -182,7 +184,15 @@ __attribute__((naked)) void switch_context(uint32_t *prev_sp,
|
||||
"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;
|
||||
int i;
|
||||
for (i = 0; i < PROCS_MAX; i++) {
|
||||
@@ -196,25 +206,38 @@ struct process *create_process(uint32_t pc) {
|
||||
PANIC("no free process slots");
|
||||
|
||||
uint32_t *sp = (uint32_t *)&proc->stack[sizeof(proc->stack)];
|
||||
*--sp = 0; // s11
|
||||
*--sp = 0; // s10
|
||||
*--sp = 0; // s9
|
||||
*--sp = 0; // s8
|
||||
*--sp = 0; // s7
|
||||
*--sp = 0; // s6
|
||||
*--sp = 0; // s5
|
||||
*--sp = 0; // s4
|
||||
*--sp = 0; // s3
|
||||
*--sp = 0; // s2
|
||||
*--sp = 0; // s1
|
||||
*--sp = 0; // s0
|
||||
*--sp = (uint32_t)pc; // ra
|
||||
*--sp = 0; // s11
|
||||
*--sp = 0; // s10
|
||||
*--sp = 0; // s9
|
||||
*--sp = 0; // s8
|
||||
*--sp = 0; // s7
|
||||
*--sp = 0; // s6
|
||||
*--sp = 0; // s5
|
||||
*--sp = 0; // s4
|
||||
*--sp = 0; // s3
|
||||
*--sp = 0; // s2
|
||||
*--sp = 0; // s1
|
||||
*--sp = 0; // s0
|
||||
*--sp = (uint32_t)user_entry; // ra
|
||||
|
||||
uint32_t *page_table = (uint32_t *)alloc_pages(1);
|
||||
|
||||
for (paddr_t paddr = (paddr_t)__kernel_base;
|
||||
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);
|
||||
|
||||
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->state = PROC_RUNNABLE;
|
||||
proc->sp = (uint32_t)sp;
|
||||
@@ -248,35 +271,137 @@ 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);
|
||||
|
||||
printf("\n\n");
|
||||
|
||||
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;
|
||||
current_proc = idle_proc;
|
||||
|
||||
proc_a = create_process((uint32_t)proc_a_entry);
|
||||
proc_b = create_process((uint32_t)proc_b_entry);
|
||||
create_process(_binary_shell_bin_start, (size_t)_binary_shell_bin_size);
|
||||
|
||||
yield();
|
||||
PANIC("switched to idle process");
|
||||
|
||||
74
src/kernel.h
74
src/kernel.h
@@ -7,6 +7,7 @@ extern char __bss[], __bss_end[];
|
||||
extern char __stack_top[];
|
||||
extern char __free_ram[], __free_ram_end[];
|
||||
extern char __kernel_base[];
|
||||
extern char _binary_shell_bin_start[], _binary_shell_bin_size[];
|
||||
|
||||
// ===== SBI data ======
|
||||
struct sbiret {
|
||||
@@ -74,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
|
||||
|
||||
6
src/shell.c
Normal file
6
src/shell.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "user.h"
|
||||
|
||||
void main(void) {
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
18
src/user.c
Normal file
18
src/user.c
Normal 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
6
src/user.h
Normal 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
28
src/user.ld
Normal 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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user