Compare commits

..

201 Commits

Author SHA1 Message Date
da41784183 Fix setup-gcc.sh 2022-11-09 17:15:02 +01:00
b8014a158c ports: Add a port of the GNU binutils =D
what a coincidence!! this big achievement is the 800th commit!!
2022-11-09 16:59:27 +01:00
2ac98ed0c1 libc: Define wint_t in wchar.h 2022-11-09 16:29:33 +01:00
078f3f5862 Kernel: Add a remainder to refactor the initial ramdisk code 2022-11-09 16:07:49 +01:00
d76af10e70 Kernel: Move initrd into the fs/ folder
It makes more sense to put it in there.
2022-11-09 16:03:16 +01:00
60bd8d9418 Kernel: Add an explanatory comment to the end of _start() 2022-11-09 15:58:00 +01:00
1ea216dfd1 Kernel: Rename assert.h to ensure.h
The macro is named "ensure", the header file should reflect that.
2022-11-09 15:28:35 +01:00
da7ad8da5b Kernel: Show the file location when a check fails 2022-11-09 15:23:59 +01:00
ea94b331fb Scheduler: Use copy_to_user in a few system calls 2022-11-09 15:20:53 +01:00
46e4d37098 toolchain: build libstdc++ without exceptions, just in case 2022-11-09 12:09:56 +01:00
d1dea3f1d6 apps: Add support for C++ apps alongside C ones 2022-11-09 12:01:48 +01:00
b6f2b41f1f Toolchain: Build libstdc++ just after gcc and libgcc
This means... C++ userland programs!!
Some parts of the standard library still have weird behavior. For example, do not try to use std::cout. That page-faults.
2022-11-09 11:56:08 +01:00
61753a6f51 libc: Symlink libm.a to libc.a
Some programs (or g++) might want to link against libm, yet on Luna it's part of libc.
2022-11-09 11:39:39 +01:00
57ef8feddb libc: Add __cxa_atexit and __cxa_finalize
To support a C++ implementation :)
2022-11-09 11:38:40 +01:00
0bfe5e27ef libc: Add localeconv()
Always returns the default C locale values.
2022-11-09 11:38:14 +01:00
d3030d2111 libc: Stub out strtod() 2022-11-09 11:37:52 +01:00
da4c0d5184 libc: Stub out strtok() 2022-11-09 11:37:41 +01:00
f8f5968753 libc: Define EOVERFLOW and make every dummy errno value unique 2022-11-09 11:37:32 +01:00
1300b8f5ee libc: Add stubs for signal() and raise() 2022-11-09 11:37:08 +01:00
bbc7a7338c libc: Add fpclassify 2022-11-09 11:36:52 +01:00
59e03d0799 libc: Add DT_* macros to dirent.h 2022-11-09 11:36:41 +01:00
accf7ee417 libc: Add unimplemented st_nlink field 2022-11-09 11:36:21 +01:00
033c41cbd7 Kernel: Rework the uid/gid system to make it compliant 2022-11-09 09:54:07 +01:00
99429baed2 tools: make moon.sym readable only by root
We don't want everyone peeking at kernel symbols.
2022-11-09 09:11:00 +01:00
bb26996cb7 Add a script to check whether all project sources are properly formatted 2022-11-08 19:17:21 +01:00
98da473fdc Run clang-format 2022-11-08 19:07:43 +01:00
0c1ffedd3f Add a script to run clang-format on all source files 2022-11-08 19:07:16 +01:00
327b60566c Use different color for luna builds than for port builds 2022-11-08 18:04:58 +01:00
054d5a8bb4 Build system: Prettify build 2022-11-08 18:03:43 +01:00
00f9981f51 Userspace: Enable dead code stripping 2022-11-08 17:39:34 +01:00
6f72f92493 Kernel: Start preparing for recursive paging 2022-11-08 17:12:59 +01:00
2230ebd969 Wrap math.h around builtins 2022-11-06 21:37:00 +01:00
472192bcf2 libc: Define LC_NUMERIC 2022-11-06 20:47:16 +01:00
78ee6ce34a libc: Stub out scanf() 2022-11-06 20:47:07 +01:00
c9e20fd38e Why are there so many errno values 2022-11-06 20:46:34 +01:00
2eeef9581f libc: Add strxfrm() 2022-11-06 19:56:36 +01:00
2e9348181d Tests: Add tests for strcmp() and strncmp() 2022-11-06 19:51:56 +01:00
5d94525c7a Kernel: Figure out why a program is misbehaving 2022-11-06 18:12:25 +01:00
c6ce7a5358 libc: Add bsearch (with a test!!) 2022-11-06 17:34:35 +01:00
1025248cc7 libc: Add qsort (with a test!!) 2022-11-06 17:16:08 +01:00
44815b08c7 Kernel: Use copy_from_user() and copy_to_user() in read/write
This is huge. Finally, we can do proper reads/writes with invalid addresses, or big buffers.
2022-11-06 15:19:23 +01:00
04ae97a6ec libc: Add difftime() 2022-11-06 15:09:38 +01:00
2980ee3973 libc: Make fread() and fwrite() less awkward 2022-11-06 14:53:05 +01:00
88011fc162 init: Use fread() properly 2022-11-06 14:52:50 +01:00
40b078e0a2 libc: Make fread/fwrite return the number of items read/written instead of the number of bytes 2022-11-06 14:46:22 +01:00
d7692a7f59 libc: Rename fold() to lowercase() in strcasecmp and strncasecmp 2022-11-06 14:45:58 +01:00
da182f1c2f libc: Add mktemp() and mkdtemp(), along with a test for mktemp 2022-11-05 18:07:45 +01:00
b2fb740d99 Kernel: Refuse to mkdir any directory named . or .. 2022-11-05 12:09:01 +01:00
b78f6f269d Kernel: Omit displaying KernelHeap's buffer location in the log 2022-11-05 11:59:00 +01:00
cedefaf93d libc: Exit with -errno if we fail initialization 2022-11-05 11:56:31 +01:00
9ab3ab7c40 Kernel: Show init's exit status before restarting 2022-11-05 11:56:16 +01:00
b63a9f5ba9 Kernel: Improve strdup_from_user and add copy to and from user
Still a bit funky, that's why we're not using it in read() and write() yet.
2022-11-05 11:54:55 +01:00
67f536cf91 Kernel: Split number-parsing code into a separate file 2022-11-05 10:50:58 +01:00
d0efc106b0 Kernel: Add UBSAN (Undefined Behaviour Sanitizer) 2022-11-05 10:47:18 +01:00
ffcaac0ca3 Kernel: Add validate_user_write() and validate_user_read()
Not at copy_from_user and copy_to_user yet, but an improvement over blindly using physical memory.
2022-11-04 22:46:48 +01:00
cbc2e76082 Remove the .elf suffix from the kernel filename 2022-11-03 20:44:37 +01:00
e5b2641019 Kernel: Enforce W^X when loading executables
From now on, if an executable contains segments that
want to be loaded as both writable and executable,
we refuse and abort with ENOEXEC.
2022-11-03 20:28:54 +01:00
2c08de044f Kernel, libc: Add support for querying the framebuffer's scanline via ioctl() 2022-11-03 20:20:22 +01:00
71633e264f Kernel: Make the serial port writable for everyone :) 2022-11-03 17:13:32 +01:00
979569eb5a Ports: Refactor the NASM port's package script a bit 2022-11-03 17:06:27 +01:00
3b86f87c76 libc: Support %i in printf()
Which is the same as %d.
2022-11-03 16:53:19 +01:00
2cfefc25cc libc: Stub out rename() 2022-11-03 16:52:41 +01:00
0fd31698b2 Kernel: Accept not opened file descriptors in dup2()
This involves renaming the descriptor_from_fd function to the more appropriately named
open_descriptor_from_fd (since we check if the descriptor was opened and error out otherwise),
and creating a new function that does not verify that the file descriptor was opened.
2022-11-03 16:52:21 +01:00
7afbff08b6 apps: Add a little screen utility
It tells you the resolution of your screen :)
2022-11-02 21:00:23 +01:00
249c79f8a3 Kernel, libc: Add ioctl()
Right now, only the framebuffer supports this system call, to query its dimensions.
2022-11-02 20:59:42 +01:00
feab66c0d3 Kernel: Remove the paint() system call
That was a very old one from back in the old days. Now that the framebuffer is finally a device file,
and it can be memory-mapped by user programs for more performance,
this syscall is MORE than obsolete.
2022-11-02 20:35:06 +01:00
8f2308c80d Kernel: Implement mmap-able device files (regular files are not mmap-able yet) 2022-11-02 20:24:07 +01:00
42a805fd60 Kernel: Move the translated keyboard to /dev/console and make /dev/kbd provide raw scancodes 2022-11-02 19:51:54 +01:00
c604c074a1 Kernel: Rename ASSERT() to ensure()
Doesn't get stripped on release builds, so it shouldn't be named assert.
2022-11-02 19:38:15 +01:00
e5cf32c7b3 Kernel: Introduce page ownership
Some pages, such as framebuffer pages, are not physical memory frames reserved for the current process.
Some, such as the framebuffer, may be shared between all processes.
Yet, on exit() or on exec(), a process frees all frames mapped into its address spaces.
And on fork(), it copies all data between frames. So how could we map framebuffers.

Simple: we use one of the bits in page table entries which are available to the OS, and mark whether that page is owned by the current process.

If it is owned, it will be:
- Freed on address space destruction
- Its data will be copied to a new page owned by the child process on fork()

If it is not owned, it will be:
- Left alone on address space destruction
- On fork(), the child's virtual page will be mapped to the same physical frame as the parent

This still needs a bit more work, such as keeping a reference of how many processes use a page to free it when all processes using it exit/exec.
This should be done for MAP_SHARED mappings, for example, since they are not permanent forever,
unlike the framebuffer for example.
2022-11-02 19:32:28 +01:00
875d971d3b Tools: Make list-ports.sh exit when ports.list does not exist 2022-11-02 18:53:47 +01:00
dfcc827103 libc: Add PROT_EXEC 2022-11-02 18:40:05 +01:00
07e518c38f Kernel: Make sys_mmap log the correct prot value 2022-11-02 18:39:58 +01:00
22740e69bf Kernel: Add support for the NX bit
Not support, actually. We now REQUIRE it.
2022-11-02 18:34:57 +01:00
534500cda0 Implement enough runtime for binutils to compile 2022-10-31 12:29:53 +01:00
497a52dd82 apps: add a little mkdir utility 2022-10-31 09:53:52 +01:00
c2a08060cf VFS: Add support for an implementation-defined value 2022-10-30 20:53:45 +01:00
5eae93bbb0 Bugfix: do not crash if we are blocking for no reason 2022-10-30 20:51:32 +01:00
aabff7a1d3 libc: Add mktime() 2022-10-30 20:46:25 +01:00
45f40a31d6 Remove old FIXME 2022-10-30 20:40:53 +01:00
f83c78bcad libc: Implement gettimeofday() and instantly mark it as deprecated 2022-10-30 20:40:05 +01:00
7bd1cba1e3 libc: Stub out bsearch() 2022-10-30 20:02:03 +01:00
52d4f34f05 Kernel, libc: Implement umask() 2022-10-30 19:55:38 +01:00
0d443385e9 libc: Add system() 2022-10-30 19:43:37 +01:00
29c59abf7d Kernel: Rename blocking_wait_info's wait_pid to pid 2022-10-30 19:28:43 +01:00
9c3792718c Kernel: Remove waitpid() debug messages and add more checks 2022-10-30 19:24:56 +01:00
e244c150c2 Kernel, libc: Add ECHILD 2022-10-30 19:24:26 +01:00
c68d040484 Scheduler: Make sure we are in the kernel's address space when resuming a blocked process 2022-10-30 19:10:46 +01:00
a7e4f2bdd2 sh: Make it much more versatile
This commit implements:
Argument matching, to show help and version
Support for running scripts
Support for parsing and running commands (via -c)

This is the first step to implementing the system() libc function.
2022-10-30 19:09:18 +01:00
e58aa361c8 sh: Show message when child called abort() 2022-10-30 18:36:52 +01:00
00f90246c8 Kernel: Implement very basic escape sequences for TextRenderer 2022-10-30 18:34:40 +01:00
08c4dac2c2 Kernel: Enable -Wvla 2022-10-30 18:20:16 +01:00
b4ccd786f9 remove-port.sh: export DESTDIR 2022-10-30 18:19:12 +01:00
97df9d8d3a Ports: Add nasm port 2022-10-30 18:14:57 +01:00
948361bec5 printf: Show which format specifier is unknown 2022-10-30 18:08:29 +01:00
d186d573dd libc: Document strftime in time.h 2022-10-30 17:48:54 +01:00
d5a6c7f27f libc: Implement strftime() 2022-10-30 17:47:47 +01:00
4c096bd36c uptime: break time down into more understandable units
80 seconds -> 1 minute, 20 seconds for example
647 seconds would be 10 minutes, 47 seconds
and more...
2022-10-30 10:31:59 +01:00
af0f4d2037 Kernel: Remove /dev/uptime
This information can now be fetched with clock_gettime.
2022-10-30 10:16:53 +01:00
6df5b8a703 Make backspace work 2022-10-30 10:08:52 +01:00
e640c6e245 Kernel, libc, userspace: Add file timestamps (atime,ctime,mtime) 2022-10-30 09:57:17 +01:00
8d46c9bbe2 Kernel, libc: Fix a big bug in printf()
Every time printf flushes the buffer to us in sprintf() or snprintf(), we call strncat to append the data.

But we want to start from the beginning in the first flush. What if there was data already there?
Well, we just append to the old data. Which is not good, and breaks snprintf()'s maximum size policy.

This fix sets the first byte of str to NULL, to avoid this.
2022-10-30 09:53:23 +01:00
e705810af3 apps: Add a new date utility
This program tells you the current date and time :)
2022-10-30 09:09:24 +01:00
e2b5c1bfdd uptime: Use clock_gettime instead of reading from /dev/uptime 2022-10-30 09:09:03 +01:00
324fb42ee2 libc: Add support for the new time functionality in the kernel 2022-10-30 09:08:29 +01:00
688a640a16 Kernel: Add the clock_gettime syscall, which replaces clock as it can be used for more stuff 2022-10-30 09:07:59 +01:00
baf97840e9 Kernel: Keep track of boot time 2022-10-30 09:07:03 +01:00
a9da58421f su: do not pass out-of-bounds argv to execv when argc is 1 2022-10-29 20:13:40 +02:00
d1e4bc5504 Kernel: Use framebuffer virtual address instead of physical address
Just found out bootboot.fb_ptr was the physical address, not virtual.
That explains why we were getting page faults while writing to the physical address of the framebuffer. (we were in a user address space when doing so)
So this should probably make the system much more stable!!
2022-10-29 20:10:49 +02:00
8395eb16f6 session: endpwent on exit 2022-10-29 10:18:39 +02:00
7aad256acd Update README.md 2022-10-29 10:13:36 +02:00
33ed6e5c17 su: endpwent on exit 2022-10-29 10:01:51 +02:00
8375701bf6 session, su: Split password-collecting logic into a separate function 2022-10-29 10:01:44 +02:00
3e052c72a0 init: don't wait before executing the session binary 2022-10-29 10:01:17 +02:00
32e09d3417 libc: Stub out more functionality so part of binutils builds 2022-10-29 09:39:12 +02:00
9db1e8cdb3 Inform of the default user configuration in the MOTD 2022-10-28 21:58:17 +02:00
e9092ab235 sh: Display username instead of shell's PID in prompt 2022-10-28 21:57:07 +02:00
0b838572e1 apps: Add a new session program which manages user login 2022-10-28 21:56:52 +02:00
702cc0442c endpwent: Close all opened instances of /etc/passwd, including those used by getpwuid() and getpwnam() 2022-10-28 21:02:55 +02:00
2ca20c1a1e compilation fix 2022-10-28 21:02:23 +02:00
91470851cd stat: Show username of file owner 2022-10-28 21:00:33 +02:00
5aba1c5f15 su: Make it setuid root, ask for a password, and accept a username
It asks for a password only if you are not root, obviously.
2022-10-28 20:57:13 +02:00
477af66cdc ps: Show usernames of processes, using getpwuid() 2022-10-28 20:55:39 +02:00
7d0e442cde libc: Add /etc/passwd and the pwd.h API
getpwent, getpwnam, getpwuid... they may have been a pain to implement but once they work they're awesome :)

