summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rwxr-xr-xhyra-build.sh52
-rw-r--r--sys/arch/aarch64/conf/GENERIC5
-rw-r--r--sys/arch/aarch64/conf/link.ld6
-rw-r--r--sys/arch/amd64/amd64/hpet.c16
-rw-r--r--sys/arch/amd64/amd64/proc_machdep.c1
-rw-r--r--sys/arch/amd64/amd64/trap.c35
-rw-r--r--sys/arch/amd64/conf/GENERIC13
-rw-r--r--sys/arch/amd64/conf/link.ld6
-rw-r--r--sys/dev/acpi/uacpi.c13
-rw-r--r--sys/dev/ic/ahci.c2
-rw-r--r--sys/fs/devfs.c7
-rw-r--r--sys/include/dev/timer.h1
-rw-r--r--sys/include/sys/disklabel.h4
-rw-r--r--sys/include/sys/driver.h61
-rw-r--r--sys/include/sys/proc.h3
-rw-r--r--sys/include/sys/syslog.h2
-rw-r--r--sys/include/sys/systm.h2
-rw-r--r--sys/kern/driver_subr.c64
-rw-r--r--sys/kern/init_main.c42
-rw-r--r--sys/kern/kern_descrip.c9
-rw-r--r--sys/kern/kern_exit.c17
-rw-r--r--sys/kern/kern_instl.c323
-rw-r--r--sys/kern/kern_spawn.c2
-rw-r--r--sys/kern/kern_stub.c6
-rw-r--r--sys/kern/kern_syslog.c120
-rw-r--r--usr.bin/osh/osh.c25
27 files changed, 792 insertions, 51 deletions
diff --git a/README.md b/README.md
index 99d879d..559c971 100644
--- a/README.md
+++ b/README.md
@@ -21,11 +21,7 @@ After running the configure script, you can now actually build Hyra:
`./hyra-build.sh`
-This will create two ISO files:
-
-`Hyra.iso Hyra-install.iso`
-
-As of now, you should ignore the second file (`Hyra-install.iso`)
+This will generate a new `Hyra.iso` file.
Documentation:
--------------
diff --git a/hyra-build.sh b/hyra-build.sh
index 20a05d2..d8903c8 100755
--- a/hyra-build.sh
+++ b/hyra-build.sh
@@ -85,11 +85,6 @@ gen_isofs() {
# Stage 1 - build production media
####################################
stage1() {
- if [[ $install_flag == "true" ]]
- then
- make clean
- fi
-
iso_root_skel
sysroot_skel
@@ -119,6 +114,7 @@ stage2() {
sysroot_skel
echo "[*] stage2: Generate stage 2 RAMFS via OMAR"
+ mv Hyra.iso base/boot/
$RAMFS_TOOL -i base/ -o $RAMFS_NAME
echo "[*] stage2: Build kernel"
@@ -130,9 +126,43 @@ stage2() {
# Clean up
rm $RAMFS_NAME
+ rm base/boot/Hyra.iso
rm -r iso_root
}
+##################################
+# Clean up completly after build
+##################################
+hard_clean() {
+ make clean
+ rm -rf base/
+}
+
+##################################
+# Build results
+#
+# ++ ARGS ++
+# $1: ISO output name
+# -- --
+##################################
+result() {
+ echo "-------------------------------------------"
+ echo "Build finish"
+
+ if [[ $1 == "Hyra-install.iso" ]]
+ then
+ hard_clean # XXX: For safety
+ echo "Installer is at ./Hyra-install.iso"
+ echo "!!WARNING!!: Installer is _automatic_"
+ echo "!!NOTE!!: OSMORA is not responsible for incidental data loss"
+ else
+ echo "Boot image is at ./Hyra.iso"
+ fi
+
+ echo "Finished in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds"
+ echo "-------------------------------------------"
+}
+
while getopts "ih" flag
do
case "${flag}" in
@@ -167,13 +197,15 @@ if [[ $install_flag != "true" ]]
then
echo "[?] Not building installer (-i unset)"
echo "-- Skipping stage 2 --"
+ result "Hyra.iso"
else
echo "-- Begin stage 2 --"
stage2
+ result "Hyra-install.iso"
fi
-echo "-------------------------------------------"
-echo "Build finish"
-echo "Installer is at ./Hyra-install.iso"
-echo "Finished in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds"
-echo "-------------------------------------------"
+if [[ $install_flag == "true" ]]
+then
+ make clean
+ rm -rf base/
+fi
diff --git a/sys/arch/aarch64/conf/GENERIC b/sys/arch/aarch64/conf/GENERIC
index 5691685..a716ac2 100644
--- a/sys/arch/aarch64/conf/GENERIC
+++ b/sys/arch/aarch64/conf/GENERIC
@@ -1,5 +1,6 @@
// Kernel options
-option SERIAL_DEBUG yes
+option SERIAL_DEBUG yes // Enable kmsg serial logging
+option USER_KMSG yes // Show kmsg in user consoles
// Kernel constants
-setval SCHED_NQUEUE 4
+setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ)
diff --git a/sys/arch/aarch64/conf/link.ld b/sys/arch/aarch64/conf/link.ld
index c64cec3..2aa8c93 100644
--- a/sys/arch/aarch64/conf/link.ld
+++ b/sys/arch/aarch64/conf/link.ld
@@ -40,6 +40,12 @@ SECTIONS
__drivers_init_end = .;
} :rodata
+ .drivers.defer : {
+ __driversd_init_start = .;
+ *(.drivers.defer .drivers.defer)
+ __driversd_init_end = .;
+ } :rodata
+
/* Move to the next memory page for .data */
. += CONSTANT(MAXPAGESIZE);
diff --git a/sys/arch/amd64/amd64/hpet.c b/sys/arch/amd64/amd64/hpet.c
index 1670546..3b0ca46 100644
--- a/sys/arch/amd64/amd64/hpet.c
+++ b/sys/arch/amd64/amd64/hpet.c
@@ -47,6 +47,7 @@
#define CAP_CLK_PERIOD(caps) (caps >> 32)
#define FSEC_PER_SECOND 1000000000000000ULL
+#define NSEC_PER_SECOND 1000000000ULL
#define USEC_PER_SECOND 1000000ULL
static void *hpet_base = NULL;
@@ -135,6 +136,20 @@ hpet_time_usec(void)
}
static size_t
+hpet_time_nsec(void)
+{
+ uint64_t period, freq, caps;
+ uint64_t counter;
+
+ caps = hpet_read(HPET_REG_CAPS);
+ period = CAP_CLK_PERIOD(caps);
+ freq = FSEC_PER_SECOND / period;
+
+ counter = hpet_read(HPET_REG_MAIN_COUNTER);
+ return (counter * NSEC_PER_SECOND) / freq;
+}
+
+static size_t
hpet_time_sec(void)
{
return hpet_time_usec() / USEC_PER_SECOND;
@@ -180,6 +195,7 @@ hpet_init(void)
timer.usleep = hpet_usleep;
timer.nsleep = hpet_nsleep;
timer.get_time_usec = hpet_time_usec;
+ timer.get_time_nsec = hpet_time_nsec;
timer.get_time_sec = hpet_time_sec;
register_timer(TIMER_GP, &timer);
return 0;
diff --git a/sys/arch/amd64/amd64/proc_machdep.c b/sys/arch/amd64/amd64/proc_machdep.c
index 9579b7e..f6b91f9 100644
--- a/sys/arch/amd64/amd64/proc_machdep.c
+++ b/sys/arch/amd64/amd64/proc_machdep.c
@@ -201,6 +201,7 @@ md_spawn(struct proc *p, struct proc *parent, uintptr_t ip)
*/
if (rpl == 0) {
stack_base += VM_HIGHER_HALF;
+ p->flags |= PROC_KTD;
} else {
vm_map(pcbp->addrsp, stack_base, stack_base,
PROT_READ | PROT_WRITE | PROT_USER, PROC_STACK_PAGES);
diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c
index 6492a29..84a6a77 100644
--- a/sys/arch/amd64/amd64/trap.c
+++ b/sys/arch/amd64/amd64/trap.c
@@ -60,6 +60,17 @@ static const char *trap_type[] = {
[TRAP_SS] = "stack-segment fault"
};
+/* Page-fault flags */
+static const char pf_flags[] = {
+ 'p', /* Present */
+ 'w', /* Write */
+ 'u', /* User */
+ 'r', /* Reserved write */
+ 'x', /* Instruction fetch */
+ 'k', /* Protection key violation */
+ 's' /* Shadow stack access */
+};
+
static inline uintptr_t
pf_faultaddr(void)
{
@@ -69,6 +80,23 @@ pf_faultaddr(void)
}
static void
+pf_code(uint64_t error_code)
+{
+ char tab[8] = {
+ '-', '-', '-',
+ '-', '-', '-',
+ '-', '\0'
+ };
+
+ for (int i = 0; i < 7; ++i) {
+ if (ISSET(error_code, BIT(i))) {
+ tab[i] = pf_flags[i];
+ }
+ }
+ kprintf("code=[%s]\n", tab);
+}
+
+static void
regdump(struct trapframe *tf)
{
uintptr_t cr3, cr2 = pf_faultaddr();
@@ -79,6 +107,10 @@ regdump(struct trapframe *tf)
: "memory"
);
+ if (tf->trapno == TRAP_PAGEFLT) {
+ pf_code(tf->error_code);
+ }
+
kprintf(OMIT_TIMESTAMP
"RAX=%p RCX=%p RDX=%p\n"
"RBX=%p RSI=%p RDI=%p\n"
@@ -101,6 +133,9 @@ trap_user(struct trapframe *tf)
switch (tf->trapno) {
case TRAP_PROTFLT:
case TRAP_PAGEFLT:
+ if (tf->trapno == TRAP_PAGEFLT) {
+ pf_code(tf->error_code);
+ }
sigaddset(&sigset, SIGSEGV);
break;
case TRAP_ARITH_ERR:
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index db3ce4c..7a28038 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,9 +1,16 @@
+//
// Kernel options
-option SPECTRE_IBRS no
-option SERIAL_DEBUG yes
+//
+// XXX: Indirect branch restricted speculation (SPECTRE_IBRS)
+// is disabled by default as it can lead to significant
+// performance degradation.
+//
+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
// Kernel constants
-setval SCHED_NQUEUE 4
+setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ)
// Console attributes
setval CONSOLE_BG 0x000000
diff --git a/sys/arch/amd64/conf/link.ld b/sys/arch/amd64/conf/link.ld
index 9c47a81..a43824f 100644
--- a/sys/arch/amd64/conf/link.ld
+++ b/sys/arch/amd64/conf/link.ld
@@ -29,6 +29,12 @@ SECTIONS
__drivers_init_end = .;
} :rodata
+ .drivers.defer : {
+ __driversd_init_start = .;
+ *(.drivers.defer .drivers.defer)
+ __driversd_init_end = .;
+ } :rodata
+
. += CONSTANT(MAXPAGESIZE);
.data : {
diff --git a/sys/dev/acpi/uacpi.c b/sys/dev/acpi/uacpi.c
index 03d0ecf..612f23b 100644
--- a/sys/dev/acpi/uacpi.c
+++ b/sys/dev/acpi/uacpi.c
@@ -513,9 +513,18 @@ uacpi_u64
uacpi_kernel_get_nanoseconds_since_boot(void)
{
static uacpi_u64 time = 0;
+ static struct timer tmr = {0};
+ tmrr_status_t tmr_error;
+
+ if (time == 0) {
+ tmr_error = req_timer(TIMER_GP, &tmr);
+ if (tmr_error != TMRR_SUCCESS) {
+ time += 1000000;
+ return time;
+ }
+ }
- /* TODO */
- time += 1000000;
+ time = tmr.get_time_nsec();
return time;
}
diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c
index d07acd7..905149e 100644
--- a/sys/dev/ic/ahci.c
+++ b/sys/dev/ic/ahci.c
@@ -1018,4 +1018,4 @@ static struct bdevsw ahci_bdevsw = {
.bsize = ahci_dev_bsize
};
-DRIVER_EXPORT(ahci_init);
+DRIVER_DEFER(ahci_init);
diff --git a/sys/fs/devfs.c b/sys/fs/devfs.c
index 1dfc10d..a2ac7e8 100644
--- a/sys/fs/devfs.c
+++ b/sys/fs/devfs.c
@@ -170,13 +170,6 @@ devfs_getattr(struct vop_getattr_args *args)
static int
devfs_reclaim(struct vnode *vp)
{
- struct devfs_node *dnp;
-
- if ((dnp = vp->data) != NULL) {
- dynfree(dnp->name);
- dynfree(vp->data);
- }
-
vp->data = NULL;
return 0;
}
diff --git a/sys/include/dev/timer.h b/sys/include/dev/timer.h
index e54adcc..fe91323 100644
--- a/sys/include/dev/timer.h
+++ b/sys/include/dev/timer.h
@@ -69,6 +69,7 @@ struct timer {
const char *name; /* e.g "HPET" */
size_t(*calibrate)(void); /* Returns frequency, 0 for unspecified */
size_t(*get_time_usec)(void); /* Time since init (microseconds) */
+ size_t(*get_time_nsec)(void); /* Time since init (nanoseconds) */
size_t(*get_time_sec)(void); /* Time since init (seconds) */
int(*msleep)(size_t ms);
int(*usleep)(size_t us);
diff --git a/sys/include/sys/disklabel.h b/sys/include/sys/disklabel.h
index d1e160b..895c35e 100644
--- a/sys/include/sys/disklabel.h
+++ b/sys/include/sys/disklabel.h
@@ -35,8 +35,10 @@
#define DISK_MAG 0x4F445421UL /* "ODT!" */
/*
+ * Represents a disk table.
+ *
* @magic: Magic number (`DISK_MAG')
- * @sect_size: Sector size
+ * @sect_size: Disk sector size
*/
struct disklabel {
uint32_t magic;
diff --git a/sys/include/sys/driver.h b/sys/include/sys/driver.h
index 05c40fa..6e4a267 100644
--- a/sys/include/sys/driver.h
+++ b/sys/include/sys/driver.h
@@ -31,27 +31,88 @@
#define _SYS_DRIVER_H_
#include <sys/cdefs.h>
+#include <sys/proc.h>
+#include <sys/types.h>
#if defined(_KERNEL)
+/* Variable driver data */
+struct driver_var {
+ uint8_t deferred : 1;
+};
+
struct driver {
int(*init)(void);
+ struct driver_var *data;
};
+extern struct proc g_proc0;
+
+/* Early (high priority) drivers */
extern char __drivers_init_start[];
extern char __drivers_init_end[];
+/* Deferred (low priority) drivers */
+extern char __driversd_init_start[];
+extern char __driversd_init_end[];
+
#define DRIVER_EXPORT(INIT) \
+ static struct driver_var __driver_var = { \
+ .deferred = 0 \
+ }; \
+ \
__attribute__((used, section(".drivers"))) \
static struct driver __driver_desc = { \
.init = INIT, \
+ .data = &__driver_var \
+ }
+
+/*
+ * Some drivers are not required to start up
+ * early for proper system operation and may
+ * be deferred to start at a later time.
+ *
+ * Examples of such (deferrable) drivers include code
+ * that waits for I/O (e.g., disks, network cards,
+ * et cetera). This allows for faster boot times
+ * as only *required* drivers are started before
+ * everything else.
+ *
+ * Drivers that wish to be deferred may export themselves
+ * via the DRIVER_DEFER() macro. The DRIVER_DEFERRED()
+ * macro gives the value of 1 if the current driver
+ * context has yet to be initialized. The driver may
+ * use this to defer requests for I/O.
+ */
+#if !defined(_INSTALL_MEDIA)
+#define DRIVER_DEFER(INIT) \
+ static struct driver_var __driver_var = { \
+ .deferred = 1 \
+ }; \
+ \
+ __attribute__((used, section(".drivers.defer"))) \
+ static struct driver __driver_desc = { \
+ .init = INIT, \
+ .data = &__driver_var \
}
+#define DRIVER_DEFERRED() __driver_var.deferred
+#else
+#define DRIVER_DEFER(INIT) DRIVER_EXPORT(INIT)
+#define DRIVER_DEFERRED() 0
+#endif /* _INSTALL_MEDIA */
+
#define DRIVERS_INIT() \
for (struct driver *__d = (struct driver *)__drivers_init_start; \
(uintptr_t)__d < (uintptr_t)__drivers_init_end; ++__d) \
{ \
__d->init(); \
}
+
+#define DRIVERS_SCHED() \
+ spawn(&g_proc0,__driver_init_td, NULL, 0, NULL)
+
+void __driver_init_td(void);
+
#endif /* _KERNEL */
#endif /* !_SYS_DRIVER_H_ */
diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h
index 1b59de9..0a7d133 100644
--- a/sys/include/sys/proc.h
+++ b/sys/include/sys/proc.h
@@ -83,6 +83,7 @@ struct proc {
#define PROC_ZOMB BIT(2) /* Zombie (dead but not deallocated) */
#define PROC_LEAFQ BIT(3) /* Leaf queue is active */
#define PROC_WAITED BIT(4) /* Being waited on by parent */
+#define PROC_KTD BIT(5) /* Kernel thread */
struct proc *this_td(void);
struct proc *get_child(struct proc *cur, pid_t pid);
@@ -95,7 +96,7 @@ void md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog);
__dead void md_td_kick(struct proc *td);
int fork1(struct proc *cur, int flags, void(*ip)(void), struct proc **newprocp);
-int exit1(struct proc *td);
+int exit1(struct proc *td, int flags);
__dead scret_t sys_exit(struct syscall_args *scargs);
#endif /* _KERNEL */
diff --git a/sys/include/sys/syslog.h b/sys/include/sys/syslog.h
index defb341..b9d34ab 100644
--- a/sys/include/sys/syslog.h
+++ b/sys/include/sys/syslog.h
@@ -31,11 +31,13 @@
#define _SYS_SYSLOG_H_
#include <stdarg.h>
+#include <stdbool.h>
#if defined(_KERNEL)
#define OMIT_TIMESTAMP "\x01"
+void syslog_silence(bool option);
void kprintf(const char *fmt, ...);
void serial_init(void);
void serial_putc(char c);
diff --git a/sys/include/sys/systm.h b/sys/include/sys/systm.h
index 42e1723..5d06257 100644
--- a/sys/include/sys/systm.h
+++ b/sys/include/sys/systm.h
@@ -55,5 +55,7 @@ __sigraise(int signo)
dispatch_signals(td);
}
+int hyra_install(void);
+
#endif /* _KERNEL */
#endif /* !_SYS_SYSTM_H_ */
diff --git a/sys/kern/driver_subr.c b/sys/kern/driver_subr.c
new file mode 100644
index 0000000..29eac71
--- /dev/null
+++ b/sys/kern/driver_subr.c
@@ -0,0 +1,64 @@
+/*
+ * 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/driver.h>
+#include <sys/proc.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
+#include <sys/panic.h>
+#include <dev/timer.h>
+#include <machine/sync.h>
+
+/*
+ * Initialize early drivers
+ *
+ * XXX: This should *NOT* be called directly,
+ * use DRIVERS_SCHED() instead.
+ */
+void
+__driver_init_td(void)
+{
+ const struct driver *dp;
+ struct driver_var *var;
+ struct proc *td;
+ uintptr_t start, end;
+
+ td = this_td();
+ start = (uintptr_t)__driversd_init_start;
+ end = (uintptr_t)__driversd_init_end;
+
+ for (dp = (void *)start; (uintptr_t)dp < end; ++dp) {
+ var = dp->data;
+ dp->init();
+ var->deferred = 0;
+ }
+
+ exit1(td, 0);
+ __builtin_unreachable();
+}
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index ff965bd..123a91d 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -35,6 +35,7 @@
#include <sys/exec.h>
#include <sys/driver.h>
#include <sys/panic.h>
+#include <sys/systm.h>
#include <dev/acpi/uacpi.h>
#include <dev/cons/cons.h>
#include <dev/acpi/acpi.h>
@@ -43,7 +44,7 @@
#include <vm/vm.h>
#include <string.h>
-static struct proc proc0;
+struct proc g_proc0;
static void
copyright(void)
@@ -52,6 +53,18 @@ copyright(void)
"Copyright (c) 2023-2025 Ian Marco Moffett and the OSMORA team\n");
}
+#if defined(_INSTALL_MEDIA)
+static void
+begin_install(void)
+{
+ struct cpu_info *ci;
+
+ ci = this_cpu();
+ ci->curtd = &g_proc0;
+ hyra_install();
+}
+#endif
+
static void
start_init(void)
{
@@ -84,11 +97,6 @@ main(void)
kprintf("Starting Hyra/%s v%s: %s\n", HYRA_ARCH, HYRA_VERSION,
HYRA_BUILDDATE);
-#if _INSTALL_MEDIA
- kprintf("Hyra install media detected\n");
- kprintf("Reform Industry!\n");
-#endif /* _INSTALL_MEDIA */
-
/* Start the ACPI subsystem */
acpi_init();
@@ -107,17 +115,29 @@ main(void)
md_intoff();
sched_init();
- memset(&proc0, 0, sizeof(proc0));
+ memset(&g_proc0, 0, sizeof(g_proc0));
/* Startup pid 1 */
- spawn(&proc0, start_init, NULL, 0, NULL);
-
- /* Load all drivers */
+ spawn(&g_proc0, start_init, NULL, 0, NULL);
md_inton();
+
+ /* Load all early drivers */
DRIVERS_INIT();
- /* Bootstrap APs and here we go! */
+#if defined(_INSTALL_MEDIA)
+ kprintf("Hyra install media detected\n");
+ kprintf("Reform Industry!\n");
+ begin_install();
+#endif
+
+ syslog_silence(true);
+
+ /*
+ * Bootstrap APs, schedule all other drivers
+ * and here we go!
+ */
mp_bootstrap_aps(&g_bsp_ci);
+ DRIVERS_SCHED();
sched_enter();
__builtin_unreachable();
}
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index d122e89..dd14267 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -205,11 +205,20 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
goto done;
}
+ /* End of file? */
+ if (n == 0) {
+ retval = 0;
+ goto done;
+ }
+
if (copyout(kbuf, buf, count) < 0) {
retval = -EFAULT;
goto done;
}
}
+
+ /* Increment the offset per read */
+ filedes->offset += n;
retval = count;
done:
if (kbuf != NULL) {
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index d6c9168..242b221 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -35,6 +35,7 @@
#include <vm/vm.h>
#include <vm/map.h>
#include <machine/pcb.h>
+#include <machine/cpu.h>
#define pr_trace(fmt, ...) kprintf("exit: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
@@ -49,6 +50,10 @@ unload_td(struct proc *td)
size_t len;
sched_detach(td);
+ if (ISSET(td->flags, PROC_KTD)) {
+ return;
+ }
+
execp = &td->exec;
auxvalp = &execp->auxval;
pcbp = &td->pcb;
@@ -79,13 +84,15 @@ unload_td(struct proc *td)
* @td: Thread to exit
*/
int
-exit1(struct proc *td)
+exit1(struct proc *td, int flags)
{
struct pcb *pcbp;
struct proc *curtd, *procp;
+ struct cpu_info *ci;
uintptr_t stack;
pid_t target_pid, curpid;
+ ci = this_cpu();
target_pid = td->pid;
curtd = this_td();
pcbp = &td->pcb;
@@ -97,7 +104,7 @@ exit1(struct proc *td)
/* If we have any children, kill them too */
if (td->nleaves > 0) {
TAILQ_FOREACH(procp, &td->leafq, leaf_link) {
- exit1(procp);
+ exit1(procp, flags);
}
}
@@ -130,8 +137,10 @@ exit1(struct proc *td)
* If we are the thread exiting, reenter the scheduler
* and do not return.
*/
- if (target_pid == curpid)
+ if (target_pid == curpid) {
+ ci->curtd = NULL;
sched_enter();
+ }
return 0;
}
@@ -145,6 +154,6 @@ sys_exit(struct syscall_args *scargs)
struct proc *td = this_td();
td->exit_status = scargs->arg0;
- exit1(td);
+ exit1(td, 0);
__builtin_unreachable();
}
diff --git a/sys/kern/kern_instl.c b/sys/kern/kern_instl.c
new file mode 100644
index 0000000..4f4d0f1
--- /dev/null
+++ b/sys/kern/kern_instl.c
@@ -0,0 +1,323 @@
+/*
+ * 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/systm.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <sys/syslog.h>
+#include <sys/sio.h>
+#include <sys/vnode.h>
+#include <sys/disklabel.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/sched.h>
+#include <sys/reboot.h>
+#include <machine/cdefs.h>
+#include <vm/dynalloc.h>
+#include <vm/vm.h>
+#include <dev/timer.h>
+#include <assert.h>
+#include <string.h>
+
+#define DEFAULT_TIMEOUT 3
+#define YIELD_TIMEOUT 200000 /* In usec */
+#define BLOCK_SIZE 512
+#define BLOCK_THRESHOLD (BLOCK_SIZE * 1024)
+
+struct progress_bar {
+ bool dec;
+ uint8_t progress;
+};
+
+__used static struct timer tmr;
+__used static struct timer sched_timer;
+__used static struct sio_txn hdd_sio;
+
+#if defined(_INSTALL_MEDIA)
+__dead static void
+installer_quit(uint32_t seconds)
+{
+ kprintf("restarting in %d seconds...\n", seconds);
+ tmr.msleep(seconds * 1000);
+ cpu_reboot(REBOOT_RESET);
+ __builtin_unreachable();
+}
+
+static inline void
+installer_yield(void)
+{
+ md_inton();
+ sched_timer.oneshot_us(YIELD_TIMEOUT);
+ md_hlt();
+ md_intoff();
+}
+
+/*
+ * Throttle CPU usage by giving it small
+ * breaks based on the amount of data already
+ * read. The installer needs to perform very
+ * large block I/O operations and we want to
+ * avoid significant temperature spikes that
+ * would be kind of scary :(
+ *
+ * @n: Number of bytes read
+ */
+static void
+installer_throttle(size_t n)
+{
+ if ((n % BLOCK_THRESHOLD) == 0) {
+ installer_yield();
+ }
+}
+
+/*
+ * Create a progress bar animation for long
+ * operations.
+ *
+ * @bp: Pointer to a progress bar structure.
+ * @n: Number of blocks operated on.
+ * @max: Max blocks per bar update.
+ */
+static void
+progress_update(struct progress_bar *bp, size_t n, size_t max)
+{
+ const char CHR = '.';
+
+ /*
+ * We only want to update the progress bar
+ * per `max' blocks written.
+ */
+ if ((n > 0) && (n % max) != 0) {
+ return;
+ }
+
+ /* Add more '.' chars */
+ if (bp->progress < 8 && !bp->dec) {
+ kprintf(OMIT_TIMESTAMP "%c\f", CHR);
+ } else if (bp->progress >= 8) {
+ bp->dec = true;
+ }
+
+ /* Remove '.' chars */
+ if (bp->dec && bp->progress > 0) {
+ kprintf(OMIT_TIMESTAMP "\b\f");
+ } else if (bp->progress == 0) {
+ bp->dec = false;
+ }
+
+ if (!bp->dec) {
+ ++bp->progress;
+ } else {
+ --bp->progress;
+ }
+}
+
+static void
+installer_wipe(struct filedesc *hdd, uint32_t count)
+{
+ struct sio_txn sio;
+ struct progress_bar bar = {0, 0};
+ size_t write_len, total_blocks;
+ size_t write_blocks;
+ char buf[BLOCK_SIZE * 2];
+
+ write_len = sizeof(buf);
+ memset(buf, 0, write_len);
+ write_blocks = write_len / BLOCK_SIZE;
+
+ total_blocks = ALIGN_UP(count, BLOCK_SIZE);
+ total_blocks /= BLOCK_SIZE;
+
+ if (__unlikely(total_blocks == 0)) {
+ kprintf("bad block size for /dev/sd1\n");
+ installer_quit(DEFAULT_TIMEOUT);
+ }
+
+ sio.buf = buf;
+ sio.offset = 0;
+ sio.len = write_len;
+
+ /* Zero that shit */
+ kprintf("zeroing %d blocks...\n", total_blocks);
+ for (int i = 0; i < total_blocks; i += write_blocks) {
+ vfs_vop_write(hdd->vp, &sio);
+ installer_throttle(sio.offset);
+ sio.offset += write_len;
+ progress_update(&bar, i, 1000);
+ }
+
+ /* Cool off then continue */
+ installer_yield();
+ hdd_sio.offset = 0;
+ kprintf(OMIT_TIMESTAMP "OK\n");
+ tmr.msleep(1000);
+}
+
+/*
+ * Write data to the drive.
+ *
+ * @hdd: HDD file descriptor
+ * @file: Optional source file descriptor
+ * @p: Data pointer
+ * @len: Length of data.
+ */
+static void
+installer_write(struct filedesc *hdd, struct filedesc *file, void *p, size_t len)
+{
+ size_t nblocks;
+ struct sio_txn sio;
+ struct progress_bar bar = {0, 0};
+ char buf[BLOCK_SIZE];
+
+ len = ALIGN_UP(len, BLOCK_SIZE);
+ nblocks = len / BLOCK_SIZE;
+
+ hdd_sio.len = BLOCK_SIZE;
+ hdd_sio.buf = (len < BLOCK_SIZE) ? buf : p;
+
+ sio.len = BLOCK_SIZE;
+ sio.offset = 0;
+ sio.buf = (len < BLOCK_SIZE) ? buf : p;
+
+ if (len < BLOCK_SIZE) {
+ memcpy(buf, p, len);
+ }
+
+ kprintf("writing %d block(s)...\n", nblocks);
+ for (size_t i = 0; i < nblocks; ++i) {
+ if (file != NULL) {
+ vfs_vop_read(file->vp, &sio);
+ }
+ vfs_vop_write(hdd->vp, &hdd_sio);
+ installer_throttle(hdd_sio.offset);
+
+ sio.offset += BLOCK_SIZE;
+ hdd_sio.offset += BLOCK_SIZE;
+ progress_update(&bar, i, 400);
+ }
+
+ kprintf(OMIT_TIMESTAMP "OK\n");
+}
+
+#endif
+
+int
+hyra_install(void)
+{
+#if defined(_INSTALL_MEDIA)
+ int fd, hdd_fd;
+ char buf[BLOCK_SIZE];
+ struct filedesc *fildes, *hdd_fildes;
+ struct disklabel label;
+ struct vop_getattr_args getattr_args;
+ struct vattr iso_attr;
+ size_t iso_size;
+ size_t nzeros = 0; /* Zero byte count */
+ tmrr_status_t tmr_error;
+
+ /* Needed for msleep() */
+ tmr_error = req_timer(TIMER_GP, &tmr);
+ if (__unlikely(tmr_error != TMRR_SUCCESS)) {
+ kprintf("could not fetch TIMER_GP\n");
+ installer_quit(DEFAULT_TIMEOUT);
+ }
+
+ /*
+ * Grab the scheduler timer since we can
+ * reasonably assume it has oneshot capability.
+ */
+ tmr_error = req_timer(TIMER_SCHED, &sched_timer);
+ if (__unlikely(tmr_error != TMRR_SUCCESS)) {
+ kprintf("could not fetch TIMER_SCHED\n");
+ installer_quit(DEFAULT_TIMEOUT);
+ }
+
+ kprintf("::::::::::::::::::::::::::::\n");
+ kprintf("::::: Hyra Installer ::::::\n");
+ kprintf("::::::::::::::::::::::::::::\n");
+ kprintf("!! DRIVE WILL BE WIPED !!\n");
+ tmr.msleep(5000);
+
+
+ /*
+ * See if the target drive exists
+ *
+ * XXX: As of now, we only support SATA drives
+ * as a target for the installer.
+ */
+ hdd_fd = fd_open("/dev/sd1", O_RDWR);
+ if (hdd_fd < 0) {
+ kprintf("could not open /dev/sd1\n");
+ installer_quit(DEFAULT_TIMEOUT);
+ }
+
+ kprintf("installing to /dev/sd1...\n");
+
+ fd = fd_open("/boot/Hyra.iso", O_RDONLY);
+ if (fd < 0) {
+ kprintf("could not open /boot/Hyra.iso (status=%d)\n", fd);
+ installer_quit(DEFAULT_TIMEOUT);
+ }
+
+ /* Get attributes */
+ fildes = fd_get(fd);
+ getattr_args.vp = fildes->vp;
+ getattr_args.res = &iso_attr;
+ vfs_vop_getattr(fildes->vp, &getattr_args);
+
+ /* Get the ISO size */
+ iso_size = ALIGN_UP(iso_attr.size, BLOCK_SIZE);
+
+ /*
+ * First, wipe part of the drive of any data.
+ * This is done by simply filling it with
+ * zeros.
+ */
+ hdd_fildes = fd_get(hdd_fd);
+ nzeros = iso_size + sizeof(struct disklabel);
+ nzeros += BLOCK_SIZE;
+ installer_wipe(hdd_fildes, nzeros);
+
+ /*
+ * Now since the drive is zerored, we can prep
+ * our data buffers to write the actual install.
+ */
+ label.magic = DISK_MAG;
+ label.sect_size = BLOCK_SIZE;
+ installer_write(hdd_fildes, fildes, buf, iso_size);
+ installer_write(hdd_fildes, NULL, &label, sizeof(label));
+
+ kprintf("Installation complete!\n");
+ kprintf("Please remove installation media\n");
+ installer_quit(5);
+#endif /* _INSTALL_MEDIA */
+ return 0;
+}
diff --git a/sys/kern/kern_spawn.c b/sys/kern/kern_spawn.c
index cb898dc..032ba24 100644
--- a/sys/kern/kern_spawn.c
+++ b/sys/kern/kern_spawn.c
@@ -84,7 +84,7 @@ spawn_thunk(void)
if (execve(cur, &execve_args) != 0) {
pr_error("execve failed, aborting\n");
- exit1(this_td());
+ exit1(this_td(), 0);
}
__builtin_unreachable();
}
diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c
index 1a5fb0e..fa1be65 100644
--- a/sys/kern/kern_stub.c
+++ b/sys/kern/kern_stub.c
@@ -41,7 +41,7 @@ sigfpe_default(int signo)
td = this_td();
kprintf("Floating point exception (pid=%d)\n", td->pid);
- exit1(td);
+ exit1(td, 0);
}
void
@@ -51,7 +51,7 @@ sigkill_default(int signo)
td = this_td();
kprintf("Terminated (pid=%d)\n", td->pid);
- exit1(td);
+ exit1(td, 0);
}
void
@@ -61,7 +61,7 @@ sigsegv_default(int signo)
td = this_td();
kprintf("Segmentation fault (pid=%d)\n", td->pid);
- exit1(td);
+ exit1(td, 0);
}
int
diff --git a/sys/kern/kern_syslog.c b/sys/kern/kern_syslog.c
index 656362e..eb4fa8d 100644
--- a/sys/kern/kern_syslog.c
+++ b/sys/kern/kern_syslog.c
@@ -28,9 +28,14 @@
*/
#include <sys/syslog.h>
+#include <sys/cdefs.h>
+#include <sys/sio.h>
#include <sys/spinlock.h>
+#include <sys/device.h>
+#include <sys/errno.h>
#include <dev/cons/cons.h>
#include <dev/timer.h>
+#include <fs/devfs.h>
#include <stdarg.h>
#include <string.h>
@@ -40,8 +45,78 @@
#define SERIAL_DEBUG 0
#endif
+#if defined(__USER_KMSG)
+#define USER_KMSG __USER_KMSG
+#else
+#define USER_KMSG 0
+#endif
+
+#define KBUF_SIZE (1 << 16)
+
+/* Sanity check */
+__static_assert(KBUF_SIZE <= (1 << 16), "KBUF_SIZE too high!");
+
/* Global logger lock */
static struct spinlock lock = {0};
+static struct spinlock kmsg_lock = {0};
+static bool no_cons_log = false;
+
+/* Kernel message buffer */
+static char kmsg[KBUF_SIZE];
+static size_t kmsg_i = 0;
+static struct cdevsw kmsg_cdevw;
+
+static void
+kmsg_append(const char *s, size_t len)
+{
+ spinlock_acquire(&kmsg_lock);
+ if ((kmsg_i + len) >= KBUF_SIZE) {
+ kmsg_i = 0;
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ kmsg[kmsg_i + i] = s[i];
+ }
+ kmsg_i += len;
+ spinlock_release(&kmsg_lock);
+}
+
+/*
+ * Character device function.
+ */
+static int
+kmsg_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ size_t len, offset, j;
+ size_t bytes_read = 0;
+ char *p = sio->buf;
+
+ spinlock_acquire(&kmsg_lock);
+ len = sio->len;
+ offset = sio->offset;
+
+ if (len == 0) {
+ spinlock_release(&kmsg_lock);
+ return -EINVAL;
+ }
+ if (offset >= kmsg_i) {
+ spinlock_release(&kmsg_lock);
+ return 0;
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ j = offset + i;
+ if (j > kmsg_i) {
+ break;
+ }
+
+ p[i] = kmsg[j];
+ ++bytes_read;
+ }
+
+ spinlock_release(&kmsg_lock);
+ return bytes_read;
+}
static void
syslog_write(const char *s, size_t len)
@@ -58,6 +133,17 @@ syslog_write(const char *s, size_t len)
}
}
+ kmsg_append(s, len);
+
+ /*
+ * If the USER_KMSG option is disabled in kconf,
+ * do not log to the console if everything else
+ * has already started.
+ */
+ if (!USER_KMSG && no_cons_log) {
+ return;
+ }
+
cons_putstr(&g_root_scr, s, len);
}
@@ -116,3 +202,37 @@ kprintf(const char *fmt, ...)
va_end(ap);
spinlock_release(&lock);
}
+
+/*
+ * Silence kernel messages in if the system
+ * is already operating in a user context.
+ *
+ * XXX: This is ignored if the kconf USER_KMSG
+ * option is set to "no". A kmsg device file
+ * is also created on the first call.
+ */
+void
+syslog_silence(bool option)
+{
+ static bool once = false;
+ static char devname[] = "kmsg";
+ devmajor_t major;
+ dev_t dev;
+
+ if (!once) {
+ once = true;
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+
+ dev_register(major, dev, &kmsg_cdevw);
+ devfs_create_entry(devname, major, dev, 0444);
+
+ }
+
+ no_cons_log = option;
+}
+
+static struct cdevsw kmsg_cdevw = {
+ .read = kmsg_read,
+ .write = nowrite
+};
diff --git a/usr.bin/osh/osh.c b/usr.bin/osh/osh.c
index c45d9c8..9b4e9ab 100644
--- a/usr.bin/osh/osh.c
+++ b/usr.bin/osh/osh.c
@@ -49,6 +49,7 @@
"echo - Print the arguments to the console\n" \
"reboot - Reboot the machine\n" \
"shutdown - Power off the machine\n" \
+ "kmsg - Print kernel message buffer\n" \
"exit - Exit the shell\n"
#define PROMPT "[root::osmora]~ "
@@ -87,6 +88,29 @@ cmd_shutdown(int fd, int argc, char *argv[])
}
void
+cmd_kmsg(int fd, int argc, char *argv[])
+{
+ int mfd;
+ ssize_t retval;
+ char linebuf[256];
+
+ if ((mfd = open("/dev/kmsg", O_RDONLY)) < 0) {
+ return;
+ }
+
+ for (;;) {
+ retval = read(mfd, linebuf, sizeof(linebuf) - 1);
+ if (retval <= 0) {
+ break;
+ }
+ linebuf[retval] = '\0';
+ prcons(fd, linebuf);
+ }
+
+ close(mfd);
+}
+
+void
cmd_echo(int fd, int argc, char *argv[])
{
for (i = 1; i < argc; i++) {
@@ -171,6 +195,7 @@ struct command cmds[] = {
{"exit", cmd_exit},
{"reboot", cmd_reboot},
{"shutdown", cmd_shutdown},
+ {"kmsg", cmd_kmsg},
{NULL, NULL}
};