diff --git a/README-demo.gif b/README-demo.gif
index 49a1c12..4f2d03d 100644
Binary files a/README-demo.gif and b/README-demo.gif differ
diff --git a/README.md b/README.md
index b3db0d6..4cf02f1 100644
--- a/README.md
+++ b/README.md
@@ -15,10 +15,7 @@ Hux - An x86 32-bit single-CPU toy operating system kernel built from scratch, f
## Tutorial / Development Doc
-I document the whole development process of Hux - its skeleton, related theories, practice pitfalls, plus everything I reckon important as a complete set of tutorials. They can be found at:
-
-- The [**WIKI pages 📝**](https://github.com/josehu07/hux-kernel/wiki) of this repo ✭
-- The Hux kernel dev doc PDF (identical, WIP)
+I document the whole development process of Hux as a complete set of tutorials. They can be found at the [**WIKI pages 📝**](https://github.com/josehu07/hux-kernel/wiki) of this GitHub repository ✭.
If there are any typos / mistakes / errors, please raise an issue!
@@ -30,14 +27,13 @@ Requires a Linux host development environment. Tested on Ubuntu Xenial, Bionic,
Clone the repo, set up a development cross-compilation toolchain following [this wiki page](https://github.com/josehu07/hux-kernel/wiki/02.-The-Very-First-Skeleton), then build Hux by:
```bash
+$ make clean
$ make
```
-Or, if you just want to try out Hux without a development toolchain, download both the released kernel image `hux.iso` and the initial file system image `vsfs.img` to the folder.
-
-(The images have bot been uploaded yet. The first release of Hux is coming soon.)
+Or, if you just want to try out Hux without a development toolchain, download both the released kernel image `hux.iso` and the initial file system image `vsfs.img` to the folder. (The images have bot been uploaded yet. The first release of Hux is coming soon.)
-To run Hux in QEMU, do:
+To run Hux in QEMU (currently only supports GUI VGA mode, "nographics" mode is coming soon), do:
```bash
$ make qemu
@@ -45,8 +41,6 @@ $ make qemu
You will see the QEMU GUI popping up with GRUB loaded. Choose the "`Hux`" option with Enter to boot into Hux.
-A random screenshot of the system at current progress:
-
For development setup & instructions, please check out the wiki pages (recommended). I have every single detail documented there.
@@ -99,11 +93,12 @@ Check the "References" section [here](https://github.com/josehu07/hux-kernel/wik
- [x] Essential system calls
- [x] Time-sharing scheduler
- [x] Basic IDE disk driver
-- [ ] Very simple file system
+- [x] Very simple file system
+- [ ] More user-level utilities
+- [ ] Thorough user-level tests
- [ ] Caching, swapping, & logging
-- [ ] Multi-threading concurrency
- [x] Synchronization primitives
+- [ ] Multi-threading concurrency
- [ ] Move to APIC & IOAPIC
- [ ] Direct memory access
-- [ ] Summary of current flaws
- [ ] Extend Hux to Rux with Rust
diff --git a/src/common/printf.c b/src/common/printf.c
index 08fcdb5..ddf56e2 100644
--- a/src/common/printf.c
+++ b/src/common/printf.c
@@ -896,6 +896,8 @@ _vsnprintf(char *buf, size_t count, const char *fmt, va_list va)
seg_end++;
fmt++;
}
+
+ *buf = '\0';
}
diff --git a/src/filesys/exec.c b/src/filesys/exec.c
index 94e3159..fcc6f9d 100644
--- a/src/filesys/exec.c
+++ b/src/filesys/exec.c
@@ -175,18 +175,19 @@ exec_program(mem_inode_t *inode, char *filename, char **argv)
goto fail;
sp = sp - (strlen(argv[argc]) + 1);
sp &= 0xFFFFFFFC; /** Align to 32-bit words. */
- memcpy((char *) (paddr_top - (USER_MAX - sp)), argv[argc],
+ memcpy((char *) (paddr_top + PAGE_SIZE - (USER_MAX - sp)), argv[argc],
strlen(argv[argc]) + 1);
ustack[3 + argc] = sp;
}
ustack[3 + argc] = 0; /** End of argv list. */
ustack[2] = sp - (argc + 1) * 4; /** `argv` */
- ustack[1] = argc; /** `argv` */
+ ustack[1] = argc; /** `argc` */
ustack[0] = 0x0000DEAD; /** Fake return address. */
sp -= (3 + argc + 1) * 4;
- memcpy((char *) (paddr_top - (USER_MAX - sp)), ustack, (3 + argc + 1) * 4);
+ memcpy((char *) (paddr_top + PAGE_SIZE - (USER_MAX - sp)), ustack,
+ (3 + argc + 1) * 4);
/** Change process name. */
strncpy(proc->name, filename, strlen(filename));
diff --git a/src/filesys/file.c b/src/filesys/file.c
index 46a83df..9400d95 100644
--- a/src/filesys/file.c
+++ b/src/filesys/file.c
@@ -10,6 +10,7 @@
#include "file.h"
#include "block.h"
#include "vsfs.h"
+#include "sysfile.h"
#include "../common/debug.h"
#include "../common/string.h"
@@ -521,3 +522,17 @@ file_put(file_t *file)
/** Actually closing, put inode. */
inode_put(inode);
}
+
+
+/** Get metadata information of a file. */
+void
+file_stat(file_t *file, file_stat_t *stat)
+{
+ inode_lock(file->inode);
+
+ stat->inumber = file->inode->inumber;
+ stat->type = file->inode->d_inode.type;
+ stat->size = file->inode->d_inode.size;
+
+ inode_unlock(file->inode);
+}
diff --git a/src/filesys/file.h b/src/filesys/file.h
index 0a499d1..2f52241 100644
--- a/src/filesys/file.h
+++ b/src/filesys/file.h
@@ -12,6 +12,7 @@
#include
#include "vsfs.h"
+#include "sysfile.h"
#include "../common/spinlock.h"
#include "../common/parklock.h"
@@ -73,5 +74,7 @@ file_t *file_get();
void file_ref(file_t *file);
void file_put(file_t *file);
+void file_stat(file_t *file, file_stat_t *stat);
+
#endif
diff --git a/src/filesys/sysfile.c b/src/filesys/sysfile.c
index 9e7b683..998da9f 100644
--- a/src/filesys/sysfile.c
+++ b/src/filesys/sysfile.c
@@ -193,3 +193,20 @@ syscall_exec(void)
}
return SYS_FAIL_RC;
}
+
+/** int32_t fstat(int32_t fd, file_stat_t *stat); */
+int32_t
+syscall_fstat(void)
+{
+ int32_t fd;
+ file_stat_t *stat;
+
+ if (!sysarg_get_int(0, &fd))
+ return SYS_FAIL_RC;
+ if (!sysarg_get_mem(1, (char **) &stat, sizeof(file_stat_t)))
+ return SYS_FAIL_RC;
+
+ if (!filesys_fstat(fd, stat))
+ return SYS_FAIL_RC;
+ return 0;
+}
diff --git a/src/filesys/sysfile.h b/src/filesys/sysfile.h
index f712ace..971c0aa 100644
--- a/src/filesys/sysfile.h
+++ b/src/filesys/sysfile.h
@@ -19,6 +19,15 @@
#define CREATE_DIR 0x2
+/** For the `fstat()` syscall. */
+struct file_stat {
+ uint32_t inumber;
+ uint32_t type;
+ uint32_t size;
+};
+typedef struct file_stat file_stat_t;
+
+
int32_t syscall_open();
int32_t syscall_close();
int32_t syscall_create();
@@ -28,6 +37,7 @@ int32_t syscall_write();
int32_t syscall_chdir();
int32_t syscall_getcwd();
int32_t syscall_exec();
+int32_t syscall_fstat();
#endif
diff --git a/src/filesys/vsfs.c b/src/filesys/vsfs.c
index 36ff24c..84ddc23 100644
--- a/src/filesys/vsfs.c
+++ b/src/filesys/vsfs.c
@@ -664,6 +664,21 @@ filesys_exec(char *path, char **argv)
}
+/** Get metadata information about an open file. */
+bool
+filesys_fstat(int8_t fd, file_stat_t *stat)
+{
+ file_t *file = _find_process_file(fd);
+ if (file == NULL) {
+ warn("fstat: cannot find file for fd %d", fd);
+ return false;
+ }
+
+ file_stat(file, stat);
+ return true;
+}
+
+
/** Flush the in-memory modified bitmap block to disk. */
bool
inode_bitmap_update(uint32_t slot_no)
diff --git a/src/filesys/vsfs.h b/src/filesys/vsfs.h
index 014f2ae..3d2fb5e 100644
--- a/src/filesys/vsfs.h
+++ b/src/filesys/vsfs.h
@@ -13,6 +13,8 @@
#include
#include
+#include "sysfile.h"
+
#include "../common/bitmap.h"
@@ -126,6 +128,8 @@ bool filesys_getcwd(char *buf, size_t limit);
bool filesys_exec(char *path, char **argv);
+bool filesys_fstat(int8_t fd, file_stat_t *stat);
+
bool inode_bitmap_update(uint32_t slot_no);
bool data_bitmap_update(uint32_t slot_no);
diff --git a/src/interrupt/syscall.c b/src/interrupt/syscall.c
index ad9b146..8b70385 100644
--- a/src/interrupt/syscall.c
+++ b/src/interrupt/syscall.c
@@ -46,7 +46,8 @@ static syscall_t syscall_handlers[] = {
[SYSCALL_WRITE] syscall_write,
[SYSCALL_CHDIR] syscall_chdir,
[SYSCALL_GETCWD] syscall_getcwd,
- [SYSCALL_EXEC] syscall_exec
+ [SYSCALL_EXEC] syscall_exec,
+ [SYSCALL_FSTAT] syscall_fstat
};
#define NUM_SYSCALLS ((int32_t) (sizeof(syscall_handlers) / sizeof(syscall_t)))
diff --git a/src/interrupt/syscall.h b/src/interrupt/syscall.h
index 763fa53..27a7968 100644
--- a/src/interrupt/syscall.h
+++ b/src/interrupt/syscall.h
@@ -37,6 +37,7 @@
#define SYSCALL_CHDIR 17
#define SYSCALL_GETCWD 18
#define SYSCALL_EXEC 19
+#define SYSCALL_FSTAT 20
/**
diff --git a/user/init.c b/user/init.c
index 71a20be..8180990 100644
--- a/user/init.c
+++ b/user/init.c
@@ -4,6 +4,7 @@
#include
+#include
#include "lib/syscall.h"
#include "lib/debug.h"
@@ -22,8 +23,9 @@ main(void)
if (shell_pid == 0) {
/** Child: exec the command line shell. */
- char *argv[1];
- argv[0] = 0;
+ char *argv[2];
+ argv[0] = "shell";
+ argv[1] = NULL;
exec("shell", argv);
error("init: failed to exec the shell program");
diff --git a/user/lib/printf.c b/user/lib/printf.c
index cdd552e..d7e7c07 100644
--- a/user/lib/printf.c
+++ b/user/lib/printf.c
@@ -632,6 +632,8 @@ _vsnprintf(char *buf, size_t count, const char *fmt, va_list va)
seg_end++;
fmt++;
}
+
+ *buf = '\0';
}
/** Wrapper over string printing for `printf()` and `cprintf()`. */
diff --git a/user/lib/syscall.h b/user/lib/syscall.h
index c25bf57..893b25c 100644
--- a/user/lib/syscall.h
+++ b/user/lib/syscall.h
@@ -24,6 +24,29 @@
#define CREATE_FILE 0x1
#define CREATE_DIR 0x2
+/** Struct & type code for `fstat()`. */
+#define INODE_TYPE_EMPTY 0
+#define INODE_TYPE_FILE 1
+#define INODE_TYPE_DIR 2
+
+struct file_stat {
+ uint32_t inumber;
+ uint32_t type;
+ uint32_t size;
+};
+typedef struct file_stat file_stat_t;
+
+/** Struct of a directory entry, see `vsfs.h`. */
+#define DENTRY_SIZE 128
+#define MAX_FILENAME 100
+
+struct dentry {
+ uint32_t valid;
+ uint32_t inumber;
+ char filename[DENTRY_SIZE - 8];
+} __attribute__((packed));
+typedef struct dentry dentry_t;
+
/**
* Externed from ASM `syscall.s`.
@@ -50,6 +73,7 @@ extern int32_t write(int32_t fd, char *src, uint32_t len);
extern int32_t chdir(char *path);
extern int32_t getcwd(char *buf, uint32_t limit);
extern int32_t exec(char *path, char **argv);
+extern int32_t fstat(int32_t fd, file_stat_t *stat);
#endif
diff --git a/user/lib/syscall.s b/user/lib/syscall.s
index 4ec9595..fcbddf1 100644
--- a/user/lib/syscall.s
+++ b/user/lib/syscall.s
@@ -39,3 +39,4 @@ SYSCALL_LIBGEN write, SYSCALL_WRITE
SYSCALL_LIBGEN chdir, SYSCALL_CHDIR
SYSCALL_LIBGEN getcwd, SYSCALL_GETCWD
SYSCALL_LIBGEN exec, SYSCALL_EXEC
+SYSCALL_LIBGEN fstat, SYSCALL_FSTAT
diff --git a/user/lib/syslist.s b/user/lib/syslist.s
index 50d5e4b..d8319ee 100644
--- a/user/lib/syslist.s
+++ b/user/lib/syslist.s
@@ -30,3 +30,4 @@ SYSCALL_WRITE = 16
SYSCALL_CHDIR = 17
SYSCALL_GETCWD = 18
SYSCALL_EXEC = 19
+SYSCALL_FSTAT = 20
diff --git a/user/ls.c b/user/ls.c
new file mode 100644
index 0000000..31c637b
--- /dev/null
+++ b/user/ls.c
@@ -0,0 +1,128 @@
+/**
+ * Command line utility - list directory.
+ */
+
+
+#include
+#include
+#include
+
+#include "lib/syscall.h"
+#include "lib/printf.h"
+#include "lib/debug.h"
+#include "lib/string.h"
+
+
+#define CONCAT_BUT_SIZE 300
+
+
+static char *
+_get_filename(char *path)
+{
+ int32_t idx;
+ for (idx = strlen(path) - 1; idx >= 0; --idx) {
+ if (path[idx] == '/')
+ return &path[idx + 1];
+ }
+ return path;
+}
+
+static void
+_print_file_stat(char *filename, file_stat_t *stat)
+{
+ bool is_dir = stat->type == INODE_TYPE_DIR;
+ printf("%5u %s %8u ", stat->inumber, is_dir ? "D" : "F", stat->size);
+ cprintf(is_dir ? VGA_COLOR_LIGHT_BLUE : VGA_COLOR_LIGHT_GREY,
+ "%s\n", filename);
+}
+
+
+static void
+_list_directory(char *path)
+{
+ int8_t fd = open(path, OPEN_RD);
+ if (fd < 0) {
+ warn("ls: cannot open path '%s'", path);
+ return;
+ }
+
+ file_stat_t stat;
+ if (fstat(fd, &stat) != 0) {
+ warn("ls: cannot get stat of '%s'", path);
+ close(fd);
+ return;
+ }
+
+ /** Listing on a regular file. */
+ if (stat.type == INODE_TYPE_FILE) {
+ _print_file_stat(_get_filename(path), &stat);
+ close(fd);
+ return;
+ }
+
+ /**
+ * Listing a directory, then read out its content, interpret as an
+ * array of directory entries.
+ */
+ char concat_buf[CONCAT_BUT_SIZE];
+ strncpy(concat_buf, path, CONCAT_BUT_SIZE - 2);
+ if (CONCAT_BUT_SIZE - 1 - strlen(concat_buf) < MAX_FILENAME) {
+ warn("ls: path '%s' too long", path);
+ close(fd);
+ return;
+ }
+ char *name_buf = &concat_buf[strlen(concat_buf)];
+ *(name_buf++) = '/';
+ *name_buf = '\0';
+
+ dentry_t dentry;
+ while (read(fd, (char *) &dentry, sizeof(dentry_t)) == sizeof(dentry_t)) {
+ if (dentry.valid != 1)
+ continue;
+
+ strncpy(name_buf, dentry.filename, MAX_FILENAME);
+ int8_t inner_fd = open(concat_buf, OPEN_RD);
+ if (inner_fd < 0) {
+ warn("ls: cannot open path '%s'", concat_buf);
+ close(fd);
+ return;
+ }
+
+ file_stat_t inner_stat;
+ if (fstat(inner_fd, &inner_stat) != 0) {
+ warn("ls: cannot get stat of '%s'", concat_buf);
+ close(inner_fd);
+ close(fd);
+ return;
+ }
+
+ _print_file_stat(dentry.filename, &inner_stat);
+ close(inner_fd);
+ }
+
+ close(fd);
+}
+
+
+void
+main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ _list_directory(".");
+ exit();
+ }
+
+ if (argc == 2) {
+ _list_directory(argv[1]);
+ exit();
+ }
+
+ /** If multiple directories, display as multiple sections. */
+ for (int i = 1; i < argc; ++i) {
+ printf("%s:\n", argv[i]);
+ _list_directory(argv[i]);
+ if (i < argc - 1)
+ printf("\n");
+ }
+ exit();
+}
diff --git a/user/shell.c b/user/shell.c
index a9194f5..2aeb6cb 100644
--- a/user/shell.c
+++ b/user/shell.c
@@ -11,7 +11,14 @@
#include "lib/printf.h"
#include "lib/debug.h"
#include "lib/string.h"
-#include "lib/malloc.h"
+#include "lib/types.h"
+
+
+#define CWD_BUF_SIZE 256
+#define LINE_BUF_SIZE 256
+#define MAX_EXEC_ARGS 32
+
+#define ENV_PATH "/"
/** A fancy welcome logo in ASCII. */
@@ -29,172 +36,149 @@ _shell_welcome_logo(void)
"\n");
}
+/** Compose & print the prompt. */
+static void
+_print_prompt(void)
+{
+ char username[5] = "hush";
+ cprintf(VGA_COLOR_GREEN, "%s", username);
+
+ cprintf(VGA_COLOR_DARK_GREY, ":");
+
+ char cwd[CWD_BUF_SIZE];
+ memset(cwd, 0, CWD_BUF_SIZE);
+ if (getcwd(cwd, CWD_BUF_SIZE - MAX_FILENAME - 1) != 0)
+ error("shell: failed to get cwd");
+ size_t ret_len = strlen(cwd);
+ if (cwd[ret_len - 1] != '/') {
+ cwd[ret_len] = '/';
+ cwd[ret_len + 1] = '\0';
+ }
+ cprintf(VGA_COLOR_CYAN, "%s", cwd);
+
+ cprintf(VGA_COLOR_DARK_GREY, "$ ");
+}
+
+
+/** Built-in cmd `cd`: change directory. */
+static void
+_change_cwd(char *path)
+{
+ if (path == NULL) /** empty path, go to "/". */
+ path = "/";
+
+ if (chdir(path) != 0)
+ warn("shell: cd to path '%s' failed", path);
+}
+
+/** Fork + exec external command executable. */
+static void
+_fork_exec(char *path, char **argv)
+{
+ int8_t pid = fork(0);
+ if (pid < 0) {
+ warn("shell: failed to fork child process");
+ return;
+ }
+
+ if (pid == 0) { /** Child. */
+ /**
+ * Try the executable under current working directory if found.
+ * Otherwise, fall through to try the one under ENV_PATH (which
+ * is the root directory "/").
+ */
+ if (open(path, OPEN_RD) < 0) { /** Not found. */
+ char full[CWD_BUF_SIZE];
+ snprintf(full, CWD_BUF_SIZE, "%s/%s", ENV_PATH, path);
+ exec(full, argv);
+ } else
+ exec(path, argv);
+
+ warn("shell: failed to exec '%s'", path);
+ exit();
+
+ } else { /** Parent shell. */
+ int8_t pid_w = wait();
+ if (pid_w != pid)
+ warn("shell: waited pid %d does not equal %d", pid_w, pid);
+ }
+}
+
-__attribute__((unused))
+/**
+ * Parse a command line. Parse the tokens according to whitespaces,
+ * interpret the first one as the executable filename, and together
+ * with the rest as the argument list.
+ */
+static int
+_parse_tokens(char *line, char **argv)
+{
+ size_t argi;
+ size_t pos = 0;
+
+ for (argi = 0; argi < MAX_EXEC_ARGS - 1; ++argi) {
+ while (isspace(line[pos]))
+ pos++;
+ if (line[pos] == '\0') {
+ argv[argi] = NULL;
+ return argi;
+ }
+
+ argv[argi] = &line[pos];
+
+ while(!isspace(line[pos]))
+ pos++;
+ if (line[pos] != '\0')
+ line[pos++] = '\0';
+ }
+
+ argv[argi] = NULL;
+ return argi;
+}
+
+/**
+ * Handle a command line.
+ * - If is a built-in command, handle directly;
+ * - Else, fork a child process to run the command executable.
+ */
static void
-_test_child_workload(void)
+_handle_cmdline(char *line)
{
- int32_t res = 0;
- for (int32_t i = 12345; i < 57896; ++i)
- for (int32_t j = i; j > 12345; --j)
- res += (i * j) % 567;
- printf("res %d: %d\n", getpid(), res);
+ char *argv[MAX_EXEC_ARGS];
+ int argc = _parse_tokens(line, argv);
+ if (argc <= 0)
+ return;
+ else if (argc >= MAX_EXEC_ARGS)
+ warn("shell: line exceeds max num of args %d", MAX_EXEC_ARGS);
+
+ if (strncmp(argv[0], "cd", 2) == 0)
+ _change_cwd(argv[1]); /** Could be NULL. */
+ else
+ _fork_exec(argv[0], argv);
}
void
-main(void)
+main(int argc, char *argv[])
{
- _shell_welcome_logo();
+ (void) argc; // Unused.
+ (void) argv;
- printf("Hello from the exec'ed shell program!\n");
+ _shell_welcome_logo();
- char cmd_buf[256];
- memset(cmd_buf, 0, 256);
+ char cmd_buf[LINE_BUF_SIZE];
+ memset(cmd_buf, 0, LINE_BUF_SIZE);
while (1) {
- printf("temp shell $ ");
+ _print_prompt();
- if (kbdstr(cmd_buf, 256) < 0)
- warn("shell: failed to get keyboard string");
+ if (kbdstr(cmd_buf, LINE_BUF_SIZE) < 0)
+ error("shell: failed to get keyboard string");
else
- printf("%s", cmd_buf);
+ _handle_cmdline(cmd_buf);
- memset(cmd_buf, 0, 256);
+ memset(cmd_buf, 0, LINE_BUF_SIZE);
}
- // char dirname[128] = "temp";
- // char filepath[128] = "temp/test.txt";
- // char filename[128] = "test.txt";
-
- // printf("[P] Created dir '%s' -> %d\n", dirname, create(dirname, CREATE_DIR));
- // printf("[P] Created file '%s' -> %d\n", filepath, create(filepath, CREATE_FILE));
- // printf("[P] Changed cwd to '%s' -> %d\n", dirname, chdir(dirname));
-
- // int8_t pid = fork(0);
- // assert(pid >= 0);
-
- // if (pid == 0) {
- // // Child.
- // char cwd[100];
- // printf("[C] Called getcwd -> %d\n", getcwd(cwd, 100));
- // printf(" cwd: %s\n", cwd);
- // int8_t fd = open(filename, OPEN_WR);
- // printf("[C] Opened file '%s' -> %d\n", filename, fd);
- // printf("[C] Written to fd %d -> %d\n", fd, write(fd, "AAAAA", 5));
- // printf(" src: %s\n", "AAAAA");
- // exit();
-
- // } else {
- // // Parent.
- // assert(wait() == pid);
- // printf("[P] Changed cwd to '%s' -> %d\n", "./..", chdir("./.."));
- // int8_t fd = open(filepath, OPEN_RD);
- // printf("[P] Opened file '%s' -> %d\n", filepath, fd);
- // char buf[6] = {0};
- // printf("[P] Read from fd %d -> %d\n", fd, read(fd, buf, 5));
- // printf(" dst: %s\n", buf);
- // printf("[P] Closing fd %d -> %d\n", fd, close(fd));
- // printf("[P] Removing file '%s' -> %d\n", filepath, remove(filepath));
- // printf("[P] Removing dir '%s' -> %d\n", dirname, remove(dirname));
- // }
-
- // printf("Files done!\n");
-
- // int8_t i;
-
- // printf("parent: forking...\n");
- // for (i = 1; i <= 3; ++i) {
- // int8_t pid = fork(i*4);
- // if (pid < 0)
- // error("parent: forking child i=%d failed", i);
- // if (pid == 0) {
- // // Child.
- // _test_child_workload();
- // exit();
- // } else
- // printf("parent: forked child pid=%d, timeslice=%d\n", pid, i*4);
- // }
-
- // printf("parent: waiting...\n");
- // for (i = 1; i <= 3; ++i) {
- // int8_t pid = wait();
- // printf("parent: waited child pid=%d\n", pid);
- // }
-
- // printf("On-stack buffer of size 8200...\n");
- // char buf[8200];
- // buf[0] = 'A';
- // buf[1] = '\0';
- // printf("Variable buf @ %p: %s\n", buf, buf);
-
- // printf("\nOn-heap allocations & frees...\n");
- // char *buf1 = (char *) malloc(200);
- // printf("Buf1: %p\n", buf1);
- // char *buf2 = (char *) malloc(4777);
- // printf("Buf2: %p\n", buf2);
- // mfree(buf1);
- // char *buf3 = (char *) malloc(8);
- // printf("Buf3: %p\n", buf3);
- // mfree(buf3);
- // mfree(buf2);
-
- // char kbd_buf[100];
- // printf("Please enter an input str: ");
- // kbdstr(kbd_buf, 100);
- // printf("Fetched str from keyboard: %s\n", kbd_buf);
-
- // int32_t mypid = getpid();
- // printf(" Parent: parent gets pid - %d\n", mypid);
- // sleep(2000);
-
- // cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 1 --\n");
- // printf(" Parent: forking child 1\n");
- // int32_t pid1 = fork(0);
- // if (pid1 < 0) {
- // cprintf(VGA_COLOR_RED, " Parent: fork failed\n");
- // _fake_halt();
- // }
- // if (pid1 == 0) {
- // // Child.
- // printf(" Child1: entering an infinite loop\n");
- // _fake_halt();
- // } else {
- // // Parent.
- // printf(" Parent: child 1 has pid - %d\n", pid1);
- // sleep(1500);
- // printf(" Parent: slept 1.5 secs, going to kill child 1\n");
- // kill(pid1);
- // wait();
- // printf(" Parent: killed child 1\n");
- // }
-
- // cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 2 --\n");
- // printf(" Current uptime: %d ms\n", uptime());
- // printf(" Going to sleep for 2000 ms...\n");
- // sleep(2000);
- // printf(" Current uptime: %d ms\n", uptime());
-
- // cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 3 --\n");
- // printf(" Parent: forking child 2\n");
- // int32_t pid2 = fork(0);
- // if (pid2 < 0) {
- // cprintf(VGA_COLOR_RED, " Parent: fork failed\n");
- // _fake_halt();
- // }
- // if (pid2 == 0) {
- // // Child.
- // printf(" Child2: going to sleep 2 secs\n");
- // sleep(2000);
- // exit();
- // } else {
- // // Parent.
- // printf(" Parent: child 2 has pid - %d\n", pid2);
- // wait();
- // printf(" Parent: waited child 2\n");
- // }
-
- // cprintf(VGA_COLOR_GREEN, "\n Cases done!\n");
-
exit();
}
diff --git a/user/tests/filetest.c b/user/tests/filetest.c
new file mode 100644
index 0000000..1d9e169
--- /dev/null
+++ b/user/tests/filetest.c
@@ -0,0 +1,58 @@
+/**
+ * User test program - file system operations.
+ */
+
+
+#include
+#include
+#include
+
+#include "../lib/debug.h"
+#include "../lib/printf.h"
+#include "../lib/syscall.h"
+
+
+void
+main(int argc, char *argv[])
+{
+ (void) argc; // Unused.
+ (void) argv;
+
+ char dirname[128] = "temp";
+ char filepath[128] = "temp/test.txt";
+ char filename[128] = "test.txt";
+
+ printf("[P] Created dir '%s' -> %d\n", dirname, create(dirname, CREATE_DIR));
+ printf("[P] Created file '%s' -> %d\n", filepath, create(filepath, CREATE_FILE));
+ printf("[P] Changed cwd to '%s' -> %d\n", dirname, chdir(dirname));
+
+ int8_t pid = fork(0);
+ assert(pid >= 0);
+
+ if (pid == 0) {
+ // Child.
+ char cwd[100];
+ printf("[C] Called getcwd -> %d\n", getcwd(cwd, 100));
+ printf(" cwd: %s\n", cwd);
+ int8_t fd = open(filename, OPEN_WR);
+ printf("[C] Opened file '%s' -> %d\n", filename, fd);
+ printf("[C] Written to fd %d -> %d\n", fd, write(fd, "AAAAA", 5));
+ printf(" src: %s\n", "AAAAA");
+ exit();
+
+ } else {
+ // Parent.
+ assert(wait() == pid);
+ printf("[P] Changed cwd to '%s' -> %d\n", "./..", chdir("./.."));
+ int8_t fd = open(filepath, OPEN_RD);
+ printf("[P] Opened file '%s' -> %d\n", filepath, fd);
+ char buf[6] = {0};
+ printf("[P] Read from fd %d -> %d\n", fd, read(fd, buf, 5));
+ printf(" dst: %s\n", buf);
+ printf("[P] Closing fd %d -> %d\n", fd, close(fd));
+ printf("[P] Removing file '%s' -> %d\n", filepath, remove(filepath));
+ printf("[P] Removing dir '%s' -> %d\n", dirname, remove(dirname));
+ }
+
+ exit();
+}
diff --git a/user/tests/memtest.c b/user/tests/memtest.c
new file mode 100644
index 0000000..60dbd0b
--- /dev/null
+++ b/user/tests/memtest.c
@@ -0,0 +1,40 @@
+/**
+ * User test program - file system operations.
+ */
+
+
+#include
+#include
+#include
+
+#include "../lib/debug.h"
+#include "../lib/printf.h"
+#include "../lib/syscall.h"
+#include "../lib/malloc.h"
+
+
+void
+main(int argc, char *argv[])
+{
+ (void) argc; // Unused.
+ (void) argv;
+
+ printf("On-stack buffer of size 8200...\n");
+ char buf[8200];
+ buf[0] = 'A';
+ buf[1] = '\0';
+ printf("Variable buf @ %p: %s\n", buf, buf);
+
+ printf("\nOn-heap allocations & frees...\n");
+ char *buf1 = (char *) malloc(200);
+ printf("Buf1: %p\n", buf1);
+ char *buf2 = (char *) malloc(4777);
+ printf("Buf2: %p\n", buf2);
+ mfree(buf1);
+ char *buf3 = (char *) malloc(8);
+ printf("Buf3: %p\n", buf3);
+ mfree(buf3);
+ mfree(buf2);
+
+ exit();
+}
diff --git a/user/tests/proctest.c b/user/tests/proctest.c
new file mode 100644
index 0000000..61fd3b4
--- /dev/null
+++ b/user/tests/proctest.c
@@ -0,0 +1,75 @@
+/**
+ * User test program - file system operations.
+ */
+
+
+#include
+#include
+#include
+
+#include "../lib/debug.h"
+#include "../lib/printf.h"
+#include "../lib/syscall.h"
+
+
+void
+main(int argc, char *argv[])
+{
+ (void) argc; // Unused.
+ (void) argv;
+
+ int32_t mypid = getpid();
+ printf(" Parent: parent gets pid - %d\n", mypid);
+ sleep(2000);
+
+ cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 1 --\n");
+ printf(" Parent: forking child 1\n");
+ int32_t pid1 = fork(0);
+ if (pid1 < 0) {
+ cprintf(VGA_COLOR_RED, " Parent: fork failed\n");
+ exit();
+ }
+ if (pid1 == 0) {
+ // Child.
+ printf(" Child1: entering an infinite loop\n");
+ while (true)
+ sleep(5000);
+ } else {
+ // Parent.
+ printf(" Parent: child 1 has pid - %d\n", pid1);
+ sleep(1500);
+ printf(" Parent: slept 1.5 secs, going to kill child 1\n");
+ kill(pid1);
+ wait();
+ printf(" Parent: killed child 1\n");
+ }
+
+ cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 2 --\n");
+ printf(" Current uptime: %d ms\n", uptime());
+ printf(" Going to sleep for 2000 ms...\n");
+ sleep(2000);
+ printf(" Current uptime: %d ms\n", uptime());
+
+ cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 3 --\n");
+ printf(" Parent: forking child 2\n");
+ int32_t pid2 = fork(0);
+ if (pid2 < 0) {
+ cprintf(VGA_COLOR_RED, " Parent: fork failed\n");
+ exit();
+ }
+ if (pid2 == 0) {
+ // Child.
+ printf(" Child2: going to sleep 2 secs\n");
+ sleep(2000);
+ exit();
+ } else {
+ // Parent.
+ printf(" Parent: child 2 has pid - %d\n", pid2);
+ wait();
+ printf(" Parent: waited child 2\n");
+ }
+
+ cprintf(VGA_COLOR_GREEN, "\n Cases done!\n");
+
+ exit();
+}
diff --git a/user/tests/schedtest.c b/user/tests/schedtest.c
new file mode 100644
index 0000000..d6aea62
--- /dev/null
+++ b/user/tests/schedtest.c
@@ -0,0 +1,54 @@
+/**
+ * User test program - weighted scheduling.
+ */
+
+
+#include
+#include
+#include
+
+#include "../lib/debug.h"
+#include "../lib/printf.h"
+#include "../lib/syscall.h"
+
+
+static void
+_test_child_workload(void)
+{
+ int32_t res = 0;
+ for (int32_t i = 12345; i < 57896; ++i)
+ for (int32_t j = i; j > 12345; --j)
+ res += (i * j) % 567;
+ printf("res %d: %d\n", getpid(), res);
+}
+
+
+void
+main(int argc, char *argv[])
+{
+ (void) argc; // Unused.
+ (void) argv;
+
+ int8_t i;
+
+ printf("parent: forking...\n");
+ for (i = 1; i <= 3; ++i) {
+ int8_t pid = fork(i*4);
+ if (pid < 0)
+ error("parent: forking child i=%d failed", i);
+ if (pid == 0) {
+ // Child.
+ _test_child_workload();
+ exit();
+ } else
+ printf("parent: forked child pid=%d, timeslice=%d\n", pid, i*4);
+ }
+
+ printf("parent: waiting...\n");
+ for (i = 1; i <= 3; ++i) {
+ int8_t pid = wait();
+ printf("parent: waited child pid=%d\n", pid);
+ }
+
+ exit();
+}
diff --git a/wiki b/wiki
index 2b48c13..a1c932e 160000
--- a/wiki
+++ b/wiki
@@ -1 +1 @@
-Subproject commit 2b48c13242a74af964b2a23186ff4a18698f02c6
+Subproject commit a1c932ec867e9cc10abf74851ccadf6b19476dd2