summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in2
-rw-r--r--lib/libc/src/hyra/wait.c37
-rw-r--r--share/man/man1/beep.145
-rw-r--r--share/man/man1/cat.147
-rw-r--r--share/man/man1/echo.147
-rw-r--r--share/man/man1/mex.148
-rw-r--r--share/man/man1/nerve.173
-rw-r--r--share/man/man2/exit.255
-rw-r--r--sys/arch/aarch64/conf/GENERIC7
-rw-r--r--sys/arch/amd64/amd64/machdep.c16
-rw-r--r--sys/arch/amd64/amd64/mp.c17
-rw-r--r--sys/arch/amd64/conf/GENERIC8
-rw-r--r--sys/conf/BLACKLIST1
-rw-r--r--sys/conf/GENERIC9
-rw-r--r--sys/include/arch/amd64/cpu.h1
-rw-r--r--sys/include/arch/amd64/intr.h1
-rw-r--r--sys/include/sys/proc.h14
-rw-r--r--sys/include/sys/spawn.h2
-rw-r--r--sys/include/sys/syscall.h1
-rw-r--r--sys/include/sys/wait.h37
-rw-r--r--sys/kern/init_main.c3
-rw-r--r--sys/kern/kern_exit.c7
-rw-r--r--sys/kern/kern_sched.c66
-rw-r--r--sys/kern/kern_spawn.c72
-rw-r--r--sys/kern/kern_syscall.c1
-rw-r--r--usr.bin/Makefile3
-rw-r--r--usr.bin/kstat/Makefile6
-rw-r--r--usr.bin/kstat/kstat.c73
-rw-r--r--usr.bin/login/login.c3
-rw-r--r--usr.bin/nerve/Makefile6
-rw-r--r--usr.bin/nerve/nerve.c377
-rw-r--r--usr.bin/osh/osh.c54
-rw-r--r--usr.bin/whoami/Makefile6
-rw-r--r--usr.bin/whoami/whoami.c38
-rw-r--r--usr.sbin/init/main.c3
-rw-r--r--usr.sbin/install/install.c8
36 files changed, 1119 insertions, 75 deletions
diff --git a/Makefile.in b/Makefile.in
index 0552761..42a0f94 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -15,7 +15,7 @@ override KERNEL_LDFLAGS = -no-pie -nostdlib -znoexecstack -zmax-page-size=0x1000
override QEMU_FLAGS = @QEMU_FLAGS@
override KERNEL_DEFINES = $(KBUILD_ARGS) -DHYRA_VERSION="\"$(HYRA_VERSION)\""\
-DHYRA_BUILDDATE="\"@HYRA_BUILDDATE@\""\
- -DHYRA_ARCH="\"@ARCH@\"" $(shell cat sys/arch/$(ARCH)/conf/GENERIC | tools/kconf/kconf)
+ -DHYRA_ARCH="\"@ARCH@\"" $(shell cat sys/arch/$(ARCH)/conf/GENERIC sys/conf/GENERIC | tools/kconf/kconf)
######################
# Toolchain
######################
diff --git a/lib/libc/src/hyra/wait.c b/lib/libc/src/hyra/wait.c
new file mode 100644
index 0000000..99f9228
--- /dev/null
+++ b/lib/libc/src/hyra/wait.c
@@ -0,0 +1,37 @@
+/*
+ * 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/syscall.h>
+#include <sys/wait.h>
+
+pid_t
+waitpid(pid_t pid, int *wstatus, int options)
+{
+ return syscall(SYS_waitpid, pid, (uintptr_t)wstatus, options);
+}
diff --git a/share/man/man1/beep.1 b/share/man/man1/beep.1
new file mode 100644
index 0000000..9905952
--- /dev/null
+++ b/share/man/man1/beep.1
@@ -0,0 +1,45 @@
+.\" Copyright (c) 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.
+.Dd Jul 17 2025
+.Dt BEEP 1
+.Os HYRA
+.Sh NAME
+.Nm beep - beep the speaker
+.Sh SYNOPSIS
+beep [freq] [duration]
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command beeps the PC speaker at a given frequency ('freq') in hertz for
+a given duration ('duration') in milliseconds. This can be useful scripts
+that notify the user or just for fun! Just beware that it can get very annoying
+very fast!
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/cat.1 b/share/man/man1/cat.1
new file mode 100644
index 0000000..8214724
--- /dev/null
+++ b/share/man/man1/cat.1
@@ -0,0 +1,47 @@
+.\" Copyright (c) 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.
+.Dd Jul 17 2025
+.Dt CAT 1
+.Os HYRA
+.Sh NAME
+.Nm cat - concatenate files and print to standard output
+.Sh SYNOPSIS
+cat [FILE] ...
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command can be used concatenate files or simply write their contents
+to standard output.
+
+.Sh SEE ALSO
+
+mex(1)
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/echo.1 b/share/man/man1/echo.1
new file mode 100644
index 0000000..6adf192
--- /dev/null
+++ b/share/man/man1/echo.1
@@ -0,0 +1,47 @@
+.\" Copyright (c) 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.
+.Dd Jul 17 2025
+.Dt ECHO 1
+.Os HYRA
+.Sh NAME
+.Nm echo - print a line of text
+.Sh SYNOPSIS
+echo [STRING]
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command displays a given string to the console. Used within scripts for logging
+information or debugging. An example usage of this command is:
+
+.Bd -literal
+echo MEOWWWW !!!
+.Ed
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/mex.1 b/share/man/man1/mex.1
new file mode 100644
index 0000000..8d19752
--- /dev/null
+++ b/share/man/man1/mex.1
@@ -0,0 +1,48 @@
+.\" Copyright (c) 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.
+.Dd Jul 17 2025
+.Dt MEX 1
+.Os HYRA
+.Sh NAME
+.Nm mex - perform a hexdump on a file
+.Sh SYNOPSIS
+mex [ /path/to/file ]
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command dumps the bytes of a given file (specified by a path) in
+base-16 (hexadecimal) representation. It is useful for debugging,
+binary analysis, and satisfying ones nagging curiosity.
+
+.Sh SEE ALSO
+
+cat(1)
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/nerve.1 b/share/man/man1/nerve.1
new file mode 100644
index 0000000..8f2d19e
--- /dev/null
+++ b/share/man/man1/nerve.1
@@ -0,0 +1,73 @@
+.\" Copyright (c) 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.
+.Dd Jul 18 2025
+.Dt NERVE 1
+.Os HYRA
+.Sh NAME
+.Nm nerve - interact with control files
+.Sh SYNOPSIS
+nerve <verb> [ .. payload for pokes ..]
+
+verb 'poke': Poke a nerve
+
+verb 'peek': Peek at a nerve
+
+nerve ending 'consattr': Console attributes
+
+nerve ending 'consfeat': Console features
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command can be used to modify system attributes and behaviour through various
+nerve endings (e.g., control files, et cetera). To drive a nerve, one must provide
+a verb (describing an operation) as well as a payload. An example of this command
+is moving the cursor position to the HOME (0, 0) position:
+
+.Bd -literal
+nerve poke consattr 0 0
+ / / / /
+ verb nerve [x] [y]
+.Ed
+
+A nerve may also be peeked at by using the 'peek' verb. Here is an example of the usage
+of this verb:
+
+.Bd -literal
+nerve peek consfeat
+ / /
+ verb nerve
+
+output:
+ ansi_esc=1
+ show_curs=0
+ ...
+.Ed
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man2/exit.2 b/share/man/man2/exit.2
new file mode 100644
index 0000000..8342f50
--- /dev/null
+++ b/share/man/man2/exit.2
@@ -0,0 +1,55 @@
+.\" Copyright (c) 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.
+.Dd Jul 17 2025
+.Dt EXIT 2
+.Os HYRA
+.Sh NAME
+.Nm exit, _Exit
+.Sh SYNOPSIS
+#include <stdlib.h>
+
+[[noreturn]] void exit(int status)
+
+[[noreturn]] void _Exit(int status)
+
+.Sh DESCRIPTION
+
+The exit() function terminates the calling process while calling exit
+handlers and performing libc specific cleanups. On the other hand, the
+_Exit() function terminates the calling process *immediately* bypassing
+internal libc exit routines and cleanup hooks.
+
+The value
+.Ft status[7:0]
+is returned to the parent process to indicate the reason of process termination.
+
+.Sh RETURN VALUE
+
+These functions do not return
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/sys/arch/aarch64/conf/GENERIC b/sys/arch/aarch64/conf/GENERIC
index eeb9d9d..702a248 100644
--- a/sys/arch/aarch64/conf/GENERIC
+++ b/sys/arch/aarch64/conf/GENERIC
@@ -1,10 +1,3 @@
// Kernel options
option SERIAL_DEBUG yes // Enable kmsg serial logging
option USER_KMSG yes // Show kmsg in user consoles
-
-// Kernel constants
-setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ)
-
-// Console attributes
-setval CONSOLE_BG 0x000000
-setval CONSOLE_FG 0xB57614
diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c
index 40950f9..efd1af8 100644
--- a/sys/arch/amd64/amd64/machdep.c
+++ b/sys/arch/amd64/amd64/machdep.c
@@ -111,8 +111,16 @@ tlb_shootdown_isr(void *p)
}
static void
-setup_vectors(void)
+setup_vectors(struct cpu_info *ci)
{
+ union tss_stack scstack;
+
+ /* Try to allocate a syscall stack */
+ if (tss_alloc_stack(&scstack, DEFAULT_PAGESIZE) != 0) {
+ panic("failed to allocate syscall stack\n");
+ }
+
+ tss_update_ist(ci, scstack, IST_SYSCALL);
idt_set_desc(0x0, IDT_TRAP_GATE, ISR(arith_err), 0);
idt_set_desc(0x2, IDT_TRAP_GATE, ISR(nmi), 0);
idt_set_desc(0x3, IDT_TRAP_GATE, ISR(breakpoint_handler), 0);
@@ -125,7 +133,7 @@ setup_vectors(void)
idt_set_desc(0xC, IDT_TRAP_GATE, ISR(ss_fault), 0);
idt_set_desc(0xD, IDT_TRAP_GATE, ISR(general_prot), 0);
idt_set_desc(0xE, IDT_TRAP_GATE, ISR(page_fault), 0);
- idt_set_desc(0x80, IDT_USER_INT_GATE, ISR(syscall_isr), 0);
+ idt_set_desc(0x80, IDT_USER_INT_GATE, ISR(syscall_isr), IST_SYSCALL);
idt_set_desc(HALT_VECTOR, IDT_INT_GATE, ISR(cpu_halt_isr), 0);
idt_set_desc(TLB_VECTOR, IDT_INT_GATE, ISR(tlb_shootdown_isr), 0);
pin_isr_load();
@@ -405,10 +413,10 @@ cpu_startup(struct cpu_info *ci)
gdt_load();
idt_load();
- setup_vectors();
wrmsr(IA32_GS_BASE, (uintptr_t)ci);
-
init_tss(ci);
+ setup_vectors(ci);
+
try_mitigate_spectre();
cpu_get_info(ci);
diff --git a/sys/arch/amd64/amd64/mp.c b/sys/arch/amd64/amd64/mp.c
index 21881b2..20f550f 100644
--- a/sys/arch/amd64/amd64/mp.c
+++ b/sys/arch/amd64/amd64/mp.c
@@ -56,6 +56,7 @@ static void
ap_trampoline(struct limine_smp_info *si)
{
struct cpu_info *ci;
+ struct proc *idle;
ci = dynalloc(sizeof(*ci));
__assert(ci != NULL);
@@ -64,6 +65,11 @@ ap_trampoline(struct limine_smp_info *si)
cpu_startup(ci);
spinlock_acquire(&ci_list_lock);
ci_list[ncpu_up] = ci;
+
+ ci->id = ncpu_up;
+ spawn(&g_proc0, sched_enter, NULL, 0, &idle);
+ proc_pin(idle, ci->id);
+
spinlock_release(&ci_list_lock);
atomic_inc_int(&ncpu_up);
@@ -110,6 +116,7 @@ mp_bootstrap_aps(struct cpu_info *ci)
{
struct limine_smp_response *resp = g_smp_req.response;
struct limine_smp_info **cpus;
+ struct proc *idle;
size_t cpu_init_counter;
uint32_t ncpu;
@@ -121,6 +128,10 @@ mp_bootstrap_aps(struct cpu_info *ci)
cpu_init_counter = ncpu - 1;
ci_list[0] = ci;
+ /* Pin an idle thread to the BSP */
+ spawn(&g_proc0, sched_enter, NULL, 0, &idle);
+ proc_pin(idle, 0);
+
if (resp->cpu_count == 1) {
pr_trace("CPU has 1 core, no APs to bootstrap...\n");
return;
@@ -136,12 +147,6 @@ mp_bootstrap_aps(struct cpu_info *ci)
cpus[i]->goto_address = ap_trampoline;
}
- /* Start up idle threads */
- pr_trace("kicking %d idle threads...\n", ncpu);
- for (uint32_t i = 0; i < ncpu; ++i) {
- spawn(&g_proc0, sched_enter, NULL, 0, NULL);
- }
-
/* Wait for all cores to be ready */
while ((ncpu_up - 1) < cpu_init_counter);
}
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index e407fa9..6f573f3 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -9,12 +9,4 @@ option SPECTRE_IBRS no // Enable the IBRS CPU feature
option SERIAL_DEBUG yes // Enable kmsg serial logging
option USER_KMSG no // Show kmsg in user consoles
option CPU_SMEP yes // Supervisor Memory Exec Protection
-option PANIC_SCR no // Clear screen on panic
option I8042_POLL yes // Use polling for the i8042
-
-// Kernel constants
-setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ)
-
-// Console attributes
-setval CONSOLE_BG 0x000000
-setval CONSOLE_FG 0xB57614
diff --git a/sys/conf/BLACKLIST b/sys/conf/BLACKLIST
new file mode 100644
index 0000000..4eb32d8
--- /dev/null
+++ b/sys/conf/BLACKLIST
@@ -0,0 +1 @@
+xhci ahci nvme
diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC
new file mode 100644
index 0000000..5734c43
--- /dev/null
+++ b/sys/conf/GENERIC
@@ -0,0 +1,9 @@
+// Kernel options
+option PANIC_SCR no // Clear screen on panic
+
+// Kernel constants
+setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ)
+
+// Console attributes
+setval CONSOLE_BG 0x000000
+setval CONSOLE_FG 0xB57614
diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h
index a047cef..046b621 100644
--- a/sys/include/arch/amd64/cpu.h
+++ b/sys/include/arch/amd64/cpu.h
@@ -46,6 +46,7 @@
struct cpu_info {
uint32_t apicid;
uint32_t feat;
+ uint8_t id; /* MI Logical ID */
uint8_t model : 4; /* CPU model number */
uint8_t family : 4; /* CPU family ID */
uint8_t has_x2apic : 1;
diff --git a/sys/include/arch/amd64/intr.h b/sys/include/arch/amd64/intr.h
index 1877d20..3870f18 100644
--- a/sys/include/arch/amd64/intr.h
+++ b/sys/include/arch/amd64/intr.h
@@ -35,6 +35,7 @@
#define IST_SCHED 1U
#define IST_HW_IRQ 2U
#define IST_SW_INT 3U
+#define IST_SYSCALL 4U
/* Upper 4 bits of interrupt vector */
#define IPL_SHIFT 4
diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h
index decc615..9cc9238 100644
--- a/sys/include/sys/proc.h
+++ b/sys/include/sys/proc.h
@@ -74,6 +74,14 @@ struct __packed coredump {
uint32_t checksum;
};
+/*
+ * Sometimes we may need to pin a process
+ * to a specific CPU. This type represents
+ * the (machine independent) logical processor
+ * ID for a process to be pinned to.
+ */
+typedef int16_t affinity_t;
+
struct proc {
pid_t pid;
struct exec_prog exec;
@@ -86,6 +94,7 @@ struct proc {
struct trapframe tf;
struct pcb pcb;
struct proc *parent;
+ affinity_t affinity;
void *data;
size_t priority;
int exit_status;
@@ -107,10 +116,14 @@ struct proc {
#define PROC_WAITED BIT(4) /* Being waited on by parent */
#define PROC_KTD BIT(5) /* Kernel thread */
#define PROC_SLEEP BIT(6) /* Thread execution paused */
+#define PROC_PINNED BIT(7) /* Pinned to CPU */
struct proc *this_td(void);
struct proc *get_child(struct proc *cur, pid_t pid);
+void proc_pin(struct proc *td, affinity_t cpu);
+void proc_unpin(struct proc *td);
+
void proc_reap(struct proc *td);
void proc_coredump(struct proc *td, uintptr_t fault_addr);
@@ -119,6 +132,7 @@ pid_t getppid(void);
scret_t sys_getpid(struct syscall_args *scargs);
scret_t sys_getppid(struct syscall_args *scargs);
+scret_t sys_waitpid(struct syscall_args *scargs);
int md_spawn(struct proc *p, struct proc *parent, uintptr_t ip);
diff --git a/sys/include/sys/spawn.h b/sys/include/sys/spawn.h
index 0c54e4c..28dbe5b 100644
--- a/sys/include/sys/spawn.h
+++ b/sys/include/sys/spawn.h
@@ -33,8 +33,6 @@
#include <sys/types.h>
#include <sys/param.h>
-#define SPAWN_WAIT BIT(0)
-
#if !defined(_KERNEL)
pid_t spawn(const char *pathname, char **argv, char **envp, int flags);
#endif /* _KERNEL */
diff --git a/sys/include/sys/syscall.h b/sys/include/sys/syscall.h
index 51c2579..02629a9 100644
--- a/sys/include/sys/syscall.h
+++ b/sys/include/sys/syscall.h
@@ -58,6 +58,7 @@
#define SYS_getppid 17
#define SYS_setuid 18
#define SYS_getuid 19
+#define SYS_waitpid 20
#if defined(_KERNEL)
/* Syscall return value and arg type */
diff --git a/sys/include/sys/wait.h b/sys/include/sys/wait.h
new file mode 100644
index 0000000..07a2d4e
--- /dev/null
+++ b/sys/include/sys/wait.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_WAIT_H_
+#define _SYS_WAIT_H_
+
+#include <sys/types.h>
+
+pid_t waitpid(pid_t pid, int *wstatus, int options);
+
+#endif /* !_SYS_WAIT_H_ */
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index 6b3e09b..5e351a8 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -52,6 +52,7 @@
#endif /* _INSTALL_MEDIA */
struct proc g_proc0;
+struct proc *g_init;
static void
copyright(void)
@@ -114,7 +115,7 @@ main(void)
memset(&g_proc0, 0, sizeof(g_proc0));
/* Startup pid 1 */
- spawn(&g_proc0, start_init, NULL, 0, NULL);
+ spawn(&g_proc0, start_init, NULL, 0, &g_init);
md_inton();
/* Load all early drivers */
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 6b41cbd..9377eed 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -46,6 +46,7 @@
#define pr_error(...) pr_trace(__VA_ARGS__)
extern volatile size_t g_nthreads;
+extern struct proc g_init;
static void
unload_td(struct proc *td)
@@ -150,17 +151,17 @@ exit1(struct proc *td, int flags)
curtd = this_td();
curpid = curtd->pid;
+
td->flags |= PROC_EXITING;
parent = td->parent;
/* We have one less process in the system! */
atomic_dec_64(&g_nthreads);
- /* If we have any children, kill them too */
+ /* Reassign children to init */
if (td->nleaves > 0) {
TAILQ_FOREACH(procp, &td->leafq, leaf_link) {
- if (!ISSET(procp->flags, PROC_EXITING))
- exit1(procp, flags);
+ procp->parent = &g_init;
}
}
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
index e259a2c..23a1ebb 100644
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -78,13 +78,37 @@ sched_oneshot(bool now)
timer.oneshot_us(usec);
}
+/*
+ * Returns true if a processor is associated
+ * with a specific thread
+ *
+ * @ci: CPU that wants to take 'td'
+ * @td: Thread to check against
+ */
+static bool
+cpu_is_assoc(struct cpu_info *ci, struct proc *td)
+{
+ /*
+ * If we are not pinned, any processor is
+ * associated.
+ */
+ if (!ISSET(td->flags, PROC_PINNED)) {
+ return true;
+ }
+
+ return ci->id == td->affinity;
+}
+
struct proc *
sched_dequeue_td(void)
{
struct sched_queue *queue;
struct proc *td = NULL;
+ struct cpu_info *ci;
+ uint32_t ncpu = 0;
spinlock_acquire(&tdq_lock);
+ ci = this_cpu();
for (size_t i = 0; i < SCHED_NQUEUE; ++i) {
queue = &qlist[i];
@@ -104,6 +128,19 @@ sched_dequeue_td(void)
}
}
+ /*
+ * If we are on a multicore system and this isn't
+ * our process, don't take it. Some threads might
+ * be pinned to a specific processor.
+ */
+ ncpu = cpu_count();
+ while (!cpu_is_assoc(ci, td) && ncpu > 1) {
+ td = TAILQ_NEXT(td, link);
+ if (td == NULL) {
+ break;
+ }
+ }
+
if (td == NULL) {
continue;
}
@@ -249,6 +286,35 @@ sched_detach(struct proc *td)
spinlock_release(&tdq_lock);
}
+/*
+ * Pin a process to a specific processor
+ *
+ * @td: Process to pin
+ * @cpu: Logical processor ID to pin `td' to.
+ *
+ * XXX: 'cpu' is a machine independent value, representing
+ * CPU<n>
+ */
+void
+proc_pin(struct proc *td, affinity_t cpu)
+{
+ td->affinity = cpu;
+ td->flags |= PROC_PINNED;
+}
+
+/*
+ * Unpin a pinned process, allowing it to be
+ * picked up by any processor
+ *
+ * @td: Process to unpin
+ */
+void
+proc_unpin(struct proc *td)
+{
+ td->affinity = 0;
+ td->flags &= ~PROC_PINNED;
+}
+
void
sched_init(void)
{
diff --git a/sys/kern/kern_spawn.c b/sys/kern/kern_spawn.c
index 75ebaa7..b9551f3 100644
--- a/sys/kern/kern_spawn.c
+++ b/sys/kern/kern_spawn.c
@@ -28,6 +28,7 @@
*/
#include <sys/spawn.h>
+#include <sys/wait.h>
#include <sys/proc.h>
#include <sys/exec.h>
#include <sys/mman.h>
@@ -96,6 +97,35 @@ spawn_thunk(void)
__builtin_unreachable();
}
+pid_t
+waitpid(pid_t pid, int *wstatus, int options)
+{
+ struct proc *child, *td;
+ pid_t ret;
+
+ td = this_td();
+ child = get_child(td, pid);
+
+ if (child == NULL) {
+ return -1;
+ }
+
+ /* Wait for it to be done */
+ while (!ISSET(child->flags, PROC_ZOMB)) {
+ sched_yield();
+ }
+
+
+ /* Give back the status */
+ if (wstatus != NULL) {
+ copyout(&child->exit_status, wstatus, sizeof(*wstatus));
+ }
+
+ ret = child->pid;
+ proc_reap(child);
+ return ret;
+}
+
/*
* Spawn a new process
*
@@ -173,24 +203,6 @@ spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **new
signals_init(newproc);
sched_enqueue_td(newproc);
pid = newproc->pid;
-
- if (ISSET(flags, SPAWN_WAIT)) {
- cur->flags |= PROC_SLEEP;
-
- while (ISSET(cur->flags, PROC_SLEEP)) {
- sched_yield();
- }
- while (!ISSET(newproc->flags, PROC_ZOMB)) {
- sched_yield();
- }
-
- if (newproc->exit_status < 0) {
- pid = newproc->exit_status;
- }
-
- proc_reap(newproc);
- }
-
return pid;
}
@@ -208,6 +220,9 @@ get_child(struct proc *cur, pid_t pid)
struct proc *procp;
TAILQ_FOREACH(procp, &cur->leafq, leaf_link) {
+ if (procp == NULL) {
+ continue;
+ }
if (procp->pid == pid) {
return procp;
}
@@ -217,6 +232,27 @@ get_child(struct proc *cur, pid_t pid)
}
/*
+ * arg0: PID
+ * arg1: wstatus
+ * arg2: options
+ *
+ * Returns PID of terminated child, returns
+ * -1 on failure.
+ */
+scret_t
+sys_waitpid(struct syscall_args *scargs)
+{
+ pid_t pid;
+ int *u_wstatus;
+ int options;
+
+ pid = scargs->arg0;
+ u_wstatus = (void *)scargs->arg1;
+ options = scargs->arg2;
+ return waitpid(pid, u_wstatus, options);
+}
+
+/*
* arg0: The file /path/to/executable
* arg1: Argv
* arg2: Envp (TODO)
diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c
index a28d2dd..cb7e1d2 100644
--- a/sys/kern/kern_syscall.c
+++ b/sys/kern/kern_syscall.c
@@ -59,6 +59,7 @@ scret_t(*g_sctab[])(struct syscall_args *) = {
sys_getppid, /* SYS_getppid */
sys_setuid, /* SYS_setuid */
sys_getuid, /* SYS_getuid */
+ sys_waitpid, /* SYS_waitpid */
};
const size_t MAX_SYSCALLS = NELEM(g_sctab);
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index e84831a..b190492 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -20,3 +20,6 @@ all:
make -C readcore/ $(ARGS)
make -C login/ $(ARGS)
make -C sleep/ $(ARGS)
+ make -C kstat/ $(ARGS)
+ make -C nerve/ $(ARGS)
+ make -C whoami/ $(ARGS)
diff --git a/usr.bin/kstat/Makefile b/usr.bin/kstat/Makefile
new file mode 100644
index 0000000..ccceb3c
--- /dev/null
+++ b/usr.bin/kstat/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/kstat:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/kstat/kstat.c b/usr.bin/kstat/kstat.c
new file mode 100644
index 0000000..853bc8b
--- /dev/null
+++ b/usr.bin/kstat/kstat.c
@@ -0,0 +1,73 @@
+/*
+ * 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/sched.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static void
+get_sched_stat(void)
+{
+ struct sched_stat stat;
+ struct sched_cpu *cpu;
+ int fd;
+
+ fd = open("/ctl/sched/stat", O_RDONLY);
+ if (fd < 0) {
+ printf("failed to get sched stat\n");
+ return;
+ }
+ if (read(fd, &stat, sizeof(stat)) < 0) {
+ printf("failed to read sched stat\n");
+ return;
+ }
+
+ close(fd);
+ printf("-------------------------------\n");
+ printf("Number of tasks: %d\n", stat.nproc);
+ printf("Number of cores online: %d\n", stat.ncpu);
+ printf("Scheduler quantum: %d usec\n", stat.quantum_usec);
+ printf("-------------------------------\n");
+
+ /*
+ * Log out some per-cpu information
+ */
+ for (int i = 0; i < stat.ncpu; ++i) {
+ cpu = &stat.cpus[i];
+ printf("[cpu %d]: %d switches\n", i, cpu->nswitch);
+ }
+}
+
+int
+main(void)
+{
+ get_sched_stat();
+ return 0;
+}
diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c
index 93b08ed..5b21303 100644
--- a/usr.bin/login/login.c
+++ b/usr.bin/login/login.c
@@ -104,6 +104,7 @@ check_user(char *alias, char *hash, char *entry)
short have_uid = 0;
short have_shell = 0;
uid_t uid = -1;
+ pid_t shell_pid;
if (alias == NULL || entry == NULL) {
return -EINVAL;
@@ -172,7 +173,7 @@ check_user(char *alias, char *hash, char *entry)
setuid(uid);
shell_argv[0] = shell_path;
- spawn(shell_argv[0], shell_argv, envp, SPAWN_WAIT);
+ shell_pid = spawn(shell_argv[0], shell_argv, envp, 0);
return 0;
}
diff --git a/usr.bin/nerve/Makefile b/usr.bin/nerve/Makefile
new file mode 100644
index 0000000..cc0fd91
--- /dev/null
+++ b/usr.bin/nerve/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/nerve:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/nerve/nerve.c b/usr.bin/nerve/nerve.c
new file mode 100644
index 0000000..75a19be
--- /dev/null
+++ b/usr.bin/nerve/nerve.c
@@ -0,0 +1,377 @@
+/*
+ * 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/errno.h>
+#include <sys/console.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+/* Verb numeric defs (see string defs) */
+#define VERB_UNKNOWN -1
+#define VERB_POKE 0x0000
+#define VERB_PEEK 0x0001
+
+/* Verb string defs (see numeric defs) */
+#define SVERB_POKE "poke"
+#define SVERB_PEEK "peek"
+
+/* Nerve numeric defs (see string defs) */
+#define NERVE_UNKNOWN -1
+#define NERVE_CONSATTR 0x0000
+#define NERVE_CONSFEAT 0x0001
+
+/* Nerve string defs (see numeric defs) */
+#define SNERVE_CONSATTR "consattr"
+#define SNERVE_CONSFEAT "consfeat"
+
+/* Misc defines */
+#define NERVE_PACKET_LEN 16
+
+struct verb_handler;
+
+static int poke_nerve(const char *nerve, struct verb_handler *h);
+static int peek_nerve(const char *nerve, struct verb_handler *h);
+static int nerve_to_def(const char *nerve);
+
+/*
+ * Contains verb handlers called when a verb
+ * (e.g., 'poke') is matched.
+ */
+struct verb_handler {
+ int(*run)(const char *nerve, struct verb_handler *h);
+ char **argv;
+ size_t argc;
+};
+
+/*
+ * Holds information that may be sent down
+ * my nerves.
+ *
+ * Example: nerve poke <x> 1 0 1
+ * * * *
+ * +--------+ / / /
+ * | meow | <------+ / /
+ * |--------| / /
+ * | foo | <------+ /
+ * |--------| /
+ * | foobar | <------+
+ * +--------+
+ * packet
+ */
+struct nerve_payload {
+ uint32_t packet[NERVE_PACKET_LEN];
+ uint16_t len;
+};
+
+/*
+ * Verb handler table, when a verb is matched,
+ * its respective handler is called.
+ */
+static struct verb_handler verbtab[] = {
+ { poke_nerve },
+ { peek_nerve }
+};
+
+/*
+ * Print list of available options as well as
+ * information about the program.
+ */
+static void
+help(void)
+{
+ printf(
+ "nerve: usage: nerve <verb> [ .. data ..]\n"
+ "verb 'poke': Poke a control (/ctl) nerve\n"
+ "???????????????? NERVES ????????????????\n"
+ "consattr: Console attributes\n"
+ "consfeat: Console features\n"
+ );
+}
+
+/*
+ * The user gets to send data down my nerves through
+ * a nerve payload. This function acquires the nerve
+ * payload. Please don't hurt me.
+ *
+ * @argc: Number of arguments within argv
+ * @argv: Argument vector
+ * @res: Where the payload goes
+ */
+static int
+get_nerve_payload(int argc, char *argv[], struct nerve_payload *res)
+{
+ char *payload_str;
+ uint32_t datum;
+
+ /* Do we have a nerve payload? */
+ if (argc < 4) {
+ printf("[!] missing nerve payload\n");
+ return -1;
+ }
+
+ /* Reset fields */
+ res->len = 0;
+ memset(res->packet, 0, sizeof(res->packet));
+
+ /* Start grabbing bytes */
+ for (int i = 3; i < argc; ++i) {
+ if (res->len >= NERVE_PACKET_LEN) {
+ printf("[*] truncated packet\n");
+ break;
+ }
+ payload_str = argv[i];
+ datum = atoi(payload_str);
+ res->packet[res->len++] = datum;
+ }
+
+ return 0;
+}
+
+/*
+ * Peek at a control nerve located in /ctl/
+ *
+ * @nerve: Name of nerve to peek at
+ * @h: Verb handler, instance of self
+ *
+ * Returns less than zero if the nerve does
+ * not match.
+ */
+static int
+peek_nerve(const char *nerve, struct verb_handler *h)
+{
+ int error, nerve_idx = -1;
+
+ if (nerve == NULL || h == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ switch (nerve_idx) {
+ case NERVE_CONSATTR:
+ {
+ struct console_attr c;
+ int fd;
+
+ fd = open("/ctl/console/attr", O_RDONLY);
+ read(fd, &c, sizeof(c));
+ printf("(cursx=%d, cursy=%d)\n", c.cursor_x, c.cursor_y);
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ fd = open("/ctl/console/feat", O_RDONLY);
+ read(fd, &f, sizeof(f));
+ printf("ansi_esc=%d\n", f.ansi_esc);
+ printf("show_curs=%d\n", f.show_curs);
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Poke a control nerve located in /ctl/
+ *
+ * @nerve: Name of the nerve (e.g., consattr)
+ * @h: Verb handler, instance of self
+ *
+ * Returns less than zero if the nerve does not
+ * match.
+ */
+static int
+poke_nerve(const char *nerve, struct verb_handler *h)
+{
+ struct nerve_payload payload;
+ int error, nerve_idx = -1;
+
+ if (nerve == NULL || h == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ /* Grab the payload passed by the user */
+ error = get_nerve_payload(h->argc, h->argv, &payload);
+ if (error < 0) {
+ printf("[!] nerve error\n");
+ return -1;
+ }
+
+ switch (nerve_idx) {
+ case NERVE_CONSATTR:
+ {
+ struct console_attr c;
+ int fd;
+
+ c.cursor_x = payload.packet[0];
+ c.cursor_y = payload.packet[1];
+
+ fd = open("/ctl/console/attr", O_WRONLY);
+ write(fd, &c, sizeof(c));
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ f.ansi_esc = payload.packet[0] & 0xFF;
+ f.show_curs = payload.packet[1] & 0xFF;
+
+ fd = open("/ctl/console/feat", O_WRONLY);
+ write(fd, &f, sizeof(f));
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * Convert a nerve name into a numeric nerve
+ * definition
+ *
+ * @nerve: Nerve name to convert
+ */
+static int
+nerve_to_def(const char *nerve)
+{
+ /*
+ * Now we need to parse the nerve string
+ * and see if it matches with anything
+ * that we know.
+ */
+ switch (*nerve) {
+ case 'c':
+ if (strcmp(nerve, SNERVE_CONSATTR) == 0) {
+ return NERVE_CONSATTR;
+ } else if (strcmp(nerve, SNERVE_CONSFEAT) == 0) {
+ return NERVE_CONSFEAT;
+ }
+ }
+
+ return NERVE_UNKNOWN;
+}
+
+/*
+ * Convert a string verb, passed in through the command
+ * line, into a numeric definition
+ *
+ * @verb: String verb
+ */
+static int
+verb_to_def(const char *verb)
+{
+ if (verb == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Parse the verb and try to match it against
+ * a constant.
+ *
+ * XXX: Here we are first matching the first character
+ * before we match the entire verb as that is more
+ * efficient than scanning each entire string until
+ * one matches.
+ */
+ switch (*verb) {
+ case 'p':
+ if (strcmp(verb, SVERB_POKE) == 0) {
+ return VERB_POKE;
+ }
+ if (strcmp(verb, SVERB_PEEK) == 0) {
+ return VERB_PEEK;
+ }
+ default:
+ printf("[!] bad verb \"%s\"\n", verb);
+ return VERB_UNKNOWN;
+ }
+
+ return VERB_UNKNOWN;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct verb_handler *verbd;
+ int verb;
+
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ verb = verb_to_def(argv[1]);
+ if (verb < 0) {
+ return -1;
+ }
+
+ /* Make sure the arguments match */
+ switch (verb) {
+ case VERB_POKE:
+ if (argc < 3) {
+ printf("[!] missing nerve name\n");
+ help();
+ return -1;
+ }
+ break;
+ }
+
+ verbd = &verbtab[verb];
+ verbd->argv = argv;
+ verbd->argc = argc;
+ return verbd->run(argv[2], verbd);
+}
diff --git a/usr.bin/osh/osh.c b/usr.bin/osh/osh.c
index 5bcd2e2..0f2108c 100644
--- a/usr.bin/osh/osh.c
+++ b/usr.bin/osh/osh.c
@@ -31,6 +31,7 @@
#include <sys/cdefs.h>
#include <sys/reboot.h>
#include <sys/spawn.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdbool.h>
@@ -38,7 +39,9 @@
#include <string.h>
#include <stdio.h>
+#define is_printable(C) ((C) >= 32 && (C) <= 126)
#define is_ascii(C) ((C) >= 0 && (C) <= 128)
+
#define COMMENT '@'
#define WELCOME \
":::::::::::::::::::::::::::::::::::::::\n" \
@@ -232,7 +235,7 @@ getstr(void)
} else if (bell_fd > 0 && bs_bell) {
write(bell_fd, &beep_payload, sizeof(beep_payload));
}
- } else if (is_ascii(c) && buf_i < sizeof(buf) - 1) {
+ } else if (is_printable(c) && buf_i < sizeof(buf) - 1) {
/* write to fd and add to buffer */
buf[buf_i++] = c;
putchar(c);
@@ -250,16 +253,11 @@ builtin_run(struct builtin_cmd *cmd, int argc, char *argv[])
}
static int
-cmd_run(const char *input, int argc, char *argv[], bool wait)
+cmd_run(const char *input, int argc, char *argv[])
{
char bin_path[512];
char *envp[1] = { NULL };
- int error, spawn_flags = 0;
-
- /* Should we wait or daemonize? */
- if (wait) {
- spawn_flags |= SPAWN_WAIT;
- }
+ pid_t child;
/*
* If we can access the raw input as a file, try to
@@ -268,11 +266,11 @@ cmd_run(const char *input, int argc, char *argv[], bool wait)
* path directly into the console.
*/
if (access(input, F_OK) == 0) {
- error = spawn(input, argv, envp, spawn_flags);
- if (error < 0) {
- return error;
+ child = spawn(input, argv, envp, 0);
+ if (child < 0) {
+ return child;
}
- return 0;
+ return child;
}
snprintf(bin_path, sizeof(bin_path), "/usr/bin/%s", input);
@@ -282,11 +280,11 @@ cmd_run(const char *input, int argc, char *argv[], bool wait)
return -1;
}
- if ((error = spawn(bin_path, argv, envp, spawn_flags)) < 0) {
- return error;
+ if ((child = spawn(bin_path, argv, envp, 0)) < 0) {
+ return child;
}
- return 0;
+ return child;
}
/*
@@ -295,27 +293,29 @@ cmd_run(const char *input, int argc, char *argv[], bool wait)
* @input: Command input
* @argc: Argument count
* @argv: Argument vector
- * @wait: If false, program will be daemonized
*/
-static void
-command_match(const char *input, int argc, char *argv[], bool wait)
+static int
+command_match(const char *input, int argc, char *argv[])
{
int found = 0;
int i;
+ pid_t child = -1;
for (i = 0; cmds[i].name != NULL; i++) {
if (strcmp(input, cmds[i].name) == 0) {
builtin_run(&cmds[i], argc, argv);
found = 1;
- break;
}
}
if (found == 0) {
- if (cmd_run(input, argc, argv, wait) < 0) {
+ if ((child = cmd_run(input, argc, argv)) < 0) {
puts("Unrecognized command");
+ return -1;
}
}
+
+ return child;
}
static void
@@ -335,6 +335,7 @@ open_script(const char *pathname)
int fd, argc, buf_i = 0;
char c, *input, *argv[16];
char buf[256];
+ pid_t child;
fd = open(pathname, O_RDONLY);
if (fd < 0) {
@@ -361,7 +362,11 @@ open_script(const char *pathname)
if (c == '\n') {
buf[buf_i] = '\0';
argc = parse_args(buf, argv, sizeof(argv));
- command_match(buf, argc, argv, true);
+
+ child = command_match(buf, argc, argv);
+ if (child > 0) {
+ waitpid(child, NULL, 0);
+ }
argv[0] = NULL;
argv[1] = NULL;
@@ -381,6 +386,7 @@ main(int argc, char **argv)
int stdout_fd;
char *input, *prog_argv[16], *p;
char c;
+ pid_t child;
if (argc > 1) {
return open_script(argv[1]);
@@ -406,7 +412,11 @@ main(int argc, char **argv)
continue;
}
- command_match(input, prog_argc, prog_argv, true);
+ child = command_match(input, prog_argc, prog_argv);
+ if (child > 0) {
+ waitpid(child, NULL, 0);
+ }
+
found = 0;
buf[0] = '\0';
}
diff --git a/usr.bin/whoami/Makefile b/usr.bin/whoami/Makefile
new file mode 100644
index 0000000..ced9ae2
--- /dev/null
+++ b/usr.bin/whoami/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/whoami:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/whoami/whoami.c b/usr.bin/whoami/whoami.c
new file mode 100644
index 0000000..c3adcf0
--- /dev/null
+++ b/usr.bin/whoami/whoami.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ printf("%s\f\n", getlogin());
+ return 0;
+}
diff --git a/usr.sbin/init/main.c b/usr.sbin/init/main.c
index b5c4fab..e1ee4d8 100644
--- a/usr.sbin/init/main.c
+++ b/usr.sbin/init/main.c
@@ -46,6 +46,7 @@ main(int argc, char **argv)
start_argv[1] = NULL;
/* Start the login manager */
- spawn(login_argv[0], login_argv, envp, SPAWN_WAIT);
+ spawn(login_argv[0], login_argv, envp, 0);
+ for (;;);
return 0;
}
diff --git a/usr.sbin/install/install.c b/usr.sbin/install/install.c
index 91f75df..803c864 100644
--- a/usr.sbin/install/install.c
+++ b/usr.sbin/install/install.c
@@ -32,6 +32,7 @@
#include <sys/fbdev.h>
#include <sys/reboot.h>
#include <sys/spawn.h>
+#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
@@ -85,6 +86,7 @@ pre_installer(void)
char *argv[] = { "/usr/bin/osh", NULL };
char *envp[] = { NULL };
char c;
+ pid_t child = -1;
puts("[S]hell/[I]nstall");
for (;;) {
@@ -92,13 +94,17 @@ pre_installer(void)
if (c == 's') {
puts("\033[0m");
installer_clearscr(0x000000, false);
- spawn(argv[0], argv, envp, SPAWN_WAIT);
+ child = spawn(argv[0], argv, envp, 0);
installer_clearscr(INSTALLER_BG, true);
break;
} else if (c == 'i') {
break;
}
}
+
+ if (child > 0) {
+ waitpid(child, NULL, 0);
+ }
}
static void