diff --git a/.gitignore b/.gitignore index 9080a4e..3a02ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ cmake-build-debug CMakeLists.txt __pycache__ res.txt +vibing.txt \ No newline at end of file diff --git a/Makefile b/Makefile index e09fd6a..1a1b522 100644 --- a/Makefile +++ b/Makefile @@ -106,7 +106,7 @@ debug-nox: image.bin -ex "break _start" \ -ex "continue" -USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss +USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss ./user/snake fs.img: ./kernel.bin ./tools/mkfs $(USERPROGS) ./tools/mkfs $@ $< $(USERPROGS) diff --git a/cpu/idt.c b/cpu/idt.c index cf804bf..291986d 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -3,7 +3,10 @@ #include "memlayout.h" #include "../syscall.h" #include "../proc.h" +#include "../drivers/keyboard.h" #include "../drivers/port.h" +#include "../drivers/pit.h" +#include "../drivers/vga.h" #include "../console.h" enum { @@ -147,7 +150,31 @@ bool is_userspace_ptr_mapped(uint32_t ptr) { return 1; } -static void* get_userspace_cstr(uint32_t ptr) { +static bool is_userspace_range_mapped(uint32_t ptr, uint32_t size) { + if (size == 0) { + return 1; + } + if (ptr >= KERNBASE) { + return 0; + } + + uint32_t end = ptr + size - 1; + if (end < ptr || end >= KERNBASE) { + return 0; + } + + uint32_t last_page = PGROUNDDOWN(end); + for (uint32_t addr = ptr;; addr = PGROUNDDOWN(addr) + PGSIZE) { + if (!is_userspace_ptr_mapped(addr)) { + return 0; + } + if (PGROUNDDOWN(addr) == last_page) { + return 1; + } + } +} + +static bool is_userspace_cstr(uint32_t ptr) { for (uint32_t addr = ptr;; addr++) { if (addr == 0 || !is_userspace_ptr_mapped(addr)) { return 0; @@ -158,16 +185,75 @@ static void* get_userspace_cstr(uint32_t ptr) { } } -static int handle_puts(const char* s) { - if (!s) { +static _Noreturn void userspace_panic(const char* msg) { + if (!vga_is_text_mode()) { + switch_to_text_mode(); + vga_clear_screen(); + } + printk(msg); + killproc(); +} + +static void handle_puts(uintptr_t s) { + if (!is_userspace_cstr(s)) { + userspace_panic("SYS_puts panic: page fault\n"); + } + printk((const char*)s); +} + +static void require_text_mode_for_userspace_text_syscall() { + if (!vga_is_text_mode()) { + userspace_panic("Userspace panic: text syscall in graphics mode\n"); + } +} + +static void handle_swap_frame(uintptr_t frame) { + enum { + VGA_GRAPHICS_FRAME_SIZE = VGA_GRAPHICS_WIDTH * VGA_GRAPHICS_HEIGHT, + }; + + if (vga_is_text_mode()) { + userspace_panic("Userspace panic: frame swap in text mode\n"); + } + if (!is_userspace_range_mapped(frame, VGA_GRAPHICS_FRAME_SIZE)) { + userspace_panic("SYS_swap_frame panic: page fault\n"); + } + + uint8_t *video = (uint8_t*)(KERNBASE + 0xA0000); + uint8_t *user = (uint8_t*)frame; + for (uint32_t i = 0; i < VGA_GRAPHICS_FRAME_SIZE; i++) { + video[i] = user[i]; + } +} + +static void refill_keyboard_copy_buffer(void) { + size_t count = kbd_state_shrd.len; + if (count == 0) { + return; + } + + cli(); + size_t rem = KEYBOARD_INTERRUPT_BUF_CAP - kbd_state_shrd.copy_len; + size_t copying = rem < count ? rem : count; + memcpy(kbd_state_shrd.copy_buf, kbd_state_shrd.buf, copying); + kbd_state_shrd.len -= copying; + kbd_state_shrd.copy_len += copying; + sti(); +} + +static int handle_getc(void) { + if (kbd_can_take_from_copy_buffer()) { + return kbd_take_from_copy_buffer(); + } + + refill_keyboard_copy_buffer(); + if (!kbd_can_take_from_copy_buffer()) { return -1; } - printk(s); - return 0; + return kbd_take_from_copy_buffer(); } static void handle_syscall(registers_t* r) { - int ret; switch (r->eax) { case SYS_exit: if (r->ebx == 0) { @@ -177,24 +263,44 @@ static void handle_syscall(registers_t* r) { } killproc(); case SYS_greet: + require_text_mode_for_userspace_text_syscall(); printk("Hello world!\n"); r->eax = 0; break; case SYS_putc: - printk((const char[]){r->ebx, '\0'}); + require_text_mode_for_userspace_text_syscall(); + printk((const char[]){(char)r->ebx, '\0'}); r->eax = 0; break; case SYS_puts: - ret = handle_puts(get_userspace_cstr(r->ebx)); - if (ret < 0) { - printk("SYS_puts panic: page fault\n"); - killproc(); - } + require_text_mode_for_userspace_text_syscall(); + handle_puts(r->ebx); r->eax = 0; break; + case SYS_switch_to_text: + switch_to_text_mode(); + r->eax = 0; + break; + case SYS_switch_to_graphics: + switch_to_graphics_mode(); + r->eax = 0; + break; + case SYS_swap_frame: + handle_swap_frame(r->ebx); + r->eax = 0; + break; + case SYS_time_ms: + r->eax = get_uptime_ms(); + break; + case SYS_halt: + asm volatile("hlt"); + r->eax = 0; + break; + case SYS_getc: + r->eax = handle_getc(); + break; default: - printk("Userspace panic: Unknown syscall\n"); - killproc(); + userspace_panic("Userspace panic: Unknown syscall\n"); } } diff --git a/drivers/graphics.c b/drivers/graphics.c index 48110ab..1c70551 100644 --- a/drivers/graphics.c +++ b/drivers/graphics.c @@ -4,10 +4,13 @@ borrowed bits from http://files.osdev.org/mirrors/geezer/osd/graphics/modes.c */ +#include "vga.h" #include "cpu/x86.h" #include "cpu/memlayout.h" #include "port.h" +static vga_display_mode_t current_display_mode = VGA_DISPLAY_MODE_TEXT; + static inline char inb(int port) { return port_byte_in(port); } @@ -410,6 +413,7 @@ void vgaMode13() { outb(0x3C0, 0x20); setdefaultVGApalette(); + current_display_mode = VGA_DISPLAY_MODE_GRAPHICS; } void vgaMode3() { @@ -518,4 +522,21 @@ void vgaMode3() { inb(VGA+0x1A); outb(0x3C0, 0x20); + current_display_mode = VGA_DISPLAY_MODE_TEXT; +} + +void switch_to_graphics_mode() { + vgaMode13(); +} + +void switch_to_text_mode() { + vgaMode3(); +} + +vga_display_mode_t vga_get_display_mode() { + return current_display_mode; +} + +bool vga_is_text_mode() { + return current_display_mode == VGA_DISPLAY_MODE_TEXT; } diff --git a/drivers/pit.c b/drivers/pit.c index 21d44d6..f964575 100644 --- a/drivers/pit.c +++ b/drivers/pit.c @@ -25,8 +25,11 @@ enum { PIT_MODES_HW_TRIGGERRED_STROBE = 0x5, }; + +// Wtf is that O_o static timer_callback callbacks[100]; static int registered_callbacks = 0; +static volatile uint32_t uptime_ms = 0; void add_timer_callback(timer_callback tc) { callbacks[registered_callbacks++] = tc; @@ -45,7 +48,7 @@ struct pit_command_t { unsigned char select_channel : 2; } __attribute__((packed)); -static void dec_sleep_counter(void); +static void timer_int_callback(void); void init_pit() { struct pit_command_t cmd = { @@ -59,15 +62,20 @@ void init_pit() { port_byte_out(0x40, (PIT_PROGRAM_REG & 0xff00) >> 8); register_interrupt_handler(IRQ0, timer_interrupt_handler); - add_timer_callback(dec_sleep_counter); + add_timer_callback(timer_int_callback); } static volatile int sleep_counter = 0; -static void dec_sleep_counter(void) { +static void timer_int_callback(void) { + uptime_ms += 1000 / CLOCK_PRECISION_HZ; sleep_counter--; } +uint32_t get_uptime_ms(void) { + return uptime_ms; +} + void msleep(int ms) { sleep_counter = ms / 10; while (sleep_counter > 0) { diff --git a/drivers/pit.h b/drivers/pit.h index 01b72ad..0778e7b 100644 --- a/drivers/pit.h +++ b/drivers/pit.h @@ -1,8 +1,11 @@ #pragma once +#include + typedef void (*timer_callback)(void); void init_pit(void); void add_timer_callback(timer_callback tc); +uint32_t get_uptime_ms(void); void msleep(int ms); diff --git a/drivers/vga.h b/drivers/vga.h index 8bdca64..77d9083 100644 --- a/drivers/vga.h +++ b/drivers/vga.h @@ -1,5 +1,6 @@ #pragma once +#include #include enum { @@ -23,5 +24,17 @@ void vga_clear_screen(); void vga_set_char(unsigned offset, char c); void vga_print_string(const char* s); +#define VGA_GRAPHICS_WIDTH 320 +#define VGA_GRAPHICS_HEIGHT 200 + +typedef enum { + VGA_DISPLAY_MODE_TEXT = 0, + VGA_DISPLAY_MODE_GRAPHICS = 1, +} vga_display_mode_t; + void vgaMode13(); void vgaMode3(); +void switch_to_graphics_mode(); +void switch_to_text_mode(); +vga_display_mode_t vga_get_display_mode(); +bool vga_is_text_mode(); diff --git a/kernel.c b/kernel.c index 24d3a1f..b5a3bb0 100644 --- a/kernel.c +++ b/kernel.c @@ -19,14 +19,14 @@ void vga_set_pixel(int x, int y, int color) { } void graphtest() { - vgaMode13(); + switch_to_graphics_mode(); for (int i = 0; i < 320; ++i) { for (int j = 0; j < 200; ++j) { vga_set_pixel(i, j, (i+j)/2); } } msleep(5000); - vgaMode3(); + switch_to_text_mode(); vga_clear_screen(); } diff --git a/proc.c b/proc.c index 8bc0a58..64e8602 100644 --- a/proc.c +++ b/proc.c @@ -1,4 +1,5 @@ #include "proc.h" +#include "drivers/vga.h" struct context { // matches the behavior of swtch() @@ -28,9 +29,9 @@ void trapret(); void swtch(void** oldstack, void* newstack); pde_t *get_user_proc_page_directory() { - // if (!vm.user_task) { - // return 0; - // } + if (!vm.user_task) { + return 0; + } return vm.user_task->pgdir; } @@ -87,6 +88,10 @@ void run_elf(const char* name) { _Noreturn void killproc() { void* task_stack; + if (!vga_is_text_mode()) { + switch_to_text_mode(); + vga_clear_screen(); + } switchkvm(); freevm(vm.user_task->pgdir); sti(); diff --git a/syscall.h b/syscall.h index 368bb69..ea60dc2 100644 --- a/syscall.h +++ b/syscall.h @@ -8,6 +8,12 @@ enum { SYS_greet = 1, SYS_putc = 2, SYS_puts = 3, + SYS_switch_to_text = 4, + SYS_switch_to_graphics = 5, + SYS_swap_frame = 6, + SYS_time_ms = 7, + SYS_halt = 8, + SYS_getc = 9, }; int syscall(int call, uintptr_t arg); diff --git a/user/snake.c b/user/snake.c index 7a3057f..9302398 100644 --- a/user/snake.c +++ b/user/snake.c @@ -1,8 +1,61 @@ #include "../syscall.h" +#include "../drivers/vga.h" #include -#include -int main() { - return 0; +enum { + FRAME_SIZE = VGA_GRAPHICS_WIDTH * VGA_GRAPHICS_HEIGHT, + BLOCK_WIDTH = 270, + BLOCK_HEIGHT = 100, + BLOCK_X = 10, + BLOCK_Y = 0, +}; +// Non-zero initializer keeps the framebuffer in the program image, which this +// loader maps more reliably than a large BSS object. +static uint8_t frame[FRAME_SIZE] = { 1 }; + +static uint32_t time_ms(void) { + return (uint32_t)syscall(SYS_time_ms, 0); +} + +static void clear_frame(uint8_t color) { + for (uint32_t i = 0; i < FRAME_SIZE; i++) { + frame[i] = color; + } +} + +static void put_pixel(int x, int y, uint8_t color) { + if (x < 0 || x >= VGA_GRAPHICS_WIDTH || y < 0 || y >= VGA_GRAPHICS_HEIGHT) { + return; + } + frame[y * VGA_GRAPHICS_WIDTH + x] = color; +} + +static void draw_demo(void) { + clear_frame(0x01); + + for (int y = 0; y < BLOCK_HEIGHT; y++) { + for (int x = 0; x < BLOCK_WIDTH; x++) { + int screen_x = BLOCK_X + x; + int screen_y = BLOCK_Y + y; + uint8_t color = (uint8_t)((x + 2 * y) & 0x3f); + + if (x < 3 || y < 3 || x >= BLOCK_WIDTH - 3 || y >= BLOCK_HEIGHT - 3) { + color = 0x3f; + } + put_pixel(screen_x, screen_y, color); + } + } +} + +int main(void) { + syscall(SYS_switch_to_graphics, 0); + draw_demo(); + syscall(SYS_swap_frame, (uintptr_t)frame); + + uint32_t start = time_ms(); + while ((uint32_t)(time_ms() - start) < 2000) { + } + + return 0; }