Right now passwords are stored in plaintext in the world-readable passwd file, which is not good.
But I don't have any sort of hashing implemented so it'll stay that way for now.
2022-10-28 20:55:00 +02:00
e05f3f5325 Kernel: Read file modes from the initrd, filtering out write permissions 2022-10-28 20:52:10 +02:00
a2d9ada4dc oopsie 2022-10-28 19:38:04 +02:00
b8296eb92d su: Also set group ID 2022-10-28 17:55:56 +02:00
fa0dc4b18c apps: Remove sym and crash
Not necessary anymore.
2022-10-28 17:53:22 +02:00
68d0d0b759 apps: Add a new su utility 2022-10-28 17:52:46 +02:00
8f0e358360 libc: Add setuid, setgid, seteuid, setegid 2022-10-28 17:52:39 +02:00
09a615bd99 Kernel, libc: Expose processes' UID and GID in pstat() 2022-10-28 17:31:34 +02:00
acdc2d3ad7 init: Open /dev/console for appending 2022-10-28 17:31:00 +02:00
17671fd435 libc: Implement getuid, geteuid, getgid, getegid 2022-10-28 17:24:28 +02:00
1c4f1ab867 Kernel, libc: Add setuid() and setgid() system calls 2022-10-28 17:19:26 +02:00
2269ec267c apps: Add a 'stat' utility 2022-10-28 17:14:20 +02:00
b1729689df libc: Open stdout and stderr write-only on init 2022-10-28 17:14:09 +02:00
77d331b258 init: Open /etc/motd with O_CLOEXEC to avoid leaking this file to children 2022-10-28 17:13:45 +02:00
c312d81de4 Kernel, libc: Add st_uid and st_gid to stat, and handle st_mode differently 2022-10-28 17:13:20 +02:00
26b20938de Kernel, libc: Use mode in mkdir() 2022-10-28 17:11:35 +02:00
0115cce750 Kernel/VFS: Add file owners and file modes, and check those in system calls 2022-10-28 17:10:28 +02:00
6ddfc5ee52 Kernel: Invoke sys_stat when asked 2022-10-28 17:06:33 +02:00
16dc227a05 Kernel: Add UID and GID fields to Task 2022-10-28 17:06:13 +02:00
3effe8b004 Kernel: Add EACCES to std/errno.h 2022-10-28 17:02:27 +02:00
b2f321c0b8 libc: Parse the mode string 2022-10-28 17:02:08 +02:00
8ed0ff1381 libc: Ensure /dev/random is opened with O_CLOEXEC on init 2022-10-27 17:50:15 +02:00
d93a4062a2 libc: Do not use the heavy variadic syscall() function for wrappers
That function is meant more for user programs, and should still be rarely used, since it's not portable.
Instead, we already know the number of arguments. We just call __lc_fast_syscallN, which also sets errno.
2022-10-27 17:42:00 +02:00
0eb0ca4028 libc: Add an empty locale.h 2022-10-27 17:23:59 +02:00
7155cf8d6b libc: Add alloca.h 2022-10-27 17:23:50 +02:00
703b0a1435 libc: Make tv_usec in timeval signed, as it should be 2022-10-27 17:20:46 +02:00
50cda50f01 Kernel, libc: Add F_GETFD, F_SETFD and FD_CLOEXEC 2022-10-27 17:17:24 +02:00
fcf53ef6a5 Kernel: Make waitpid() block by default unless WNOHANG is specified 2022-10-27 17:05:42 +02:00
da8a3de480 It's actually S_ISCHR in this case 2022-10-27 08:10:35 +02:00
cdb1f46b93 libc: Add S_ISDEV 2022-10-27 08:09:10 +02:00
06e6429567 Kernel: Reorganize a bit of scheduler code 2022-10-27 08:07:34 +02:00
f9dad8a8d6 Kernel, libc: Stub out struct stat.st_dev 2022-10-27 08:01:33 +02:00
9b0f6b6595 Kernel, libc: Add O_EXCL 2022-10-27 07:55:59 +02:00
211c76f920 libc: Provide a definition for timeval 2022-10-27 07:55:02 +02:00
1c35eabb2b open(): Add a third optional mode argument 2022-10-27 07:52:57 +02:00
a3c6635f3e Kernel, libc: Add O_APPEND and stub out O_CREAT and O_TRUNC 2022-10-27 07:43:55 +02:00
651ffe6d64 Ports: Simplify remove-port.sh 2022-10-26 22:00:31 +02:00
d875224045 Ports: Add mpc, mpfr and gmp ports 2022-10-26 21:56:43 +02:00
f004122735 cat: Remove unnecessary include 2022-10-26 20:55:08 +02:00
41f7232b77 Devices: Return EOF after first read for some devices
Still not optimal.
2022-10-26 20:54:47 +02:00
d5a64319f9 apps: Add cat 2022-10-26 20:51:20 +02:00
2512acc716 ls: Use command-line arguments 2022-10-26 20:30:22 +02:00
e1f58c0163 Kernel: If wrapping a line at screen bottom, scroll properly 2022-10-26 20:14:24 +02:00
6892fd96d0 sh: Split a command into arguments and pass those on
This is a big achievement!!
2022-10-26 20:06:21 +02:00
a4e430d35e Kernel: count the null byte while calculating how much stack space argv will use 2022-10-26 20:05:24 +02:00
23b12d2d56 sh: Clear exit status if we do not execute another command 2022-10-26 19:39:04 +02:00
5492b1b44d sh: Implement our own execvp() while we wait for libc.
Of course, this is a very primitive execvp with hardcoded paths.
If it were decent, it would be integrated into libc instantly.
2022-10-26 19:36:09 +02:00
796d61020b argv might be null when we're init 2022-10-26 19:22:50 +02:00
4f6333ca17 compilation fix 2022-10-26 19:17:26 +02:00
8eb986df63 libc: Add program_invocation_name
This is a GNU extension, but I'm fine with adding it to libc.
It's guarded by the _GNU_SOURCE feature test macro anyways.
2022-10-26 19:17:05 +02:00
7d20c507b1 Kernel, libc, userspace: Implement command-line arguments (argv)
The only thing missing now is for sh to pass them on.
2022-10-26 18:57:06 +02:00
0bad662c2f sh: If fork() fails, show an error containing fork 2022-10-26 17:13:47 +02:00
9b1e50ae27 libc: Add EACCES 2022-10-25 20:29:56 +02:00
57658f2d3e libc: Add EOPNOTSUPP 2022-10-25 20:28:12 +02:00
13fce2c4b3 libc: Update headers with more comments 2022-10-25 19:27:24 +02:00
af452e2b2a Kernel, libc: Add dup2() 2022-10-25 18:58:06 +02:00
af46b8d9ac Kernel: Cleanup file descriptor validation 2022-10-25 18:35:17 +02:00
ec2c314234 Kernel: Add /dev/null 2022-10-25 17:59:55 +02:00
8cfede341f Tools: Fix run.sh and update rebuild-and-run.sh to use fast-run.sh 2022-10-24 21:18:28 +02:00
ada7f1e701 Update README.md 2022-10-24 21:15:22 +02:00
7c460855cc Update README.md with information about hourly builds 2022-10-24 21:12:22 +02:00
35da7e0223 Fix up install-built-ports.sh if ports.list is not present 2022-10-24 20:52:52 +02:00
d458c5c848 Fix kernel/Makefile 2022-10-24 20:52:11 +02:00
1716a81e82 no need to unset filter-lines 2022-10-24 20:48:57 +02:00
b17ff6319a libc: compilation fix for libc's printf 2022-10-24 20:47:00 +02:00
040fbde462 Unset LD and AR so gcc doesn't get confused 2022-10-24 18:33:29 +00:00
bd56c7f496 Unset LD and AR so binutils doesn't get confused during the build process 2022-10-24 18:32:57 +00:00
33f6765a5c libc: Make the userspace printf much better 2022-10-24 17:21:40 +02:00
315d2f9f24 libc: actually return buf in getcwd() 2022-10-24 17:08:40 +02:00
0dec5f7bad libc: Add dummy getcwd() 2022-10-24 17:05:28 +02:00
e7d41fa6dc ports: Add a little script to list installed ports 2022-10-23 20:11:30 +02:00
3db342e897 tools: only unset filter-lines if it is not set 2022-10-23 20:07:26 +02:00
2d807e3ca5 tools: Add prefix messages that tell us where we're at in a build, inspired by SerenityOS :) 2022-10-23 20:03:29 +02:00
fb0fc29087 Adjust install-built-ports.sh 2022-10-23 19:27:34 +02:00
c7598b08e0 BUILD FASTER 2022-10-23 19:27:19 +02:00
b615166373 libc: Stub out tmpfile() as well as add an empty math.h 2022-10-23 18:58:48 +02:00
58b01b74e2 Kernel, libc: Add stat() 2022-10-23 18:35:32 +02:00
0c04246300 Next version! 2022-10-23 17:24:18 +02:00
180 changed files with 5866 additions and 893 deletions

7
.gitignore vendored
View File

