Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Will this project ever reach its goals? Probably not, but that's not the point.

### User Space

- Custom superset of the ANSI C standard library including threading, file I/O, math, PatchworkOS extensions, etc.
- Custom superset of the ANSI C standard library including threading, buffered I/O, math.h, PatchworkOS extensions, etc.
- Highly modular shared memory based desktop environment.
- Theming via [config files](https://github.com/KaiNorberg/PatchworkOS/blob/main/root/cfg).
- Capability based containerization security model using per-process mountpoint namespaces. See [Security](#security) for more info.
Expand All @@ -84,12 +84,12 @@ Will this project ever reach its goals? Probably not, but that's not the point.

## Notable Future Plans

- Fully Asynchronous I/O and syscalls (io_uring?). <-- Currently being worked on.
- Implement 9P file servers. <-- Currently being worked on.
- Implement user system in user-space using namespaces.
- Improve `share()` and `claim()` security by specifying a target PID when sharing.
- Overhaul Desktop Window Manager to use the new security system and file servers?
- Port LUA and use it for dynamic system configuration.
- Fully Asynchronous I/O and syscalls (io_uring?).
- USB support.

## Setup
Expand Down Expand Up @@ -215,20 +215,20 @@ read(fd, id, 31);
close(fd);
```

Using the `sread()` helper which reads a null-terminated string from a file descriptor, we can simplify this to:
Using the `reads()` helper which reads a null-terminated string from a file descriptor, we can simplify this to:

```c
fd_t fd = open("/net/local/seqpacket");
char* id = sread(fd);
char* id = reads(fd);
close(fd);
// ... do stuff ...
free(id);
```

Finally, using use the `sreadfile()` helper which reads a null-terminated string from a file from its path, we can simplify this even further to:
Finally, using use the `readfiles()` helper which reads a null-terminated string from a file from its path, we can simplify this even further to:

```c
char* id = sreadfile("/net/local/seqpacket");
char* id = readfiles("/net/local/seqpacket");
// ... do stuff ...
free(id);
```
Expand Down Expand Up @@ -256,18 +256,18 @@ write(ctl, str, strlen(str));
close(ctl);
```

Using the `F()` macro which allocates formatted strings on the stack and the `swrite()` helper that writes a null-terminated string to a file descriptor:
Using the `F()` macro which allocates formatted strings on the stack and the `writes()` helper that writes a null-terminated string to a file descriptor:

```c
fd_t ctl = open(F("/net/local/%s/ctl", id));
swrite(ctl, "bind myserver && listen")
writes(ctl, "bind myserver && listen")
close(ctl);
```

Finally, using the `swritefile()` helper which writes a null-terminated string to a file from its path:
Finally, using the `writefiles()` helper which writes a null-terminated string to a file from its path:

```c
swritefile(F("/net/local/%s/ctl", id), "bind myserver && listen");
writefiles(F("/net/local/%s/ctl", id), "bind myserver && listen");
```

If we wanted to accept a connection using our newly created server, we just open its accept file:
Expand All @@ -283,8 +283,8 @@ The file descriptor returned when the accept file is opened can be used to send
For the sake of completeness, to connect the server we just create a new socket and use the `connect` command:

```c
char* id = sreadfile("/net/local/seqpacket");
swritefile(F("/net/local/%s/ctl", id), "connect myserver");
char* id = readfiles("/net/local/seqpacket");
writefiles(F("/net/local/%s/ctl", id), "connect myserver");
free(id);
```

Expand Down Expand Up @@ -361,21 +361,21 @@ At this point, the process exists but its stuck blocking before it is can load i
Now we can redirect the stdio file descriptors in the child process using the `/proc/[pid]/ctl` file, which just like the socket ctl file, allows us to send commands to control the process. In this case, we want to use two commands, `dup2` to redirect the stdio file descriptors and `close` to close the unneeded file descriptors.

```c
swritefile(F("/proc/%d/ctl", child), F("dup2 %d 0 && dup2 %d 1 && dup2 %d 2 && close 3 -1", stdin, stdout, stderr));
writefiles(F("/proc/%d/ctl", child), F("dup2 %d 0 && dup2 %d 1 && dup2 %d 2 && close 3 -1", stdin, stdout, stderr));
```

> Note that `close` can either take one or two arguments. When two arguments are provided, it closes all file descriptors in the specified range. In our case `-1` causes a underflow to the maximum file descriptor value, closing all file descriptors higher than or equal to the first argument.

Next, we create the environment variable by creating a file in the child's `/proc/[pid]/env/` directory:

```c
swritefile(F("/proc/%d/env/MY_VAR:create", child), "my_value");
writefiles(F("/proc/%d/env/MY_VAR:create", child), "my_value");
```

Finally, we can start the child process using the `start` command:

```c
swritefile(F("/proc/%d/ctl", child), "start");
writefiles(F("/proc/%d/ctl", child), "start");
```

At this point the child process will begin executing with its stdio redirected to the specified file descriptors and the environment variable set as expected.
Expand Down Expand Up @@ -426,7 +426,7 @@ For a basic example, say we have a process A which creates a child process B. Pr
const char* argv[] = {"/base/bin/b", NULL};
pid_t child = spawn(argv, SPAWN_EMPTY_NS | SPAWN_SUSPENDED);
// Mount/bind other needed directories but not /secret
swritefile(F("/proc/%d/ctl", child), "mount ... && bind ... && start");
writefiles(F("/proc/%d/ctl", child), "mount ... && bind ... && start");
```

Alternatively, process A could mount a new empty tmpfs instance in its own namespace over the `/secret` directory using the ":private" flag. This prevents a child namespace from inheriting the mountpoint and process A could store whatever it wanted there:
Expand Down
5 changes: 3 additions & 2 deletions include/kernel/cpu/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@
*/
typedef enum
{
SYS_PROCESS_EXIT,
SYS_EXITS,
SYS_THREAD_EXIT,
SYS_SPAWN,
SYS_NANOSLEEP,
SYS_ERRNO,
SYS_GETPID,
SYS_GETTID,
SYS_UPTIME,
SYS_UNIX_EPOCH,
SYS_EPOCH,
SYS_OPEN,
SYS_OPEN2,
SYS_CLOSE,
Expand Down Expand Up @@ -101,6 +101,7 @@ typedef enum
SYS_SYMLINK,
SYS_MOUNT,
SYS_UNMOUNT,
SYS_ARCH_PRCTL,
SYS_TOTAL_AMOUNT
} syscall_number_t;

Expand Down
2 changes: 1 addition & 1 deletion include/kernel/sched/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ void sched_enable(void);
*
* @param status The exit status of the process.
*/
_NORETURN void sched_process_exit(const char* status);
_NORETURN void sched_exits(const char* status);

/**
* @brief Terminates the currently executing thread.
Expand Down
1 change: 1 addition & 0 deletions include/kernel/sched/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ typedef struct thread
syscall_ctx_t syscall;
perf_thread_ctx_t perf;
rcu_entry_t rcu;
uintptr_t fsBase; ///< The FS base address for the thread.
/**
* The threads interrupt frame is used to save the values in the CPU registers such that the scheduler can continue
* executing the thread later on.
Expand Down
14 changes: 5 additions & 9 deletions include/kernel/sync/rcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,10 @@ typedef struct rcu_entry rcu_entry_t;
* `rcu_entry_t` member and a callback function that will free the structure.
*
* To access RCU protected data, a read-side critical section must be created using `rcu_read_lock()` and
* `rcu_read_unlock()`, or the `RCU_READ_SCOPE()` macro. Within the critical section, RCU protected pointers should be
* accessed using the `RCU_DEREFERENCE()` macro, and any updates to RCU protected pointers should be done using the
* `RCU_ASSIGN_POINTER()` macro.
* `rcu_read_unlock()`, or the `RCU_READ_SCOPE()` macro.
*
* @see https://en.wikipedia.org/wiki/Read-copy-update for more information about RCU.
* @see https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt for a explanation of RCU in the Linux kernel.
* @see [https://en.wikipedia.org/wiki/Read-copy-update](Wikipedia) for more information about RCU.
* @see [https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt](kernel.org) for a explanation of RCU in the Linux kernel.
*
* @{
*/
Expand Down Expand Up @@ -76,7 +74,7 @@ typedef struct rcu_entry
*/
static inline void rcu_read_lock(void)
{
cli_push();
sched_disable();
}

/**
Expand All @@ -86,7 +84,7 @@ static inline void rcu_read_lock(void)
*/
static inline void rcu_read_unlock(void)
{
cli_pop();
sched_enable();
}

/**
Expand Down Expand Up @@ -114,8 +112,6 @@ void rcu_call(rcu_entry_t* entry, rcu_callback_t func, void* arg);

/**
* @brief Called during a context switch to report a quiescent state.
*
* Will also be invoked via an IPI when a grace period starts if the CPU is not in a quiescent state.
*/
void rcu_report_quiescent(void);

Expand Down
16 changes: 8 additions & 8 deletions include/libstd/sys/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ size_t read(fd_t fd, void* buffer, size_t count);
/**
* @brief Wrapper for reading a file directly into a null-terminated string.
*
* The `sread()` function reads the entire contents of a file into a newly allocated null-terminated string.
* The `reads()` function reads the entire contents of a file into a newly allocated null-terminated string.
* The caller is responsible for freeing the returned string.
*
* @param fd The file descriptor to read from.
* @return On success, a pointer to the null-terminated string. On failure, `NULL` and `errno` is set.
*/
char* sread(fd_t fd);
char* reads(fd_t fd);

/**
* @brief Wrapper for reading a file directly using a path.
Expand All @@ -158,15 +158,15 @@ size_t readfile(const char* path, void* buffer, size_t count, size_t offset);
/**
* @brief Wrapper for reading an entire file directly into a null-terminated string.
*
* The `sreadfile()` function reads the entire contents of a file into a newly allocated null-terminated string.
* The `readfiles()` function reads the entire contents of a file into a newly allocated null-terminated string.
* The caller is responsible for freeing the returned string.
*
* Equivalent to calling `open()`, `sread()`, and `close()` in sequence.
* Equivalent to calling `open()`, `reads()`, and `close()` in sequence.
*
* @param path The path to the file.
* @return On success, a pointer to the null-terminated string. On failure, `NULL` and `errno` is set.
*/
char* sreadfile(const char* path);
char* readfiles(const char* path);

/**
* @brief System call for writing to files.
Expand All @@ -185,7 +185,7 @@ size_t write(fd_t fd, const void* buffer, size_t count);
* @param string The null-terminated string to write.
* @return On success, the number of bytes written. On failure, `ERR` and `errno` is set.
*/
size_t swrite(fd_t fd, const char* string);
size_t writes(fd_t fd, const char* string);

/**
* @brief Wrapper for writing to a file directly using a path.
Expand All @@ -203,13 +203,13 @@ size_t writefile(const char* path, const void* buffer, size_t count, size_t offs
/**
* @brief Wrapper for writing a null-terminated string directly to a file using a path.
*
* Equivalent to calling `open()`, `swrite()`, and `close()` in sequence.
* Equivalent to calling `open()`, `writes()`, and `close()` in sequence.
*
* @param path The path to the file.
* @param string The null-terminated string to write.
* @return On success, the number of bytes written. On failure, `ERR` and `errno` is set.
*/
size_t swritefile(const char* path, const char* string);
size_t writefiles(const char* path, const char* string);

/**
* @brief Wrapper for reading from a file descriptor using scan formatting.
Expand Down
60 changes: 58 additions & 2 deletions include/libstd/sys/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdatomic.h>

/**
* @brief Doubly linked list header.
* @ingroup libstd
* @defgroup libstd_sys_list Doubly linked list
*
* The `sys/list.h` header implements a intrusive doubly linked list where the linked list entry structure is stored
* within each entry instead of each entry having a pointer to each stucture.
* The `sys/list.h` header implements a intrusive doubly linked list.
*
* Given a entry within a structure, the `CONTAINER_OF()` macro can be used to get a pointer to the structure from the
* list entry pointer.
*
* @warning If a list is protected with RCU, the `list_*_rcu()` functions must be used.
*
* @{
*/
Expand Down Expand Up @@ -233,6 +235,30 @@ static inline void list_add(list_entry_t* prev, list_entry_t* next, list_entry_t
prev->next = entry;
}

/**
* @brief Adds a new element between two existing list entries in a RCU-safe manner.
*
* @param prev A pointer to the list entry that will precede the new element.
* @param next A pointer to the list entry that will follow the new element.
* @param elem A pointer to the `list_entry_t` to add.
*/
static inline void list_add_rcu(list_entry_t* prev, list_entry_t* next, list_entry_t* entry)
{
assert(prev != NULL);
assert(next != NULL);
assert(entry != NULL);
// For RCU we allow adding an entry that is already in a list
// as we cant properly remove it until all readers are done.
//assert(entry->next == entry && entry->prev == entry);
//assert(prev->next == next && next->prev == prev);

next->prev = entry;
entry->next = next;
entry->prev = prev;
atomic_thread_fence(memory_order_release);
prev->next = entry;
}

/**
* @brief Appends an entry to the list.
*
Expand Down Expand Up @@ -270,6 +296,22 @@ static inline void list_remove(list_entry_t* entry)
list_entry_init(entry);
}

/**
* @brief Removes a list entry from its current list in a RCU-safe manner.
*
* @warning After calling this function the entry will still be connected to the list, but iteration over the list will not find it.
*
* @param entry A pointer to the `list_entry_t` to remove.
*/
static inline void list_remove_rcu(list_entry_t* entry)
{
assert(entry != NULL);

entry->prev->next = entry->next;
atomic_thread_fence(memory_order_release);
entry->next->prev = entry->prev;
}

/**
* @brief Pushes an entry to the end of the list.
*
Expand All @@ -285,6 +327,20 @@ static inline void list_push_back(list_t* list, list_entry_t* entry)
list_add(list->head.prev, &list->head, entry);
}

/**
* @brief Pushes an entry to the end of the list in a RCU-safe manner.
*
* @param list A pointer to the `list_t` to push the entry to.
* @param entry A pointer to the `list_entry_t` to push.
*/
static inline void list_push_back_rcu(list_t* list, list_entry_t* entry)
{
assert(list != NULL);
assert(entry != NULL);

list_add_rcu(list->head.prev, &list->head, entry);
}

/**
* @brief Pushes an entry to the front of the list.
*
Expand Down
4 changes: 2 additions & 2 deletions include/libstd/sys/math.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
#include <stdint.h>

/**
* @brief Common math macros.
* @brief Helper Macros for math operations.
* @ingroup libstd
* @defgroup libstd_sys_math Common math macros
* @defgroup libstd_sys_math Math Helpers
*
* The `sys/math.h` header provides common math macros for operations such as clamping, rounding, and linear
* interpolation.
Expand Down
Loading