From 298d1be60ce2300b006146a7e117c82b86248fa1 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 21 Sep 2022 16:53:45 +0300 Subject: [PATCH 01/56] Initial commit --- Makefile | 12 ++++++++++++ README.md | 1 + mbr.S | 28 ++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 mbr.S diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..79aec12 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +AS=x86_64-elf-as +LD=x86_64-elf-ld + +run: mbr.bin + qemu-system-i386 -drive format=raw,file=$< + +%.o: %.S + $(AS) $^ -o $@ + +mbr.bin: mbr.o + $(LD) -Ttext=0x7c00 --oformat=binary $^ -o $@ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed69b2f --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Yet Another BootLoader, OS Kernel and Other stuff diff --git a/mbr.S b/mbr.S new file mode 100644 index 0000000..4aceb13 --- /dev/null +++ b/mbr.S @@ -0,0 +1,28 @@ + .code16 + .global _start +_start: #точка входа + mov $banner, %si + call print_string + + jmp . // loop forever + + +print_string: + mov $0x0e, %ah // "teletype output" +repeat: + lodsb // equivalent to mov (%si), %al; inc %si + + test %al, %al + je done + + int $0x10 // bios interrupt + jmp repeat +done: + ret + + . = _start + 256 # pad to 256 bytes +banner: + .asciz "YABLOKO bootloader started\r\n" + + . = _start + 510 # pad to 510 bytes + .byte 0x55, 0xaa # boot sector magic value From 1cc3169cd09ee227d8debd42e3b780bc11c41b4a Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 21 Sep 2022 17:37:27 +0300 Subject: [PATCH 02/56] Add targets "debug" and "clean". --- Makefile | 10 ++++++++++ mbr.S | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 79aec12..3be925f 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,19 @@ LD=x86_64-elf-ld run: mbr.bin qemu-system-i386 -drive format=raw,file=$< +debug: mbr.bin + qemu-system-i386 -drive format=raw,file=$< -s -S & + i386-elf-gdb \ + -ex "target remote localhost:1234" \ + -ex "set architecture i8086" \ + -ex "break *0x7c00" \ + -ex "continue" + %.o: %.S $(AS) $^ -o $@ mbr.bin: mbr.o $(LD) -Ttext=0x7c00 --oformat=binary $^ -o $@ +clean: + rm *.bin *.o diff --git a/mbr.S b/mbr.S index 4aceb13..419ec05 100644 --- a/mbr.S +++ b/mbr.S @@ -1,6 +1,6 @@ .code16 .global _start -_start: #точка входа +_start: mov $banner, %si call print_string From dbaad4cf880f7c919994836cc4815078c7ec5d18 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 21 Sep 2022 18:25:06 +0300 Subject: [PATCH 03/56] Add dummy kernel. --- Makefile | 14 +++++++++-- kernel.c | 7 ++++++ mbr.S | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 kernel.c diff --git a/Makefile b/Makefile index 3be925f..e5bb583 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ AS=x86_64-elf-as LD=x86_64-elf-ld +CC=x86_64-elf-gcc -run: mbr.bin +run: image.bin qemu-system-i386 -drive format=raw,file=$< -debug: mbr.bin +debug: image.bin qemu-system-i386 -drive format=raw,file=$< -s -S & i386-elf-gdb \ -ex "target remote localhost:1234" \ @@ -12,6 +13,15 @@ debug: mbr.bin -ex "break *0x7c00" \ -ex "continue" +image.bin: mbr.bin kernel.bin + cat $^ >$@ + +kernel.bin: kernel.o + $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary + +%.o: %.c + $(CC) -m32 -ffreestanding -c $< -o $@ + %.o: %.S $(AS) $^ -o $@ diff --git a/kernel.c b/kernel.c new file mode 100644 index 0000000..a1e5157 --- /dev/null +++ b/kernel.c @@ -0,0 +1,7 @@ +asm("jmp main"); + +int main() { + char* video_memory = (char*) 0xb8000; + *video_memory = 'X'; + return 0; +} diff --git a/mbr.S b/mbr.S index 419ec05..1b51de8 100644 --- a/mbr.S +++ b/mbr.S @@ -1,12 +1,56 @@ .code16 .global _start _start: + mov %dl, boot_drive mov $banner, %si call print_string + call load_kernel + call switch_to_32bit + jmp . // loop forever +.equ KERNEL_OFFSET, 0x1000 +load_kernel: + mov $2, %ah // read mode + mov $2, %al // sectors to read + mov $2, %cl // start from sector 2 + mov $0, %ch // cylinder 0 + mov $0, %dh // head 0 + mov $KERNEL_OFFSET, %bx // bx -> destination + mov boot_drive, %dl // dl -> disk + int $0x13 + // no error checking, let's hope it works + ret + + +switch_to_32bit: + cli // 1. disable interrupts + lgdt gdt_descriptor // 2. load GDT descriptor + mov %cr0, %eax + or $1, %eax // 3. enable protected mode + mov %eax, %cr0 + ljmp $CODE_SEG, $init_32bit // 4. far jump + + +.code32 +init_32bit: + mov $DATA_SEG, %ax // 5. update segment registers + mov %ax, %ds + mov %ax, %ss + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + mov $0x90000, %ebp // 6. setup stack + mov %ebp, %esp + + call KERNEL_OFFSET // 7. jump to the kernel + jmp . // 8. loop forever + + +.code16 print_string: mov $0x0e, %ah // "teletype output" repeat: @@ -21,8 +65,41 @@ done: ret . = _start + 256 # pad to 256 bytes +boot_drive: + .byte 0 banner: .asciz "YABLOKO bootloader started\r\n" +gdt_start: + .quad 0x0 // null descriptor + +// code segment descriptor +gdt_code: + .word 0xffff // segment length, bits 0-15 + .word 0x0 // segment base, bits 0-15 + .byte 0x0 // segment base, bits 16-23 + .byte 0b10011010 // flags (8 bits) + .byte 0b11001111 // flags (4 bits) + segment length, bits 16-19 + .byte 0x0 // segment base, bits 24-31 + +// data segment descriptor +gdt_data: + .word 0xffff // segment length, bits 0-15 + .word 0x0 // segment base, bits 0-15 + .byte 0x0 // segment base, bits 16-23 + .byte 0b10010010 // flags (8 bits) + .byte 0b11001111 // flags (4 bits) + segment length, bits 16-19 + .byte 0x0 // segment base, bits 24-31 + +gdt_end: + +// GDT descriptor +gdt_descriptor: + .word gdt_end - gdt_start - 1 // size (16 bit) + .int gdt_start // address (32 bit) + +.equ CODE_SEG, gdt_code - gdt_start +.equ DATA_SEG, gdt_data - gdt_start + . = _start + 510 # pad to 510 bytes .byte 0x55, 0xaa # boot sector magic value From d95ab832c3351ba2cc61782a6eee3c74298c1684 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 14 Nov 2022 00:23:42 +0300 Subject: [PATCH 04/56] Add vga_print_string. --- Makefile | 2 +- kernel.c | 6 ++++-- port.h | 11 +++++++++++ vga.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ vga.h | 5 +++++ 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 port.h create mode 100644 vga.c create mode 100644 vga.h diff --git a/Makefile b/Makefile index e5bb583..65b4b3d 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ debug: image.bin image.bin: mbr.bin kernel.bin cat $^ >$@ -kernel.bin: kernel.o +kernel.bin: kernel.o vga.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary %.o: %.c diff --git a/kernel.c b/kernel.c index a1e5157..52fa679 100644 --- a/kernel.c +++ b/kernel.c @@ -1,7 +1,9 @@ asm("jmp main"); +#include "vga.h" + int main() { - char* video_memory = (char*) 0xb8000; - *video_memory = 'X'; + vga_clear_screen(); + vga_print_string("hello world"); return 0; } diff --git a/port.h b/port.h new file mode 100644 index 0000000..edfdf90 --- /dev/null +++ b/port.h @@ -0,0 +1,11 @@ +#pragma once + +static unsigned char port_byte_in(unsigned short port) { + unsigned char result; + __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); + return result; +} + +static void port_byte_out(unsigned short port, unsigned char data) { + __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); +} diff --git a/vga.c b/vga.c new file mode 100644 index 0000000..c3d10bb --- /dev/null +++ b/vga.c @@ -0,0 +1,51 @@ +#include "port.h" + +char* const video_memory = (char*) 0xb8000; + +enum { + LINES = 25, + COLS = 80, + WHITE_ON_BLACK = 0x0f, + + VGA_CTRL_REGISTER = 0x3d4, + VGA_DATA_REGISTER = 0x3d5, + VGA_OFFSET_LOW = 0x0f, + VGA_OFFSET_HIGH = 0x0e, +}; + +void vga_set_cursor(unsigned offset) { + port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH); + port_byte_out(VGA_DATA_REGISTER, (unsigned char) (offset >> 8)); + port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_LOW); + port_byte_out(VGA_DATA_REGISTER, (unsigned char) (offset & 0xff)); +} + +unsigned vga_get_cursor() { + port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH); + unsigned offset = port_byte_in(VGA_DATA_REGISTER) << 8; + port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_LOW); + offset += port_byte_in(VGA_DATA_REGISTER); + return offset; +} + +void vga_set_char(unsigned offset, char c) { + video_memory[2 * offset] = c; + video_memory[2 * offset + 1] = WHITE_ON_BLACK; +} + +void vga_clear_screen() { + for (unsigned i = 0; i < LINES * COLS; ++i) { + vga_set_char(i, ' '); + } + vga_set_cursor(0); +} + +void vga_print_string(const char* s) { + int offset = vga_get_cursor(); + while (*s != 0) { + vga_set_char(offset, *s); + s++; + offset++; + } + vga_set_cursor(offset); +} diff --git a/vga.h b/vga.h new file mode 100644 index 0000000..4f09b11 --- /dev/null +++ b/vga.h @@ -0,0 +1,5 @@ +#pragma once + +void vga_clear_screen(); + +void vga_print_string(const char* s); From 0be6fbed9d45ce27a1ec396efbe78ff0d5df5764 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 14 Nov 2022 01:01:22 +0300 Subject: [PATCH 05/56] Scrolling support. --- Makefile | 2 +- kernel.c | 4 +++- string.c | 16 +++++++++++++++ string.h | 5 +++++ vga.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 string.c create mode 100644 string.h diff --git a/Makefile b/Makefile index 65b4b3d..36e2470 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ debug: image.bin image.bin: mbr.bin kernel.bin cat $^ >$@ -kernel.bin: kernel.o vga.o +kernel.bin: kernel.o vga.o string.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary %.o: %.c diff --git a/kernel.c b/kernel.c index 52fa679..181cc7f 100644 --- a/kernel.c +++ b/kernel.c @@ -4,6 +4,8 @@ asm("jmp main"); int main() { vga_clear_screen(); - vga_print_string("hello world"); + for (int i = 0; i < 24; i++) { + vga_print_string("hello world\n"); + } return 0; } diff --git a/string.c b/string.c new file mode 100644 index 0000000..bb71bff --- /dev/null +++ b/string.c @@ -0,0 +1,16 @@ +#include "string.h" + +void kmemmove(char* dst, char* src, size_t size) { + if (dst == src) return; + if (dst > src && dst < src + size) { // s d + // copy right-to-left + for (; size != 0; size--) { + dst[size - 1] = src[size - 1]; + } + } else { + // copy left-to-right + for (size_t i = 0; i < size; ++i) { + dst[i] = src[i]; + } + } +} diff --git a/string.h b/string.h new file mode 100644 index 0000000..9d8b0de --- /dev/null +++ b/string.h @@ -0,0 +1,5 @@ +#pragma once + +typedef unsigned size_t; + +void kmemmove(char* dst, char* src, size_t size); diff --git a/vga.c b/vga.c index c3d10bb..18e9619 100644 --- a/vga.c +++ b/vga.c @@ -1,11 +1,34 @@ #include "port.h" +#include "string.h" char* const video_memory = (char*) 0xb8000; +enum colors16 { + black = 0, + blue, + green, + cyan, + red, + magenta, + brown, + light_gray, + dark_gray, + light_blue, + light_green, + light_cyan, + light_red, + light_magenta, + yellow, + white, +}; + +static unsigned char get_color(unsigned char fg, unsigned char bg) { + return (bg << 4) + fg; +} + enum { - LINES = 25, + ROWS = 25, COLS = 80, - WHITE_ON_BLACK = 0x0f, VGA_CTRL_REGISTER = 0x3d4, VGA_DATA_REGISTER = 0x3d5, @@ -13,6 +36,14 @@ enum { VGA_OFFSET_HIGH = 0x0e, }; +static unsigned get_offset(unsigned col, unsigned row) { + return row * COLS + col; +} + +static unsigned get_row_from_offset(unsigned offset) { + return offset / COLS; +} + void vga_set_cursor(unsigned offset) { port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH); port_byte_out(VGA_DATA_REGISTER, (unsigned char) (offset >> 8)); @@ -30,22 +61,37 @@ unsigned vga_get_cursor() { void vga_set_char(unsigned offset, char c) { video_memory[2 * offset] = c; - video_memory[2 * offset + 1] = WHITE_ON_BLACK; + video_memory[2 * offset + 1] = get_color(light_gray, black); } void vga_clear_screen() { - for (unsigned i = 0; i < LINES * COLS; ++i) { + for (unsigned i = 0; i < ROWS * COLS; ++i) { vga_set_char(i, ' '); } vga_set_cursor(0); } +static unsigned scroll() { + kmemmove(video_memory, video_memory + COLS, 2 * COLS * (ROWS-1)); + for (int col = 0; col < COLS; col++) { + vga_set_char(get_offset(col, ROWS - 1), ' '); + } + return get_offset(0, ROWS - 1); +} + void vga_print_string(const char* s) { - int offset = vga_get_cursor(); + unsigned offset = vga_get_cursor(); while (*s != 0) { - vga_set_char(offset, *s); + if (*s == '\n') { + offset = get_offset(0, get_row_from_offset(offset) + 1); + } else { + vga_set_char(offset, *s); + offset++; + } s++; - offset++; + if (offset > COLS * ROWS) { + offset = scroll(); + } } vga_set_cursor(offset); } From e31e56f8f9419eeccd12a6af65b621b442081d69 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Thu, 17 Nov 2022 22:57:04 +0300 Subject: [PATCH 06/56] Steal ata driver. --- drivers/ata.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/ata.h | 2 ++ 2 files changed, 82 insertions(+) create mode 100644 drivers/ata.c create mode 100644 drivers/ata.h diff --git a/drivers/ata.c b/drivers/ata.c new file mode 100644 index 0000000..2fe161c --- /dev/null +++ b/drivers/ata.c @@ -0,0 +1,80 @@ +// stolen from https://github.com/dhavalhirdhav/LearnOS/blob/master/drivers/ata/ata.c + +#include + +#include "ata.h" +#include "../ports.h" + +/* + BSY: a 1 means that the controller is busy executing a command. No register should be accessed (except the digital output register) while this bit is set. +RDY: a 1 means that the controller is ready to accept a command, and the drive is spinning at correct speed.. +WFT: a 1 means that the controller detected a write fault. +SKC: a 1 means that the read/write head is in position (seek completed). +DRQ: a 1 means that the controller is expecting data (for a write) or is sending data (for a read). Don't access the data register while this bit is 0. +COR: a 1 indicates that the controller had to correct data, by using the ECC bytes (error correction code: extra bytes at the end of the sector that allows to verify its integrity and, sometimes, to correct errors). +IDX: a 1 indicates the the controller retected the index mark (which is not a hole on hard-drives). +ERR: a 1 indicates that an error occured. An error code has been placed in the error register. +*/ + +#define STATUS_BSY 0x80 +#define STATUS_RDY 0x40 +#define STATUS_DRQ 0x08 +#define STATUS_DF 0x20 +#define STATUS_ERR 0x01 + +//This is really specific to out OS now, assuming ATA bus 0 master +//Source - OsDev wiki +static void ATA_wait_BSY(); +static void ATA_wait_DRQ(); +void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count) +{ + + ATA_wait_BSY(); + port_byte_out(0x1F6,0xE0 | ((LBA >>24) & 0xF)); + port_byte_out(0x1F2,sector_count); + port_byte_out(0x1F3, (uint8_t) LBA); + port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F7,0x20); //Send the read command + + uint16_t *target = (uint16_t*) target_address; + + for (int j =0;j>24) & 0xF)); + port_byte_out(0x1F2,sector_count); + port_byte_out(0x1F3, (uint8_t) LBA); + port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F7,0x30); //Send the write command + + for (int j =0;j Date: Thu, 17 Nov 2022 23:16:57 +0300 Subject: [PATCH 07/56] Use ELF kernel image. --- Makefile | 8 ++++---- drivers/ata.c | 2 +- elf.h | 38 ++++++++++++++++++++++++++++++++++++++ kernel.c | 5 +---- mbr.S | 40 +++++++++++++++++++++++++++++++++++++--- port.h | 10 ++++++++++ 6 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 elf.h diff --git a/Makefile b/Makefile index 36e2470..2aa02de 100644 --- a/Makefile +++ b/Makefile @@ -7,17 +7,17 @@ run: image.bin debug: image.bin qemu-system-i386 -drive format=raw,file=$< -s -S & - i386-elf-gdb \ + x86_64-elf-gdb \ -ex "target remote localhost:1234" \ -ex "set architecture i8086" \ - -ex "break *0x7c00" \ + -ex "break *0x7c5d" \ -ex "continue" image.bin: mbr.bin kernel.bin cat $^ >$@ -kernel.bin: kernel.o vga.o string.o - $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary +kernel.bin: kernel.o vga.o string.o drivers/ata.o + $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ %.o: %.c $(CC) -m32 -ffreestanding -c $< -o $@ diff --git a/drivers/ata.c b/drivers/ata.c index 2fe161c..8574c79 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -3,7 +3,7 @@ #include #include "ata.h" -#include "../ports.h" +#include "../port.h" /* BSY: a 1 means that the controller is busy executing a command. No register should be accessed (except the digital output register) while this bit is set. diff --git a/elf.h b/elf.h new file mode 100644 index 0000000..3f46100 --- /dev/null +++ b/elf.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +enum { + EI_NIDENT = 16, +}; + +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + uint32_t p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +} Elf32_Phdr; diff --git a/kernel.c b/kernel.c index 181cc7f..e9959e9 100644 --- a/kernel.c +++ b/kernel.c @@ -1,11 +1,8 @@ -asm("jmp main"); - #include "vga.h" -int main() { +void _start() { vga_clear_screen(); for (int i = 0; i < 24; i++) { vga_print_string("hello world\n"); } - return 0; } diff --git a/mbr.S b/mbr.S index 1b51de8..b4104a8 100644 --- a/mbr.S +++ b/mbr.S @@ -11,11 +11,39 @@ _start: jmp . // loop forever +.equ ELF32_ENTRY_OFFSET, 0x18 +.equ ELF32_PHDR_OFFSET, 0x1c +.equ ELF32_PHDR_FILESZ_OFFSET, 4*4 .equ KERNEL_OFFSET, 0x1000 + +.equ MBR_SECTORS, 1 +.equ SECTOR_BASE, 1 +.equ ELFHDR_SECTORS, 8 + +.equ SECTOR_SIZE, 512 +.equ SECTOR_SHIFT, 9 + load_kernel: + mov $1, %al // sectors to read + mov $SECTOR_BASE + MBR_SECTORS, %cl // start after MBR + call bios_disk_read + + mov KERNEL_OFFSET + ELF32_ENTRY_OFFSET, %si + mov %si, entry // store entry point + + mov KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di + mov KERNEL_OFFSET + ELF32_PHDR_FILESZ_OFFSET(%di), %ax + sub $0x1000, %ax // we won't load the header + add $SECTOR_SIZE - 1, %ax + shr $SECTOR_SHIFT, %ax // round up to sector count + + mov $SECTOR_BASE + MBR_SECTORS + ELFHDR_SECTORS, %cl //start after ELF header + call bios_disk_read + ret + +bios_disk_read: + // expects %al to specify number of sectors, %cl the initial sector mov $2, %ah // read mode - mov $2, %al // sectors to read - mov $2, %cl // start from sector 2 mov $0, %ch // cylinder 0 mov $0, %dh // head 0 mov $KERNEL_OFFSET, %bx // bx -> destination @@ -46,7 +74,8 @@ init_32bit: mov $0x90000, %ebp // 6. setup stack mov %ebp, %esp - call KERNEL_OFFSET // 7. jump to the kernel + movzwl entry, %esi + call *%esi // 7. jump to the kernel jmp . // 8. loop forever @@ -70,6 +99,11 @@ boot_drive: banner: .asciz "YABLOKO bootloader started\r\n" + .balign 2 +entry: + .word 0 + + .balign 4 gdt_start: .quad 0x0 // null descriptor diff --git a/port.h b/port.h index edfdf90..2cbb9d0 100644 --- a/port.h +++ b/port.h @@ -6,6 +6,16 @@ static unsigned char port_byte_in(unsigned short port) { return result; } +static unsigned short port_word_in(unsigned short port) { + unsigned short result; + __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); + return result; +} + static void port_byte_out(unsigned short port, unsigned char data) { __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); } + +static void port_long_out(unsigned short port, unsigned int data) { + __asm__("out %%eax, %%dx" : : "a" (data), "d" (port)); +} From 8a8fb959659b1512528fe58687615c3ce897fdee Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 18 Nov 2022 18:33:40 +0300 Subject: [PATCH 08/56] Enable debug info. --- Makefile | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 2aa02de..b9bd7ea 100644 --- a/Makefile +++ b/Makefile @@ -5,12 +5,19 @@ CC=x86_64-elf-gcc run: image.bin qemu-system-i386 -drive format=raw,file=$< +debug-boot: image.bin mbr.elf + qemu-system-i386 -drive format=raw,file=$< -s -S & + x86_64-elf-gdb mbr.elf \ + -ex "set architecture i386" \ + -ex "target remote localhost:1234" \ + -ex "break init_32bit" \ + -ex "continue" + debug: image.bin qemu-system-i386 -drive format=raw,file=$< -s -S & - x86_64-elf-gdb \ + x86_64-elf-gdb kernel.bin \ -ex "target remote localhost:1234" \ - -ex "set architecture i8086" \ - -ex "break *0x7c5d" \ + -ex "break _start" \ -ex "continue" image.bin: mbr.bin kernel.bin @@ -20,13 +27,16 @@ kernel.bin: kernel.o vga.o string.o drivers/ata.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ %.o: %.c - $(CC) -m32 -ffreestanding -c $< -o $@ + $(CC) -m32 -ffreestanding -c -g $< -o $@ %.o: %.S - $(AS) $^ -o $@ + $(AS) $^ -g -o $@ mbr.bin: mbr.o $(LD) -Ttext=0x7c00 --oformat=binary $^ -o $@ +mbr.elf: mbr.o + $(LD) -Ttext=0x7c00 $^ -o $@ + clean: rm *.bin *.o From 24e807423cb1e9d3fd3d7f21eb215d4eff3bb9da Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 18 Nov 2022 19:27:04 +0300 Subject: [PATCH 09/56] Fix binary architectures. --- Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index b9bd7ea..826ea27 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,6 @@ run: image.bin debug-boot: image.bin mbr.elf qemu-system-i386 -drive format=raw,file=$< -s -S & x86_64-elf-gdb mbr.elf \ - -ex "set architecture i386" \ -ex "target remote localhost:1234" \ -ex "break init_32bit" \ -ex "continue" @@ -30,13 +29,13 @@ kernel.bin: kernel.o vga.o string.o drivers/ata.o $(CC) -m32 -ffreestanding -c -g $< -o $@ %.o: %.S - $(AS) $^ -g -o $@ + $(CC) -m32 $^ -c -g -o $@ mbr.bin: mbr.o - $(LD) -Ttext=0x7c00 --oformat=binary $^ -o $@ + $(LD) -m elf_i386 -Ttext=0x7c00 --oformat=binary $^ -o $@ mbr.elf: mbr.o - $(LD) -Ttext=0x7c00 $^ -o $@ + $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm *.bin *.o + rm -f *.bin *.o From 7e678618d1dcc7c29b7dbdeaa72f699579867d83 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 18 Nov 2022 22:58:49 +0300 Subject: [PATCH 10/56] Add fs.h. --- Makefile | 12 ++++++-- drivers/ata.c | 22 +++++++------- drivers/ata.h | 3 ++ drivers/misc.h | 8 ++++++ fs/fs.h | 50 ++++++++++++++++++++++++++++++++ kernel.c | 16 +++++++++-- mbr.S | 2 +- port.h | 8 ++++-- tools/mkfs.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 179 insertions(+), 20 deletions(-) create mode 100644 drivers/misc.h create mode 100644 fs/fs.h create mode 100644 tools/mkfs.c diff --git a/Makefile b/Makefile index 826ea27..b780b65 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,10 @@ debug: image.bin -ex "break _start" \ -ex "continue" -image.bin: mbr.bin kernel.bin +fs.img: kernel.bin tools/mkfs + tools/mkfs $@ $< + +image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: kernel.o vga.o string.o drivers/ata.o @@ -29,7 +32,7 @@ kernel.bin: kernel.o vga.o string.o drivers/ata.o $(CC) -m32 -ffreestanding -c -g $< -o $@ %.o: %.S - $(CC) -m32 $^ -c -g -o $@ + $(AS) --32 -g $^ -o $@ mbr.bin: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 --oformat=binary $^ -o $@ @@ -38,4 +41,7 @@ mbr.elf: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm -f *.bin *.o + rm -f *.bin *.o tools/mkfs + +tools/%: tools/%.c + gcc -Wall -Werror -g $^ -o $@ diff --git a/drivers/ata.c b/drivers/ata.c index 8574c79..693dac5 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -22,7 +22,7 @@ ERR: a 1 indicates that an error occured. An error code has been placed in the e #define STATUS_DF 0x20 #define STATUS_ERR 0x01 -//This is really specific to out OS now, assuming ATA bus 0 master +//This is really specific to our OS now, assuming ATA bus 0 master //Source - OsDev wiki static void ATA_wait_BSY(); static void ATA_wait_DRQ(); @@ -30,22 +30,22 @@ void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_ { ATA_wait_BSY(); - port_byte_out(0x1F6,0xE0 | ((LBA >>24) & 0xF)); - port_byte_out(0x1F2,sector_count); + port_byte_out(0x1F6, 0xE0 | ((LBA >>24) & 0xF)); + port_byte_out(0x1F2, sector_count); port_byte_out(0x1F3, (uint8_t) LBA); port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); - port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); - port_byte_out(0x1F7,0x20); //Send the read command + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F7, 0x20); //Send the read command uint16_t *target = (uint16_t*) target_address; - for (int j =0;j> 8)); - port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); port_byte_out(0x1F7,0x30); //Send the write command for (int j =0;j + void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count); void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t* bytes); diff --git a/drivers/misc.h b/drivers/misc.h new file mode 100644 index 0000000..ff6ee08 --- /dev/null +++ b/drivers/misc.h @@ -0,0 +1,8 @@ +#pragma once +#include "../port.h" + +__attribute__((noreturn)) +static inline void qemu_shutdown() { + port_word_out(0x604, 0x2000); + while(1); +} diff --git a/fs/fs.h b/fs/fs.h new file mode 100644 index 0000000..8de61a3 --- /dev/null +++ b/fs/fs.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +/* Directory structure: + + 32-byte entries +┌───────────────────────────────┐ +│Reserved │ +│char[32] │ +├──────┬──────┬────────┬────────┤ +│Offset│Size │Reserved│Name │ +│uint32│uint32│uint32 │char[20]│ +├──────┼──────┼────────┼────────┤ +│ ... │ │ │ │ + +Offset is in sectors (zero-based), +size is in bytes, name is 0-terminated. + +*/ + +enum { + sector_size = 512, +}; + +struct dirent { + uint32_t offset_sectors; + uint32_t size_bytes; + uint32_t reserved; + char name[20]; +}; + +struct dir { + char reserved[32]; + struct dirent entries[15]; +}; + +struct stat { + uint32_t size; + uint32_t reserved[3]; +}; + +/* Find file by name and fill information in buf. + * Returns zero on success, nonzero on failure. */ +int stat(const char* name, struct stat *buf); + +/* Find file by name and read it into buffer with size bufsize. + * At most (bufsize & ~511) bytes will be read. + * Return number of bytes read or -1 on failure. */ +int read_file(const char* name, void* buf, uint32_t bufsize); diff --git a/kernel.c b/kernel.c index e9959e9..c17ed02 100644 --- a/kernel.c +++ b/kernel.c @@ -1,8 +1,18 @@ +asm(".asciz \"kernel start\""); + #include "vga.h" +#include "drivers/ata.h" +#include "drivers/misc.h" void _start() { + char buf[512]; + vga_clear_screen(); - for (int i = 0; i < 24; i++) { - vga_print_string("hello world\n"); - } + vga_print_string("YABLOKO\n"); + + read_sectors_ATA_PIO((uint32_t)buf, 10, 1); + vga_print_string(buf); + + asm("hlt"); + qemu_shutdown(); } diff --git a/mbr.S b/mbr.S index b4104a8..a2c098a 100644 --- a/mbr.S +++ b/mbr.S @@ -16,7 +16,7 @@ _start: .equ ELF32_PHDR_FILESZ_OFFSET, 4*4 .equ KERNEL_OFFSET, 0x1000 -.equ MBR_SECTORS, 1 +.equ MBR_SECTORS, 2 .equ SECTOR_BASE, 1 .equ ELFHDR_SECTORS, 8 diff --git a/port.h b/port.h index 2cbb9d0..283418b 100644 --- a/port.h +++ b/port.h @@ -13,9 +13,13 @@ static unsigned short port_word_in(unsigned short port) { } static void port_byte_out(unsigned short port, unsigned char data) { - __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); + __asm__("outb %%al, %%dx" : : "a" (data), "d" (port)); +} + +static void port_word_out(unsigned short port, unsigned short data) { + __asm__("outw %%ax, %%dx" : : "a" (data), "d" (port)); } static void port_long_out(unsigned short port, unsigned int data) { - __asm__("out %%eax, %%dx" : : "a" (data), "d" (port)); + __asm__("outl %%eax, %%dx" : : "a" (data), "d" (port)); } diff --git a/tools/mkfs.c b/tools/mkfs.c new file mode 100644 index 0000000..40f0485 --- /dev/null +++ b/tools/mkfs.c @@ -0,0 +1,78 @@ +#include "../fs/fs.h" + +#include +#include + +char* basename(char* path) { + char* c = strrchr(path, '/'); + if (c && *c) { + return c + 1; + } + return path; +} + +int main(int argc, char* argv[]) { + char sector[sector_size]; + struct dir dir = {{0}}; + + if (argc < 3) { + fprintf(stderr, "Usage: %s OUT.FS KERNEL.BIN [FILES...]\n", argv[0]); + return 1; + } + + FILE* image = fopen(argv[1], "wb"); + if (!image) { + perror(argv[1]); + return 1; + } + + if (fwrite(&dir, sizeof(dir), 1, image) < 1) { + perror("fwrite"); + return 1; + } + uint32_t sector_offset = 1; + + for (int i = 2; i < argc; ++i) { + char* name = argv[i]; + struct dirent *dirent = &dir.entries[i-2]; + dirent->offset_sectors = sector_offset; + dirent->size_bytes = 0; + + FILE* file = fopen(name, "rb"); + if (!file) { + perror(name); + return 1; + } + + size_t read_size; + while ((read_size = fread(sector, 1, sizeof(sector), file))) { + if (fwrite(sector, 1, sizeof(sector), image) != sizeof(sector)) { + perror(name); + return 1; + } + sector_offset++; + dirent->size_bytes += read_size; + } + + if (fclose(file)) { + perror(name); + return 1; + } + + dirent->reserved = 0; + strlcpy(dirent->name, basename(name), sizeof(dirent->name)); + } + + fseek(image, 0, SEEK_SET); + if (fwrite(&dir, sizeof(dir), 1, image) < 1) { + perror("fwrite"); + return 1; + } + + if (fclose(image)) { + perror(argv[0]); + return 1; + } + + return 0; +} From e40bde036de6f0875ab43c221dc7f19ed907f8c0 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 19 Nov 2022 22:02:37 +0300 Subject: [PATCH 11/56] Enable A20. --- mbr.S | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mbr.S b/mbr.S index a2c098a..6471c95 100644 --- a/mbr.S +++ b/mbr.S @@ -54,8 +54,11 @@ bios_disk_read: switch_to_32bit: + mov $2, %al + out %al, $0x92 // enable A20 + cli // 1. disable interrupts - lgdt gdt_descriptor // 2. load GDT descriptor + lgdt gdt_descriptor // 2. load GDT descriptor mov %cr0, %eax or $1, %eax // 3. enable protected mode mov %eax, %cr0 From f29ddf75d42aa59e0a8d27af8099f8af011af53b Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sun, 20 Nov 2022 16:06:32 +0300 Subject: [PATCH 12/56] Move vga.* to drivers/. --- Makefile | 4 ++-- drivers/ata.c | 2 +- drivers/misc.h | 2 +- port.h => drivers/port.h | 0 vga.c => drivers/vga.c | 2 +- vga.h => drivers/vga.h | 0 kernel.c | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename port.h => drivers/port.h (100%) rename vga.c => drivers/vga.c (98%) rename vga.h => drivers/vga.h (100%) diff --git a/Makefile b/Makefile index b780b65..9e93572 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ fs.img: kernel.bin tools/mkfs image.bin: mbr.bin fs.img cat $^ >$@ -kernel.bin: kernel.o vga.o string.o drivers/ata.o +kernel.bin: kernel.o drivers/vga.o string.o drivers/ata.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ %.o: %.c @@ -41,7 +41,7 @@ mbr.elf: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm -f *.bin *.o tools/mkfs + rm -f *.elf *.bin *.o tools/mkfs tools/%: tools/%.c gcc -Wall -Werror -g $^ -o $@ diff --git a/drivers/ata.c b/drivers/ata.c index 693dac5..5abee68 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -3,7 +3,7 @@ #include #include "ata.h" -#include "../port.h" +#include "port.h" /* BSY: a 1 means that the controller is busy executing a command. No register should be accessed (except the digital output register) while this bit is set. diff --git a/drivers/misc.h b/drivers/misc.h index ff6ee08..0d790d7 100644 --- a/drivers/misc.h +++ b/drivers/misc.h @@ -1,5 +1,5 @@ #pragma once -#include "../port.h" +#include "port.h" __attribute__((noreturn)) static inline void qemu_shutdown() { diff --git a/port.h b/drivers/port.h similarity index 100% rename from port.h rename to drivers/port.h diff --git a/vga.c b/drivers/vga.c similarity index 98% rename from vga.c rename to drivers/vga.c index 18e9619..844892d 100644 --- a/vga.c +++ b/drivers/vga.c @@ -1,5 +1,5 @@ #include "port.h" -#include "string.h" +#include "../string.h" char* const video_memory = (char*) 0xb8000; diff --git a/vga.h b/drivers/vga.h similarity index 100% rename from vga.h rename to drivers/vga.h diff --git a/kernel.c b/kernel.c index c17ed02..7d6f4ed 100644 --- a/kernel.c +++ b/kernel.c @@ -1,6 +1,6 @@ asm(".asciz \"kernel start\""); -#include "vga.h" +#include "drivers/vga.h" #include "drivers/ata.h" #include "drivers/misc.h" From f860ec1ef0ac6e287e05d9f65c6743131b9722d4 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sun, 20 Nov 2022 18:59:53 +0300 Subject: [PATCH 13/56] Load IDT. --- Makefile | 4 +- console.c | 6 +++ console.h | 3 ++ cpu/idt.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ cpu/isr.h | 47 +++++++++++++++++ cpu/vectors.S | 51 +++++++++++++++++++ kernel.c | 3 ++ 7 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 console.c create mode 100644 console.h create mode 100644 cpu/idt.c create mode 100644 cpu/isr.h create mode 100644 cpu/vectors.S diff --git a/Makefile b/Makefile index 9e93572..bda8f47 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ fs.img: kernel.bin tools/mkfs image.bin: mbr.bin fs.img cat $^ >$@ -kernel.bin: kernel.o drivers/vga.o string.o drivers/ata.o +kernel.bin: kernel.o console.o drivers/vga.o string.o drivers/ata.o cpu/vectors.o cpu/idt.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ %.o: %.c @@ -41,7 +41,7 @@ mbr.elf: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm -f *.elf *.bin *.o tools/mkfs + rm -f *.elf *.bin *.o */*.o tools/mkfs tools/%: tools/%.c gcc -Wall -Werror -g $^ -o $@ diff --git a/console.c b/console.c new file mode 100644 index 0000000..d358abe --- /dev/null +++ b/console.c @@ -0,0 +1,6 @@ +#include "console.h" +#include "drivers/vga.h" + +void printk(const char* msg) { + vga_print_string(msg); +} diff --git a/console.h b/console.h new file mode 100644 index 0000000..d115f2a --- /dev/null +++ b/console.h @@ -0,0 +1,3 @@ +#pragma once + +void printk(const char* msg); diff --git a/cpu/idt.c b/cpu/idt.c new file mode 100644 index 0000000..715580a --- /dev/null +++ b/cpu/idt.c @@ -0,0 +1,138 @@ +#include "isr.h" +#include "../drivers/port.h" +#include "../console.h" + +enum { + IDT_HANDLERS = 256, +}; + +typedef struct { + uint16_t low_offset; + uint16_t selector; + uint8_t always0; + uint8_t flags; + uint16_t high_offset; +} __attribute__((packed)) idt_gate_t; + +idt_gate_t idt[IDT_HANDLERS]; + +#define low_16(address) (uint16_t)((address) & 0xFFFF) +#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF) + +void set_idt_gate(int n, uint32_t handler) { + idt[n].low_offset = low_16(handler); + idt[n].selector = 0x08; // see GDT + idt[n].always0 = 0; + // 0x8E = 1 00 0 1 110 + // P DPL 0 D Type + idt[n].flags = 0x8E; + idt[n].high_offset = high_16(handler); +} + +// defined in vectors.S +extern uint32_t default_handlers[IDT_HANDLERS]; + +void init_idt() { + for (int i = 0; i < IDT_HANDLERS; i++) { + set_idt_gate(i, default_handlers[i]); + } +} + +const char *exception_messages[] = { + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + + "Coprocessor Fault", + "Alignment Check", + "Machine Check", +}; + +#define ARRLEN(a) (sizeof(a) / sizeof(a[0])) + +isr_t interrupt_handlers[IDT_HANDLERS]; + +void trap(registers_t *r) { + if (r->int_no < 32) { + const char* msg = "Reserved"; + if (r->int_no < ARRLEN(exception_messages)) { + msg = exception_messages[r->int_no]; + } + printk(msg); + printk("\n"); + } + + /* Handle the interrupt in a more modular way */ + if (interrupt_handlers[r->int_no] != 0) { + isr_t handler = interrupt_handlers[r->int_no]; + handler(r); + } + + // EOI + if (r->int_no >= 40) { + port_byte_out(0xA0, 0x20); /* follower */ + } + if (r->int_no >= 32) { + port_byte_out(0x20, 0x20); /* leader */ + } +} + +static void init_pic() { + // ICW1 + port_byte_out(0x20, 0x11); + port_byte_out(0xA0, 0x11); + + // ICW2 + port_byte_out(0x21, 0x20); + port_byte_out(0xA1, 0x28); + + // ICW3 + port_byte_out(0x21, 0x04); + port_byte_out(0xA1, 0x02); + + // ICW4 + port_byte_out(0x21, 0x01); + port_byte_out(0xA1, 0x01); + + // OCW1 + port_byte_out(0x21, 0x0); + port_byte_out(0xA1, 0x0); +} + +typedef struct { + uint16_t limit; + void* base; +} __attribute__((packed)) idt_register_t; + +static idt_register_t idt_reg; + +void load_idt() { + init_idt(); + init_pic(); + + idt_reg.base = &idt; + idt_reg.limit = sizeof(idt) - 1; + asm("lidt %0" : : "m"(idt_reg)); +} + +void cli() { + asm("cli"); +} + +void sti() { + asm("sti"); +} diff --git a/cpu/isr.h b/cpu/isr.h new file mode 100644 index 0000000..1175355 --- /dev/null +++ b/cpu/isr.h @@ -0,0 +1,47 @@ +#pragma once +#include + +enum { + IRQ0 = 32, + IRQ1, + IRQ2, + IRQ3, + IRQ4, + IRQ5, + IRQ6, + IRQ7, + IRQ8, + IRQ9, + IRQ10, + IRQ11, + IRQ12, + IRQ13, + IRQ14, + IRQ15, +}; + +/* Struct which aggregates many registers. + * It matches exactly the pushes on interrupt.asm. From the bottom: + * - Pushed by the processor automatically + * - `push byte`s on the isr-specific code: error code, then int number + * - All the registers by pusha + * - `push eax` whose lower 16-bits contain DS + */ +typedef struct { + uint32_t ds; /* Data segment selector */ + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */ + uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */ + uint32_t eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */ +} registers_t; + +void isr_install(); + +void isr_handler(registers_t *r); + +typedef void (*isr_t)(registers_t *); + +void register_interrupt_handler(uint8_t n, isr_t handler); + +void load_idt(); +void cli(); +void sti(); diff --git a/cpu/vectors.S b/cpu/vectors.S new file mode 100644 index 0000000..9fd80ed --- /dev/null +++ b/cpu/vectors.S @@ -0,0 +1,51 @@ +alltraps: + # Build trap frame. + pushl %ds + pushl %es + pushl %fs + pushl %gs + pushal + + mov $10, %ax + mov %ax, %ds + + # Call trap(tf), where tf=%esp + pushl %esp + call trap + add $4, %esp + + popal + popl %gs + popl %fs + popl %es + popl %ds + addl $0x8, %esp # trapno and errcode + iret + +.macro handler i +vector\i : + .if (!(\i == 8 || (\i >= 10 && \i <= 14) || \i == 17)) + pushl $0 + .endif + pushl $\i + jmp alltraps +.endm + +.altmacro + +.macro irq_insertX number + .section .text + handler \number + + .section .data + .long vector\number +.endm + +.section .data +.global default_handlers +default_handlers: +.set i,0 +.rept 256 + irq_insertX %i + .set i, i+1 +.endr diff --git a/kernel.c b/kernel.c index 7d6f4ed..80ca3a3 100644 --- a/kernel.c +++ b/kernel.c @@ -1,10 +1,13 @@ asm(".asciz \"kernel start\""); +#include "cpu/isr.h" #include "drivers/vga.h" #include "drivers/ata.h" #include "drivers/misc.h" void _start() { + load_idt(); + sti(); char buf[512]; vga_clear_screen(); From b8604932ebae3c63538d2e104f5d5d21d5e839de Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sun, 20 Nov 2022 19:48:55 +0300 Subject: [PATCH 14/56] Load kernel with a .data segment. --- Makefile | 4 ++-- cpu/idt.c | 8 ++++++-- cpu/vectors.S | 2 +- drivers/port.h | 10 +++++----- kernel.c | 10 ++++++++-- mbr.S | 12 +++++++++++- 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index bda8f47..42bb9ac 100644 --- a/Makefile +++ b/Makefile @@ -25,11 +25,11 @@ fs.img: kernel.bin tools/mkfs image.bin: mbr.bin fs.img cat $^ >$@ -kernel.bin: kernel.o console.o drivers/vga.o string.o drivers/ata.o cpu/vectors.o cpu/idt.o +kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o string.o drivers/ata.o cpu/vectors.o cpu/idt.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ %.o: %.c - $(CC) -m32 -ffreestanding -c -g $< -o $@ + $(CC) -m32 -ffreestanding -Wall -Werror -c -g $< -o $@ %.o: %.S $(AS) --32 -g $^ -o $@ diff --git a/cpu/idt.c b/cpu/idt.c index 715580a..7b2f41b 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -30,7 +30,7 @@ void set_idt_gate(int n, uint32_t handler) { } // defined in vectors.S -extern uint32_t default_handlers[IDT_HANDLERS]; +extern uint32_t default_handlers[]; void init_idt() { for (int i = 0; i < IDT_HANDLERS; i++) { @@ -64,7 +64,11 @@ const char *exception_messages[] = { #define ARRLEN(a) (sizeof(a) / sizeof(a[0])) -isr_t interrupt_handlers[IDT_HANDLERS]; +static isr_t interrupt_handlers[IDT_HANDLERS]; + +void register_interrupt_handler(uint8_t i, isr_t handler) { + interrupt_handlers[i] = handler; +} void trap(registers_t *r) { if (r->int_no < 32) { diff --git a/cpu/vectors.S b/cpu/vectors.S index 9fd80ed..ef92bfc 100644 --- a/cpu/vectors.S +++ b/cpu/vectors.S @@ -45,7 +45,7 @@ vector\i : .global default_handlers default_handlers: .set i,0 -.rept 256 +.rept 40 irq_insertX %i .set i, i+1 .endr diff --git a/drivers/port.h b/drivers/port.h index 283418b..380272a 100644 --- a/drivers/port.h +++ b/drivers/port.h @@ -1,25 +1,25 @@ #pragma once -static unsigned char port_byte_in(unsigned short port) { +static inline unsigned char port_byte_in(unsigned short port) { unsigned char result; __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); return result; } -static unsigned short port_word_in(unsigned short port) { +static inline unsigned short port_word_in(unsigned short port) { unsigned short result; __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); return result; } -static void port_byte_out(unsigned short port, unsigned char data) { +static inline void port_byte_out(unsigned short port, unsigned char data) { __asm__("outb %%al, %%dx" : : "a" (data), "d" (port)); } -static void port_word_out(unsigned short port, unsigned short data) { +static inline void port_word_out(unsigned short port, unsigned short data) { __asm__("outw %%ax, %%dx" : : "a" (data), "d" (port)); } -static void port_long_out(unsigned short port, unsigned int data) { +static inline void port_long_out(unsigned short port, unsigned int data) { __asm__("outl %%eax, %%dx" : : "a" (data), "d" (port)); } diff --git a/kernel.c b/kernel.c index 80ca3a3..35cb1af 100644 --- a/kernel.c +++ b/kernel.c @@ -1,21 +1,27 @@ asm(".asciz \"kernel start\""); +#include "console.h" #include "cpu/isr.h" +#include "drivers/keyboard.h" #include "drivers/vga.h" #include "drivers/ata.h" #include "drivers/misc.h" void _start() { + init_keyboard(); load_idt(); sti(); char buf[512]; vga_clear_screen(); - vga_print_string("YABLOKO\n"); + printk("YABLOKO\n"); read_sectors_ATA_PIO((uint32_t)buf, 10, 1); - vga_print_string(buf); + printk(buf); + while (1) { + asm("pause"); + } asm("hlt"); qemu_shutdown(); } diff --git a/mbr.S b/mbr.S index 6471c95..9b1b007 100644 --- a/mbr.S +++ b/mbr.S @@ -13,6 +13,9 @@ _start: .equ ELF32_ENTRY_OFFSET, 0x18 .equ ELF32_PHDR_OFFSET, 0x1c +.equ ELF32_PHENTSIZE_OFFSET, ELF32_PHDR_OFFSET + 14 +.equ ELF32_PHNUM_OFFSET, ELF32_PHENTSIZE_OFFSET + 2 +.equ ELF32_PHDR_P_OFFSET, 4 .equ ELF32_PHDR_FILESZ_OFFSET, 4*4 .equ KERNEL_OFFSET, 0x1000 @@ -31,8 +34,15 @@ load_kernel: mov KERNEL_OFFSET + ELF32_ENTRY_OFFSET, %si mov %si, entry // store entry point - mov KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di + mov KERNEL_OFFSET + ELF32_PHNUM_OFFSET, %ax + dec %ax // no offset to the first entry + mulb KERNEL_OFFSET + ELF32_PHENTSIZE_OFFSET + mov %ax, %di + + add KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di + // now di holds offset to the last phentry mov KERNEL_OFFSET + ELF32_PHDR_FILESZ_OFFSET(%di), %ax + add KERNEL_OFFSET + ELF32_PHDR_P_OFFSET(%di), %ax sub $0x1000, %ax // we won't load the header add $SECTOR_SIZE - 1, %ax shr $SECTOR_SHIFT, %ax // round up to sector count From 38d384175c95c8e18aad3ffc57ef3f59713d866a Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sun, 20 Nov 2022 22:17:13 +0300 Subject: [PATCH 15/56] Attempt to fix bootloader. --- Makefile | 10 +++++++++- console.c | 8 ++++++++ console.h | 1 + cpu/idt.c | 8 ++++++-- cpu/vectors.S | 2 +- mbr.S | 29 +++++++++++++++++++++++++++-- 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 42bb9ac..934ab3a 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,14 @@ CC=x86_64-elf-gcc run: image.bin qemu-system-i386 -drive format=raw,file=$< +debug-preboot: image.bin mbr.elf + qemu-system-i386 -drive format=raw,file=$< -s -S & + x86_64-elf-gdb mbr.elf \ + -ex "set architecture i8086" \ + -ex "target remote localhost:1234" \ + -ex "break *0x7c00" \ + -ex "continue" + debug-boot: image.bin mbr.elf qemu-system-i386 -drive format=raw,file=$< -s -S & x86_64-elf-gdb mbr.elf \ @@ -41,7 +49,7 @@ mbr.elf: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm -f *.elf *.bin *.o */*.o tools/mkfs + rm -f *.elf *.img *.bin *.o */*.o tools/mkfs tools/%: tools/%.c gcc -Wall -Werror -g $^ -o $@ diff --git a/console.c b/console.c index d358abe..b45f234 100644 --- a/console.c +++ b/console.c @@ -4,3 +4,11 @@ void printk(const char* msg) { vga_print_string(msg); } + +void panic(const char* msg) { + printk("Kernel panic: "); + printk(msg); + while (1) { + asm("hlt"); + } +} diff --git a/console.h b/console.h index d115f2a..2e5c22a 100644 --- a/console.h +++ b/console.h @@ -1,3 +1,4 @@ #pragma once void printk(const char* msg); +void panic(const char* msg); diff --git a/cpu/idt.c b/cpu/idt.c index 7b2f41b..218c88d 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -33,6 +33,9 @@ void set_idt_gate(int n, uint32_t handler) { extern uint32_t default_handlers[]; void init_idt() { + if (default_handlers[0] == 0) { + panic("handler table empty\n"); + } for (int i = 0; i < IDT_HANDLERS; i++) { set_idt_gate(i, default_handlers[i]); } @@ -126,11 +129,12 @@ static idt_register_t idt_reg; void load_idt() { init_idt(); - init_pic(); idt_reg.base = &idt; idt_reg.limit = sizeof(idt) - 1; - asm("lidt %0" : : "m"(idt_reg)); + asm("lidt (%0)" : : "r"(&idt_reg)); + + init_pic(); } void cli() { diff --git a/cpu/vectors.S b/cpu/vectors.S index ef92bfc..9fd80ed 100644 --- a/cpu/vectors.S +++ b/cpu/vectors.S @@ -45,7 +45,7 @@ vector\i : .global default_handlers default_handlers: .set i,0 -.rept 40 +.rept 256 irq_insertX %i .set i, i+1 .endr diff --git a/mbr.S b/mbr.S index 9b1b007..2451313 100644 --- a/mbr.S +++ b/mbr.S @@ -5,11 +5,19 @@ _start: mov $banner, %si call print_string + call get_drive_geometry call load_kernel call switch_to_32bit + hlt jmp . // loop forever +get_drive_geometry: + mov $8, %ah + mov boot_drive, %dl + int $0x13 + // TODO + .equ ELF32_ENTRY_OFFSET, 0x18 .equ ELF32_PHDR_OFFSET, 0x1c @@ -53,15 +61,30 @@ load_kernel: bios_disk_read: // expects %al to specify number of sectors, %cl the initial sector - mov $2, %ah // read mode + xor %ah, %ah + mov %ax, %si mov $0, %ch // cylinder 0 mov $0, %dh // head 0 mov $KERNEL_OFFSET, %bx // bx -> destination mov boot_drive, %dl // dl -> disk + mov $1, %al + +1: + mov $2, %ah // read mode int $0x13 - // no error checking, let's hope it works + test %ah, %ah + jnz fail + add $SECTOR_SIZE, %bx + inc %cl + dec %si + jnz 1b ret +fail: + mov $read_error, %si + call print_string + hlt + jmp . switch_to_32bit: mov $2, %al @@ -111,6 +134,8 @@ boot_drive: .byte 0 banner: .asciz "YABLOKO bootloader started\r\n" +read_error: + .asciz "Read error\r\n" .balign 2 entry: From e4fcd27e31eca27e144bec0c961ecaf8259dec7b Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sun, 20 Nov 2022 23:40:45 +0300 Subject: [PATCH 16/56] Make .data empty. --- cpu/idt.c | 4 ++-- cpu/vectors.S | 4 ++-- mbr.S | 16 ++++++++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cpu/idt.c b/cpu/idt.c index 218c88d..da94d89 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -30,7 +30,7 @@ void set_idt_gate(int n, uint32_t handler) { } // defined in vectors.S -extern uint32_t default_handlers[]; +extern const uint32_t default_handlers[]; void init_idt() { if (default_handlers[0] == 0) { @@ -41,7 +41,7 @@ void init_idt() { } } -const char *exception_messages[] = { +const char * const exception_messages[] = { "Division By Zero", "Debug", "Non Maskable Interrupt", diff --git a/cpu/vectors.S b/cpu/vectors.S index 9fd80ed..5c9f42e 100644 --- a/cpu/vectors.S +++ b/cpu/vectors.S @@ -37,11 +37,11 @@ vector\i : .section .text handler \number - .section .data + .section .rodata .long vector\number .endm -.section .data +.section .rodata .global default_handlers default_handlers: .set i,0 diff --git a/mbr.S b/mbr.S index 2451313..dac62bb 100644 --- a/mbr.S +++ b/mbr.S @@ -16,7 +16,11 @@ get_drive_geometry: mov $8, %ah mov boot_drive, %dl int $0x13 - // TODO + inc %dh // number of heads + mov %dh, disk_heads + and 0x3f, %cl + mov %cl, sectors_per_track + ret .equ ELF32_ENTRY_OFFSET, 0x18 @@ -42,13 +46,17 @@ load_kernel: mov KERNEL_OFFSET + ELF32_ENTRY_OFFSET, %si mov %si, entry // store entry point + #if 0 mov KERNEL_OFFSET + ELF32_PHNUM_OFFSET, %ax dec %ax // no offset to the first entry mulb KERNEL_OFFSET + ELF32_PHENTSIZE_OFFSET mov %ax, %di - add KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di // now di holds offset to the last phentry + #else + mov KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di + // now di holds offset to the first phentry + #endif mov KERNEL_OFFSET + ELF32_PHDR_FILESZ_OFFSET(%di), %ax add KERNEL_OFFSET + ELF32_PHDR_P_OFFSET(%di), %ax sub $0x1000, %ax // we won't load the header @@ -140,6 +148,10 @@ read_error: .balign 2 entry: .word 0 +disk_heads: + .byte 0 +sectors_per_track: + .byte 0 .balign 4 gdt_start: From 250372de4b0dea25087808a23cadd0bf7c176a59 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 21 Nov 2022 00:05:59 +0300 Subject: [PATCH 17/56] Fix trap frame layout. --- cpu/isr.h | 2 +- cpu/vectors.S | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpu/isr.h b/cpu/isr.h index 1175355..de12f30 100644 --- a/cpu/isr.h +++ b/cpu/isr.h @@ -28,8 +28,8 @@ enum { * - `push eax` whose lower 16-bits contain DS */ typedef struct { - uint32_t ds; /* Data segment selector */ uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */ + uint32_t gs, fs, es, ds; /* Data segment selector */ uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */ uint32_t eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */ } registers_t; diff --git a/cpu/vectors.S b/cpu/vectors.S index 5c9f42e..896264e 100644 --- a/cpu/vectors.S +++ b/cpu/vectors.S @@ -6,8 +6,8 @@ alltraps: pushl %gs pushal - mov $10, %ax - mov %ax, %ds + //mov $10, %ax + //mov %ax, %ds # Call trap(tf), where tf=%esp pushl %esp From 2ebc32c21c41e5e924e244ea8adee61347ba5054 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 21 Nov 2022 00:16:28 +0300 Subject: [PATCH 18/56] Add keyboard driver. --- drivers/keyboard.c | 24 ++++++++++++++++++++++++ drivers/keyboard.h | 3 +++ 2 files changed, 27 insertions(+) create mode 100644 drivers/keyboard.c create mode 100644 drivers/keyboard.h diff --git a/drivers/keyboard.c b/drivers/keyboard.c new file mode 100644 index 0000000..8de3bec --- /dev/null +++ b/drivers/keyboard.c @@ -0,0 +1,24 @@ +#include "keyboard.h" +#include "../cpu/isr.h" +#include "../console.h" +#include "port.h" + +static const char sc_ascii[] = { + '?', '?', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', + 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', + 'H', 'J', 'K', 'L', ';', '\'', '`', '?', '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' ', +}; + +static void interrupt_handler(registers_t *r) { + uint8_t scancode = port_byte_in(0x60); + if (scancode < sizeof(sc_ascii)) { + char string[] = {sc_ascii[scancode], '\0'}; + printk(string); + } +} + +void init_keyboard() { + register_interrupt_handler(IRQ1, interrupt_handler); +} diff --git a/drivers/keyboard.h b/drivers/keyboard.h new file mode 100644 index 0000000..33601ea --- /dev/null +++ b/drivers/keyboard.h @@ -0,0 +1,3 @@ +#pragma once + +void init_keyboard(); From bdfcf429dbad221f6ff0c9fa11934bfb3dbb002e Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 21 Nov 2022 00:53:29 +0300 Subject: [PATCH 19/56] Copy UART driver from xv6. --- Makefile | 5 +++-- console.c | 7 ++++++- cpu/idt.c | 3 +-- drivers/uart.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/uart.h | 4 ++++ kernel.c | 5 +++-- 6 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 drivers/uart.c create mode 100644 drivers/uart.h diff --git a/Makefile b/Makefile index 934ab3a..6333f8b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ LD=x86_64-elf-ld CC=x86_64-elf-gcc run: image.bin - qemu-system-i386 -drive format=raw,file=$< + qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio debug-preboot: image.bin mbr.elf qemu-system-i386 -drive format=raw,file=$< -s -S & @@ -33,7 +33,8 @@ fs.img: kernel.bin tools/mkfs image.bin: mbr.bin fs.img cat $^ >$@ -kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o string.o drivers/ata.o cpu/vectors.o cpu/idt.o +kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ + string.o drivers/ata.o cpu/vectors.o cpu/idt.o drivers/uart.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ %.o: %.c diff --git a/console.c b/console.c index b45f234..1064b89 100644 --- a/console.c +++ b/console.c @@ -1,13 +1,18 @@ #include "console.h" #include "drivers/vga.h" +#include "drivers/uart.h" void printk(const char* msg) { vga_print_string(msg); + for (; *msg; ++msg) { + uartputc(*msg); + } } void panic(const char* msg) { - printk("Kernel panic: "); + printk("\nKernel panic: "); printk(msg); + asm("cli"); while (1) { asm("hlt"); } diff --git a/cpu/idt.c b/cpu/idt.c index da94d89..7b855a2 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -79,8 +79,7 @@ void trap(registers_t *r) { if (r->int_no < ARRLEN(exception_messages)) { msg = exception_messages[r->int_no]; } - printk(msg); - printk("\n"); + panic(msg); } /* Handle the interrupt in a more modular way */ diff --git a/drivers/uart.c b/drivers/uart.c new file mode 100644 index 0000000..613567f --- /dev/null +++ b/drivers/uart.c @@ -0,0 +1,44 @@ +#include "uart.h" +#include "port.h" + +static int uart; + +enum { + COM1 = 0x3f8, +}; + +void uartinit() { + // Turn off the FIFO + port_byte_out(COM1+2, 0); + + // 9600 baud, 8 data bits, 1 stop bit, parity off. + port_byte_out(COM1+3, 0x80); // Unlock divisor + port_byte_out(COM1+0, 115200/9600); + port_byte_out(COM1+1, 0); + port_byte_out(COM1+3, 0x03); // Lock divisor, 8 data bits. + port_byte_out(COM1+4, 0); + port_byte_out(COM1+1, 0x01); // Enable receive interrupts. + + // If status is 0xFF, no serial port. + if(port_byte_in(COM1+5) == 0xFF) + return; + uart = 1; + + // Acknowledge pre-existing interrupt conditions; + // enable interrupts. + port_byte_in(COM1+2); + port_byte_in(COM1+0); +} + +void +uartputc(char c) +{ + int i; + + if (!uart) + return; + for (i = 0; i < 128 && !(port_byte_in(COM1+5) & 0x20); i++) { + asm("pause"); + } + port_byte_out(COM1+0, c); +} diff --git a/drivers/uart.h b/drivers/uart.h new file mode 100644 index 0000000..dfbc8da --- /dev/null +++ b/drivers/uart.h @@ -0,0 +1,4 @@ +#pragma once + +void uartputc(char c); +void uartinit(); diff --git a/kernel.c b/kernel.c index 35cb1af..2cb823c 100644 --- a/kernel.c +++ b/kernel.c @@ -6,9 +6,11 @@ asm(".asciz \"kernel start\""); #include "drivers/vga.h" #include "drivers/ata.h" #include "drivers/misc.h" +#include "drivers/uart.h" void _start() { init_keyboard(); + uartinit(); load_idt(); sti(); char buf[512]; @@ -20,8 +22,7 @@ void _start() { printk(buf); while (1) { - asm("pause"); + asm("hlt"); } - asm("hlt"); qemu_shutdown(); } From 48eaa5c94f262d152ff64e6529944ada56c064f6 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 21 Nov 2022 00:57:23 +0300 Subject: [PATCH 20/56] Give credit to code sources. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ed69b2f..a063a2b 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ Yet Another BootLoader, OS Kernel and Other stuff + +Includes code from: +* https://github.com/mit-pdos/xv6-public +* https://github.com/FRosner/FrOS +* https://github.com/dhavalhirdhav/LearnOS +* https://wiki.osdev.org From c40e007d8a721fd14a0264e59e764bf16e183ce5 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 21 Nov 2022 01:04:58 +0300 Subject: [PATCH 21/56] Add .gitignore. --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d3323b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.o +*.img +*.bin +*.elf +*.dSYM +tools/mkfs From f6bbebbee3c779971d12afeb12b115b5467ca9b2 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 21 Nov 2022 01:50:02 +0300 Subject: [PATCH 22/56] Add user/. --- Makefile | 9 +++++++-- user/crt.c | 6 ++++++ user/false.c | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 user/crt.c create mode 100644 user/false.c diff --git a/Makefile b/Makefile index 6333f8b..760de1a 100644 --- a/Makefile +++ b/Makefile @@ -27,15 +27,20 @@ debug: image.bin -ex "break _start" \ -ex "continue" -fs.img: kernel.bin tools/mkfs +fs.img: kernel.bin tools/mkfs user/false tools/mkfs $@ $< +LDFLAGS=-m elf_i386 + +user/%: user/%.o user/crt.o + $(LD) $(LDFLAGS) -o $@ -Ttext 0x101000 $^ + image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ string.o drivers/ata.o cpu/vectors.o cpu/idt.o drivers/uart.o - $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ + $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c $(CC) -m32 -ffreestanding -Wall -Werror -c -g $< -o $@ diff --git a/user/crt.c b/user/crt.c new file mode 100644 index 0000000..1a42f40 --- /dev/null +++ b/user/crt.c @@ -0,0 +1,6 @@ +int main(); + +void _start() { + int exit_status = main(); + asm("int $0x84": :"a"(exit_status)); +} diff --git a/user/false.c b/user/false.c new file mode 100644 index 0000000..6346e2d --- /dev/null +++ b/user/false.c @@ -0,0 +1,3 @@ +int main() { + return 1; +} From fb88c62f678dc588f7a474d21e92d782471e056d Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 22 Nov 2022 21:24:49 +0300 Subject: [PATCH 23/56] Install new GDT in C code. --- Makefile | 2 +- cpu/gdt.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ cpu/gdt.h | 19 +++++++++++++++++++ kernel.c | 2 ++ mbr.S | 3 +-- 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 cpu/gdt.c create mode 100644 cpu/gdt.h diff --git a/Makefile b/Makefile index 760de1a..733507b 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ - string.o drivers/ata.o cpu/vectors.o cpu/idt.o drivers/uart.o + string.o drivers/ata.o cpu/vectors.o cpu/idt.o cpu/gdt.o drivers/uart.o $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c diff --git a/cpu/gdt.c b/cpu/gdt.c new file mode 100644 index 0000000..4b1b6d2 --- /dev/null +++ b/cpu/gdt.c @@ -0,0 +1,49 @@ +#include "gdt.h" + +#include + +struct seg_desc_t { + uint16_t lim_15_0; // Low bits of segment limit + uint16_t base_15_0; // Low bits of segment base address + uint8_t base_23_16; // Middle bits of segment base address + uint8_t type : 4; // Segment type (see STS_ constants) + uint8_t s : 1; // 0 = system, 1 = application + uint8_t dpl : 2; // Descriptor Privilege Level + uint8_t p : 1; // Present + uint8_t lim_19_16 : 4; // High bits of segment limit + uint8_t avl : 1; // Unused (available for software use) + uint8_t rsv1 : 1; // Reserved + uint8_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment + uint8_t g : 1; // Granularity: limit scaled by 4K when set + uint8_t base_31_24; // High bits of segment base address +} __attribute__((packed)); + +typedef unsigned uint; + +#define SEG(type, base, lim, dpl) (struct seg_desc_t) \ +{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ + ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } + +struct seg_desc_t seg_desc[6]; + +void init_seg_desc() { + seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0); + seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); + seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); + seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); +} + +struct gdt_desc_t { + uint16_t size; + void* ptr; +} __attribute__((packed)); + +void load_gdt() { + init_seg_desc(); + + struct gdt_desc_t gdt_desc; + gdt_desc.size = sizeof(seg_desc) - 1; + gdt_desc.ptr = seg_desc; + asm("lgdt %0": : "m"(gdt_desc)); +} diff --git a/cpu/gdt.h b/cpu/gdt.h new file mode 100644 index 0000000..578fcc8 --- /dev/null +++ b/cpu/gdt.h @@ -0,0 +1,19 @@ +#pragma once + +#define STA_X 0x8 // Executable segment +#define STA_W 0x2 // Writeable (non-executable segments) +#define STA_R 0x2 // Readable (executable segments) + +#define DPL_USER 3 + +#define SEG_KCODE 1 +#define SEG_KDATA 2 +#define SEG_UCODE 3 +#define SEG_UDATA 4 +#define SEG_TSS 5 + +#define USER_BASE 0x400000 // 4 MB + +#ifndef __ASSEMBLER__ +void load_gdt(); +#endif diff --git a/kernel.c b/kernel.c index 2cb823c..430c8d5 100644 --- a/kernel.c +++ b/kernel.c @@ -2,6 +2,7 @@ asm(".asciz \"kernel start\""); #include "console.h" #include "cpu/isr.h" +#include "cpu/gdt.h" #include "drivers/keyboard.h" #include "drivers/vga.h" #include "drivers/ata.h" @@ -9,6 +10,7 @@ asm(".asciz \"kernel start\""); #include "drivers/uart.h" void _start() { + load_gdt(); init_keyboard(); uartinit(); load_idt(); diff --git a/mbr.S b/mbr.S index dac62bb..a214220 100644 --- a/mbr.S +++ b/mbr.S @@ -80,8 +80,7 @@ bios_disk_read: 1: mov $2, %ah // read mode int $0x13 - test %ah, %ah - jnz fail + jc fail add $SECTOR_SIZE, %bx inc %cl dec %si From 1e326bddc107a262345868d5809f176af4d6ebee Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 22 Nov 2022 21:43:13 +0300 Subject: [PATCH 24/56] Use xv6 macros for gdt init. --- Makefile | 2 +- cpu/gdt.h | 5 +++++ mbr.S | 30 ++++++------------------------ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index 733507b..70d5b06 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ $(CC) -m32 -ffreestanding -Wall -Werror -c -g $< -o $@ %.o: %.S - $(AS) --32 -g $^ -o $@ + $(CC) -m32 -ffreestanding -c -g $^ -o $@ mbr.bin: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 --oformat=binary $^ -o $@ diff --git a/cpu/gdt.h b/cpu/gdt.h index 578fcc8..ca1acba 100644 --- a/cpu/gdt.h +++ b/cpu/gdt.h @@ -12,6 +12,11 @@ #define SEG_UDATA 4 #define SEG_TSS 5 +#define SEG_ASM(type,base,lim) \ + .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ + .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ + (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) + #define USER_BASE 0x400000 // 4 MB #ifndef __ASSEMBLER__ diff --git a/mbr.S b/mbr.S index a214220..34fa7c5 100644 --- a/mbr.S +++ b/mbr.S @@ -1,3 +1,5 @@ +#include "cpu/gdt.h" + .code16 .global _start _start: @@ -102,12 +104,12 @@ switch_to_32bit: mov %cr0, %eax or $1, %eax // 3. enable protected mode mov %eax, %cr0 - ljmp $CODE_SEG, $init_32bit // 4. far jump + ljmp $SEG_KCODE << 3, $init_32bit // 4. far jump .code32 init_32bit: - mov $DATA_SEG, %ax // 5. update segment registers + mov $SEG_KDATA << 3, %ax // 5. update segment registers mov %ax, %ds mov %ax, %ss mov %ax, %es @@ -155,25 +157,8 @@ sectors_per_track: .balign 4 gdt_start: .quad 0x0 // null descriptor - -// code segment descriptor -gdt_code: - .word 0xffff // segment length, bits 0-15 - .word 0x0 // segment base, bits 0-15 - .byte 0x0 // segment base, bits 16-23 - .byte 0b10011010 // flags (8 bits) - .byte 0b11001111 // flags (4 bits) + segment length, bits 16-19 - .byte 0x0 // segment base, bits 24-31 - -// data segment descriptor -gdt_data: - .word 0xffff // segment length, bits 0-15 - .word 0x0 // segment base, bits 0-15 - .byte 0x0 // segment base, bits 16-23 - .byte 0b10010010 // flags (8 bits) - .byte 0b11001111 // flags (4 bits) + segment length, bits 16-19 - .byte 0x0 // segment base, bits 24-31 - + SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg + SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg gdt_end: // GDT descriptor @@ -181,8 +166,5 @@ gdt_descriptor: .word gdt_end - gdt_start - 1 // size (16 bit) .int gdt_start // address (32 bit) -.equ CODE_SEG, gdt_code - gdt_start -.equ DATA_SEG, gdt_data - gdt_start - . = _start + 510 # pad to 510 bytes .byte 0x55, 0xaa # boot sector magic value From 7a2cc02ba8e7c3721bcb168ffeec9b6619f10e43 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 22 Nov 2022 22:40:10 +0300 Subject: [PATCH 25/56] Setup TSS. --- cpu/gdt.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ cpu/gdt.h | 6 ++++++ mbr.S | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/cpu/gdt.c b/cpu/gdt.c index 4b1b6d2..f5b2572 100644 --- a/cpu/gdt.c +++ b/cpu/gdt.c @@ -24,6 +24,10 @@ typedef unsigned uint; { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } +#define SEG16(type, base, lim, dpl) (struct seg_desc_t) \ +{ (lim) & 0xffff, (uint)(base) & 0xffff, \ + ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 } struct seg_desc_t seg_desc[6]; @@ -34,16 +38,66 @@ void init_seg_desc() { seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); } +typedef uint16_t ushort; + +struct taskstate { + uint link; // Old ts selector + uint esp0; // Stack pointers and segment selectors + ushort ss0; // after an increase in privilege level + ushort padding1; + uint *esp1; + ushort ss1; + ushort padding2; + uint *esp2; + ushort ss2; + ushort padding3; + void *cr3; // Page directory base + uint *eip; // Saved state from last task switch + uint eflags; + uint eax; // More saved state (registers) + uint ecx; + uint edx; + uint ebx; + uint *esp; + uint *ebp; + uint esi; + uint edi; + ushort es; // Even more saved state (segment selectors) + ushort padding4; + ushort cs; + ushort padding5; + ushort ss; + ushort padding6; + ushort ds; + ushort padding7; + ushort fs; + ushort padding8; + ushort gs; + ushort padding9; + ushort ldt; + ushort padding10; + ushort t; // Trap on task switch + ushort iomb; // I/O map base address +} tss; + struct gdt_desc_t { uint16_t size; void* ptr; } __attribute__((packed)); void load_gdt() { + seg_desc[SEG_TSS] = SEG16(STS_T32A, &tss, sizeof(tss)-1, 0); + seg_desc[SEG_TSS].s = 0; + tss.ss0 = SEG_KDATA << 3; + tss.esp0 = KERN_STACK_BASE; + // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit + // forbids I/O instructions (e.g., inb and outb) from user space + tss.iomb = (ushort) 0xFFFF; init_seg_desc(); struct gdt_desc_t gdt_desc; gdt_desc.size = sizeof(seg_desc) - 1; gdt_desc.ptr = seg_desc; asm("lgdt %0": : "m"(gdt_desc)); + asm("ltr %0": : "r"((ushort)(SEG_TSS << 3))); } diff --git a/cpu/gdt.h b/cpu/gdt.h index ca1acba..6f3bce7 100644 --- a/cpu/gdt.h +++ b/cpu/gdt.h @@ -4,6 +4,11 @@ #define STA_W 0x2 // Writeable (non-executable segments) #define STA_R 0x2 // Readable (executable segments) +// System segment type bits +#define STS_T32A 0x9 // Available 32-bit TSS +#define STS_IG32 0xE // 32-bit Interrupt Gate +#define STS_TG32 0xF // 32-bit Trap Gate + #define DPL_USER 3 #define SEG_KCODE 1 @@ -18,6 +23,7 @@ (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) #define USER_BASE 0x400000 // 4 MB +#define KERN_STACK_BASE 0x90000 #ifndef __ASSEMBLER__ void load_gdt(); diff --git a/mbr.S b/mbr.S index 34fa7c5..fea161f 100644 --- a/mbr.S +++ b/mbr.S @@ -116,7 +116,7 @@ init_32bit: mov %ax, %fs mov %ax, %gs - mov $0x90000, %ebp // 6. setup stack + mov $KERN_STACK_BASE, %ebp // 6. setup stack mov %ebp, %esp movzwl entry, %esi From 2e56f726c3d7c6f519bb44d6e9887ca0f2ebb500 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 25 Nov 2022 09:18:38 +0000 Subject: [PATCH 26/56] Make it run on Linux. --- Makefile | 2 ++ tools/mkfs.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 70d5b06..d9aabf3 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ +ifeq ($(shell uname -s),Darwin) AS=x86_64-elf-as LD=x86_64-elf-ld CC=x86_64-elf-gcc +endif run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio diff --git a/tools/mkfs.c b/tools/mkfs.c index 40f0485..32b1b86 100644 --- a/tools/mkfs.c +++ b/tools/mkfs.c @@ -60,7 +60,8 @@ int main(int argc, char* argv[]) { } dirent->reserved = 0; - strlcpy(dirent->name, basename(name), sizeof(dirent->name)); + dirent->name[sizeof(dirent->name) - 1] = '\0'; + strncpy(dirent->name, basename(name), sizeof(dirent->name) - 1); } fseek(image, 0, SEEK_SET); From 6ddcf19a0605cc37f33d86408c358576a525e5c7 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 25 Nov 2022 09:57:00 +0000 Subject: [PATCH 27/56] GDB var. --- Makefile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index d9aabf3..ee34527 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,18 @@ +GDB=gdb + ifeq ($(shell uname -s),Darwin) AS=x86_64-elf-as LD=x86_64-elf-ld CC=x86_64-elf-gcc +GDB=x86_64-elf-gdb endif run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio -debug-preboot: image.bin mbr.elf - qemu-system-i386 -drive format=raw,file=$< -s -S & - x86_64-elf-gdb mbr.elf \ +debug-preboot-nox: image.bin mbr.elf + qemu-system-i386 -nographic -drive format=raw,file=$< -s -S & + $(GDB) mbr.elf \ -ex "set architecture i8086" \ -ex "target remote localhost:1234" \ -ex "break *0x7c00" \ @@ -17,14 +20,14 @@ debug-preboot: image.bin mbr.elf debug-boot: image.bin mbr.elf qemu-system-i386 -drive format=raw,file=$< -s -S & - x86_64-elf-gdb mbr.elf \ + $(GDB) mbr.elf \ -ex "target remote localhost:1234" \ -ex "break init_32bit" \ -ex "continue" debug: image.bin qemu-system-i386 -drive format=raw,file=$< -s -S & - x86_64-elf-gdb kernel.bin \ + $(GDB) kernel.bin \ -ex "target remote localhost:1234" \ -ex "break _start" \ -ex "continue" From 1d4edf4e0c47d9f11498c5f77e8416ede9d0b83d Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 25 Nov 2022 12:57:49 +0300 Subject: [PATCH 28/56] Amend CFLAGS. --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ee34527..6670f7d 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,10 @@ CC=x86_64-elf-gcc GDB=x86_64-elf-gdb endif +CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \ + -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer +CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) + run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio @@ -48,7 +52,7 @@ kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c - $(CC) -m32 -ffreestanding -Wall -Werror -c -g $< -o $@ + $(CC) $(CFLAGS) -c $< -o $@ %.o: %.S $(CC) -m32 -ffreestanding -c -g $^ -o $@ From f564a042d884e5d57c7d039f5deef84224d16030 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 25 Nov 2022 10:42:07 +0000 Subject: [PATCH 29/56] Better ELF header parsing. --- Makefile | 2 +- drivers/vga.c | 2 +- mbr.S | 24 ++++++++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 6670f7d..d139caf 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ GDB=x86_64-elf-gdb endif CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \ - -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer + -Os -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) run: image.bin diff --git a/drivers/vga.c b/drivers/vga.c index 844892d..beb624f 100644 --- a/drivers/vga.c +++ b/drivers/vga.c @@ -1,7 +1,7 @@ #include "port.h" #include "../string.h" -char* const video_memory = (char*) 0xb8000; +static char* const video_memory = (char*) 0xb8000; enum colors16 { black = 0, diff --git a/mbr.S b/mbr.S index fea161f..42e5d60 100644 --- a/mbr.S +++ b/mbr.S @@ -30,9 +30,12 @@ get_drive_geometry: .equ ELF32_PHENTSIZE_OFFSET, ELF32_PHDR_OFFSET + 14 .equ ELF32_PHNUM_OFFSET, ELF32_PHENTSIZE_OFFSET + 2 .equ ELF32_PHDR_P_OFFSET, 4 +.equ ELF32_PHDR_PTYPE_OFFSET, 0 .equ ELF32_PHDR_FILESZ_OFFSET, 4*4 .equ KERNEL_OFFSET, 0x1000 +.equ PT_LOAD, 1 + .equ MBR_SECTORS, 2 .equ SECTOR_BASE, 1 .equ ELFHDR_SECTORS, 8 @@ -48,18 +51,23 @@ load_kernel: mov KERNEL_OFFSET + ELF32_ENTRY_OFFSET, %si mov %si, entry // store entry point - #if 0 - mov KERNEL_OFFSET + ELF32_PHNUM_OFFSET, %ax - dec %ax // no offset to the first entry + mov KERNEL_OFFSET + ELF32_PHNUM_OFFSET, %si +read_segment: + dec %si // no offset to the first entry + mov %si, %ax mulb KERNEL_OFFSET + ELF32_PHENTSIZE_OFFSET mov %ax, %di add KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di - // now di holds offset to the last phentry - #else - mov KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di - // now di holds offset to the first phentry - #endif + // now di holds offset to the phentry + mov KERNEL_OFFSET + ELF32_PHDR_PTYPE_OFFSET(%di), %ax + cmp $PT_LOAD, %ax + jnz read_segment // not a PT_LOAD segment mov KERNEL_OFFSET + ELF32_PHDR_FILESZ_OFFSET(%di), %ax + test %ax, %ax + jz read_segment // empty segment + + // now di holds offset to the last phentry loaded from file, ax its filesz + add KERNEL_OFFSET + ELF32_PHDR_P_OFFSET(%di), %ax sub $0x1000, %ax // we won't load the header add $SECTOR_SIZE - 1, %ax From e6dc668a3f92906c8f8184722d926cc65c07887a Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 30 Nov 2022 14:48:13 +0000 Subject: [PATCH 30/56] Disable -Os; reformat ata.c. --- Makefile | 14 ++++++- drivers/ata.c | 104 ++++++++++++++++++++++++-------------------------- 2 files changed, 61 insertions(+), 57 deletions(-) diff --git a/Makefile b/Makefile index d139caf..a6c0ceb 100644 --- a/Makefile +++ b/Makefile @@ -8,12 +8,15 @@ GDB=x86_64-elf-gdb endif CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \ - -Os -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer + -Wall -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio +run-nox: image.bin + qemu-system-i386 -nographic -drive format=raw,file=$< -serial mon:stdio + debug-preboot-nox: image.bin mbr.elf qemu-system-i386 -nographic -drive format=raw,file=$< -s -S & $(GDB) mbr.elf \ @@ -36,6 +39,13 @@ debug: image.bin -ex "break _start" \ -ex "continue" +debug-nox: image.bin + qemu-system-i386 -nographic -drive format=raw,file=$< -s -S & + $(GDB) kernel.bin \ + -ex "target remote localhost:1234" \ + -ex "break _start" \ + -ex "continue" + fs.img: kernel.bin tools/mkfs user/false tools/mkfs $@ $< @@ -64,7 +74,7 @@ mbr.elf: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm -f *.elf *.img *.bin *.o */*.o tools/mkfs + rm -f *.elf *.img *.bin *.o */*.o tools/mkfs user/false tools/%: tools/%.c gcc -Wall -Werror -g $^ -o $@ diff --git a/drivers/ata.c b/drivers/ata.c index 5abee68..13e9889 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -1,80 +1,74 @@ -// stolen from https://github.com/dhavalhirdhav/LearnOS/blob/master/drivers/ata/ata.c +// stolen from +// https://github.com/dhavalhirdhav/LearnOS/blob/master/drivers/ata/ata.c + +#include "ata.h" #include -#include "ata.h" #include "port.h" -/* - BSY: a 1 means that the controller is busy executing a command. No register should be accessed (except the digital output register) while this bit is set. -RDY: a 1 means that the controller is ready to accept a command, and the drive is spinning at correct speed.. -WFT: a 1 means that the controller detected a write fault. -SKC: a 1 means that the read/write head is in position (seek completed). -DRQ: a 1 means that the controller is expecting data (for a write) or is sending data (for a read). Don't access the data register while this bit is 0. -COR: a 1 indicates that the controller had to correct data, by using the ECC bytes (error correction code: extra bytes at the end of the sector that allows to verify its integrity and, sometimes, to correct errors). -IDX: a 1 indicates the the controller retected the index mark (which is not a hole on hard-drives). -ERR: a 1 indicates that an error occured. An error code has been placed in the error register. -*/ - #define STATUS_BSY 0x80 #define STATUS_RDY 0x40 #define STATUS_DRQ 0x08 -#define STATUS_DF 0x20 +#define STATUS_DF 0x20 #define STATUS_ERR 0x01 -//This is really specific to our OS now, assuming ATA bus 0 master -//Source - OsDev wiki +// This is really specific to our OS now, assuming ATA bus 0 master +// Source - OsDev wiki https://wiki.osdev.org/ATA_PIO_Mode + static void ATA_wait_BSY(); -static void ATA_wait_DRQ(); +static void ATA_wait_RDY(); + void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count) { + ATA_wait_BSY(); + port_byte_out(0x1F6, 0xE0 | ((LBA >> 24) & 0xF)); + port_byte_out(0x1F2, sector_count); + port_byte_out(0x1F3, (uint8_t)LBA); + port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F7, 0x20); // Send the read command - ATA_wait_BSY(); - port_byte_out(0x1F6, 0xE0 | ((LBA >>24) & 0xF)); - port_byte_out(0x1F2, sector_count); - port_byte_out(0x1F3, (uint8_t) LBA); - port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); - port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); - port_byte_out(0x1F7, 0x20); //Send the read command + uint16_t *target = (uint16_t *)target_address; - uint16_t *target = (uint16_t*) target_address; - - for (int j = 0; j < sector_count; j++) - { - ATA_wait_BSY(); - ATA_wait_DRQ(); - for(int i = 0; i < 256; i++) - target[i] = port_word_in(0x1F0); - target += 256; - } + for (int j = 0; j < sector_count; j++) + { + ATA_wait_BSY(); + ATA_wait_RDY(); + for (int i = 0; i < 256; i++) + target[i] = port_word_in(0x1F0); + target += 256; + } } -void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t* bytes) +void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t *bytes) { - ATA_wait_BSY(); - port_byte_out(0x1F6,0xE0 | ((LBA >>24) & 0xF)); - port_byte_out(0x1F2,sector_count); - port_byte_out(0x1F3, (uint8_t) LBA); - port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); - port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); - port_byte_out(0x1F7,0x30); //Send the write command + ATA_wait_BSY(); + port_byte_out(0x1F6, 0xE0 | ((LBA >> 24) & 0xF)); + port_byte_out(0x1F2, sector_count); + port_byte_out(0x1F3, (uint8_t)LBA); + port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F7, 0x30); // Send the write command - for (int j =0;j Date: Wed, 30 Nov 2022 18:11:06 +0300 Subject: [PATCH 31/56] Unified debug-boot*. --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a6c0ceb..351c481 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ run: image.bin run-nox: image.bin qemu-system-i386 -nographic -drive format=raw,file=$< -serial mon:stdio -debug-preboot-nox: image.bin mbr.elf +debug-boot-nox: image.bin mbr.elf qemu-system-i386 -nographic -drive format=raw,file=$< -s -S & $(GDB) mbr.elf \ -ex "set architecture i8086" \ @@ -28,8 +28,9 @@ debug-preboot-nox: image.bin mbr.elf debug-boot: image.bin mbr.elf qemu-system-i386 -drive format=raw,file=$< -s -S & $(GDB) mbr.elf \ + -ex "set architecture i8086" \ -ex "target remote localhost:1234" \ - -ex "break init_32bit" \ + -ex "break *0x7c00" \ -ex "continue" debug: image.bin From af760c928caf4ea9f5e1c445be33b1e920e2764e Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 12 Dec 2022 19:07:48 +0300 Subject: [PATCH 32/56] Add setup.sh. --- setup.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 setup.sh diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..d51750f --- /dev/null +++ b/setup.sh @@ -0,0 +1,10 @@ +#!/bin/sh -x + +if [ `uname` = Darwin ]; then + brew install \ + x86_64-elf-binutils x86_64-elf-gcc \ + x86_64-elf-gdb qemu +elif [ `uname` = Linux ]; then + sudo apt-get update + sudo apt-get install qemu-system-x86 +fi From 8cafc151168d85f61f685c824dadfa0f3ecc7036 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Mon, 12 Dec 2022 19:13:49 +0300 Subject: [PATCH 33/56] README.md: quickstart note. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a063a2b..4422fad 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ Yet Another BootLoader, OS Kernel and Other stuff +Quickstart: +``` +$ ./setup.sh +$ make +``` + Includes code from: * https://github.com/mit-pdos/xv6-public * https://github.com/FRosner/FrOS From babf3897f4d3dda277c3de3ad2acf11c9f0d2a09 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 13 Dec 2022 00:50:28 +0300 Subject: [PATCH 34/56] Add fs.c. --- Makefile | 3 ++- drivers/ata.h | 3 +-- fs/fs.c | 39 +++++++++++++++++++++++++++++++++++++++ fs/fs.h | 3 ++- kernel.c | 10 +++++++--- string.c | 12 ++++++++++++ string.h | 1 + 7 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 fs/fs.c diff --git a/Makefile b/Makefile index 351c481..d0e184e 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,8 @@ image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ - string.o drivers/ata.o cpu/vectors.o cpu/idt.o cpu/gdt.o drivers/uart.o + string.o drivers/ata.o cpu/vectors.o cpu/idt.o cpu/gdt.o drivers/uart.o \ + fs/fs.o $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c diff --git a/drivers/ata.h b/drivers/ata.h index 3d5b3d6..67aea2f 100644 --- a/drivers/ata.h +++ b/drivers/ata.h @@ -1,5 +1,4 @@ #pragma once #include -void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count); -void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t* bytes); +void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_count); diff --git a/fs/fs.c b/fs/fs.c new file mode 100644 index 0000000..6a5c74a --- /dev/null +++ b/fs/fs.c @@ -0,0 +1,39 @@ +#include "fs.h" +#include "../string.h" +#include "../drivers/ata.h" + +enum { + fs_start = 1, // sector where the FS starts +}; + +int stat(const char* name, struct stat *buf) { + struct dir dir; + read_sectors_ATA_PIO(&dir, fs_start, 1); + for (int i = 0; i < ents_in_dir; ++i) { + struct dirent *e = &dir.entries[i]; + if (!strncmp(e->name, name, sizeof(e->name))) { + buf->size = e->size_bytes; + buf->reserved[0] = e->offset_sectors; + return 0; + } + } + return -1; +} + +/* Find file by name and read it into buffer with size bufsize. + * At most (bufsize & -512) bytes will be read. + * Return number of bytes read or -1 on failure. */ +int read_file(const char* name, void* buf, uint32_t bufsize) { + struct stat statbuf; + if (stat(name, &statbuf) != 0) { + return -1; + } + uint32_t sector = fs_start + statbuf.reserved[0]; + while (bufsize >= sector_size) { + read_sectors_ATA_PIO(buf, sector, 1); + sector++; + bufsize -= sector_size; + buf += sector_size; + } + return 0; +} diff --git a/fs/fs.h b/fs/fs.h index 8de61a3..67f01a2 100644 --- a/fs/fs.h +++ b/fs/fs.h @@ -21,6 +21,7 @@ size is in bytes, name is 0-terminated. enum { sector_size = 512, + ents_in_dir = 15, }; struct dirent { @@ -32,7 +33,7 @@ struct dirent { struct dir { char reserved[32]; - struct dirent entries[15]; + struct dirent entries[ents_in_dir]; }; struct stat { diff --git a/kernel.c b/kernel.c index 430c8d5..a7b3ec2 100644 --- a/kernel.c +++ b/kernel.c @@ -8,6 +8,7 @@ asm(".asciz \"kernel start\""); #include "drivers/ata.h" #include "drivers/misc.h" #include "drivers/uart.h" +#include "fs/fs.h" void _start() { load_gdt(); @@ -15,13 +16,16 @@ void _start() { uartinit(); load_idt(); sti(); - char buf[512]; + char buf[4096 + 512]; vga_clear_screen(); printk("YABLOKO\n"); - read_sectors_ATA_PIO((uint32_t)buf, 10, 1); - printk(buf); + if (read_file("kernel.bin", buf, sizeof(buf)) == 0) { + printk(buf + 4096); + } else { + printk("failed to read file\n"); + } while (1) { asm("hlt"); diff --git a/string.c b/string.c index bb71bff..99e1171 100644 --- a/string.c +++ b/string.c @@ -14,3 +14,15 @@ void kmemmove(char* dst, char* src, size_t size) { } } } + +int strncmp(const char* s1, const char* s2, size_t size) { + while (size && *s1 && *s2 && *s1 == *s2) { + size--; + s1++; + s2++; + } + if (!size) { + return 0; + } + return (unsigned char)(*s1) - (unsigned char)(*s2); +} diff --git a/string.h b/string.h index 9d8b0de..584f7d7 100644 --- a/string.h +++ b/string.h @@ -3,3 +3,4 @@ typedef unsigned size_t; void kmemmove(char* dst, char* src, size_t size); +int strncmp(const char* s1, const char* s2, size_t size); From 5c804fa301225434e02f0481e4b571763c527ec5 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 13 Dec 2022 00:51:05 +0300 Subject: [PATCH 35/56] crt.c: extract _exit. --- user/crt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/user/crt.c b/user/crt.c index 1a42f40..12dd78e 100644 --- a/user/crt.c +++ b/user/crt.c @@ -1,6 +1,9 @@ int main(); -void _start() { - int exit_status = main(); - asm("int $0x84": :"a"(exit_status)); +void _exit(int exit_status) { + asm("int $0x84": : "a"(0), "b"(exit_status)); +} + +void _start() { + _exit(main()); } From 781029e00ad1efb141eb77e3f72f03f33bc03ce6 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 13 Dec 2022 01:18:30 +0300 Subject: [PATCH 36/56] Support the "halt" command. --- drivers/keyboard.c | 19 ++++++++++++++----- drivers/keyboard.h | 3 +++ fs/fs.c | 8 ++++++-- kernel.c | 16 +++++++++++++--- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/keyboard.c b/drivers/keyboard.c index 8de3bec..f64a084 100644 --- a/drivers/keyboard.c +++ b/drivers/keyboard.c @@ -5,20 +5,29 @@ static const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', - 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', - 'H', 'J', 'K', 'L', ';', '\'', '`', '?', '\\', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' ', + '7', '8', '9', '0', '-', '=', '?', '?', 'q', 'w', 'e', 'r', 't', 'y', + 'u', 'i', 'o', 'p', '[', ']', '\n', '?', 'a', 's', 'd', 'f', 'g', + 'h', 'j', 'k', 'l', ';', '\'', '`', '?', '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' ', }; static void interrupt_handler(registers_t *r) { uint8_t scancode = port_byte_in(0x60); if (scancode < sizeof(sc_ascii)) { - char string[] = {sc_ascii[scancode], '\0'}; + char c = sc_ascii[scancode]; + if (kbd_buf_size < 1024) { + kbd_buf[kbd_buf_size++] = c; + } + char string[] = {c, '\0'}; printk(string); } } +char* kbd_buf; +unsigned kbd_buf_size; + void init_keyboard() { + kbd_buf = (char*)(1 << 20); + register_interrupt_handler(IRQ1, interrupt_handler); } diff --git a/drivers/keyboard.h b/drivers/keyboard.h index 33601ea..c13d4bb 100644 --- a/drivers/keyboard.h +++ b/drivers/keyboard.h @@ -1,3 +1,6 @@ #pragma once void init_keyboard(); + +extern unsigned kbd_buf_size; +extern char *kbd_buf; diff --git a/fs/fs.c b/fs/fs.c index 6a5c74a..abd1fef 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -29,11 +29,15 @@ int read_file(const char* name, void* buf, uint32_t bufsize) { return -1; } uint32_t sector = fs_start + statbuf.reserved[0]; - while (bufsize >= sector_size) { + uint32_t bytes_read = 0; + uint32_t file_sectors = (statbuf.size + sector_size - 1) / sector_size; + while (bufsize >= sector_size && file_sectors > 0) { read_sectors_ATA_PIO(buf, sector, 1); sector++; + file_sectors--; bufsize -= sector_size; buf += sector_size; + bytes_read += sector_size; } - return 0; + return bytes_read < statbuf.size ? bytes_read : statbuf.size; } diff --git a/kernel.c b/kernel.c index a7b3ec2..436dce6 100644 --- a/kernel.c +++ b/kernel.c @@ -9,6 +9,7 @@ asm(".asciz \"kernel start\""); #include "drivers/misc.h" #include "drivers/uart.h" #include "fs/fs.h" +#include "string.h" void _start() { load_gdt(); @@ -16,19 +17,28 @@ void _start() { uartinit(); load_idt(); sti(); - char buf[4096 + 512]; + + char *buf = (char*)(16 << 20); vga_clear_screen(); printk("YABLOKO\n"); - if (read_file("kernel.bin", buf, sizeof(buf)) == 0) { + if (read_file("kernel.bin", buf, 4096 + sector_size) > 0) { printk(buf + 4096); } else { printk("failed to read file\n"); } + printk("\n> "); while (1) { + if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { + if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) { + qemu_shutdown(); + } else { + printk("unknown command, try: halt\n> "); + } + kbd_buf_size = 0; + } asm("hlt"); } - qemu_shutdown(); } From caf78f3c9fd7ddbd26b3f6c4b87a98951ad0295a Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 13 Dec 2022 01:25:27 +0300 Subject: [PATCH 37/56] Use __builtin_unreachable. --- drivers/misc.h | 2 +- user/crt.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc.h b/drivers/misc.h index 0d790d7..bbb1701 100644 --- a/drivers/misc.h +++ b/drivers/misc.h @@ -4,5 +4,5 @@ __attribute__((noreturn)) static inline void qemu_shutdown() { port_word_out(0x604, 0x2000); - while(1); + __builtin_unreachable(); } diff --git a/user/crt.c b/user/crt.c index 12dd78e..17320ea 100644 --- a/user/crt.c +++ b/user/crt.c @@ -1,7 +1,9 @@ int main(); +_Noreturn void _exit(int exit_status) { asm("int $0x84": : "a"(0), "b"(exit_status)); + __builtin_unreachable(); } void _start() { From 507d47f0b76903b238083c4679066b22b3379e13 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 13 Dec 2022 15:33:10 +0000 Subject: [PATCH 38/56] Fix Linux build. --- drivers/ata.c | 2 +- mbr.S | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/ata.c b/drivers/ata.c index 13e9889..f3d9ac8 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -19,7 +19,7 @@ static void ATA_wait_BSY(); static void ATA_wait_RDY(); -void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count) +void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_count) { ATA_wait_BSY(); port_byte_out(0x1F6, 0xE0 | ((LBA >> 24) & 0xF)); diff --git a/mbr.S b/mbr.S index 42e5d60..20b5fe3 100644 --- a/mbr.S +++ b/mbr.S @@ -150,9 +150,9 @@ done: boot_drive: .byte 0 banner: - .asciz "YABLOKO bootloader started\r\n" + .asciz "YABLOKO bootloader started\n\r" read_error: - .asciz "Read error\r\n" + .asciz "Read error\n\r" .balign 2 entry: From b0c53f6c83b0370c7d8a8e0315136b90ac917554 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 13 Dec 2022 19:06:51 +0300 Subject: [PATCH 39/56] Move string.[ch] to lib/. --- drivers/vga.c | 2 +- fs/fs.c | 2 +- kernel.c | 8 ++++++-- string.c => lib/string.c | 0 string.h => lib/string.h | 0 5 files changed, 8 insertions(+), 4 deletions(-) rename string.c => lib/string.c (100%) rename string.h => lib/string.h (100%) diff --git a/drivers/vga.c b/drivers/vga.c index beb624f..7cd44ee 100644 --- a/drivers/vga.c +++ b/drivers/vga.c @@ -1,5 +1,5 @@ #include "port.h" -#include "../string.h" +#include "../lib/string.h" static char* const video_memory = (char*) 0xb8000; diff --git a/fs/fs.c b/fs/fs.c index abd1fef..6c7e6b6 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -1,5 +1,5 @@ #include "fs.h" -#include "../string.h" +#include "../lib/string.h" #include "../drivers/ata.h" enum { diff --git a/kernel.c b/kernel.c index 436dce6..fbf61b8 100644 --- a/kernel.c +++ b/kernel.c @@ -1,4 +1,4 @@ -asm(".asciz \"kernel start\""); +asm(".asciz \"kernel start\\n\""); #include "console.h" #include "cpu/isr.h" @@ -9,7 +9,7 @@ asm(".asciz \"kernel start\""); #include "drivers/misc.h" #include "drivers/uart.h" #include "fs/fs.h" -#include "string.h" +#include "lib/string.h" void _start() { load_gdt(); @@ -34,6 +34,10 @@ void _start() { if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) { qemu_shutdown(); + } else if (!strncmp("run ", kbd_buf, kbd_buf_size)) { + kbd_buf[kbd_buf_size-1] = '\0'; + // const char* cmd = kbd_buf + 4; + // run_elf(cmd); } else { printk("unknown command, try: halt\n> "); } diff --git a/string.c b/lib/string.c similarity index 100% rename from string.c rename to lib/string.c diff --git a/string.h b/lib/string.h similarity index 100% rename from string.h rename to lib/string.h From 529ba03e50e64edf981f1ee5a4b1672d2f05ba84 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 13 Dec 2022 19:12:31 +0300 Subject: [PATCH 40/56] Add lib/mem.*. --- Makefile | 4 ++-- drivers/keyboard.c | 7 +++++-- lib/mem.c | 12 ++++++++++++ lib/mem.h | 5 +++++ 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 lib/mem.c create mode 100644 lib/mem.h diff --git a/Makefile b/Makefile index d0e184e..c2585fb 100644 --- a/Makefile +++ b/Makefile @@ -59,8 +59,8 @@ image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ - string.o drivers/ata.o cpu/vectors.o cpu/idt.o cpu/gdt.o drivers/uart.o \ - fs/fs.o + drivers/ata.o cpu/vectors.o cpu/idt.o cpu/gdt.o drivers/uart.o \ + fs/fs.o lib/mem.o lib/string.o $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c diff --git a/drivers/keyboard.c b/drivers/keyboard.c index f64a084..d7ade6e 100644 --- a/drivers/keyboard.c +++ b/drivers/keyboard.c @@ -2,6 +2,7 @@ #include "../cpu/isr.h" #include "../console.h" #include "port.h" +#include "../lib/mem.h" static const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', @@ -11,11 +12,13 @@ static const char sc_ascii[] = { 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' ', }; +enum { kbd_buf_capacity = 1024 }; + static void interrupt_handler(registers_t *r) { uint8_t scancode = port_byte_in(0x60); if (scancode < sizeof(sc_ascii)) { char c = sc_ascii[scancode]; - if (kbd_buf_size < 1024) { + if (kbd_buf_size < kbd_buf_capacity) { kbd_buf[kbd_buf_size++] = c; } char string[] = {c, '\0'}; @@ -27,7 +30,7 @@ char* kbd_buf; unsigned kbd_buf_size; void init_keyboard() { - kbd_buf = (char*)(1 << 20); + kbd_buf = kmalloc(kbd_buf_capacity); register_interrupt_handler(IRQ1, interrupt_handler); } diff --git a/lib/mem.c b/lib/mem.c new file mode 100644 index 0000000..0bf188b --- /dev/null +++ b/lib/mem.c @@ -0,0 +1,12 @@ +#include "mem.h" + +static void* freeptr; + +void* kmalloc(size_t size) { + if (!freeptr) { + freeptr = (void*)(1<<20); + } + void* result = freeptr; + freeptr += size; + return result; +} diff --git a/lib/mem.h b/lib/mem.h new file mode 100644 index 0000000..1a77610 --- /dev/null +++ b/lib/mem.h @@ -0,0 +1,5 @@ +#pragma once + +typedef unsigned size_t; + +void* kmalloc(size_t size); From dd9f5786c220496d220b6485b481786c17a11945 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 14 Dec 2022 09:00:32 +0300 Subject: [PATCH 41/56] Run user process. --- Makefile | 6 ++--- cpu/gdt.h | 7 ++++-- cpu/idt.c | 38 +++++++++++++++---------------- cpu/swtch.S | 20 +++++++++++++++++ cpu/vectors.S | 4 ++++ kernel.c | 13 ++++++----- lib/string.c | 7 ++++++ lib/string.h | 1 + proc.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ proc.h | 3 +++ 10 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 cpu/swtch.S create mode 100644 proc.c create mode 100644 proc.h diff --git a/Makefile b/Makefile index c2585fb..9615153 100644 --- a/Makefile +++ b/Makefile @@ -48,19 +48,19 @@ debug-nox: image.bin -ex "continue" fs.img: kernel.bin tools/mkfs user/false - tools/mkfs $@ $< + tools/mkfs $@ $< user/false LDFLAGS=-m elf_i386 user/%: user/%.o user/crt.o - $(LD) $(LDFLAGS) -o $@ -Ttext 0x101000 $^ + $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ drivers/ata.o cpu/vectors.o cpu/idt.o cpu/gdt.o drivers/uart.o \ - fs/fs.o lib/mem.o lib/string.o + fs/fs.o lib/mem.o lib/string.o proc.o cpu/swtch.o $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c diff --git a/cpu/gdt.h b/cpu/gdt.h index 6f3bce7..0f174b8 100644 --- a/cpu/gdt.h +++ b/cpu/gdt.h @@ -11,6 +11,8 @@ #define DPL_USER 3 +#define FL_IF 0x00000200 + #define SEG_KCODE 1 #define SEG_KDATA 2 #define SEG_UCODE 3 @@ -22,8 +24,9 @@ .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) -#define USER_BASE 0x400000 // 4 MB -#define KERN_STACK_BASE 0x90000 +#define USER_BASE 0x400000 // 4 MB +#define USER_STACK_BASE 0xf00000 // 15 MB +#define KERN_STACK_BASE 0x90000 #ifndef __ASSEMBLER__ void load_gdt(); diff --git a/cpu/idt.c b/cpu/idt.c index 7b855a2..9ae3674 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -42,27 +42,27 @@ void init_idt() { } const char * const exception_messages[] = { - "Division By Zero", - "Debug", - "Non Maskable Interrupt", - "Breakpoint", - "Into Detected Overflow", - "Out of Bounds", - "Invalid Opcode", - "No Coprocessor", + [0] = "Division By Zero", + [1] = "Debug", + [2] = "Non Maskable Interrupt", + [3] = "Breakpoint", + [4] = "Into Detected Overflow", + [5] = "Out of Bounds", + [6] = "Invalid Opcode", + [7] = "No Coprocessor", - "Double Fault", - "Coprocessor Segment Overrun", - "Bad TSS", - "Segment Not Present", - "Stack Fault", - "General Protection Fault", - "Page Fault", - "Unknown Interrupt", + [8] = "Double Fault", + [9] = "Coprocessor Segment Overrun", + [10] = "Bad TSS", + [11] = "Segment Not Present", + [12] = "Stack Fault", + [13] = "General Protection Fault", + [14] = "Page Fault", + [15] = "Unknown Interrupt", - "Coprocessor Fault", - "Alignment Check", - "Machine Check", + [16] = "Coprocessor Fault", + [17] = "Alignment Check", + [18] = "Machine Check", }; #define ARRLEN(a) (sizeof(a) / sizeof(a[0])) diff --git a/cpu/swtch.S b/cpu/swtch.S new file mode 100644 index 0000000..db6b56e --- /dev/null +++ b/cpu/swtch.S @@ -0,0 +1,20 @@ + // swtch(void** oldstack, void* newstack) + .global swtch +swtch: + mov 4(%esp), %eax // eax holds "oldstack" + mov 8(%esp), %ecx + + push %ebx + push %ebp + push %esi + push %edi + + mov %esp, (%eax) // save stack ptr to "oldstack" + mov %ecx, %esp // use "newstack" as stack ptr + + pop %edi + pop %esi + pop %ebp + pop %ebx + + ret diff --git a/cpu/vectors.S b/cpu/vectors.S index 896264e..21ddded 100644 --- a/cpu/vectors.S +++ b/cpu/vectors.S @@ -1,3 +1,5 @@ + .global trapret + alltraps: # Build trap frame. pushl %ds @@ -13,7 +15,9 @@ alltraps: pushl %esp call trap add $4, %esp + // execution falls through to trapret +trapret: popal popl %gs popl %fs diff --git a/kernel.c b/kernel.c index fbf61b8..90fdcff 100644 --- a/kernel.c +++ b/kernel.c @@ -10,6 +10,8 @@ asm(".asciz \"kernel start\\n\""); #include "drivers/uart.h" #include "fs/fs.h" #include "lib/string.h" +#include "proc.h" + void _start() { load_gdt(); @@ -28,20 +30,19 @@ void _start() { } else { printk("failed to read file\n"); } - printk("\n> "); - while (1) { if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) { qemu_shutdown(); - } else if (!strncmp("run ", kbd_buf, kbd_buf_size)) { + } else if (!strncmp("run ", kbd_buf, 4)) { kbd_buf[kbd_buf_size-1] = '\0'; - // const char* cmd = kbd_buf + 4; - // run_elf(cmd); + const char* cmd = kbd_buf + 4; + run_elf(cmd); } else { - printk("unknown command, try: halt\n> "); + printk("unknown command, try: halt"); } kbd_buf_size = 0; + printk("\n> "); } asm("hlt"); } diff --git a/lib/string.c b/lib/string.c index 99e1171..e269955 100644 --- a/lib/string.c +++ b/lib/string.c @@ -26,3 +26,10 @@ int strncmp(const char* s1, const char* s2, size_t size) { } return (unsigned char)(*s1) - (unsigned char)(*s2); } + +void memset(void* b, char c, size_t len) { + char* p = b; + for (size_t i = 0; i < len; ++i) { + p[i] = c; + } +} diff --git a/lib/string.h b/lib/string.h index 584f7d7..6184c33 100644 --- a/lib/string.h +++ b/lib/string.h @@ -4,3 +4,4 @@ typedef unsigned size_t; void kmemmove(char* dst, char* src, size_t size); int strncmp(const char* s1, const char* s2, size_t size); +void memset(void* b, char c, size_t len); diff --git a/proc.c b/proc.c new file mode 100644 index 0000000..3c19495 --- /dev/null +++ b/proc.c @@ -0,0 +1,62 @@ +#include "elf.h" +#include "proc.h" +#include "fs/fs.h" +#include "cpu/gdt.h" +#include "cpu/isr.h" +#include "lib/mem.h" +#include "lib/string.h" +#include "console.h" + +struct context { + // matches the behavior of swtch() + uint32_t edi, esi, ebp, ebx; + uint32_t eip; // return address for swtch() +}; + +struct kstack { + uint32_t space[400]; + struct context context; + registers_t trapframe; +}; + +struct vm { + void *kernel_thread; + void *user_thread; + struct kstack *user_kstack; +} *vm; + +void trapret(); +void swtch(void** oldstack, void* newstack); + +void run_elf(const char* name) { + if (!vm) { + vm = kmalloc(sizeof(struct vm)); + vm->user_kstack = kmalloc(sizeof(struct kstack)); + } + if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) { + printk(name); + printk(": file not found\n"); + return; + } + Elf32_Ehdr *hdr = (void*)USER_BASE; + + struct kstack *u = vm->user_kstack; + memset(u, 0, sizeof(*u)); + u->context.eip = (uint32_t)trapret; + + registers_t *tf = &u->trapframe; + tf->eip = hdr->e_entry; + tf->cs = (SEG_UCODE << 3) | DPL_USER; + tf->ds = (SEG_UDATA << 3) | DPL_USER; + tf->es = tf->ds; + tf->fs = tf->ds; + tf->gs = tf->ds; + tf->ss = tf->ds; + tf->eflags = FL_IF; + tf->useresp = USER_STACK_BASE; + + // initialization done, now switch to the process + swtch(&vm->kernel_thread, &u->context); + + // process has finished +} diff --git a/proc.h b/proc.h new file mode 100644 index 0000000..86a4322 --- /dev/null +++ b/proc.h @@ -0,0 +1,3 @@ +#pragma once + +void run_elf(const char* name); From 5f3cbb988dc3b9ead7165f3d77c41248e76f80b4 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 14 Dec 2022 12:28:30 +0300 Subject: [PATCH 42/56] Fix TSS init. --- cpu/gdt.c | 64 +++++++++++-------------------------------------------- cpu/gdt.h | 44 ++++++++++++++++++++++++++++++++++++++ proc.c | 13 ++++++++--- 3 files changed, 67 insertions(+), 54 deletions(-) diff --git a/cpu/gdt.c b/cpu/gdt.c index f5b2572..f2356a3 100644 --- a/cpu/gdt.c +++ b/cpu/gdt.c @@ -1,4 +1,5 @@ #include "gdt.h" +#include "../lib/string.h" #include @@ -18,8 +19,6 @@ struct seg_desc_t { uint8_t base_31_24; // High bits of segment base address } __attribute__((packed)); -typedef unsigned uint; - #define SEG(type, base, lim, dpl) (struct seg_desc_t) \ { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ @@ -38,66 +37,29 @@ void init_seg_desc() { seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); } -typedef uint16_t ushort; - -struct taskstate { - uint link; // Old ts selector - uint esp0; // Stack pointers and segment selectors - ushort ss0; // after an increase in privilege level - ushort padding1; - uint *esp1; - ushort ss1; - ushort padding2; - uint *esp2; - ushort ss2; - ushort padding3; - void *cr3; // Page directory base - uint *eip; // Saved state from last task switch - uint eflags; - uint eax; // More saved state (registers) - uint ecx; - uint edx; - uint ebx; - uint *esp; - uint *ebp; - uint esi; - uint edi; - ushort es; // Even more saved state (segment selectors) - ushort padding4; - ushort cs; - ushort padding5; - ushort ss; - ushort padding6; - ushort ds; - ushort padding7; - ushort fs; - ushort padding8; - ushort gs; - ushort padding9; - ushort ldt; - ushort padding10; - ushort t; // Trap on task switch - ushort iomb; // I/O map base address -} tss; - struct gdt_desc_t { uint16_t size; void* ptr; } __attribute__((packed)); void load_gdt() { - seg_desc[SEG_TSS] = SEG16(STS_T32A, &tss, sizeof(tss)-1, 0); - seg_desc[SEG_TSS].s = 0; - tss.ss0 = SEG_KDATA << 3; - tss.esp0 = KERN_STACK_BASE; - // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit - // forbids I/O instructions (e.g., inb and outb) from user space - tss.iomb = (ushort) 0xFFFF; init_seg_desc(); struct gdt_desc_t gdt_desc; gdt_desc.size = sizeof(seg_desc) - 1; gdt_desc.ptr = seg_desc; asm("lgdt %0": : "m"(gdt_desc)); +} + +void switchuvm(struct taskstate *tss, void* esp) { + memset(tss, 0, sizeof(*tss)); + seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0); + seg_desc[SEG_TSS].s = 0; + tss->ss0 = SEG_KDATA << 3; + tss->esp0 = (uint)esp; + // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit + // forbids I/O instructions (e.g., inb and outb) from user space + tss->iomb = (ushort) 0xFFFF; + asm("ltr %0": : "r"((ushort)(SEG_TSS << 3))); } diff --git a/cpu/gdt.h b/cpu/gdt.h index 0f174b8..00dd40b 100644 --- a/cpu/gdt.h +++ b/cpu/gdt.h @@ -29,5 +29,49 @@ #define KERN_STACK_BASE 0x90000 #ifndef __ASSEMBLER__ +typedef unsigned uint; +typedef unsigned short ushort; + +struct taskstate { + uint link; // Old ts selector + uint esp0; // Stack pointers and segment selectors + ushort ss0; // after an increase in privilege level + ushort padding1; + uint *esp1; + ushort ss1; + ushort padding2; + uint *esp2; + ushort ss2; + ushort padding3; + void *cr3; // Page directory base + uint *eip; // Saved state from last task switch + uint eflags; + uint eax; // More saved state (registers) + uint ecx; + uint edx; + uint ebx; + uint *esp; + uint *ebp; + uint esi; + uint edi; + ushort es; // Even more saved state (segment selectors) + ushort padding4; + ushort cs; + ushort padding5; + ushort ss; + ushort padding6; + ushort ds; + ushort padding7; + ushort fs; + ushort padding8; + ushort gs; + ushort padding9; + ushort ldt; + ushort padding10; + ushort t; // Trap on task switch + ushort iomb; // I/O map base address +}; + void load_gdt(); +void switchuvm(struct taskstate *tss, void* esp); #endif diff --git a/proc.c b/proc.c index 3c19495..6b19510 100644 --- a/proc.c +++ b/proc.c @@ -17,12 +17,18 @@ struct kstack { uint32_t space[400]; struct context context; registers_t trapframe; + char bottom[]; +}; + +struct task { + struct taskstate tss; + struct kstack stack; }; struct vm { void *kernel_thread; void *user_thread; - struct kstack *user_kstack; + struct task *user_task; } *vm; void trapret(); @@ -31,7 +37,8 @@ void swtch(void** oldstack, void* newstack); void run_elf(const char* name) { if (!vm) { vm = kmalloc(sizeof(struct vm)); - vm->user_kstack = kmalloc(sizeof(struct kstack)); + vm->user_task = kmalloc(sizeof(struct task)); + switchuvm(&vm->user_task->tss, vm->user_task->stack.bottom); } if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) { printk(name); @@ -40,7 +47,7 @@ void run_elf(const char* name) { } Elf32_Ehdr *hdr = (void*)USER_BASE; - struct kstack *u = vm->user_kstack; + struct kstack *u = &vm->user_task->stack; memset(u, 0, sizeof(*u)); u->context.eip = (uint32_t)trapret; From 069625a6e06c35fec1e5116b32fe1d567e33b3a7 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 14 Dec 2022 12:43:33 +0300 Subject: [PATCH 43/56] Enable int 0x84 from userspace. --- cpu/idt.c | 21 +++++++++++++++------ cpu/isr.h | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/cpu/idt.c b/cpu/idt.c index 9ae3674..60cf250 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -1,4 +1,5 @@ #include "isr.h" +#include "gdt.h" #include "../drivers/port.h" #include "../console.h" @@ -10,7 +11,10 @@ typedef struct { uint16_t low_offset; uint16_t selector; uint8_t always0; - uint8_t flags; + uint8_t type: 4; + uint8_t s: 1; + uint8_t dpl: 2; + uint8_t p: 1; uint16_t high_offset; } __attribute__((packed)) idt_gate_t; @@ -19,13 +23,17 @@ idt_gate_t idt[IDT_HANDLERS]; #define low_16(address) (uint16_t)((address) & 0xFFFF) #define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF) -void set_idt_gate(int n, uint32_t handler) { +#define STS_IG32 0xE // 32-bit Interrupt Gate +#define STS_TG32 0xF // 32-bit Trap Gate + +void set_idt_gate(int n, _Bool istrap, uint32_t handler, uint8_t dpl) { idt[n].low_offset = low_16(handler); idt[n].selector = 0x08; // see GDT idt[n].always0 = 0; - // 0x8E = 1 00 0 1 110 - // P DPL 0 D Type - idt[n].flags = 0x8E; + idt[n].type = istrap ? STS_TG32 : STS_IG32; + idt[n].s = 0; + idt[n].dpl = dpl; + idt[n].p = 1; idt[n].high_offset = high_16(handler); } @@ -37,8 +45,9 @@ void init_idt() { panic("handler table empty\n"); } for (int i = 0; i < IDT_HANDLERS; i++) { - set_idt_gate(i, default_handlers[i]); + set_idt_gate(i, 0, default_handlers[i], 0); } + set_idt_gate(T_SYSCALL, 1, default_handlers[T_SYSCALL], DPL_USER); } const char * const exception_messages[] = { diff --git a/cpu/isr.h b/cpu/isr.h index de12f30..e7d52fd 100644 --- a/cpu/isr.h +++ b/cpu/isr.h @@ -18,6 +18,7 @@ enum { IRQ13, IRQ14, IRQ15, + T_SYSCALL = 0x84, }; /* Struct which aggregates many registers. From a6598886f0f46eab06c624cabadf82403c4c22cc Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 14 Dec 2022 16:50:10 +0300 Subject: [PATCH 44/56] Add two syscalls. --- Makefile | 4 ++-- cpu/idt.c | 22 ++++++++++++++++++++++ cpu/isr.h | 1 - cpu/vectors.S | 4 ++-- proc.c | 6 ++++++ proc.h | 1 + syscall.h | 9 +++++++++ user/crt.c | 9 ++++++++- user/greet.c | 7 +++++++ 9 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 syscall.h create mode 100644 user/greet.c diff --git a/Makefile b/Makefile index 9615153..2e8fc22 100644 --- a/Makefile +++ b/Makefile @@ -47,8 +47,8 @@ debug-nox: image.bin -ex "break _start" \ -ex "continue" -fs.img: kernel.bin tools/mkfs user/false - tools/mkfs $@ $< user/false +fs.img: kernel.bin tools/mkfs user/false user/greet + tools/mkfs $@ $< user/false user/greet LDFLAGS=-m elf_i386 diff --git a/cpu/idt.c b/cpu/idt.c index 60cf250..4b4137c 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -1,5 +1,7 @@ #include "isr.h" #include "gdt.h" +#include "../syscall.h" +#include "../proc.h" #include "../drivers/port.h" #include "../console.h" @@ -83,6 +85,26 @@ void register_interrupt_handler(uint8_t i, isr_t handler) { } void trap(registers_t *r) { + if (r->int_no == T_SYSCALL) { + switch (r->eax) { + case SYS_exit: + if (r->ebx == 0) { + printk("* success\n"); + } else { + printk("* failure\n"); + } + killproc(); + case SYS_greet: + printk("Hello world!\n"); + r->eax = 0; + break; + default: + printk("Unknown syscall\n"); + r->eax = -1; + } + return; + } + if (r->int_no < 32) { const char* msg = "Reserved"; if (r->int_no < ARRLEN(exception_messages)) { diff --git a/cpu/isr.h b/cpu/isr.h index e7d52fd..de12f30 100644 --- a/cpu/isr.h +++ b/cpu/isr.h @@ -18,7 +18,6 @@ enum { IRQ13, IRQ14, IRQ15, - T_SYSCALL = 0x84, }; /* Struct which aggregates many registers. diff --git a/cpu/vectors.S b/cpu/vectors.S index 21ddded..b1131fb 100644 --- a/cpu/vectors.S +++ b/cpu/vectors.S @@ -8,8 +8,8 @@ alltraps: pushl %gs pushal - //mov $10, %ax - //mov %ax, %ds + mov $0x10, %ax + mov %ax, %ds # Call trap(tf), where tf=%esp pushl %esp diff --git a/proc.c b/proc.c index 6b19510..48d9800 100644 --- a/proc.c +++ b/proc.c @@ -67,3 +67,9 @@ void run_elf(const char* name) { // process has finished } + +_Noreturn void killproc() { + void* task_stack; + swtch(&task_stack, vm->kernel_thread); + __builtin_unreachable(); +} diff --git a/proc.h b/proc.h index 86a4322..54ede88 100644 --- a/proc.h +++ b/proc.h @@ -1,3 +1,4 @@ #pragma once void run_elf(const char* name); +_Noreturn void killproc(); diff --git a/syscall.h b/syscall.h new file mode 100644 index 0000000..4c9ba03 --- /dev/null +++ b/syscall.h @@ -0,0 +1,9 @@ +#pragma once + +enum { + T_SYSCALL = 0x84, + SYS_exit = 0, + SYS_greet = 1, +}; + +int syscall(int call, int arg); diff --git a/user/crt.c b/user/crt.c index 17320ea..5032810 100644 --- a/user/crt.c +++ b/user/crt.c @@ -1,8 +1,15 @@ +#include "../syscall.h" + int main(); +int syscall(int call, int arg) { + asm("int $0x84": "+a"(call) : "b"(arg)); + return call; +} + _Noreturn void _exit(int exit_status) { - asm("int $0x84": : "a"(0), "b"(exit_status)); + syscall(SYS_exit, exit_status); __builtin_unreachable(); } diff --git a/user/greet.c b/user/greet.c new file mode 100644 index 0000000..28b539b --- /dev/null +++ b/user/greet.c @@ -0,0 +1,7 @@ +#include "../syscall.h" + +int main(void) { + syscall(SYS_greet, 0); + syscall(SYS_greet, 0); + return 0; +} From c3992d8db5ff8ca9ec0a23eca30d2e1aa2f58384 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 14 Dec 2022 17:02:15 +0300 Subject: [PATCH 45/56] Add user/div0. --- Makefile | 4 ++-- user/div0.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 user/div0.c diff --git a/Makefile b/Makefile index 2e8fc22..76c3031 100644 --- a/Makefile +++ b/Makefile @@ -47,8 +47,8 @@ debug-nox: image.bin -ex "break _start" \ -ex "continue" -fs.img: kernel.bin tools/mkfs user/false user/greet - tools/mkfs $@ $< user/false user/greet +fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 + tools/mkfs $@ $< user/false user/greet user/div0 LDFLAGS=-m elf_i386 diff --git a/user/div0.c b/user/div0.c new file mode 100644 index 0000000..76c2038 --- /dev/null +++ b/user/div0.c @@ -0,0 +1,5 @@ +int main(void) { + volatile int x = 1, y = 0; + x /= y; + return 0; +} From 7bf039da8bd13e8a018d8117326961aa3a040831 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 14 Dec 2022 17:17:27 +0300 Subject: [PATCH 46/56] Improve prompting. --- kernel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel.c b/kernel.c index 90fdcff..db26e72 100644 --- a/kernel.c +++ b/kernel.c @@ -30,6 +30,7 @@ void _start() { } else { printk("failed to read file\n"); } + printk("\n> "); while (1) { if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) { @@ -39,7 +40,7 @@ void _start() { const char* cmd = kbd_buf + 4; run_elf(cmd); } else { - printk("unknown command, try: halt"); + printk("unknown command, try: halt | run CMD"); } kbd_buf_size = 0; printk("\n> "); From c84d214daa507fdc267acfc92b0895b417c9553a Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Wed, 25 Jan 2023 14:56:38 +0400 Subject: [PATCH 47/56] Add drivers/pit.c by 3Hren. --- Makefile | 2 +- drivers/pit.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pit.h | 8 ++++++ kernel.c | 7 +++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 drivers/pit.c create mode 100644 drivers/pit.h diff --git a/Makefile b/Makefile index ab4d4f6..6593b63 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ image.bin: mbr.bin fs.img kernel.bin: kernel.o console.o drivers/vga.o drivers/keyboard.o \ drivers/ata.o cpu/vectors.o cpu/idt.o cpu/gdt.o drivers/uart.o \ - fs/fs.o lib/mem.o lib/string.o proc.o cpu/swtch.o + fs/fs.o lib/mem.o lib/string.o proc.o cpu/swtch.o drivers/pit.o $(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c diff --git a/drivers/pit.c b/drivers/pit.c new file mode 100644 index 0000000..7b8edc1 --- /dev/null +++ b/drivers/pit.c @@ -0,0 +1,76 @@ +#include "pit.h" +#include "port.h" +#include "../cpu/isr.h" + +enum { + PIT_CRYSTAL_HZ = 1193182, + + CLOCK_PRECISION_HZ = 100, + + PIT_PROGRAM_REG = PIT_CRYSTAL_HZ / CLOCK_PRECISION_HZ, + + PIT_SELECT_CHANNEL0 = 0x0, + PIT_SELECT_CHANNEL1 = 0x1, + PIT_SELECT_CHANNEL2 = 0x2, + PIT_SELECT_CHANNEL_RB = 0x3, + PIT_ACCESS_MODE_LATCH_COUNT_VALUE_COMMAND = 0x0, + PIT_ACCESS_MODE_LOBYTE_ONLY = 0x1, + PIT_ACCESS_MODE_HIBYTE_ONLY = 0x2, + PIT_ACCESS_MODE_LOHIBYTE = 0x3, + PIT_MODE_INTERRUPT_ON_TERMINAL_COUNT = 0x0, + PIT_MODE_HW_ONESHOT = 0x1, + PIT_MODE_RATE_GENERATOR = 0x2, + PIT_MODES_SQUARE_WAVE_GENERATOR = 0x3, + PIT_MODES_SW_TRIGGERRED_STROBE = 0x4, + PIT_MODES_HW_TRIGGERRED_STROBE = 0x5, +}; + +static timer_callback callbacks[100]; +static int registered_callbacks = 0; + +void add_timer_callback(timer_callback tc) { + callbacks[registered_callbacks++] = tc; +} + +static void timer_interrupt_handler(registers_t *r) { + for (int i = 0; i < registered_callbacks; ++i) { + callbacks[i](); + } +} + +struct pit_command_t { + unsigned char bcd : 1; + unsigned char operating_mode : 3; + unsigned char access_mode : 2; + unsigned char select_channel : 2; +} __attribute__((packed)); + +static void dec_sleep_counter(void); + +void init_pit() { + struct pit_command_t cmd = { + .select_channel = PIT_SELECT_CHANNEL0, + .access_mode = PIT_ACCESS_MODE_LOHIBYTE, + .operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR, + .bcd = 0, + }; + port_byte_out(0x43, *(unsigned char *)(&cmd)); + port_byte_out(0x40, PIT_PROGRAM_REG & 0xff); + port_byte_out(0x40, (PIT_PROGRAM_REG & 0xff00) >> 8); + + register_interrupt_handler(IRQ0, timer_interrupt_handler); + add_timer_callback(dec_sleep_counter); +} + +static int sleep_counter = 0; + +static void dec_sleep_counter(void) { + sleep_counter--; +} + +void msleep(int ms) { + sleep_counter = ms / 10; + while (sleep_counter > 0) { + asm("hlt"); + } +} diff --git a/drivers/pit.h b/drivers/pit.h new file mode 100644 index 0000000..01b72ad --- /dev/null +++ b/drivers/pit.h @@ -0,0 +1,8 @@ +#pragma once + +typedef void (*timer_callback)(void); + +void init_pit(void); +void add_timer_callback(timer_callback tc); + +void msleep(int ms); diff --git a/kernel.c b/kernel.c index db26e72..e502a2c 100644 --- a/kernel.c +++ b/kernel.c @@ -7,6 +7,7 @@ asm(".asciz \"kernel start\\n\""); #include "drivers/vga.h" #include "drivers/ata.h" #include "drivers/misc.h" +#include "drivers/pit.h" #include "drivers/uart.h" #include "fs/fs.h" #include "lib/string.h" @@ -16,6 +17,7 @@ asm(".asciz \"kernel start\\n\""); void _start() { load_gdt(); init_keyboard(); + init_pit(); uartinit(); load_idt(); sti(); @@ -35,6 +37,11 @@ void _start() { if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) { qemu_shutdown(); + } else if (!strncmp("work\n", kbd_buf, kbd_buf_size)) { + for (int i = 0; i < 5; ++i) { + msleep(1000); + printk("."); + } } else if (!strncmp("run ", kbd_buf, 4)) { kbd_buf[kbd_buf_size-1] = '\0'; const char* cmd = kbd_buf + 4; From d846d0f0c6a76176d18e21b00917999aeb3c9af0 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 31 Jan 2023 16:11:20 +0400 Subject: [PATCH 48/56] Add SYS_putc and SYS_puts. --- Makefile | 4 ++-- cpu/idt.c | 22 ++++++++++++++++++++++ syscall.h | 2 ++ user/shout.c | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 user/shout.c diff --git a/Makefile b/Makefile index 5078804..ad49a62 100644 --- a/Makefile +++ b/Makefile @@ -69,8 +69,8 @@ debug-nox: image.bin -ex "break _start" \ -ex "continue" -fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 - tools/mkfs $@ $< user/false user/greet user/div0 +fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 user/shout + tools/mkfs $@ $< user/false user/greet user/div0 user/shout LDFLAGS=-m elf_i386 diff --git a/cpu/idt.c b/cpu/idt.c index 7adc01d..ff4d3d8 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -109,6 +109,21 @@ void trap(registers_t *r) { } } +static void* get_userspace_ptr(uint32_t ptr) { + if (ptr >= 0xffffffff - USER_BASE) { + return 0; + } + return (void*)(ptr + USER_BASE); +} + +static int handle_puts(const char* s) { + if (!s) { + return -1; + } + printk(s); + return 0; +} + static void handle_syscall(registers_t* r) { switch (r->eax) { case SYS_exit: @@ -122,6 +137,13 @@ static void handle_syscall(registers_t* r) { printk("Hello world!\n"); r->eax = 0; break; + case SYS_putc: + printk((const char[]){r->ebx, '\0'}); + r->eax = 0; + break; + case SYS_puts: + r->eax = handle_puts(get_userspace_ptr(r->ebx)); + break; default: printk("Unknown syscall\n"); r->eax = -1; diff --git a/syscall.h b/syscall.h index 4c9ba03..7973ecb 100644 --- a/syscall.h +++ b/syscall.h @@ -4,6 +4,8 @@ enum { T_SYSCALL = 0x84, SYS_exit = 0, SYS_greet = 1, + SYS_putc = 2, + SYS_puts = 3, }; int syscall(int call, int arg); diff --git a/user/shout.c b/user/shout.c new file mode 100644 index 0000000..4f77e42 --- /dev/null +++ b/user/shout.c @@ -0,0 +1,16 @@ +#include "../syscall.h" +#include + +void userspace_puts(const char* s) { + char c; + while ((c = *s++)) { + syscall(SYS_putc, c); + } +} + +int main() { + const char* spell = "cra cra trif traf not sgnieflet\n"; + userspace_puts(spell); + syscall(SYS_puts, (uint32_t)spell); + return 0; +} From 506472a106858d9635482407129c56bf251654b6 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 17 Jan 2025 20:44:33 +0400 Subject: [PATCH 49/56] Enable paging, run kernel in high half. --- Makefile | 8 +++--- bootmain.c | 4 +-- cpu/memlayout.h | 3 +++ kernel.c | 2 +- kernel/kstart.S | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 cpu/memlayout.h create mode 100644 kernel/kstart.S diff --git a/Makefile b/Makefile index a5eeab3..ef4a381 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \ -mno-sse \ -Wall -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) -ASMFLAGS = -m32 -ffreestanding -c -g +ASMFLAGS = -m32 -ffreestanding -c -g -I. ifeq ($(LLVM),on) #AS=llvm-as @@ -24,7 +24,7 @@ ASMFLAGS = -target elf-i386 -ffreestanding -c -g LDKERNELFLAGS = --script=script.ld endif -OBJECTS = kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ +OBJECTS = kernel/kstart.o kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ cpu/idt.o cpu/gdt.o cpu/swtch.o cpu/vectors.o lib/mem.o proc.o lib/string.o \ fs/fs.o drivers/ata.o lib/mem.o lib/string.o proc.o drivers/pit.o @@ -91,13 +91,13 @@ fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 user/shout LDFLAGS=-m elf_i386 user/%: user/%.o user/crt.o - $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ + $(LD) $(LDFLAGS) -o $@ -Ttext 0x10000 $^ image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: $(OBJECTS) - $(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0x9000 $^ + $(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0xf0009000 $^ bootmain.o: bootmain.c $(CC) $(CFLAGS) -Os -c $< -o $@ diff --git a/bootmain.c b/bootmain.c index 95a10fd..77d3263 100644 --- a/bootmain.c +++ b/bootmain.c @@ -40,7 +40,7 @@ bootmain(void) ph = (Elf32_Phdr*)((uchar*)elf + elf->e_phoff); eph = ph + elf->e_phnum; for(; ph < eph; ph++) { - pa = (uchar*)ph->p_paddr; + pa = (uchar*)(ph->p_paddr & 0x0fffffff); readseg(pa, ph->p_filesz, ph->p_offset); if(ph->p_memsz > ph->p_filesz) stosb(pa + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz); @@ -48,7 +48,7 @@ bootmain(void) // Call the entry point from the ELF header. // Does not return! - entry = (void(*)(void))(elf->e_entry); + entry = (void(*)(void))(elf->e_entry & 0x0fffffff); entry(); } diff --git a/cpu/memlayout.h b/cpu/memlayout.h new file mode 100644 index 0000000..0702d52 --- /dev/null +++ b/cpu/memlayout.h @@ -0,0 +1,3 @@ +#pragma once + +#define KERNBASE 0xf0000000 \ No newline at end of file diff --git a/kernel.c b/kernel.c index e502a2c..fa83a72 100644 --- a/kernel.c +++ b/kernel.c @@ -14,7 +14,7 @@ asm(".asciz \"kernel start\\n\""); #include "proc.h" -void _start() { +void kmain() { load_gdt(); init_keyboard(); init_pit(); diff --git a/kernel/kstart.S b/kernel/kstart.S new file mode 100644 index 0000000..73f49fb --- /dev/null +++ b/kernel/kstart.S @@ -0,0 +1,71 @@ +#include "cpu/memlayout.h" + +/* + Memory layout at this point (see https://wiki.osdev.org/Memory_Map_(x86) for more details): + 0x00500 - 0x08fff: usable memory + 0x09000 – 0x14fff: kernel code and global data + 0x15000 - 0x7ffff: usable memory + 0x80000 - 0xfffff: BDA and upper memory + 0x100000 - 0x8000000 (1 MiB - 128 MiB): usable memory +*/ + .intel_syntax noprefix + .global _start + .asciz "kernel start\n" +_start: +.code32 + mov dword ptr [patch_addr - KERNBASE], offset gdt32_begin - KERNBASE + lgdt gdtr32 - KERNBASE + + // zero our PD at 0x1000 and PT at 0x2000 + xor eax, eax + mov ecx, 2048 + rep stosd + + // Set up the page table for the lowest 4 MiB: PD[0] -> PT + mov dword ptr [0x1000], 0x2000 | 0x3 + + // KERNBASE = 0xF000_0000 + // Use the same page table for the first 4 MiB after KERNBASE: PD[0x3c0] -> PT + mov dword ptr [0x1000 + (0x3c0 * 4)], 0x2000 | 0x3 + + // Identity map the first 2 MiB + mov edi, 0x2000 // Start of the PT + mov ebx, 0x00000003 // Present, R/W + mov ecx, 512 // 2 MiB / 4 KiB = 512 +SetEntry: + mov dword ptr [edi], ebx + add ebx, 0x1000 + add edi, 4 + loop SetEntry + + // Load physical address of PD into CR3 + mov edi, 0x1000 + mov cr3, edi + + // Enable paging + mov eax, cr0 + or eax, 1 << 31 // Set the PG bit + mov cr0, eax + + .att_syntax + jmp $0x8, $loadseg - KERNBASE + .intel_syntax noprefix + +loadseg: + // jump to the high half + add esp, KERNBASE + lea eax, kmain + jmp eax + +.align 16 +gdtr32: + .word gdt32_end - gdt32_begin - 1 +patch_addr: + .long gdt32_begin - KERNBASE + +.align 16 +gdt32_begin: + .long 0x00000000, 0x00000000 # Null descriptor + .long 0x0000ffff, 0x00cf9a00 # Code segment + .long 0x0000ffff, 0x00cf9200 # Data segment +gdt32_end: From 0a1928a02c630603cfa006ee9afed941094ac256 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 17 Jan 2025 21:46:47 +0400 Subject: [PATCH 50/56] Fix build. --- Makefile | 3 +- cpu/memlayout.h | 7 ++++- cpu/x86.h | 13 ++++++++ drivers/keyboard.c | 11 +++---- lib/mem.c | 74 +++++++++++++++++++++++++++++++++++++++++----- lib/mem.h | 6 ++-- lib/string.c | 7 ----- proc.c | 4 +-- 8 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 cpu/x86.h diff --git a/Makefile b/Makefile index ef4a381..2fbbb6e 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ endif CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \ -mno-sse \ + -I. \ -Wall -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) ASMFLAGS = -m32 -ffreestanding -c -g -I. @@ -75,7 +76,7 @@ debug: image.bin qemu-system-i386 -drive format=raw,file=$< -s -S & $(GDB) kernel.bin \ -ex "target remote localhost:1234" \ - -ex "break _start" \ + -ex "break kmain" \ -ex "continue" debug-nox: image.bin diff --git a/cpu/memlayout.h b/cpu/memlayout.h index 0702d52..d45326e 100644 --- a/cpu/memlayout.h +++ b/cpu/memlayout.h @@ -1,3 +1,8 @@ #pragma once -#define KERNBASE 0xf0000000 \ No newline at end of file +#define KERNBASE 0xf0000000 +#define PGSIZE 0x1000 +#define PHYSTOP 0x8000000 + +#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) +#define V2P(a) (((uintptr_t) (a)) - KERNBASE) diff --git a/cpu/x86.h b/cpu/x86.h new file mode 100644 index 0000000..ae0488e --- /dev/null +++ b/cpu/x86.h @@ -0,0 +1,13 @@ +#pragma once + +static inline void +stosl(void *addr, int data, int cnt) +{ + asm volatile("cld; rep stosl" : : "D"(addr), "c"(cnt), "a"(data) : "memory"); +} + +static inline void +stosb(void *addr, unsigned char data, int cnt) +{ + asm volatile("cld; rep stosb" : : "D"(addr), "c"(cnt), "a"(data) : "memory"); +} diff --git a/drivers/keyboard.c b/drivers/keyboard.c index d7ade6e..ad38a9b 100644 --- a/drivers/keyboard.c +++ b/drivers/keyboard.c @@ -1,8 +1,9 @@ #include "keyboard.h" -#include "../cpu/isr.h" -#include "../console.h" +#include "cpu/isr.h" +#include "cpu/memlayout.h" +#include "console.h" #include "port.h" -#include "../lib/mem.h" +#include "lib/mem.h" static const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', @@ -12,7 +13,7 @@ static const char sc_ascii[] = { 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' ', }; -enum { kbd_buf_capacity = 1024 }; +enum { kbd_buf_capacity = PGSIZE }; static void interrupt_handler(registers_t *r) { uint8_t scancode = port_byte_in(0x60); @@ -30,7 +31,7 @@ char* kbd_buf; unsigned kbd_buf_size; void init_keyboard() { - kbd_buf = kmalloc(kbd_buf_capacity); + kbd_buf = kalloc(); register_interrupt_handler(IRQ1, interrupt_handler); } diff --git a/lib/mem.c b/lib/mem.c index 0bf188b..c59f685 100644 --- a/lib/mem.c +++ b/lib/mem.c @@ -1,12 +1,70 @@ +// Physical memory allocator, intended to allocate +// memory for user processes, kernel stacks, page table pages, +// and pipe buffers. Allocates 4096-byte pages. + +#include #include "mem.h" +#include "console.h" +#include "cpu/memlayout.h" +#include "cpu/x86.h" -static void* freeptr; +struct run { + struct run *next; +}; -void* kmalloc(size_t size) { - if (!freeptr) { - freeptr = (void*)(1<<20); - } - void* result = freeptr; - freeptr += size; - return result; +struct { + struct run *freelist; +} kmem; + +void +freerange(void *vstart, void *vend) +{ + char *p; + p = (char*)PGROUNDUP((uintptr_t)vstart); + for(; p + PGSIZE <= (char*)vend; p += PGSIZE) + kfree(p); +} + +void* +memset(void *dst, unsigned c, uint64_t n) +{ + if ((uintptr_t)dst%4 == 0 && n%4 == 0){ + c &= 0xFF; + stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4); + } else + stosb(dst, c, n); + return dst; +} + +// Free the page of physical memory pointed at by v, +// which normally should have been returned by a +// call to kalloc(). (The exception is when +// initializing the allocator; see kinit above.) +void +kfree(char *v) +{ + struct run *r; + + if((uintptr_t)v % PGSIZE || V2P(v) >= PHYSTOP) + panic("kfree"); + + // Fill with junk to catch dangling refs. + memset(v, 1, PGSIZE); + + r = (struct run*)v; + r->next = kmem.freelist; + kmem.freelist = r; +} + +// Allocate one 4096-byte page of physical memory. +// Returns a pointer that the kernel can use. +// Returns 0 if the memory cannot be allocated. +void* kalloc(void) +{ + struct run *r; + + r = kmem.freelist; + if(r) + kmem.freelist = r->next; + return (char*)r; } diff --git a/lib/mem.h b/lib/mem.h index 1a77610..8af959f 100644 --- a/lib/mem.h +++ b/lib/mem.h @@ -1,5 +1,5 @@ #pragma once -typedef unsigned size_t; - -void* kmalloc(size_t size); +void freerange(void *vstart, void *vend); +void* kalloc(void); +void kfree(char*); diff --git a/lib/string.c b/lib/string.c index e269955..99e1171 100644 --- a/lib/string.c +++ b/lib/string.c @@ -26,10 +26,3 @@ int strncmp(const char* s1, const char* s2, size_t size) { } return (unsigned char)(*s1) - (unsigned char)(*s2); } - -void memset(void* b, char c, size_t len) { - char* p = b; - for (size_t i = 0; i < len; ++i) { - p[i] = c; - } -} diff --git a/proc.c b/proc.c index 6ec0971..47b9432 100644 --- a/proc.c +++ b/proc.c @@ -36,8 +36,8 @@ void swtch(void** oldstack, void* newstack); void run_elf(const char* name) { if (!vm) { - vm = kmalloc(sizeof(struct vm)); - vm->user_task = kmalloc(sizeof(struct task)); + vm = kalloc(); + vm->user_task = kalloc(); switchuvm(&vm->user_task->tss, vm->user_task->stack.bottom); } if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) { From e1bcd70747ea5c9b7ab64ebf1add36ba7abda2dc Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 17 Jan 2025 23:29:01 +0400 Subject: [PATCH 51/56] Add kvmalloc. --- Makefile | 6 +++--- cpu/gdt.c | 1 + cpu/memlayout.h | 24 +++++++++++++++++++++++- cpu/x86.h | 6 ++++++ drivers/keyboard.c | 2 +- drivers/vga.c | 3 ++- kernel.c | 8 ++++++-- kernel/kstart.S | 11 ++++++++--- {lib => kernel}/mem.c | 0 kernel/mem.h | 14 ++++++++++++++ kernel/vm.c | 18 ++++++++++++++++++ lib/mem.h | 5 ----- lib/string.h | 1 - proc.c | 2 +- 14 files changed, 83 insertions(+), 18 deletions(-) rename {lib => kernel}/mem.c (100%) create mode 100644 kernel/mem.h create mode 100644 kernel/vm.c delete mode 100644 lib/mem.h diff --git a/Makefile b/Makefile index 2fbbb6e..dbaa2ec 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,8 @@ LDKERNELFLAGS = --script=script.ld endif OBJECTS = kernel/kstart.o kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ - cpu/idt.o cpu/gdt.o cpu/swtch.o cpu/vectors.o lib/mem.o proc.o lib/string.o \ - fs/fs.o drivers/ata.o lib/mem.o lib/string.o proc.o drivers/pit.o + cpu/idt.o cpu/gdt.o cpu/swtch.o cpu/vectors.o kernel/mem.o proc.o lib/string.o \ + fs/fs.o drivers/ata.o lib/string.o proc.o drivers/pit.o kernel/vm.o run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio @@ -98,7 +98,7 @@ image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: $(OBJECTS) - $(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0xf0009000 $^ + $(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0x80009000 $^ bootmain.o: bootmain.c $(CC) $(CFLAGS) -Os -c $< -o $@ diff --git a/cpu/gdt.c b/cpu/gdt.c index f511ef0..0b046c9 100644 --- a/cpu/gdt.c +++ b/cpu/gdt.c @@ -1,5 +1,6 @@ #include "gdt.h" #include "../lib/string.h" +#include "kernel/mem.h" #include diff --git a/cpu/memlayout.h b/cpu/memlayout.h index d45326e..33a5818 100644 --- a/cpu/memlayout.h +++ b/cpu/memlayout.h @@ -1,8 +1,30 @@ #pragma once -#define KERNBASE 0xf0000000 +#define KERNBASE 0x80000000 #define PGSIZE 0x1000 #define PHYSTOP 0x8000000 #define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) + +#define PDXSHIFT 22 // offset of PDX in a linear address + +#define PXMASK 0x3FF + +#ifndef __ASSEMBLER__ +#include #define V2P(a) (((uintptr_t) (a)) - KERNBASE) +#define P2V(a) ((void *)(((uintptr_t) (a)) + KERNBASE)) + +// page directory index +#define PDX(va) (((uintptr_t)(va) >> PDXSHIFT) & PXMASK) +#endif + +// Page table/directory entry flags. +#define PTE_P 0x001 // Present +#define PTE_W 0x002 // Writeable +#define PTE_U 0x004 // User +#define PTE_PWT 0x008 // Write-Through +#define PTE_PCD 0x010 // Cache-Disable +#define PTE_A 0x020 // Accessed +#define PTE_D 0x040 // Dirty +#define PTE_PS 0x080 // Page Size diff --git a/cpu/x86.h b/cpu/x86.h index ae0488e..fafd892 100644 --- a/cpu/x86.h +++ b/cpu/x86.h @@ -11,3 +11,9 @@ stosb(void *addr, unsigned char data, int cnt) { asm volatile("cld; rep stosb" : : "D"(addr), "c"(cnt), "a"(data) : "memory"); } + +static inline void +lcr3(uint32_t val) +{ + asm volatile("mov %0,%%cr3" : : "r" (val)); +} diff --git a/drivers/keyboard.c b/drivers/keyboard.c index ad38a9b..c78615f 100644 --- a/drivers/keyboard.c +++ b/drivers/keyboard.c @@ -3,7 +3,7 @@ #include "cpu/memlayout.h" #include "console.h" #include "port.h" -#include "lib/mem.h" +#include "kernel/mem.h" static const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', diff --git a/drivers/vga.c b/drivers/vga.c index 7cd44ee..ad0a364 100644 --- a/drivers/vga.c +++ b/drivers/vga.c @@ -1,7 +1,8 @@ #include "port.h" #include "../lib/string.h" +#include "cpu/memlayout.h" -static char* const video_memory = (char*) 0xb8000; +static char* const video_memory = (char*) (KERNBASE + 0xb8000); enum colors16 { black = 0, diff --git a/kernel.c b/kernel.c index fa83a72..98695fb 100644 --- a/kernel.c +++ b/kernel.c @@ -1,8 +1,7 @@ -asm(".asciz \"kernel start\\n\""); - #include "console.h" #include "cpu/isr.h" #include "cpu/gdt.h" +#include "cpu/memlayout.h" #include "drivers/keyboard.h" #include "drivers/vga.h" #include "drivers/ata.h" @@ -12,9 +11,14 @@ asm(".asciz \"kernel start\\n\""); #include "fs/fs.h" #include "lib/string.h" #include "proc.h" +#include "kernel/mem.h" void kmain() { + freerange(P2V(1u<<20), P2V(2u<<20)); // 1MB - 2MB + kvmalloc(); // map all of physical memory at KERNBASE + freerange(P2V(2u<<20), P2V(PHYSTOP)); + load_gdt(); init_keyboard(); init_pit(); diff --git a/kernel/kstart.S b/kernel/kstart.S index 73f49fb..51327a1 100644 --- a/kernel/kstart.S +++ b/kernel/kstart.S @@ -24,9 +24,14 @@ _start: // Set up the page table for the lowest 4 MiB: PD[0] -> PT mov dword ptr [0x1000], 0x2000 | 0x3 - // KERNBASE = 0xF000_0000 - // Use the same page table for the first 4 MiB after KERNBASE: PD[0x3c0] -> PT - mov dword ptr [0x1000 + (0x3c0 * 4)], 0x2000 | 0x3 + // KERNBASE = 0x8000_0000 + // Use the same page table for the first 4 MiB after KERNBASE: PD[0x200] -> PT + mov dword ptr [0x1000 + ((KERNBASE >> 22) * 4)], 0x2000 | 0x3 + + // Enable 4 MiB pages + mov eax, cr4 + or eax, 0x10 // Set the PSE bit (bit 4) + mov cr4, eax // Identity map the first 2 MiB mov edi, 0x2000 // Start of the PT diff --git a/lib/mem.c b/kernel/mem.c similarity index 100% rename from lib/mem.c rename to kernel/mem.c diff --git a/kernel/mem.h b/kernel/mem.h new file mode 100644 index 0000000..56ee2d5 --- /dev/null +++ b/kernel/mem.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef uintptr_t pde_t; +typedef uintptr_t pte_t; + +void* memset(void *dst, unsigned c, uint64_t n); + +void freerange(void *vstart, void *vend); +void* kalloc(void); +void kfree(char*); + +void kvmalloc(); \ No newline at end of file diff --git a/kernel/vm.c b/kernel/vm.c new file mode 100644 index 0000000..e6836e6 --- /dev/null +++ b/kernel/vm.c @@ -0,0 +1,18 @@ +#include "mem.h" +#include "cpu/memlayout.h" +#include "cpu/x86.h" + +pde_t *kvm; + +void kvmalloc() { + kvm = kalloc(); + memset(kvm, 0, PGSIZE); + + // Map physical memory to KERNBASE..KERNBASE+PHYSTOP + for (uintptr_t pa = 0; pa < PHYSTOP; pa += 4 << 20) { + uintptr_t va = KERNBASE + pa; + kvm[PDX(va)] = pa | PTE_P | PTE_W | PTE_PS; + } + + lcr3(V2P(kvm)); +} diff --git a/lib/mem.h b/lib/mem.h deleted file mode 100644 index 8af959f..0000000 --- a/lib/mem.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -void freerange(void *vstart, void *vend); -void* kalloc(void); -void kfree(char*); diff --git a/lib/string.h b/lib/string.h index 6184c33..584f7d7 100644 --- a/lib/string.h +++ b/lib/string.h @@ -4,4 +4,3 @@ typedef unsigned size_t; void kmemmove(char* dst, char* src, size_t size); int strncmp(const char* s1, const char* s2, size_t size); -void memset(void* b, char c, size_t len); diff --git a/proc.c b/proc.c index 47b9432..3b462d6 100644 --- a/proc.c +++ b/proc.c @@ -3,7 +3,7 @@ #include "fs/fs.h" #include "cpu/gdt.h" #include "cpu/isr.h" -#include "lib/mem.h" +#include "kernel/mem.h" #include "lib/string.h" #include "console.h" From 5a05c48afc22e854ffd6df14ea681e80469e3681 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 18 Jan 2025 01:11:31 +0400 Subject: [PATCH 52/56] kstart.S: remove leftovers from the 64-bit version. --- kernel/kstart.S | 48 ++++++++---------------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/kernel/kstart.S b/kernel/kstart.S index 51327a1..e1e87b8 100644 --- a/kernel/kstart.S +++ b/kernel/kstart.S @@ -12,36 +12,22 @@ .global _start .asciz "kernel start\n" _start: -.code32 - mov dword ptr [patch_addr - KERNBASE], offset gdt32_begin - KERNBASE - lgdt gdtr32 - KERNBASE - - // zero our PD at 0x1000 and PT at 0x2000 + // zero out PD at 0x1000 xor eax, eax - mov ecx, 2048 + mov ecx, 1024 rep stosd - // Set up the page table for the lowest 4 MiB: PD[0] -> PT - mov dword ptr [0x1000], 0x2000 | 0x3 - - // KERNBASE = 0x8000_0000 - // Use the same page table for the first 4 MiB after KERNBASE: PD[0x200] -> PT - mov dword ptr [0x1000 + ((KERNBASE >> 22) * 4)], 0x2000 | 0x3 - // Enable 4 MiB pages mov eax, cr4 or eax, 0x10 // Set the PSE bit (bit 4) mov cr4, eax - // Identity map the first 2 MiB - mov edi, 0x2000 // Start of the PT - mov ebx, 0x00000003 // Present, R/W - mov ecx, 512 // 2 MiB / 4 KiB = 512 -SetEntry: - mov dword ptr [edi], ebx - add ebx, 0x1000 - add edi, 4 - loop SetEntry + // Identity map low 4 MiB + mov dword ptr [0x1000], 0 | PTE_P | PTE_W | PTE_PS + + // KERNBASE = 0x8000_0000 + // Same mapping for the first 4 MiB after KERNBASE + mov dword ptr [0x1000 + ((KERNBASE >> 22) * 4)], 0 | PTE_P | PTE_W | PTE_PS // Load physical address of PD into CR3 mov edi, 0x1000 @@ -52,25 +38,7 @@ SetEntry: or eax, 1 << 31 // Set the PG bit mov cr0, eax - .att_syntax - jmp $0x8, $loadseg - KERNBASE - .intel_syntax noprefix - -loadseg: // jump to the high half add esp, KERNBASE lea eax, kmain jmp eax - -.align 16 -gdtr32: - .word gdt32_end - gdt32_begin - 1 -patch_addr: - .long gdt32_begin - KERNBASE - -.align 16 -gdt32_begin: - .long 0x00000000, 0x00000000 # Null descriptor - .long 0x0000ffff, 0x00cf9a00 # Code segment - .long 0x0000ffff, 0x00cf9200 # Data segment -gdt32_end: From 62c290f4496080209e1ec7f3dc483b9f9009f696 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 18 Jan 2025 01:29:23 +0400 Subject: [PATCH 53/56] Flat memory model, read_file() accepts statbuf. --- cpu/gdt.c | 8 ++++---- fs/fs.c | 14 +++++--------- fs/fs.h | 5 +++-- kernel.c | 7 ------- proc.c | 25 +++++++++++++++---------- 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/cpu/gdt.c b/cpu/gdt.c index 0b046c9..f005541 100644 --- a/cpu/gdt.c +++ b/cpu/gdt.c @@ -32,10 +32,10 @@ struct seg_desc_t { struct seg_desc_t seg_desc[6]; void init_seg_desc() { - seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0); - seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); - seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); - seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); + seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0); + seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); + seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER); + seg_desc[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER); } struct gdt_desc_t { diff --git a/fs/fs.c b/fs/fs.c index 6c7e6b6..7299058 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -13,7 +13,7 @@ int stat(const char* name, struct stat *buf) { struct dirent *e = &dir.entries[i]; if (!strncmp(e->name, name, sizeof(e->name))) { buf->size = e->size_bytes; - buf->reserved[0] = e->offset_sectors; + buf->start_sector = e->offset_sectors; return 0; } } @@ -23,14 +23,10 @@ int stat(const char* name, struct stat *buf) { /* Find file by name and read it into buffer with size bufsize. * At most (bufsize & -512) bytes will be read. * Return number of bytes read or -1 on failure. */ -int read_file(const char* name, void* buf, uint32_t bufsize) { - struct stat statbuf; - if (stat(name, &statbuf) != 0) { - return -1; - } - uint32_t sector = fs_start + statbuf.reserved[0]; +int read_file(const struct stat *statbuf, void* buf, uint32_t bufsize) { + uint32_t sector = fs_start + statbuf->start_sector; uint32_t bytes_read = 0; - uint32_t file_sectors = (statbuf.size + sector_size - 1) / sector_size; + uint32_t file_sectors = (statbuf->size + sector_size - 1) / sector_size; while (bufsize >= sector_size && file_sectors > 0) { read_sectors_ATA_PIO(buf, sector, 1); sector++; @@ -39,5 +35,5 @@ int read_file(const char* name, void* buf, uint32_t bufsize) { buf += sector_size; bytes_read += sector_size; } - return bytes_read < statbuf.size ? bytes_read : statbuf.size; + return bytes_read < statbuf->size ? bytes_read : statbuf->size; } diff --git a/fs/fs.h b/fs/fs.h index 67f01a2..a67d569 100644 --- a/fs/fs.h +++ b/fs/fs.h @@ -38,7 +38,8 @@ struct dir { struct stat { uint32_t size; - uint32_t reserved[3]; + uint32_t start_sector; + uint32_t reserved1, reserved2; }; /* Find file by name and fill information in buf. @@ -48,4 +49,4 @@ int stat(const char* name, struct stat *buf); /* Find file by name and read it into buffer with size bufsize. * At most (bufsize & ~511) bytes will be read. * Return number of bytes read or -1 on failure. */ -int read_file(const char* name, void* buf, uint32_t bufsize); +int read_file(const struct stat *statbuf, void* buf, uint32_t bufsize); diff --git a/kernel.c b/kernel.c index 98695fb..5b309e5 100644 --- a/kernel.c +++ b/kernel.c @@ -26,16 +26,9 @@ void kmain() { load_idt(); sti(); - char *buf = (char*)(16 << 20); - vga_clear_screen(); printk("YABLOKO\n"); - if (read_file("kernel.bin", buf, 4096 + sector_size) > 0) { - printk(buf + 4096); - } else { - printk("failed to read file\n"); - } printk("\n> "); while (1) { if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { diff --git a/proc.c b/proc.c index 3b462d6..e6d81af 100644 --- a/proc.c +++ b/proc.c @@ -23,31 +23,36 @@ struct kstack { struct task { struct taskstate tss; struct kstack stack; + pde_t *pgdir; }; struct vm { void *kernel_thread; - void *user_thread; struct task *user_task; -} *vm; +} vm; void trapret(); void swtch(void** oldstack, void* newstack); void run_elf(const char* name) { - if (!vm) { - vm = kalloc(); - vm->user_task = kalloc(); - switchuvm(&vm->user_task->tss, vm->user_task->stack.bottom); + struct stat statbuf; + if (stat(name, &statbuf) != 0) { + printk(name); + printk(": file not found\n"); + return; } - if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) { + if (!vm.user_task) { + vm.user_task = kalloc(); + switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom); + } + if (read_file(&statbuf, (void*)USER_BASE, 100 << 20) <= 0) { printk(name); printk(": file not found\n"); return; } Elf32_Ehdr *hdr = (void*)USER_BASE; - struct kstack *u = &vm->user_task->stack; + struct kstack *u = &vm.user_task->stack; memset(u, 0, sizeof(*u)); u->context.eip = (uint32_t)trapret; @@ -63,13 +68,13 @@ void run_elf(const char* name) { tf->useresp = USER_STACK_BASE; // initialization done, now switch to the process - swtch(&vm->kernel_thread, &u->context); + swtch(&vm.kernel_thread, &u->context); // process has finished } _Noreturn void killproc() { void* task_stack; - swtch(&task_stack, vm->kernel_thread); + swtch(&task_stack, vm.kernel_thread); __builtin_unreachable(); } From 117f6423c4c00fdd3081b70b5fe8e9cb47599459 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 18 Jan 2025 02:05:34 +0400 Subject: [PATCH 54/56] Fix run_elf (still leaking memory now). --- Makefile | 2 +- cpu/gdt.c | 6 +++- cpu/gdt.h | 3 +- cpu/memlayout.h | 8 +++++ kernel/mem.h | 5 ++- kernel/vm.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++--- proc.c | 7 ++++- 7 files changed, 106 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index dbaa2ec..195eff9 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 user/shout LDFLAGS=-m elf_i386 user/%: user/%.o user/crt.o - $(LD) $(LDFLAGS) -o $@ -Ttext 0x10000 $^ + $(LD) $(LDFLAGS) -o $@ -Ttext 0x401000 $^ image.bin: mbr.bin fs.img cat $^ >$@ diff --git a/cpu/gdt.c b/cpu/gdt.c index f005541..5a031d2 100644 --- a/cpu/gdt.c +++ b/cpu/gdt.c @@ -1,4 +1,6 @@ #include "gdt.h" +#include "x86.h" +#include "memlayout.h" #include "../lib/string.h" #include "kernel/mem.h" @@ -52,7 +54,7 @@ void load_gdt() { asm("lgdt %0": : "m"(gdt_desc)); } -void switchuvm(struct taskstate *tss, void* esp) { +void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir) { memset(tss, 0, sizeof(*tss)); seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0); seg_desc[SEG_TSS].s = 0; @@ -63,4 +65,6 @@ void switchuvm(struct taskstate *tss, void* esp) { tss->iomb = (ushort) 0xFFFF; asm("ltr %0": : "r"((ushort)(SEG_TSS << 3))); + + lcr3(V2P(pgdir)); } diff --git a/cpu/gdt.h b/cpu/gdt.h index 00dd40b..d67c11a 100644 --- a/cpu/gdt.h +++ b/cpu/gdt.h @@ -29,6 +29,7 @@ #define KERN_STACK_BASE 0x90000 #ifndef __ASSEMBLER__ +#include "kernel/mem.h" typedef unsigned uint; typedef unsigned short ushort; @@ -73,5 +74,5 @@ struct taskstate { }; void load_gdt(); -void switchuvm(struct taskstate *tss, void* esp); +void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir); #endif diff --git a/cpu/memlayout.h b/cpu/memlayout.h index 33a5818..414a0be 100644 --- a/cpu/memlayout.h +++ b/cpu/memlayout.h @@ -5,7 +5,9 @@ #define PHYSTOP 0x8000000 #define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) +#define PGROUNDDOWN(a) (((a)) & ~((uintptr_t)(PGSIZE-1))) +#define PTXSHIFT 12 // offset of PTX in a linear address #define PDXSHIFT 22 // offset of PDX in a linear address #define PXMASK 0x3FF @@ -17,6 +19,12 @@ // page directory index #define PDX(va) (((uintptr_t)(va) >> PDXSHIFT) & PXMASK) + +// page table index +#define PTX(va) (((uintptr_t)(va) >> PTXSHIFT) & PXMASK) + +// Address in page table or page directory entry +#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF) #endif // Page table/directory entry flags. diff --git a/kernel/mem.h b/kernel/mem.h index 56ee2d5..5d66fcd 100644 --- a/kernel/mem.h +++ b/kernel/mem.h @@ -11,4 +11,7 @@ void freerange(void *vstart, void *vend); void* kalloc(void); void kfree(char*); -void kvmalloc(); \ No newline at end of file +pde_t *setupkvm(); +void kvmalloc(); +void switchkvm(); +int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top); diff --git a/kernel/vm.c b/kernel/vm.c index e6836e6..99ba35b 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -1,11 +1,12 @@ #include "mem.h" #include "cpu/memlayout.h" #include "cpu/x86.h" +#include "console.h" pde_t *kvm; -void kvmalloc() { - kvm = kalloc(); +pde_t *setupkvm(void) { + pde_t *kvm = kalloc(); memset(kvm, 0, PGSIZE); // Map physical memory to KERNBASE..KERNBASE+PHYSTOP @@ -13,6 +14,81 @@ void kvmalloc() { uintptr_t va = KERNBASE + pa; kvm[PDX(va)] = pa | PTE_P | PTE_W | PTE_PS; } - - lcr3(V2P(kvm)); + return kvm; +} + +void kvmalloc() { + kvm = setupkvm(); + switchkvm(); +} + +void switchkvm(void) +{ + lcr3(V2P(kvm)); // switch to the kernel page table +} + +// Return the address of the PTE in page table pgdir +// that corresponds to virtual address va. If alloc!=0, +// create any required page table pages. +static pte_t * +walkpgdir(pde_t *pgdir, const void *va, int alloc) +{ + pde_t *pde; + pte_t *pgtab; + + pde = &pgdir[PDX(va)]; + if (*pde & PTE_P) { + pgtab = (pte_t *)P2V(PTE_ADDR(*pde)); + } else { + if (!alloc || (pgtab = (pte_t *)kalloc()) == 0) + return 0; + // Make sure all those PTE_P bits are zero. + memset(pgtab, 0, PGSIZE); + // The permissions here are overly generous, but they can + // be further restricted by the permissions in the page table + // entries, if necessary. + *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U; + } + return &pgtab[PTX(va)]; +} + +// Create PTEs for virtual addresses starting at va that refer to +// physical addresses starting at pa. va and size might not +// be page-aligned. +static int +mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa, int perm) +{ + char *a, *last; + pte_t *pte; + + a = (char *)PGROUNDDOWN((uintptr_t)va); + last = (char *)PGROUNDDOWN(((uintptr_t)va) + size - 1); + for (;;) + { + if ((pte = walkpgdir(pgdir, a, 1)) == 0) + return -1; + if (*pte & PTE_P) + panic("remap"); + *pte = pa | perm | PTE_P; + if (a == last) + break; + a += PGSIZE; + pa += PGSIZE; + } + return 0; +} + +int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) { + for (uintptr_t a = PGROUNDUP(base); a < top; a += PGSIZE) { + char *pa = kalloc(); + if (pa == 0) { + return -1; + } + memset(pa, 0, PGSIZE); + if (mappages(pgdir, (void*)a, PGSIZE, V2P(pa), PTE_W | PTE_U) < 0) { + kfree(pa); + return -1; + } + } + return 0; } diff --git a/proc.c b/proc.c index e6d81af..a89f124 100644 --- a/proc.c +++ b/proc.c @@ -3,6 +3,7 @@ #include "fs/fs.h" #include "cpu/gdt.h" #include "cpu/isr.h" +#include "cpu/memlayout.h" #include "kernel/mem.h" #include "lib/string.h" #include "console.h" @@ -43,7 +44,10 @@ void run_elf(const char* name) { } if (!vm.user_task) { vm.user_task = kalloc(); - switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom); + vm.user_task->pgdir = setupkvm(); + allocuvm(vm.user_task->pgdir, USER_BASE, USER_BASE + statbuf.size); + allocuvm(vm.user_task->pgdir, USER_STACK_BASE - 2 * PGSIZE, USER_STACK_BASE); + switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom, vm.user_task->pgdir); } if (read_file(&statbuf, (void*)USER_BASE, 100 << 20) <= 0) { printk(name); @@ -75,6 +79,7 @@ void run_elf(const char* name) { _Noreturn void killproc() { void* task_stack; + switchkvm(); swtch(&task_stack, vm.kernel_thread); __builtin_unreachable(); } From e8845b501243dbe73865ebac0eec3a9618568afb Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 18 Jan 2025 02:38:44 +0400 Subject: [PATCH 55/56] Free user VM. --- cpu/memlayout.h | 3 +++ kernel/mem.c | 4 ++-- kernel/mem.h | 3 ++- kernel/vm.c | 18 ++++++++++++++++++ proc.c | 10 ++++++---- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/cpu/memlayout.h b/cpu/memlayout.h index 414a0be..fd0dcbd 100644 --- a/cpu/memlayout.h +++ b/cpu/memlayout.h @@ -7,6 +7,9 @@ #define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) #define PGROUNDDOWN(a) (((a)) & ~((uintptr_t)(PGSIZE-1))) +#define NPDENTRIES 1024 // # directory entries per page directory +#define NPTENTRIES 1024 // # PTEs per page table + #define PTXSHIFT 12 // offset of PTX in a linear address #define PDXSHIFT 22 // offset of PDX in a linear address diff --git a/kernel/mem.c b/kernel/mem.c index c59f685..5fd7f82 100644 --- a/kernel/mem.c +++ b/kernel/mem.c @@ -41,7 +41,7 @@ memset(void *dst, unsigned c, uint64_t n) // call to kalloc(). (The exception is when // initializing the allocator; see kinit above.) void -kfree(char *v) +kfree(void *v) { struct run *r; @@ -51,7 +51,7 @@ kfree(char *v) // Fill with junk to catch dangling refs. memset(v, 1, PGSIZE); - r = (struct run*)v; + r = v; r->next = kmem.freelist; kmem.freelist = r; } diff --git a/kernel/mem.h b/kernel/mem.h index 5d66fcd..be1f5e6 100644 --- a/kernel/mem.h +++ b/kernel/mem.h @@ -9,9 +9,10 @@ void* memset(void *dst, unsigned c, uint64_t n); void freerange(void *vstart, void *vend); void* kalloc(void); -void kfree(char*); +void kfree(void*); pde_t *setupkvm(); void kvmalloc(); void switchkvm(); int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top); +void freevm(pde_t *pgdir); diff --git a/kernel/vm.c b/kernel/vm.c index 99ba35b..7ece1ec 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -92,3 +92,21 @@ int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) { } return 0; } + +static void freept(pte_t *pt) { + for (int i = 0; i < NPTENTRIES; i++) { + if (pt[i] & PTE_P) { + kfree((char*)P2V(PTE_ADDR(pt[i]))); + } + } + kfree((char*)pt); +} + +void freevm(pde_t *pgdir) { + for (int i = 0; i < NPDENTRIES / 2; i++) { + if (pgdir[i] & PTE_P) { + freept((pte_t*)P2V(PTE_ADDR(pgdir[i]))); + } + } + kfree(pgdir); +} diff --git a/proc.c b/proc.c index a89f124..851471b 100644 --- a/proc.c +++ b/proc.c @@ -44,11 +44,12 @@ void run_elf(const char* name) { } if (!vm.user_task) { vm.user_task = kalloc(); - vm.user_task->pgdir = setupkvm(); - allocuvm(vm.user_task->pgdir, USER_BASE, USER_BASE + statbuf.size); - allocuvm(vm.user_task->pgdir, USER_STACK_BASE - 2 * PGSIZE, USER_STACK_BASE); - switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom, vm.user_task->pgdir); } + vm.user_task->pgdir = setupkvm(); + allocuvm(vm.user_task->pgdir, USER_BASE, USER_BASE + statbuf.size); + allocuvm(vm.user_task->pgdir, USER_STACK_BASE - 2 * PGSIZE, USER_STACK_BASE); + switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom, vm.user_task->pgdir); + if (read_file(&statbuf, (void*)USER_BASE, 100 << 20) <= 0) { printk(name); printk(": file not found\n"); @@ -80,6 +81,7 @@ void run_elf(const char* name) { _Noreturn void killproc() { void* task_stack; switchkvm(); + freevm(vm.user_task->pgdir); swtch(&task_stack, vm.kernel_thread); __builtin_unreachable(); } From 4c71e65cc595a01226653fc013ab41228fe2d3a5 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 18 Jan 2025 02:46:38 +0400 Subject: [PATCH 56/56] Kill usermode process on exceptions. --- cpu/idt.c | 7 +++++++ proc.c | 1 + 2 files changed, 8 insertions(+) diff --git a/cpu/idt.c b/cpu/idt.c index ff4d3d8..91d8d02 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -105,6 +105,13 @@ void trap(registers_t *r) { if (r->int_no < ARRLEN(exception_messages)) { msg = exception_messages[r->int_no]; } + if (r->cs & 3) { + // exception from user mode, kill offending process + printk("Exception: "); + printk(msg); + printk("\n"); + killproc(); + } panic(msg); } } diff --git a/proc.c b/proc.c index 851471b..4bacc84 100644 --- a/proc.c +++ b/proc.c @@ -82,6 +82,7 @@ _Noreturn void killproc() { void* task_stack; switchkvm(); freevm(vm.user_task->pgdir); + sti(); swtch(&task_stack, vm.kernel_thread); __builtin_unreachable(); }