summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/init_main.c1
-rw-r--r--sys/kern/kern_fork.c33
-rw-r--r--sys/kern/kern_sched.c86
-rw-r--r--sys/kern/kern_socket.c211
-rw-r--r--sys/kern/kern_syscall.c1
-rw-r--r--sys/kern/vfs_vcache.c2
6 files changed, 325 insertions, 9 deletions
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index 5e351a8..e8255bd 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -119,6 +119,7 @@ main(void)
md_inton();
/* Load all early drivers */
+ driver_blacklist("ahci");
DRIVERS_INIT();
/* Only log to kmsg from here */
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
new file mode 100644
index 0000000..2755ea0
--- /dev/null
+++ b/sys/kern/kern_fork.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/proc.h>
+
+
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
index 23a1ebb..9c5e215 100644
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -315,6 +315,92 @@ proc_unpin(struct proc *td)
td->flags &= ~PROC_PINNED;
}
+/*
+ * Suspend a process for a specified amount
+ * of time. This calling process will yield for
+ * the amount of time specified in 'tv'
+ *
+ * @td: Process to suspend (NULL for current)
+ * @tv: Time value to use
+ *
+ * XXX: 'tv' being NULL is equivalent to calling
+ * sched_detach()
+ */
+void
+sched_suspend(struct proc *td, const struct timeval *tv)
+{
+ struct timer tmr;
+ const time_t USEC_PER_SEC = 1000000;
+ ssize_t usec;
+ time_t usec_cur, usec_tmp;
+ bool have_timer = true;
+ tmrr_status_t tmr_status;
+
+ if (td == NULL)
+ td = this_td();
+ if (__unlikely(td == NULL))
+ return;
+
+ if (tv == NULL) {
+ sched_detach(td);
+ return;
+ }
+
+ /*
+ * Now, we need a generic timer so that we can compute
+ * how much time has elapsed since this process has
+ * requested to be suspended. However, we cannot assume
+ * that it would be present. If the lookup fails, all we
+ * can do is try to estimate how much time went by which
+ * works fine too, just not as accurate.
+ */
+ tmr_status = req_timer(TIMER_GP, &tmr);
+ if (tmr_status != TMRR_SUCCESS) {
+ have_timer = false;
+ }
+
+ /* We need microsecond precision */
+ if (tmr.get_time_sec == NULL) {
+ have_timer = false;
+ }
+
+ /*
+ * Compute the max time in microseconds that
+ * we will wait. We are using both tv->tv_sec
+ * and tv->tv_usec
+ */
+ usec = tv->tv_usec;
+ usec += tv->tv_sec * USEC_PER_SEC;
+ usec_cur = (have_timer) ? tmr.get_time_usec() : 0;
+
+ for (;;) {
+ sched_yield();
+
+ /*
+ * If we have a timer in our paws, compute how much
+ * time went by. Otherwise we estimate by subtracting
+ * the scheduler quantum.
+ *
+ * XXX: The timing here works decently as intended. However,
+ * it would be nice to smoothen out any jitter. Such can
+ * probably be done by subtracting 'usec' by the exponential
+ * moving average of 'usec_tmp' rather than the raw original
+ * value.
+ */
+ if (have_timer) {
+ usec_tmp = (tmr.get_time_usec() - usec_cur);
+ } else {
+ usec_tmp = DEFAULT_TIMESLICE_USEC;
+ }
+
+ /* We are done here! */
+ usec -= usec_tmp;
+ if (usec <= 0) {
+ break;
+ }
+ }
+}
+
void
sched_init(void)
{
diff --git a/sys/kern/kern_socket.c b/sys/kern/kern_socket.c
index 1717f5a..d0fbe19 100644
--- a/sys/kern/kern_socket.c
+++ b/sys/kern/kern_socket.c
@@ -31,6 +31,7 @@
#include <sys/sio.h>
#include <sys/systm.h>
#include <sys/proc.h>
+#include <sys/time.h>
#include <sys/namei.h>
#include <sys/sched.h>
#include <sys/errno.h>
@@ -47,6 +48,17 @@
static struct vops socket_vops;
/*
+ * This table maps socket option names to
+ * lengths of their underlying structure.
+ *
+ * This is used for bounds/length checking within
+ * setsockopt()
+ */
+static size_t sockopt_lentab[_SO_MAX] = {
+ [ SO_RCVTIMEO ] = sizeof(struct timeval)
+};
+
+/*
* Get a kernel socket structure from a
* file descriptor.
*
@@ -101,6 +113,7 @@ static int
socket_reclaim(struct vnode *vp)
{
struct ksocket *ksock;
+ struct sockopt *opt;
/* Is this even a socket? */
if (vp->type != VSOCK) {
@@ -112,6 +125,15 @@ socket_reclaim(struct vnode *vp)
return -EIO;
}
+ /* Free up any used options */
+ for (int i = 0; i < _SO_MAX; ++i) {
+ opt = ksock->opt[i];
+ if (opt != NULL) {
+ dynfree(opt);
+ ksock->opt[i] = NULL;
+ }
+ }
+
fd_close(ksock->sockfd);
mutex_free(ksock->mtx);
dynfree(ksock);
@@ -185,6 +207,46 @@ connect_domain(int sockfd, struct ksocket *ksock, struct sockaddr_un *un)
}
/*
+ * Wait until data is received for the
+ * recv() function.
+ *
+ * @sockfd: Socket we are waiting on
+ *
+ * Returns zero on success, otherwise a less
+ * than zero value is returned.
+ */
+static int
+socket_rx_wait(int sockfd)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+ struct timeval tv;
+ int error;
+
+ if (ksock == NULL) {
+ return -EINVAL;
+ }
+
+ error = get_ksock(sockfd, &ksock);
+ if (error < 0) {
+ return error;
+ }
+
+ /*
+ * If the socket does not have this option set,
+ * we will assume that there is no timeout value.
+ */
+ opt = ksock->opt[SO_RCVTIMEO];
+ if (opt == NULL) {
+ return 0;
+ }
+
+ memcpy(&tv, opt->data, opt->len);
+ sched_suspend(NULL, &tv);
+ return 0;
+}
+
+/*
* Send data to socket - POSIX send(2) core
*
* @sockfd: File descriptor that backs this socket
@@ -254,7 +316,7 @@ send(int sockfd, const void *buf, size_t size, int flags)
* @size: Size of the buffer
* @flags: Optional flags
*
- * Returns zero on success, otherwise a less
+ * Returns length on success, otherwise a less
* than zero errno.
*/
ssize_t
@@ -264,6 +326,7 @@ recv(int sockfd, void *buf, size_t len, int flags)
struct sockbuf *sbuf;
struct netbuf *netbuf;
size_t head;
+ ssize_t retval = len;
int error;
/* Length cannot be zero */
@@ -283,7 +346,8 @@ recv(int sockfd, void *buf, size_t len, int flags)
if (netbuf->len == 0) {
sbuf->head = 0;
sbuf->tail = 0;
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto done;
}
if (len > netbuf->len) {
@@ -292,10 +356,10 @@ recv(int sockfd, void *buf, size_t len, int flags)
head = sbuf->head;
memcpy(buf, &netbuf->data[head], len);
-
sbuf->head = (sbuf->head + len) % NETBUF_LEN;
+done:
mutex_release(ksock->mtx);
- return len;
+ return retval;
}
/*
@@ -322,6 +386,7 @@ socket(int domain, int type, int protocol)
goto fail;
}
+ memset(ksock, 0, sizeof(*ksock));
sbuf = &ksock->buf;
sbuf->head = 0;
sbuf->tail = 0;
@@ -394,6 +459,68 @@ bind(int sockfd, const struct sockaddr *addr, socklen_t len)
}
/*
+ * Set socket options - POSIX setsockopt(3) core
+ *
+ * @sockfd: File descriptor of socket
+ * @level: Protocol level
+ * @v: Options value
+ * @len: Length of data pointed to by 'v'
+ */
+int
+setsockopt(int sockfd, int level, int name, const void *v, socklen_t len)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+ size_t exp_len;
+ int error;
+
+ /* Must have a valid fd */
+ if (sockfd < 0) {
+ return -EBADF;
+ }
+
+ /* Ensure value and length are valid */
+ if (v == NULL || len == 0) {
+ return -EINVAL;
+ }
+
+ /* Verify the name */
+ if (name >= _SO_MAX) {
+ return -EINVAL;
+ }
+
+ /* Grab a new socket */
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ /* Clamp the input length as needed */
+ exp_len = sockopt_lentab[name];
+ if (len > exp_len) {
+ len = exp_len;
+ }
+
+ /*
+ * Here we will grab the socket options. If it is
+ * NULL, we'll need to allocate one.
+ */
+ if ((opt = ksock->opt[name]) == NULL) {
+ opt = dynalloc(sizeof(*opt) + len);
+
+ if (opt == NULL) {
+ return -ENOMEM;
+ }
+
+ opt->len = len;
+ ksock->opt[name] = opt;
+ }
+
+ memcpy(opt->data, v, len);
+ opt->len = len;
+ return 0;
+}
+
+/*
* Connect to a socket
*
* @sockfd: File descriptor to connect
@@ -614,10 +741,29 @@ sys_recv(struct syscall_args *scargs)
return -ENOBUFS;
}
- do {
+ for (;;) {
error = recv(sockfd, buf, len, flags);
- sched_yield();
- } while (error == -EAGAIN);
+ if (error <= 0 && error != -EAGAIN) {
+ break;
+ }
+
+ /*
+ * Wait for data to be ready on the socket.
+ * If a less than zero value is returned, don't
+ * handle timeouts.
+ */
+ error = socket_rx_wait(sockfd);
+ if (error < 0) {
+ continue;
+ }
+
+ /* Try one more time, obey timeout */
+ error = recv(sockfd, buf, len, flags);
+ if (error == -EAGAIN) {
+ return error;
+ }
+ break;
+ }
if (error < 0) {
pr_error("sys_recv: recv() fail (fd=%d)\n", sockfd);
@@ -776,7 +922,7 @@ sys_sendmsg(struct syscall_args *scargs)
}
/*
- * connnect(3) syscall
+ * connect(3) syscall
*
* arg0: sockfd
* arg1: address
@@ -807,6 +953,55 @@ sys_connect(struct syscall_args *scargs)
return connect(sockfd, sockaddr, len);
}
+/*
+ * POSIX setsockopt(3) syscall
+ *
+ * arg0: sockfd
+ * arg1: level
+ * arg2: name
+ * arg3: data
+ * arg4: len
+ */
+scret_t
+sys_setsockopt(struct syscall_args *scargs)
+{
+ int sockfd = scargs->arg0;
+ int level = scargs->arg1;
+ int name = scargs->arg2;
+ void *u_data = (void *)scargs->arg3;
+ socklen_t len = scargs->arg4;
+ void *data;
+ size_t exp_len;
+ int retval;
+
+ /* Verify that the name is correct */
+ if (name >= _SO_MAX) {
+ return -EINVAL;
+ }
+
+ /* Clamp length as needed */
+ exp_len = sockopt_lentab[name];
+ if (len > exp_len) {
+ len = exp_len;
+ }
+
+ data = dynalloc(len);
+ if (data == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Grab data from userland */
+ retval = copyin(u_data, data, len);
+ if (retval < 0) {
+ dynfree(data);
+ return retval;
+ }
+
+ retval = setsockopt(sockfd, level, name, data, len);
+ dynfree(data);
+ return retval;
+}
+
static struct vops socket_vops = {
.read = NULL,
.write = NULL,
diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c
index 576b7aa..ba70b12 100644
--- a/sys/kern/kern_syscall.c
+++ b/sys/kern/kern_syscall.c
@@ -68,6 +68,7 @@ scret_t(*g_sctab[])(struct syscall_args *) = {
sys_sendmsg, /* SYS_sendmsg */
sys_recvmsg, /* SYS_recvmsg */
sys_connect, /* SYS_connect */
+ sys_setsockopt, /* SYS_setsockopt */
};
const size_t MAX_SYSCALLS = NELEM(g_sctab);
diff --git a/sys/kern/vfs_vcache.c b/sys/kern/vfs_vcache.c
index 25e244c..6c08caf 100644
--- a/sys/kern/vfs_vcache.c
+++ b/sys/kern/vfs_vcache.c
@@ -161,7 +161,7 @@ vfs_vcache_migrate(int newtype)
args.oldp = NULL;
args.oldlenp = NULL;
args.newp = sysctl_val;
- args.newlen = strlen(sysctl_val);
+ args.newlen = strlen(sysctl_val) + 1;
if ((retval = sysctl(&args)) != 0) {
return retval;