@ -2,14 +2,13 @@ Luna.iso
toolchain/ toolchain/
.vscode/ .vscode/
**/*.o **/*.o
initrd/boot/moon.elf initrd/boot/moon
kernel/bin/moon.elf kernel/bin/moon
initrd/sys/moon.sym initrd/sys/moon.sym
initrd/bin/** initrd/bin/**
apps/bin/** apps/bin/**
tests/**/bin/** tests/**/bin/**
base/usr/include/** base/usr/**
base/usr/lib/**
**/*.a **/*.a
ports/**/workdir/** ports/**/workdir/**
ports/ports.list ports/ports.list

View File

@ -5,22 +5,22 @@ AR := x86_64-luna-ar
LD := x86_64-luna-ld LD := x86_64-luna-ld
build: build:
tools/sync-libc.sh +@tools/sync-libc.sh
@$(MAKE) -C kernel build +@tools/buildstep.sh kernel build
@$(MAKE) -C libs build +@tools/buildstep.sh libs build
@$(MAKE) -C apps build +@tools/buildstep.sh apps build
clean: initrd-clean clean: initrd-clean
@$(MAKE) -C kernel clean +@tools/buildstep.sh kernel clean
@$(MAKE) -C libs clean +@tools/buildstep.sh libs clean
@$(MAKE) -C apps clean +@tools/buildstep.sh apps clean
initrd-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 rm -rf $(LUNA_ROOT)/initrd/bin
install: install:
@$(MAKE) -C kernel install +@tools/buildstep.sh kernel install
@$(MAKE) -C libs install +@tools/buildstep.sh libs install
@$(MAKE) -C apps install +@tools/buildstep.sh apps install
@tools/install-built-ports.sh +@tools/install-built-ports.sh

View File

@ -1,16 +1,20 @@
# Luna # 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 ## Features
- x86_64-compatible [kernel](kernel/). - 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). - 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. - 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.
- Basic preemptive multitasking, round-robin [scheduler](kernel/src/thread/) that can switch between tasks. - Preemptive multitasking, with a 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. - Can [load ELF programs](kernel/src/sys/elf/) from the file system as userspace tasks.
- [System call](kernel/src/sys/) interface and simple [C Library](libs/libc/), aiming to be POSIX-compatible. - [System call](kernel/src/sys/) interface and [C Library](libs/libc/), aiming to be mostly POSIX-compatible.
- Some very simple [example programs](apps/), written in C, that then can get loaded and executed by the kernel at userspace demand.
- UNIX-like [multitasking primitives](kernel/src/sys/exec.cpp), which allow user tasks to spawn other tasks. - 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 ## 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)). 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) `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. 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 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. 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? ## Is there third-party software I can use on Luna?
Yes, actually! Check out the [ports](ports/) directory. 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/C++ (and C++ was added recently, its standard library doesn't work very well) 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 for Luna right now.
But feel free to try to port some program yourself and add it to the ports directory! But feel free to try to port some program yourself and add it to the ports directory!

View File

@ -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_DIR := $(LUNA_ROOT)/apps
APPS_SRC := $(APPS_DIR)/src APPS_SRC := $(APPS_DIR)/src
APPS_BIN := $(APPS_DIR)/bin 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 $(APPS_BIN)/%: $(APPS_SRC)/%.c
@mkdir -p $(@D) @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 @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: clean:
rm -f $(APPS_BIN)/* rm -f $(APPS_BIN)/*

7
apps/src/args.c Normal file
View 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
View 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);
}
}
}
}

View File

@ -1,5 +0,0 @@
int main()
{
int* ptr = (int*)0xdeadbeef;
*ptr = 6;
}

9
apps/src/date.c Normal file
View 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
View File

@ -0,0 +1,6 @@
#include <cstdio>
int main()
{
printf("Well hello world!\n");
}

View File

@ -1,4 +1,5 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <luna.h> #include <luna.h>
#include <stdio.h> #include <stdio.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -6,15 +7,21 @@
void show_motd() 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 (!fp)
{ {
if (errno != ENOENT) { perror("fopen"); } perror("fopen");
return; return;
} }
char buf[4096]; 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)) if (ferror(fp))
{ {
perror("fread"); perror("fread");
@ -34,14 +41,18 @@ int main()
{ {
if (getpid() != 1) 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; return 1;
} }
show_motd(); show_motd();
msleep(200);
pid_t child = fork(); pid_t child = fork();
if (child < 0) if (child < 0)
{ {
@ -50,7 +61,8 @@ int main()
} }
if (child == 0) if (child == 0)
{ {
execv("/bin/sh", NULL); char* argv[] = {"/bin/session", NULL};
execv(argv[0], argv);
perror("execv"); perror("execv");
return 1; return 1;
} }
@ -59,10 +71,7 @@ int main()
for (;;) for (;;)
{ {
while ((result = wait(NULL)) == 0) // No child has exited yet result = wait(NULL);
{ if (result == child) return 0;
msleep(100);
}
if (result == child) { return 0; }
} }
} }

View File

@ -1,22 +1,31 @@
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.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) if (!dp)
{ {
perror("opendir"); perror("opendir");
return 1; return 1;
} }
bool first_ent = true;
do { do {
struct dirent* ent = readdir(dp); struct dirent* ent = readdir(dp);
if (!ent) break; if (!ent) break;
printf("%s\n", ent->d_name); printf(first_ent ? "%s" : " %s", ent->d_name);
first_ent = false;
} while (1); } while (1);
printf("\n");
closedir(dp); closedir(dp);
return 0; return 0;
} }

17
apps/src/mkdir.c Normal file
View 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;
}
}

View File

@ -1,5 +1,6 @@
#include <errno.h> #include <errno.h>
#include <luna/pstat.h> #include <luna/pstat.h>
#include <pwd.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@ -17,8 +18,10 @@ pid_t get_current_max_threads()
void display_process(struct pstat* pstatbuf) 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), struct passwd* pwd = getpwuid(pstatbuf->pt_uid);
pstatbuf->pt_time); 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) int try_pstat(pid_t pid, struct pstat* pstatbuf)
@ -41,4 +44,5 @@ int main()
{ {
if (try_pstat(pid, &pst)) { display_process(&pst); } if (try_pstat(pid, &pst)) { display_process(&pst); }
} }
endpwent();
} }

32
apps/src/screen.c Normal file
View 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
View 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;
}

View File

@ -1,6 +1,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <luna.h> #include <luna.h>
#include <pwd.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -8,29 +9,89 @@
#include <unistd.h> #include <unistd.h>
static int status = 0; static int status = 0;
static char* username = NULL;
typedef struct typedef struct
{ {
char* buffer; char* buffer;
size_t size; size_t size;
size_t capacity; 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() void show_prompt()
{ {
if (WEXITSTATUS(status)) { printf("%d [%ld]> ", WEXITSTATUS(status), getpid()); } if (WEXITSTATUS(status)) { printf("%d [%s]> ", WEXITSTATUS(status), username); }
else 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 if (cmd->size <= strlen(string)) // cmd->size includes null terminator
return 0; return 0;
return strncmp(cmd->buffer, string, strlen(string)) == 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 if (cmd->size <= strlen(string)) // cmd->size includes null terminator
return 0; return 0;
@ -38,19 +99,24 @@ int command_matches_exactly(command* cmd, const char* string)
return strncmp(cmd->buffer, string, strlen(string)) == 0; 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(cmd, "exit ")) { exit(atoi(cmd->buffer + 5)); }
if (command_matches_exactly(cmd, "exit")) { exit(0); } 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 1;
} }
return 0; 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); char* buffer = realloc(cmd->buffer, new_capacity);
if (!buffer) if (!buffer)
@ -62,105 +128,101 @@ void command_expand(command* cmd, long new_capacity)
cmd->capacity = 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); if (cmd->size == cmd->capacity) command_expand(cmd, cmd->capacity + 8);
cmd->buffer[cmd->size] = c; cmd->buffer[cmd->size] = c;
cmd->size++; cmd->size++;
} }
void command_pop(command* cmd) void command_pop(command_t* cmd)
{ {
cmd->size--; cmd->size--;
} }
void command_init(command* cmd) void command_init(command_t* cmd)
{ {
cmd->buffer = malloc(5); cmd->buffer = malloc(5);
cmd->capacity = 5; cmd->capacity = 5;
cmd->size = 0; cmd->size = 0;
} }
void command_clear(command* cmd) void command_clear(command_t* cmd)
{ {
free(cmd->buffer); free(cmd->buffer);
return command_init(cmd); 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'); command_push(cmd, '\0');
if (command_match_builtins(cmd)) if (command_match_builtins(cmd))
{ {
command_clear(cmd); command_clear(cmd);
show_prompt(); if (cmd->interactive) show_prompt();
return; return;
} }
pid_t child = fork(); pid_t child = fork();
if (child < 0) if (child < 0)
{ {
perror(cmd->buffer); perror("fork");
command_clear(cmd); command_clear(cmd);
show_prompt(); if (cmd->interactive) show_prompt();
return; return;
} }
if (child == 0) if (child == 0) process_execute_command(cmd->buffer);
{ pid_t result = waitpid(child, &status, 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 (result < 0) if (result < 0)
{ {
perror("waitpid"); perror("waitpid");
command_clear(cmd); command_clear(cmd);
show_prompt(); if (cmd->interactive) show_prompt();
return; return;
} }
int exit_status = WEXITSTATUS(status); int exit_status = WEXITSTATUS(status);
if (exit_status == -2 || exit_status == -3) printf("(PID %ld) Segmentation fault\n", result); 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); 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 (c == '\b')
{ {
if (cmd->size != 0) if (cmd->size != 0)
{ {
putchar(c); if (cmd->interactive) putchar(c);
command_pop(cmd); command_pop(cmd);
} }
} }
else if (c == '\n') else if (c == '\n')
{ {
putchar(c); if (cmd->interactive) putchar(c);
if (cmd->size == 0) show_prompt(); if (cmd->size == 0)
{
status = 0;
if (cmd->interactive) show_prompt();
}
else else
command_execute(cmd); command_execute(cmd);
} }
else else
{ {
putchar(c); if (cmd->interactive) putchar(c);
command_push(cmd, c); command_push(cmd, c);
} }
} }
void command_concat(command* cmd, const char* str) void command_concat(command_t* cmd, const char* str)
{ {
while (*str) while (*str)
{ {
@ -169,13 +231,15 @@ void command_concat(command* cmd, const char* str)
} }
} }
int main() void shell_interactive()
{ {
show_prompt(); show_prompt();
command shell_command; command_t shell_command;
command_init(&shell_command); command_init(&shell_command);
shell_command.interactive = 1;
while (1) while (1)
{ {
int c = getchar(); int c = getchar();
@ -184,11 +248,112 @@ int main()
if (ferror(stdin)) if (ferror(stdin))
{ {
perror("getchar"); 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 assert(false); // we should never get here
} }
command_concat_char(&shell_command, (char)c); 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
View 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
View 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);
}

View File

@ -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;
}

View File

@ -1,27 +1,38 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <time.h>
#define VALUE_SINGULAR_AT_ONE(v) v, v == 1 ? "" : "s"
int main() int main()
{ {
FILE* fp = fopen("/dev/uptime", "r"); struct timespec tp;
if (!fp) 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"); printf("up for %d year%s, %d day%s, %d hour%s, %d minute%s, %d second%s\n",
return 1; 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));
} }
else if (time->tm_yday)
char buf[BUFSIZ];
fgets(buf, sizeof(buf), fp);
if (ferror(fp))
{ {
perror("fgets"); printf("up for %d day%s, %d hour%s, %d minute%s, %d second%s\n", VALUE_SINGULAR_AT_ONE(time->tm_yday),
return 1; 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_hour)
long ms_uptime = atol(buf); {
printf("up for %d hour%s, %d minute%s, %d second%s\n", VALUE_SINGULAR_AT_ONE(time->tm_hour),
printf("up for %ld seconds\n", ms_uptime / 1000); VALUE_SINGULAR_AT_ONE(time->tm_min), VALUE_SINGULAR_AT_ONE(time->tm_sec));
}
fclose(fp); 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));
} }

View File

@ -1,3 +1,3 @@
Welcome to Luna! 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
View File

@ -0,0 +1,2 @@
root:secure:0:0:Administrator:/:/bin/sh
selene:moon:1:1:Default User:/:/bin/sh

View File

@ -1,3 +1,3 @@
screen=1024x768 screen=1024x768
kernel=boot/moon.elf kernel=boot/moon
verbose=1 verbose=1

View File

@ -4,7 +4,7 @@ MOON_OBJ := $(MOON_DIR)/lib
MOON_BIN := $(MOON_DIR)/bin MOON_BIN := $(MOON_DIR)/bin
CFLAGS ?= -Os 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 CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy
ASMFLAGS := -felf64 ASMFLAGS := -felf64
LDFLAGS := -T$(MOON_DIR)/moon.ld -nostdlib -lgcc -Wl,--build-id=none -z max-page-size=0x1000 -mno-red-zone -mcmodel=kernel 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 endif
ifeq ($(MOON_BUILD_DEBUG), 1) ifeq ($(MOON_BUILD_DEBUG), 1)
CFLAGS := -ggdb ${CFLAGS} CFLAGS := -ggdb -fsanitize=undefined ${CFLAGS}
endif endif
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) 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)/%.c, $(MOON_OBJ)/%.c.o, $(C_SRC))
OBJS += $(patsubst $(MOON_SRC)/%.asm, $(MOON_OBJ)/%.asm.o, $(NASM_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 $(MOON_OBJ)/main.cpp.o: $(MOON_SRC)/main.cpp
@mkdir -p $(@D) @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 $(MOON_OBJ)/misc/config.cpp.o: $(MOON_SRC)/misc/config.cpp FORCE
@mkdir -p $(@D) @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 $(MOON_OBJ)/init/Init.cpp.o: $(MOON_SRC)/init/Init.cpp
@mkdir -p $(@D) @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 $(MOON_OBJ)/%.cpp.o: $(MOON_SRC)/%.cpp
@mkdir -p $(@D) @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 $(MOON_OBJ)/%.c.o: $(MOON_SRC)/%.c
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) $(CFLAGS) -o $@ -c $^ @$(CC) $(CFLAGS) -o $@ -c $^
@echo " CC $^"
$(MOON_OBJ)/%.asm.o: $(MOON_SRC)/%.asm $(MOON_OBJ)/%.asm.o: $(MOON_SRC)/%.asm
@mkdir -p $(@D) @mkdir -p $(@D)
$(ASM) $(ASMFLAGS) -o $@ $^ @$(ASM) $(ASMFLAGS) -o $@ $^
@echo " ASM $^"
build: $(OBJS) build: $(OBJS)
@mkdir -p $(@D) @mkdir -p $(MOON_BIN)
$(CC) $(OBJS) $(LDFLAGS) -o $(MOON_BIN)/moon.elf @$(CC) $(OBJS) $(LDFLAGS) -o $(MOON_BIN)/moon
@echo " CCLD $(MOON_BIN)/moon"
clean: clean:
rm -rf $(MOON_OBJ)/* rm -rf $(MOON_OBJ)/*
rm -rf $(MOON_BIN)/* rm -rf $(MOON_BIN)/*
install: $(MOON_BIN)/moon.elf install: $(MOON_BIN)/moon
@mkdir -p $(@D) @mkdir -p $(LUNA_ROOT)/initrd/boot
cp $^ $(LUNA_ROOT)/initrd/boot/moon.elf @cp $^ $(LUNA_ROOT)/initrd/boot/moon
$(LUNA_ROOT)/tools/generate-symbols.sh @echo " INSTALL $^"
$(STRIP) $(LUNA_ROOT)/initrd/boot/moon.elf @$(LUNA_ROOT)/tools/generate-symbols.sh
@$(STRIP) $(LUNA_ROOT)/initrd/boot/moon
@echo " STRIP $(LUNA_ROOT)/initrd/boot/moon"
.PHONY: build clean install FORCE .PHONY: build clean install FORCE
FORCE: FORCE:

View File

@ -10,4 +10,5 @@ namespace CPU
uint64_t get_feature_bitmask(); uint64_t get_feature_bitmask();
uint64_t get_initial_apic_id(); uint64_t get_initial_apic_id();
bool has_feature(CPU::Features); bool has_feature(CPU::Features);
bool has_nx();
} }

View File

@ -32,6 +32,13 @@ struct Descriptor
ssize_t read(size_t size, char* buffer); ssize_t read(size_t size, char* buffer);
ssize_t write(size_t size, const 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); void open(VFS::Node* node, bool can_read, bool can_write, bool able_to_block, bool close_on_exec);
int seek(long offset); int seek(long offset);
@ -55,6 +62,11 @@ struct Descriptor
return m_close_on_exec; return m_close_on_exec;
} }
void set_close_on_exec(bool value)
{
m_close_on_exec = value;
}
Descriptor(const Descriptor& other); Descriptor(const Descriptor& other);
Descriptor(); Descriptor();

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <sys/types.h>
#define TAR_MAGIC "ustar" #define TAR_MAGIC "ustar"
#define TAR_BLOCKSIZE 512 #define TAR_BLOCKSIZE 512
@ -33,6 +34,7 @@ namespace InitRD
uint64_t size; uint64_t size;
uint64_t size_in_blocks; uint64_t size_in_blocks;
void* addr; void* addr;
mode_t mode;
}; };
uint64_t get_total_blocks(); uint64_t get_total_blocks();

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <sys/types.h>
typedef long ssize_t; 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_read)(Node*, size_t, size_t, char*);
typedef ssize_t (*node_write)(Node*, size_t, size_t, const char*); typedef ssize_t (*node_write)(Node*, size_t, size_t, const char*);
typedef Node* (*node_finddir)(Node*, 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 int (*node_block)(Node*);
typedef Node* (*node_readdir)(Node*, long); 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 struct Node
{ {
char name[NAME_MAX]; char name[NAME_MAX];
uint64_t inode;
uint64_t length;
int type; int type;
int flags; 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_read read_func;
node_finddir find_func; node_finddir find_func;
node_readdir readdir_func; node_readdir readdir_func;
node_mkdir mkdir_func; node_mkdir mkdir_func;
node_write write_func; node_write write_func;
node_block block_func; node_block block_func;
int tty = 0; node_mmap mmap_func;
node_ioctl ioctl_func;
Node* link; Node* link;
}; };
@ -45,6 +57,9 @@ namespace VFS
int mkdir(const char* path, const char* name); int mkdir(const char* path, const char* name);
int mkdir(const char* pathname); 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); int would_block(Node* node);
void mount_root(Node* root); void mount_root(Node* root);
@ -61,4 +76,11 @@ namespace VFS
Node* root(); Node* root();
Node* readdir(Node* dir, long offset); 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);
} }

View File

@ -6,4 +6,10 @@ namespace ConsoleDevice
VFS::Node* create_new(const char* devname); VFS::Node* create_new(const char* devname);
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer); 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);
} }

View 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);
}

View File

@ -1,9 +1,10 @@
#pragma once #pragma once
#include "fs/VFS.h" #include "fs/VFS.h"
namespace UptimeDevice namespace NullDevice
{ {
VFS::Node* create_new(const char* devname); 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); ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
} }

View File

@ -8,11 +8,14 @@
#define MAP_READ_WRITE 1 << 0 #define MAP_READ_WRITE 1 << 0
#define MAP_USER 1 << 1 #define MAP_USER 1 << 1
#define MAP_EXEC 1 << 2 #define MAP_EXEC 1 << 2
#define MAP_AS_OWNED_BY_TASK 1 << 3
namespace MemoryManager namespace MemoryManager
{ {
void init(); void init();
void protect_kernel_sections();
void* get_mapping(void* physicalAddress, int flags = MAP_READ_WRITE); void* get_mapping(void* physicalAddress, int flags = MAP_READ_WRITE);
void release_mapping(void* mapping); void release_mapping(void* mapping);
@ -31,4 +34,7 @@ namespace MemoryManager
void release_pages(void* pages, uint64_t count); void release_pages(void* pages, uint64_t count);
void protect(void* page, uint64_t count, int flags); 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);
} }

View File

@ -17,13 +17,18 @@ struct PageDirectoryEntry
bool larger_pages : 1; bool larger_pages : 1;
bool ignore1 : 1; bool ignore1 : 1;
uint8_t available : 3; 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); void set_address(uint64_t addr);
uint64_t get_address(); uint64_t get_address();
}; } __attribute__((packed));
struct PageTable struct PageTable
{ {
PageDirectoryEntry entries[512]; PageDirectoryEntry entries[512];
} __attribute__((aligned(PAGE_SIZE))); } __attribute__((aligned(PAGE_SIZE)));
static_assert(sizeof(PageDirectoryEntry) == 8UL);

View File

@ -6,7 +6,8 @@ enum Flags
{ {
ReadWrite = 1 << 0, ReadWrite = 1 << 0,
User = 1 << 1, User = 1 << 1,
Execute = 1 << 2 Execute = 1 << 2,
OwnedByTask = 1 << 3,
}; };
namespace VMM namespace VMM
{ {
@ -33,9 +34,11 @@ namespace VMM
PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr); PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr);
void propagate_read_write(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 propagate_user(PageTable* root, uint64_t vaddr);
void flush_tlb(uint64_t addr); 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, void decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
uint64_t& pdp_index); uint64_t& pdp_index);

View File

@ -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)

View 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

View File

@ -4,10 +4,13 @@
#define ENOENT 2 #define ENOENT 2
#define ESRCH 3 #define ESRCH 3
#define EINTR 4 #define EINTR 4
#define E2BIG 7
#define ENOEXEC 8 #define ENOEXEC 8
#define EBADF 9 #define EBADF 9
#define ECHILD 10
#define EAGAIN 11 #define EAGAIN 11
#define ENOMEM 12 #define ENOMEM 12
#define EACCES 13
#define EFAULT 14 #define EFAULT 14
#define EEXIST 17 #define EEXIST 17
#define ENOTDIR 20 #define ENOTDIR 20
@ -16,5 +19,6 @@
#define EMFILE 24 #define EMFILE 24
#define ENOTTY 25 #define ENOTTY 25
#define ENOSPC 28 #define ENOSPC 28
#define ERANGE 36
#define ENOSYS 38 #define ENOSYS 38
#define ENOTSUP 95 #define ENOTSUP 95

View File

@ -1,34 +1,43 @@
#pragma once #pragma once
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include <stddef.h> #include <stddef.h>
#include <sys/types.h>
#define SYS_exit 0 #define SYS_exit 0
#define SYS_yield 1 #define SYS_yield 1
#define SYS_sleep 2 #define SYS_sleep 2
#define SYS_write 3 #define SYS_write 3
#define SYS_paint 4 #define SYS_getprocid 4
#define SYS_getprocid 5 #define SYS_mmap 5
#define SYS_mmap 6 #define SYS_munmap 6
#define SYS_munmap 7 #define SYS_open 7
#define SYS_open 8 #define SYS_read 8
#define SYS_read 9 #define SYS_close 9
#define SYS_close 10 #define SYS_seek 10
#define SYS_seek 11 #define SYS_execv 11
#define SYS_exec 12 #define SYS_fcntl 12
#define SYS_fcntl 13 #define SYS_mprotect 13
#define SYS_mprotect 14 #define SYS_clock_gettime 14
#define SYS_clock 15 #define SYS_mkdir 15
#define SYS_mkdir 16 #define SYS_fork 16
#define SYS_fork 17 #define SYS_waitpid 17
#define SYS_waitpid 18 #define SYS_access 18
#define SYS_access 19 #define SYS_fstat 19
#define SYS_fstat 20 #define SYS_pstat 20
#define SYS_pstat 21 #define SYS_getdents 21
#define SYS_getdents 22 #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 stat;
struct pstat; struct pstat;
struct luna_dirent; struct luna_dirent;
struct timespec;
namespace Syscall namespace Syscall
{ {
@ -39,22 +48,29 @@ void sys_exit(Context* context, int status);
void sys_yield(Context* context); void sys_yield(Context* context);
void sys_sleep(Context* context, uint64_t ms); void sys_sleep(Context* context, uint64_t ms);
void sys_write(Context* context, int fd, size_t size, const char* addr); 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_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_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_read(Context* context, int fd, size_t size, char* buffer);
void sys_close(Context* context, int fd); void sys_close(Context* context, int fd);
void sys_seek(Context* context, int fd, long offset, int whence); 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_fcntl(Context* context, int fd, int command, uintptr_t arg);
void sys_mprotect(Context* context, void* address, size_t size, int prot); void sys_mprotect(Context* context, void* address, size_t size, int prot);
void sys_clock(Context* context); void sys_clock_gettime(Context* context, int clock_id, struct timespec* tp);
void sys_mkdir(Context* context, const char* filename); void sys_mkdir(Context* context, const char* filename, mode_t mode);
void sys_fork(Context* context); void sys_fork(Context* context);
void sys_waitpid(Context* context, long pid, int* wstatus, int options); void sys_waitpid(Context* context, long pid, int* wstatus, int options);
void sys_access(Context* context, const char* path, int amode); void sys_access(Context* context, const char* path, int amode);
void sys_fstat(Context* context, int fd, struct stat* buf); void sys_fstat(Context* context, int fd, struct stat* buf);
void sys_pstat(Context* context, long pid, struct pstat* 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_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);

View File

@ -8,8 +8,16 @@
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/utils.h" #include "misc/utils.h"
#include <stddef.h>
char* strdup_from_user(const char* user_string); 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 // 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. // it, so that's not really possible. But it should be done in the future.

View File

@ -4,9 +4,20 @@
#include "memory/AddressSpace.h" #include "memory/AddressSpace.h"
#include "memory/UserHeap.h" #include "memory/UserHeap.h"
#include "sys/elf/Image.h" #include "sys/elf/Image.h"
#include <sys/types.h>
#define TASK_MAX_FDS 32 #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 struct Task
{ {
enum TaskState enum TaskState
@ -29,6 +40,11 @@ struct Task
int64_t task_time = 0; int64_t task_time = 0;
uid_t uid;
uid_t euid;
gid_t gid;
gid_t egid;
Task* next_task = nullptr; Task* next_task = nullptr;
Task* prev_task = nullptr; Task* prev_task = nullptr;
@ -45,7 +61,8 @@ struct Task
bool is_user_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]; Descriptor files[TASK_MAX_FDS];
@ -69,14 +86,38 @@ struct Task
char name[128]; char name[128];
struct mode_t umask;
{
size_t size;
int fd;
char* buf;
} blocking_read_info;
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(); 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();
}; };

View File

@ -0,0 +1,4 @@
#pragma once
#include <stdint.h>
void determine_user_page_fault_reason(uintptr_t faulting_address);

View File

@ -0,0 +1,4 @@
#pragma once
long parse_decimal(const char* str);
long parse_octal(const char* str);

View 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]);

View File

@ -16,14 +16,19 @@ SECTIONS
kernel_start = .; kernel_start = .;
.text : { .text : {
KEEP(*(.text.boot)) *(.text .text.*) /* code */ KEEP(*(.text.boot)) *(.text .text.*) /* code */
. = ALIGN(0x1000);
start_of_kernel_rodata = .;
*(.rodata .rodata.*) /* data */ *(.rodata .rodata.*) /* data */
end_of_kernel_rodata = .;
. = ALIGN(0x1000);
start_of_kernel_data = .;
*(.data .data.*) *(.data .data.*)
} :boot } :boot
.bss (NOLOAD) : { /* bss */ .bss (NOLOAD) : { /* bss */
. = ALIGN(16);
*(.bss .bss.*) *(.bss .bss.*)
*(COMMON) *(COMMON)
} :boot } :boot
end_of_kernel_data = .;
kernel_end = .; kernel_end = .;
/DISCARD/ : { *(.eh_frame) *(.comment) } /DISCARD/ : { *(.eh_frame) *(.comment) }

