diff --git a/src/Makefile b/src/Makefile index 5726ed3..a1f95a0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,10 +2,13 @@ CC = clang OBJCOPY=llvm-objcopy CFLAGS = -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib -kernel: shell +kernel: shell disk.tar $(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 + +disk.tar: $(wildcard disk/*.txt) + cd disk && tar cf ../disk.tar --format=ustar ./*.txt diff --git a/src/common.h b/src/common.h index c7e803b..c9009b8 100644 --- a/src/common.h +++ b/src/common.h @@ -31,6 +31,13 @@ typedef uint32_t vaddr_t; #define USER_BASE 0x1000000 #define SSTATUS_SPIE (1 << 5) +#define SSTATUS_SUM (1 << 18) + +#define SYS_PUTCHAR 1 +#define SYS_GETCHAR 2 +#define SYS_EXIT 3 +#define SYS_READFILE 4 +#define SYS_WRITEFILE 5 void *memset(void *buf, char c, size_t n); void *memcpy(void *dst, const void *src, size_t n); diff --git a/src/kernel.c b/src/kernel.c index cf13f75..2e1ed90 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -21,6 +21,10 @@ struct sbiret sbi_call(long arg0, long arg1, long arg2, long arg3, long arg4, } void putchar(char ch) { sbi_call(ch, 0, 0, 0, 0, 0, 0, 1); } +long getchar(void) { + struct sbiret ret = sbi_call(0, 0, 0, 0, 0, 0, 0, 2); + return ret.error; +} // ===== MEMORY ALLOCATION ===== paddr_t alloc_pages(uint32_t n) { @@ -134,13 +138,20 @@ __attribute__((naked)) __attribute__((aligned(4))) void kernel_entry(void) { "sret\n"); } +void handle_syscall(struct trap_frame *f); void handle_trap(struct trap_frame *f) { uint32_t scause = READ_CSR(scause); uint32_t stval = READ_CSR(stval); uint32_t user_pc = READ_CSR(sepc); + if (scause == SCAUSE_ECALL) { + handle_syscall(f); + user_pc += 4; + } else { + PANIC("unexpected trap scause=%x, stval=%x, sepc=%x\n", scause, stval, + user_pc); + } - PANIC("unexpected trap scause=%x, stval=%x, sepc=%x\n", scause, stval, - user_pc); + WRITE_CSR(sepc, user_pc); } // ===== Multiprocessing ===== @@ -185,11 +196,13 @@ __attribute__((naked)) void switch_context(uint32_t *prev_sp, } __attribute__((naked)) void user_entry(void) { - __asm__ __volatile__("csrw sepc, %[sepc] \n" - "csrw sstatus, %[sstatus] \n" - "sret \n" + __asm__ __volatile__("csrw sepc, %[sepc]\n" + "csrw sstatus, %[sstatus]\n" + "sret\n" : - : [sepc] "r"(USER_BASE), [sstatus] "r"(SSTATUS_SPIE)); + : [sepc] "r"(USER_BASE), + [sstatus] "r"(SSTATUS_SPIE | SSTATUS_SUM) // updated + ); } struct process *create_process(const void *image, size_t image_size) { @@ -380,6 +393,148 @@ void read_write_disk(void *buf, unsigned sector, int is_write) { memcpy(buf, blk_req->data, SECTOR_SIZE); } +// ===== Filesystem ===== +struct file files[FILES_MAX]; +uint8_t disk[DISK_MAX_SIZE]; + +int oct2int(char *oct, int len) { + int dec = 0; + for (int i = 0; i < len; i++) { + if (oct[i] < '0' || oct[i] > '7') + break; + + dec = dec * 8 + (oct[i] - '0'); + } + return dec; +} + +void fs_init(void) { + for (unsigned sector = 0; sector < sizeof(disk) / SECTOR_SIZE; sector++) + read_write_disk(&disk[sector * SECTOR_SIZE], sector, false); + + unsigned off = 0; + for (int i = 0; i < FILES_MAX; i++) { + struct tar_header *header = (struct tar_header *)&disk[off]; + if (header->name[0] == '\0') + break; + + if (strcmp(header->magic, "ustar") != 0) + PANIC("invalid tar header: magic=\"%s\"", header->magic); + + int filesz = oct2int(header->size, sizeof(header->size)); + struct file *file = &files[i]; + file->in_use = true; + strcpy(file->name, header->name); + memcpy(file->data, header->data, filesz); + file->size = filesz; + printf("file: %s, size=%d\n", file->name, file->size); + + off += align_up(sizeof(struct tar_header) + filesz, SECTOR_SIZE); + } +} +void fs_flush(void) { + memset(disk, 0, sizeof(disk)); + unsigned off = 0; + for (int file_i = 0; file_i < FILES_MAX; file_i++) { + struct file *file = &files[file_i]; + if (!file->in_use) + continue; + + struct tar_header *header = (struct tar_header *)&disk[off]; + memset(header, 0, sizeof(*header)); + strcpy(header->name, file->name); + strcpy(header->mode, "000644"); + strcpy(header->magic, "ustar"); + strcpy(header->version, "00"); + header->type = '0'; + + int filesz = file->size; + for (int i = sizeof(header->size); i > 0; i--) { + header->size[i - 1] = (filesz % 8) + '0'; + filesz /= 8; + } + + int checksum = ' ' * sizeof(header->checksum); + for (unsigned i = 0; i < sizeof(struct tar_header); i++) + checksum += (unsigned char)disk[off + i]; + + for (int i = 5; i >= 0; i--) { + header->checksum[i] = (checksum % 8) + '0'; + checksum /= 8; + } + + memcpy(header->data, file->data, file->size); + off += align_up(sizeof(struct tar_header) + file->size, SECTOR_SIZE); + } + + for (unsigned sector = 0; sector < sizeof(disk) / SECTOR_SIZE; sector++) + read_write_disk(&disk[sector * SECTOR_SIZE], sector, true); + + printf("wrote %d bytes to disk\n", sizeof(disk)); +} + +struct file *fs_lookup(const char *filename) { + for (int i = 0; i < FILES_MAX; i++) { + struct file *file = &files[i]; + if (!strcmp(file->name, filename)) + return file; + } + + return NULL; +} +// ===== System calls ===== +void handle_syscall(struct trap_frame *f) { + switch (f->a3) { + case SYS_PUTCHAR: + putchar(f->a0); + break; + case SYS_GETCHAR: + while (1) { + long ch = getchar(); + if (ch >= 0) { + f->a0 = ch; + break; + } + + yield(); + } + break; + case SYS_EXIT: + printf("process %d exited\n", current_proc->pid); + current_proc->state = PROC_EXITED; + yield(); + PANIC("unreachable"); + case SYS_READFILE: + case SYS_WRITEFILE: { + const char *filename = (const char *)f->a0; + char *buf = (char *)f->a1; + int len = f->a2; + struct file *file = fs_lookup(filename); + if (!file) { + printf("file not found: %s\n", filename); + f->a0 = -1; + break; + } + + if (len > (int)sizeof(file->data)) + len = file->size; + + if (f->a3 == SYS_WRITEFILE) { + memcpy(file->data, buf, len); + file->size = len; + fs_flush(); + } else { + memcpy(buf, file->data, len); + } + + f->a0 = len; + break; + } + default: + PANIC("unexpected syscall a3=%x\n", f->a3); + } +} + // ===== ENTRY POINT ===== void kernel_main(void) { memset(__bss, 0, (size_t)__bss_end - (size_t)__bss); @@ -389,13 +544,7 @@ 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); + fs_init(); idle_proc = create_process(NULL, 0); idle_proc->pid = -1; diff --git a/src/kernel.h b/src/kernel.h index 8e79dc7..3fc23f7 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -55,6 +55,7 @@ struct trap_frame { #define PROC_UNUSED 0 #define PROC_RUNNABLE 1 +#define PROC_EXITED 2 struct process { int pid; @@ -148,6 +149,41 @@ struct virtio_blk_req *blk_req; paddr_t blk_req_paddr; unsigned blk_capacity; +// ===== Filesystem ===== +#define FILES_MAX 2 +#define DISK_MAX_SIZE align_up(sizeof(struct file) * FILES_MAX, SECTOR_SIZE) + +struct tar_header { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char type; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; + char padding[12]; + char data[]; +} __attribute__((packed)); + +struct file { + bool in_use; + char name[100]; + char data[1024]; + size_t size; +}; + +// ===== System calls ===== +#define SCAUSE_ECALL 8 + // ===== Macros ===== #define READ_CSR(reg) \ ({ \ diff --git a/src/lorem.txt b/src/lorem.txt deleted file mode 100644 index e872814..0000000 Binary files a/src/lorem.txt and /dev/null differ diff --git a/src/run.bat b/src/run.bat index fcb2b0e..e54cef5 100644 --- a/src/run.bat +++ b/src/run.bat @@ -1,6 +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 -drive id=drive0,file=lorem.txt,format=raw,if=none -device virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0 -kernel %~dp0kernel.elf +qemu-system-riscv32.exe -machine virt -bios default -nographic -serial mon:stdio --no-reboot -drive id=drive0,file=disk.tar,format=raw,if=none -device virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0 -kernel %~dp0kernel.elf diff --git a/src/run.sh b/src/run.sh index e97f37c..b84dc5b 100755 --- a/src/run.sh +++ b/src/run.sh @@ -1,4 +1,4 @@ #!/bin/bash set -xue -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 +make kernel && qemu-system-riscv32 -machine virt -bios default -nographic -serial mon:stdio --no-reboot -drive id=drive0,file=disk.tar,format=raw,if=none -device virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0 -kernel kernel.elf diff --git a/src/shell.c b/src/shell.c index 0952729..de055a8 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1,6 +1,37 @@ #include "user.h" void main(void) { - for (;;) - ; + while (1) { + prompt: + printf("> "); + char cmdline[128]; + for (int i = 0;; i++) { + char ch = getchar(); + putchar(ch); + if (i == sizeof(cmdline) - 1) { + printf("command line too long\n"); + goto prompt; + } else if (ch == '\r') { + printf("\n"); + cmdline[i] = '\0'; + break; + } else { + cmdline[i] = ch; + } + } + + if (strcmp(cmdline, "hello") == 0) + printf("Hello world from shell!\n"); + else if (strcmp(cmdline, "exit") == 0) + exit(); + else if (strcmp(cmdline, "readfile") == 0) { + char buf[128]; + int len = readfile("./hello.txt", buf, sizeof(buf)); + buf[len] = '\0'; + printf("%s\n", buf); + } else if (strcmp(cmdline, "writefile") == 0) + writefile("./hello.txt", "Hello from shell!\n", 19); + else + printf("unknown command: %s\n", cmdline); + } } diff --git a/src/user.c b/src/user.c index a8f4710..eaf731e 100644 --- a/src/user.c +++ b/src/user.c @@ -2,17 +2,41 @@ extern char __stack_top[]; +int syscall(int sysno, int arg0, int arg1, int arg2) { + register int a0 __asm__("a0") = arg0; + register int a1 __asm__("a1") = arg1; + register int a2 __asm__("a2") = arg2; + register int a3 __asm__("a3") = sysno; + + __asm__ __volatile__("ecall" + : "=r"(a0) + : "r"(a0), "r"(a1), "r"(a2), "r"(a3) + : "memory"); + + return a0; +} + +void putchar(char ch) { syscall(SYS_PUTCHAR, ch, 0, 0); } + +int getchar(void) { return syscall(SYS_GETCHAR, 0, 0, 0); } + +int readfile(const char *filename, char *buf, int len) { + return syscall(SYS_READFILE, (int)filename, (int)buf, len); +} + +int writefile(const char *filename, const char *buf, int len) { + return syscall(SYS_WRITEFILE, (int)filename, (int)buf, len); +} + __attribute__((noreturn)) void exit(void) { + syscall(SYS_EXIT, 0, 0, 0); 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)); + __asm__ __volatile__("mv sp, %[stack_top]\n" + "call main\n" + "call exit\n" ::[stack_top] "r"(__stack_top)); } diff --git a/src/user.h b/src/user.h index c28ef51..6a1a99b 100644 --- a/src/user.h +++ b/src/user.h @@ -2,5 +2,14 @@ #include "common.h" -__attribute__((noreturn)) void exit(void); +struct sysret { + int a0; + int a1; + int a2; +}; + void putchar(char ch); +int getchar(void); +int readfile(const char *filename, char *buf, int len); +int writefile(const char *filename, const char *buf, int len); +__attribute__((noreturn)) void exit(void);