Compare commits
201 Commits
185757e2a7
...
da41784183
Author | SHA1 | Date | |
---|---|---|---|
da41784183 | |||
b8014a158c | |||
2ac98ed0c1 | |||
078f3f5862 | |||
d76af10e70 | |||
60bd8d9418 | |||
1ea216dfd1 | |||
da7ad8da5b | |||
ea94b331fb | |||
46e4d37098 | |||
d1dea3f1d6 | |||
b6f2b41f1f | |||
61753a6f51 | |||
57ef8feddb | |||
0bfe5e27ef | |||
d3030d2111 | |||
da4c0d5184 | |||
f8f5968753 | |||
1300b8f5ee | |||
bbc7a7338c | |||
59e03d0799 | |||
accf7ee417 | |||
033c41cbd7 | |||
99429baed2 | |||
bb26996cb7 | |||
98da473fdc | |||
0c1ffedd3f | |||
327b60566c | |||
054d5a8bb4 | |||
00f9981f51 | |||
6f72f92493 | |||
2230ebd969 | |||
472192bcf2 | |||
78ee6ce34a | |||
c9e20fd38e | |||
2eeef9581f | |||
2e9348181d | |||
5d94525c7a | |||
c6ce7a5358 | |||
1025248cc7 | |||
44815b08c7 | |||
04ae97a6ec | |||
2980ee3973 | |||
88011fc162 | |||
40b078e0a2 | |||
d7692a7f59 | |||
da182f1c2f | |||
b2fb740d99 | |||
b78f6f269d | |||
cedefaf93d | |||
9ab3ab7c40 | |||
b63a9f5ba9 | |||
67f536cf91 | |||
d0efc106b0 | |||
ffcaac0ca3 | |||
cbc2e76082 | |||
e5b2641019 | |||
2c08de044f | |||
71633e264f | |||
979569eb5a | |||
3b86f87c76 | |||
2cfefc25cc | |||
0fd31698b2 | |||
7afbff08b6 | |||
249c79f8a3 | |||
feab66c0d3 | |||
8f2308c80d | |||
42a805fd60 | |||
c604c074a1 | |||
e5cf32c7b3 | |||
875d971d3b | |||
dfcc827103 | |||
07e518c38f | |||
22740e69bf | |||
534500cda0 | |||
497a52dd82 | |||
c2a08060cf | |||
5eae93bbb0 | |||
aabff7a1d3 | |||
45f40a31d6 | |||
f83c78bcad | |||
7bd1cba1e3 | |||
52d4f34f05 | |||
0d443385e9 | |||
29c59abf7d | |||
9c3792718c | |||
e244c150c2 | |||
c68d040484 | |||
a7e4f2bdd2 | |||
e58aa361c8 | |||
00f90246c8 | |||
08c4dac2c2 | |||
b4ccd786f9 | |||
97df9d8d3a | |||
948361bec5 | |||
d186d573dd | |||
d5a6c7f27f | |||
4c096bd36c | |||
af0f4d2037 | |||
6df5b8a703 | |||
e640c6e245 | |||
8d46c9bbe2 | |||
e705810af3 | |||
e2b5c1bfdd | |||
324fb42ee2 | |||
688a640a16 | |||
baf97840e9 | |||
a9da58421f | |||
d1e4bc5504 | |||
8395eb16f6 | |||
7aad256acd | |||
33ed6e5c17 | |||
8375701bf6 | |||
3e052c72a0 | |||
32e09d3417 | |||
9db1e8cdb3 | |||
e9092ab235 | |||
0b838572e1 | |||
702cc0442c | |||
2ca20c1a1e | |||
91470851cd | |||
5aba1c5f15 | |||
477af66cdc | |||
7d0e442cde | |||
e05f3f5325 | |||
a2d9ada4dc | |||
b8296eb92d | |||
fa0dc4b18c | |||
68d0d0b759 | |||
8f0e358360 | |||
09a615bd99 | |||
acdc2d3ad7 | |||
17671fd435 | |||
1c4f1ab867 | |||
2269ec267c | |||
b1729689df | |||
77d331b258 | |||
c312d81de4 | |||
26b20938de | |||
0115cce750 | |||
6ddfc5ee52 | |||
16dc227a05 | |||
3effe8b004 | |||
b2f321c0b8 | |||
8ed0ff1381 | |||
d93a4062a2 | |||
0eb0ca4028 | |||
7155cf8d6b | |||
703b0a1435 | |||
50cda50f01 | |||
fcf53ef6a5 | |||
da8a3de480 | |||
cdb1f46b93 | |||
06e6429567 | |||
f9dad8a8d6 | |||
9b0f6b6595 | |||
211c76f920 | |||
1c35eabb2b | |||
a3c6635f3e | |||
651ffe6d64 | |||
d875224045 | |||
f004122735 | |||
41f7232b77 | |||
d5a64319f9 | |||
2512acc716 | |||
e1f58c0163 | |||
6892fd96d0 | |||
a4e430d35e | |||
23b12d2d56 | |||
5492b1b44d | |||
796d61020b | |||
4f6333ca17 | |||
8eb986df63 | |||
7d20c507b1 | |||
0bad662c2f | |||
9b1e50ae27 | |||
57658f2d3e | |||
13fce2c4b3 | |||
af452e2b2a | |||
af46b8d9ac | |||
ec2c314234 | |||
8cfede341f | |||
ada7f1e701 | |||
7c460855cc | |||
35da7e0223 | |||
d458c5c848 | |||
1716a81e82 | |||
b17ff6319a | |||
040fbde462 | |||
bd56c7f496 | |||
33f6765a5c | |||
315d2f9f24 | |||
0dec5f7bad | |||
e7d41fa6dc | |||
3db342e897 | |||
2d807e3ca5 | |||
fb0fc29087 | |||
c7598b08e0 | |||
b615166373 | |||
58b01b74e2 | |||
0c04246300 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -2,14 +2,13 @@ Luna.iso
|
||||
toolchain/
|
||||
.vscode/
|
||||
**/*.o
|
||||
initrd/boot/moon.elf
|
||||
kernel/bin/moon.elf
|
||||
initrd/boot/moon
|
||||
kernel/bin/moon
|
||||
initrd/sys/moon.sym
|
||||
initrd/bin/**
|
||||
apps/bin/**
|
||||
tests/**/bin/**
|
||||
base/usr/include/**
|
||||
base/usr/lib/**
|
||||
base/usr/**
|
||||
**/*.a
|
||||
ports/**/workdir/**
|
||||
ports/ports.list
|
||||
|
24
Makefile
24
Makefile
@ -5,22 +5,22 @@ AR := x86_64-luna-ar
|
||||
LD := x86_64-luna-ld
|
||||
|
||||
build:
|
||||
tools/sync-libc.sh
|
||||
@$(MAKE) -C kernel build
|
||||
@$(MAKE) -C libs build
|
||||
@$(MAKE) -C apps build
|
||||
+@tools/sync-libc.sh
|
||||
+@tools/buildstep.sh kernel build
|
||||
+@tools/buildstep.sh libs build
|
||||
+@tools/buildstep.sh apps build
|
||||
|
||||
clean: initrd-clean
|
||||
@$(MAKE) -C kernel clean
|
||||
@$(MAKE) -C libs clean
|
||||
@$(MAKE) -C apps clean
|
||||
+@tools/buildstep.sh kernel clean
|
||||
+@tools/buildstep.sh libs clean
|
||||
+@tools/buildstep.sh apps clean
|
||||
|
||||
initrd-clean:
|
||||
rm -f $(LUNA_ROOT)/initrd/boot/moon.elf $(LUNA_ROOT)/Luna.iso
|
||||
rm -f $(LUNA_ROOT)/initrd/boot/moon $(LUNA_ROOT)/Luna.iso
|
||||
rm -rf $(LUNA_ROOT)/initrd/bin
|
||||
|
||||
install:
|
||||
@$(MAKE) -C kernel install
|
||||
@$(MAKE) -C libs install
|
||||
@$(MAKE) -C apps install
|
||||
@tools/install-built-ports.sh
|
||||
+@tools/buildstep.sh kernel install
|
||||
+@tools/buildstep.sh libs install
|
||||
+@tools/buildstep.sh apps install
|
||||
+@tools/install-built-ports.sh
|
32
README.md
32
README.md
@ -1,16 +1,20 @@
|
||||
# Luna
|
||||
A simple kernel and userspace for the x86_64 platform, written mostly in C++.
|
||||
A simple kernel and userspace for the x86_64 platform, written mostly in C++ and C.
|
||||
|
||||
## Features
|
||||
- x86_64-compatible [kernel](kernel/).
|
||||
- Keeps track of which [memory](kernel/src/memory/) is used and which memory is free, and can allocate memory for itself and [user programs](kernel/src/sys/mem.cpp).
|
||||
- Can load files from a [virtual file system](kernel/src/fs/) supporting an initial ramdisk, device pseudo-filesystems... but no hard disks yet.
|
||||
- Basic preemptive multitasking, round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
||||
- Can [load userspace ELF programs](kernel/src/sys/elf/) from the file system as user tasks.
|
||||
- [System call](kernel/src/sys/) interface and simple [C Library](libs/libc/), aiming to be POSIX-compatible.
|
||||
- Some very simple [example programs](apps/), written in C, that then can get loaded and executed by the kernel at userspace demand.
|
||||
- Can read, write and execute files from a [virtual file system](kernel/src/fs/) supporting an initial ramdisk, device pseudo-filesystems... but no hard disks yet.
|
||||
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
||||
- Can [load ELF programs](kernel/src/sys/elf/) from the file system as userspace tasks.
|
||||
- [System call](kernel/src/sys/) interface and [C Library](libs/libc/), aiming to be mostly POSIX-compatible.
|
||||
- UNIX-like [multitasking primitives](kernel/src/sys/exec.cpp), which allow user tasks to spawn other tasks.
|
||||
- Very simple [command-line shell](apps/src/sh.c), allowing interactive use of the system.
|
||||
- Some simple [userspace programs](apps/src/), written in C.
|
||||
- Simple [command-line shell](apps/src/sh.c), allowing for interactive use of the system.
|
||||
- Basic multi-user system with [a password file](initrd/etc/passwd) and utilities for [logging in](apps/src/session.c) and [switching users](apps/src/su.c).
|
||||
|
||||
## Notes
|
||||
- The default user is named 'selene' and you can log into it with the password 'moon'.
|
||||
|
||||
## Setup
|
||||
To build and run Luna, you will need to build a [GCC Cross-Compiler](https://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler%3F) and cross-binutils for `x86_64-luna`. (Yes, Luna is advanced enough that it can use its own [OS-Specific Toolchain](https://wiki.osdev.org/OS_Specific_Toolchain), instead of a bare metal target like `x86_64-elf`. It is the first of my OS projects to be able to do so. The patches for Binutils and GCC are [binutils.patch](tools/binutils.patch) and [gcc.patch](tools/gcc.patch)).
|
||||
@ -58,7 +62,7 @@ You can choose between 3 run scripts:
|
||||
|
||||
`tools/debug.sh` will run Luna in QEMU with a port open for GDB to connect to. (run `tools/build-debug.sh`, `tools/gdb.sh`, and then `tools/debug.sh` in a separate terminal for an optimal debugging experience)
|
||||
|
||||
Beware that running without hardware virtualization/with optimizations disabled may cause the kernel to behave differently, which is why I don't use it that often.
|
||||
Beware that running with optimizations disabled may cause the kernel to behave differently, which is why I don't use it that often.
|
||||
|
||||
Essentially, since `run.sh` builds the toolchain if it hasn't been built, builds Luna if it hasn't been built, and runs it, you could just checkout this repo, run `run.sh`, and you're done. No need for the other scripts. Those are included for more fine-grained control/building step-by-step.
|
||||
|
||||
@ -66,18 +70,20 @@ You can pass any arguments you want to the run scripts, and those will be forwar
|
||||
|
||||
## Prebuilt images
|
||||
|
||||
Prebuilt ISO images for every version can be found at [pub.cloudapio.eu](https://pub.cloudapio.eu/luna).
|
||||
Prebuilt ISO images (numbered) for every version can be found at [pub.cloudapio.eu](https://pub.cloudapio.eu/luna/releases).
|
||||
|
||||
These images are built manually whenever I decide to make a new version, and thus don't reflect the latest changes on the `main` branch.
|
||||
|
||||
Every hour, my server pulls the latest commits on `main` and builds an hourly ISO image. The ten most recent ones can be found in the [hourly](https://pub.cloudapio.eu/luna/hourly) directory, and [Luna-latest.iso](https://pub.cloudapio.eu/luna/Luna-latest.iso) should always be symlinked to the newest one.
|
||||
|
||||
These images do reflect the latest changes on the `main` branch, but are obviously less stable. Additionally, an hourly image will be skipped if building the latest commit of the project fails.
|
||||
|
||||
## Is there third-party software I can use on Luna?
|
||||
|
||||
Yes, actually! Check out the [ports](ports/) directory.
|
||||
Right now, only [bc](https://github.com/gavinhoward/bc) is ported, because right now our C Library is quite primitive and doesn't support projects more complex than that.
|
||||
Right now, there are very few ports, because our C Library is a bit primitive and doesn't support complex projects.
|
||||
|
||||
And bc itself doesn't run very well... most notably, user input doesn't echo. But that's on our side. At least it runs!
|
||||
|
||||
You should also keep in mind that it is not possible to compile software written in any language other than C for Luna right now.
|
||||
You should also keep in mind that it is not possible to compile software written in any language other than C/C++ (and C++ was added recently, its standard library doesn't work very well) for Luna right now.
|
||||
|
||||
But feel free to try to port some program yourself and add it to the ports directory!
|
||||
|
||||
|
@ -1,22 +1,33 @@
|
||||
APPS := init sym sh crash uname uptime hello ps ls
|
||||
C_APPS := init sh uname uptime hello ps ls args cat stat su session date mkdir screen
|
||||
CXX_APPS := hello-cpp
|
||||
|
||||
APPS_DIR := $(LUNA_ROOT)/apps
|
||||
APPS_SRC := $(APPS_DIR)/src
|
||||
APPS_BIN := $(APPS_DIR)/bin
|
||||
|
||||
REAL_APPS := $(patsubst %, $(APPS_BIN)/%, $(APPS))
|
||||
C_APPS_PATH := $(patsubst %, $(APPS_BIN)/%, $(C_APPS))
|
||||
CXX_APPS_PATH := $(patsubst %, $(APPS_BIN)/%, $(CXX_APPS))
|
||||
|
||||
CFLAGS := -Wall -Wextra -Werror -Os -fno-asynchronous-unwind-tables
|
||||
CFLAGS := -Wall -Wextra -Werror -Os -fno-asynchronous-unwind-tables -ffunction-sections -fdata-sections -Wl,--gc-sections
|
||||
CXXFLAGS := -fno-exceptions
|
||||
|
||||
$(APPS_BIN)/%: $(APPS_SRC)/%.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
@$(CC) $(CFLAGS) -o $@ $^
|
||||
@echo " CC $^"
|
||||
|
||||
build: $(REAL_APPS)
|
||||
$(APPS_BIN)/%: $(APPS_SRC)/%.cpp
|
||||
@mkdir -p $(@D)
|
||||
@$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ $^
|
||||
@echo " CXX $^"
|
||||
|
||||
install: $(REAL_APPS)
|
||||
build: $(C_APPS_PATH) $(CXX_APPS_PATH)
|
||||
|
||||
install: $(C_APPS_PATH) $(CXX_APPS_PATH)
|
||||
@mkdir -p $(LUNA_ROOT)/initrd/bin
|
||||
cp $(REAL_APPS) $(LUNA_ROOT)/initrd/bin
|
||||
@cp $(C_APPS_PATH) $(CXX_APPS_PATH) $(LUNA_ROOT)/initrd/bin
|
||||
@echo " INSTALL $(C_APPS_PATH) $(CXX_APPS_PATH)"
|
||||
@chmod a+s $(LUNA_ROOT)/initrd/bin/su
|
||||
|
||||
clean:
|
||||
rm -f $(APPS_BIN)/*
|
7
apps/src/args.c
Normal file
7
apps/src/args.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
for (int i = 0; i < argc; i++) puts(argv[i]);
|
||||
}
|
45
apps/src/cat.c
Normal file
45
apps/src/cat.c
Normal file
@ -0,0 +1,45 @@
|
||||
#define _GNU_SOURCE // for program_invocation_name
|
||||
#include <errno.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void cat(FILE* stream)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
do {
|
||||
fgets(buf, BUFSIZ, stream);
|
||||
if (ferror(stream))
|
||||
{
|
||||
perror(program_invocation_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fputs(buf, stdout);
|
||||
} while (!feof(stream));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc == 1) cat(stdin);
|
||||
else
|
||||
{
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "-") == 0) cat(stdin);
|
||||
else if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--newline") == 0)
|
||||
putchar('\n');
|
||||
else
|
||||
{
|
||||
FILE* stream = fopen(argv[i], "r");
|
||||
if (!stream)
|
||||
{
|
||||
perror(program_invocation_name);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cat(stream);
|
||||
fclose(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
int main()
|
||||
{
|
||||
int* ptr = (int*)0xdeadbeef;
|
||||
*ptr = 6;
|
||||
}
|
9
apps/src/date.c
Normal file
9
apps/src/date.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
time_t date = time(NULL);
|
||||
|
||||
fputs(ctime(&date), stdout);
|
||||
}
|
6
apps/src/hello-cpp.cpp
Normal file
6
apps/src/hello-cpp.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Well hello world!\n");
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <luna.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
@ -6,15 +7,21 @@
|
||||
|
||||
void show_motd()
|
||||
{
|
||||
FILE* fp = fopen("/etc/motd", "r");
|
||||
int fd = open("/etc/motd", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (errno != ENOENT) { perror("open"); }
|
||||
return;
|
||||
}
|
||||
FILE* fp = fdopen(fd, "r");
|
||||
if (!fp)
|
||||
{
|
||||
if (errno != ENOENT) { perror("fopen"); }
|
||||
perror("fopen");
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
size_t nread = fread(buf, sizeof(buf) - 1, 1, fp);
|
||||
size_t nread = fread(buf, 1, sizeof(buf) - 1, fp);
|
||||
if (ferror(fp))
|
||||
{
|
||||
perror("fread");
|
||||
@ -34,14 +41,18 @@ int main()
|
||||
{
|
||||
if (getpid() != 1)
|
||||
{
|
||||
fprintf(stderr, "init should be started as PID 1\n");
|
||||
fprintf(stderr, "init must be started as PID 1\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (getuid() != 0)
|
||||
{
|
||||
fprintf(stderr, "init must be started as root\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
show_motd();
|
||||
|
||||
msleep(200);
|
||||
|
||||
pid_t child = fork();
|
||||
if (child < 0)
|
||||
{
|
||||
@ -50,7 +61,8 @@ int main()
|
||||
}
|
||||
if (child == 0)
|
||||
{
|
||||
execv("/bin/sh", NULL);
|
||||
char* argv[] = {"/bin/session", NULL};
|
||||
execv(argv[0], argv);
|
||||
perror("execv");
|
||||
return 1;
|
||||
}
|
||||
@ -59,10 +71,7 @@ int main()
|
||||
|
||||
for (;;)
|
||||
{
|
||||
while ((result = wait(NULL)) == 0) // No child has exited yet
|
||||
{
|
||||
msleep(100);
|
||||
}
|
||||
if (result == child) { return 0; }
|
||||
result = wait(NULL);
|
||||
if (result == child) return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,31 @@
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
DIR* dp = opendir("/bin");
|
||||
const char* pathname;
|
||||
if (argc == 1) pathname = "/";
|
||||
else
|
||||
pathname = argv[1];
|
||||
DIR* dp = opendir(pathname);
|
||||
if (!dp)
|
||||
{
|
||||
perror("opendir");
|
||||
return 1;
|
||||
}
|
||||
bool first_ent = true;
|
||||
do {
|
||||
struct dirent* ent = readdir(dp);
|
||||
if (!ent) break;
|
||||
printf("%s\n", ent->d_name);
|
||||
printf(first_ent ? "%s" : " %s", ent->d_name);
|
||||
first_ent = false;
|
||||
} while (1);
|
||||
|
||||
printf("\n");
|
||||
|
||||
closedir(dp);
|
||||
return 0;
|
||||
}
|
17
apps/src/mkdir.c
Normal file
17
apps/src/mkdir.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [directory]", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mkdir(argv[1], 0755) < 0)
|
||||
{
|
||||
perror("mkdir");
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include <errno.h>
|
||||
#include <luna/pstat.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@ -17,8 +18,10 @@ pid_t get_current_max_threads()
|
||||
|
||||
void display_process(struct pstat* pstatbuf)
|
||||
{
|
||||
printf("%ld %ld %s %s (%ld ms)\n", pstatbuf->pt_pid, pstatbuf->pt_ppid, pstatbuf->pt_name, pstatname(pstatbuf),
|
||||
pstatbuf->pt_time);
|
||||
struct passwd* pwd = getpwuid(pstatbuf->pt_uid);
|
||||
if (!pwd && errno) perror("getpwuid");
|
||||
printf("%s %ld %ld %s %s (%ld ms)\n", pwd ? pwd->pw_name : "???", pstatbuf->pt_pid, pstatbuf->pt_ppid,
|
||||
pstatbuf->pt_name, pstatname(pstatbuf), pstatbuf->pt_time);
|
||||
}
|
||||
|
||||
int try_pstat(pid_t pid, struct pstat* pstatbuf)
|
||||
@ -41,4 +44,5 @@ int main()
|
||||
{
|
||||
if (try_pstat(pid, &pst)) { display_process(&pst); }
|
||||
}
|
||||
endpwent();
|
||||
}
|
32
apps/src/screen.c
Normal file
32
apps/src/screen.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int fd = open("/dev/fb0", O_WRONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fb_width = ioctl(fd, FB_GET_WIDTH);
|
||||
if (fb_width < 0)
|
||||
{
|
||||
perror("ioctl(FB_GET_WIDTH)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fb_height = ioctl(fd, FB_GET_HEIGHT);
|
||||
if (fb_height < 0)
|
||||
{
|
||||
perror("ioctl(FB_GET_HEIGHT)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Your screen is %dx%d\n", fb_width, fb_height);
|
||||
|
||||
close(fd);
|
||||
}
|
145
apps/src/session.c
Normal file
145
apps/src/session.c
Normal file
@ -0,0 +1,145 @@
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static char* echoing_fgets(char* buf, size_t size, FILE* stream)
|
||||
{
|
||||
char* s = buf;
|
||||
memset(buf, 0, size);
|
||||
size_t oldsize = size;
|
||||
while (size)
|
||||
{
|
||||
int c = fgetc(stream);
|
||||
if (c == EOF)
|
||||
{
|
||||
if (ferror(stream)) return NULL;
|
||||
if (feof(stream))
|
||||
{
|
||||
if (s != buf) return s;
|
||||
else
|
||||
return NULL;
|
||||
};
|
||||
}
|
||||
if ((char)c == '\b')
|
||||
{
|
||||
if (size != oldsize)
|
||||
{
|
||||
buf--;
|
||||
size++;
|
||||
putchar('\b');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size--;
|
||||
*buf = (char)c;
|
||||
buf++;
|
||||
putchar((char)c);
|
||||
if ((char)c == '\n') return s;
|
||||
}
|
||||
*buf = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static void strip_newline(char* str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
if (str[len - 1] == '\n') str[len - 1] = 0;
|
||||
}
|
||||
|
||||
static char* collect_password()
|
||||
{
|
||||
static char buf[BUFSIZ];
|
||||
|
||||
printf("Password: ");
|
||||
fgets(buf, BUFSIZ, stdin);
|
||||
|
||||
strip_newline(buf);
|
||||
putchar('\n');
|
||||
|
||||
char* copy = strdup(
|
||||
buf); // The password only stays in a caller-controlled heap-allocated buffer, where it can be freed at will.
|
||||
|
||||
memset(buf, 0, BUFSIZ);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void login_as(struct passwd* user)
|
||||
{
|
||||
pid_t child = fork();
|
||||
if (child < 0)
|
||||
{
|
||||
perror("fork");
|
||||
return;
|
||||
}
|
||||
if (child == 0)
|
||||
{
|
||||
setgid(user->pw_gid);
|
||||
setuid(user->pw_uid);
|
||||
char* argv[] = {user->pw_shell, NULL};
|
||||
execv(argv[0], argv);
|
||||
perror("execv");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
wait(NULL);
|
||||
}
|
||||
|
||||
static int login()
|
||||
{
|
||||
printf("Username: ");
|
||||
char username[BUFSIZ];
|
||||
echoing_fgets(username, BUFSIZ, stdin);
|
||||
strip_newline(username);
|
||||
if (strcmp("exit", username) == 0) return 1;
|
||||
struct passwd* user = getpwnam(username);
|
||||
if (!user)
|
||||
{
|
||||
if (errno) perror("getpwnam");
|
||||
else
|
||||
printf("Unknown user %s\n", username);
|
||||
return 0;
|
||||
}
|
||||
char* password = collect_password();
|
||||
putchar('\n');
|
||||
if (strcmp(user->pw_passwd, password) == 0)
|
||||
{
|
||||
free(password);
|
||||
login_as(user);
|
||||
puts("logout\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
free(password);
|
||||
puts("Invalid password.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
(void)argc;
|
||||
|
||||
if (getuid() != 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s must be run as root.\nYou are probably looking for the 'su' command, which lets you switch users "
|
||||
"once logged in.\n",
|
||||
argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (login()) break;
|
||||
}
|
||||
|
||||
endpwent();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
261
apps/src/sh.c
261
apps/src/sh.c
@ -1,6 +1,7 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <luna.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -8,29 +9,89 @@
|
||||
#include <unistd.h>
|
||||
|
||||
static int status = 0;
|
||||
static char* username = NULL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* buffer;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} command;
|
||||
int interactive;
|
||||
} command_t;
|
||||
|
||||
char** split_command_into_argv(const char* cmd)
|
||||
{
|
||||
size_t argc = 1;
|
||||
char* ptr = strdup(cmd);
|
||||
char* endptr;
|
||||
char** arr = calloc(sizeof(char*), argc);
|
||||
for (;;)
|
||||
{
|
||||
endptr = strchr(ptr, ' ');
|
||||
arr[argc - 1] = ptr;
|
||||
if (endptr == NULL) break;
|
||||
*endptr = 0;
|
||||
ptr = endptr + 1;
|
||||
if (*ptr)
|
||||
{
|
||||
argc++;
|
||||
arr = realloc(arr, sizeof(char*) * argc);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
argc++;
|
||||
arr = realloc(arr, sizeof(char*) * argc);
|
||||
arr[argc - 1] = NULL;
|
||||
return arr;
|
||||
}
|
||||
|
||||
char* shell_concat_path(const char* dirname, const char* basename)
|
||||
{
|
||||
char* buf = malloc(strlen(basename) + strlen(dirname) + 6);
|
||||
strlcpy(buf, dirname, strlen(dirname) + 1);
|
||||
strncat(buf, basename, strlen(basename));
|
||||
return buf;
|
||||
}
|
||||
|
||||
void shell_execvp(char* pathname, char* const argv[])
|
||||
{
|
||||
char* new_path;
|
||||
if (access(pathname, F_OK) == 0)
|
||||
{
|
||||
execv(pathname, argv);
|
||||
return;
|
||||
}
|
||||
if (pathname[0] == '/') return; // We do not want to lookup absolute paths
|
||||
new_path = shell_concat_path("/bin/", pathname);
|
||||
if (access(new_path, F_OK) == 0)
|
||||
{
|
||||
execv(new_path, argv);
|
||||
return;
|
||||
}
|
||||
free(new_path);
|
||||
new_path = shell_concat_path("/usr/bin/", pathname);
|
||||
execv(new_path, argv);
|
||||
int saved = errno;
|
||||
free(new_path);
|
||||
errno = saved;
|
||||
}
|
||||
|
||||
void show_prompt()
|
||||
{
|
||||
if (WEXITSTATUS(status)) { printf("%d [%ld]> ", WEXITSTATUS(status), getpid()); }
|
||||
if (WEXITSTATUS(status)) { printf("%d [%s]> ", WEXITSTATUS(status), username); }
|
||||
else
|
||||
printf("[%ld]> ", getpid());
|
||||
printf("[%s]> ", username);
|
||||
}
|
||||
|
||||
int command_matches(command* cmd, const char* string)
|
||||
int command_matches(command_t* cmd, const char* string)
|
||||
{
|
||||
if (cmd->size <= strlen(string)) // cmd->size includes null terminator
|
||||
return 0;
|
||||
return strncmp(cmd->buffer, string, strlen(string)) == 0;
|
||||
}
|
||||
|
||||
int command_matches_exactly(command* cmd, const char* string)
|
||||
int command_matches_exactly(command_t* cmd, const char* string)
|
||||
{
|
||||
if (cmd->size <= strlen(string)) // cmd->size includes null terminator
|
||||
return 0;
|
||||
@ -38,19 +99,24 @@ int command_matches_exactly(command* cmd, const char* string)
|
||||
return strncmp(cmd->buffer, string, strlen(string)) == 0;
|
||||
}
|
||||
|
||||
int command_match_builtins(command* cmd)
|
||||
int command_match_builtins(command_t* cmd)
|
||||
{
|
||||
if (command_matches(cmd, "exit ")) { exit(atoi(cmd->buffer + 5)); }
|
||||
if (command_matches_exactly(cmd, "exit")) { exit(0); }
|
||||
if (command_matches_exactly(cmd, "pid"))
|
||||
if (command_matches_exactly(cmd, "id"))
|
||||
{
|
||||
printf("pid %ld, ppid %ld\n", getpid(), getppid());
|
||||
printf("pid %ld, ppid %ld, uid %d (%s), gid %d\n", getpid(), getppid(), getuid(), username, getgid());
|
||||
return 1;
|
||||
}
|
||||
if (command_matches_exactly(cmd, "clear"))
|
||||
{
|
||||
fputs("\033@", stdout); // clear screen. for now, escape sequences in luna are non-standard.
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void command_expand(command* cmd, long new_capacity)
|
||||
void command_expand(command_t* cmd, long new_capacity)
|
||||
{
|
||||
char* buffer = realloc(cmd->buffer, new_capacity);
|
||||
if (!buffer)
|
||||
@ -62,105 +128,101 @@ void command_expand(command* cmd, long new_capacity)
|
||||
cmd->capacity = new_capacity;
|
||||
}
|
||||
|
||||
void command_push(command* cmd, char c)
|
||||
void command_push(command_t* cmd, char c)
|
||||
{
|
||||
if (cmd->size == cmd->capacity) command_expand(cmd, cmd->capacity + 8);
|
||||
cmd->buffer[cmd->size] = c;
|
||||
cmd->size++;
|
||||
}
|
||||
|
||||
void command_pop(command* cmd)
|
||||
void command_pop(command_t* cmd)
|
||||
{
|
||||
cmd->size--;
|
||||
}
|
||||
|
||||
void command_init(command* cmd)
|
||||
void command_init(command_t* cmd)
|
||||
{
|
||||
cmd->buffer = malloc(5);
|
||||
cmd->capacity = 5;
|
||||
cmd->size = 0;
|
||||
}
|
||||
|
||||
void command_clear(command* cmd)
|
||||
void command_clear(command_t* cmd)
|
||||
{
|
||||
free(cmd->buffer);
|
||||
return command_init(cmd);
|
||||
}
|
||||
|
||||
void command_execute(command* cmd)
|
||||
void process_execute_command(const char* command)
|
||||
{
|
||||
char** argv = split_command_into_argv(command);
|
||||
shell_execvp(argv[0], argv);
|
||||
perror(argv[0]);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
void command_execute(command_t* cmd)
|
||||
{
|
||||
command_push(cmd, '\0');
|
||||
if (command_match_builtins(cmd))
|
||||
{
|
||||
command_clear(cmd);
|
||||
show_prompt();
|
||||
if (cmd->interactive) show_prompt();
|
||||
return;
|
||||
}
|
||||
pid_t child = fork();
|
||||
if (child < 0)
|
||||
{
|
||||
perror(cmd->buffer);
|
||||
perror("fork");
|
||||
command_clear(cmd);
|
||||
show_prompt();
|
||||
if (cmd->interactive) show_prompt();
|
||||
return;
|
||||
}
|
||||
if (child == 0)
|
||||
{
|
||||
if (cmd->buffer[0] != '/' && access(cmd->buffer, F_OK) < 0) // FIXME: Race condition.
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{ // Try in /bin
|
||||
char* buf = malloc(cmd->size + 6);
|
||||
strlcpy(buf, "/bin/", 6);
|
||||
strncat(buf, cmd->buffer, cmd->size);
|
||||
execv(buf, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
execv(cmd->buffer, NULL);
|
||||
perror(cmd->buffer);
|
||||
exit(127);
|
||||
}
|
||||
pid_t result;
|
||||
while ((result = waitpid(child, &status, 0)) == 0) { msleep(20); }
|
||||
if (child == 0) process_execute_command(cmd->buffer);
|
||||
pid_t result = waitpid(child, &status, 0);
|
||||
if (result < 0)
|
||||
{
|
||||
perror("waitpid");
|
||||
command_clear(cmd);
|
||||
show_prompt();
|
||||
if (cmd->interactive) show_prompt();
|
||||
return;
|
||||
}
|
||||
int exit_status = WEXITSTATUS(status);
|
||||
if (exit_status == -2 || exit_status == -3) printf("(PID %ld) Segmentation fault\n", result);
|
||||
if (exit_status == -1) printf("(PID %ld) Aborted\n", result);
|
||||
command_clear(cmd);
|
||||
show_prompt();
|
||||
if (cmd->interactive) show_prompt();
|
||||
}
|
||||
|
||||
void command_concat_char(command* cmd, char c)
|
||||
void command_concat_char(command_t* cmd, char c)
|
||||
{
|
||||
if (c == '\b')
|
||||
{
|
||||
if (cmd->size != 0)
|
||||
{
|
||||
putchar(c);
|
||||
if (cmd->interactive) putchar(c);
|
||||
command_pop(cmd);
|
||||
}
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
putchar(c);
|
||||
if (cmd->size == 0) show_prompt();
|
||||
if (cmd->interactive) putchar(c);
|
||||
if (cmd->size == 0)
|
||||
{
|
||||
status = 0;
|
||||
if (cmd->interactive) show_prompt();
|
||||
}
|
||||
else
|
||||
command_execute(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
putchar(c);
|
||||
if (cmd->interactive) putchar(c);
|
||||
command_push(cmd, c);
|
||||
}
|
||||
}
|
||||
|
||||
void command_concat(command* cmd, const char* str)
|
||||
void command_concat(command_t* cmd, const char* str)
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
@ -169,13 +231,15 @@ void command_concat(command* cmd, const char* str)
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
void shell_interactive()
|
||||
{
|
||||
show_prompt();
|
||||
|
||||
command shell_command;
|
||||
command_t shell_command;
|
||||
command_init(&shell_command);
|
||||
|
||||
shell_command.interactive = 1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int c = getchar();
|
||||
@ -184,11 +248,112 @@ int main()
|
||||
if (ferror(stdin))
|
||||
{
|
||||
perror("getchar");
|
||||
return 1;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (feof(stdin)) { return 0; }
|
||||
if (feof(stdin)) exit(EXIT_SUCCESS);
|
||||
assert(false); // we should never get here
|
||||
}
|
||||
command_concat_char(&shell_command, (char)c);
|
||||
}
|
||||
}
|
||||
|
||||
void shell_read_from_file(const char* pathname)
|
||||
{
|
||||
FILE* fp = fopen(pathname, "r");
|
||||
if (!fp)
|
||||
{
|
||||
perror("sh");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
command_t file_command;
|
||||
command_init(&file_command);
|
||||
|
||||
file_command.interactive = 0;
|
||||
|
||||
char buffer[BUFSIZ];
|
||||
while (fgets(buffer, BUFSIZ, fp))
|
||||
{
|
||||
command_concat(&file_command, buffer);
|
||||
if (feof(fp)) break;
|
||||
}
|
||||
|
||||
if (file_command.size > 0) // last line of file, does not end with newline
|
||||
{
|
||||
command_execute(&file_command);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void shell_execute_command(const char* command)
|
||||
{
|
||||
command_t cmd;
|
||||
cmd.buffer = strdup(command);
|
||||
cmd.size = strlen(command) + 1;
|
||||
if (command_match_builtins(&cmd)) return;
|
||||
command_clear(&cmd);
|
||||
process_execute_command(command);
|
||||
}
|
||||
|
||||
void fetch_username()
|
||||
{
|
||||
struct passwd* user = getpwuid(getuid());
|
||||
if (!user)
|
||||
{
|
||||
perror("getpwuid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
username = user->pw_name;
|
||||
endpwent();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
fetch_username();
|
||||
|
||||
if (argc == 1) shell_interactive();
|
||||
else if (argc == 2)
|
||||
{
|
||||
if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))
|
||||
{
|
||||
puts("Luna sh version 0.1"); // FIXME: Store the version somewhere, or use the kernel's version.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
|
||||
{
|
||||
printf("To use interactively: %s\n", argv[0]);
|
||||
printf("To run a script: %s [script-name]\n", argv[0]);
|
||||
printf("To get help: %s --help\n", argv[0]);
|
||||
printf("To show the version: %s --version\n", argv[0]);
|
||||
printf("To run a command: %s -c [command]\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--command"))
|
||||
{
|
||||
fprintf(stderr, "Usage: %s %s [command]\n", argv[0], argv[1]);
|
||||
fprintf(stderr, "Use the --help flag for more help.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
shell_read_from_file(argv[1]);
|
||||
}
|
||||
else if (argc == 3)
|
||||
{
|
||||
if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--command")) shell_execute_command(argv[2]);
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: too many arguments\n", argv[0]);
|
||||
fprintf(stderr, "Use the --help flag for more help.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: too many arguments\n", argv[0]);
|
||||
fprintf(stderr, "Use the --help flag for more help.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
66
apps/src/stat.c
Normal file
66
apps/src/stat.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
const char* mode_to_string(mode_t mode)
|
||||
{
|
||||
static char mode_string[12];
|
||||
|
||||
char mode_set[12] = {'s', 'g', 'r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x', 0};
|
||||
mode_t mode_val[12] = {S_ISUID, S_ISGID, S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP,
|
||||
S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH, S_IFMT};
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
if (mode & mode_val[i]) mode_string[i] = mode_set[i];
|
||||
else
|
||||
mode_string[i] = '-';
|
||||
}
|
||||
|
||||
return mode_string;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
fprintf(stderr, "Usage: stat [file]\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(argv[1], &st) < 0)
|
||||
{
|
||||
perror("stat");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Type: ");
|
||||
|
||||
switch (st.st_mode & S_IFMT)
|
||||
{
|
||||
case S_IFREG: puts("Regular file"); break;
|
||||
case S_IFDIR: puts("Directory"); break;
|
||||
case S_IFCHR: puts("Character device"); break;
|
||||
default: puts("Unknown"); break;
|
||||
}
|
||||
|
||||
struct passwd* own = getpwuid(st.st_uid);
|
||||
|
||||
printf("Length: %ld\n", st.st_size);
|
||||
printf("Inode: %ld\n", st.st_ino);
|
||||
if (!own) printf("Owned by: UID %d\n", st.st_uid);
|
||||
else
|
||||
printf("Owned by: %s\n", own->pw_name);
|
||||
printf("Mode: %s\n", mode_to_string(st.st_mode));
|
||||
|
||||
printf("Accessed on: %s", ctime(&st.st_atime));
|
||||
printf("Modified on: %s", ctime(&st.st_mtime));
|
||||
printf("Changed on: %s", ctime(&st.st_ctime));
|
||||
|
||||
endpwent();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
88
apps/src/su.c
Normal file
88
apps/src/su.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void run_program(char** argv)
|
||||
{
|
||||
execv(argv[0], argv);
|
||||
|
||||
perror("execv");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void strip_newline(char* str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
if (str[len - 1] == '\n') str[len - 1] = 0;
|
||||
}
|
||||
|
||||
static const char* collect_password()
|
||||
{
|
||||
static char buf[BUFSIZ];
|
||||
|
||||
printf("Password: ");
|
||||
fgets(buf, BUFSIZ, stdin);
|
||||
|
||||
strip_newline(buf);
|
||||
putchar('\n');
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* username;
|
||||
|
||||
if (argc == 1) username = "root";
|
||||
else
|
||||
username = argv[1];
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
fprintf(stderr, "%s must be setuid root", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct passwd* user = getpwnam(username);
|
||||
|
||||
endpwent();
|
||||
|
||||
if (!user)
|
||||
{
|
||||
if (errno) perror("getpwnam");
|
||||
else
|
||||
fprintf(stderr, "Unknown user %s\n", username);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (getuid() != geteuid()) // we were started from a non-root user
|
||||
{
|
||||
const char* pw = collect_password();
|
||||
if (strcmp(pw, user->pw_passwd) != 0)
|
||||
{
|
||||
fprintf(stderr, "Invalid password\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (setgid(user->pw_gid) < 0)
|
||||
{
|
||||
perror("setgid");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (setuid(user->pw_uid) < 0)
|
||||
{
|
||||
perror("setuid");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char* default_argv[] = {user->pw_shell, NULL};
|
||||
|
||||
if (argc < 3) run_program(default_argv);
|
||||
else
|
||||
run_program(argv + 2);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
FILE* syms = fopen("/sys/moon.sym", "r");
|
||||
if (!syms)
|
||||
{
|
||||
perror("fopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buf[1200];
|
||||
|
||||
if (fseek(syms, -1199, SEEK_END) < 0)
|
||||
{
|
||||
perror("fseek");
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t nread = fread(buf, 1199, 1, syms);
|
||||
if (ferror(syms))
|
||||
{
|
||||
perror("fread");
|
||||
return 1;
|
||||
}
|
||||
|
||||
buf[nread] = 0;
|
||||
|
||||
printf("%s\n", strchr(buf, '\n') + 1);
|
||||
|
||||
fclose(syms);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,27 +1,38 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define VALUE_SINGULAR_AT_ONE(v) v, v == 1 ? "" : "s"
|
||||
|
||||
int main()
|
||||
{
|
||||
FILE* fp = fopen("/dev/uptime", "r");
|
||||
if (!fp)
|
||||
struct timespec tp;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tp); // On Luna, CLOCK_MONOTONIC starts at boot.
|
||||
|
||||
struct tm* time = gmtime(
|
||||
&tp.tv_sec); // just splitting the value into seconds, minutes, hours, days... not the best way to do it but ok.
|
||||
time->tm_year -= 70;
|
||||
|
||||
if (time->tm_year)
|
||||
{
|
||||
perror("fopen");
|
||||
return 1;
|
||||
printf("up for %d year%s, %d day%s, %d hour%s, %d minute%s, %d second%s\n",
|
||||
VALUE_SINGULAR_AT_ONE(time->tm_year), VALUE_SINGULAR_AT_ONE(time->tm_yday),
|
||||
VALUE_SINGULAR_AT_ONE(time->tm_hour), VALUE_SINGULAR_AT_ONE(time->tm_min),
|
||||
VALUE_SINGULAR_AT_ONE(time->tm_sec));
|
||||
}
|
||||
|
||||
char buf[BUFSIZ];
|
||||
fgets(buf, sizeof(buf), fp);
|
||||
|
||||
if (ferror(fp))
|
||||
else if (time->tm_yday)
|
||||
{
|
||||
perror("fgets");
|
||||
return 1;
|
||||
printf("up for %d day%s, %d hour%s, %d minute%s, %d second%s\n", VALUE_SINGULAR_AT_ONE(time->tm_yday),
|
||||
VALUE_SINGULAR_AT_ONE(time->tm_hour), VALUE_SINGULAR_AT_ONE(time->tm_min),
|
||||
VALUE_SINGULAR_AT_ONE(time->tm_sec));
|
||||
}
|
||||
|
||||
long ms_uptime = atol(buf);
|
||||
|
||||
printf("up for %ld seconds\n", ms_uptime / 1000);
|
||||
|
||||
fclose(fp);
|
||||
else if (time->tm_hour)
|
||||
{
|
||||
printf("up for %d hour%s, %d minute%s, %d second%s\n", VALUE_SINGULAR_AT_ONE(time->tm_hour),
|
||||
VALUE_SINGULAR_AT_ONE(time->tm_min), VALUE_SINGULAR_AT_ONE(time->tm_sec));
|
||||
}
|
||||
else if (time->tm_min)
|
||||
printf("up for %d minute%s, %d second%s\n", VALUE_SINGULAR_AT_ONE(time->tm_min),
|
||||
VALUE_SINGULAR_AT_ONE(time->tm_sec));
|
||||
else
|
||||
printf("up for %d second%s\n", VALUE_SINGULAR_AT_ONE(time->tm_sec));
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
Welcome to Luna!
|
||||
|
||||
Tip of the day: Type 'help' to get started!
|
||||
Tip of the day: Log in as user 'selene' and password 'moon' :)
|
2
initrd/etc/passwd
Normal file
2
initrd/etc/passwd
Normal file
@ -0,0 +1,2 @@
|
||||
root:secure:0:0:Administrator:/:/bin/sh
|
||||
selene:moon:1:1:Default User:/:/bin/sh
|
@ -1,3 +1,3 @@
|
||||
screen=1024x768
|
||||
kernel=boot/moon.elf
|
||||
kernel=boot/moon
|
||||
verbose=1
|
@ -4,7 +4,7 @@ MOON_OBJ := $(MOON_DIR)/lib
|
||||
MOON_BIN := $(MOON_DIR)/bin
|
||||
|
||||
CFLAGS ?= -Os
|
||||
CFLAGS := ${CFLAGS} -pedantic -Wall -Wextra -Werror -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion -ffreestanding -fstack-protector-strong -fno-omit-frame-pointer -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fshort-wchar -mcmodel=kernel -I$(MOON_DIR)/include -isystem $(MOON_DIR)/include/std
|
||||
CFLAGS := ${CFLAGS} -pedantic -Wall -Wextra -Werror -Wvla -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion -ffreestanding -fstack-protector-strong -fno-omit-frame-pointer -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fshort-wchar -mcmodel=kernel -I$(MOON_DIR)/include -isystem $(MOON_DIR)/include/std
|
||||
CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy
|
||||
ASMFLAGS := -felf64
|
||||
LDFLAGS := -T$(MOON_DIR)/moon.ld -nostdlib -lgcc -Wl,--build-id=none -z max-page-size=0x1000 -mno-red-zone -mcmodel=kernel
|
||||
@ -14,7 +14,7 @@ CFLAGS := ${CFLAGS} -D_MOON_SUFFIX=-$(shell git rev-parse --short HEAD)
|
||||
endif
|
||||
|
||||
ifeq ($(MOON_BUILD_DEBUG), 1)
|
||||
CFLAGS := -ggdb ${CFLAGS}
|
||||
CFLAGS := -ggdb -fsanitize=undefined ${CFLAGS}
|
||||
endif
|
||||
|
||||
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
|
||||
@ -27,45 +27,59 @@ OBJS = $(patsubst $(MOON_SRC)/%.cpp, $(MOON_OBJ)/%.cpp.o, $(CXX_SRC))
|
||||
OBJS += $(patsubst $(MOON_SRC)/%.c, $(MOON_OBJ)/%.c.o, $(C_SRC))
|
||||
OBJS += $(patsubst $(MOON_SRC)/%.asm, $(MOON_OBJ)/%.asm.o, $(NASM_SRC))
|
||||
|
||||
default: $(MOON_BIN)/moon.elf
|
||||
default: $(MOON_BIN)/moon
|
||||
|
||||
$(MOON_OBJ)/main.cpp.o: $(MOON_SRC)/main.cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(CFLAGS) -fno-stack-protector $(CXXFLAGS) -o $@ -c $^
|
||||
@$(CXX) $(CFLAGS) -fno-stack-protector $(CXXFLAGS) -o $@ -c $^
|
||||
@echo " CXX $^"
|
||||
|
||||
$(MOON_OBJ)/misc/config.cpp.o: $(MOON_SRC)/misc/config.cpp FORCE
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $(MOON_SRC)/misc/config.cpp
|
||||
@$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $(MOON_SRC)/misc/config.cpp
|
||||
@echo " CXX $^"
|
||||
|
||||
$(MOON_OBJ)/init/Init.cpp.o: $(MOON_SRC)/init/Init.cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(CFLAGS) -fno-stack-protector $(CXXFLAGS) -o $@ -c $^
|
||||
@$(CXX) $(CFLAGS) -fno-stack-protector $(CXXFLAGS) -o $@ -c $^
|
||||
@echo " CXX $^"
|
||||
|
||||
$(MOON_OBJ)/%.cpp.o: $(MOON_SRC)/%.cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $^
|
||||
@$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $^
|
||||
@echo " CXX $^"
|
||||
|
||||
$(MOON_OBJ)/memory/liballoc/liballoc.c.o: $(MOON_SRC)/memory/liballoc/liballoc.c
|
||||
@mkdir -p $(@D)
|
||||
@$(CC) $(CFLAGS) -fno-sanitize=undefined -o $@ -c $^
|
||||
@echo " CC $^"
|
||||
|
||||
$(MOON_OBJ)/%.c.o: $(MOON_SRC)/%.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -o $@ -c $^
|
||||
@$(CC) $(CFLAGS) -o $@ -c $^
|
||||
@echo " CC $^"
|
||||
|
||||
$(MOON_OBJ)/%.asm.o: $(MOON_SRC)/%.asm
|
||||
@mkdir -p $(@D)
|
||||
$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@echo " ASM $^"
|
||||
|
||||
build: $(OBJS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(OBJS) $(LDFLAGS) -o $(MOON_BIN)/moon.elf
|
||||
@mkdir -p $(MOON_BIN)
|
||||
@$(CC) $(OBJS) $(LDFLAGS) -o $(MOON_BIN)/moon
|
||||
@echo " CCLD $(MOON_BIN)/moon"
|
||||
|
||||
clean:
|
||||
rm -rf $(MOON_OBJ)/*
|
||||
rm -rf $(MOON_BIN)/*
|
||||
|
||||
install: $(MOON_BIN)/moon.elf
|
||||
@mkdir -p $(@D)
|
||||
cp $^ $(LUNA_ROOT)/initrd/boot/moon.elf
|
||||
$(LUNA_ROOT)/tools/generate-symbols.sh
|
||||
$(STRIP) $(LUNA_ROOT)/initrd/boot/moon.elf
|
||||
install: $(MOON_BIN)/moon
|
||||
@mkdir -p $(LUNA_ROOT)/initrd/boot
|
||||
@cp $^ $(LUNA_ROOT)/initrd/boot/moon
|
||||
@echo " INSTALL $^"
|
||||
@$(LUNA_ROOT)/tools/generate-symbols.sh
|
||||
@$(STRIP) $(LUNA_ROOT)/initrd/boot/moon
|
||||
@echo " STRIP $(LUNA_ROOT)/initrd/boot/moon"
|
||||
|
||||
.PHONY: build clean install FORCE
|
||||
FORCE:
|
@ -10,4 +10,5 @@ namespace CPU
|
||||
uint64_t get_feature_bitmask();
|
||||
uint64_t get_initial_apic_id();
|
||||
bool has_feature(CPU::Features);
|
||||
bool has_nx();
|
||||
}
|
@ -32,6 +32,13 @@ struct Descriptor
|
||||
ssize_t read(size_t size, char* buffer);
|
||||
ssize_t write(size_t size, const char* buffer);
|
||||
|
||||
ssize_t user_read(size_t size, char* buffer);
|
||||
ssize_t user_write(size_t size, const char* buffer);
|
||||
|
||||
uintptr_t mmap(uintptr_t addr, size_t size, int prot, off_t offset);
|
||||
|
||||
long ioctl(int cmd, uintptr_t arg);
|
||||
|
||||
void open(VFS::Node* node, bool can_read, bool can_write, bool able_to_block, bool close_on_exec);
|
||||
|
||||
int seek(long offset);
|
||||
@ -55,6 +62,11 @@ struct Descriptor
|
||||
return m_close_on_exec;
|
||||
}
|
||||
|
||||
void set_close_on_exec(bool value)
|
||||
{
|
||||
m_close_on_exec = value;
|
||||
}
|
||||
|
||||
Descriptor(const Descriptor& other);
|
||||
Descriptor();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define TAR_MAGIC "ustar"
|
||||
#define TAR_BLOCKSIZE 512
|
||||
@ -33,6 +34,7 @@ namespace InitRD
|
||||
uint64_t size;
|
||||
uint64_t size_in_blocks;
|
||||
void* addr;
|
||||
mode_t mode;
|
||||
};
|
||||
|
||||
uint64_t get_total_blocks();
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef long ssize_t;
|
||||
|
||||
@ -19,24 +20,35 @@ namespace VFS
|
||||
typedef ssize_t (*node_read)(Node*, size_t, size_t, char*);
|
||||
typedef ssize_t (*node_write)(Node*, size_t, size_t, const char*);
|
||||
typedef Node* (*node_finddir)(Node*, const char*);
|
||||
typedef int (*node_mkdir)(Node*, const char*);
|
||||
typedef int (*node_mkdir)(Node*, const char*, mode_t);
|
||||
typedef int (*node_block)(Node*);
|
||||
typedef Node* (*node_readdir)(Node*, long);
|
||||
typedef uintptr_t (*node_mmap)(Node*, uintptr_t, size_t, int, off_t);
|
||||
typedef long (*node_ioctl)(Node*, int, uintptr_t);
|
||||
|
||||
struct Node
|
||||
{
|
||||
char name[NAME_MAX];
|
||||
uint64_t inode;
|
||||
uint64_t length;
|
||||
int type;
|
||||
int flags;
|
||||
int tty = 0;
|
||||
int uid;
|
||||
int gid;
|
||||
mode_t mode;
|
||||
uint64_t impl;
|
||||
uint64_t atime;
|
||||
uint64_t ctime;
|
||||
uint64_t mtime;
|
||||
uint64_t inode;
|
||||
uint64_t length;
|
||||
node_read read_func;
|
||||
node_finddir find_func;
|
||||
node_readdir readdir_func;
|
||||
node_mkdir mkdir_func;
|
||||
node_write write_func;
|
||||
node_block block_func;
|
||||
int tty = 0;
|
||||
node_mmap mmap_func;
|
||||
node_ioctl ioctl_func;
|
||||
Node* link;
|
||||
};
|
||||
|
||||
@ -45,6 +57,9 @@ namespace VFS
|
||||
int mkdir(const char* path, const char* name);
|
||||
int mkdir(const char* pathname);
|
||||
|
||||
int do_mkdir(const char* path, const char* name, int uid, int gid, mode_t mode);
|
||||
int do_mkdir(const char* pathname, int uid, int gid, mode_t mode);
|
||||
|
||||
int would_block(Node* node);
|
||||
|
||||
void mount_root(Node* root);
|
||||
@ -61,4 +76,11 @@ namespace VFS
|
||||
Node* root();
|
||||
|
||||
Node* readdir(Node* dir, long offset);
|
||||
|
||||
bool can_execute(Node* node, int uid, int gid);
|
||||
bool can_read(Node* node, int uid, int gid);
|
||||
bool can_write(Node* node, int uid, int gid);
|
||||
|
||||
bool is_setuid(Node* node);
|
||||
bool is_setgid(Node* node);
|
||||
}
|
@ -6,4 +6,10 @@ namespace ConsoleDevice
|
||||
VFS::Node* create_new(const char* devname);
|
||||
|
||||
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
|
||||
|
||||
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||
|
||||
int would_block(VFS::Node* node);
|
||||
|
||||
void append(char c);
|
||||
}
|
13
kernel/include/fs/devices/Framebuffer.h
Normal file
13
kernel/include/fs/devices/Framebuffer.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "fs/VFS.h"
|
||||
|
||||
namespace FramebufferDevice
|
||||
{
|
||||
VFS::Node* create_new(const char* devname);
|
||||
|
||||
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
|
||||
|
||||
uintptr_t mmap(VFS::Node* node, uintptr_t addr, size_t size, int prot, off_t offset);
|
||||
|
||||
long ioctl(VFS::Node* node, int cmd, uintptr_t arg);
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#include "fs/VFS.h"
|
||||
|
||||
namespace UptimeDevice
|
||||
namespace NullDevice
|
||||
{
|
||||
VFS::Node* create_new(const char* devname);
|
||||
|
||||
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
|
||||
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||
}
|
@ -8,11 +8,14 @@
|
||||
#define MAP_READ_WRITE 1 << 0
|
||||
#define MAP_USER 1 << 1
|
||||
#define MAP_EXEC 1 << 2
|
||||
#define MAP_AS_OWNED_BY_TASK 1 << 3
|
||||
|
||||
namespace MemoryManager
|
||||
{
|
||||
void init();
|
||||
|
||||
void protect_kernel_sections();
|
||||
|
||||
void* get_mapping(void* physicalAddress, int flags = MAP_READ_WRITE);
|
||||
void release_mapping(void* mapping);
|
||||
|
||||
@ -31,4 +34,7 @@ namespace MemoryManager
|
||||
void release_pages(void* pages, uint64_t count);
|
||||
|
||||
void protect(void* page, uint64_t count, int flags);
|
||||
|
||||
void map_several_pages(uint64_t physicalAddress, uint64_t virtualAddress, uint64_t count,
|
||||
int flags = MAP_READ_WRITE);
|
||||
}
|
@ -17,13 +17,18 @@ struct PageDirectoryEntry
|
||||
bool larger_pages : 1;
|
||||
bool ignore1 : 1;
|
||||
uint8_t available : 3;
|
||||
uint64_t address : 52;
|
||||
uint64_t address : 48;
|
||||
bool owned_by_task : 1; // Part of the available for OS use bits.
|
||||
uint8_t available2 : 2;
|
||||
bool no_execute : 1;
|
||||
|
||||
void set_address(uint64_t addr);
|
||||
uint64_t get_address();
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PageTable
|
||||
{
|
||||
PageDirectoryEntry entries[512];
|
||||
} __attribute__((aligned(PAGE_SIZE)));
|
||||
|
||||
static_assert(sizeof(PageDirectoryEntry) == 8UL);
|
@ -6,7 +6,8 @@ enum Flags
|
||||
{
|
||||
ReadWrite = 1 << 0,
|
||||
User = 1 << 1,
|
||||
Execute = 1 << 2
|
||||
Execute = 1 << 2,
|
||||
OwnedByTask = 1 << 3,
|
||||
};
|
||||
namespace VMM
|
||||
{
|
||||
@ -33,9 +34,11 @@ namespace VMM
|
||||
PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr);
|
||||
|
||||
void propagate_read_write(PageTable* root, uint64_t vaddr);
|
||||
void propagate_no_execute(PageTable* root, uint64_t vaddr);
|
||||
void propagate_user(PageTable* root, uint64_t vaddr);
|
||||
|
||||
void flush_tlb(uint64_t addr);
|
||||
void flush_tlb_full();
|
||||
|
||||
void decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
|
||||
uint64_t& pdp_index);
|
||||
|
@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
#include "panic/Panic.h"
|
||||
|
||||
#define ASSERT(expr) (bool)(expr) || panic("Assertion failed: " #expr)
|
||||
|
||||
#define TODO(message) panic("TODO: " message)
|
9
kernel/include/std/ensure.h
Normal file
9
kernel/include/std/ensure.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "panic/Panic.h"
|
||||
|
||||
#define STRINGIZE(x) #x
|
||||
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
|
||||
|
||||
// clang-format off
|
||||
#define ensure(expr) (bool)(expr) || panic("Check failed at " __FILE__ ", line " STRINGIZE_VALUE_OF(__LINE__) ": " #expr)
|
||||
// clang-format on
|
@ -4,10 +4,13 @@
|
||||
#define ENOENT 2
|
||||
#define ESRCH 3
|
||||
#define EINTR 4
|
||||
#define E2BIG 7
|
||||
#define ENOEXEC 8
|
||||
#define EBADF 9
|
||||
#define ECHILD 10
|
||||
#define EAGAIN 11
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EFAULT 14
|
||||
#define EEXIST 17
|
||||
#define ENOTDIR 20
|
||||
@ -16,5 +19,6 @@
|
||||
#define EMFILE 24
|
||||
#define ENOTTY 25
|
||||
#define ENOSPC 28
|
||||
#define ERANGE 36
|
||||
#define ENOSYS 38
|
||||
#define ENOTSUP 95
|
@ -1,34 +1,43 @@
|
||||
#pragma once
|
||||
#include "interrupts/Context.h"
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define SYS_exit 0
|
||||
#define SYS_yield 1
|
||||
#define SYS_sleep 2
|
||||
#define SYS_write 3
|
||||
#define SYS_paint 4
|
||||
#define SYS_getprocid 5
|
||||
#define SYS_mmap 6
|
||||
#define SYS_munmap 7
|
||||
#define SYS_open 8
|
||||
#define SYS_read 9
|
||||
#define SYS_close 10
|
||||
#define SYS_seek 11
|
||||
#define SYS_exec 12
|
||||
#define SYS_fcntl 13
|
||||
#define SYS_mprotect 14
|
||||
#define SYS_clock 15
|
||||
#define SYS_mkdir 16
|
||||
#define SYS_fork 17
|
||||
#define SYS_waitpid 18
|
||||
#define SYS_access 19
|
||||
#define SYS_fstat 20
|
||||
#define SYS_pstat 21
|
||||
#define SYS_getdents 22
|
||||
#define SYS_getprocid 4
|
||||
#define SYS_mmap 5
|
||||
#define SYS_munmap 6
|
||||
#define SYS_open 7
|
||||
#define SYS_read 8
|
||||
#define SYS_close 9
|
||||
#define SYS_seek 10
|
||||
#define SYS_execv 11
|
||||
#define SYS_fcntl 12
|
||||
#define SYS_mprotect 13
|
||||
#define SYS_clock_gettime 14
|
||||
#define SYS_mkdir 15
|
||||
#define SYS_fork 16
|
||||
#define SYS_waitpid 17
|
||||
#define SYS_access 18
|
||||
#define SYS_fstat 19
|
||||
#define SYS_pstat 20
|
||||
#define SYS_getdents 21
|
||||
#define SYS_stat 22
|
||||
#define SYS_dup2 23
|
||||
#define SYS_setuid 24
|
||||
#define SYS_setgid 25
|
||||
#define SYS_umask 26
|
||||
#define SYS_ioctl 27
|
||||
#define SYS_seteuid 28
|
||||
#define SYS_setegid 29
|
||||
|
||||
struct stat;
|
||||
struct pstat;
|
||||
struct luna_dirent;
|
||||
struct timespec;
|
||||
|
||||
namespace Syscall
|
||||
{
|
||||
@ -39,22 +48,29 @@ void sys_exit(Context* context, int status);
|
||||
void sys_yield(Context* context);
|
||||
void sys_sleep(Context* context, uint64_t ms);
|
||||
void sys_write(Context* context, int fd, size_t size, const char* addr);
|
||||
void sys_paint(Context* context, uint64_t x, uint64_t y, uint64_t w, uint64_t h, uint64_t col);
|
||||
void sys_getprocid(Context* context, int field);
|
||||
void sys_mmap(Context* context, void* address, size_t size, int prot);
|
||||
void sys_mmap(Context* context, void* address, size_t size, int prot, int fd, off_t offset);
|
||||
void sys_munmap(Context* context, void* address, size_t size);
|
||||
void sys_open(Context* context, const char* filename, int flags);
|
||||
void sys_open(Context* context, const char* filename, int flags, mode_t mode);
|
||||
void sys_read(Context* context, int fd, size_t size, char* buffer);
|
||||
void sys_close(Context* context, int fd);
|
||||
void sys_seek(Context* context, int fd, long offset, int whence);
|
||||
void sys_exec(Context* context, const char* pathname);
|
||||
void sys_execv(Context* context, const char* pathname, char** argv);
|
||||
void sys_fcntl(Context* context, int fd, int command, uintptr_t arg);
|
||||
void sys_mprotect(Context* context, void* address, size_t size, int prot);
|
||||
void sys_clock(Context* context);
|
||||
void sys_mkdir(Context* context, const char* filename);
|
||||
void sys_clock_gettime(Context* context, int clock_id, struct timespec* tp);
|
||||
void sys_mkdir(Context* context, const char* filename, mode_t mode);
|
||||
void sys_fork(Context* context);
|
||||
void sys_waitpid(Context* context, long pid, int* wstatus, int options);
|
||||
void sys_access(Context* context, const char* path, int amode);
|
||||
void sys_fstat(Context* context, int fd, struct stat* buf);
|
||||
void sys_pstat(Context* context, long pid, struct pstat* buf);
|
||||
void sys_getdents(Context* context, int fd, struct luna_dirent* buf, size_t count);
|
||||
void sys_stat(Context* context, const char* path, struct stat* buf);
|
||||
void sys_dup2(Context* context, int fd, int fd2);
|
||||
void sys_setuid(Context* context, uid_t uid);
|
||||
void sys_setgid(Context* context, gid_t gid);
|
||||
void sys_umask(Context* context, mode_t cmask);
|
||||
void sys_ioctl(Context* context, int fd, int request, uintptr_t arg);
|
||||
void sys_seteuid(Context* context, uid_t euid);
|
||||
void sys_setegid(Context* context, gid_t egid);
|
@ -8,8 +8,16 @@
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "misc/utils.h"
|
||||
#include <stddef.h>
|
||||
|
||||
char* strdup_from_user(const char* user_string);
|
||||
bool validate_user_readable_page(uintptr_t address);
|
||||
bool validate_user_writable_page(uintptr_t address);
|
||||
bool validate_user_read(uintptr_t address, size_t size);
|
||||
bool validate_user_write(uintptr_t address, size_t size);
|
||||
|
||||
bool copy_from_user(const void* user_ptr, void* ptr, size_t size);
|
||||
bool copy_to_user(void* user_ptr, const void* ptr, size_t size);
|
||||
|
||||
// FIXME: Map the physical addresses into kernel address space. Right now, something overwrites KernelHeap and crashes
|
||||
// it, so that's not really possible. But it should be done in the future.
|
||||
|
@ -4,9 +4,20 @@
|
||||
#include "memory/AddressSpace.h"
|
||||
#include "memory/UserHeap.h"
|
||||
#include "sys/elf/Image.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
#define TASK_MAX_FDS 32
|
||||
|
||||
enum class BlockReason
|
||||
{
|
||||
None,
|
||||
Reading,
|
||||
Waiting,
|
||||
};
|
||||
|
||||
// FIXME: To make this struct more C++-styled, maybe we could make a lot of these variables private and add
|
||||
// getters/setters?
|
||||
|
||||
struct Task
|
||||
{
|
||||
enum TaskState
|
||||
@ -29,6 +40,11 @@ struct Task
|
||||
|
||||
int64_t task_time = 0;
|
||||
|
||||
uid_t uid;
|
||||
uid_t euid;
|
||||
gid_t gid;
|
||||
gid_t egid;
|
||||
|
||||
Task* next_task = nullptr;
|
||||
Task* prev_task = nullptr;
|
||||
|
||||
@ -45,7 +61,8 @@ struct Task
|
||||
|
||||
bool is_user_task();
|
||||
|
||||
ELFImage* image = nullptr;
|
||||
ELFImage* image = nullptr; // FIXME: we probably don't need to keep track of this anymore since the ELF sections are
|
||||
// freed automatically when calling destroy() or clear() on the address space.
|
||||
|
||||
Descriptor files[TASK_MAX_FDS];
|
||||
|
||||
@ -69,14 +86,38 @@ struct Task
|
||||
|
||||
char name[128];
|
||||
|
||||
struct
|
||||
{
|
||||
size_t size;
|
||||
int fd;
|
||||
char* buf;
|
||||
} blocking_read_info;
|
||||
mode_t umask;
|
||||
|
||||
void resume_read();
|
||||
BlockReason block_reason;
|
||||
|
||||
union {
|
||||
struct
|
||||
{
|
||||
size_t size;
|
||||
int fd;
|
||||
char* buf;
|
||||
} blocking_read_info;
|
||||
struct
|
||||
{
|
||||
int64_t pid;
|
||||
int* wstatus;
|
||||
} blocking_wait_info;
|
||||
};
|
||||
|
||||
void resume();
|
||||
|
||||
bool is_still_blocking();
|
||||
|
||||
// FIXME: These two functions are a bit clunky.
|
||||
Descriptor* open_descriptor_from_fd(int fd, int& error);
|
||||
Descriptor* descriptor_from_fd(int fd, int& error);
|
||||
|
||||
bool is_superuser();
|
||||
|
||||
private:
|
||||
void resume_read();
|
||||
void resume_wait();
|
||||
|
||||
bool is_read_still_blocking();
|
||||
bool is_wait_still_blocking();
|
||||
};
|
4
kernel/include/utils/PageFaultReason.h
Normal file
4
kernel/include/utils/PageFaultReason.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
void determine_user_page_fault_reason(uintptr_t faulting_address);
|
4
kernel/include/utils/StringParsing.h
Normal file
4
kernel/include/utils/StringParsing.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
long parse_decimal(const char* str);
|
||||
long parse_octal(const char* str);
|
8
kernel/include/utils/Time.h
Normal file
8
kernel/include/utils/Time.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
int make_yday(int year, int month);
|
||||
|
||||
uint64_t broken_down_to_unix(uint64_t year, uint64_t yday, uint64_t hour, uint64_t min, uint64_t sec);
|
||||
|
||||
uint64_t unix_boottime(uint8_t boottime[8]);
|
@ -16,14 +16,19 @@ SECTIONS
|
||||
kernel_start = .;
|
||||
.text : {
|
||||
KEEP(*(.text.boot)) *(.text .text.*) /* code */
|
||||
. = ALIGN(0x1000);
|
||||
start_of_kernel_rodata = .;
|
||||
*(.rodata .rodata.*) /* data */
|
||||
end_of_kernel_rodata = .;
|
||||
. = ALIGN(0x1000);
|
||||
start_of_kernel_data = .;
|
||||
*(.data .data.*)
|
||||
} :boot
|
||||
.bss (NOLOAD) : { /* bss */
|
||||
. = ALIGN(16);
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :boot
|
||||
end_of_kernel_data = .;
|
||||
kernel_end = .;
|
||||
|
||||
/DISCARD/ : { *(.eh_frame) *(.comment) }
|
||||
|
@ -66,9 +66,17 @@ uint64_t CPU::get_feature_bitmask()
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
bool CPU::has_nx()
|
||||
{
|
||||
unsigned int unused;
|
||||
unsigned int edx;
|
||||
__get_cpuid(0x80000001, &unused, &unused, &unused, &edx);
|
||||
return edx & (1 << 20);
|
||||
}
|
||||
|
||||
static bool _has_feature(int feature)
|
||||
{
|
||||
return (CPU::get_feature_bitmask() & (uint64_t)(1 << feature)) > 0;
|
||||
return (CPU::get_feature_bitmask() & (uint64_t)(1UL << feature)) > 0;
|
||||
}
|
||||
|
||||
bool CPU::has_feature(CPU::Features feature)
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "fs/FileDescriptor.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "sys/UserMemory.h"
|
||||
|
||||
Descriptor::Descriptor() : m_is_open(false)
|
||||
{
|
||||
@ -37,6 +39,42 @@ ssize_t Descriptor::write(size_t size, const char* buffer)
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t Descriptor::user_read(size_t size, char* buffer)
|
||||
{
|
||||
char* buf = (char*)kmalloc(size);
|
||||
if (!buf) return -ENOMEM;
|
||||
ssize_t result = read(size, buf);
|
||||
if (!copy_to_user(buffer, buf, size)) result = -EFAULT;
|
||||
kfree(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t Descriptor::user_write(size_t size, const char* buffer)
|
||||
{
|
||||
char* buf = (char*)kmalloc(size);
|
||||
if (!buf) return -ENOMEM;
|
||||
ssize_t result;
|
||||
if (!copy_from_user(buffer, buf, size)) result = -EFAULT;
|
||||
else
|
||||
result = write(size, buf);
|
||||
kfree(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
|
||||
|
||||
uintptr_t Descriptor::mmap(uintptr_t addr, size_t size, int prot, off_t offset)
|
||||
{
|
||||
if (!m_node->mmap_func) return MAP_FAIL(ENOTSUP);
|
||||
return m_node->mmap_func(m_node, addr, size, prot, offset);
|
||||
}
|
||||
|
||||
long Descriptor::ioctl(int cmd, uintptr_t arg)
|
||||
{
|
||||
if (!m_node->ioctl_func) return MAP_FAIL(ENOTSUP);
|
||||
return m_node->ioctl_func(m_node, cmd, arg);
|
||||
}
|
||||
|
||||
int Descriptor::seek(long offset)
|
||||
{
|
||||
if (m_node->type == VFS_FILE && (uint64_t)offset > m_node->length)
|
||||
|
@ -1,6 +1,6 @@
|
||||
#define MODULE "initrd"
|
||||
|
||||
#include "init/InitRD.h"
|
||||
#include "fs/InitRD.h"
|
||||
#include "bootboot.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "io/Serial.h"
|
||||
@ -10,6 +10,9 @@
|
||||
#include "std/errno.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
#include "utils/StringParsing.h"
|
||||
|
||||
// FIXME: A lot of this code was written before the VFS was created and thus is quite messy and assumes other stuff.
|
||||
|
||||
extern BOOTBOOT bootboot;
|
||||
|
||||
@ -18,6 +21,9 @@ static bool initrd_initialized = false;
|
||||
|
||||
static VFS::Node initrd_root;
|
||||
|
||||
extern uint64_t clock_boot(); // defined in sys/clock.cpp
|
||||
extern uint64_t clock_now();
|
||||
|
||||
bool InitRD::is_initialized()
|
||||
{
|
||||
return initrd_initialized;
|
||||
@ -51,17 +57,14 @@ uint64_t InitRD::get_file_physical_address(InitRD::File& file)
|
||||
InitRD::File InitRD::get_file(TarHeader* header)
|
||||
{
|
||||
File result;
|
||||
result.size = 0;
|
||||
char null_terminated_size[13];
|
||||
memcpy(null_terminated_size, header->size, 12);
|
||||
null_terminated_size[12] = 0;
|
||||
result.size = parse_octal(null_terminated_size);
|
||||
memcpy(result.name, header->name, 100);
|
||||
int multiplier =
|
||||
1; // why they decided to store the size as an octal-encoded string instead of an integer is beyond me
|
||||
for (int i = 10; i >= 0; i--)
|
||||
{
|
||||
result.size += (multiplier * (header->size[i] - 48));
|
||||
multiplier *= 8;
|
||||
}
|
||||
result.addr = (void*)((uint64_t)header + TAR_BLOCKSIZE);
|
||||
result.size_in_blocks = Utilities::get_blocks_from_size(TAR_BLOCKSIZE, result.size);
|
||||
result.mode = (mode_t)parse_octal(header->mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -116,7 +119,7 @@ void InitRD::for_each(void (*callback)(File& f))
|
||||
}
|
||||
}
|
||||
|
||||
#define INITRD_MAX_FILES_IN_DIR 16
|
||||
#define INITRD_MAX_FILES_IN_DIR 32
|
||||
#define INITRD_MAX_FILES 64
|
||||
|
||||
namespace InitRD
|
||||
@ -195,7 +198,7 @@ VFS::Node* initrd_read_dir(VFS::Node* node, long offset)
|
||||
return dir.files[offset];
|
||||
}
|
||||
|
||||
int initrd_mkdir(VFS::Node* node, const char* name) // FIXME: Return proper error numbers.
|
||||
int initrd_mkdir(VFS::Node* node, const char* name, mode_t mode) // FIXME: Return proper error numbers.
|
||||
{
|
||||
if (total_dirs >= 32)
|
||||
{
|
||||
@ -225,6 +228,9 @@ int initrd_mkdir(VFS::Node* node, const char* name) // FIXME: Return proper erro
|
||||
new_node.mkdir_func = initrd_mkdir;
|
||||
new_node.length = 0;
|
||||
new_node.type = VFS_DIRECTORY;
|
||||
new_node.mode = mode;
|
||||
new_node.uid = new_node.gid = 0;
|
||||
new_node.atime = new_node.ctime = new_node.mtime = clock_now();
|
||||
strncpy(new_node.name, name, sizeof(new_node.name));
|
||||
InitRD::Directory dir;
|
||||
strncpy(dir.name, name, sizeof(dir.name));
|
||||
@ -282,6 +288,9 @@ static bool initrd_register_dir(InitRD::Directory& dir, uint64_t inode)
|
||||
node.mkdir_func = initrd_mkdir;
|
||||
node.readdir_func = initrd_read_dir;
|
||||
node.length = 0;
|
||||
node.mode = 0755;
|
||||
node.uid = node.gid = 0;
|
||||
node.atime = node.ctime = node.mtime = clock_boot();
|
||||
strncpy(node.name, buffer, sizeof(node.name));
|
||||
strncpy(dir.name, buffer, sizeof(dir.name));
|
||||
|
||||
@ -341,6 +350,9 @@ static bool initrd_register_file(InitRD::File& f, uint64_t inode)
|
||||
node.read_func = initrd_read;
|
||||
node.length = f.size;
|
||||
node.type = VFS_FILE;
|
||||
node.mode = f.mode & 07555; // don't allow writing
|
||||
node.uid = node.gid = 0;
|
||||
node.atime = node.ctime = node.mtime = clock_boot();
|
||||
strncpy(node.name, buffer, sizeof(node.name));
|
||||
strncpy(f.name, buffer, sizeof(f.name));
|
||||
|
||||
@ -359,7 +371,7 @@ static bool initrd_register_file(InitRD::File& f, uint64_t inode)
|
||||
static void initrd_scan()
|
||||
{
|
||||
initrd_for_each_dir([](InitRD::Directory& dir) {
|
||||
if (total_dirs >= 32)
|
||||
if (total_dirs >= INITRD_MAX_FILES)
|
||||
{
|
||||
kwarnln("Failed to register directory %s: Too many directories in initrd", dir.name);
|
||||
return;
|
||||
@ -368,7 +380,7 @@ static void initrd_scan()
|
||||
if (initrd_register_dir(dir, inode)) dirs[total_dirs++] = dir;
|
||||
});
|
||||
InitRD::for_each([](InitRD::File& f) {
|
||||
if (total_files >= 32)
|
||||
if (total_files >= INITRD_MAX_FILES)
|
||||
{
|
||||
kwarnln("Failed to register file %s: Too many files in initrd", f.name);
|
||||
return;
|
||||
@ -383,6 +395,9 @@ static void initrd_initialize_root()
|
||||
initrd_root.length = 0;
|
||||
initrd_root.inode = 0;
|
||||
initrd_root.type |= VFS_DIRECTORY;
|
||||
initrd_root.mode = 0755;
|
||||
initrd_root.uid = initrd_root.gid = 0;
|
||||
initrd_root.atime = initrd_root.ctime = initrd_root.mtime = clock_boot();
|
||||
InitRD::Directory& root = dirs[0];
|
||||
total_dirs++;
|
||||
strncpy(initrd_root.name, "initrd", sizeof(initrd_root.name));
|
@ -152,12 +152,58 @@ int VFS::mkdir(const char* path, const char* name)
|
||||
kwarnln("Chosen node does not support finddir()");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!strncmp(name, ".", strlen(name)) || !strncmp(name, "..", strlen(name)))
|
||||
{
|
||||
kwarnln("Attempted to mkdir . or .., which already exist");
|
||||
return -EEXIST;
|
||||
}
|
||||
if (node->find_func(node, name) != nullptr)
|
||||
{
|
||||
kwarnln("Already exists");
|
||||
return -EEXIST;
|
||||
}
|
||||
return node->mkdir_func(node, name);
|
||||
return node->mkdir_func(node, name, 0755);
|
||||
}
|
||||
|
||||
int VFS::do_mkdir(const char* path, const char* name, int uid, int gid, mode_t mode)
|
||||
{
|
||||
Node* node = resolve_path(path, vfs_root);
|
||||
if (!node)
|
||||
{
|
||||
kwarnln("Attempting to mkdir in %s, which does not exist", path);
|
||||
return -ENOENT;
|
||||
}
|
||||
if (node->type != VFS_DIRECTORY)
|
||||
{
|
||||
kwarnln("Attempting to mkdir in %s, which is not a directory!!", path);
|
||||
return -ENOTDIR;
|
||||
}
|
||||
if (!node->mkdir_func)
|
||||
{
|
||||
kwarnln("Chosen node does not support mkdir()");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!node->find_func)
|
||||
{
|
||||
kwarnln("Chosen node does not support finddir()");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!strncmp(name, ".", strlen(name)) || !strncmp(name, "..", strlen(name)))
|
||||
{
|
||||
kwarnln("Attempted to mkdir . or .., which already exist");
|
||||
return -EEXIST;
|
||||
}
|
||||
if (node->find_func(node, name) != nullptr)
|
||||
{
|
||||
kwarnln("Already exists");
|
||||
return -EEXIST;
|
||||
}
|
||||
if (!can_write(node, uid, gid))
|
||||
{
|
||||
kwarnln("Not enough permissions");
|
||||
return -EACCES;
|
||||
}
|
||||
return node->mkdir_func(node, name, mode);
|
||||
}
|
||||
|
||||
int VFS::mkdir(const char* pathname)
|
||||
@ -178,6 +224,24 @@ int VFS::mkdir(const char* pathname)
|
||||
return result;
|
||||
}
|
||||
|
||||
int VFS::do_mkdir(const char* pathname, int uid, int gid, mode_t mode)
|
||||
{
|
||||
char* bstr = strdup(pathname);
|
||||
char* dstr = strdup(pathname);
|
||||
|
||||
char* base = basename(bstr);
|
||||
char* dir = dirname(dstr);
|
||||
|
||||
kdbgln("mkdir(): creating %s in directory %s", base, dir);
|
||||
|
||||
int result = do_mkdir(dir, base, uid, gid, mode);
|
||||
|
||||
kfree(bstr);
|
||||
kfree(dstr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VFS::exists(const char* pathname)
|
||||
{
|
||||
return resolve_path(pathname) != nullptr;
|
||||
@ -209,3 +273,34 @@ VFS::Node* VFS::readdir(VFS::Node* dir, long offset)
|
||||
if (!dir->readdir_func) return 0;
|
||||
return dir->readdir_func(dir, offset);
|
||||
}
|
||||
|
||||
bool VFS::can_execute(VFS::Node* node, int uid, int gid)
|
||||
{
|
||||
if (uid == node->uid) return node->mode & 0100;
|
||||
if (gid == node->gid) return node->mode & 0010;
|
||||
return node->mode & 0001;
|
||||
}
|
||||
|
||||
bool VFS::can_write(VFS::Node* node, int uid, int gid)
|
||||
{
|
||||
if (uid == node->uid) return node->mode & 0200;
|
||||
if (gid == node->gid) return node->mode & 0020;
|
||||
return node->mode & 0002;
|
||||
}
|
||||
|
||||
bool VFS::can_read(VFS::Node* node, int uid, int gid)
|
||||
{
|
||||
if (uid == node->uid) return node->mode & 0400;
|
||||
if (gid == node->gid) return node->mode & 0040;
|
||||
return node->mode & 0004;
|
||||
}
|
||||
|
||||
bool VFS::is_setuid(VFS::Node* node)
|
||||
{
|
||||
return node->mode & 04000;
|
||||
}
|
||||
|
||||
bool VFS::is_setgid(VFS::Node* node)
|
||||
{
|
||||
return node->mode & 02000;
|
||||
}
|
@ -5,15 +5,30 @@
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
char* conin_buffer = nullptr;
|
||||
uint64_t conin_bufsize = 0;
|
||||
|
||||
int ConsoleDevice::would_block(VFS::Node*)
|
||||
{
|
||||
return conin_bufsize == 0;
|
||||
}
|
||||
|
||||
extern uint64_t clock_now();
|
||||
|
||||
VFS::Node* ConsoleDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
dev->write_func = ConsoleDevice::write;
|
||||
dev->read_func = ConsoleDevice::read;
|
||||
dev->block_func = ConsoleDevice::would_block;
|
||||
dev->inode = 0;
|
||||
dev->length = 0;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
dev->tty = 1;
|
||||
dev->uid = dev->gid = 0;
|
||||
dev->mode = 0666;
|
||||
dev->atime = dev->ctime = dev->mtime = clock_now();
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
@ -24,3 +39,23 @@ ssize_t ConsoleDevice::write(VFS::Node* node, size_t, size_t size, const char* b
|
||||
TextRenderer::write(buffer, size);
|
||||
return (ssize_t)size;
|
||||
}
|
||||
|
||||
ssize_t ConsoleDevice::read(VFS::Node* node, size_t, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if (!conin_buffer) return 0;
|
||||
if (size > conin_bufsize) size = conin_bufsize;
|
||||
memcpy(buffer, conin_buffer, size);
|
||||
memmove(conin_buffer, conin_buffer + size, conin_bufsize - size);
|
||||
conin_bufsize -= size;
|
||||
conin_buffer = (char*)krealloc(conin_buffer, conin_bufsize);
|
||||
return (ssize_t)size;
|
||||
}
|
||||
|
||||
void ConsoleDevice::append(char c)
|
||||
{
|
||||
conin_bufsize++;
|
||||
conin_buffer = (char*)krealloc(
|
||||
conin_buffer, conin_bufsize); // FIXME: We should probably not be calling realloc every time a key is pressed.
|
||||
conin_buffer[conin_bufsize - 1] = c;
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
#include "fs/devices/DeviceFS.h"
|
||||
#include "fs/devices/Console.h"
|
||||
#include "fs/devices/Framebuffer.h"
|
||||
#include "fs/devices/Keyboard.h"
|
||||
#include "fs/devices/NullDevice.h"
|
||||
#include "fs/devices/Random.h"
|
||||
#include "fs/devices/Serial.h"
|
||||
#include "fs/devices/Uptime.h"
|
||||
#include "fs/devices/Version.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
@ -15,6 +16,8 @@ VFS::Node* devfs_root = nullptr;
|
||||
VFS::Node* devfs_files[DEVFS_MAX_FILES];
|
||||
int devfs_file_count = 0;
|
||||
|
||||
extern uint64_t clock_boot();
|
||||
|
||||
VFS::Node* DeviceFS::get()
|
||||
{
|
||||
if (devfs_root) return devfs_root;
|
||||
@ -24,6 +27,9 @@ VFS::Node* DeviceFS::get()
|
||||
devfs_root->type = VFS_DIRECTORY;
|
||||
devfs_root->find_func = DeviceFS::finddir;
|
||||
devfs_root->readdir_func = DeviceFS::readdir;
|
||||
devfs_root->mode = 0755;
|
||||
devfs_root->uid = devfs_root->gid = 0;
|
||||
devfs_root->atime = devfs_root->ctime = devfs_root->mtime = clock_boot();
|
||||
strncpy(devfs_root->name, "dev", sizeof(devfs_root->name));
|
||||
|
||||
devfs_files[devfs_file_count++] = VersionDevice::create_new("version");
|
||||
@ -31,7 +37,8 @@ VFS::Node* DeviceFS::get()
|
||||
devfs_files[devfs_file_count++] = SerialDevice::create_new("serial");
|
||||
devfs_files[devfs_file_count++] = RandomDevice::create_new("random");
|
||||
devfs_files[devfs_file_count++] = KeyboardDevice::create_new("kbd");
|
||||
devfs_files[devfs_file_count++] = UptimeDevice::create_new("uptime");
|
||||
devfs_files[devfs_file_count++] = NullDevice::create_new("null");
|
||||
devfs_files[devfs_file_count++] = FramebufferDevice::create_new("fb0");
|
||||
devfs_root->length = devfs_file_count;
|
||||
return devfs_root;
|
||||
}
|
||||
|
71
kernel/src/fs/devices/Framebuffer.cpp
Normal file
71
kernel/src/fs/devices/Framebuffer.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "fs/devices/Framebuffer.h"
|
||||
#include "bootboot.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "misc/utils.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/stdio.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
#include "utils/Addresses.h"
|
||||
|
||||
extern BOOTBOOT bootboot;
|
||||
extern char fb[1];
|
||||
|
||||
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
|
||||
|
||||
extern uint64_t clock_now();
|
||||
|
||||
VFS::Node* FramebufferDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
dev->write_func = FramebufferDevice::write;
|
||||
dev->mmap_func = FramebufferDevice::mmap;
|
||||
dev->ioctl_func = FramebufferDevice::ioctl;
|
||||
dev->inode = 0;
|
||||
dev->length = 0;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
dev->uid = dev->gid = 0;
|
||||
dev->mode = 0222;
|
||||
dev->atime = dev->ctime = dev->mtime = clock_now();
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
|
||||
uintptr_t FramebufferDevice::mmap(VFS::Node* node, uintptr_t addr, size_t size, int prot, off_t offset)
|
||||
{
|
||||
if (!node) return -1;
|
||||
int real_prot = prot & ~(MAP_AS_OWNED_BY_TASK);
|
||||
if (round_down_to_nearest_page(offset) != (uintptr_t)offset) { return MAP_FAIL(EINVAL); }
|
||||
if ((size + offset) > bootboot.fb_size)
|
||||
{
|
||||
return MAP_FAIL(ERANGE); // FIXME: Should probably be EOVERFLOW.
|
||||
}
|
||||
MemoryManager::map_several_pages(bootboot.fb_ptr + offset, addr, Utilities::get_blocks_from_size(PAGE_SIZE, size),
|
||||
real_prot);
|
||||
return addr;
|
||||
}
|
||||
|
||||
ssize_t FramebufferDevice::write(VFS::Node* node, size_t offset, size_t size, const char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if ((size + offset) > (uint64_t)bootboot.fb_size) { size = (uint64_t)bootboot.fb_size - offset; }
|
||||
memcpy(fb + offset, buffer, size);
|
||||
return (ssize_t)size;
|
||||
}
|
||||
|
||||
#define FB_GET_WIDTH 0
|
||||
#define FB_GET_HEIGHT 1
|
||||
#define FB_GET_SCANLINE 2
|
||||
|
||||
long FramebufferDevice::ioctl(VFS::Node* node, int cmd, uintptr_t)
|
||||
{
|
||||
if (!node) return -1;
|
||||
switch (cmd)
|
||||
{
|
||||
case FB_GET_WIDTH: return (long)bootboot.fb_width;
|
||||
case FB_GET_HEIGHT: return (long)bootboot.fb_height;
|
||||
case FB_GET_SCANLINE: return (long)bootboot.fb_scanline;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ int KeyboardDevice::would_block(VFS::Node*)
|
||||
return kbd_bufsize == 0;
|
||||
}
|
||||
|
||||
extern uint64_t clock_boot();
|
||||
|
||||
VFS::Node* KeyboardDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
@ -26,6 +28,9 @@ VFS::Node* KeyboardDevice::create_new(const char* devname)
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
dev->tty = 1;
|
||||
dev->uid = dev->gid = 0;
|
||||
dev->mode = 0444;
|
||||
dev->atime = dev->ctime = dev->mtime = clock_boot();
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
|
34
kernel/src/fs/devices/NullDevice.cpp
Normal file
34
kernel/src/fs/devices/NullDevice.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "fs/devices/NullDevice.h"
|
||||
#include "std/stdio.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
extern uint64_t clock_boot();
|
||||
|
||||
VFS::Node* NullDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
dev->write_func = NullDevice::write;
|
||||
dev->read_func = NullDevice::read;
|
||||
dev->inode = 0;
|
||||
dev->length = 0;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
dev->uid = dev->gid = 0;
|
||||
dev->mode = 0666;
|
||||
dev->atime = dev->ctime = dev->mtime = clock_boot();
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
|
||||
ssize_t NullDevice::write(VFS::Node* node, size_t, size_t size, const char*)
|
||||
{
|
||||
if (!node) return -1;
|
||||
return (ssize_t)size;
|
||||
}
|
||||
|
||||
ssize_t NullDevice::read(VFS::Node* node, size_t, size_t, char*)
|
||||
{
|
||||
if (!node) return -1;
|
||||
return 0;
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
extern uint64_t clock_boot();
|
||||
|
||||
VFS::Node* RandomDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
@ -14,6 +16,9 @@ VFS::Node* RandomDevice::create_new(const char* devname)
|
||||
dev->length = 0;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
dev->uid = dev->gid = 0;
|
||||
dev->mode = 0444;
|
||||
dev->atime = dev->ctime = dev->mtime = clock_boot();
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
extern uint64_t clock_boot();
|
||||
|
||||
VFS::Node* SerialDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
@ -13,6 +15,9 @@ VFS::Node* SerialDevice::create_new(const char* devname)
|
||||
dev->length = 0;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
dev->uid = dev->gid = 0;
|
||||
dev->mode = 0222;
|
||||
dev->atime = dev->ctime = dev->mtime = clock_boot();
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
#include "fs/devices/Uptime.h"
|
||||
#include "std/stdio.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
#include "thread/PIT.h"
|
||||
|
||||
VFS::Node* UptimeDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
dev->read_func = UptimeDevice::read;
|
||||
dev->inode = 0;
|
||||
dev->length = 0;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
|
||||
ssize_t UptimeDevice::read(VFS::Node* node, size_t, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
snprintf(buffer, size + 1, "%ld", PIT::ms_since_boot); // FIXME: Support offseting this read
|
||||
return (ssize_t)size;
|
||||
}
|
@ -4,6 +4,8 @@
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
extern uint64_t clock_boot();
|
||||
|
||||
VFS::Node* VersionDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
@ -12,6 +14,9 @@ VFS::Node* VersionDevice::create_new(const char* devname)
|
||||
dev->length = strlen(moon_version()) + 5;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
dev->uid = dev->gid = 0;
|
||||
dev->mode = 0444;
|
||||
dev->atime = dev->ctime = dev->mtime = clock_boot();
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
@ -19,8 +24,7 @@ VFS::Node* VersionDevice::create_new(const char* devname)
|
||||
ssize_t VersionDevice::read(VFS::Node* node, size_t offset, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if (offset > node->length) return -1;
|
||||
if (offset + size > node->length) { size = node->length - offset; }
|
||||
if (offset > 0) return 0; // EOF after first read (FIXME: Should be only if everything was read)
|
||||
snprintf(buffer, size + 1, "moon %s", moon_version()); // FIXME: Support offseting this read
|
||||
return (ssize_t)size;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
#include "gdt/GDT.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/string.h"
|
||||
#include <stdint.h>
|
||||
|
||||
@ -73,7 +73,7 @@ static void set_base(GDTEntry* entry, uint32_t base)
|
||||
|
||||
static void set_limit(GDTEntry* entry, uint32_t limit)
|
||||
{
|
||||
ASSERT(limit <= 0xFFFFF);
|
||||
ensure(limit <= 0xFFFFF);
|
||||
entry->limit0 = limit & 0xFFFF;
|
||||
entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF);
|
||||
}
|
||||
|
@ -3,29 +3,32 @@
|
||||
#include "init/Init.h"
|
||||
#include "bootboot.h"
|
||||
#include "cpu/CPU.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "fs/InitRD.h"
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "io/Serial.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "misc/MSR.h"
|
||||
#include "misc/hang.h"
|
||||
#include "rand/Init.h"
|
||||
#include "rand/Mersenne.h"
|
||||
#include "render/Framebuffer.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/string.h"
|
||||
#include "utils/Time.h"
|
||||
|
||||
extern BOOTBOOT bootboot;
|
||||
extern "C" char environment[4096];
|
||||
extern uintptr_t fb;
|
||||
|
||||
uintptr_t __stack_chk_guard = 0xfeff34;
|
||||
|
||||
void Init::check_magic()
|
||||
{
|
||||
ASSERT(strncmp((char*)bootboot.magic, BOOTBOOT_MAGIC, 4) == 0);
|
||||
if (strncmp((char*)bootboot.magic, BOOTBOOT_MAGIC, 4) != 0) hang();
|
||||
}
|
||||
|
||||
void Init::disable_smp()
|
||||
@ -35,6 +38,42 @@ void Init::disable_smp()
|
||||
}
|
||||
|
||||
extern "C" void asm_enable_sse();
|
||||
extern void clock_init();
|
||||
|
||||
extern void panic_prepare_keyboard_triple_fault();
|
||||
|
||||
#define NO_EXECUTE_ENABLED (1 << 11)
|
||||
|
||||
static void check_and_enable_nx()
|
||||
{
|
||||
if (!CPU::has_nx())
|
||||
{
|
||||
kerrorln("This machine does not support the NX feature, which is required to continue booting.");
|
||||
kerrorln("On most cases, this means your machine is too old and not supported.");
|
||||
|
||||
kinfoln("Press any key to restart and select an OS that is suitable for your CPU.");
|
||||
|
||||
panic_prepare_keyboard_triple_fault();
|
||||
|
||||
while (1) halt();
|
||||
}
|
||||
|
||||
kdbgln("nx supported");
|
||||
|
||||
MSR efer(IA32_EFER_MSR);
|
||||
|
||||
uint64_t value = efer.read();
|
||||
|
||||
if (value & NO_EXECUTE_ENABLED)
|
||||
{
|
||||
kdbgln("nx already enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
kdbgln("nx not enabled, enabling it");
|
||||
|
||||
efer.write(value | NO_EXECUTE_ENABLED);
|
||||
}
|
||||
|
||||
void Init::early_init()
|
||||
{
|
||||
@ -43,11 +82,14 @@ void Init::early_init()
|
||||
|
||||
asm_enable_sse();
|
||||
|
||||
framebuffer0.init((void*)bootboot.fb_ptr, bootboot.fb_type, bootboot.fb_scanline, bootboot.fb_width,
|
||||
bootboot.fb_height);
|
||||
framebuffer0.init((void*)&fb, bootboot.fb_type, bootboot.fb_scanline, bootboot.fb_width, bootboot.fb_height);
|
||||
|
||||
check_and_enable_nx();
|
||||
|
||||
MemoryManager::init();
|
||||
|
||||
MemoryManager::protect_kernel_sections();
|
||||
|
||||
if (strstr(environment, "quiet=1"))
|
||||
{
|
||||
KernelLog::toggle_log_level(LogLevel::DEBUG);
|
||||
@ -55,6 +97,8 @@ void Init::early_init()
|
||||
}
|
||||
else if (!strstr(environment, "verbose=1")) { KernelLog::toggle_log_level(LogLevel::DEBUG); }
|
||||
|
||||
clock_init();
|
||||
|
||||
InitRD::init();
|
||||
|
||||
Mersenne::init();
|
||||
|
@ -5,17 +5,19 @@
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "io/Serial.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "misc/hang.h"
|
||||
#include "panic/Panic.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/stdio.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "trace/StackTracer.h"
|
||||
#include "utils/PageFaultReason.h"
|
||||
|
||||
extern "C" void common_handler(Context* context)
|
||||
{
|
||||
ASSERT(Interrupts::is_in_handler());
|
||||
ensure(Interrupts::is_in_handler());
|
||||
if (context->number >= 0x20 && context->number < 0x30)
|
||||
{
|
||||
IRQ::interrupt_handler(context);
|
||||
@ -28,6 +30,8 @@ extern "C" void common_handler(Context* context)
|
||||
if (context->cs == 0x8) { int_panic(context, "GPF in kernel task"); }
|
||||
else
|
||||
{
|
||||
VMM::enter_syscall_context();
|
||||
|
||||
kerrorln("General protection fault at RIP %lx, cs %ld, ss %ld, RSP %lx, error code %ld", context->rip,
|
||||
context->cs, context->ss, context->rsp, context->error_code);
|
||||
kinfoln("Stack trace:");
|
||||
@ -44,6 +48,8 @@ extern "C" void common_handler(Context* context)
|
||||
if (context->cs == 0x8) { int_panic(context, "Page fault in kernel task"); }
|
||||
else
|
||||
{
|
||||
VMM::enter_syscall_context();
|
||||
|
||||
kerrorln("Page fault in ring 3 (RIP %lx), while trying to access %lx, error code %ld", context->rip,
|
||||
context->cr2, context->error_code);
|
||||
kinfoln("Stack trace:");
|
||||
@ -51,6 +57,8 @@ extern "C" void common_handler(Context* context)
|
||||
StackTracer tracer(context->rbp);
|
||||
tracer.trace_with_ip(context->rip);
|
||||
|
||||
determine_user_page_fault_reason(context->cr2);
|
||||
|
||||
Scheduler::task_misbehave(context, -3);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "interrupts/IDT.h"
|
||||
#include "log/Log.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
|
||||
struct IDTEntry
|
||||
{
|
||||
@ -37,8 +37,8 @@ uint64_t IDTEntry::get_offset()
|
||||
|
||||
void IDT::add_handler(short interrupt_number, void* handler, uint8_t type_attr)
|
||||
{
|
||||
ASSERT(handler != nullptr);
|
||||
ASSERT(interrupt_number < 256);
|
||||
ensure(handler != nullptr);
|
||||
ensure(interrupt_number < 256);
|
||||
IDTEntry* entry_for_handler = &entries[interrupt_number];
|
||||
entry_for_handler->selector = 0x08;
|
||||
entry_for_handler->type_attr = type_attr;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#define MODULE "irq"
|
||||
|
||||
#include "interrupts/IRQ.h"
|
||||
#include "fs/devices/Console.h"
|
||||
#include "fs/devices/Keyboard.h"
|
||||
#include "io/IO.h"
|
||||
#include "io/PIC.h"
|
||||
@ -21,10 +22,11 @@ void IRQ::interrupt_handler(Context* context)
|
||||
break;
|
||||
case 1: {
|
||||
unsigned char scancode = IO::inb(0x60);
|
||||
KeyboardDevice::append((char)scancode);
|
||||
bool ignore = false;
|
||||
char key = translate_scancode(scancode, &ignore);
|
||||
if (ignore) break;
|
||||
KeyboardDevice::append(key);
|
||||
ConsoleDevice::append(key);
|
||||
break;
|
||||
}
|
||||
default: kwarnln("Unhandled IRQ: %ld", context->irq_number); break;
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryMap.h"
|
||||
#include "misc/hang.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "thread/PIT.h"
|
||||
#include "thread/Scheduler.h"
|
||||
@ -23,8 +23,8 @@
|
||||
|
||||
extern "C" void _start()
|
||||
{
|
||||
Init::check_magic();
|
||||
Init::disable_smp(); // Put all other cores except the bootstrap one in an infinite loop
|
||||
Init::check_magic();
|
||||
Init::early_init();
|
||||
|
||||
kinfoln("Starting Moon %s", moon_version());
|
||||
@ -52,9 +52,9 @@ extern "C" void _start()
|
||||
kinfoln("Prepared scheduler");
|
||||
|
||||
#ifdef RUN_TEST_AS_INIT
|
||||
ASSERT(Scheduler::load_user_task(STRINGIZE_VALUE_OF(RUN_TEST_AS_INIT)) > 0);
|
||||
ensure(Scheduler::load_user_task(STRINGIZE_VALUE_OF(RUN_TEST_AS_INIT)) > 0);
|
||||
#else
|
||||
ASSERT(Scheduler::load_user_task("/bin/init") > 0);
|
||||
ensure(Scheduler::load_user_task("/bin/init") > 0);
|
||||
#endif
|
||||
|
||||
Scheduler::add_kernel_task("[reaper]", []() {
|
||||
@ -67,7 +67,7 @@ extern "C" void _start()
|
||||
|
||||
kinfoln("Prepared scheduler tasks");
|
||||
|
||||
ASSERT(VFS::mkdir("/dev") == 0);
|
||||
ensure(VFS::mkdir("/dev") == 0);
|
||||
VFS::mount("/dev", DeviceFS::get());
|
||||
|
||||
Init::finish_kernel_boot();
|
||||
@ -78,5 +78,10 @@ extern "C" void _start()
|
||||
|
||||
Interrupts::enable();
|
||||
|
||||
while (1) halt();
|
||||
while (1)
|
||||
halt(); // As soon as the first timer interrupt arrives, this idle loop is gone, since the main function is not
|
||||
// registered as a task and thus the scheduler will never schedule this again. We still have to do
|
||||
// something while waiting for a timer interrupt to arrive, though. In fact, in most cases, calling
|
||||
// halt() once would be enough, since that function halts the CPU until the next interrupt (most likely
|
||||
// the timer one) arrives. But we have to guarantee this function never returns.
|
||||
}
|
@ -26,8 +26,11 @@ void AddressSpace::destroy()
|
||||
if (!pdp_pde.present) continue;
|
||||
if (pdp_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pdp_pde.get_address());
|
||||
if (pdp_pde.owned_by_task)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pdp_pde.get_address());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
PageTable* pdp = (PageTable*)pdp_pde.get_address();
|
||||
@ -37,8 +40,11 @@ void AddressSpace::destroy()
|
||||
if (!pd_pde.present) continue;
|
||||
if (pd_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pd_pde.get_address());
|
||||
if (pd_pde.owned_by_task)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pd_pde.get_address());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
PageTable* pd = (PageTable*)pd_pde.get_address();
|
||||
@ -48,8 +54,11 @@ void AddressSpace::destroy()
|
||||
if (!pt_pde.present) continue;
|
||||
if (pt_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pt_pde.get_address());
|
||||
if (pt_pde.owned_by_task)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pt_pde.get_address());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
PageTable* pt = (PageTable*)pt_pde.get_address();
|
||||
@ -57,6 +66,7 @@ void AddressSpace::destroy()
|
||||
{
|
||||
PageDirectoryEntry& pde = pt->entries[l];
|
||||
if (!pde.present) continue;
|
||||
if (!pde.owned_by_task) continue;
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pde.get_address());
|
||||
}
|
||||
@ -84,8 +94,11 @@ void AddressSpace::clear()
|
||||
if (!pdp_pde.present) continue;
|
||||
if (pdp_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pdp_pde.get_address());
|
||||
if (pdp_pde.owned_by_task)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pdp_pde.get_address());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
PageTable* pdp = (PageTable*)pdp_pde.get_address();
|
||||
@ -95,8 +108,11 @@ void AddressSpace::clear()
|
||||
if (!pd_pde.present) continue;
|
||||
if (pd_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pd_pde.get_address());
|
||||
if (pd_pde.owned_by_task)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pd_pde.get_address());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
PageTable* pd = (PageTable*)pd_pde.get_address();
|
||||
@ -106,8 +122,11 @@ void AddressSpace::clear()
|
||||
if (!pt_pde.present) continue;
|
||||
if (pt_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pt_pde.get_address());
|
||||
if (pt_pde.owned_by_task)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pt_pde.get_address());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
PageTable* pt = (PageTable*)pt_pde.get_address();
|
||||
@ -115,6 +134,7 @@ void AddressSpace::clear()
|
||||
{
|
||||
PageDirectoryEntry& pde = pt->entries[l];
|
||||
if (!pde.present) continue;
|
||||
if (!pde.owned_by_task) continue;
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pde.get_address());
|
||||
}
|
||||
@ -154,6 +174,11 @@ AddressSpace AddressSpace::clone() // FIXME: Add out-of-memory checks to this fu
|
||||
if (!pdp_pde.present) continue;
|
||||
if (pdp_pde.larger_pages)
|
||||
{
|
||||
if (!pdp_pde.owned_by_task)
|
||||
{
|
||||
memcpy(&cloned_pdp_pde, &pdp_pde, sizeof(PageDirectoryEntry));
|
||||
continue;
|
||||
}
|
||||
void* cloned = try_clone_page_table((PageTable*)pdp_pde.get_address());
|
||||
if (!cloned)
|
||||
{
|
||||
@ -178,6 +203,11 @@ AddressSpace AddressSpace::clone() // FIXME: Add out-of-memory checks to this fu
|
||||
if (!pd_pde.present) continue;
|
||||
if (pd_pde.larger_pages)
|
||||
{
|
||||
if (!pd_pde.owned_by_task)
|
||||
{
|
||||
memcpy(&cloned_pd_pde, &pd_pde, sizeof(PageDirectoryEntry));
|
||||
continue;
|
||||
}
|
||||
void* cloned = try_clone_page_table((PageTable*)pd_pde.get_address());
|
||||
if (!cloned)
|
||||
{
|
||||
@ -202,6 +232,11 @@ AddressSpace AddressSpace::clone() // FIXME: Add out-of-memory checks to this fu
|
||||
if (!pt_pde.present) continue;
|
||||
if (pt_pde.larger_pages)
|
||||
{
|
||||
if (!pt_pde.owned_by_task)
|
||||
{
|
||||
memcpy(&cloned_pt_pde, &pt_pde, sizeof(PageDirectoryEntry));
|
||||
continue;
|
||||
}
|
||||
void* cloned = try_clone_page_table((PageTable*)pt_pde.get_address());
|
||||
if (!cloned)
|
||||
{
|
||||
@ -223,6 +258,11 @@ AddressSpace AddressSpace::clone() // FIXME: Add out-of-memory checks to this fu
|
||||
{
|
||||
PageDirectoryEntry& pde = pt->entries[l];
|
||||
PageDirectoryEntry& cloned_pde = cloned_pt->entries[l];
|
||||
if (!pde.owned_by_task)
|
||||
{
|
||||
memcpy(&cloned_pde, &pde, sizeof(PageDirectoryEntry));
|
||||
continue;
|
||||
}
|
||||
if (!pde.present) continue;
|
||||
void* cloned = try_clone_page_table((PageTable*)pde.get_address());
|
||||
if (!cloned)
|
||||
|
@ -35,7 +35,6 @@ static void bitmap_set(uint64_t index, bool value)
|
||||
void KernelHeap::clear()
|
||||
{
|
||||
memset(page_bitmap, 0, sizeof(page_bitmap));
|
||||
kinfoln("page bitmap located at %p", (void*)page_bitmap);
|
||||
}
|
||||
|
||||
uint64_t KernelHeap::request_virtual_page()
|
||||
|
@ -7,7 +7,8 @@
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "std/assert.h"
|
||||
#include "misc/utils.h"
|
||||
#include "std/ensure.h"
|
||||
|
||||
void MemoryManager::init()
|
||||
{
|
||||
@ -17,6 +18,19 @@ void MemoryManager::init()
|
||||
PMM::map_bitmap_to_virtual();
|
||||
}
|
||||
|
||||
extern char start_of_kernel_rodata[1];
|
||||
extern char end_of_kernel_rodata[1];
|
||||
extern char start_of_kernel_data[1];
|
||||
extern char end_of_kernel_data[1];
|
||||
|
||||
void MemoryManager::protect_kernel_sections()
|
||||
{
|
||||
protect(start_of_kernel_rodata,
|
||||
Utilities::get_blocks_from_size(PAGE_SIZE, end_of_kernel_rodata - start_of_kernel_rodata), 0);
|
||||
protect(start_of_kernel_data, Utilities::get_blocks_from_size(PAGE_SIZE, end_of_kernel_data - start_of_kernel_data),
|
||||
MAP_READ_WRITE);
|
||||
}
|
||||
|
||||
void* MemoryManager::get_mapping(void* physicalAddress, int flags)
|
||||
{
|
||||
uint64_t virtualAddress = KernelHeap::request_virtual_page();
|
||||
@ -124,7 +138,7 @@ void* MemoryManager::get_page_at(uint64_t addr, int flags)
|
||||
void MemoryManager::release_page(void* page)
|
||||
{
|
||||
uint64_t physicalAddress = VMM::get_physical((uint64_t)page);
|
||||
ASSERT(physicalAddress != UINT64_MAX); // this address is not mapped in the virtual address space...
|
||||
ensure(physicalAddress != UINT64_MAX); // this address is not mapped in the virtual address space...
|
||||
VMM::unmap((uint64_t)page);
|
||||
PMM::free_page((void*)physicalAddress);
|
||||
}
|
||||
@ -184,7 +198,7 @@ void MemoryManager::release_pages(void* pages, uint64_t count)
|
||||
{
|
||||
void* page = (void*)((uint64_t)pages + (i * PAGE_SIZE));
|
||||
uint64_t physicalAddress = VMM::get_physical((uint64_t)page);
|
||||
ASSERT(physicalAddress != UINT64_MAX);
|
||||
ensure(physicalAddress != UINT64_MAX);
|
||||
VMM::unmap((uint64_t)page);
|
||||
PMM::free_page((void*)physicalAddress);
|
||||
}
|
||||
@ -195,3 +209,11 @@ void MemoryManager::protect(void* page, uint64_t count, int flags)
|
||||
{
|
||||
for (uint64_t i = 0; i < count; i++) { VMM::remap((uint64_t)page + (i * PAGE_SIZE), flags); }
|
||||
}
|
||||
|
||||
void MemoryManager::map_several_pages(uint64_t physicalAddress, uint64_t virtualAddress, uint64_t count, int flags)
|
||||
{
|
||||
for (uint64_t i = 0; i < count; i++)
|
||||
{
|
||||
VMM::map(virtualAddress + (i * PAGE_SIZE), physicalAddress + (i * PAGE_SIZE), flags);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "misc/utils.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/string.h"
|
||||
|
||||
extern BOOTBOOT bootboot;
|
||||
@ -50,7 +50,7 @@ void PMM::init()
|
||||
|
||||
bitmap_addr = (char*)biggest_chunk;
|
||||
virtual_bitmap_addr = bitmap_addr;
|
||||
ASSERT((total_mem / PAGE_SIZE / 8) < biggest_chunk_size);
|
||||
ensure((total_mem / PAGE_SIZE / 8) < biggest_chunk_size);
|
||||
bitmap_size = total_mem / PAGE_SIZE / 8 + 1;
|
||||
memset(bitmap_addr, 0xFF, bitmap_size);
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "log/Log.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "misc/utils.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/string.h"
|
||||
#include "utils/Addresses.h"
|
||||
#include "utils/Registers.h"
|
||||
@ -13,6 +13,8 @@ static PageTable* kernel_pml4;
|
||||
static PageTable* current_pml4;
|
||||
static AddressSpace* user_address_space;
|
||||
|
||||
// FIXME: Switch to recursive paging instead of naively assuming the physical address space is identity mapped.
|
||||
|
||||
void VMM::switch_back_to_kernel_address_space()
|
||||
{
|
||||
if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; }
|
||||
@ -59,6 +61,16 @@ void VMM::init()
|
||||
{
|
||||
kernel_pml4 = (PageTable*)read_cr3();
|
||||
current_pml4 = kernel_pml4;
|
||||
|
||||
// Set up recursive paging
|
||||
PageDirectoryEntry& recursive_pde = kernel_pml4->entries[510];
|
||||
memset(&recursive_pde, 0, sizeof(PageDirectoryEntry));
|
||||
recursive_pde.present = true;
|
||||
recursive_pde.read_write = true;
|
||||
recursive_pde.larger_pages = false;
|
||||
recursive_pde.no_execute = true;
|
||||
recursive_pde.set_address((uint64_t)kernel_pml4);
|
||||
flush_tlb_full();
|
||||
}
|
||||
|
||||
void VMM::unmap(uint64_t vaddr)
|
||||
@ -85,6 +97,12 @@ void VMM::remap(uint64_t vaddr, int flags)
|
||||
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
|
||||
else
|
||||
pde->read_write = false;
|
||||
if (flags & Execute) pde->no_execute = false;
|
||||
else
|
||||
pde->no_execute = true;
|
||||
if (flags & OwnedByTask) pde->owned_by_task = true;
|
||||
else
|
||||
pde->owned_by_task = false;
|
||||
flush_tlb(vaddr);
|
||||
}
|
||||
|
||||
@ -99,11 +117,13 @@ uint64_t VMM::get_physical(uint64_t vaddr)
|
||||
uint64_t VMM::get_flags(uint64_t vaddr)
|
||||
{
|
||||
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
|
||||
if (!pde) return 0; // Not mapped
|
||||
if (!pde) return (uint64_t)-1; // Not mapped
|
||||
|
||||
uint64_t flags = 0;
|
||||
if (pde->user) flags |= User;
|
||||
if (pde->read_write) flags |= ReadWrite;
|
||||
if (!pde->no_execute) flags |= Execute;
|
||||
if (pde->owned_by_task) flags |= OwnedByTask;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@ -121,6 +141,7 @@ void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
|
||||
{
|
||||
unmap(vaddr);
|
||||
pde = create_pde_if_not_exists(current_pml4, vaddr);
|
||||
will_flush_tlb = false; // unmap() already flushes the TLB for us
|
||||
}
|
||||
|
||||
pde->set_address(round_down_to_nearest_page(paddr));
|
||||
@ -130,6 +151,12 @@ void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
|
||||
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
|
||||
else
|
||||
pde->read_write = false;
|
||||
if (flags & Execute) pde->no_execute = false;
|
||||
else
|
||||
pde->no_execute = true;
|
||||
if (flags & OwnedByTask) pde->owned_by_task = true;
|
||||
else
|
||||
pde->owned_by_task = false;
|
||||
if (will_flush_tlb) flush_tlb(vaddr);
|
||||
}
|
||||
|
||||
@ -170,7 +197,7 @@ PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vadd
|
||||
|
||||
auto pde_create_if_not_present = [&]() {
|
||||
pt = (PageTable*)PMM::request_page();
|
||||
ASSERT(!(PMM_DID_FAIL(pt)));
|
||||
ensure(!(PMM_DID_FAIL(pt)));
|
||||
memset(pt, 0, PAGE_SIZE);
|
||||
pde->set_address((uint64_t)pt);
|
||||
pde->present = true;
|
||||
@ -246,11 +273,44 @@ void VMM::propagate_user(PageTable* root, uint64_t vaddr)
|
||||
pde->user = true;
|
||||
}
|
||||
|
||||
void VMM::propagate_no_execute(PageTable* root, uint64_t vaddr)
|
||||
{
|
||||
uint64_t page_index;
|
||||
PageDirectoryEntry* pde;
|
||||
PageTable* pt = root;
|
||||
|
||||
uint64_t indexes[3];
|
||||
|
||||
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
pde = &pt->entries[indexes[i]];
|
||||
if (!pde->present) return;
|
||||
else
|
||||
{
|
||||
pde->no_execute = true;
|
||||
if (pde->larger_pages) return;
|
||||
pt = (PageTable*)pde->get_address();
|
||||
}
|
||||
}
|
||||
|
||||
pde = &pt->entries[page_index];
|
||||
if (!pde->present) return;
|
||||
else
|
||||
pde->no_execute = true;
|
||||
}
|
||||
|
||||
void VMM::flush_tlb(uint64_t addr)
|
||||
{
|
||||
asm volatile("invlpg (%0)" : : "r"(addr) : "memory");
|
||||
}
|
||||
|
||||
void VMM::flush_tlb_full()
|
||||
{
|
||||
write_cr3(current_pml4);
|
||||
}
|
||||
|
||||
void VMM::decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
|
||||
uint64_t& pdp_index)
|
||||
{
|
||||
|
@ -8,7 +8,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef MOON_MINOR
|
||||
#define MOON_MINOR 13
|
||||
#define MOON_MINOR 14
|
||||
#endif
|
||||
|
||||
#ifndef _MOON_SUFFIX
|
||||
|
205
kernel/src/misc/ubsan.cpp
Normal file
205
kernel/src/misc/ubsan.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
#define MODULE "ubsan"
|
||||
|
||||
#include <log/Log.h>
|
||||
#include <panic/Panic.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
|
||||
struct source_location
|
||||
{
|
||||
const char* file;
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
};
|
||||
|
||||
struct type_descriptor
|
||||
{
|
||||
uint16_t kind;
|
||||
uint16_t info;
|
||||
char name[];
|
||||
};
|
||||
|
||||
struct type_mismatch_info
|
||||
{
|
||||
struct source_location location;
|
||||
struct type_descriptor* type;
|
||||
uintptr_t alignment;
|
||||
uint8_t type_check_kind;
|
||||
};
|
||||
|
||||
struct type_mismatch_info_v1
|
||||
{
|
||||
struct source_location location;
|
||||
struct type_descriptor* type;
|
||||
unsigned char log_alignment;
|
||||
unsigned char type_check_kind;
|
||||
};
|
||||
|
||||
struct overflow_info
|
||||
{
|
||||
struct source_location location;
|
||||
struct type_descriptor* type;
|
||||
};
|
||||
|
||||
struct unreachable_info
|
||||
{
|
||||
struct source_location location;
|
||||
};
|
||||
|
||||
struct out_of_bounds_info
|
||||
{
|
||||
struct source_location location;
|
||||
struct type_descriptor* array_type;
|
||||
struct type_descriptor* index_type;
|
||||
};
|
||||
|
||||
struct invalid_value_info
|
||||
{
|
||||
struct source_location location;
|
||||
struct type_descriptor* type;
|
||||
};
|
||||
|
||||
struct shift_out_of_bounds_info
|
||||
{
|
||||
struct source_location location;
|
||||
struct type_descriptor* lhs_type;
|
||||
struct type_descriptor* rhs_type;
|
||||
};
|
||||
|
||||
struct pointer_overflow_info
|
||||
{
|
||||
struct source_location location;
|
||||
};
|
||||
|
||||
#define is_aligned(value, alignment) !(value & (alignment - 1))
|
||||
|
||||
const char* Type_Check_Kinds[] = {
|
||||
"load of",
|
||||
"store to",
|
||||
"reference binding to",
|
||||
"member access within",
|
||||
"member call on",
|
||||
"constructor call on",
|
||||
"downcast of",
|
||||
"downcast of",
|
||||
"upcast of",
|
||||
"cast to virtual base of",
|
||||
};
|
||||
|
||||
static void log_location(source_location* location)
|
||||
{
|
||||
kinfoln("at %s:%d:%d", location->file, location->line, location->column);
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_type_mismatch(type_mismatch_info* type_mismatch, uintptr_t pointer)
|
||||
{
|
||||
source_location* location = &type_mismatch->location;
|
||||
if (pointer == 0) { kinfoln("Null pointer access"); }
|
||||
else if (type_mismatch->alignment != 0 && is_aligned(pointer, type_mismatch->alignment))
|
||||
{
|
||||
// Most useful on architectures with stricter memory alignment requirements, like ARM.
|
||||
kinfoln("Unaligned memory access");
|
||||
}
|
||||
else
|
||||
{
|
||||
kinfoln("Insufficient size");
|
||||
kinfoln("%s address %p with insufficient space for object of type %s\n",
|
||||
Type_Check_Kinds[type_mismatch->type_check_kind], (void*)pointer, type_mismatch->type->name);
|
||||
}
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_type_mismatch_v1(type_mismatch_info_v1* type_mismatch, unsigned long pointer)
|
||||
{
|
||||
type_mismatch_info info = {type_mismatch->location, type_mismatch->type, 1UL << type_mismatch->log_alignment,
|
||||
type_mismatch->type_check_kind};
|
||||
|
||||
__ubsan_handle_type_mismatch(&info, pointer);
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_add_overflow(overflow_info* overflow_data, uintptr_t, uintptr_t)
|
||||
{
|
||||
source_location* location = &overflow_data->location;
|
||||
kinfoln("Addition overflow for two values of type %s", overflow_data->type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_sub_overflow(overflow_info* overflow_data, uintptr_t, uintptr_t)
|
||||
{
|
||||
source_location* location = &overflow_data->location;
|
||||
kinfoln("Substraction overflow for two values of type %s", overflow_data->type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_mul_overflow(overflow_info* overflow_data, uintptr_t, uintptr_t)
|
||||
{
|
||||
source_location* location = &overflow_data->location;
|
||||
kinfoln("Multiplication overflow for two values of type %s", overflow_data->type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_negate_overflow(overflow_info* overflow_data, uintptr_t)
|
||||
{
|
||||
source_location* location = &overflow_data->location;
|
||||
kinfoln("Negation overflow for two values of type %s", overflow_data->type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_divrem_overflow(overflow_info* overflow_data, uintptr_t, uintptr_t)
|
||||
{
|
||||
source_location* location = &overflow_data->location;
|
||||
kinfoln("Division overflow for two values of type %s", overflow_data->type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_builtin_unreachable(unreachable_info* unreachable_data)
|
||||
{
|
||||
source_location* location = &unreachable_data->location;
|
||||
kinfoln("Reached the unreachable");
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_out_of_bounds(out_of_bounds_info* out_of_bounds_data, uintptr_t index)
|
||||
{
|
||||
source_location* location = &out_of_bounds_data->location;
|
||||
kinfoln("Out of bounds access at index %ld of array type %s and index type %s", index,
|
||||
out_of_bounds_data->array_type->name, out_of_bounds_data->index_type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_load_invalid_value(invalid_value_info* invalid_value_data, uintptr_t)
|
||||
{
|
||||
source_location* location = &invalid_value_data->location;
|
||||
kinfoln("Invalid value load of type %s", invalid_value_data->type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_shift_out_of_bounds(shift_out_of_bounds_info* shift_out_of_bounds_data, uintptr_t,
|
||||
uintptr_t)
|
||||
{
|
||||
source_location* location = &shift_out_of_bounds_data->location;
|
||||
kinfoln("Shift out of bounds for type %s", shift_out_of_bounds_data->lhs_type->name);
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
extern "C" void __ubsan_handle_pointer_overflow(pointer_overflow_info* pointer_overflow_data, uintptr_t, uintptr_t)
|
||||
{
|
||||
source_location* location = &pointer_overflow_data->location;
|
||||
kinfoln("Pointer overflow");
|
||||
log_location(location);
|
||||
panic("Undefined behaviour detected");
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
@ -1,7 +1,7 @@
|
||||
#define MODULE "panic"
|
||||
|
||||
#include "panic/Panic.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "fs/InitRD.h"
|
||||
#include "interrupts/IDT.h"
|
||||
#include "io/PIC.h"
|
||||
#include "log/Log.h"
|
||||
@ -16,7 +16,7 @@
|
||||
static bool g_is_in_panic = false;
|
||||
static bool g_is_in_double_panic = false;
|
||||
|
||||
static void panic_prepare_keyboard_triple_fault()
|
||||
void panic_prepare_keyboard_triple_fault()
|
||||
{
|
||||
PIC::enable_master(0b11111101); // enable keyboard only
|
||||
PIC::enable_slave(0b11111111);
|
||||
|
@ -1,7 +1,7 @@
|
||||
#define MODULE "rand"
|
||||
|
||||
#include "rand/Mersenne.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include <stddef.h>
|
||||
|
||||
typedef uint64_t word_t;
|
||||
@ -47,7 +47,7 @@ uint64_t Mersenne::get()
|
||||
{
|
||||
if (index >= STATE_SIZE)
|
||||
{
|
||||
ASSERT(index == STATE_SIZE && "Mersenne generator was never seeded");
|
||||
ensure(index == STATE_SIZE && "Mersenne generator was never seeded");
|
||||
twist();
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "render/TextRenderer.h"
|
||||
#include "bootboot.h"
|
||||
#include "font.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "fs/InitRD.h"
|
||||
#include "io/Serial.h"
|
||||
#include "log/Log.h"
|
||||
#include "render/Framebuffer.h"
|
||||
@ -30,7 +30,7 @@ void TextRenderer::reset()
|
||||
#pragma GCC optimize("O0")
|
||||
|
||||
static void putchar_at_offset(
|
||||
char c, [[maybe_unused]] uint32_t cx, [[maybe_unused]] uint32_t cy, [[maybe_unused]] Color& fg,
|
||||
char c, uint32_t cx, uint32_t cy, [[maybe_unused]] Color& fg,
|
||||
[[maybe_unused]] Color& bg) // FIXME: Rewrite this function to actually work with foreground and background colors.
|
||||
{
|
||||
uint8_t* glyph = &font[c * 16];
|
||||
@ -46,49 +46,67 @@ static void putchar_at_offset(
|
||||
}
|
||||
}
|
||||
|
||||
static bool g_escape_sequence = false;
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
void TextRenderer::putchar(char chr)
|
||||
{
|
||||
switch (chr)
|
||||
if (g_escape_sequence)
|
||||
{
|
||||
case '\n': {
|
||||
ypos += FONT_HEIGHT;
|
||||
if ((ypos + FONT_HEIGHT) >= bootboot.fb_height)
|
||||
g_escape_sequence = false;
|
||||
switch (chr)
|
||||
{
|
||||
memcpy((void*)bootboot.fb_ptr, (char*)bootboot.fb_ptr + (bootboot.fb_scanline * FONT_HEIGHT),
|
||||
bootboot.fb_size - (bootboot.fb_scanline * FONT_HEIGHT));
|
||||
ypos -= FONT_HEIGHT;
|
||||
framebuffer0.paint_rect(0, ypos, bootboot.fb_width, FONT_HEIGHT, Color::Black);
|
||||
case '@':
|
||||
reset();
|
||||
framebuffer0.clear(Color::Black);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
xpos = 0;
|
||||
break;
|
||||
}
|
||||
case '\r': xpos = 0; break;
|
||||
case '\b':
|
||||
if (xpos != 0)
|
||||
else
|
||||
{
|
||||
switch (chr)
|
||||
{
|
||||
xpos -= FONT_WIDTH;
|
||||
framebuffer0.paint_rect(xpos, ypos, FONT_WIDTH, FONT_HEIGHT, Color::Black);
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
putchar_at_offset(chr, xpos, ypos, fgColor, bgColor);
|
||||
xpos += FONT_WIDTH;
|
||||
if ((xpos + FONT_WIDTH) > bootboot.fb_width)
|
||||
{
|
||||
xpos = 0;
|
||||
case '\n': {
|
||||
ypos += FONT_HEIGHT;
|
||||
if (ypos > bootboot.fb_height)
|
||||
if ((ypos + FONT_HEIGHT) >= bootboot.fb_height)
|
||||
{
|
||||
memcpy((void*)bootboot.fb_ptr, (char*)bootboot.fb_ptr + (bootboot.fb_scanline * FONT_HEIGHT),
|
||||
bootboot.fb_size - (bootboot.fb_scanline * FONT_HEIGHT));
|
||||
ypos -= FONT_HEIGHT;
|
||||
framebuffer0.paint_rect(0, ypos, bootboot.fb_width, FONT_HEIGHT, Color::Black);
|
||||
}
|
||||
xpos = 0;
|
||||
break;
|
||||
}
|
||||
case '\r': xpos = 0; break;
|
||||
case '\b':
|
||||
if (xpos != 0)
|
||||
{
|
||||
xpos -= FONT_WIDTH;
|
||||
framebuffer0.paint_rect(xpos, ypos, FONT_WIDTH, FONT_HEIGHT, Color::Black);
|
||||
}
|
||||
break;
|
||||
case '\033': g_escape_sequence = true; break;
|
||||
default: {
|
||||
putchar_at_offset(chr, xpos, ypos, fgColor, bgColor);
|
||||
xpos += FONT_WIDTH;
|
||||
if ((xpos + FONT_WIDTH) > bootboot.fb_width)
|
||||
{
|
||||
xpos = 0;
|
||||
ypos += FONT_HEIGHT;
|
||||
if ((ypos + FONT_HEIGHT) >= bootboot.fb_height)
|
||||
{
|
||||
memcpy((void*)bootboot.fb_ptr, (char*)bootboot.fb_ptr + (bootboot.fb_scanline * FONT_HEIGHT),
|
||||
bootboot.fb_size - (bootboot.fb_scanline * FONT_HEIGHT));
|
||||
ypos -= FONT_HEIGHT;
|
||||
framebuffer0.paint_rect(0, ypos, bootboot.fb_width, FONT_HEIGHT, Color::Black);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,6 +228,7 @@ int sprintf(char* __s, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
if (__s) *__s = 0;
|
||||
int written = internal_printf(
|
||||
fmt,
|
||||
[&](const char* s) {
|
||||
@ -242,6 +243,7 @@ int snprintf(char* __s, size_t max, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
if (__s && max) *__s = 0;
|
||||
int written = internal_printf(
|
||||
fmt,
|
||||
[&](const char* s) {
|
||||
@ -266,6 +268,7 @@ int vkprintf(const char* fmt, va_list ap)
|
||||
|
||||
int vsprintf(char* __s, const char* fmt, va_list ap)
|
||||
{
|
||||
*__s = 0;
|
||||
return internal_printf(
|
||||
fmt,
|
||||
[&](const char* s) {
|
||||
@ -276,6 +279,7 @@ int vsprintf(char* __s, const char* fmt, va_list ap)
|
||||
|
||||
int vsnprintf(char* __s, size_t max, const char* fmt, va_list ap)
|
||||
{
|
||||
if (max) *__s = 0;
|
||||
return internal_printf(
|
||||
fmt,
|
||||
[&](const char* s) {
|
||||
|
@ -15,27 +15,36 @@ void Syscall::entry(Context* context)
|
||||
case SYS_yield: sys_yield(context); break;
|
||||
case SYS_sleep: sys_sleep(context, context->rdi); break;
|
||||
case SYS_write: sys_write(context, (int)context->rdi, context->rsi, (const char*)context->rdx); break;
|
||||
case SYS_paint: sys_paint(context, context->rdi, context->rsi, context->rdx, context->r10, context->r8); break;
|
||||
case SYS_getprocid: sys_getprocid(context, (int)context->rdi); break;
|
||||
case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
|
||||
case SYS_mmap:
|
||||
sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx, (int)context->r10, (off_t)context->r8);
|
||||
break;
|
||||
case SYS_munmap: sys_munmap(context, (void*)context->rdi, context->rsi); break;
|
||||
case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break;
|
||||
case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi, (mode_t)context->rdx); break;
|
||||
case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break;
|
||||
case SYS_close: sys_close(context, (int)context->rdi); break;
|
||||
case SYS_seek: sys_seek(context, (int)context->rdi, (long)context->rsi, (int)context->rdx); break;
|
||||
case SYS_exec: sys_exec(context, (const char*)context->rdi); break;
|
||||
case SYS_execv: sys_execv(context, (const char*)context->rdi, (char**)context->rsi); break;
|
||||
case SYS_fcntl: sys_fcntl(context, (int)context->rdi, (int)context->rsi, context->rdx); break;
|
||||
case SYS_mprotect: sys_mprotect(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
|
||||
case SYS_clock: sys_clock(context); break;
|
||||
case SYS_mkdir: sys_mkdir(context, (const char*)context->rdi); break;
|
||||
case SYS_clock_gettime: sys_clock_gettime(context, (int)context->rdi, (struct timespec*)context->rsi); break;
|
||||
case SYS_mkdir: sys_mkdir(context, (const char*)context->rdi, (mode_t)context->rsi); break;
|
||||
case SYS_fork: sys_fork(context); break;
|
||||
case SYS_waitpid: sys_waitpid(context, (long)context->rdi, (int*)context->rsi, (int)context->rdx); break;
|
||||
case SYS_access: sys_access(context, (const char*)context->rdi, (int)context->rsi); break;
|
||||
case SYS_fstat: sys_fstat(context, (int)context->rdi, (struct stat*)context->rsi); break;
|
||||
case SYS_stat: sys_stat(context, (const char*)context->rdi, (struct stat*)context->rsi); break;
|
||||
case SYS_pstat: sys_pstat(context, (long)context->rdi, (struct pstat*)context->rsi); break;
|
||||
case SYS_getdents:
|
||||
sys_getdents(context, (int)context->rdi, (struct luna_dirent*)context->rsi, (size_t)context->rdx);
|
||||
break;
|
||||
case SYS_dup2: sys_dup2(context, (int)context->rdi, (int)context->rsi); break;
|
||||
case SYS_setuid: sys_setuid(context, (uid_t)context->rdi); break;
|
||||
case SYS_setgid: sys_setgid(context, (gid_t)context->rdi); break;
|
||||
case SYS_umask: sys_umask(context, (mode_t)context->rdi); break;
|
||||
case SYS_ioctl: sys_ioctl(context, (int)context->rdi, (int)context->rsi, (uintptr_t)context->rdx); break;
|
||||
case SYS_seteuid: sys_seteuid(context, (uid_t)context->rdi); break;
|
||||
case SYS_setegid: sys_setegid(context, (gid_t)context->rdi); break;
|
||||
default: context->rax = -ENOSYS; break;
|
||||
}
|
||||
VMM::exit_syscall_context();
|
||||
|
@ -1,10 +1,212 @@
|
||||
#include "sys/UserMemory.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
#include "utils/Addresses.h"
|
||||
|
||||
char* strdup_from_user(
|
||||
const char* user_string) // FIXME: This function is a little hacky. Use the obtain_user_ref and similar functions.
|
||||
struct dynamic_string
|
||||
{
|
||||
uint64_t phys = VMM::get_physical((uint64_t)user_string);
|
||||
if (phys == (uint64_t)-1) { return nullptr; }
|
||||
return strdup((const char*)phys);
|
||||
char* buf;
|
||||
long capacity;
|
||||
long size;
|
||||
};
|
||||
|
||||
bool dynamic_expand(dynamic_string* str, long new_capacity)
|
||||
{
|
||||
char* buffer = (char*)krealloc(str->buf, new_capacity);
|
||||
if (!buffer) { return false; }
|
||||
str->buf = buffer;
|
||||
str->capacity = new_capacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dynamic_push(dynamic_string* str, char c)
|
||||
{
|
||||
if (str->size == str->capacity)
|
||||
{
|
||||
if (!dynamic_expand(str, str->capacity + 16)) return false;
|
||||
}
|
||||
str->buf[str->size] = c;
|
||||
str->size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dynamic_init(dynamic_string* str)
|
||||
{
|
||||
str->buf = (char*)kmalloc(10);
|
||||
if (!str->buf) return false;
|
||||
str->capacity = 10;
|
||||
str->size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validate_user_readable_page(uintptr_t address)
|
||||
{
|
||||
if (Memory::is_kernel_address(address)) return false;
|
||||
auto rc = VMM::get_flags(address);
|
||||
if (rc == (uint64_t)-1) return false;
|
||||
if (rc & MAP_USER) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool validate_user_writable_page(uintptr_t address)
|
||||
{
|
||||
if (Memory::is_kernel_address(address)) return false;
|
||||
auto rc = VMM::get_flags(address);
|
||||
if (rc == (uint64_t)-1) return false;
|
||||
if (rc & (MAP_USER | MAP_READ_WRITE)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
char* strdup_from_user(const char* user_string)
|
||||
{
|
||||
uintptr_t user_ptr = (uintptr_t)user_string;
|
||||
auto aligned = round_down_to_nearest_page(user_ptr);
|
||||
char* ptr = nullptr;
|
||||
uintptr_t index = 0;
|
||||
if (aligned != user_ptr) // Otherwise, we already do this check below.
|
||||
{
|
||||
if (!validate_user_readable_page(aligned)) return nullptr;
|
||||
ptr = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(aligned), 0);
|
||||
index = user_ptr - aligned;
|
||||
}
|
||||
dynamic_string str;
|
||||
if (!dynamic_init(&str))
|
||||
{
|
||||
if (ptr) MemoryManager::release_mapping(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
while (true) // FIXME: set a limit for this and fail with ENAMETOOLONG otherwise.
|
||||
{
|
||||
if (user_ptr % PAGE_SIZE == 0)
|
||||
{
|
||||
index = 0;
|
||||
if (ptr) MemoryManager::release_mapping(ptr);
|
||||
if (!validate_user_readable_page(user_ptr))
|
||||
{
|
||||
kfree(str.buf);
|
||||
return nullptr;
|
||||
}
|
||||
ptr = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(user_ptr), 0);
|
||||
}
|
||||
char c = ptr[index];
|
||||
if (!dynamic_push(&str, c))
|
||||
{
|
||||
MemoryManager::release_mapping(ptr);
|
||||
kfree(str.buf);
|
||||
return nullptr;
|
||||
}
|
||||
if (!c) // We reached the null terminator!!
|
||||
{
|
||||
MemoryManager::release_mapping(ptr);
|
||||
return str.buf;
|
||||
}
|
||||
user_ptr++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
bool validate_user_read(uintptr_t address, size_t size)
|
||||
{
|
||||
auto aligned = round_down_to_nearest_page(address);
|
||||
if (aligned != address) // Otherwise, we already do this check below.
|
||||
{
|
||||
if (!validate_user_readable_page(aligned)) return false;
|
||||
}
|
||||
while (size--)
|
||||
{
|
||||
if (address % PAGE_SIZE == 0)
|
||||
{
|
||||
if (!validate_user_readable_page(address)) return false;
|
||||
}
|
||||
address++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validate_user_write(uintptr_t address, size_t size)
|
||||
{
|
||||
auto aligned = round_down_to_nearest_page(address);
|
||||
if (aligned != address) // Otherwise, we already do this check below.
|
||||
{
|
||||
if (!validate_user_writable_page(aligned)) return false;
|
||||
}
|
||||
while (size--)
|
||||
{
|
||||
if (address % PAGE_SIZE == 0)
|
||||
{
|
||||
if (!validate_user_writable_page(address)) return false;
|
||||
}
|
||||
address++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool do_copy_from_user(const char* uptr, char* ptr, size_t size)
|
||||
{
|
||||
uintptr_t user_ptr = (uintptr_t)uptr;
|
||||
auto aligned = round_down_to_nearest_page(user_ptr);
|
||||
char* mapping = nullptr;
|
||||
uintptr_t index = 0;
|
||||
if (aligned != user_ptr) // Otherwise, we already do this check below.
|
||||
{
|
||||
if (!validate_user_readable_page(aligned)) return false;
|
||||
mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(aligned), 0);
|
||||
index = user_ptr - aligned;
|
||||
}
|
||||
while (size--)
|
||||
{
|
||||
if (user_ptr % PAGE_SIZE == 0)
|
||||
{
|
||||
if (mapping) MemoryManager::release_mapping(mapping);
|
||||
index = 0;
|
||||
if (!validate_user_readable_page(user_ptr)) return false;
|
||||
mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(user_ptr), 0);
|
||||
}
|
||||
*ptr = mapping[index];
|
||||
user_ptr++;
|
||||
ptr++;
|
||||
index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool do_copy_to_user(char* uptr, const char* ptr, size_t size)
|
||||
{
|
||||
uintptr_t user_ptr = (uintptr_t)uptr;
|
||||
auto aligned = round_down_to_nearest_page(user_ptr);
|
||||
char* mapping = nullptr;
|
||||
uintptr_t index = 0;
|
||||
if (aligned != user_ptr) // Otherwise, we already do this check below.
|
||||
{
|
||||
if (!validate_user_writable_page(aligned)) return false;
|
||||
mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(aligned));
|
||||
index = user_ptr - aligned;
|
||||
}
|
||||
while (size--)
|
||||
{
|
||||
if (user_ptr % PAGE_SIZE == 0)
|
||||
{
|
||||
if (mapping) MemoryManager::release_mapping(mapping);
|
||||
index = 0;
|
||||
if (!validate_user_writable_page(user_ptr)) return false;
|
||||
mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(user_ptr));
|
||||
}
|
||||
mapping[index] = *ptr;
|
||||
user_ptr++;
|
||||
ptr++;
|
||||
index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool copy_from_user(const void* user_ptr, void* ptr, size_t size)
|
||||
{
|
||||
return do_copy_from_user((const char*)user_ptr, (char*)ptr, size);
|
||||
}
|
||||
|
||||
bool copy_to_user(void* user_ptr, const void* ptr, size_t size)
|
||||
{
|
||||
return do_copy_to_user((char*)user_ptr, (const char*)ptr, size);
|
||||
}
|
@ -1,9 +1,83 @@
|
||||
#include "bootboot.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "std/errno.h"
|
||||
#include "sys/UserMemory.h"
|
||||
#include "thread/PIT.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "utils/Time.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
void sys_clock(Context* context)
|
||||
static uint64_t unix_boot_time;
|
||||
|
||||
#define CLOCK_REALTIME 0
|
||||
#define CLOCK_MONOTONIC 1
|
||||
#define CLOCK_PROCTIME 2
|
||||
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
};
|
||||
|
||||
struct timespec
|
||||
{
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
};
|
||||
|
||||
static void ms_to_timespec(long ms, struct timespec* tv)
|
||||
{
|
||||
tv->tv_sec = ms / 1000;
|
||||
tv->tv_nsec = (ms % 1000) * 1000000;
|
||||
}
|
||||
|
||||
void sys_clock_gettime(Context* context, int clock, struct timespec* tp)
|
||||
{
|
||||
struct timespec* ktp = obtain_user_ref(tp);
|
||||
if (!ktp)
|
||||
{
|
||||
context->rax = -EFAULT; // FIXME: Not sure if clock_gettime can return EFAULT.
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
context->rax = (long)current_task->cpu_time;
|
||||
switch (clock)
|
||||
{
|
||||
case CLOCK_REALTIME: {
|
||||
ms_to_timespec(PIT::ms_since_boot, ktp);
|
||||
ktp->tv_sec += unix_boot_time;
|
||||
break;
|
||||
}
|
||||
case CLOCK_MONOTONIC: {
|
||||
ms_to_timespec(PIT::ms_since_boot, ktp);
|
||||
break;
|
||||
}
|
||||
case CLOCK_PROCTIME: {
|
||||
ms_to_timespec(current_task->cpu_time, ktp);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
release_user_ref(ktp);
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
release_user_ref(ktp);
|
||||
context->rax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
extern BOOTBOOT bootboot;
|
||||
|
||||
void clock_init()
|
||||
{
|
||||
unix_boot_time = unix_boottime(bootboot.datetime);
|
||||
}
|
||||
|
||||
uint64_t clock_now()
|
||||
{
|
||||
return unix_boot_time + (PIT::ms_since_boot / 1000);
|
||||
}
|
||||
|
||||
uint64_t clock_boot()
|
||||
{
|
||||
return unix_boot_time;
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
#define MODULE "elf"
|
||||
|
||||
#include "sys/elf/ELFLoader.h"
|
||||
#include "fs/InitRD.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "misc/utils.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
@ -25,6 +25,16 @@ static const char* format_permissions(uint32_t flags)
|
||||
return perms;
|
||||
}
|
||||
|
||||
static bool can_execute_segment(int flags)
|
||||
{
|
||||
return flags & 1;
|
||||
}
|
||||
|
||||
static bool can_write_segment(int flags)
|
||||
{
|
||||
return flags & 2;
|
||||
}
|
||||
|
||||
ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename)
|
||||
{
|
||||
VFS::Node* node = VFS::resolve_path(filename);
|
||||
@ -67,15 +77,15 @@ long ELFLoader::check_elf_image_from_filesystem(const char* filename)
|
||||
ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
|
||||
{
|
||||
Elf64_Ehdr elf_ehdr;
|
||||
ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0);
|
||||
ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) ==
|
||||
ensure(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0);
|
||||
ensure(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) ==
|
||||
0); // If you haven't checked the ELF executable with check_elf_image() first, then an assertion fail is your
|
||||
// fault =D
|
||||
ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64);
|
||||
ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
|
||||
ASSERT(elf_ehdr.e_type == ET_EXEC);
|
||||
ASSERT(elf_ehdr.e_machine == EM_MACH);
|
||||
ASSERT(elf_ehdr.e_phnum != 0);
|
||||
ensure(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64);
|
||||
ensure(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
|
||||
ensure(elf_ehdr.e_type == ET_EXEC);
|
||||
ensure(elf_ehdr.e_machine == EM_MACH);
|
||||
ensure(elf_ehdr.e_phnum != 0);
|
||||
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
|
||||
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
|
||||
image->entry = elf_ehdr.e_entry;
|
||||
@ -88,7 +98,9 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
|
||||
{
|
||||
kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s", phdr.p_vaddr,
|
||||
phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags));
|
||||
ASSERT(phdr.p_vaddr);
|
||||
ensure(phdr.p_vaddr);
|
||||
|
||||
ensure(!(can_write_segment(phdr.p_flags) && can_execute_segment(phdr.p_flags)));
|
||||
|
||||
uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr.p_vaddr % PAGE_SIZE) + phdr.p_memsz);
|
||||
void* buffer = (void*)((uint64_t)MemoryManager::get_pages_at(round_down_to_nearest_page(phdr.p_vaddr),
|
||||
@ -105,7 +117,12 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
|
||||
VMM::apply_address_space();
|
||||
VMM::switch_to_previous_user_address_space();
|
||||
|
||||
MemoryManager::protect(buffer, pages, phdr.p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER);
|
||||
int new_flags = MAP_USER | MAP_AS_OWNED_BY_TASK;
|
||||
if (can_write_segment(phdr.p_flags)) new_flags |= MAP_READ_WRITE;
|
||||
else if (can_execute_segment(phdr.p_flags))
|
||||
new_flags |= MAP_EXEC;
|
||||
|
||||
MemoryManager::protect(buffer, pages, new_flags);
|
||||
|
||||
image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
|
||||
(image->section_count + 1) * sizeof(ELFSection));
|
||||
@ -116,7 +133,7 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
|
||||
}
|
||||
else { kdbgln("skipping non-loadable segment"); }
|
||||
}
|
||||
ASSERT(image->section_count);
|
||||
ensure(image->section_count);
|
||||
return image;
|
||||
}
|
||||
|
||||
@ -177,6 +194,11 @@ long ELFLoader::check_elf_image(VFS::Node* node)
|
||||
kerrorln("trying to load ELF into kernel memory");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (can_write_segment(phdr.p_flags) && can_execute_segment(phdr.p_flags))
|
||||
{
|
||||
kwarnln("executable violates W^X");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
loadable_sections++;
|
||||
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE;
|
||||
}
|
||||
|
@ -2,10 +2,11 @@
|
||||
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
@ -44,6 +45,11 @@ void sys_fork(Context* context)
|
||||
|
||||
child->ppid = parent->id;
|
||||
|
||||
child->uid = parent->uid;
|
||||
child->euid = parent->euid;
|
||||
child->gid = parent->gid;
|
||||
child->egid = parent->egid;
|
||||
|
||||
child->regs.rax = 0;
|
||||
context->rax = child->id;
|
||||
|
||||
@ -56,7 +62,16 @@ void sys_fork(Context* context)
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_exec(Context* context, const char* pathname)
|
||||
void push_on_user_stack(uint64_t* rsp, char* value,
|
||||
size_t size) // FIXME: Handle segments of stack that extend beyond one page.
|
||||
{
|
||||
(*rsp) -= size;
|
||||
char* kvalue = (char*)VMM::get_physical(*rsp);
|
||||
ensure(kvalue != (char*)UINT64_MAX);
|
||||
memcpy(kvalue, value, size);
|
||||
}
|
||||
|
||||
void sys_execv(Context* context, const char* pathname, char** argv)
|
||||
{
|
||||
char* kpathname = strdup_from_user(pathname);
|
||||
if (!kpathname)
|
||||
@ -82,6 +97,13 @@ void sys_exec(Context* context, const char* pathname)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VFS::can_execute(program, Scheduler::current_task()->euid, Scheduler::current_task()->egid))
|
||||
{
|
||||
kfree(kpathname);
|
||||
context->rax = -EACCES;
|
||||
return;
|
||||
}
|
||||
|
||||
long memusage;
|
||||
if ((memusage = ELFLoader::check_elf_image(program)) < 0)
|
||||
{
|
||||
@ -97,11 +119,88 @@ void sys_exec(Context* context, const char* pathname)
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t kargc = 0;
|
||||
char** kargv = nullptr;
|
||||
char* arg;
|
||||
|
||||
auto free_kernel_argv_copy = [&]() {
|
||||
for (uint64_t i = 0; i < kargc; i++)
|
||||
{
|
||||
if (kargv[i]) kfree(kargv[i]);
|
||||
}
|
||||
if (kargv) kfree(kargv);
|
||||
};
|
||||
|
||||
// FIXME: This code is a bit messy. Should probably be refactored and moved into a separate function.
|
||||
|
||||
do {
|
||||
if (!copy_from_user(argv, &arg, sizeof(char*)))
|
||||
{
|
||||
free_kernel_argv_copy();
|
||||
context->rax = -EFAULT;
|
||||
return;
|
||||
}
|
||||
kargv = (char**)krealloc(kargv, (kargc + 1) * sizeof(char*)); // we need a vector class for the kernel.
|
||||
if (!kargv)
|
||||
{
|
||||
free_kernel_argv_copy();
|
||||
context->rax = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
if (arg)
|
||||
{
|
||||
char* kcopy = strdup_from_user(arg);
|
||||
if (!kcopy) // FIXME: This could also be EFAULT.
|
||||
{
|
||||
free_kernel_argv_copy();
|
||||
context->rax = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
kargv[kargc] = kcopy;
|
||||
}
|
||||
else
|
||||
{
|
||||
kargv[kargc] = nullptr;
|
||||
break;
|
||||
}
|
||||
kargc++;
|
||||
argv++;
|
||||
} while (arg != nullptr);
|
||||
|
||||
kinfoln("Copied %lu arguments from user process", kargc);
|
||||
|
||||
size_t stack_size = 0;
|
||||
for (uint64_t i = 0; i <= kargc; i++)
|
||||
{
|
||||
stack_size += sizeof(char*);
|
||||
if (kargv[i])
|
||||
{
|
||||
stack_size += strlen(kargv[i]) + 1; // count the null byte
|
||||
}
|
||||
}
|
||||
|
||||
if (stack_size >
|
||||
((TASK_PAGES_IN_STACK / 2) *
|
||||
PAGE_SIZE)) // FIXME: Maybe we should allocate a larger stack in this case, but still set a larger upper limit.
|
||||
{
|
||||
free_kernel_argv_copy();
|
||||
context->rax = -E2BIG;
|
||||
return;
|
||||
}
|
||||
|
||||
char** user_argv = (char**)kcalloc(kargc + 1, sizeof(char*));
|
||||
if (!user_argv)
|
||||
{
|
||||
free_kernel_argv_copy();
|
||||
context->rax = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
Interrupts::disable();
|
||||
ASSERT(!Interrupts::are_enabled()); // This part is pretty sensitive.
|
||||
ensure(!Interrupts::are_enabled()); // This part is pretty sensitive.
|
||||
|
||||
Task* task = Scheduler::current_task();
|
||||
ASSERT(task);
|
||||
ensure(task);
|
||||
|
||||
// At this point, pretty much nothing can fail.
|
||||
|
||||
@ -112,13 +211,16 @@ void sys_exec(Context* context, const char* pathname)
|
||||
task->address_space.clear();
|
||||
task->allocated_stack = (uint64_t)MemoryManager::get_pages_at(
|
||||
0x100000, TASK_PAGES_IN_STACK,
|
||||
MAP_USER | MAP_READ_WRITE); // If we had enough space for the old stack, there should be enough space for the
|
||||
// new stack.
|
||||
MAP_USER | MAP_READ_WRITE | MAP_AS_OWNED_BY_TASK); // If we had enough space for the old stack, there should be
|
||||
// enough space for the new stack.
|
||||
|
||||
ELFImage* image = ELFLoader::load_elf_from_vfs(program);
|
||||
ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
|
||||
ensure(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
|
||||
// wrong.
|
||||
|
||||
if (VFS::is_setuid(program)) task->euid = program->uid;
|
||||
if (VFS::is_setgid(program)) task->egid = program->gid;
|
||||
|
||||
strlcpy(task->name, kpathname, sizeof(task->name));
|
||||
|
||||
Scheduler::reset_task(task, image);
|
||||
@ -129,9 +231,29 @@ void sys_exec(Context* context, const char* pathname)
|
||||
if (file.close_on_exec()) { file.close(); }
|
||||
}
|
||||
|
||||
task->restore_context(context);
|
||||
for (uint64_t i = 0; i <= kargc; i++)
|
||||
{
|
||||
if (kargv[i])
|
||||
{
|
||||
push_on_user_stack(&task->regs.rsp, kargv[i], strlen(kargv[i]) + 1);
|
||||
user_argv[i] = (char*)task->regs.rsp;
|
||||
}
|
||||
else
|
||||
user_argv[i] = nullptr;
|
||||
}
|
||||
push_on_user_stack(&task->regs.rsp, (char*)user_argv, (kargc + 1) * sizeof(char*));
|
||||
task->regs.rdi = kargc; // argc
|
||||
task->regs.rsi = task->regs.rsp; // argv
|
||||
|
||||
task->regs.rsp &= (UINT64_MAX ^ 15); // align it
|
||||
|
||||
free_kernel_argv_copy();
|
||||
|
||||
kfree(user_argv);
|
||||
|
||||
kfree(kpathname);
|
||||
|
||||
task->restore_context(context);
|
||||
|
||||
return;
|
||||
}
|
@ -1,8 +1,13 @@
|
||||
#include "std/errno.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
#define ID_PID 0
|
||||
#define ID_PPID 1
|
||||
#define ID_UID 2
|
||||
#define ID_EUID 3
|
||||
#define ID_GID 4
|
||||
#define ID_EGID 5
|
||||
|
||||
void sys_getprocid(Context* context, int field)
|
||||
{
|
||||
@ -16,9 +21,103 @@ void sys_getprocid(Context* context, int field)
|
||||
context->rax = Scheduler::current_task()->ppid;
|
||||
return;
|
||||
}
|
||||
else if (field == ID_UID)
|
||||
{
|
||||
context->rax = Scheduler::current_task()->uid;
|
||||
return;
|
||||
}
|
||||
else if (field == ID_EUID)
|
||||
{
|
||||
context->rax = Scheduler::current_task()->euid;
|
||||
return;
|
||||
}
|
||||
else if (field == ID_GID)
|
||||
{
|
||||
context->rax = Scheduler::current_task()->gid;
|
||||
return;
|
||||
}
|
||||
else if (field == ID_EGID)
|
||||
{
|
||||
context->rax = Scheduler::current_task()->egid;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void sys_setuid(Context* context, uid_t uid)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
|
||||
if (!current_task->is_superuser())
|
||||
{
|
||||
if (uid != current_task->uid && uid != current_task->euid)
|
||||
{
|
||||
context->rax = -EPERM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
current_task->uid = uid;
|
||||
current_task->euid = uid;
|
||||
|
||||
context->rax = 0;
|
||||
}
|
||||
|
||||
void sys_seteuid(Context* context, uid_t euid)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
|
||||
if (!current_task->is_superuser())
|
||||
{
|
||||
if (euid != current_task->uid)
|
||||
{
|
||||
context->rax = -EPERM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
current_task->euid = euid;
|
||||
|
||||
context->rax = 0;
|
||||
}
|
||||
|
||||
void sys_setgid(Context* context, gid_t gid)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
|
||||
if (!current_task->is_superuser())
|
||||
{
|
||||
if (gid != current_task->gid && gid != current_task->egid)
|
||||
{
|
||||
context->rax = -EPERM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
current_task->gid = gid;
|
||||
current_task->egid = gid;
|
||||
|
||||
context->rax = 0;
|
||||
}
|
||||
|
||||
void sys_setegid(Context* context, gid_t egid)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
|
||||
if (!current_task->is_superuser())
|
||||
{
|
||||
if (egid != current_task->gid)
|
||||
{
|
||||
context->rax = -EPERM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
current_task->egid = egid;
|
||||
|
||||
context->rax = 0;
|
||||
}
|
@ -10,30 +10,34 @@
|
||||
#include "thread/Scheduler.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAP_READ 1
|
||||
#define MAP_WRITE 2
|
||||
#define MAP_NONE 0
|
||||
#define PROT_READ 1
|
||||
#define PROT_WRITE 2
|
||||
#define PROT_NONE 0
|
||||
#define PROT_EXEC 4
|
||||
|
||||
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
|
||||
|
||||
static const char* format_prot(int prot)
|
||||
{
|
||||
static char prot_string[3];
|
||||
prot_string[2] = 0;
|
||||
prot_string[0] = ((prot & MAP_READ) > 0) ? 'r' : '-';
|
||||
prot_string[1] = ((prot & MAP_WRITE) > 0) ? 'w' : '-';
|
||||
static char prot_string[4];
|
||||
prot_string[3] = 0;
|
||||
prot_string[0] = ((prot & PROT_READ) > 0) ? 'r' : '-';
|
||||
prot_string[1] = ((prot & PROT_WRITE) > 0) ? 'w' : '-';
|
||||
prot_string[2] = ((prot & PROT_EXEC) > 0) ? 'x' : '-';
|
||||
return prot_string;
|
||||
}
|
||||
|
||||
static int mman_flags_from_prot(int prot)
|
||||
{
|
||||
prot &= 0b11;
|
||||
if (prot == MAP_NONE) return 0;
|
||||
if ((prot & MAP_WRITE) > 0) return MAP_USER | MAP_READ_WRITE;
|
||||
return MAP_USER;
|
||||
prot &= 0b111;
|
||||
int flags = MAP_USER | MAP_AS_OWNED_BY_TASK;
|
||||
if (prot == PROT_NONE) return MAP_AS_OWNED_BY_TASK;
|
||||
if ((prot & PROT_WRITE) > 0) { flags |= MAP_READ_WRITE; }
|
||||
if ((prot & PROT_EXEC) > 0) { flags |= MAP_EXEC; }
|
||||
return flags;
|
||||
}
|
||||
|
||||
void sys_mmap(Context* context, void* address, size_t size, int prot)
|
||||
void sys_mmap(Context* context, void* address, size_t size, int prot, int fd, off_t offset)
|
||||
{
|
||||
if (size < PAGE_SIZE)
|
||||
{
|
||||
@ -50,7 +54,7 @@ void sys_mmap(Context* context, void* address, size_t size, int prot)
|
||||
int real_flags = mman_flags_from_prot(prot);
|
||||
if (address)
|
||||
{
|
||||
kdbgln("mmap(): %ld pages at address %p, %s", size / PAGE_SIZE, address, format_prot(prot));
|
||||
kdbgln("mmap(): %ld pages at address %p, %s, fd %d", size / PAGE_SIZE, address, format_prot(prot), fd);
|
||||
if (Memory::is_kernel_address((uintptr_t)address))
|
||||
{
|
||||
kwarnln("munmap() failed: attempted to unmap a kernel page");
|
||||
@ -63,8 +67,20 @@ void sys_mmap(Context* context, void* address, size_t size, int prot)
|
||||
context->rax = MAP_FAIL(ENOMEM);
|
||||
return;
|
||||
}
|
||||
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
||||
void* result = MemoryManager::get_pages_at((uint64_t)address - offset,
|
||||
uint64_t addr_offset = (uint64_t)address % PAGE_SIZE;
|
||||
if (fd >= 0)
|
||||
{
|
||||
int err;
|
||||
Descriptor* file = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = MAP_FAIL(err);
|
||||
return;
|
||||
}
|
||||
context->rax = file->mmap((uint64_t)address - addr_offset, size, real_flags, offset);
|
||||
return;
|
||||
}
|
||||
void* result = MemoryManager::get_pages_at((uint64_t)address - addr_offset,
|
||||
Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
|
||||
if (result)
|
||||
{
|
||||
@ -79,7 +95,8 @@ void sys_mmap(Context* context, void* address, size_t size, int prot)
|
||||
return;
|
||||
}
|
||||
}
|
||||
kdbgln("mmap(): %ld pages at any address, %s", Utilities::get_blocks_from_size(PAGE_SIZE, size), format_prot(prot));
|
||||
kdbgln("mmap(): %ld pages at any address, %s, fd %d", Utilities::get_blocks_from_size(PAGE_SIZE, size),
|
||||
format_prot(prot), fd);
|
||||
uint64_t ptr =
|
||||
Scheduler::current_task()->allocator.request_virtual_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
if (!ptr)
|
||||
@ -88,6 +105,18 @@ void sys_mmap(Context* context, void* address, size_t size, int prot)
|
||||
context->rax = MAP_FAIL(ENOMEM);
|
||||
return;
|
||||
}
|
||||
if (fd >= 0)
|
||||
{
|
||||
int err;
|
||||
Descriptor* file = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = MAP_FAIL(err);
|
||||
return;
|
||||
}
|
||||
context->rax = file->mmap(ptr, size, real_flags, offset);
|
||||
return;
|
||||
}
|
||||
void* result = MemoryManager::get_pages_at(ptr, Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
|
||||
if (result)
|
||||
{
|
||||
@ -130,8 +159,8 @@ void sys_munmap(Context* context, void* address, size_t size)
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
uint64_t phys = VMM::get_physical((uint64_t)address);
|
||||
if (phys == (uint64_t)-1)
|
||||
uint64_t flags = VMM::get_flags((uint64_t)address);
|
||||
if (flags == (uint64_t)-1)
|
||||
{
|
||||
kwarnln("munmap() failed: attempted to unmap a non-existent page");
|
||||
context->rax = -EINVAL;
|
||||
@ -140,7 +169,12 @@ void sys_munmap(Context* context, void* address, size_t size)
|
||||
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
||||
Scheduler::current_task()->allocator.free_virtual_pages(((uint64_t)address - offset),
|
||||
Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
MemoryManager::release_pages((void*)((uint64_t)address - offset), Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
if (flags & MAP_AS_OWNED_BY_TASK)
|
||||
MemoryManager::release_pages((void*)((uint64_t)address - offset),
|
||||
Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
else
|
||||
MemoryManager::release_unaligned_mappings((void*)((uint64_t)address - offset),
|
||||
Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
kdbgln("munmap() succeeded");
|
||||
context->rax = 0;
|
||||
return;
|
||||
@ -174,8 +208,8 @@ void sys_mprotect(Context* context, void* address, size_t size, int prot)
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
uint64_t phys = VMM::get_physical((uint64_t)address);
|
||||
if (phys == (uint64_t)-1)
|
||||
uint64_t flags = VMM::get_flags((uint64_t)address);
|
||||
if (flags == (uint64_t)-1)
|
||||
{
|
||||
kwarnln("mprotect() failed: attempted to protect a non-existent page");
|
||||
context->rax = -EINVAL;
|
||||
@ -184,7 +218,8 @@ void sys_mprotect(Context* context, void* address, size_t size, int prot)
|
||||
|
||||
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
||||
MemoryManager::protect((void*)((uint64_t)address - offset), Utilities::get_blocks_from_size(PAGE_SIZE, size),
|
||||
mman_flags_from_prot(prot));
|
||||
flags & MAP_AS_OWNED_BY_TASK ? mman_flags_from_prot(prot)
|
||||
: mman_flags_from_prot(prot) & ~(MAP_AS_OWNED_BY_TASK));
|
||||
kdbgln("mprotect() succeeded");
|
||||
context->rax = 0;
|
||||
return;
|
||||
|
@ -1,27 +0,0 @@
|
||||
#include "bootboot.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "render/Framebuffer.h"
|
||||
#include "std/errno.h"
|
||||
#include <stdint.h>
|
||||
|
||||
extern BOOTBOOT bootboot;
|
||||
|
||||
void sys_paint(Context* context, uint64_t x, uint64_t y, uint64_t w, uint64_t h, uint64_t c)
|
||||
{
|
||||
if ((x + w) > bootboot.fb_width)
|
||||
{
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
if ((y + h) > bootboot.fb_height)
|
||||
{
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t color = (uint32_t)c;
|
||||
|
||||
framebuffer0.paint_rect((uint32_t)x, (uint32_t)y, (uint32_t)w, (uint32_t)h, Color::from_integer(color));
|
||||
|
||||
context->rax = 0;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
#include "fs/VFS.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "sys/UserMemory.h"
|
||||
#include "thread/Scheduler.h"
|
||||
|
||||
typedef unsigned long off_t;
|
||||
typedef unsigned short mode_t;
|
||||
typedef unsigned long ino_t;
|
||||
|
||||
@ -13,8 +13,37 @@ struct stat // FIXME: This struct is quite stubbed out.
|
||||
ino_t st_ino;
|
||||
mode_t st_mode;
|
||||
off_t st_size;
|
||||
int st_dev; // FIXME: Implement this.
|
||||
int st_nlink; // FIXME: Implement this.
|
||||
uid_t st_uid;
|
||||
gid_t st_gid;
|
||||
time_t st_atime;
|
||||
time_t st_mtime;
|
||||
time_t st_ctime;
|
||||
};
|
||||
|
||||
void do_stat(Context* context, VFS::Node* node, struct stat* buf)
|
||||
{
|
||||
struct stat* kstat = obtain_user_ref(buf);
|
||||
if (!kstat)
|
||||
{
|
||||
context->rax = -EFAULT; // FIXME: The manual doesn't say fstat can return EFAULT, but it seems logical here...
|
||||
return;
|
||||
}
|
||||
kstat->st_ino = node->inode;
|
||||
kstat->st_mode = node->mode | ((1 << (node->type)) * 010000);
|
||||
kstat->st_size = node->length;
|
||||
kstat->st_uid = node->uid;
|
||||
kstat->st_gid = node->gid;
|
||||
kstat->st_atime = node->atime;
|
||||
kstat->st_ctime = node->ctime;
|
||||
kstat->st_mtime = node->mtime;
|
||||
kstat->st_dev = 0;
|
||||
kstat->st_nlink = 0;
|
||||
release_user_ref(kstat);
|
||||
context->rax = 0;
|
||||
}
|
||||
|
||||
void sys_fstat(Context* context, int fd, struct stat* buf)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
@ -29,16 +58,24 @@ void sys_fstat(Context* context, int fd, struct stat* buf)
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
struct stat* kstat = obtain_user_ref(buf);
|
||||
if (!kstat)
|
||||
VFS::Node* node = file.node();
|
||||
return do_stat(context, node, buf);
|
||||
}
|
||||
|
||||
void sys_stat(Context* context, const char* path, struct stat* buf)
|
||||
{
|
||||
char* kpath = strdup_from_user(path);
|
||||
if (!kpath)
|
||||
{
|
||||
context->rax = -EFAULT; // FIXME: The manual doesn't say fstat can return EFAULT, but it seems logical here...
|
||||
context->rax = -EFAULT;
|
||||
return;
|
||||
}
|
||||
VFS::Node* node = file.node();
|
||||
kstat->st_ino = node->inode;
|
||||
kstat->st_mode = (mode_t)node->type;
|
||||
kstat->st_size = node->length;
|
||||
release_user_ref(kstat);
|
||||
context->rax = 0;
|
||||
VFS::Node* node = VFS::resolve_path(kpath);
|
||||
kfree(kpath);
|
||||
if (!node)
|
||||
{
|
||||
context->rax = -ENOENT;
|
||||
return;
|
||||
}
|
||||
return do_stat(context, node, buf);
|
||||
}
|
@ -11,12 +11,17 @@
|
||||
#include "sys/UserMemory.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "thread/Task.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
#define OPEN_READ 1
|
||||
#define OPEN_WRITE 2
|
||||
#define OPEN_NONBLOCK 4
|
||||
#define OPEN_CLOEXEC 8
|
||||
#define OPEN_DIRECTORY 16
|
||||
#define OPEN_TRUNCATED 32
|
||||
#define OPEN_CREATE 64
|
||||
#define OPEN_APPEND 128
|
||||
#define OPEN_EXCL 256
|
||||
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
@ -24,47 +29,64 @@
|
||||
|
||||
#define FCNTL_DUPFD 0
|
||||
#define FCNTL_ISTTY 1
|
||||
#define FCNTL_GETFD 2
|
||||
#define FCNTL_SETFD 3
|
||||
|
||||
#define FD_CLOEXEC 1
|
||||
|
||||
void sys_fcntl(Context* context, int fd, int command, uintptr_t arg)
|
||||
{
|
||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open())
|
||||
int err;
|
||||
Descriptor* file = current_task->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
Descriptor& file = current_task->files[fd];
|
||||
if (command == FCNTL_DUPFD)
|
||||
{
|
||||
if ((int)arg < 0 || (int)arg >= TASK_MAX_FDS)
|
||||
int minfd = (int)arg;
|
||||
if (minfd < 0 || minfd >= TASK_MAX_FDS)
|
||||
{
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
int dupfd = current_task->alloc_fd_greater_than_or_equal((int)arg);
|
||||
int dupfd = current_task->alloc_fd_greater_than_or_equal(minfd);
|
||||
if (dupfd < 0)
|
||||
{
|
||||
context->rax = -EMFILE;
|
||||
return;
|
||||
}
|
||||
current_task->files[dupfd] = file;
|
||||
current_task->files[dupfd] = *file;
|
||||
context->rax = dupfd;
|
||||
kdbgln("fcntl(F_DUPFD): duplicated fd %d, result is %d", fd, dupfd);
|
||||
return;
|
||||
}
|
||||
else if (command == FCNTL_ISTTY)
|
||||
{
|
||||
VFS::Node* node = file.node();
|
||||
VFS::Node* node = file->node();
|
||||
if (node->tty) { context->rax = 1; }
|
||||
else
|
||||
context->rax = -ENOTTY;
|
||||
return;
|
||||
}
|
||||
else if (command == FCNTL_GETFD)
|
||||
{
|
||||
int flags = 0;
|
||||
if (file->close_on_exec()) context->rax |= FD_CLOEXEC;
|
||||
context->rax = flags;
|
||||
return;
|
||||
}
|
||||
else if (command == FCNTL_SETFD)
|
||||
{
|
||||
int flags = (int)arg;
|
||||
if (flags & FD_CLOEXEC) file->set_close_on_exec(true);
|
||||
else
|
||||
file->set_close_on_exec(false);
|
||||
context->rax = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->rax = -EINVAL;
|
||||
@ -79,24 +101,19 @@ void sys_seek(Context* context, int fd, long offset, int whence)
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||
int err;
|
||||
Descriptor* file = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Descriptor& file = current_task->files[fd];
|
||||
long new_offset;
|
||||
if (whence == SEEK_SET) new_offset = offset;
|
||||
else if (whence == SEEK_CUR)
|
||||
new_offset = offset + file.offset();
|
||||
new_offset = offset + file->offset();
|
||||
else if (whence == SEEK_END)
|
||||
new_offset = file.length() + offset;
|
||||
new_offset = file->length() + offset;
|
||||
else
|
||||
__builtin_unreachable();
|
||||
if (new_offset < 0)
|
||||
@ -104,12 +121,12 @@ void sys_seek(Context* context, int fd, long offset, int whence)
|
||||
context->rax = -EINVAL; // FIXME: Is this the right error?
|
||||
return;
|
||||
}
|
||||
if (new_offset == file.offset())
|
||||
if (new_offset == file->offset())
|
||||
{
|
||||
context->rax = new_offset;
|
||||
return;
|
||||
}
|
||||
int result = file.seek(new_offset);
|
||||
int result = file->seek(new_offset);
|
||||
if (result < 0)
|
||||
{
|
||||
context->rax = result;
|
||||
@ -121,34 +138,29 @@ void sys_seek(Context* context, int fd, long offset, int whence)
|
||||
|
||||
void sys_write(Context* context, int fd, size_t size, const char* addr)
|
||||
{
|
||||
if (!addr)
|
||||
if (!validate_user_read((uintptr_t)addr, size))
|
||||
{
|
||||
context->rax = -EFAULT;
|
||||
return;
|
||||
}
|
||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||
int err;
|
||||
Descriptor* file = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
if (!file->can_write())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Descriptor& file = current_task->files[fd];
|
||||
if (!file.can_write())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
ssize_t result = file.write(size, (const char*)VMM::get_physical((uint64_t)addr));
|
||||
ssize_t result = file->user_write(size, addr);
|
||||
context->rax = (size_t)result;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_open(Context* context, const char* filename, int flags)
|
||||
void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXME: mode is not used.
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
int fd = current_task->alloc_fd();
|
||||
@ -168,10 +180,23 @@ void sys_open(Context* context, const char* filename, int flags)
|
||||
VFS::Node* node = VFS::resolve_path(kfilename);
|
||||
if (!node)
|
||||
{
|
||||
bool create = (flags & OPEN_CREATE) > 0;
|
||||
if (create) kwarnln("FIXME: open(O_CREAT) is not implemented");
|
||||
kfree(kfilename);
|
||||
context->rax = -ENOENT;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool excl = (flags & OPEN_EXCL) > 0;
|
||||
|
||||
if (excl)
|
||||
{
|
||||
kfree(kfilename);
|
||||
context->rax = -EEXIST;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool can_read = (flags & OPEN_READ) > 0;
|
||||
bool can_write = (flags & OPEN_WRITE) > 0;
|
||||
@ -182,11 +207,40 @@ void sys_open(Context* context, const char* filename, int flags)
|
||||
return;
|
||||
}
|
||||
|
||||
if (can_read && !VFS::can_read(node, current_task->euid, current_task->egid))
|
||||
{
|
||||
kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for reading",
|
||||
current_task->euid, current_task->egid, kfilename, node->mode);
|
||||
kfree(kfilename);
|
||||
context->rax = -EACCES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (can_write && !VFS::can_write(node, current_task->euid, current_task->egid))
|
||||
{
|
||||
kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for writing",
|
||||
current_task->euid, current_task->egid, kfilename, node->mode);
|
||||
kfree(kfilename);
|
||||
context->rax = -EACCES;
|
||||
return;
|
||||
}
|
||||
|
||||
bool able_to_block = (flags & OPEN_NONBLOCK) == 0;
|
||||
bool close_on_exec = (flags & OPEN_CLOEXEC) > 0;
|
||||
|
||||
bool only_directory = (flags & OPEN_DIRECTORY) > 0;
|
||||
|
||||
bool truncate = (flags & OPEN_TRUNCATED) > 0;
|
||||
if (truncate)
|
||||
{
|
||||
kfree(kfilename);
|
||||
kerrorln("FIXME: open(O_TRUNC) is not implemented");
|
||||
context->rax = -ENOTSUP;
|
||||
return;
|
||||
}
|
||||
|
||||
bool append = (flags & OPEN_APPEND) > 0;
|
||||
|
||||
if (only_directory && node->type != VFS_DIRECTORY)
|
||||
{
|
||||
kfree(kfilename);
|
||||
@ -202,68 +256,71 @@ void sys_open(Context* context, const char* filename, int flags)
|
||||
|
||||
kfree(kfilename);
|
||||
current_task->files[fd].open(node, can_read, can_write, able_to_block, close_on_exec);
|
||||
|
||||
if (append && current_task->files[fd].node()->type != VFS_DEVICE)
|
||||
{
|
||||
current_task->files[fd].seek((long)current_task->files[fd].length());
|
||||
}
|
||||
|
||||
context->rax = fd;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_read(Context* context, int fd, size_t size, char* buffer)
|
||||
{
|
||||
if (!buffer)
|
||||
if (!validate_user_write((uintptr_t)buffer, size))
|
||||
{
|
||||
context->rax = -EFAULT;
|
||||
return;
|
||||
}
|
||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||
int err;
|
||||
Descriptor* file = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
if (!file->can_read())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open() || !current_task->files[fd].can_read())
|
||||
if (VFS::would_block(file->node()))
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
if (VFS::would_block(current_task->files[fd].node()))
|
||||
{
|
||||
if (!current_task->files[fd].able_to_block())
|
||||
if (!file->able_to_block())
|
||||
{
|
||||
context->rax = -EAGAIN;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
current_task->state = current_task->Blocking;
|
||||
current_task->block_reason = BlockReason::Reading;
|
||||
current_task->blocking_read_info.fd = fd;
|
||||
current_task->blocking_read_info.buf = (char*)VMM::get_physical((uint64_t)buffer); // FIXME: Handle errors.
|
||||
current_task->blocking_read_info.buf = buffer;
|
||||
current_task->blocking_read_info.size = size;
|
||||
return Scheduler::task_yield(context);
|
||||
}
|
||||
ssize_t result = current_task->files[fd].read(
|
||||
size, (char*)VMM::get_physical((uint64_t)buffer)); // FIXME: Handle errors, and big buffers which may not be
|
||||
// across continuous physical pages.
|
||||
ssize_t result = file->user_read(size, buffer);
|
||||
context->rax = (size_t)result;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_close(Context* context, int fd)
|
||||
{
|
||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||
int err;
|
||||
Descriptor* file = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
kdbgln("close(): releasing file descriptor %d", fd);
|
||||
current_task->files[fd].close();
|
||||
file->close();
|
||||
context->rax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_mkdir(Context* context, const char* filename)
|
||||
void sys_mkdir(Context* context, const char* filename, mode_t mode)
|
||||
{
|
||||
char* kfilename = strdup_from_user(filename);
|
||||
if (!kfilename)
|
||||
@ -272,7 +329,9 @@ void sys_mkdir(Context* context, const char* filename)
|
||||
return;
|
||||
}
|
||||
|
||||
int rc = VFS::mkdir(kfilename);
|
||||
Task* current_task = Scheduler::current_task();
|
||||
|
||||
int rc = VFS::do_mkdir(kfilename, current_task->euid, current_task->egid, mode & (~current_task->umask));
|
||||
|
||||
kfree(kfilename);
|
||||
|
||||
@ -287,3 +346,44 @@ void sys_access(Context* context, const char* path, int) // FIXME: Use the amode
|
||||
context->rax = 0;
|
||||
kfree(kpath);
|
||||
}
|
||||
|
||||
void sys_dup2(Context* context, int fd, int fd2)
|
||||
{
|
||||
int err;
|
||||
Descriptor* file1 = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file1)
|
||||
{
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
Descriptor* file2 = Scheduler::current_task()->descriptor_from_fd(fd2, err);
|
||||
if (!file2)
|
||||
{
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
if (file2->is_open()) file2->close();
|
||||
*file2 = *file1;
|
||||
kinfoln("dup2(): overwrote fd %d with fd %d", fd2, fd);
|
||||
context->rax = fd2;
|
||||
}
|
||||
|
||||
void sys_umask(Context* context, mode_t cmask)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
context->rax = current_task->umask;
|
||||
current_task->umask = cmask;
|
||||
}
|
||||
|
||||
void sys_ioctl(Context* context, int fd, int cmd, uintptr_t arg)
|
||||
{
|
||||
int err;
|
||||
Descriptor* file = Scheduler::current_task()->open_descriptor_from_fd(fd, err);
|
||||
if (!file)
|
||||
{
|
||||
context->rax = -err;
|
||||
return;
|
||||
}
|
||||
kinfoln("ioctl(): fd %d, cmd %d, arg %lu", fd, cmd, arg);
|
||||
context->rax = file->ioctl(cmd, arg);
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
#include "misc/reboot.h"
|
||||
#include "misc/utils.h"
|
||||
#include "panic/Panic.h"
|
||||
#include "std/assert.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
@ -25,7 +25,7 @@ static uint64_t task_num = 0;
|
||||
|
||||
static Task idle_task;
|
||||
|
||||
static uint64_t free_tid = 0;
|
||||
static uint64_t free_pid = 0;
|
||||
|
||||
static Task* sched_current_task;
|
||||
static Task* base_task;
|
||||
@ -72,7 +72,7 @@ void Scheduler::append_task(Task* task)
|
||||
{
|
||||
if (!base_task)
|
||||
{
|
||||
ASSERT(!end_task);
|
||||
ensure(!end_task);
|
||||
base_task = task;
|
||||
end_task = base_task;
|
||||
task->next_task = task;
|
||||
@ -91,7 +91,7 @@ void Scheduler::append_task(Task* task)
|
||||
void Scheduler::init()
|
||||
{
|
||||
memset(&idle_task, 0, sizeof(Task));
|
||||
idle_task.id = free_tid++;
|
||||
idle_task.id = free_pid++;
|
||||
idle_task.regs.rip = (uint64_t)idle_task_function;
|
||||
idle_task.regs.rsp = get_top_of_stack((uint64_t)MemoryManager::get_page(), 1);
|
||||
idle_task.regs.cs = 0x08;
|
||||
@ -99,6 +99,7 @@ void Scheduler::init()
|
||||
idle_task.regs.rflags = (1 << 21) | (1 << 9);
|
||||
idle_task.task_sleep = 1000;
|
||||
idle_task.user_task = false;
|
||||
idle_task.block_reason = BlockReason::None;
|
||||
idle_task.state = idle_task.Idle;
|
||||
|
||||
strlcpy(idle_task.name, "[cpu-idle]", sizeof(idle_task.name));
|
||||
@ -111,10 +112,11 @@ void Scheduler::init()
|
||||
void Scheduler::add_kernel_task(const char* taskname, void (*task)(void))
|
||||
{
|
||||
Task* new_task = new Task;
|
||||
ASSERT(new_task);
|
||||
ensure(new_task);
|
||||
new_task->user_task = false;
|
||||
new_task->id = free_tid++;
|
||||
new_task->id = free_pid++;
|
||||
new_task->ppid = 0;
|
||||
new_task->uid = new_task->euid = new_task->gid = new_task->egid = 0;
|
||||
new_task->regs.rip = (uint64_t)task;
|
||||
new_task->allocated_stack =
|
||||
(uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK); // 16 KB is enough for everyone, right?
|
||||
@ -128,6 +130,7 @@ void Scheduler::add_kernel_task(const char* taskname, void (*task)(void))
|
||||
new_task->cpu_time = 0;
|
||||
strlcpy(new_task->name, taskname, sizeof(new_task->name));
|
||||
append_task(new_task);
|
||||
new_task->block_reason = BlockReason::None;
|
||||
new_task->state = new_task->Running;
|
||||
task_num++;
|
||||
kinfoln("Adding kernel task: %s, starts at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name,
|
||||
@ -140,11 +143,12 @@ Task* Scheduler::create_user_task()
|
||||
if (!new_task) return nullptr;
|
||||
memset(&new_task->regs, 0, sizeof(Context));
|
||||
new_task->user_task = true;
|
||||
new_task->id = free_tid++;
|
||||
new_task->id = free_pid++;
|
||||
new_task->ppid = 0;
|
||||
new_task->task_sleep = 0;
|
||||
new_task->task_time = 0;
|
||||
new_task->cpu_time = 0;
|
||||
new_task->block_reason = BlockReason::None;
|
||||
append_task(new_task);
|
||||
task_num++;
|
||||
return new_task;
|
||||
@ -154,42 +158,51 @@ long Scheduler::load_user_task(const char* filename)
|
||||
{
|
||||
kinfoln("Loading user task: %s", filename);
|
||||
Interrupts::push_and_disable();
|
||||
long result;
|
||||
if ((result = ELFLoader::check_elf_image_from_filesystem(filename)) < 0)
|
||||
{
|
||||
kerrorln("Failed to load %s from initrd", filename);
|
||||
Interrupts::pop();
|
||||
return result;
|
||||
}
|
||||
if ((uint64_t)result > PMM::get_free()) { return -ENOMEM; }
|
||||
Task* new_task = new Task;
|
||||
ASSERT(new_task);
|
||||
ensure(new_task);
|
||||
memset(&new_task->regs, 0, sizeof(Context));
|
||||
new_task->id = free_tid++;
|
||||
new_task->id = free_pid++;
|
||||
new_task->ppid = 0;
|
||||
new_task->uid = new_task->euid = new_task->gid = new_task->egid = 0;
|
||||
if (!new_task->allocator.init())
|
||||
{
|
||||
delete new_task;
|
||||
free_tid--;
|
||||
free_pid--;
|
||||
Interrupts::pop();
|
||||
return -ENOMEM;
|
||||
}
|
||||
new_task->address_space = AddressSpace::create();
|
||||
VMM::switch_to_user_address_space(new_task->address_space);
|
||||
ELFImage* image = ELFLoader::load_elf_from_filesystem(
|
||||
filename); // FIXME: TOCTOU? Right now, impossible, since interrupts are disabled and SMP is not a thing. But in
|
||||
// the future, it might be possible.
|
||||
ASSERT(image);
|
||||
long result;
|
||||
if ((result = ELFLoader::check_elf_image_from_filesystem(filename)) < 0)
|
||||
{
|
||||
delete new_task;
|
||||
free_pid--;
|
||||
kerrorln("Failed to load %s from initrd", filename);
|
||||
Interrupts::pop();
|
||||
return result;
|
||||
}
|
||||
if ((uint64_t)result > PMM::get_free())
|
||||
{
|
||||
delete new_task;
|
||||
free_pid--;
|
||||
kerrorln("Not enough memory for task %s", filename);
|
||||
Interrupts::pop();
|
||||
return -ENOMEM;
|
||||
}
|
||||
ELFImage* image = ELFLoader::load_elf_from_filesystem(filename);
|
||||
ensure(image);
|
||||
new_task->user_task = true;
|
||||
new_task->regs.rip = image->entry;
|
||||
new_task->image = image;
|
||||
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages_at(
|
||||
0x100000, TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
||||
0x100000, TASK_PAGES_IN_STACK,
|
||||
MAP_READ_WRITE | MAP_USER | MAP_AS_OWNED_BY_TASK); // 16 KB is enough for everyone, right?
|
||||
if (!new_task->allocated_stack)
|
||||
{
|
||||
new_task->address_space.destroy();
|
||||
delete new_task;
|
||||
free_tid--;
|
||||
free_pid--;
|
||||
ELFLoader::release_elf_image(image);
|
||||
VMM::switch_back_to_kernel_address_space();
|
||||
Interrupts::pop();
|
||||
@ -205,6 +218,7 @@ long Scheduler::load_user_task(const char* filename)
|
||||
new_task->cpu_time = 0;
|
||||
strlcpy(new_task->name, filename, sizeof(new_task->name));
|
||||
append_task(new_task);
|
||||
new_task->block_reason = BlockReason::None;
|
||||
new_task->state = new_task->Running;
|
||||
task_num++;
|
||||
kinfoln("Adding user task: %s, loaded at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name,
|
||||
@ -227,16 +241,17 @@ void Scheduler::reset_task(Task* task, ELFImage* new_image)
|
||||
task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
|
||||
task->task_sleep = 0;
|
||||
task->cpu_time = 0;
|
||||
task->block_reason = BlockReason::None;
|
||||
kinfoln("Resetting task: %s, loaded at %lx, PID %ld, stack at %lx, total tasks: %ld", task->name, task->regs.rip,
|
||||
task->id, task->regs.rsp, task_num);
|
||||
}
|
||||
|
||||
void Scheduler::reap_task(Task* task)
|
||||
{
|
||||
ASSERT(!Interrupts::is_in_handler());
|
||||
ensure(!Interrupts::is_in_handler());
|
||||
task_num--;
|
||||
Task* exiting_task = task;
|
||||
ASSERT(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK?
|
||||
ensure(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK?
|
||||
if (exiting_task->is_user_task())
|
||||
{
|
||||
VMM::switch_back_to_kernel_address_space();
|
||||
@ -245,13 +260,10 @@ void Scheduler::reap_task(Task* task)
|
||||
}
|
||||
kinfoln("reaping task %s, PID %ld, exited with code %ld", exiting_task->name, exiting_task->id,
|
||||
exiting_task->exit_status);
|
||||
if (exiting_task->id == (free_pid - 1)) free_pid--; // If we are the last spawned thread, free our PID.
|
||||
if (exiting_task->allocated_stack && !exiting_task->is_user_task())
|
||||
MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||
if (exiting_task->image) // FIXME: Also free pages the task has mmap-ed but not munmap-ed.
|
||||
{
|
||||
// ELFLoader::release_elf_image(exiting_task->image);
|
||||
kfree(exiting_task->image);
|
||||
}
|
||||
if (exiting_task->image) kfree(exiting_task->image);
|
||||
if (exiting_task->is_user_task())
|
||||
{
|
||||
exiting_task->allocator.free();
|
||||
@ -262,7 +274,6 @@ void Scheduler::reap_task(Task* task)
|
||||
Interrupts::pop();
|
||||
}
|
||||
for (int i = 0; i < TASK_MAX_FDS; i++) { exiting_task->files[i].close(); }
|
||||
if (exiting_task->id == (free_tid - 1)) free_tid--; // If we are the last spawned thread, free our PID.
|
||||
delete exiting_task;
|
||||
}
|
||||
|
||||
@ -281,6 +292,7 @@ void sched_common_exit(Context* context, int64_t status)
|
||||
}
|
||||
else
|
||||
{
|
||||
kinfoln("PID 1 exited with code %ld", status);
|
||||
#ifndef RUN_TEST_AS_INIT
|
||||
reboot();
|
||||
#else
|
||||
@ -292,7 +304,7 @@ void sched_common_exit(Context* context, int64_t status)
|
||||
|
||||
void Scheduler::task_exit(Context* context, int64_t status)
|
||||
{
|
||||
ASSERT(Interrupts::is_in_handler());
|
||||
ensure(Interrupts::is_in_handler());
|
||||
kdbgln("exit: task %ld finished running, used %ld ms of cpu time", sched_current_task->id,
|
||||
sched_current_task->cpu_time);
|
||||
sched_common_exit(context, status);
|
||||
@ -300,7 +312,7 @@ void Scheduler::task_exit(Context* context, int64_t status)
|
||||
|
||||
void Scheduler::task_misbehave(Context* context, int64_t status)
|
||||
{
|
||||
ASSERT(Interrupts::is_in_handler());
|
||||
ensure(Interrupts::is_in_handler());
|
||||
kdbgln("exit: task %ld misbehaved, used %ld ms of cpu time", sched_current_task->id, sched_current_task->cpu_time);
|
||||
sched_common_exit(context, status);
|
||||
}
|
||||
@ -308,7 +320,7 @@ void Scheduler::task_misbehave(Context* context, int64_t status)
|
||||
void Scheduler::reap_tasks()
|
||||
{
|
||||
Interrupts::disable();
|
||||
ASSERT(!Interrupts::is_in_handler());
|
||||
ensure(!Interrupts::is_in_handler());
|
||||
Task* reap_base = nullptr;
|
||||
Task* reap_end = nullptr;
|
||||
Task* task = base_task;
|
||||
@ -371,7 +383,7 @@ static void sched_decrement_sleep_times()
|
||||
|
||||
void Scheduler::task_tick(Context* context)
|
||||
{
|
||||
ASSERT(Interrupts::is_in_handler());
|
||||
ensure(Interrupts::is_in_handler());
|
||||
Interrupts::disable();
|
||||
sched_decrement_sleep_times();
|
||||
sched_current_task->task_time -= frequency;
|
||||
@ -387,7 +399,7 @@ void Scheduler::task_tick(Context* context)
|
||||
|
||||
void Scheduler::task_yield(Context* context)
|
||||
{
|
||||
ASSERT(Interrupts::is_in_handler());
|
||||
ensure(Interrupts::is_in_handler());
|
||||
Interrupts::disable();
|
||||
sched_current_task->save_context(context);
|
||||
bool was_idle = false;
|
||||
@ -401,11 +413,7 @@ void Scheduler::task_yield(Context* context)
|
||||
sched_current_task = sched_current_task->next_task;
|
||||
if (sched_current_task->state == sched_current_task->Blocking)
|
||||
{
|
||||
if (!sched_current_task->is_still_blocking())
|
||||
{
|
||||
sched_current_task->resume_read();
|
||||
sched_current_task->state = sched_current_task->Running;
|
||||
}
|
||||
if (!sched_current_task->is_still_blocking()) sched_current_task->resume();
|
||||
}
|
||||
if (sched_current_task->state == sched_current_task->Running)
|
||||
{
|
||||
@ -461,8 +469,10 @@ Task* Scheduler::current_task()
|
||||
return sched_current_task;
|
||||
}
|
||||
|
||||
#define WNOHANG 1
|
||||
|
||||
void sys_waitpid(Context* context, long pid, int* wstatus,
|
||||
int) // FIXME: Use the value in options and block if WNOHANG has not been specified.
|
||||
int options) // FIXME: only allow waiting for child processes when specifying a PID.
|
||||
{
|
||||
Task* child = nullptr;
|
||||
if (pid == -1)
|
||||
@ -477,8 +487,17 @@ void sys_waitpid(Context* context, long pid, int* wstatus,
|
||||
});
|
||||
if (!child)
|
||||
{
|
||||
context->rax = 0; // No child has exited, let's return 0.
|
||||
return;
|
||||
if (options & WNOHANG)
|
||||
{
|
||||
context->rax = 0; // No child has exited, let's return 0.
|
||||
return;
|
||||
}
|
||||
kdbgln("blocking wait on any child");
|
||||
sched_current_task->state = sched_current_task->Blocking;
|
||||
sched_current_task->block_reason = BlockReason::Waiting;
|
||||
sched_current_task->blocking_wait_info.pid = -1;
|
||||
sched_current_task->blocking_wait_info.wstatus = wstatus;
|
||||
return Scheduler::task_yield(context);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -486,33 +505,84 @@ void sys_waitpid(Context* context, long pid, int* wstatus,
|
||||
child = Scheduler::find_by_pid(pid);
|
||||
if (!child)
|
||||
{
|
||||
context->rax = -ESRCH;
|
||||
context->rax = -ECHILD;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (child->state != child->Dying) // FIXME: This should block if WNOHANG has not been specified.
|
||||
if (child->ppid != sched_current_task->id)
|
||||
{
|
||||
context->rax = 0;
|
||||
// We are trying to call waitpid() on a task that isn't a child of ours. This is not allowed.
|
||||
context->rax = -ECHILD;
|
||||
return;
|
||||
}
|
||||
if (wstatus)
|
||||
if (child->state != child->Dying)
|
||||
{
|
||||
int* kwstatus = obtain_user_ref(wstatus);
|
||||
if (kwstatus)
|
||||
if (options & WNOHANG)
|
||||
{
|
||||
*kwstatus = (int)(child->exit_status & 0xff);
|
||||
release_user_ref(kwstatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
kinfoln("wstatus ptr is invalid: %p", (void*)wstatus);
|
||||
child->state = child->Exited;
|
||||
context->rax = -EFAULT;
|
||||
context->rax = 0; // No child has exited, let's return 0.
|
||||
return;
|
||||
}
|
||||
sched_current_task->state = sched_current_task->Blocking;
|
||||
sched_current_task->block_reason = BlockReason::Waiting;
|
||||
sched_current_task->blocking_wait_info.pid = pid;
|
||||
sched_current_task->blocking_wait_info.wstatus = wstatus;
|
||||
return Scheduler::task_yield(context);
|
||||
}
|
||||
child->state = child->Exited;
|
||||
context->rax = (long)child->id;
|
||||
if (wstatus)
|
||||
{
|
||||
int status = (int)(child->exit_status & 0xff);
|
||||
if (!copy_to_user(wstatus, &status, sizeof(int))) context->rax = -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
bool Task::is_wait_still_blocking()
|
||||
{
|
||||
Task* child = nullptr;
|
||||
if (blocking_wait_info.pid == -1)
|
||||
{
|
||||
sched_for_each_child(sched_current_task, [&](Task* task) {
|
||||
if (task->state == task->Dying)
|
||||
{
|
||||
child = task;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!child) return true;
|
||||
else
|
||||
{
|
||||
blocking_wait_info.pid = child->id; // We're committed to this child now.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child = Scheduler::find_by_pid(blocking_wait_info.pid);
|
||||
ensure(child); // since sys_waitpid should have validated this child, and the only way for it to disappear from
|
||||
// the process list is for someone to wait for it, this should be pretty safe.
|
||||
if (child->state != child->Dying) return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Task::resume_wait()
|
||||
{
|
||||
ensure(blocking_wait_info.pid != -1); // is_wait_still_blocking should have chosen a child for us if the user
|
||||
// process told us to wait for any child.
|
||||
Task* child = Scheduler::find_by_pid(blocking_wait_info.pid);
|
||||
ensure(child); // This should also already have been validated.
|
||||
|
||||
child->state = child->Exited;
|
||||
regs.rax = (long)child->id;
|
||||
|
||||
if (blocking_wait_info.wstatus)
|
||||
{
|
||||
int wstatus = (int)(child->exit_status & 0xff);
|
||||
if (!copy_to_user(blocking_wait_info.wstatus, &wstatus, sizeof(int))) regs.rax = -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
struct pstat
|
||||
@ -522,12 +592,14 @@ struct pstat
|
||||
char pt_name[128];
|
||||
int pt_state;
|
||||
long pt_time;
|
||||
uid_t pt_uid;
|
||||
gid_t pt_gid;
|
||||
};
|
||||
|
||||
void sys_pstat(Context* context, long pid, struct pstat* buf)
|
||||
{
|
||||
Task* task;
|
||||
if (pid == -1) task = Scheduler::find_by_pid(free_tid - 1);
|
||||
if (pid == -1) task = Scheduler::find_by_pid(free_pid - 1);
|
||||
else if (pid == 0)
|
||||
task = &idle_task;
|
||||
else
|
||||
@ -542,21 +614,17 @@ void sys_pstat(Context* context, long pid, struct pstat* buf)
|
||||
context->rax = -ESRCH;
|
||||
return;
|
||||
}
|
||||
context->rax = task->id;
|
||||
if (buf)
|
||||
{
|
||||
struct pstat* kpstat = obtain_user_ref(buf);
|
||||
if (!kpstat)
|
||||
{
|
||||
context->rax = -EFAULT;
|
||||
return;
|
||||
}
|
||||
kpstat->pt_pid = task->id;
|
||||
kpstat->pt_ppid = task->ppid;
|
||||
kpstat->pt_state = (int)task->state;
|
||||
kpstat->pt_time = (long)task->cpu_time;
|
||||
strlcpy(kpstat->pt_name, task->name, sizeof(kpstat->pt_name));
|
||||
release_user_ref(kpstat);
|
||||
struct pstat stat;
|
||||
stat.pt_pid = task->id;
|
||||
stat.pt_ppid = task->ppid;
|
||||
stat.pt_state = (int)task->state;
|
||||
stat.pt_time = (long)task->cpu_time;
|
||||
stat.pt_uid = task->uid;
|
||||
stat.pt_gid = task->gid;
|
||||
strlcpy(stat.pt_name, task->name, sizeof(stat.pt_name));
|
||||
if (!copy_to_user(buf, &stat, sizeof(struct pstat))) context->rax = -EFAULT;
|
||||
}
|
||||
context->rax = task->id;
|
||||
return;
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
#include "thread/Task.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "std/ensure.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/string.h"
|
||||
|
||||
void Task::restore_context(Context* context)
|
||||
@ -71,15 +73,68 @@ bool Task::has_died()
|
||||
}
|
||||
|
||||
void Task::resume_read()
|
||||
{
|
||||
regs.rax = files[blocking_read_info.fd].user_read(blocking_read_info.size, blocking_read_info.buf);
|
||||
}
|
||||
|
||||
bool Task::is_read_still_blocking()
|
||||
{
|
||||
return VFS::would_block(files[blocking_read_info.fd].node());
|
||||
}
|
||||
|
||||
void Task::resume()
|
||||
{
|
||||
VMM::switch_back_to_kernel_address_space();
|
||||
VMM::apply_address_space();
|
||||
VMM::switch_to_previous_user_address_space();
|
||||
regs.rax = files[blocking_read_info.fd].read(blocking_read_info.size, blocking_read_info.buf);
|
||||
VMM::switch_to_user_address_space(address_space);
|
||||
switch (block_reason)
|
||||
{
|
||||
case BlockReason::None: return;
|
||||
case BlockReason::Reading: resume_read(); break;
|
||||
case BlockReason::Waiting: resume_wait(); break;
|
||||
|
||||
default: ensure(false);
|
||||
}
|
||||
VMM::apply_address_space();
|
||||
block_reason = BlockReason::None;
|
||||
state = Running;
|
||||
}
|
||||
|
||||
bool Task::is_still_blocking()
|
||||
{
|
||||
return VFS::would_block(files[blocking_read_info.fd].node());
|
||||
switch (block_reason)
|
||||
{
|
||||
case BlockReason::None: return true;
|
||||
case BlockReason::Reading: return is_read_still_blocking();
|
||||
case BlockReason::Waiting: return is_wait_still_blocking();
|
||||
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
Descriptor* Task::open_descriptor_from_fd(int fd, int& error)
|
||||
{
|
||||
Descriptor* file = descriptor_from_fd(fd, error);
|
||||
if (!file) return nullptr;
|
||||
if (!file->is_open())
|
||||
{
|
||||
error = EBADF;
|
||||
return nullptr;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
Descriptor* Task::descriptor_from_fd(int fd, int& error)
|
||||
{
|
||||
if (fd < 0 || fd >= TASK_MAX_FDS)
|
||||
{
|
||||
error = EBADF;
|
||||
return nullptr;
|
||||
}
|
||||
return &files[fd];
|
||||
}
|
||||
|
||||
bool Task::is_superuser()
|
||||
{
|
||||
return euid == 0;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "trace/Resolve.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "fs/InitRD.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
#include <stddef.h>
|
||||
@ -7,7 +7,7 @@
|
||||
extern int kernel_start;
|
||||
extern int kernel_end;
|
||||
|
||||
static InitRD::File symbol_map = {"", 0, 0, (void*)-1};
|
||||
static InitRD::File symbol_map = {"", 0, 0, (void*)-1, 0};
|
||||
|
||||
static size_t symbol_strlen(const char* symbol)
|
||||
{
|
||||
|
20
kernel/src/utils/PageFaultReason.cpp
Normal file
20
kernel/src/utils/PageFaultReason.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#define MODULE "mem"
|
||||
|
||||
#include "utils/PageFaultReason.h"
|
||||
#include "log/Log.h"
|
||||
|
||||
#define PROGRAM_STACK_BOTTOM 0x100000
|
||||
|
||||
void determine_user_page_fault_reason(uintptr_t faulting_address)
|
||||
{
|
||||
if (faulting_address < 0x1000)
|
||||
{
|
||||
kinfoln("Address 0x%lx looks like a nullptr dereference", faulting_address);
|
||||
return;
|
||||
}
|
||||
if (faulting_address < PROGRAM_STACK_BOTTOM && (PROGRAM_STACK_BOTTOM - faulting_address) < 0x1000)
|
||||
{
|
||||
kinfoln("Address 0x%lx looks like a stack overflow", faulting_address);
|
||||
return;
|
||||
}
|
||||
}
|
49
kernel/src/utils/StringParsing.cpp
Normal file
49
kernel/src/utils/StringParsing.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "utils/StringParsing.h"
|
||||
|
||||
int isdigit(int c)
|
||||
{
|
||||
return c >= '0' && c < ':';
|
||||
}
|
||||
|
||||
int isodigit(int c)
|
||||
{
|
||||
return c >= '0' && c < '8';
|
||||
}
|
||||
|
||||
int isxdigit(int c)
|
||||
{
|
||||
return isdigit(c) || ((unsigned int)c | 32) - 'a' < 6;
|
||||
}
|
||||
|
||||
template <typename T, typename ValidDigitChecker, typename Converter>
|
||||
static T string_to_integer_type(const char* str, int base, ValidDigitChecker checker, Converter converter)
|
||||
{
|
||||
bool neg = false;
|
||||
T val = 0;
|
||||
|
||||
switch (*str)
|
||||
{
|
||||
case '-':
|
||||
neg = true;
|
||||
str++;
|
||||
break;
|
||||
case '+': str++; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
while (checker(*str)) { val = (base * val) + converter(*str++); }
|
||||
|
||||
return (neg ? -val : val);
|
||||
}
|
||||
|
||||
long parse_decimal(const char* str)
|
||||
{
|
||||
return string_to_integer_type<long>(
|
||||
str, 10, [](char c) { return isdigit(c); }, [](char c) { return c - '0'; });
|
||||
}
|
||||
|
||||
long parse_octal(const char* str)
|
||||
{
|
||||
return string_to_integer_type<long>(
|
||||
str, 8, [](char c) { return isodigit(c); }, [](char c) { return c - '0'; });
|
||||
}
|
42
kernel/src/utils/Time.cpp
Normal file
42
kernel/src/utils/Time.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#define MODULE "time"
|
||||
|
||||
#include "utils/Time.h"
|
||||
#include "log/Log.h"
|
||||
|
||||
int isleap(int year)
|
||||
{
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
|
||||
int make_yday(int year, int month)
|
||||
{
|
||||
static const short int upto[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
|
||||
int yd;
|
||||
|
||||
yd = upto[month - 1];
|
||||
if (month > 2 && isleap(year)) yd++;
|
||||
return yd;
|
||||
}
|
||||
|
||||
uint64_t broken_down_to_unix(uint64_t year, uint64_t yday, uint64_t hour, uint64_t min, uint64_t sec)
|
||||
{
|
||||
return sec + min * 60 + hour * 3600 + yday * 86400 + (year - 70) * 31536000 + ((year - 69) / 4) * 86400 -
|
||||
((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400;
|
||||
}
|
||||
|
||||
static int bcd_number_to_decimal(int num)
|
||||
{
|
||||
return ((num >> 4) * 10) + (num & 0xf);
|
||||
}
|
||||
|
||||
uint64_t unix_boottime(uint8_t boottime[8])
|
||||
{
|
||||
int year = bcd_number_to_decimal(boottime[0]) * 100 + bcd_number_to_decimal(boottime[1]);
|
||||
int month = bcd_number_to_decimal(boottime[2]);
|
||||
int day = bcd_number_to_decimal(boottime[3]);
|
||||
int hour = bcd_number_to_decimal(boottime[4]);
|
||||
int minute = bcd_number_to_decimal(boottime[5]);
|
||||
int second = bcd_number_to_decimal(boottime[6]);
|
||||
kinfoln("UTC boot time: %d-%d-%d %d:%d:%d", year, month, day, hour, minute, second);
|
||||
return broken_down_to_unix(year - 1900, make_yday(year, month) + (day - 1), hour, minute, second);
|
||||
}
|
@ -5,7 +5,7 @@ LIBC_BIN := $(LIBC_DIR)/bin
|
||||
|
||||
DESTDIR ?= $(LUNA_BASE)/usr/lib
|
||||
|
||||
CFLAGS := -Os -nostdlib -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -pedantic -Wall -Wextra -Werror -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion
|
||||
CFLAGS := -Os -nostdlib -ffunction-sections -fdata-sections -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -pedantic -Wall -Wextra -Werror -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion
|
||||
CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy
|
||||
ASMFLAGS := -felf64
|
||||
|
||||
@ -21,49 +21,62 @@ OBJS += $(patsubst $(LIBC_SRC)/%.asm, $(LIBC_OBJ)/%.asm.o, $(ASM_SRC))
|
||||
|
||||
$(LIBC_OBJ)/%.cpp.o: $(LIBC_SRC)/%.cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $^
|
||||
@$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $^
|
||||
@echo " CXX $^"
|
||||
|
||||
$(LIBC_OBJ)/%.c.o: $(LIBC_SRC)/%.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -o $@ -c $^
|
||||
@$(CC) $(CFLAGS) -o $@ -c $^
|
||||
@echo " CC $^"
|
||||
|
||||
$(LIBC_OBJ)/%.asm.o: $(LIBC_SRC)/%.asm
|
||||
@mkdir -p $(@D)
|
||||
$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@echo " ASM $^"
|
||||
|
||||
$(LIBC_BIN)/libc.a: $(OBJS)
|
||||
@mkdir -p $(@D)
|
||||
$(AR) rcs $@ $(OBJS)
|
||||
@$(AR) rcs $@ $(OBJS)
|
||||
@echo " AR $@"
|
||||
|
||||
$(LIBC_BIN)/crt0.o: $(LIBC_DIR)/crt0.asm
|
||||
@mkdir -p $(@D)
|
||||
$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@echo " ASM $^"
|
||||
|
||||
$(LIBC_BIN)/crti.o: $(LIBC_DIR)/crti.asm
|
||||
@mkdir -p $(@D)
|
||||
$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@echo " ASM $^"
|
||||
|
||||
$(LIBC_BIN)/crtn.o: $(LIBC_DIR)/crtn.asm
|
||||
@mkdir -p $(@D)
|
||||
$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@$(ASM) $(ASMFLAGS) -o $@ $^
|
||||
@echo " ASM $^"
|
||||
|
||||
build: $(LIBC_BIN)/crt0.o $(LIBC_BIN)/crti.o $(LIBC_BIN)/crtn.o $(LIBC_BIN)/libc.a
|
||||
|
||||
$(DESTDIR)/libc.a: $(LIBC_BIN)/libc.a
|
||||
@mkdir -p $(@D)
|
||||
cp $^ $@
|
||||
@cp $^ $@
|
||||
@rm -f $(DESTDIR)/libm.a
|
||||
@ln -s $@ $(DESTDIR)/libm.a
|
||||
@echo " INSTALL $^"
|
||||
|
||||
$(DESTDIR)/crt0.o: $(LIBC_BIN)/crt0.o
|
||||
@mkdir -p $(@D)
|
||||
cp $^ $@
|
||||
@cp $^ $@
|
||||
@echo " INSTALL $^"
|
||||
|
||||
$(DESTDIR)/crti.o: $(LIBC_BIN)/crti.o
|
||||
@mkdir -p $(@D)
|
||||
cp $^ $@
|
||||
@cp $^ $@
|
||||
@echo " INSTALL $^"
|
||||
|
||||
$(DESTDIR)/crtn.o: $(LIBC_BIN)/crtn.o
|
||||
@mkdir -p $(@D)
|
||||
cp $^ $@
|
||||
@cp $^ $@
|
||||
@echo " INSTALL $^"
|
||||
|
||||
install: $(DESTDIR)/libc.a $(DESTDIR)/crt0.o $(DESTDIR)/crti.o $(DESTDIR)/crtn.o
|
||||
|
||||
|
@ -6,8 +6,6 @@ extern _fini
|
||||
extern initialize_libc
|
||||
extern exit
|
||||
|
||||
extern __argv
|
||||
|
||||
global _start
|
||||
_start:
|
||||
; Set up end of the stack frame linked list.
|
||||
@ -16,12 +14,15 @@ _start:
|
||||
push rbp ; rbp=0
|
||||
mov rbp, rsp
|
||||
|
||||
push rdi
|
||||
push rsi
|
||||
|
||||
call initialize_libc
|
||||
|
||||
call _init
|
||||
|
||||
mov rdi, 0 ; argc = 0
|
||||
mov rsi, __argv ; Dummy argv which is equal to {NULL}
|
||||
pop rsi ; argv
|
||||
pop rdi ; argc
|
||||
|
||||
call main
|
||||
|
||||
|
6
libs/libc/include/alloca.h
Normal file
6
libs/libc/include/alloca.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef _ALLOCA_H
|
||||
#define _ALLOCA_H
|
||||
|
||||
#define alloca __builtin_alloca
|
||||
|
||||
#endif
|
@ -18,7 +18,7 @@ extern "C"
|
||||
#ifdef NDEBUG
|
||||
#define assert(expr) (void)0
|
||||
#else
|
||||
#define assert(expr) (bool)(expr) || __assertion_failed(__FILE__, __LINE__, __FUNCTION__, #expr)
|
||||
#define assert(expr) (bool)(expr) || __assertion_failed(__FILE__, __LINE__, __FUNCTION__, #expr) // Verify a condition.
|
||||
#endif
|
||||
|
||||
#endif
|
@ -3,5 +3,9 @@
|
||||
|
||||
#define ID_PID 0
|
||||
#define ID_PPID 1
|
||||
#define ID_UID 2
|
||||
#define ID_EUID 3
|
||||
#define ID_GID 4
|
||||
#define ID_EGID 5
|
||||
|
||||
#endif
|
@ -4,7 +4,9 @@
|
||||
#define __lc_noreturn __attribute__((noreturn))
|
||||
#define __lc_align(n) __attribute__((aligned(n)))
|
||||
#define __lc_deprecated(msg) __attribute__((deprecated(msg)))
|
||||
#define __lc_is_deprecated __attribute__((deprecated))
|
||||
#define __lc_unreachable __builtin_unreachable
|
||||
#define __lc_used __attribute__((used))
|
||||
#define __lc_unused __attribute__((unused))
|
||||
|
||||
#endif
|
@ -1,6 +1,8 @@
|
||||
#ifndef _CTYPE_H
|
||||
#define _CTYPE_H
|
||||
|
||||
#include <bits/macros.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -51,6 +53,10 @@ extern "C"
|
||||
/* Returns the uppercase form of the specified character. */
|
||||
int toupper(int c);
|
||||
|
||||
/* Returns the character c, truncated to fit in the ASCII character set. This function should not be used, as it
|
||||
* will convert accented letters into random characters. */
|
||||
__lc_is_deprecated int toascii(int c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <luna/os-limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* An entry in a directory. */
|
||||
struct dirent
|
||||
{
|
||||
ino_t d_ino;
|
||||
@ -13,6 +14,16 @@ struct dirent
|
||||
char d_name[NAME_MAX];
|
||||
};
|
||||
|
||||
#define DT_BLK 1 // This is a block device.
|
||||
#define DT_CHR 2 // This is a character device.
|
||||
#define DT_DIR 3 // This is a directory.
|
||||
#define DT_FIFO 4 // This is a named pipe (FIFO).
|
||||
#define DT_LNK 5 // This is a symbolic link.
|
||||
#define DT_REG 6 // This is a regular file.
|
||||
#define DT_SOCK 7 // This is a UNIX domain socket.
|
||||
#define DT_UNKNOWN 0 // The file type could not be determined.
|
||||
|
||||
/* A stream representing a directory. */
|
||||
typedef struct
|
||||
{
|
||||
int d_dirfd;
|
||||
|
@ -4,24 +4,81 @@
|
||||
/* The last error encountered during a call to a library or system function. */
|
||||
extern int errno;
|
||||
|
||||
#define EPERM 1 // Operation not permitted
|
||||
#define ENOENT 2 // No such file or directory
|
||||
#define ESRCH 3 // No such process
|
||||
#define EINTR 4 // Interrupted system call. Not implemented.
|
||||
#define ENOEXEC 8 // Exec format error
|
||||
#define EBADF 9 // Bad file descriptor
|
||||
#define EAGAIN 11 // Resource temporarily unavailable
|
||||
#define ENOMEM 12 // Cannot allocate memory
|
||||
#define EFAULT 14 // Bad address
|
||||
#define EEXIST 17 // File exists
|
||||
#define ENOTDIR 20 // Not a directory
|
||||
#define EISDIR 21 // Is a directory
|
||||
#define EINVAL 22 // Invalid argument
|
||||
#define EMFILE 24 // Too many open files
|
||||
#define ENOTTY 25 // Inappropriate ioctl for device
|
||||
#define ENOSPC 28 // No space left on device
|
||||
#define EPIPE 32 // Broken pipe. Not implemented.
|
||||
#define ENOSYS 38 // Function not implemented
|
||||
#define ENOTSUP 95 // Operation not supported
|
||||
#define EPERM 1 // Operation not permitted
|
||||
#define ENOENT 2 // No such file or directory
|
||||
#define ESRCH 3 // No such process
|
||||
#define EINTR 4 // Interrupted system call. Not implemented.
|
||||
#define EIO 5 // Input/output error. Not implemented.
|
||||
#define ENXIO 6 // No such device or address. Not implemented.
|
||||
#define E2BIG 7 // Argument list too long
|
||||
#define ENOEXEC 8 // Exec format error
|
||||
#define EBADF 9 // Bad file descriptor
|
||||
#define ECHILD 10 // No child processes
|
||||
#define EAGAIN 11 // Resource temporarily unavailable
|
||||
#define EWOULDBLOCK 11 // Resource temporarily unavailable
|
||||
#define ENOMEM 12 // Cannot allocate memory
|
||||
#define EACCES 13 // Permission denied
|
||||
#define EFAULT 14 // Bad address
|
||||
#define EBUSY 16 // Device or resource busy. Not implemented.
|
||||
#define EEXIST 17 // File exists
|
||||
#define EXDEV 18 // Invalid cross-device link. Not implemented.
|
||||
#define ENODEV 19 // No such device. Not implemented.
|
||||
#define ENOTDIR 20 // Not a directory
|
||||
#define EISDIR 21 // Is a directory
|
||||
#define EINVAL 22 // Invalid argument
|
||||
#define ENFILE 23 // Too many open files in system. Not implemented.
|
||||
#define EMFILE 24 // Too many open files
|
||||
#define ENOTTY 25 // Inappropriate ioctl for device
|
||||
#define EFBIG 27 // File too large. Not implemented.
|
||||
#define ENOSPC 28 // No space left on device
|
||||
#define ESPIPE 29 // Illegal seek. Not implemented.
|
||||
#define EROFS 30 // Read-only file system. Not implemented.
|
||||
#define EMLINK 31 // Too many links. Not implemented.
|
||||
#define EPIPE 32 // Broken pipe. Not implemented.
|
||||
#define EDOM 33 // Numerical argument out of domain. Not implemented.
|
||||
#define ERANGE 34 // Numerical result out of range
|
||||
#define EDEADLK 35 // Resource deadlock avoided. Not implemented.
|
||||
#define ENAMETOOLONG 36 // File name too long. Not implemented.
|
||||
#define ENOLCK 37 // No locks available. Not implemented.
|
||||
#define ENOSYS 38 // Function not implemented
|
||||
#define ENOTEMPTY 39 // Directory not empty. Not implemented.
|
||||
#define ELOOP 40 // Too many levels of symbolic links. Not implemented.
|
||||
#define ENOMSG 42 // No message of desired type. Not implemented.
|
||||
#define EOVERFLOW 75 // Value too large for defined data type. Not implemented.
|
||||
#define EILSEQ 84 // Invalid or incomplete multibyte or wide character. Not implemented.
|
||||
#define ENOTSOCK 88 // Socket operation on non-socket. Not implemented.
|
||||
#define ENOTSUP 95 // Operation not supported
|
||||
#define EOPNOTSUPP 95 // Operation not supported
|
||||
#define EADDRINUSE 98 // Address already in use. Not implemented.
|
||||
#define ENETRESET 102 // Network dropped connection on reset. Not implemented.
|
||||
#define ECONNRESET 104 // Connection reset by peer. Not implemented.
|
||||
#define EISCONN 106 // Transport endpoint is already connected. Not implemented.
|
||||
#define ETIMEDOUT 110 // Connection timed out. Not implemented.
|
||||
#define EALREADY 114 // Operation already in progress. Not implemented.
|
||||
|
||||
// FIXME: Right now I don't want to have to order and label these, since we have no net support anyways.
|
||||
#define EADDRNOTAVAIL -1
|
||||
#define EAFNOSUPPORT -2
|
||||
#define ECONNABORTED -3
|
||||
#define ECONNREFUSED -4
|
||||
#define EDESTADDRREQ -5
|
||||
#define EHOSTUNREACH -6
|
||||
#define EINPROGRESS -7
|
||||
#define EMSGSIZE -8
|
||||
#define ENETDOWN -9
|
||||
#define ENETRESET -10
|
||||
#define ENETUNREACH -11
|
||||
#define ENOBUFS -12
|
||||
#define ENOMSG -13
|
||||
#define ENOPROTOOPT -14
|
||||
#define ENOTCONN -15
|
||||
#define ENOTSOCK -16
|
||||
#define EPROTONOSUPPORT -17
|
||||
#define EPROTOTYPE -18
|
||||
|
||||
#ifdef _GNU_SOURCE // Give it only to programs that ask for it.
|
||||
/* Name used to invoke calling program. Same value as argv[0] in main(), but can be used globally. */
|
||||
extern char* program_invocation_name;
|
||||
#endif
|
||||
|
||||
#endif
|
@ -13,11 +13,26 @@
|
||||
#define O_CLOEXEC 8
|
||||
/* Refuse to open the file if it is not a directory. */
|
||||
#define O_DIRECTORY 16
|
||||
/* Truncate the file on open. */
|
||||
#define O_TRUNC 32
|
||||
/* Create the file if it doesn't exist. */
|
||||
#define O_CREAT 64
|
||||
/* Open the file for appending. */
|
||||
#define O_APPEND 128
|
||||
/* Fail to open the file if it already exists. */
|
||||
#define O_EXCL 256
|
||||
|
||||
/* Duplicate a file descriptor. */
|
||||
#define F_DUPFD 0
|
||||
/* Is a file descriptor a TTY? */
|
||||
#define F_ISTTY 1
|
||||
/* Get the file descriptor flags. */
|
||||
#define F_GETFD 2
|
||||
/* Set the file descriptor flags. */
|
||||
#define F_SETFD 3
|
||||
|
||||
/* Close the file descriptor on a call to execve(). */
|
||||
#define FD_CLOEXEC 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -25,7 +40,7 @@ extern "C"
|
||||
#endif
|
||||
|
||||
/* Opens the file specified by pathname. Returns a file descriptor on success, or -1 on error. */
|
||||
int open(const char* pathname, int flags);
|
||||
int open(const char* pathname, int flags, ...);
|
||||
|
||||
/* Performs an operation on the file descriptor fd determined by cmd. */
|
||||
int fcntl(int fd, int cmd, ...);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user