Skip to content

Commit

Permalink
Working stdout implementation
Browse files Browse the repository at this point in the history
A process can now declare itself to be a terminal with the `maketty`
system call. This gives it a new file descriptor corresponding to
stdout, that is then inherited by the processes it executes, and the
processes those execute, etc...
It's the terminal's job to read from stdout (a strange concept, I know)
and do something with it.
`stdout` is backed by a circular buffer; old content may be overwritten
if not read.
  • Loading branch information
29jm committed Dec 5, 2020
1 parent 6bede58 commit f426ba0
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 59 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -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...
Expand Down
12 changes: 12 additions & 0 deletions kernel/include/kernel/pipe.h
@@ -0,0 +1,12 @@
#pragma once

#include <kernel/fs.h>

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);
3 changes: 2 additions & 1 deletion kernel/include/kernel/proc.h
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions kernel/include/kernel/uapi/uapi_fs.h
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion kernel/include/kernel/uapi/uapi_syscall.h
Expand Up @@ -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
Expand Down
85 changes: 85 additions & 0 deletions kernel/src/misc/pipe.c
@@ -0,0 +1,85 @@
#include <kernel/pipe.h>
#include <kernel/sys.h>

#include <stdlib.h>
#include <string.h>

#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;
}
72 changes: 45 additions & 27 deletions kernel/src/sys/proc.c
Expand Up @@ -5,6 +5,7 @@
#include <kernel/gdt.h>
#include <kernel/fpu.h>
#include <kernel/fs.h>
#include <kernel/pipe.h>
#include <kernel/sys.h>

#include <kernel/sched_robin.h>
Expand Down Expand Up @@ -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;

Expand All @@ -217,7 +216,8 @@ void proc_exit_current_process() {

// Free the file descriptor list
while (!list_empty(&current_process->filetable)) {
fs_close(list_first_entry(&current_process->filetable, ft_entry_t)->inode); // bit ugly
ft_entry_t* ent = list_first_entry(&current_process->filetable, ft_entry_t);
fs_close(ent->inode);
list_del(list_first(&current_process->filetable));
}

Expand Down Expand Up @@ -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, &current_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(&current_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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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, &current_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

Expand Down
14 changes: 14 additions & 0 deletions kernel/src/sys/syscall.c
Expand Up @@ -6,6 +6,7 @@
#include <kernel/fb.h>
#include <kernel/wm.h>
#include <kernel/serial.h>
#include <kernel/pipe.h>
#include <kernel/sys.h> // for UNUSED macro

#include <stdio.h>
Expand Down Expand Up @@ -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 };

Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}
4 changes: 3 additions & 1 deletion libc/include/stdio.h
@@ -1,7 +1,7 @@
#pragma once

#include <stdint.h>
#include <stddef.h>
#include <unistd.h>

#define EOF -1

Expand All @@ -10,6 +10,8 @@ typedef struct {
char* name;
} FILE;

extern FILE* stdout;

int printf(const char* __restrict, ...);
int putchar(int);
int puts(const char*);
Expand Down
5 changes: 5 additions & 0 deletions libc/include/unistd.h
@@ -1,9 +1,14 @@
#pragma once

#include <stddef.h>
#include <kernel/uapi/uapi_fs.h>

#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
9 changes: 8 additions & 1 deletion libc/src/stdio/printf.c
Expand Up @@ -6,9 +6,16 @@
#include <stdlib.h>
#include <ctype.h>

#include <kernel/sys.h>
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]);
}
Expand Down
14 changes: 6 additions & 8 deletions libc/src/stdio/putchar.c
Expand Up @@ -3,20 +3,18 @@
#ifdef _KERNEL_
#include <kernel/term.h>
#include <kernel/serial.h>
#else
#include <kernel/uapi/uapi_syscall.h>
#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;
}

0 comments on commit f426ba0

Please sign in to comment.