View File

@ -66,9 +66,17 @@ uint64_t CPU::get_feature_bitmask()
return 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) 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) bool CPU::has_feature(CPU::Features feature)

View File

@ -1,5 +1,7 @@
#include "fs/FileDescriptor.h" #include "fs/FileDescriptor.h"
#include "std/errno.h" #include "std/errno.h"
#include "std/stdlib.h"
#include "sys/UserMemory.h"
Descriptor::Descriptor() : m_is_open(false) Descriptor::Descriptor() : m_is_open(false)
{ {
@ -37,6 +39,42 @@ ssize_t Descriptor::write(size_t size, const char* buffer)
return result; 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) int Descriptor::seek(long offset)
{ {
if (m_node->type == VFS_FILE && (uint64_t)offset > m_node->length) if (m_node->type == VFS_FILE && (uint64_t)offset > m_node->length)

View File

@ -1,6 +1,6 @@
#define MODULE "initrd" #define MODULE "initrd"
#include "init/InitRD.h" #include "fs/InitRD.h"
#include "bootboot.h" #include "bootboot.h"
#include "fs/VFS.h" #include "fs/VFS.h"
#include "io/Serial.h" #include "io/Serial.h"
@ -10,6 +10,9 @@
#include "std/errno.h" #include "std/errno.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.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; extern BOOTBOOT bootboot;
@ -18,6 +21,9 @@ static bool initrd_initialized = false;
static VFS::Node initrd_root; static VFS::Node initrd_root;
extern uint64_t clock_boot(); // defined in sys/clock.cpp
extern uint64_t clock_now();
bool InitRD::is_initialized() bool InitRD::is_initialized()
{ {
return initrd_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) InitRD::File InitRD::get_file(TarHeader* header)
{ {
File result; 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); 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.addr = (void*)((uint64_t)header + TAR_BLOCKSIZE);
result.size_in_blocks = Utilities::get_blocks_from_size(TAR_BLOCKSIZE, result.size); result.size_in_blocks = Utilities::get_blocks_from_size(TAR_BLOCKSIZE, result.size);
result.mode = (mode_t)parse_octal(header->mode);
return result; 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 #define INITRD_MAX_FILES 64
namespace InitRD namespace InitRD
@ -195,7 +198,7 @@ VFS::Node* initrd_read_dir(VFS::Node* node, long offset)
return dir.files[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) 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.mkdir_func = initrd_mkdir;
new_node.length = 0; new_node.length = 0;
new_node.type = VFS_DIRECTORY; 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)); strncpy(new_node.name, name, sizeof(new_node.name));
InitRD::Directory dir; InitRD::Directory dir;
strncpy(dir.name, name, sizeof(dir.name)); 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.mkdir_func = initrd_mkdir;
node.readdir_func = initrd_read_dir; node.readdir_func = initrd_read_dir;
node.length = 0; 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(node.name, buffer, sizeof(node.name));
strncpy(dir.name, buffer, sizeof(dir.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.read_func = initrd_read;
node.length = f.size; node.length = f.size;
node.type = VFS_FILE; 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(node.name, buffer, sizeof(node.name));
strncpy(f.name, buffer, sizeof(f.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() static void initrd_scan()
{ {
initrd_for_each_dir([](InitRD::Directory& dir) { 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); kwarnln("Failed to register directory %s: Too many directories in initrd", dir.name);
return; return;
@ -368,7 +380,7 @@ static void initrd_scan()
if (initrd_register_dir(dir, inode)) dirs[total_dirs++] = dir; if (initrd_register_dir(dir, inode)) dirs[total_dirs++] = dir;
}); });
InitRD::for_each([](InitRD::File& f) { 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); kwarnln("Failed to register file %s: Too many files in initrd", f.name);
return; return;
@ -383,6 +395,9 @@ static void initrd_initialize_root()
initrd_root.length = 0; initrd_root.length = 0;
initrd_root.inode = 0; initrd_root.inode = 0;
initrd_root.type |= VFS_DIRECTORY; 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]; InitRD::Directory& root = dirs[0];
total_dirs++; total_dirs++;
strncpy(initrd_root.name, "initrd", sizeof(initrd_root.name)); strncpy(initrd_root.name, "initrd", sizeof(initrd_root.name));

View File

@ -152,12 +152,58 @@ int VFS::mkdir(const char* path, const char* name)
kwarnln("Chosen node does not support finddir()"); kwarnln("Chosen node does not support finddir()");
return -ENOTSUP; 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) if (node->find_func(node, name) != nullptr)
{ {
kwarnln("Already exists"); kwarnln("Already exists");
return -EEXIST; 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) int VFS::mkdir(const char* pathname)
@ -178,6 +224,24 @@ int VFS::mkdir(const char* pathname)
return result; 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) bool VFS::exists(const char* pathname)
{ {
return resolve_path(pathname) != nullptr; return resolve_path(pathname) != nullptr;
@ -209,3 +273,34 @@ VFS::Node* VFS::readdir(VFS::Node* dir, long offset)
if (!dir->readdir_func) return 0; if (!dir->readdir_func) return 0;
return dir->readdir_func(dir, offset); 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;
}

View File

@ -5,15 +5,30 @@
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.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* ConsoleDevice::create_new(const char* devname)
{ {
VFS::Node* dev = new VFS::Node; VFS::Node* dev = new VFS::Node;
dev->write_func = ConsoleDevice::write; dev->write_func = ConsoleDevice::write;
dev->read_func = ConsoleDevice::read;
dev->block_func = ConsoleDevice::would_block;
dev->inode = 0; dev->inode = 0;
dev->length = 0; dev->length = 0;
dev->type = VFS_DEVICE; dev->type = VFS_DEVICE;
dev->flags = 0; dev->flags = 0;
dev->tty = 1; 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)); strncpy(dev->name, devname, sizeof(dev->name));
return dev; 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); TextRenderer::write(buffer, size);
return (ssize_t)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;
}

View File

@ -1,9 +1,10 @@
#include "fs/devices/DeviceFS.h" #include "fs/devices/DeviceFS.h"
#include "fs/devices/Console.h" #include "fs/devices/Console.h"
#include "fs/devices/Framebuffer.h"
#include "fs/devices/Keyboard.h" #include "fs/devices/Keyboard.h"
#include "fs/devices/NullDevice.h"
#include "fs/devices/Random.h" #include "fs/devices/Random.h"
#include "fs/devices/Serial.h" #include "fs/devices/Serial.h"
#include "fs/devices/Uptime.h"
#include "fs/devices/Version.h" #include "fs/devices/Version.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
@ -15,6 +16,8 @@ VFS::Node* devfs_root = nullptr;
VFS::Node* devfs_files[DEVFS_MAX_FILES]; VFS::Node* devfs_files[DEVFS_MAX_FILES];
int devfs_file_count = 0; int devfs_file_count = 0;
extern uint64_t clock_boot();
VFS::Node* DeviceFS::get() VFS::Node* DeviceFS::get()
{ {
if (devfs_root) return devfs_root; if (devfs_root) return devfs_root;
@ -24,6 +27,9 @@ VFS::Node* DeviceFS::get()
devfs_root->type = VFS_DIRECTORY; devfs_root->type = VFS_DIRECTORY;
devfs_root->find_func = DeviceFS::finddir; devfs_root->find_func = DeviceFS::finddir;
devfs_root->readdir_func = DeviceFS::readdir; 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)); strncpy(devfs_root->name, "dev", sizeof(devfs_root->name));
devfs_files[devfs_file_count++] = VersionDevice::create_new("version"); 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++] = SerialDevice::create_new("serial");
devfs_files[devfs_file_count++] = RandomDevice::create_new("random"); devfs_files[devfs_file_count++] = RandomDevice::create_new("random");
devfs_files[devfs_file_count++] = KeyboardDevice::create_new("kbd"); 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; devfs_root->length = devfs_file_count;
return devfs_root; return devfs_root;
} }

View 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;
}
}

View File

@ -16,6 +16,8 @@ int KeyboardDevice::would_block(VFS::Node*)
return kbd_bufsize == 0; return kbd_bufsize == 0;
} }
extern uint64_t clock_boot();
VFS::Node* KeyboardDevice::create_new(const char* devname) VFS::Node* KeyboardDevice::create_new(const char* devname)
{ {
VFS::Node* dev = new VFS::Node; VFS::Node* dev = new VFS::Node;
@ -26,6 +28,9 @@ VFS::Node* KeyboardDevice::create_new(const char* devname)
dev->type = VFS_DEVICE; dev->type = VFS_DEVICE;
dev->flags = 0; dev->flags = 0;
dev->tty = 1; 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)); strncpy(dev->name, devname, sizeof(dev->name));
return dev; return dev;
} }

View 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;
}

View File

@ -6,6 +6,8 @@
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
extern uint64_t clock_boot();
VFS::Node* RandomDevice::create_new(const char* devname) VFS::Node* RandomDevice::create_new(const char* devname)
{ {
VFS::Node* dev = new VFS::Node; VFS::Node* dev = new VFS::Node;
@ -14,6 +16,9 @@ VFS::Node* RandomDevice::create_new(const char* devname)
dev->length = 0; dev->length = 0;
dev->type = VFS_DEVICE; dev->type = VFS_DEVICE;
dev->flags = 0; 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)); strncpy(dev->name, devname, sizeof(dev->name));
return dev; return dev;
} }

View File

@ -5,6 +5,8 @@
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
extern uint64_t clock_boot();
VFS::Node* SerialDevice::create_new(const char* devname) VFS::Node* SerialDevice::create_new(const char* devname)
{ {
VFS::Node* dev = new VFS::Node; VFS::Node* dev = new VFS::Node;
@ -13,6 +15,9 @@ VFS::Node* SerialDevice::create_new(const char* devname)
dev->length = 0; dev->length = 0;
dev->type = VFS_DEVICE; dev->type = VFS_DEVICE;
dev->flags = 0; 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)); strncpy(dev->name, devname, sizeof(dev->name));
return dev; return dev;
} }

View File

@ -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;
}

View File

@ -4,6 +4,8 @@
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
extern uint64_t clock_boot();
VFS::Node* VersionDevice::create_new(const char* devname) VFS::Node* VersionDevice::create_new(const char* devname)
{ {
VFS::Node* dev = new VFS::Node; 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->length = strlen(moon_version()) + 5;
dev->type = VFS_DEVICE; dev->type = VFS_DEVICE;
dev->flags = 0; 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)); strncpy(dev->name, devname, sizeof(dev->name));
return dev; 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) ssize_t VersionDevice::read(VFS::Node* node, size_t offset, size_t size, char* buffer)
{ {
if (!node) return -1; if (!node) return -1;
if (offset > node->length) return -1; if (offset > 0) return 0; // EOF after first read (FIXME: Should be only if everything was read)
if (offset + size > node->length) { size = node->length - offset; }
snprintf(buffer, size + 1, "moon %s", moon_version()); // FIXME: Support offseting this read snprintf(buffer, size + 1, "moon %s", moon_version()); // FIXME: Support offseting this read
return (ssize_t)size; return (ssize_t)size;
} }

View File

@ -3,7 +3,7 @@
#include "gdt/GDT.h" #include "gdt/GDT.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/string.h" #include "std/string.h"
#include <stdint.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) static void set_limit(GDTEntry* entry, uint32_t limit)
{ {
ASSERT(limit <= 0xFFFFF); ensure(limit <= 0xFFFFF);
entry->limit0 = limit & 0xFFFF; entry->limit0 = limit & 0xFFFF;
entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF); entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF);
} }

View File

