diff --git a/README.md b/README.md index e7eb490..c2ea84e 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Run either make qemu # or make bochs -to test SnowflakeOS in a VM. +to test SnowflakeOS in a VM. See [the edit/debug cycle](https://github.com/29jm/SnowflakeOS/wiki/The-edit-debug-cycle) for more options on how to compile and run SnowflakeOS. Testing this project on real hardware is possible. You can copy `SnowflakeOS.iso` to an usb drive using `dd`, like you would when making a live usb of another OS, and boot it directly. Note that this is rarely ever tested, who knows what it'll do :) I'd love to hear about it if you try this, on which hardware, etc... diff --git a/kernel/include/kernel/pipe.h b/kernel/include/kernel/pipe.h new file mode 100644 index 0000000..35bdbef --- /dev/null +++ b/kernel/include/kernel/pipe.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +typedef struct pipe_t { + inode_t inode; + uint8_t* buf; + uint32_t index; +} pipe_t; + +inode_t* pipe_new(); +inode_t* pipe_clone(inode_t* pipe); \ No newline at end of file diff --git a/kernel/include/kernel/proc.h b/kernel/include/kernel/proc.h index b28a395..d5f0afd 100644 --- a/kernel/include/kernel/proc.h +++ b/kernel/include/kernel/proc.h @@ -68,10 +68,11 @@ void proc_enter_usermode(); void proc_switch_process(process_t* next); uint32_t proc_get_current_pid(); char* proc_get_cwd(); +void proc_add_fd(ft_entry_t* entry); + void proc_sleep(uint32_t ms); void* proc_sbrk(intptr_t size); int32_t proc_exec(const char* path, char** argv); -ft_entry_t* proc_fd_to_entry(uint32_t fd); uint32_t proc_open(const char* path, uint32_t flags); void proc_close(uint32_t fd); uint32_t proc_read(uint32_t fd, uint8_t* buf, uint32_t size); diff --git a/kernel/include/kernel/uapi/uapi_fs.h b/kernel/include/kernel/uapi/uapi_fs.h index 8b510bd..f6b1024 100644 --- a/kernel/include/kernel/uapi/uapi_fs.h +++ b/kernel/include/kernel/uapi/uapi_fs.h @@ -22,6 +22,8 @@ #define DENT_FILE 1 #define DENT_DIRECTORY 2 +#define FS_STDOUT_FILENO 1 + typedef struct { uint32_t inode; uint16_t entry_size; diff --git a/kernel/include/kernel/uapi/uapi_syscall.h b/kernel/include/kernel/uapi/uapi_syscall.h index b77176a..07a3499 100644 --- a/kernel/include/kernel/uapi/uapi_syscall.h +++ b/kernel/include/kernel/uapi/uapi_syscall.h @@ -24,7 +24,8 @@ #define SYS_GETCWD 18 #define SYS_UNLINK 19 #define SYS_RENAME 20 -#define SYS_MAX 21 // First invalid syscall number +#define SYS_MAKETTY 21 +#define SYS_MAX 22 // First invalid syscall number #define SYS_INFO_UPTIME 1 #define SYS_INFO_MEMORY 2 diff --git a/kernel/src/misc/pipe.c b/kernel/src/misc/pipe.c new file mode 100644 index 0000000..70f8476 --- /dev/null +++ b/kernel/src/misc/pipe.c @@ -0,0 +1,85 @@ +#include +#include + +#include +#include + +#define PIPE_SIZE 2048 + +typedef struct pipe_fs_t { + fs_t fs; + uint8_t* buf; + uint32_t buf_size; + uint32_t content_start; + uint32_t content_size; + uint32_t refcount; +} pipe_fs_t; + +uint32_t pipe_read(pipe_fs_t* pipe, uint32_t inode, uint32_t offset, uint8_t* buf, uint32_t size) { + UNUSED(inode); // There's only one "file" in this fs, the pipe itself + UNUSED(offset); + + for (uint32_t i = 0; i < size; i++) { + if (!pipe->content_size) { + return i; + } + + buf[i] = pipe->buf[pipe->content_start]; + pipe->content_start = (pipe->content_start + 1) % pipe->buf_size; + pipe->content_size--; + } + + return size; +} + +uint32_t pipe_append(pipe_fs_t* pipe, uint32_t inode, uint8_t* data, uint32_t size) { + UNUSED(inode); + + for (uint32_t i = 0; i < size; i++) { + if (pipe->content_size == PIPE_SIZE) { + pipe->buf[pipe->content_start] = data[i]; + pipe->content_start = (pipe->content_start + 1) % pipe->buf_size; + } else { + pipe->buf[(pipe->content_start + pipe->content_size) % pipe->buf_size] = data[i]; + pipe->content_size++; + } + } + + return size; +} + +int32_t pipe_close(pipe_fs_t* fs, uint32_t ino) { + UNUSED(ino); + + if (--fs->refcount == 0) { + kfree(fs->buf); + kfree(fs->fs.root); + kfree(fs); + } + + return 0; +} + +inode_t* pipe_new() { + // TODO: have root inodes not necessarily be folders? + inode_t* p = zalloc(sizeof(folder_inode_t)); + p->fs = zalloc(sizeof(pipe_fs_t)); + + pipe_fs_t* fs = (pipe_fs_t*) p->fs; + fs->buf = zalloc(PIPE_SIZE); + fs->buf_size = PIPE_SIZE; + fs->refcount = 1; + + p->fs->root = (folder_inode_t*) p; + p->fs->read = (fs_read_t) pipe_read; + p->fs->append = (fs_append_t) pipe_append; + p->fs->close = (fs_close_t) pipe_close; + + return p; +} + +inode_t* pipe_clone(inode_t* pipe) { + ((pipe_fs_t*) pipe->fs)->refcount++; + + return pipe; +} \ No newline at end of file diff --git a/kernel/src/sys/proc.c b/kernel/src/sys/proc.c index 518e8d4..011ab8d 100644 --- a/kernel/src/sys/proc.c +++ b/kernel/src/sys/proc.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -195,8 +196,6 @@ process_t* proc_run_code(uint8_t* code, uint32_t size, char** argv) { * Implements the `exit` system call. */ void proc_exit_current_process() { - printk("terminating process %d", current_process->pid); - // Free allocated pages: code, heap, stack, page directory directory_entry_t* pd = (directory_entry_t*) 0xFFFFF000; @@ -217,7 +216,8 @@ void proc_exit_current_process() { // Free the file descriptor list while (!list_empty(¤t_process->filetable)) { - fs_close(list_first_entry(¤t_process->filetable, ft_entry_t)->inode); // bit ugly + ft_entry_t* ent = list_first_entry(¤t_process->filetable, ft_entry_t); + fs_close(ent->inode); list_del(list_first(¤t_process->filetable)); } @@ -283,6 +283,35 @@ void proc_enter_usermode() { : "%eax"); } +/* Returns the filetable entry associated with fd, if any. + */ +ft_entry_t* proc_fd_to_entry(uint32_t fd) { + ft_entry_t* ent; + + list_for_each_entry(ent, ¤t_process->filetable) { + if (ent->fd == fd) { + return ent; + } + } + + return NULL; +} + +void proc_add_fd(ft_entry_t* entry) { + if (!proc_fd_to_entry(entry->fd)) { + list_add_front(¤t_process->filetable, entry); + } +} + +/* Returns a new and unused fd for the current process. + * TODO: make it use the lowest fd available. + */ +uint32_t proc_next_fd() { + static uint32_t avail = 3; + + return avail++; +} + uint32_t proc_get_current_pid() { if (current_process) { return current_process->pid; @@ -350,6 +379,7 @@ void* proc_sbrk(intptr_t size) { } int32_t proc_exec(const char* path, char** argv) { + /* Read the executable */ inode_t* in = fs_open(path, O_RDONLY); if (!in || in->type != DENT_FILE) { @@ -360,7 +390,18 @@ int32_t proc_exec(const char* path, char** argv) { uint32_t read = fs_read(in, 0, data, in->size); if (read == in->size && in->size) { - proc_run_code(data, in->size, argv); + process_t* p = proc_run_code(data, in->size, argv); + + /* If the process to be executed has a parent that has a terminal, + * make it this process's terminal too */ + if (proc_get_current_pid() != 0) { + ft_entry_t* stdout = proc_fd_to_entry(1); + + if (stdout) { + stdout->inode = pipe_clone(stdout->inode); + list_add_front(&p->filetable, stdout); + } + } } else { printke("exec failed while reading the executable"); return -1; @@ -369,29 +410,6 @@ int32_t proc_exec(const char* path, char** argv) { return 0; } -/* Returns the filetable entry associated with fd, if any. - */ -ft_entry_t* proc_fd_to_entry(uint32_t fd) { - ft_entry_t* ent; - - list_for_each_entry(ent, ¤t_process->filetable) { - if (ent->fd == fd) { - return ent; - } - } - - return NULL; -} - -/* Returns a new and unused fd for the current process. - * TODO: make it use the lowest fd available. - */ -uint32_t proc_next_fd() { - static uint32_t avail = 1; - - return avail++; -} - uint32_t proc_open(const char* path, uint32_t flags) { inode_t* in = fs_open((char*) path, flags); // TODO diff --git a/kernel/src/sys/syscall.c b/kernel/src/sys/syscall.c index 957a747..8c47112 100644 --- a/kernel/src/sys/syscall.c +++ b/kernel/src/sys/syscall.c @@ -6,6 +6,7 @@ #include #include #include +#include #include // for UNUSED macro #include @@ -36,6 +37,7 @@ static void syscall_chdir(registers_t* regs); static void syscall_getcwd(registers_t* regs); static void syscall_unlink(registers_t* regs); static void syscall_rename(registers_t* regs); +static void syscall_maketty(registers_t* regs); handler_t syscall_handlers[SYSCALL_NUM] = { 0 }; @@ -62,6 +64,7 @@ void init_syscall() { syscall_handlers[SYS_GETCWD] = syscall_getcwd; syscall_handlers[SYS_UNLINK] = syscall_unlink; syscall_handlers[SYS_RENAME] = syscall_rename; + syscall_handlers[SYS_MAKETTY] = syscall_maketty; } static void syscall_handler(registers_t* regs) { @@ -261,4 +264,15 @@ static void syscall_rename(registers_t* regs) { char* new_path = (char*) regs->ecx; regs->eax = fs_rename(old_path, new_path); +} + +static void syscall_maketty(registers_t* regs) { + ft_entry_t* entry = zalloc(sizeof(ft_entry_t)); + + entry->fd = FS_STDOUT_FILENO; + entry->inode = pipe_new(); + + proc_add_fd(entry); + + regs->eax = 0; } \ No newline at end of file diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 4ca7450..d93f9f5 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #define EOF -1 @@ -10,6 +10,8 @@ typedef struct { char* name; } FILE; +extern FILE* stdout; + int printf(const char* __restrict, ...); int putchar(int); int puts(const char*); diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 07cf8b7..cb9760b 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -1,9 +1,14 @@ #pragma once #include +#include + +#define STDOUT_FILENO FS_STDOUT_FILENO #ifndef _KERNEL_ + int chdir(const char* path); char* getcwd(char* buf, size_t size); int unlink(const char* path); + #endif \ No newline at end of file diff --git a/libc/src/stdio/printf.c b/libc/src/stdio/printf.c index 696b9f0..0638a3c 100644 --- a/libc/src/stdio/printf.c +++ b/libc/src/stdio/printf.c @@ -6,9 +6,16 @@ #include #include -#include +static FILE __stdout = (FILE) { + .fd = STDOUT_FILENO, .name = "stdout" +}; + +FILE* stdout = &__stdout; static void print(const char* data, size_t data_length) { +#ifndef _KERNEL_ + fwrite(data, 1, data_length, stdout); +#endif for (size_t i = 0; i < data_length; i++) { putchar((int) data[i]); } diff --git a/libc/src/stdio/putchar.c b/libc/src/stdio/putchar.c index 853dd70..8c5b207 100644 --- a/libc/src/stdio/putchar.c +++ b/libc/src/stdio/putchar.c @@ -3,20 +3,18 @@ #ifdef _KERNEL_ #include #include +#else +#include #endif +extern int32_t syscall1(uint32_t eax, uint32_t ebx); + +/* Nothing to do with libc's putchar; this writes to serial output */ int putchar(int c) { #ifdef _KERNEL_ - // term_putchar(c); serial_write(c); #else - asm volatile ( - "mov $3, %%eax\n" - "mov %[c], %%ebx\n" - "int $0x30\n" - :: [c] "r" (c) - : "%eax", "%ebx" - ); + syscall1(SYS_PUTCHAR, c); #endif return c; } diff --git a/modules/src/terminal.c b/modules/src/terminal.c index 261421a..d21c83b 100644 --- a/modules/src/terminal.c +++ b/modules/src/terminal.c @@ -39,6 +39,8 @@ bool focused = true; int main() { win = snow_open_window("Terminal", twidth, theight, WM_NORMAL); + syscall(SYS_MAKETTY); + str_t* text_buf = str_new(prompt); str_t* input_buf = str_new(""); cursor = true; @@ -50,16 +52,16 @@ int main() { while (running) { wm_event_t event = snow_get_event(win); wm_kbd_event_t key = event.kbd; - bool focus_changed = false; + bool needs_redrawing = false; // Do we have focus? if (event.type & WM_EVENT_GAINED_FOCUS) { focused = true; - focus_changed = true; + needs_redrawing = true; } else if (event.type & WM_EVENT_LOST_FOCUS) { focused = false; cursor = false; - focus_changed = true; + needs_redrawing = true; } // Time & cursor blinks @@ -74,24 +76,37 @@ int main() { cursor = !cursor; event.type |= WM_EVENT_KBD; event.kbd.pressed = true; + needs_redrawing = true; } } - // Redraw on input & focus change - if (!(event.type & WM_EVENT_KBD && event.kbd.pressed) && !focus_changed) { - snow_sleep(10); - continue; + // Print things that have been output, if any, and append a prompt + const uint32_t buf_size = 256; + char buf[buf_size]; + uint32_t read; + bool anything_read = false; + + while ((read = fread(buf, 1, buf_size - 1, stdout))) { + buf[read] = '\0'; + str_append(text_buf, buf); + needs_redrawing = true; + anything_read = true; + } + + if (anything_read) { + str_append(text_buf, prompt); } - switch (event.kbd.keycode) { + if (event.type & WM_EVENT_KBD && event.kbd.pressed) { + needs_redrawing = true; + switch (key.keycode) { case KBD_ENTER: case KBD_KP_ENTER: str_append(text_buf, input_buf->buf); - str_append(text_buf, "\n"); interpret_cmd(text_buf, input_buf); + printf("\n"); input_buf->buf[0] = '\0'; input_buf->len = 0; - str_append(text_buf, prompt); break; case KBD_BACKSPACE: if (input_buf->len) { @@ -106,9 +121,12 @@ int main() { str_append(input_buf, str); } break; + } } - redraw(text_buf, input_buf); + if (needs_redrawing) { + redraw(text_buf, input_buf); + } } str_free(text_buf); @@ -228,13 +246,6 @@ void interpret_cmd(str_t* text_buf, str_t* input_buf) { if (!strcmp(input_buf->buf, "exit")) { running = false; return; - } else if (!strcmp(input_buf->buf, "log")) { - sys_info_t info = { .kernel_log = malloc(2048) }; - - syscall2(SYS_INFO, SYS_INFO_LOG, (uintptr_t) &info); - str_append(text_buf, info.kernel_log); - free(info.kernel_log); - return; } char* cmd = input_buf->buf; diff --git a/ui/include/ui.h b/ui/include/ui.h index 5952acd..65571e6 100644 --- a/ui/include/ui.h +++ b/ui/include/ui.h @@ -30,7 +30,7 @@ typedef struct { } point_t; /* Main widget class. Other widgets "inherit" it by having a `widget_t` as first - * struct member, so that they can be casted as `widget_t`. + * struct member, so that they can be cast as `widget_t`. */ typedef struct _widget_t { struct _widget_t* parent;