summaryrefslogtreecommitdiff
path: root/src/user
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-01-03 02:19:57 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-01-03 02:19:57 +0800
commit210025560c80b48af4875af199f183ad709e3bf0 (patch)
treeeb11aeb7b641187f207e3b572c5184cf3526b463 /src/user
parentbde87490d61135dcaa94d9b1489ff6c39876d169 (diff)
parent10614e04929a6d109a5e90524e446b637aaf0c13 (diff)
downloadlume-210025560c80b48af4875af199f183ad709e3bf0.tar.gz
lume-210025560c80b48af4875af199f183ad709e3bf0.tar.xz
Merge pull request #4 from Richard-Qin-001/dev/shell
Rebuilt the system's underlying structure; it is now stable enough to merge. The system now supports a read-only file system and has a simple shell implemented.
Diffstat (limited to 'src/user')
-rw-r--r--src/user/entry.S12
-rw-r--r--src/user/init.cc72
-rw-r--r--src/user/sh.cc418
-rw-r--r--src/user/user.ld12
-rw-r--r--src/user/usys.S14
5 files changed, 499 insertions, 29 deletions
diff --git a/src/user/entry.S b/src/user/entry.S
new file mode 100644
index 0000000..110ebbf
--- /dev/null
+++ b/src/user/entry.S
@@ -0,0 +1,12 @@
+.section .text
+.global _start
+
+_start:
+ li a0, 0
+ li a1, 0
+
+ call main
+
+ call exit
+
+1: j 1b \ No newline at end of file
diff --git a/src/user/init.cc b/src/user/init.cc
index 3c42adf..827a21e 100644
--- a/src/user/init.cc
+++ b/src/user/init.cc
@@ -1,34 +1,58 @@
+#include "ulib/stdio.h"
+#include "ulib/stdlib.h"
+#include "ulib/string.h"
#include "user/user.h"
-int main();
-
-extern "C" void _start()
-{
- exit(main());
-}
-
int main()
{
- int pid = fork();
+ char *argv[] = {(char *)"sh", 0};
- if (pid < 0)
- {
- write(1, "Fork failed\n", 12);
- exit(1);
- }
+ printf("[Init] Starting Lume OS Shell...\n");
- if (pid == 0)
- {
- write(1, "Hello from Child!\n", 18);
- exit(0);
- }
- else
+ while (1)
{
- write(1, "Parent waiting...\n", 18);
- wait(0);
- write(1, "Parent: Child exited.\n", 22);
- }
+ // printf("fork start\n");
+ int pid = fork();
+ // printf("fork suceess, pid=%i\n", pid);
+
+ if (pid < 0)
+ {
+ printf("[Init] fork failed!\n");
+ exit(1);
+ }
+
+ if (pid == 0)
+ {
+
+ char sh_path[] = "/sh";
+ exec(sh_path, argv);
- while (1);
+ printf("[Init] exec sh failed\n");
+ return 0;
+ exit(1);
+ }
+ else
+ {
+ // break;
+ int status;
+ int wpid;
+ while (1)
+ {
+ wpid = wait((uint64)&status);
+
+ if (wpid == pid)
+ {
+ printf("Shell Exit");
+ break;
+ }
+ if (wpid < 0)
+ {
+ printf("[Init] wait error or no child\n");
+
+ break;
+ }
+ }
+ }
+ }
return 0;
} \ No newline at end of file
diff --git a/src/user/sh.cc b/src/user/sh.cc
new file mode 100644
index 0000000..1f2e950
--- /dev/null
+++ b/src/user/sh.cc
@@ -0,0 +1,418 @@
+#include "ulib/stdio.h"
+#include "ulib/stdlib.h"
+#include "ulib/string.h"
+#include "user/user.h"
+#include "common/fcntl.h"
+
+constexpr int MAX_ARGS = 10;
+const char *WHITESPACE = " \t\r\n\v";
+const char *SYMBOLS = "<|>&;()";
+
+void panic(const char *s)
+{
+ printf("shell panic: %s\n", s);
+ exit(1);
+}
+
+int fork_or_panic()
+{
+ int pid = fork();
+ if (pid == -1)
+ panic("fork");
+ return pid;
+}
+
+struct Command
+{
+ virtual ~Command() = default;
+
+ virtual void run() = 0;
+};
+
+struct ExecCommand : public Command
+{
+ char *argv[MAX_ARGS];
+
+ ExecCommand()
+ {
+ memset(argv, 0, sizeof(argv));
+ }
+
+ void run() override
+ {
+ if (argv[0] == nullptr)
+ exit(1);
+
+ exec(argv[0], argv);
+
+ printf("exec %s failed\n", argv[0]);
+ exit(1);
+ }
+};
+
+struct RedirCommand : public Command
+{
+ Command *sub_cmd;
+ char *file;
+ char *efile; // file name end pointer (used by parser)
+ int mode;
+ int fd;
+
+ RedirCommand(Command *c, char *f, char *ef, int m, int fd_in)
+ : sub_cmd(c), file(f), efile(ef), mode(m), fd(fd_in) {}
+
+ ~RedirCommand() { delete sub_cmd; }
+
+ void run() override
+ {
+ close(fd);
+ if (open(file, mode) < 0)
+ {
+ printf("open %s failed\n", file);
+ exit(1);
+ }
+ sub_cmd->run();
+ }
+};
+
+struct PipeCommand : public Command
+{
+ Command *left;
+ Command *right;
+
+ PipeCommand(Command *l, Command *r) : left(l), right(r) {}
+ ~PipeCommand()
+ {
+ delete left;
+ delete right;
+ }
+
+ void run() override
+ {
+ int p[2];
+ if (pipe(p) < 0)
+ panic("pipe");
+
+ if (fork_or_panic() == 0)
+ {
+ // Left child: write to pipe
+ close(1); // close stdout
+ dup(p[1]); // dup write-end to stdout
+ close(p[0]);
+ close(p[1]);
+ left->run();
+ }
+
+ if (fork_or_panic() == 0)
+ {
+ // Right child: read from pipe
+ close(0); // close stdin
+ dup(p[0]); // dup read-end to stdin
+ close(p[0]);
+ close(p[1]);
+ right->run();
+ }
+
+ close(p[0]);
+ close(p[1]);
+ wait(0);
+ wait(0);
+ exit(0);
+ }
+};
+
+struct ListCommand : public Command
+{
+ Command *left;
+ Command *right;
+
+ ListCommand(Command *l, Command *r) : left(l), right(r) {}
+ ~ListCommand()
+ {
+ delete left;
+ delete right;
+ }
+
+ void run() override
+ {
+ if (fork_or_panic() == 0)
+ {
+ left->run();
+ }
+ wait(0);
+ right->run();
+ }
+};
+
+struct BackCommand : public Command
+{
+ Command *sub_cmd;
+
+ BackCommand(Command *c) : sub_cmd(c) {}
+ ~BackCommand() { delete sub_cmd; }
+
+ void run() override
+ {
+ if (fork_or_panic() == 0)
+ {
+ sub_cmd->run();
+ }
+ // Parent doesn't wait
+ exit(0);
+ }
+};
+
+class Parser
+{
+public:
+ static Command *parse(char *s)
+ {
+ char *es = s + strlen(s);
+ Command *cmd = parse_line(&s, es);
+ peek(&s, es, "");
+ if (s != es)
+ {
+ printf("leftovers: %s\n", s);
+ // panic("syntax"); // strict syntax check
+ }
+ terminate_strings(cmd);
+ return cmd;
+ }
+
+private:
+ // Helper: NUL-terminate strings found during parsing
+ static void terminate_strings(Command *cmd)
+ {
+ if (!cmd)
+ return;
+ }
+
+ static char get_token(char **ps, char *es, char **q, char **eq)
+ {
+ char *s;
+ int ret;
+
+ s = *ps;
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ if (q)
+ *q = s;
+ ret = *s;
+ switch (*s)
+ {
+ case 0:
+ break;
+ case '|':
+ case '(':
+ case ')':
+ case ';':
+ case '&':
+ case '<':
+ s++;
+ break;
+ case '>':
+ s++;
+ if (*s == '>')
+ {
+ ret = '+';
+ s++;
+ }
+ break;
+ default:
+ ret = 'a';
+ while (s < es && !strchr(WHITESPACE, *s) && !strchr(SYMBOLS, *s))
+ s++;
+ break;
+ }
+ if (eq)
+ *eq = s;
+
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ *ps = s;
+ return ret;
+ }
+
+ static int peek(char **ps, char *es, const char *toks)
+ {
+ char *s = *ps;
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ *ps = s;
+ return *s && strchr(toks, *s);
+ }
+
+ // Recursive Descent Parsers
+
+ static Command *parse_line(char **ps, char *es)
+ {
+ Command *cmd = parse_pipe(ps, es);
+ while (peek(ps, es, "&"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new BackCommand(cmd);
+ }
+ if (peek(ps, es, ";"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new ListCommand(cmd, parse_line(ps, es));
+ }
+ return cmd;
+ }
+
+ static Command *parse_pipe(char **ps, char *es)
+ {
+ Command *cmd = parse_exec(ps, es);
+ if (peek(ps, es, "|"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new PipeCommand(cmd, parse_pipe(ps, es));
+ }
+ return cmd;
+ }
+
+ static Command *parse_exec(char **ps, char *es)
+ {
+ char *q, *eq;
+ int tok, argc;
+ ExecCommand *cmd = new ExecCommand();
+ Command *ret = cmd;
+
+ argc = 0;
+ ret = parse_redirs(ret, ps, es);
+ while (!peek(ps, es, "|)&;"))
+ {
+ if ((tok = get_token(ps, es, &q, &eq)) == 0)
+ break;
+ if (tok != 'a')
+ panic("syntax");
+
+ cmd->argv[argc] = q;
+ *eq = 0; // Temp
+
+ argc++;
+ if (argc >= MAX_ARGS)
+ panic("too many args");
+ ret = parse_redirs(ret, ps, es);
+ }
+ cmd->argv[argc] = 0;
+ return ret;
+ }
+
+ static Command *parse_redirs(Command *cmd, char **ps, char *es)
+ {
+ int tok;
+ char *q, *eq;
+
+ while (peek(ps, es, "<>"))
+ {
+ tok = get_token(ps, es, 0, 0);
+ if (get_token(ps, es, &q, &eq) != 'a')
+ panic("missing file for redirection");
+
+ *eq = 0; // Null terminate filename immediately
+
+ switch (tok)
+ {
+ case '<':
+ cmd = new RedirCommand(cmd, q, eq, O_RDONLY, 0);
+ break;
+ case '>':
+ cmd = new RedirCommand(cmd, q, eq, O_WRONLY | O_CREATE | O_TRUNC, 1);
+ break;
+ case '+': // >>
+ cmd = new RedirCommand(cmd, q, eq, O_WRONLY | O_CREATE, 1); // Append not fully supported in simple OS yet?
+ break;
+ }
+ }
+ return cmd;
+ }
+};
+
+int getcmd(char *buf, int nbuf)
+{
+ write(1, "$ ", 2);
+ memset(buf, 0, nbuf);
+
+ int i = 0;
+ while (i < nbuf - 1)
+ {
+ char c;
+ int n = read(0, &c, 1);
+ if (n <= 0)
+ return -1;
+
+ if (c == '\b' || c == 127)
+ {
+ if (i > 0)
+ {
+ write(1, "\b \b", 3);
+ i--;
+ }
+ }
+ else if (c == '\n' || c == '\r')
+ {
+ write(1, "\n", 1);
+ buf[i] = 0;
+ return 0;
+ }
+ else
+ {
+ write(1, &c, 1);
+ buf[i++] = c;
+ }
+ }
+ return -1;
+}
+
+int main()
+{
+ static char buf[100];
+
+ // int fd;
+ // while ((fd = open("console", O_RDWR)) >= 0)
+ // {
+ // if (fd >= 3)
+ // {
+ // close(fd);
+ // break;
+ // }
+ // }
+
+ printf("\n[Lume Shell] Initialized! \n");
+
+ while (getcmd(buf, sizeof(buf)) >= 0)
+ {
+ if (buf[0] == 'c' && buf[1] == 'd' && (buf[2] == ' ' || buf[2] == '\n' || buf[2] == 0))
+ {
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = 0;
+
+ char *path = buf + 3;
+ if (buf[2] == '\n' || buf[2] == 0)
+ {
+ printf("cd: argument missing\n");
+ continue;
+ }
+
+ if (chdir(path) < 0)
+ {
+ printf("cannot cd %s\n", path);
+ }
+ continue;
+ }
+
+ if (fork_or_panic() == 0)
+ {
+ Command *cmd = Parser::parse(buf);
+ if (cmd)
+ {
+ cmd->run();
+ delete cmd;
+ }
+ exit(0);
+ }
+ wait(0);
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/src/user/user.ld b/src/user/user.ld
index 2111b14..8c9999a 100644
--- a/src/user/user.ld
+++ b/src/user/user.ld
@@ -1,10 +1,10 @@
-OUTPUT_ARCH(riscv)
-ENTRY(_start)
+OUTPUT_ARCH( "riscv" )
+ENTRY( _start )
SECTIONS
{
. = 0x0;
-
+
.text : {
*(.text.entry)
*(.text .text.*)
@@ -12,16 +12,22 @@ SECTIONS
.rodata : {
. = ALIGN(16);
+ *(.srodata .srodata.*)
+ . = ALIGN(16);
*(.rodata .rodata.*)
}
.data : {
. = ALIGN(16);
+ *(.sdata .sdata.*)
+ . = ALIGN(16);
*(.data .data.*)
}
.bss : {
. = ALIGN(16);
+ *(.sbss .sbss.*)
+ . = ALIGN(16);
*(.bss .bss.*)
}
} \ No newline at end of file
diff --git a/src/user/usys.S b/src/user/usys.S
index e805d82..f85917e 100644
--- a/src/user/usys.S
+++ b/src/user/usys.S
@@ -7,8 +7,16 @@
ecall; \
ret
+#define SYSCALL_NAME(name, sysname) \
+ .global name; \
+ name: \
+ li a7, SYS_ ## sysname; \
+ ecall; \
+ ret
+
+SYSCALL_NAME(sys_exit, exit)
+
SYSCALL(fork)
-SYSCALL(exit)
SYSCALL(wait)
SYSCALL(pipe)
SYSCALL(read)
@@ -28,4 +36,6 @@ SYSCALL(getpid)
SYSCALL(sbrk)
SYSCALL(sleep)
SYSCALL(uptime)
-SYSCALL(putc) \ No newline at end of file
+SYSCALL(putc)
+SYSCALL(disk_test)
+SYSCALL(lseek) \ No newline at end of file