@ -3,29 +3,32 @@
#include "init/Init.h" #include "init/Init.h"
#include "bootboot.h" #include "bootboot.h"
#include "cpu/CPU.h" #include "cpu/CPU.h"
#include "init/InitRD.h" #include "fs/InitRD.h"
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/MSR.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "rand/Init.h" #include "rand/Init.h"
#include "rand/Mersenne.h" #include "rand/Mersenne.h"
#include "render/Framebuffer.h" #include "render/Framebuffer.h"
#include "render/TextRenderer.h" #include "render/TextRenderer.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/string.h" #include "std/string.h"
#include "utils/Time.h"
extern BOOTBOOT bootboot; extern BOOTBOOT bootboot;
extern "C" char environment[4096]; extern "C" char environment[4096];
extern uintptr_t fb;
uintptr_t __stack_chk_guard = 0xfeff34; uintptr_t __stack_chk_guard = 0xfeff34;
void Init::check_magic() 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() void Init::disable_smp()
@ -35,6 +38,42 @@ void Init::disable_smp()
} }
extern "C" void asm_enable_sse(); 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() void Init::early_init()
{ {
@ -43,11 +82,14 @@ void Init::early_init()
asm_enable_sse(); asm_enable_sse();
framebuffer0.init((void*)bootboot.fb_ptr, bootboot.fb_type, bootboot.fb_scanline, bootboot.fb_width, framebuffer0.init((void*)&fb, bootboot.fb_type, bootboot.fb_scanline, bootboot.fb_width, bootboot.fb_height);
bootboot.fb_height);
check_and_enable_nx();
MemoryManager::init(); MemoryManager::init();
MemoryManager::protect_kernel_sections();
if (strstr(environment, "quiet=1")) if (strstr(environment, "quiet=1"))
{ {
KernelLog::toggle_log_level(LogLevel::DEBUG); 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); } else if (!strstr(environment, "verbose=1")) { KernelLog::toggle_log_level(LogLevel::DEBUG); }
clock_init();
InitRD::init(); InitRD::init();
Mersenne::init(); Mersenne::init();

View File

@ -5,17 +5,19 @@
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/VMM.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "panic/Panic.h" #include "panic/Panic.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/stdio.h" #include "std/stdio.h"
#include "sys/Syscall.h" #include "sys/Syscall.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "trace/StackTracer.h" #include "trace/StackTracer.h"
#include "utils/PageFaultReason.h"
extern "C" void common_handler(Context* context) extern "C" void common_handler(Context* context)
{ {
ASSERT(Interrupts::is_in_handler()); ensure(Interrupts::is_in_handler());
if (context->number >= 0x20 && context->number < 0x30) if (context->number >= 0x20 && context->number < 0x30)
{ {
IRQ::interrupt_handler(context); 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"); } if (context->cs == 0x8) { int_panic(context, "GPF in kernel task"); }
else else
{ {
VMM::enter_syscall_context();
kerrorln("General protection fault at RIP %lx, cs %ld, ss %ld, RSP %lx, error code %ld", context->rip, 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); context->cs, context->ss, context->rsp, context->error_code);
kinfoln("Stack trace:"); 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"); } if (context->cs == 0x8) { int_panic(context, "Page fault in kernel task"); }
else else
{ {
VMM::enter_syscall_context();
kerrorln("Page fault in ring 3 (RIP %lx), while trying to access %lx, error code %ld", context->rip, kerrorln("Page fault in ring 3 (RIP %lx), while trying to access %lx, error code %ld", context->rip,
context->cr2, context->error_code); context->cr2, context->error_code);
kinfoln("Stack trace:"); kinfoln("Stack trace:");
@ -51,6 +57,8 @@ extern "C" void common_handler(Context* context)
StackTracer tracer(context->rbp); StackTracer tracer(context->rbp);
tracer.trace_with_ip(context->rip); tracer.trace_with_ip(context->rip);
determine_user_page_fault_reason(context->cr2);
Scheduler::task_misbehave(context, -3); Scheduler::task_misbehave(context, -3);
} }
} }

View File

@ -2,7 +2,7 @@
#include "interrupts/IDT.h" #include "interrupts/IDT.h"
#include "log/Log.h" #include "log/Log.h"
#include "std/assert.h" #include "std/ensure.h"
struct IDTEntry struct IDTEntry
{ {
@ -37,8 +37,8 @@ uint64_t IDTEntry::get_offset()
void IDT::add_handler(short interrupt_number, void* handler, uint8_t type_attr) void IDT::add_handler(short interrupt_number, void* handler, uint8_t type_attr)
{ {
ASSERT(handler != nullptr); ensure(handler != nullptr);
ASSERT(interrupt_number < 256); ensure(interrupt_number < 256);
IDTEntry* entry_for_handler = &entries[interrupt_number]; IDTEntry* entry_for_handler = &entries[interrupt_number];
entry_for_handler->selector = 0x08; entry_for_handler->selector = 0x08;
entry_for_handler->type_attr = type_attr; entry_for_handler->type_attr = type_attr;

View File

@ -1,6 +1,7 @@
#define MODULE "irq" #define MODULE "irq"
#include "interrupts/IRQ.h" #include "interrupts/IRQ.h"
#include "fs/devices/Console.h"
#include "fs/devices/Keyboard.h" #include "fs/devices/Keyboard.h"
#include "io/IO.h" #include "io/IO.h"
#include "io/PIC.h" #include "io/PIC.h"
@ -21,10 +22,11 @@ void IRQ::interrupt_handler(Context* context)
break; break;
case 1: { case 1: {
unsigned char scancode = IO::inb(0x60); unsigned char scancode = IO::inb(0x60);
KeyboardDevice::append((char)scancode);
bool ignore = false; bool ignore = false;
char key = translate_scancode(scancode, &ignore); char key = translate_scancode(scancode, &ignore);
if (ignore) break; if (ignore) break;
KeyboardDevice::append(key); ConsoleDevice::append(key);
break; break;
} }
default: kwarnln("Unhandled IRQ: %ld", context->irq_number); break; default: kwarnln("Unhandled IRQ: %ld", context->irq_number); break;

View File

@ -13,7 +13,7 @@
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryMap.h" #include "memory/MemoryMap.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "thread/PIT.h" #include "thread/PIT.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
@ -23,8 +23,8 @@
extern "C" void _start() extern "C" void _start()
{ {
Init::check_magic();
Init::disable_smp(); // Put all other cores except the bootstrap one in an infinite loop Init::disable_smp(); // Put all other cores except the bootstrap one in an infinite loop
Init::check_magic();
Init::early_init(); Init::early_init();
kinfoln("Starting Moon %s", moon_version()); kinfoln("Starting Moon %s", moon_version());
@ -52,9 +52,9 @@ extern "C" void _start()
kinfoln("Prepared scheduler"); kinfoln("Prepared scheduler");
#ifdef RUN_TEST_AS_INIT #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 #else
ASSERT(Scheduler::load_user_task("/bin/init") > 0); ensure(Scheduler::load_user_task("/bin/init") > 0);
#endif #endif
Scheduler::add_kernel_task("[reaper]", []() { Scheduler::add_kernel_task("[reaper]", []() {
@ -67,7 +67,7 @@ extern "C" void _start()
kinfoln("Prepared scheduler tasks"); kinfoln("Prepared scheduler tasks");
ASSERT(VFS::mkdir("/dev") == 0); ensure(VFS::mkdir("/dev") == 0);
VFS::mount("/dev", DeviceFS::get()); VFS::mount("/dev", DeviceFS::get());
Init::finish_kernel_boot(); Init::finish_kernel_boot();
@ -78,5 +78,10 @@ extern "C" void _start()
Interrupts::enable(); 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.
} }

View File

@ -26,8 +26,11 @@ void AddressSpace::destroy()
if (!pdp_pde.present) continue; if (!pdp_pde.present) continue;
if (pdp_pde.larger_pages) if (pdp_pde.larger_pages)
{ {
pages_freed++; if (pdp_pde.owned_by_task)
PMM::free_page((void*)pdp_pde.get_address()); {
pages_freed++;
PMM::free_page((void*)pdp_pde.get_address());
}
continue; continue;
} }
PageTable* pdp = (PageTable*)pdp_pde.get_address(); PageTable* pdp = (PageTable*)pdp_pde.get_address();
@ -37,8 +40,11 @@ void AddressSpace::destroy()
if (!pd_pde.present) continue; if (!pd_pde.present) continue;
if (pd_pde.larger_pages) if (pd_pde.larger_pages)
{ {
pages_freed++; if (pd_pde.owned_by_task)
PMM::free_page((void*)pd_pde.get_address()); {
pages_freed++;
PMM::free_page((void*)pd_pde.get_address());
}
continue; continue;
} }
PageTable* pd = (PageTable*)pd_pde.get_address(); PageTable* pd = (PageTable*)pd_pde.get_address();
@ -48,8 +54,11 @@ void AddressSpace::destroy()
if (!pt_pde.present) continue; if (!pt_pde.present) continue;
if (pt_pde.larger_pages) if (pt_pde.larger_pages)
{ {
pages_freed++; if (pt_pde.owned_by_task)
PMM::free_page((void*)pt_pde.get_address()); {
pages_freed++;
PMM::free_page((void*)pt_pde.get_address());
}
continue; continue;
} }
PageTable* pt = (PageTable*)pt_pde.get_address(); PageTable* pt = (PageTable*)pt_pde.get_address();
@ -57,6 +66,7 @@ void AddressSpace::destroy()
{ {
PageDirectoryEntry& pde = pt->entries[l]; PageDirectoryEntry& pde = pt->entries[l];
if (!pde.present) continue; if (!pde.present) continue;
if (!pde.owned_by_task) continue;
pages_freed++; pages_freed++;
PMM::free_page((void*)pde.get_address()); PMM::free_page((void*)pde.get_address());
} }
@ -84,8 +94,11 @@ void AddressSpace::clear()
if (!pdp_pde.present) continue; if (!pdp_pde.present) continue;
if (pdp_pde.larger_pages) if (pdp_pde.larger_pages)
{ {
pages_freed++; if (pdp_pde.owned_by_task)
PMM::free_page((void*)pdp_pde.get_address()); {
pages_freed++;
PMM::free_page((void*)pdp_pde.get_address());
}
continue; continue;
} }
PageTable* pdp = (PageTable*)pdp_pde.get_address(); PageTable* pdp = (PageTable*)pdp_pde.get_address();
@ -95,8 +108,11 @@ void AddressSpace::clear()
if (!pd_pde.present) continue; if (!pd_pde.present) continue;
if (pd_pde.larger_pages) if (pd_pde.larger_pages)
{ {
pages_freed++; if (pd_pde.owned_by_task)
PMM::free_page((void*)pd_pde.get_address()); {
pages_freed++;
PMM::free_page((void*)pd_pde.get_address());
}
continue; continue;
} }
PageTable* pd = (PageTable*)pd_pde.get_address(); PageTable* pd = (PageTable*)pd_pde.get_address();
@ -106,8 +122,11 @@ void AddressSpace::clear()
if (!pt_pde.present) continue; if (!pt_pde.present) continue;
if (pt_pde.larger_pages) if (pt_pde.larger_pages)
{ {
pages_freed++; if (pt_pde.owned_by_task)
PMM::free_page((void*)pt_pde.get_address()); {
pages_freed++;
PMM::free_page((void*)pt_pde.get_address());
}
continue; continue;
} }
PageTable* pt = (PageTable*)pt_pde.get_address(); PageTable* pt = (PageTable*)pt_pde.get_address();
@ -115,6 +134,7 @@ void AddressSpace::clear()
{ {
PageDirectoryEntry& pde = pt->entries[l]; PageDirectoryEntry& pde = pt->entries[l];
if (!pde.present) continue; if (!pde.present) continue;
if (!pde.owned_by_task) continue;
pages_freed++; pages_freed++;
PMM::free_page((void*)pde.get_address()); 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.present) continue;
if (pdp_pde.larger_pages) 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()); void* cloned = try_clone_page_table((PageTable*)pdp_pde.get_address());
if (!cloned) 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.present) continue;
if (pd_pde.larger_pages) 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()); void* cloned = try_clone_page_table((PageTable*)pd_pde.get_address());
if (!cloned) 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.present) continue;
if (pt_pde.larger_pages) 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()); void* cloned = try_clone_page_table((PageTable*)pt_pde.get_address());
if (!cloned) if (!cloned)
{ {
@ -223,6 +258,11 @@ AddressSpace AddressSpace::clone() // FIXME: Add out-of-memory checks to this fu
{ {
PageDirectoryEntry& pde = pt->entries[l]; PageDirectoryEntry& pde = pt->entries[l];
PageDirectoryEntry& cloned_pde = cloned_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; if (!pde.present) continue;
void* cloned = try_clone_page_table((PageTable*)pde.get_address()); void* cloned = try_clone_page_table((PageTable*)pde.get_address());
if (!cloned) if (!cloned)

View File

@ -35,7 +35,6 @@ static void bitmap_set(uint64_t index, bool value)
void KernelHeap::clear() void KernelHeap::clear()
{ {
memset(page_bitmap, 0, sizeof(page_bitmap)); memset(page_bitmap, 0, sizeof(page_bitmap));
kinfoln("page bitmap located at %p", (void*)page_bitmap);
} }
uint64_t KernelHeap::request_virtual_page() uint64_t KernelHeap::request_virtual_page()

View File

@ -7,7 +7,8 @@
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "std/assert.h" #include "misc/utils.h"
#include "std/ensure.h"
void MemoryManager::init() void MemoryManager::init()
{ {
@ -17,6 +18,19 @@ void MemoryManager::init()
PMM::map_bitmap_to_virtual(); 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) void* MemoryManager::get_mapping(void* physicalAddress, int flags)
{ {
uint64_t virtualAddress = KernelHeap::request_virtual_page(); 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) void MemoryManager::release_page(void* page)
{ {
uint64_t physicalAddress = VMM::get_physical((uint64_t)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); VMM::unmap((uint64_t)page);
PMM::free_page((void*)physicalAddress); 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)); void* page = (void*)((uint64_t)pages + (i * PAGE_SIZE));
uint64_t physicalAddress = VMM::get_physical((uint64_t)page); uint64_t physicalAddress = VMM::get_physical((uint64_t)page);
ASSERT(physicalAddress != UINT64_MAX); ensure(physicalAddress != UINT64_MAX);
VMM::unmap((uint64_t)page); VMM::unmap((uint64_t)page);
PMM::free_page((void*)physicalAddress); 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); } 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);
}
}

View File

@ -6,7 +6,7 @@
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "misc/utils.h" #include "misc/utils.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/string.h" #include "std/string.h"
extern BOOTBOOT bootboot; extern BOOTBOOT bootboot;
@ -50,7 +50,7 @@ void PMM::init()
bitmap_addr = (char*)biggest_chunk; bitmap_addr = (char*)biggest_chunk;
virtual_bitmap_addr = bitmap_addr; 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; bitmap_size = total_mem / PAGE_SIZE / 8 + 1;
memset(bitmap_addr, 0xFF, bitmap_size); memset(bitmap_addr, 0xFF, bitmap_size);

View File

@ -4,7 +4,7 @@
#include "log/Log.h" #include "log/Log.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "misc/utils.h" #include "misc/utils.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/string.h" #include "std/string.h"
#include "utils/Addresses.h" #include "utils/Addresses.h"
#include "utils/Registers.h" #include "utils/Registers.h"
@ -13,6 +13,8 @@ static PageTable* kernel_pml4;
static PageTable* current_pml4; static PageTable* current_pml4;
static AddressSpace* user_address_space; 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() void VMM::switch_back_to_kernel_address_space()
{ {
if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; } if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; }
@ -59,6 +61,16 @@ void VMM::init()
{ {
kernel_pml4 = (PageTable*)read_cr3(); kernel_pml4 = (PageTable*)read_cr3();
current_pml4 = kernel_pml4; 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) 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); if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else else
pde->read_write = false; 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); flush_tlb(vaddr);
} }
@ -99,11 +117,13 @@ uint64_t VMM::get_physical(uint64_t vaddr)
uint64_t VMM::get_flags(uint64_t vaddr) uint64_t VMM::get_flags(uint64_t vaddr)
{ {
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(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; uint64_t flags = 0;
if (pde->user) flags |= User; if (pde->user) flags |= User;
if (pde->read_write) flags |= ReadWrite; if (pde->read_write) flags |= ReadWrite;
if (!pde->no_execute) flags |= Execute;
if (pde->owned_by_task) flags |= OwnedByTask;
return flags; return flags;
} }
@ -121,6 +141,7 @@ void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
{ {
unmap(vaddr); unmap(vaddr);
pde = create_pde_if_not_exists(current_pml4, 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)); 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); if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else else
pde->read_write = false; 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); 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 = [&]() { auto pde_create_if_not_present = [&]() {
pt = (PageTable*)PMM::request_page(); pt = (PageTable*)PMM::request_page();
ASSERT(!(PMM_DID_FAIL(pt))); ensure(!(PMM_DID_FAIL(pt)));
memset(pt, 0, PAGE_SIZE); memset(pt, 0, PAGE_SIZE);
pde->set_address((uint64_t)pt); pde->set_address((uint64_t)pt);
pde->present = true; pde->present = true;
@ -246,11 +273,44 @@ void VMM::propagate_user(PageTable* root, uint64_t vaddr)
pde->user = true; 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) void VMM::flush_tlb(uint64_t addr)
{ {
asm volatile("invlpg (%0)" : : "r"(addr) : "memory"); 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, void VMM::decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
uint64_t& pdp_index) uint64_t& pdp_index)
{ {

View File

@ -8,7 +8,7 @@
#endif #endif
#ifndef MOON_MINOR #ifndef MOON_MINOR
#define MOON_MINOR 13 #define MOON_MINOR 14
#endif #endif
#ifndef _MOON_SUFFIX #ifndef _MOON_SUFFIX

205
kernel/src/misc/ubsan.cpp Normal file
View 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

View File

@ -1,7 +1,7 @@
#define MODULE "panic" #define MODULE "panic"
#include "panic/Panic.h" #include "panic/Panic.h"
#include "init/InitRD.h" #include "fs/InitRD.h"
#include "interrupts/IDT.h" #include "interrupts/IDT.h"
#include "io/PIC.h" #include "io/PIC.h"
#include "log/Log.h" #include "log/Log.h"
@ -16,7 +16,7 @@
static bool g_is_in_panic = false; static bool g_is_in_panic = false;
static bool g_is_in_double_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_master(0b11111101); // enable keyboard only
PIC::enable_slave(0b11111111); PIC::enable_slave(0b11111111);

View File

@ -1,7 +1,7 @@
#define MODULE "rand" #define MODULE "rand"
#include "rand/Mersenne.h" #include "rand/Mersenne.h"
#include "std/assert.h" #include "std/ensure.h"
#include <stddef.h> #include <stddef.h>
typedef uint64_t word_t; typedef uint64_t word_t;
@ -47,7 +47,7 @@ uint64_t Mersenne::get()
{ {
if (index >= STATE_SIZE) if (index >= STATE_SIZE)
{ {
ASSERT(index == STATE_SIZE && "Mersenne generator was never seeded"); ensure(index == STATE_SIZE && "Mersenne generator was never seeded");
twist(); twist();
} }

View File

@ -3,7 +3,7 @@
#include "render/TextRenderer.h" #include "render/TextRenderer.h"
#include "bootboot.h" #include "bootboot.h"
#include "font.h" #include "font.h"
#include "init/InitRD.h" #include "fs/InitRD.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.h" #include "log/Log.h"
#include "render/Framebuffer.h" #include "render/Framebuffer.h"
@ -30,7 +30,7 @@ void TextRenderer::reset()
#pragma GCC optimize("O0") #pragma GCC optimize("O0")
static void putchar_at_offset( 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. [[maybe_unused]] Color& bg) // FIXME: Rewrite this function to actually work with foreground and background colors.
{ {
uint8_t* glyph = &font[c * 16]; 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 #pragma GCC pop_options
void TextRenderer::putchar(char chr) void TextRenderer::putchar(char chr)
{ {
switch (chr) if (g_escape_sequence)
{ {
case '\n': { g_escape_sequence = false;
ypos += FONT_HEIGHT; switch (chr)
if ((ypos + FONT_HEIGHT) >= bootboot.fb_height)
{ {
memcpy((void*)bootboot.fb_ptr, (char*)bootboot.fb_ptr + (bootboot.fb_scanline * FONT_HEIGHT), case '@':
bootboot.fb_size - (bootboot.fb_scanline * FONT_HEIGHT)); reset();
ypos -= FONT_HEIGHT; framebuffer0.clear(Color::Black);
framebuffer0.paint_rect(0, ypos, bootboot.fb_width, FONT_HEIGHT, Color::Black); break;
default: break;
} }
xpos = 0;
break;
} }
case '\r': xpos = 0; break; else
case '\b': {
if (xpos != 0) switch (chr)
{ {
xpos -= FONT_WIDTH; case '\n': {
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;
ypos += FONT_HEIGHT; 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), memcpy((void*)bootboot.fb_ptr, (char*)bootboot.fb_ptr + (bootboot.fb_scanline * FONT_HEIGHT),
bootboot.fb_size - (bootboot.fb_scanline * FONT_HEIGHT)); bootboot.fb_size - (bootboot.fb_scanline * FONT_HEIGHT));
ypos -= FONT_HEIGHT; ypos -= FONT_HEIGHT;
framebuffer0.paint_rect(0, ypos, bootboot.fb_width, FONT_HEIGHT, Color::Black); 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;
}
} }
} }

View File

@ -228,6 +228,7 @@ int sprintf(char* __s, const char* fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
if (__s) *__s = 0;
int written = internal_printf( int written = internal_printf(
fmt, fmt,
[&](const char* s) { [&](const char* s) {
@ -242,6 +243,7 @@ int snprintf(char* __s, size_t max, const char* fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
if (__s && max) *__s = 0;
int written = internal_printf( int written = internal_printf(
fmt, fmt,
[&](const char* s) { [&](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) int vsprintf(char* __s, const char* fmt, va_list ap)
{ {
*__s = 0;
return internal_printf( return internal_printf(
fmt, fmt,
[&](const char* s) { [&](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) int vsnprintf(char* __s, size_t max, const char* fmt, va_list ap)
{ {
if (max) *__s = 0;
return internal_printf( return internal_printf(
fmt, fmt,
[&](const char* s) { [&](const char* s) {

View File

@ -15,27 +15,36 @@ void Syscall::entry(Context* context)
case SYS_yield: sys_yield(context); break; case SYS_yield: sys_yield(context); break;
case SYS_sleep: sys_sleep(context, context->rdi); 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_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_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_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_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_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_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_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_mprotect: sys_mprotect(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
case SYS_clock: sys_clock(context); 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); 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_fork: sys_fork(context); break;
case SYS_waitpid: sys_waitpid(context, (long)context->rdi, (int*)context->rsi, (int)context->rdx); 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_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_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_pstat: sys_pstat(context, (long)context->rdi, (struct pstat*)context->rsi); break;
case SYS_getdents: case SYS_getdents:
sys_getdents(context, (int)context->rdi, (struct luna_dirent*)context->rsi, (size_t)context->rdx); sys_getdents(context, (int)context->rdi, (struct luna_dirent*)context->rsi, (size_t)context->rdx);
break; 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; default: context->rax = -ENOSYS; break;
} }
VMM::exit_syscall_context(); VMM::exit_syscall_context();

View File

@ -1,10 +1,212 @@
#include "sys/UserMemory.h" #include "sys/UserMemory.h"
#include "memory/Memory.h"
#include "memory/MemoryManager.h"
#include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
#include "utils/Addresses.h"
char* strdup_from_user( struct dynamic_string
const char* user_string) // FIXME: This function is a little hacky. Use the obtain_user_ref and similar functions.
{ {
uint64_t phys = VMM::get_physical((uint64_t)user_string); char* buf;
if (phys == (uint64_t)-1) { return nullptr; } long capacity;
return strdup((const char*)phys); 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);
} }

View File

@ -1,9 +1,83 @@
#include "bootboot.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "std/errno.h"
#include "sys/UserMemory.h"
#include "thread/PIT.h"
#include "thread/Scheduler.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(); 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; 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;
}

View File

@ -1,14 +1,14 @@
#define MODULE "elf" #define MODULE "elf"
#include "sys/elf/ELFLoader.h" #include "sys/elf/ELFLoader.h"
#include "fs/InitRD.h"
#include "fs/VFS.h" #include "fs/VFS.h"
#include "init/InitRD.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/utils.h" #include "misc/utils.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/errno.h" #include "std/errno.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
@ -25,6 +25,16 @@ static const char* format_permissions(uint32_t flags)
return perms; 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) ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename)
{ {
VFS::Node* node = VFS::resolve_path(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) ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
{ {
Elf64_Ehdr elf_ehdr; Elf64_Ehdr elf_ehdr;
ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0); ensure(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0);
ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) == 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 0); // If you haven't checked the ELF executable with check_elf_image() first, then an assertion fail is your
// fault =D // fault =D
ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64); ensure(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64);
ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB); ensure(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
ASSERT(elf_ehdr.e_type == ET_EXEC); ensure(elf_ehdr.e_type == ET_EXEC);
ASSERT(elf_ehdr.e_machine == EM_MACH); ensure(elf_ehdr.e_machine == EM_MACH);
ASSERT(elf_ehdr.e_phnum != 0); ensure(elf_ehdr.e_phnum != 0);
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection)); ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection)); memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
image->entry = elf_ehdr.e_entry; 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, 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)); 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); 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), 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::apply_address_space();
VMM::switch_to_previous_user_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 = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
(image->section_count + 1) * 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"); } else { kdbgln("skipping non-loadable segment"); }
} }
ASSERT(image->section_count); ensure(image->section_count);
return image; return image;
} }
@ -177,6 +194,11 @@ long ELFLoader::check_elf_image(VFS::Node* node)
kerrorln("trying to load ELF into kernel memory"); kerrorln("trying to load ELF into kernel memory");
return -ENOEXEC; return -ENOEXEC;
} }
if (can_write_segment(phdr.p_flags) && can_execute_segment(phdr.p_flags))
{
kwarnln("executable violates W^X");
return -ENOEXEC;
}
loadable_sections++; loadable_sections++;
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE; memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE;
} }

View File

@ -2,10 +2,11 @@
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/Memory.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/errno.h" #include "std/errno.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
@ -44,6 +45,11 @@ void sys_fork(Context* context)
child->ppid = parent->id; child->ppid = parent->id;
child->uid = parent->uid;
child->euid = parent->euid;
child->gid = parent->gid;
child->egid = parent->egid;
child->regs.rax = 0; child->regs.rax = 0;
context->rax = child->id; context->rax = child->id;
@ -56,7 +62,16 @@ void sys_fork(Context* context)
return; 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); char* kpathname = strdup_from_user(pathname);
if (!kpathname) if (!kpathname)
@ -82,6 +97,13 @@ void sys_exec(Context* context, const char* pathname)
return; return;
} }
if (!VFS::can_execute(program, Scheduler::current_task()->euid, Scheduler::current_task()->egid))
{
kfree(kpathname);
context->rax = -EACCES;
return;
}
long memusage; long memusage;
if ((memusage = ELFLoader::check_elf_image(program)) < 0) if ((memusage = ELFLoader::check_elf_image(program)) < 0)
{ {
@ -97,11 +119,88 @@ void sys_exec(Context* context, const char* pathname)
return; 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(); 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(); Task* task = Scheduler::current_task();
ASSERT(task); ensure(task);
// At this point, pretty much nothing can fail. // 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->address_space.clear();
task->allocated_stack = (uint64_t)MemoryManager::get_pages_at( task->allocated_stack = (uint64_t)MemoryManager::get_pages_at(
0x100000, TASK_PAGES_IN_STACK, 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 MAP_USER | MAP_READ_WRITE | MAP_AS_OWNED_BY_TASK); // If we had enough space for the old stack, there should be
// new stack. // enough space for the new stack.
ELFImage* image = ELFLoader::load_elf_from_vfs(program); 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. // 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)); strlcpy(task->name, kpathname, sizeof(task->name));
Scheduler::reset_task(task, image); Scheduler::reset_task(task, image);
@ -129,9 +231,29 @@ void sys_exec(Context* context, const char* pathname)
if (file.close_on_exec()) { file.close(); } 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); kfree(kpathname);
task->restore_context(context);
return; return;
} }

View File

@ -1,8 +1,13 @@
#include "std/errno.h" #include "std/errno.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include <sys/types.h>
#define ID_PID 0 #define ID_PID 0
#define ID_PPID 1 #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) void sys_getprocid(Context* context, int field)
{ {
@ -16,9 +21,103 @@ void sys_getprocid(Context* context, int field)
context->rax = Scheduler::current_task()->ppid; context->rax = Scheduler::current_task()->ppid;
return; 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 else
{ {
context->rax = -EINVAL; context->rax = -EINVAL;
return; 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;
}

View File

@ -10,30 +10,34 @@
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include <stddef.h> #include <stddef.h>
#define MAP_READ 1 #define PROT_READ 1
#define MAP_WRITE 2 #define PROT_WRITE 2
#define MAP_NONE 0 #define PROT_NONE 0
#define PROT_EXEC 4
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno) #define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
static const char* format_prot(int prot) static const char* format_prot(int prot)
{ {
static char prot_string[3]; static char prot_string[4];
prot_string[2] = 0; prot_string[3] = 0;
prot_string[0] = ((prot & MAP_READ) > 0) ? 'r' : '-'; prot_string[0] = ((prot & PROT_READ) > 0) ? 'r' : '-';
prot_string[1] = ((prot & MAP_WRITE) > 0) ? 'w' : '-'; prot_string[1] = ((prot & PROT_WRITE) > 0) ? 'w' : '-';
prot_string[2] = ((prot & PROT_EXEC) > 0) ? 'x' : '-';
return prot_string; return prot_string;
} }
static int mman_flags_from_prot(int prot) static int mman_flags_from_prot(int prot)
{ {
prot &= 0b11; prot &= 0b111;
if (prot == MAP_NONE) return 0; int flags = MAP_USER | MAP_AS_OWNED_BY_TASK;
if ((prot & MAP_WRITE) > 0) return MAP_USER | MAP_READ_WRITE; if (prot == PROT_NONE) return MAP_AS_OWNED_BY_TASK;
return MAP_USER; 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) 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); int real_flags = mman_flags_from_prot(prot);
if (address) 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)) if (Memory::is_kernel_address((uintptr_t)address))
{ {
kwarnln("munmap() failed: attempted to unmap a kernel page"); 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); context->rax = MAP_FAIL(ENOMEM);
return; return;
} }
uint64_t offset = (uint64_t)address % PAGE_SIZE; uint64_t addr_offset = (uint64_t)address % PAGE_SIZE;
void* result = MemoryManager::get_pages_at((uint64_t)address - offset, 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); Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
if (result) if (result)
{ {
@ -79,7 +95,8 @@ void sys_mmap(Context* context, void* address, size_t size, int prot)
return; 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 = uint64_t ptr =
Scheduler::current_task()->allocator.request_virtual_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size)); Scheduler::current_task()->allocator.request_virtual_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size));
if (!ptr) if (!ptr)
@ -88,6 +105,18 @@ void sys_mmap(Context* context, void* address, size_t size, int prot)
context->rax = MAP_FAIL(ENOMEM); context->rax = MAP_FAIL(ENOMEM);
return; 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); void* result = MemoryManager::get_pages_at(ptr, Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
if (result) if (result)
{ {
@ -130,8 +159,8 @@ void sys_munmap(Context* context, void* address, size_t size)
context->rax = -EINVAL; context->rax = -EINVAL;
return; return;
} }
uint64_t phys = VMM::get_physical((uint64_t)address); uint64_t flags = VMM::get_flags((uint64_t)address);
if (phys == (uint64_t)-1) if (flags == (uint64_t)-1)
{ {
kwarnln("munmap() failed: attempted to unmap a non-existent page"); kwarnln("munmap() failed: attempted to unmap a non-existent page");
context->rax = -EINVAL; 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; uint64_t offset = (uint64_t)address % PAGE_SIZE;
Scheduler::current_task()->allocator.free_virtual_pages(((uint64_t)address - offset), Scheduler::current_task()->allocator.free_virtual_pages(((uint64_t)address - offset),
Utilities::get_blocks_from_size(PAGE_SIZE, size)); 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"); kdbgln("munmap() succeeded");
context->rax = 0; context->rax = 0;
return; return;
@ -174,8 +208,8 @@ void sys_mprotect(Context* context, void* address, size_t size, int prot)
context->rax = -EINVAL; context->rax = -EINVAL;
return; return;
} }
uint64_t phys = VMM::get_physical((uint64_t)address); uint64_t flags = VMM::get_flags((uint64_t)address);
if (phys == (uint64_t)-1) if (flags == (uint64_t)-1)
{ {
kwarnln("mprotect() failed: attempted to protect a non-existent page"); kwarnln("mprotect() failed: attempted to protect a non-existent page");
context->rax = -EINVAL; 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; uint64_t offset = (uint64_t)address % PAGE_SIZE;
MemoryManager::protect((void*)((uint64_t)address - offset), Utilities::get_blocks_from_size(PAGE_SIZE, 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"); kdbgln("mprotect() succeeded");
context->rax = 0; context->rax = 0;
return; return;

View File

@ -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;
}

View File

@ -1,10 +1,10 @@
#include "fs/VFS.h" #include "fs/VFS.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "std/errno.h" #include "std/errno.h"
#include "std/stdlib.h"
#include "sys/UserMemory.h" #include "sys/UserMemory.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
typedef unsigned long off_t;
typedef unsigned short mode_t; typedef unsigned short mode_t;
typedef unsigned long ino_t; typedef unsigned long ino_t;
@ -13,8 +13,37 @@ struct stat // FIXME: This struct is quite stubbed out.
ino_t st_ino; ino_t st_ino;
mode_t st_mode; mode_t st_mode;
off_t st_size; 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) void sys_fstat(Context* context, int fd, struct stat* buf)
{ {
Task* current_task = Scheduler::current_task(); Task* current_task = Scheduler::current_task();
@ -29,16 +58,24 @@ void sys_fstat(Context* context, int fd, struct stat* buf)
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
struct stat* kstat = obtain_user_ref(buf); VFS::Node* node = file.node();
if (!kstat) 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; return;
} }
VFS::Node* node = file.node(); VFS::Node* node = VFS::resolve_path(kpath);
kstat->st_ino = node->inode; kfree(kpath);
kstat->st_mode = (mode_t)node->type; if (!node)
kstat->st_size = node->length; {
release_user_ref(kstat); context->rax = -ENOENT;
context->rax = 0; return;
}
return do_stat(context, node, buf);
} }

View File

@ -11,12 +11,17 @@
#include "sys/UserMemory.h" #include "sys/UserMemory.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "thread/Task.h" #include "thread/Task.h"
#include <sys/types.h>
#define OPEN_READ 1 #define OPEN_READ 1
#define OPEN_WRITE 2 #define OPEN_WRITE 2
#define OPEN_NONBLOCK 4 #define OPEN_NONBLOCK 4
#define OPEN_CLOEXEC 8 #define OPEN_CLOEXEC 8
#define OPEN_DIRECTORY 16 #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_SET 0
#define SEEK_CUR 1 #define SEEK_CUR 1
@ -24,47 +29,64 @@
#define FCNTL_DUPFD 0 #define FCNTL_DUPFD 0
#define FCNTL_ISTTY 1 #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) 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(); 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; return;
} }
Descriptor& file = current_task->files[fd];
if (command == FCNTL_DUPFD) 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; context->rax = -EINVAL;
return; 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) if (dupfd < 0)
{ {
context->rax = -EMFILE; context->rax = -EMFILE;
return; return;
} }
current_task->files[dupfd] = file; current_task->files[dupfd] = *file;
context->rax = dupfd; context->rax = dupfd;
kdbgln("fcntl(F_DUPFD): duplicated fd %d, result is %d", fd, dupfd); kdbgln("fcntl(F_DUPFD): duplicated fd %d, result is %d", fd, dupfd);
return; return;
} }
else if (command == FCNTL_ISTTY) else if (command == FCNTL_ISTTY)
{ {
VFS::Node* node = file.node(); VFS::Node* node = file->node();
if (node->tty) { context->rax = 1; } if (node->tty) { context->rax = 1; }
else else
context->rax = -ENOTTY; context->rax = -ENOTTY;
return; 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 else
{ {
context->rax = -EINVAL; context->rax = -EINVAL;
@ -79,24 +101,19 @@ void sys_seek(Context* context, int fd, long offset, int whence)
context->rax = -EINVAL; context->rax = -EINVAL;
return; 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; 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; long new_offset;
if (whence == SEEK_SET) new_offset = offset; if (whence == SEEK_SET) new_offset = offset;
else if (whence == SEEK_CUR) else if (whence == SEEK_CUR)
new_offset = offset + file.offset(); new_offset = offset + file->offset();
else if (whence == SEEK_END) else if (whence == SEEK_END)
new_offset = file.length() + offset; new_offset = file->length() + offset;
else else
__builtin_unreachable(); __builtin_unreachable();
if (new_offset < 0) 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? context->rax = -EINVAL; // FIXME: Is this the right error?
return; return;
} }
if (new_offset == file.offset()) if (new_offset == file->offset())
{ {
context->rax = new_offset; context->rax = new_offset;
return; return;
} }
int result = file.seek(new_offset); int result = file->seek(new_offset);
if (result < 0) if (result < 0)
{ {
context->rax = result; 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) 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; context->rax = -EFAULT;
return; 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; context->rax = -EBADF;
return; return;
} }
Task* current_task = Scheduler::current_task(); ssize_t result = file->user_write(size, addr);
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));
context->rax = (size_t)result; context->rax = (size_t)result;
return; 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(); Task* current_task = Scheduler::current_task();
int fd = current_task->alloc_fd(); 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); VFS::Node* node = VFS::resolve_path(kfilename);
if (!node) if (!node)
{ {
bool create = (flags & OPEN_CREATE) > 0;
if (create) kwarnln("FIXME: open(O_CREAT) is not implemented");
kfree(kfilename); kfree(kfilename);
context->rax = -ENOENT; context->rax = -ENOENT;
return; 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_read = (flags & OPEN_READ) > 0;
bool can_write = (flags & OPEN_WRITE) > 0; bool can_write = (flags & OPEN_WRITE) > 0;
@ -182,11 +207,40 @@ void sys_open(Context* context, const char* filename, int flags)
return; 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 able_to_block = (flags & OPEN_NONBLOCK) == 0;
bool close_on_exec = (flags & OPEN_CLOEXEC) > 0; bool close_on_exec = (flags & OPEN_CLOEXEC) > 0;
bool only_directory = (flags & OPEN_DIRECTORY) > 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) if (only_directory && node->type != VFS_DIRECTORY)
{ {
kfree(kfilename); kfree(kfilename);
@ -202,68 +256,71 @@ void sys_open(Context* context, const char* filename, int flags)
kfree(kfilename); kfree(kfilename);
current_task->files[fd].open(node, can_read, can_write, able_to_block, close_on_exec); 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; context->rax = fd;
return; return;
} }
void sys_read(Context* context, int fd, size_t size, char* buffer) 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; context->rax = -EFAULT;
return; 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; context->rax = -EBADF;
return; return;
} }
Task* current_task = Scheduler::current_task(); if (VFS::would_block(file->node()))
if (!current_task->files[fd].is_open() || !current_task->files[fd].can_read())
{ {
context->rax = -EBADF; if (!file->able_to_block())
return;
}
if (VFS::would_block(current_task->files[fd].node()))
{
if (!current_task->files[fd].able_to_block())
{ {
context->rax = -EAGAIN; context->rax = -EAGAIN;
return; return;
} }
Task* current_task = Scheduler::current_task();
current_task->state = current_task->Blocking; current_task->state = current_task->Blocking;
current_task->block_reason = BlockReason::Reading;
current_task->blocking_read_info.fd = fd; 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; current_task->blocking_read_info.size = size;
return Scheduler::task_yield(context); return Scheduler::task_yield(context);
} }
ssize_t result = current_task->files[fd].read( ssize_t result = file->user_read(size, buffer);
size, (char*)VMM::get_physical((uint64_t)buffer)); // FIXME: Handle errors, and big buffers which may not be
// across continuous physical pages.
context->rax = (size_t)result; context->rax = (size_t)result;
return; return;
} }
void sys_close(Context* context, int fd) 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; context->rax = -err;
return;
}
Task* current_task = Scheduler::current_task();
if (!current_task->files[fd].is_open())
{
context->rax = -EBADF;
return; return;
} }
kdbgln("close(): releasing file descriptor %d", fd); kdbgln("close(): releasing file descriptor %d", fd);
current_task->files[fd].close(); file->close();
context->rax = 0; context->rax = 0;
return; 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); char* kfilename = strdup_from_user(filename);
if (!kfilename) if (!kfilename)
@ -272,7 +329,9 @@ void sys_mkdir(Context* context, const char* filename)
return; 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); kfree(kfilename);
@ -287,3 +346,44 @@ void sys_access(Context* context, const char* path, int) // FIXME: Use the amode
context->rax = 0; context->rax = 0;
kfree(kpath); 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);
}

View File

@ -10,7 +10,7 @@
#include "misc/reboot.h" #include "misc/reboot.h"
#include "misc/utils.h" #include "misc/utils.h"
#include "panic/Panic.h" #include "panic/Panic.h"
#include "std/assert.h" #include "std/ensure.h"
#include "std/errno.h" #include "std/errno.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
@ -25,7 +25,7 @@ static uint64_t task_num = 0;
static Task idle_task; static Task idle_task;
static uint64_t free_tid = 0; static uint64_t free_pid = 0;
static Task* sched_current_task; static Task* sched_current_task;
static Task* base_task; static Task* base_task;
@ -72,7 +72,7 @@ void Scheduler::append_task(Task* task)
{ {
if (!base_task) if (!base_task)
{ {
ASSERT(!end_task); ensure(!end_task);
base_task = task; base_task = task;
end_task = base_task; end_task = base_task;
task->next_task = task; task->next_task = task;
@ -91,7 +91,7 @@ void Scheduler::append_task(Task* task)
void Scheduler::init() void Scheduler::init()
{ {
memset(&idle_task, 0, sizeof(Task)); 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.rip = (uint64_t)idle_task_function;
idle_task.regs.rsp = get_top_of_stack((uint64_t)MemoryManager::get_page(), 1); idle_task.regs.rsp = get_top_of_stack((uint64_t)MemoryManager::get_page(), 1);
idle_task.regs.cs = 0x08; idle_task.regs.cs = 0x08;
@ -99,6 +99,7 @@ void Scheduler::init()
idle_task.regs.rflags = (1 << 21) | (1 << 9); idle_task.regs.rflags = (1 << 21) | (1 << 9);
idle_task.task_sleep = 1000; idle_task.task_sleep = 1000;
idle_task.user_task = false; idle_task.user_task = false;
idle_task.block_reason = BlockReason::None;
idle_task.state = idle_task.Idle; idle_task.state = idle_task.Idle;
strlcpy(idle_task.name, "[cpu-idle]", sizeof(idle_task.name)); 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)) void Scheduler::add_kernel_task(const char* taskname, void (*task)(void))
{ {
Task* new_task = new Task; Task* new_task = new Task;
ASSERT(new_task); ensure(new_task);
new_task->user_task = false; new_task->user_task = false;
new_task->id = free_tid++; new_task->id = free_pid++;
new_task->ppid = 0; 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->regs.rip = (uint64_t)task;
new_task->allocated_stack = new_task->allocated_stack =
(uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK); // 16 KB is enough for everyone, right? (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; new_task->cpu_time = 0;
strlcpy(new_task->name, taskname, sizeof(new_task->name)); strlcpy(new_task->name, taskname, sizeof(new_task->name));
append_task(new_task); append_task(new_task);
new_task->block_reason = BlockReason::None;
new_task->state = new_task->Running; new_task->state = new_task->Running;
task_num++; task_num++;
kinfoln("Adding kernel task: %s, starts at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name, 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; if (!new_task) return nullptr;
memset(&new_task->regs, 0, sizeof(Context)); memset(&new_task->regs, 0, sizeof(Context));
new_task->user_task = true; new_task->user_task = true;
new_task->id = free_tid++; new_task->id = free_pid++;
new_task->ppid = 0; new_task->ppid = 0;
new_task->task_sleep = 0; new_task->task_sleep = 0;
new_task->task_time = 0; new_task->task_time = 0;
new_task->cpu_time = 0; new_task->cpu_time = 0;
new_task->block_reason = BlockReason::None;
append_task(new_task); append_task(new_task);
task_num++; task_num++;
return new_task; return new_task;
@ -154,42 +158,51 @@ long Scheduler::load_user_task(const char* filename)
{ {
kinfoln("Loading user task: %s", filename); kinfoln("Loading user task: %s", filename);
Interrupts::push_and_disable(); 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; Task* new_task = new Task;
ASSERT(new_task); ensure(new_task);
memset(&new_task->regs, 0, sizeof(Context)); memset(&new_task->regs, 0, sizeof(Context));
new_task->id = free_tid++; new_task->id = free_pid++;
new_task->ppid = 0; new_task->ppid = 0;
new_task->uid = new_task->euid = new_task->gid = new_task->egid = 0;
if (!new_task->allocator.init()) if (!new_task->allocator.init())
{ {
delete new_task; delete new_task;
free_tid--; free_pid--;
Interrupts::pop(); Interrupts::pop();
return -ENOMEM; return -ENOMEM;
} }
new_task->address_space = AddressSpace::create(); new_task->address_space = AddressSpace::create();
VMM::switch_to_user_address_space(new_task->address_space); VMM::switch_to_user_address_space(new_task->address_space);
ELFImage* image = ELFLoader::load_elf_from_filesystem( long result;
filename); // FIXME: TOCTOU? Right now, impossible, since interrupts are disabled and SMP is not a thing. But in if ((result = ELFLoader::check_elf_image_from_filesystem(filename)) < 0)
// the future, it might be possible. {
ASSERT(image); 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->user_task = true;
new_task->regs.rip = image->entry; new_task->regs.rip = image->entry;
new_task->image = image; new_task->image = image;
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages_at( 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) if (!new_task->allocated_stack)
{ {
new_task->address_space.destroy(); new_task->address_space.destroy();
delete new_task; delete new_task;
free_tid--; free_pid--;
ELFLoader::release_elf_image(image); ELFLoader::release_elf_image(image);
VMM::switch_back_to_kernel_address_space(); VMM::switch_back_to_kernel_address_space();
Interrupts::pop(); Interrupts::pop();
@ -205,6 +218,7 @@ long Scheduler::load_user_task(const char* filename)
new_task->cpu_time = 0; new_task->cpu_time = 0;
strlcpy(new_task->name, filename, sizeof(new_task->name)); strlcpy(new_task->name, filename, sizeof(new_task->name));
append_task(new_task); append_task(new_task);
new_task->block_reason = BlockReason::None;
new_task->state = new_task->Running; new_task->state = new_task->Running;
task_num++; task_num++;
kinfoln("Adding user task: %s, loaded at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name, 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->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
task->task_sleep = 0; task->task_sleep = 0;
task->cpu_time = 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, 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); task->id, task->regs.rsp, task_num);
} }
void Scheduler::reap_task(Task* task) void Scheduler::reap_task(Task* task)
{ {
ASSERT(!Interrupts::is_in_handler()); ensure(!Interrupts::is_in_handler());
task_num--; task_num--;
Task* exiting_task = task; 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()) if (exiting_task->is_user_task())
{ {
VMM::switch_back_to_kernel_address_space(); 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, kinfoln("reaping task %s, PID %ld, exited with code %ld", exiting_task->name, exiting_task->id,
exiting_task->exit_status); 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()) if (exiting_task->allocated_stack && !exiting_task->is_user_task())
MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK); 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. if (exiting_task->image) kfree(exiting_task->image);
{
// ELFLoader::release_elf_image(exiting_task->image);
kfree(exiting_task->image);
}
if (exiting_task->is_user_task()) if (exiting_task->is_user_task())
{ {
exiting_task->allocator.free(); exiting_task->allocator.free();
@ -262,7 +274,6 @@ void Scheduler::reap_task(Task* task)
Interrupts::pop(); Interrupts::pop();
} }
for (int i = 0; i < TASK_MAX_FDS; i++) { exiting_task->files[i].close(); } 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; delete exiting_task;
} }
@ -281,6 +292,7 @@ void sched_common_exit(Context* context, int64_t status)
} }
else else
{ {
kinfoln("PID 1 exited with code %ld", status);
#ifndef RUN_TEST_AS_INIT #ifndef RUN_TEST_AS_INIT
reboot(); reboot();
#else #else
@ -292,7 +304,7 @@ void sched_common_exit(Context* context, int64_t status)
void Scheduler::task_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, kdbgln("exit: task %ld finished running, used %ld ms of cpu time", sched_current_task->id,
sched_current_task->cpu_time); sched_current_task->cpu_time);
sched_common_exit(context, status); 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) 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); 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); sched_common_exit(context, status);
} }
@ -308,7 +320,7 @@ void Scheduler::task_misbehave(Context* context, int64_t status)
void Scheduler::reap_tasks() void Scheduler::reap_tasks()
{ {
Interrupts::disable(); Interrupts::disable();
ASSERT(!Interrupts::is_in_handler()); ensure(!Interrupts::is_in_handler());
Task* reap_base = nullptr; Task* reap_base = nullptr;
Task* reap_end = nullptr; Task* reap_end = nullptr;
Task* task = base_task; Task* task = base_task;
@ -371,7 +383,7 @@ static void sched_decrement_sleep_times()
void Scheduler::task_tick(Context* context) void Scheduler::task_tick(Context* context)
{ {
ASSERT(Interrupts::is_in_handler()); ensure(Interrupts::is_in_handler());
Interrupts::disable(); Interrupts::disable();
sched_decrement_sleep_times(); sched_decrement_sleep_times();
sched_current_task->task_time -= frequency; sched_current_task->task_time -= frequency;
@ -387,7 +399,7 @@ void Scheduler::task_tick(Context* context)
void Scheduler::task_yield(Context* context) void Scheduler::task_yield(Context* context)
{ {
ASSERT(Interrupts::is_in_handler()); ensure(Interrupts::is_in_handler());
Interrupts::disable(); Interrupts::disable();
sched_current_task->save_context(context); sched_current_task->save_context(context);
bool was_idle = false; bool was_idle = false;
@ -401,11 +413,7 @@ void Scheduler::task_yield(Context* context)
sched_current_task = sched_current_task->next_task; sched_current_task = sched_current_task->next_task;
if (sched_current_task->state == sched_current_task->Blocking) if (sched_current_task->state == sched_current_task->Blocking)
{ {
if (!sched_current_task->is_still_blocking()) if (!sched_current_task->is_still_blocking()) sched_current_task->resume();
{
sched_current_task->resume_read();
sched_current_task->state = sched_current_task->Running;
}
} }
if (sched_current_task->state == sched_current_task->Running) if (sched_current_task->state == sched_current_task->Running)
{ {
@ -461,8 +469,10 @@ Task* Scheduler::current_task()
return sched_current_task; return sched_current_task;
} }
#define WNOHANG 1
void sys_waitpid(Context* context, long pid, int* wstatus, 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; Task* child = nullptr;
if (pid == -1) if (pid == -1)
@ -477,8 +487,17 @@ void sys_waitpid(Context* context, long pid, int* wstatus,
}); });
if (!child) if (!child)
{ {
context->rax = 0; // No child has exited, let's return 0. if (options & WNOHANG)
return; {
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 else
@ -486,33 +505,84 @@ void sys_waitpid(Context* context, long pid, int* wstatus,
child = Scheduler::find_by_pid(pid); child = Scheduler::find_by_pid(pid);
if (!child) if (!child)
{ {
context->rax = -ESRCH; context->rax = -ECHILD;
return; 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; return;
} }
if (wstatus) if (child->state != child->Dying)
{ {
int* kwstatus = obtain_user_ref(wstatus); if (options & WNOHANG)
if (kwstatus)
{ {
*kwstatus = (int)(child->exit_status & 0xff); context->rax = 0; // No child has exited, let's return 0.
release_user_ref(kwstatus);
}
else
{
kinfoln("wstatus ptr is invalid: %p", (void*)wstatus);
child->state = child->Exited;
context->rax = -EFAULT;
return; 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; child->state = child->Exited;
context->rax = (long)child->id; 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 struct pstat
@ -522,12 +592,14 @@ struct pstat
char pt_name[128]; char pt_name[128];
int pt_state; int pt_state;
long pt_time; long pt_time;
uid_t pt_uid;
gid_t pt_gid;
}; };
void sys_pstat(Context* context, long pid, struct pstat* buf) void sys_pstat(Context* context, long pid, struct pstat* buf)
{ {
Task* task; 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) else if (pid == 0)
task = &idle_task; task = &idle_task;
else else
@ -542,21 +614,17 @@ void sys_pstat(Context* context, long pid, struct pstat* buf)
context->rax = -ESRCH; context->rax = -ESRCH;
return; return;
} }
context->rax = task->id;
if (buf) if (buf)
{ {
struct pstat* kpstat = obtain_user_ref(buf); struct pstat stat;
if (!kpstat) stat.pt_pid = task->id;
{ stat.pt_ppid = task->ppid;
context->rax = -EFAULT; stat.pt_state = (int)task->state;
return; stat.pt_time = (long)task->cpu_time;
} stat.pt_uid = task->uid;
kpstat->pt_pid = task->id; stat.pt_gid = task->gid;
kpstat->pt_ppid = task->ppid; strlcpy(stat.pt_name, task->name, sizeof(stat.pt_name));
kpstat->pt_state = (int)task->state; if (!copy_to_user(buf, &stat, sizeof(struct pstat))) context->rax = -EFAULT;
kpstat->pt_time = (long)task->cpu_time;
strlcpy(kpstat->pt_name, task->name, sizeof(kpstat->pt_name));
release_user_ref(kpstat);
} }
context->rax = task->id;
return;
} }

View File

@ -3,6 +3,8 @@
#include "thread/Task.h" #include "thread/Task.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "std/ensure.h"
#include "std/errno.h"
#include "std/string.h" #include "std/string.h"
void Task::restore_context(Context* context) void Task::restore_context(Context* context)
@ -71,15 +73,68 @@ bool Task::has_died()
} }
void Task::resume_read() 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::switch_back_to_kernel_address_space();
VMM::apply_address_space(); VMM::apply_address_space();
VMM::switch_to_previous_user_address_space(); VMM::switch_to_user_address_space(address_space);
regs.rax = files[blocking_read_info.fd].read(blocking_read_info.size, blocking_read_info.buf); 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(); VMM::apply_address_space();
block_reason = BlockReason::None;
state = Running;
} }
bool Task::is_still_blocking() 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;
} }

View File

@ -1,5 +1,5 @@
#include "trace/Resolve.h" #include "trace/Resolve.h"
#include "init/InitRD.h" #include "fs/InitRD.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
#include <stddef.h> #include <stddef.h>
@ -7,7 +7,7 @@
extern int kernel_start; extern int kernel_start;
extern int kernel_end; 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) static size_t symbol_strlen(const char* symbol)
{ {

View 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;
}
}

View 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
View 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);
}

View File

@ -5,7 +5,7 @@ LIBC_BIN := $(LIBC_DIR)/bin
DESTDIR ?= $(LUNA_BASE)/usr/lib 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 CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy
ASMFLAGS := -felf64 ASMFLAGS := -felf64
@ -21,49 +21,62 @@ OBJS += $(patsubst $(LIBC_SRC)/%.asm, $(LIBC_OBJ)/%.asm.o, $(ASM_SRC))
$(LIBC_OBJ)/%.cpp.o: $(LIBC_SRC)/%.cpp $(LIBC_OBJ)/%.cpp.o: $(LIBC_SRC)/%.cpp
@mkdir -p $(@D) @mkdir -p $(@D)
$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $^ @$(CXX) $(CFLAGS) $(CXXFLAGS) -o $@ -c $^
@echo " CXX $^"
$(LIBC_OBJ)/%.c.o: $(LIBC_SRC)/%.c $(LIBC_OBJ)/%.c.o: $(LIBC_SRC)/%.c
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) $(CFLAGS) -o $@ -c $^ @$(CC) $(CFLAGS) -o $@ -c $^
@echo " CC $^"
$(LIBC_OBJ)/%.asm.o: $(LIBC_SRC)/%.asm $(LIBC_OBJ)/%.asm.o: $(LIBC_SRC)/%.asm
@mkdir -p $(@D) @mkdir -p $(@D)
$(ASM) $(ASMFLAGS) -o $@ $^ @$(ASM) $(ASMFLAGS) -o $@ $^
@echo " ASM $^"
$(LIBC_BIN)/libc.a: $(OBJS) $(LIBC_BIN)/libc.a: $(OBJS)
@mkdir -p $(@D) @mkdir -p $(@D)
$(AR) rcs $@ $(OBJS) @$(AR) rcs $@ $(OBJS)
@echo " AR $@"
$(LIBC_BIN)/crt0.o: $(LIBC_DIR)/crt0.asm $(LIBC_BIN)/crt0.o: $(LIBC_DIR)/crt0.asm
@mkdir -p $(@D) @mkdir -p $(@D)
$(ASM) $(ASMFLAGS) -o $@ $^ @$(ASM) $(ASMFLAGS) -o $@ $^
@echo " ASM $^"
$(LIBC_BIN)/crti.o: $(LIBC_DIR)/crti.asm $(LIBC_BIN)/crti.o: $(LIBC_DIR)/crti.asm
@mkdir -p $(@D) @mkdir -p $(@D)
$(ASM) $(ASMFLAGS) -o $@ $^ @$(ASM) $(ASMFLAGS) -o $@ $^
@echo " ASM $^"
$(LIBC_BIN)/crtn.o: $(LIBC_DIR)/crtn.asm $(LIBC_BIN)/crtn.o: $(LIBC_DIR)/crtn.asm
@mkdir -p $(@D) @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 build: $(LIBC_BIN)/crt0.o $(LIBC_BIN)/crti.o $(LIBC_BIN)/crtn.o $(LIBC_BIN)/libc.a
$(DESTDIR)/libc.a: $(LIBC_BIN)/libc.a $(DESTDIR)/libc.a: $(LIBC_BIN)/libc.a
@mkdir -p $(@D) @mkdir -p $(@D)
cp $^ $@ @cp $^ $@
@rm -f $(DESTDIR)/libm.a
@ln -s $@ $(DESTDIR)/libm.a
@echo " INSTALL $^"
$(DESTDIR)/crt0.o: $(LIBC_BIN)/crt0.o $(DESTDIR)/crt0.o: $(LIBC_BIN)/crt0.o
@mkdir -p $(@D) @mkdir -p $(@D)
cp $^ $@ @cp $^ $@
@echo " INSTALL $^"
$(DESTDIR)/crti.o: $(LIBC_BIN)/crti.o $(DESTDIR)/crti.o: $(LIBC_BIN)/crti.o
@mkdir -p $(@D) @mkdir -p $(@D)
cp $^ $@ @cp $^ $@
@echo " INSTALL $^"
$(DESTDIR)/crtn.o: $(LIBC_BIN)/crtn.o $(DESTDIR)/crtn.o: $(LIBC_BIN)/crtn.o
@mkdir -p $(@D) @mkdir -p $(@D)
cp $^ $@ @cp $^ $@
@echo " INSTALL $^"
install: $(DESTDIR)/libc.a $(DESTDIR)/crt0.o $(DESTDIR)/crti.o $(DESTDIR)/crtn.o install: $(DESTDIR)/libc.a $(DESTDIR)/crt0.o $(DESTDIR)/crti.o $(DESTDIR)/crtn.o

View File

@ -6,8 +6,6 @@ extern _fini
extern initialize_libc extern initialize_libc
extern exit extern exit
extern __argv
global _start global _start
_start: _start:
; Set up end of the stack frame linked list. ; Set up end of the stack frame linked list.
@ -16,12 +14,15 @@ _start:
push rbp ; rbp=0 push rbp ; rbp=0
mov rbp, rsp mov rbp, rsp
push rdi
push rsi
call initialize_libc call initialize_libc
call _init call _init
mov rdi, 0 ; argc = 0 pop rsi ; argv
mov rsi, __argv ; Dummy argv which is equal to {NULL} pop rdi ; argc
call main call main

View File

@ -0,0 +1,6 @@
#ifndef _ALLOCA_H
#define _ALLOCA_H
#define alloca __builtin_alloca
#endif

View File

@ -18,7 +18,7 @@ extern "C"
#ifdef NDEBUG #ifdef NDEBUG
#define assert(expr) (void)0 #define assert(expr) (void)0
#else #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
#endif #endif

View File

@ -3,5 +3,9 @@
#define ID_PID 0 #define ID_PID 0
#define ID_PPID 1 #define ID_PPID 1
#define ID_UID 2
#define ID_EUID 3
#define ID_GID 4
#define ID_EGID 5
#endif #endif

View File

@ -4,7 +4,9 @@
#define __lc_noreturn __attribute__((noreturn)) #define __lc_noreturn __attribute__((noreturn))
#define __lc_align(n) __attribute__((aligned(n))) #define __lc_align(n) __attribute__((aligned(n)))
#define __lc_deprecated(msg) __attribute__((deprecated(msg))) #define __lc_deprecated(msg) __attribute__((deprecated(msg)))
#define __lc_is_deprecated __attribute__((deprecated))
#define __lc_unreachable __builtin_unreachable #define __lc_unreachable __builtin_unreachable
#define __lc_used __attribute__((used)) #define __lc_used __attribute__((used))
#define __lc_unused __attribute__((unused))
#endif #endif

View File

@ -1,6 +1,8 @@
#ifndef _CTYPE_H #ifndef _CTYPE_H
#define _CTYPE_H #define _CTYPE_H
#include <bits/macros.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
@ -51,6 +53,10 @@ extern "C"
/* Returns the uppercase form of the specified character. */ /* Returns the uppercase form of the specified character. */
int toupper(int c); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -4,6 +4,7 @@
#include <luna/os-limits.h> #include <luna/os-limits.h>
#include <sys/types.h> #include <sys/types.h>
/* An entry in a directory. */
struct dirent struct dirent
{ {
ino_t d_ino; ino_t d_ino;
@ -13,6 +14,16 @@ struct dirent
char d_name[NAME_MAX]; 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 typedef struct
{ {
int d_dirfd; int d_dirfd;

View File

@ -4,24 +4,81 @@
/* The last error encountered during a call to a library or system function. */ /* The last error encountered during a call to a library or system function. */
extern int errno; extern int errno;
#define EPERM 1 // Operation not permitted #define EPERM 1 // Operation not permitted
#define ENOENT 2 // No such file or directory #define ENOENT 2 // No such file or directory
#define ESRCH 3 // No such process #define ESRCH 3 // No such process
#define EINTR 4 // Interrupted system call. Not implemented. #define EINTR 4 // Interrupted system call. Not implemented.
#define ENOEXEC 8 // Exec format error #define EIO 5 // Input/output error. Not implemented.
#define EBADF 9 // Bad file descriptor #define ENXIO 6 // No such device or address. Not implemented.
#define EAGAIN 11 // Resource temporarily unavailable #define E2BIG 7 // Argument list too long
#define ENOMEM 12 // Cannot allocate memory #define ENOEXEC 8 // Exec format error
#define EFAULT 14 // Bad address #define EBADF 9 // Bad file descriptor
#define EEXIST 17 // File exists #define ECHILD 10 // No child processes
#define ENOTDIR 20 // Not a directory #define EAGAIN 11 // Resource temporarily unavailable
#define EISDIR 21 // Is a directory #define EWOULDBLOCK 11 // Resource temporarily unavailable
#define EINVAL 22 // Invalid argument #define ENOMEM 12 // Cannot allocate memory
#define EMFILE 24 // Too many open files #define EACCES 13 // Permission denied
#define ENOTTY 25 // Inappropriate ioctl for device #define EFAULT 14 // Bad address
#define ENOSPC 28 // No space left on device #define EBUSY 16 // Device or resource busy. Not implemented.
#define EPIPE 32 // Broken pipe. Not implemented. #define EEXIST 17 // File exists
#define ENOSYS 38 // Function not implemented #define EXDEV 18 // Invalid cross-device link. Not implemented.
#define ENOTSUP 95 // Operation not supported #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 #endif

View File

@ -13,11 +13,26 @@
#define O_CLOEXEC 8 #define O_CLOEXEC 8
/* Refuse to open the file if it is not a directory. */ /* Refuse to open the file if it is not a directory. */
#define O_DIRECTORY 16 #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. */ /* Duplicate a file descriptor. */
#define F_DUPFD 0 #define F_DUPFD 0
/* Is a file descriptor a TTY? */ /* Is a file descriptor a TTY? */
#define F_ISTTY 1 #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 #ifdef __cplusplus
extern "C" extern "C"
@ -25,7 +40,7 @@ extern "C"
#endif #endif
/* Opens the file specified by pathname. Returns a file descriptor on success, or -1 on error. */ /* 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. */ /* Performs an operation on the file descriptor fd determined by cmd. */
int fcntl(int fd, int cmd, ...); int fcntl(int fd, int cmd, ...);

Some files were not shown because too many files have changed in this diff Show More