summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/amd64/amd64/machdep.c1
-rw-r--r--sys/dev/cons/cons.c5
-rw-r--r--sys/include/arch/amd64/cdefs.h4
-rw-r--r--sys/include/arch/amd64/cpu.h16
-rw-r--r--sys/include/lib/string.h1
-rw-r--r--sys/include/sys/limits.h2
-rw-r--r--sys/include/sys/proc.h3
-rw-r--r--sys/include/sys/queue.h5
-rw-r--r--sys/include/sys/sched.h2
-rw-r--r--sys/include/sys/signal.h2
-rw-r--r--sys/include/sys/termios.h29
-rw-r--r--sys/include/sys/vsr.h165
-rw-r--r--sys/kern/kern_accnt.c20
-rw-r--r--sys/kern/kern_sig.c6
-rw-r--r--sys/kern/kern_stub.c12
-rw-r--r--sys/kern/kern_vsr.c344
-rw-r--r--sys/lib/string/strdup.c52
17 files changed, 663 insertions, 6 deletions
diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c
index efd1af8..19dcf44 100644
--- a/sys/arch/amd64/amd64/machdep.c
+++ b/sys/arch/amd64/amd64/machdep.c
@@ -418,6 +418,7 @@ cpu_startup(struct cpu_info *ci)
setup_vectors(ci);
try_mitigate_spectre();
+ ci->online = 1;
cpu_get_info(ci);
cpu_enable_smep();
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c
index 88cbcbe..d9d727a 100644
--- a/sys/dev/cons/cons.c
+++ b/sys/dev/cons/cons.c
@@ -356,6 +356,7 @@ dev_write(dev_t dev, struct sio_txn *sio, int flags)
static int
dev_read(dev_t dev, struct sio_txn *sio, int flags)
{
+ struct cons_screen *scr = &g_root_scr;
struct cons_input input;
uint8_t *p;
int retval;
@@ -369,7 +370,7 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags)
return -EFAULT;
}
- retval = cons_ibuf_pop(&g_root_scr, &input);
+ retval = cons_ibuf_pop(scr, &input);
if (retval < 0) {
return -EAGAIN;
}
@@ -386,7 +387,7 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags)
n -= 2;
/* Try to get the next byte */
- retval = cons_ibuf_pop(&g_root_scr, &input);
+ retval = cons_ibuf_pop(scr, &input);
if (retval < 0) {
break;
}
diff --git a/sys/include/arch/amd64/cdefs.h b/sys/include/arch/amd64/cdefs.h
index 0a20324..d038a15 100644
--- a/sys/include/arch/amd64/cdefs.h
+++ b/sys/include/arch/amd64/cdefs.h
@@ -31,7 +31,7 @@
#define _AMD64_CDEFS_H_
#include <sys/cdefs.h>
-#include <machine/sync.h>
+#include <machine/cpu.h>
/*
* Please use CLI wisely, it is a good idea to use
@@ -41,7 +41,7 @@
#define md_pause() __ASMV("rep; nop") /* (F3 90) PAUSE */
#define md_intoff() __ASMV("cli") /* Clear interrupts */
#define md_inton() __ASMV("sti") /* Enable interrupts */
-#define md_hlt() __ASMV("hlt") /* Halt the processor */
+#define md_hlt() cpu_halt() /* Halt the processor */
/*
* AMD64 specific defines
diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h
index 046b621..116661b 100644
--- a/sys/include/arch/amd64/cpu.h
+++ b/sys/include/arch/amd64/cpu.h
@@ -36,6 +36,7 @@
#include <sys/sched.h>
#include <sys/spinlock.h>
#include <machine/tss.h>
+#include <machine/cdefs.h>
#define CPU_IRQ(IRQ_N) (BIT((IRQ_N)) & 0xFF)
@@ -51,6 +52,7 @@ struct cpu_info {
uint8_t family : 4; /* CPU family ID */
uint8_t has_x2apic : 1;
uint8_t tlb_shootdown : 1;
+ uint8_t online : 1; /* CPU online */
uint8_t ipl;
size_t lapic_tmr_freq;
uint8_t irq_mask;
@@ -80,4 +82,18 @@ void mp_bootstrap_aps(struct cpu_info *ci);
extern struct cpu_info g_bsp_ci;
+__always_inline static inline void
+cpu_halt(void)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci != NULL)
+ ci->online = 0;
+
+ __ASMV("hlt");
+
+ if (ci != NULL)
+ ci->online = 1;
+}
+
#endif /* !_MACHINE_CPU_H_ */
diff --git a/sys/include/lib/string.h b/sys/include/lib/string.h
index c09e6f4..3255ae5 100644
--- a/sys/include/lib/string.h
+++ b/sys/include/lib/string.h
@@ -35,6 +35,7 @@
size_t strlen(const char *s);
char *itoa(int64_t value, char *buf, int base);
+char *strdup(const char *s);
int vsnprintf(char *s, size_t size, const char *fmt, va_list ap);
int snprintf(char *s, size_t size, const char *fmt, ...);
diff --git a/sys/include/sys/limits.h b/sys/include/sys/limits.h
index f6aed9d..963d113 100644
--- a/sys/include/sys/limits.h
+++ b/sys/include/sys/limits.h
@@ -36,4 +36,6 @@
#define ARG_MAX 4096
#define CHAR_BIT 8
#define CPU_MAX 256
+#define VSR_MAX_DOMAIN 16
+#define VSR_MAX_CAPSULE 16
#endif /* !_SYS_LIMITS_H_ */
diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h
index 9cc9238..89fe638 100644
--- a/sys/include/sys/proc.h
+++ b/sys/include/sys/proc.h
@@ -39,6 +39,8 @@
#include <sys/syscall.h>
#include <sys/exec.h>
#include <sys/ucred.h>
+#include <sys/limits.h>
+#include <sys/vsr.h>
#include <sys/filedesc.h>
#include <sys/signal.h>
#include <sys/vnode.h>
@@ -88,6 +90,7 @@ struct proc {
struct ucred cred;
struct ksiginfo *ksig_list[PROC_SIGMAX];
struct filedesc *fds[PROC_MAX_FILEDES];
+ struct vsr_domain *vsr_tab[VSR_MAX_DOMAIN];
struct mmap_lgdr *mlgdr;
struct vcache *vcache;
struct spinlock vcache_lock;
diff --git a/sys/include/sys/queue.h b/sys/include/sys/queue.h
index e5d607d..92c1ff2 100644
--- a/sys/include/sys/queue.h
+++ b/sys/include/sys/queue.h
@@ -27,7 +27,12 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#if !defined(_KERNEL)
+#include <stdint.h>
+#include <stddef.h>
+#else
#include <sys/types.h>
+#endif /* !_KERNEL */
#ifndef _QUEUE_H_
#define _QUEUE_H_
diff --git a/sys/include/sys/sched.h b/sys/include/sys/sched.h
index abc2718..7a859b2 100644
--- a/sys/include/sys/sched.h
+++ b/sys/include/sys/sched.h
@@ -49,11 +49,13 @@ struct sched_cpu {
*
* @nproc: Number processes running
* @ncpu: Number of CPU cores
+ * @nhlt: Number of halted CPU cores
* @quantum_usec: Scheduler quantum (microseconds)
*/
struct sched_stat {
size_t nproc;
uint16_t ncpu;
+ uint16_t nhlt;
uint32_t quantum_usec;
struct sched_cpu cpus[CPU_MAX];
};
diff --git a/sys/include/sys/signal.h b/sys/include/sys/signal.h
index 9fc767d..eaf2d41 100644
--- a/sys/include/sys/signal.h
+++ b/sys/include/sys/signal.h
@@ -37,6 +37,7 @@
#define SIGFPE 8 /* Floating point exception */
#define SIGKILL 9 /* Kill */
#define SIGSEGV 11 /* Segmentation violation */
+#define SIGTERM 15 /* Terminate gracefully */
typedef uint32_t sigset_t;
@@ -80,5 +81,6 @@ int sigismember(const sigset_t *set, int signo);
void sigfpe_default(int signo);
void sigkill_default(int signo);
void sigsegv_default(int signo);
+void sigterm_default(int signo);
#endif /* _KERNEL */
#endif /* !_SYS_SIGNAL_H_ */
diff --git a/sys/include/sys/termios.h b/sys/include/sys/termios.h
index 27339f1..a3ba794 100644
--- a/sys/include/sys/termios.h
+++ b/sys/include/sys/termios.h
@@ -33,8 +33,33 @@
/*
* c_iflag: Input flags
*/
-#define ISTRIP 0x00000000
-#define ICRNL 0x00000001
+#define ISTRIP 0x00000001 /* Strip char */
+#define ICRNL 0x00000002 /* Map CR to NL */
+#define BRKINT 0x00000004 /* Signal interrupt on break */
+#define IGNBRK 0x00000008 /* Ignore break condition */
+#define IGNCR 0x00000010 /* Ignore CR */
+#define IGNPAR 0x00000020 /* Ignore chars with parity errors */
+#define INCLR 0x00000040 /* Map NL to CR */
+#define INPCK 0x00000080 /* Enable input parity check */
+#define IXANY 0x00000100 /* Enable any char to restart output */
+#define IXOFF 0x00000200 /* Enable start/stop control */
+#define PARMRK 0x00000400 /* Mark parity errors */
+
+/*
+ * c_oflag: Output flags
+ */
+#define OPOST 0x00000001 /* Post-process output */
+#define ONLCR 0x00000002 /* Map NL to CR-NL on output */
+#define OCRNL 0x00000004 /* Map CR to NL on output */
+#define ONOCR 0x00000008 /* Map CR to output at col 0 */
+#define ONLRET 0x00000010 /* NL performs CR function */
+#define OFILL 0x00000020 /* Use fill chars for delay */
+#define NLDLY 0x00000040 /* Select newline type */
+#define CRDLY 0x00000080 /* Select carriage-return delays */
+#define TABDLY 0x00000100 /* Select horizontal-tab delays */
+#define BSDLY 0x00000200 /* Select backspace delays */
+#define VTDLY 0x00000400 /* Select veritcal tab delays */
+#define FFDLY 0x00000800 /* Select form-feed delays */
#define NCCS 20
diff --git a/sys/include/sys/vsr.h b/sys/include/sys/vsr.h
new file mode 100644
index 0000000..e63cce1
--- /dev/null
+++ b/sys/include/sys/vsr.h
@@ -0,0 +1,165 @@
+/*
+ * 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_VSR_H_
+#define _SYS_VSR_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/limits.h>
+#if defined(_KERNEL)
+#include <sys/mutex.h>
+#endif /* _KERNEL */
+
+struct proc;
+
+#define VSR_FILE 0x00000000 /* Represented by file */
+
+/*
+ * Defines the access semantics of whether
+ * r/w operations should be passed down to the
+ * global state or soley affecting a per-process
+ * shallow copy.
+ */
+typedef uint32_t vsr_mode_t;
+
+/*
+ * The Virtual System Resource namespace consists of
+ * domains containing named "capsules". The domain is
+ * simply a table indexed by a type value e.g. VSR_FILE
+ * and a capsule is simply a structure containing global data
+ * as well as a shallow copy which is controlled locally by the
+ * process. The capsule also contains various access semantics
+ * that help the VSR subsystem determine whether the access should
+ * be passed down globally or virtualized locally within the process.
+ */
+typedef uint8_t vsr_domain_t;
+
+/*
+ * VSR mode bits
+ */
+#define VSR_GLOB_WRITE BIT(0) /* Writes are global */
+#define VSR_GLOB_READ BIT(1) /* Reads are global */
+#define VSR_GLOB_CRED BIT(2) /* Global for specific creds */
+
+#if defined(_KERNEL)
+
+struct vsr_capsule;
+
+/*
+ * VSR capsule operations
+ *
+ * @reclaim: Cleanup resources
+ */
+struct capsule_ops {
+ int(*reclaim)(struct vsr_capsule *cap, int flags);
+};
+
+/*
+ * Virtual system resource access
+ * semantics.
+ *
+ * @glob: Global data
+ * @shallow: Local per process copy
+ * @mode: VSR mode (see VSR_GLOB_*)
+ * @cred: Creds (used if VSR_GLOBAL_CRED set)
+ */
+struct vsr_access {
+ void *glob;
+ void *shallow;
+ vsr_mode_t mode;
+ struct ucred cred;
+};
+
+/*
+ * A virtual system resource capsule containing
+ * resource owner specific data and hashmap
+ * buckets.
+ *
+ * @name: Capsule name (e.g., "consfeat"), must be freed
+ * @data: Owner specific data
+ * @shadow: Local shadow copy (per-process)
+ * @buckets: Hashmap buckets
+ * @link: Bucket link
+ * @ops: Capsule operations
+ * @lock: Mutex lock protecting fields
+ */
+struct vsr_capsule {
+ char *name;
+ void *data;
+ void *shadow;
+ TAILQ_HEAD(, vsr_capsule) buckets;
+ TAILQ_ENTRY(vsr_capsule) link;
+ struct capsule_ops ops;
+ struct mutex lock;
+};
+
+/*
+ * Virtual system resource table containg
+ * VSRs for various types.
+ *
+ * Each VSR table belongs to a VSR domain
+ * (e.g., VSR_FILE).
+ *
+ * @ncaps: Number of capsules
+ * @is_init: Set if hashmap is set up
+ * @capsules: VSR capsule hashmap
+ */
+struct vsr_table {
+ struct vsr_capsule *capsules[VSR_MAX_CAPSULE];
+};
+
+/*
+ * Virtual system resource domain (VSR).
+ *
+ * A VSR is represented by a specific VSR type
+ * (see VSR_*). Each VSR has a table of VSR capsules
+ * looked up by a VSR capsule name.
+ *
+ * One per process.
+ *
+ * @type: VSR type
+ * @table: VSR table
+ */
+struct vsr_domain {
+ int type;
+ struct vsr_table table;
+};
+
+void vsr_init_domains(struct proc *td);
+void vsr_destroy_domains(struct proc *td);
+
+struct vsr_domain *vsr_new_domain(struct proc *td, vsr_domain_t type);
+struct vsr_capsule *vsr_new_capsule(struct proc *td, vsr_domain_t type, const char *name);
+struct vsr_capsule *vsr_lookup_capsule(struct proc *td, vsr_domain_t type, const char *name);
+
+#endif /* _KERNEL */
+#endif /* !_SYS_VSR_H_ */
diff --git a/sys/kern/kern_accnt.c b/sys/kern/kern_accnt.c
index cd15863..51905e7 100644
--- a/sys/kern/kern_accnt.c
+++ b/sys/kern/kern_accnt.c
@@ -58,6 +58,25 @@ ctl_stat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
return sio->len;
}
+static uint16_t
+cpu_nhlt(void)
+{
+ uint16_t nhlt = 0;
+ struct cpu_info *ci;
+
+ for (size_t i = 0; i < CPU_MAX; ++i) {
+ ci = cpu_get(i);
+ if (ci == NULL) {
+ continue;
+ }
+ if (!ci->online) {
+ ++nhlt;
+ }
+ }
+
+ return nhlt;
+}
+
/*
* Get scheduler accounting information
*
@@ -71,6 +90,7 @@ sched_stat(struct sched_stat *statp)
statp->nproc = atomic_load_64(&g_nthreads);
statp->ncpu = cpu_count();
statp->quantum_usec = DEFAULT_TIMESLICE_USEC;
+ statp->nhlt = cpu_nhlt();
/*
* Setup the per-cpu info/statistics
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 58bd52d..044de7b 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -58,6 +58,12 @@ static struct sigaction sa_tab[] = {
.sa_flags = 0,
.sa_sigaction = NULL
},
+ [SIGTERM] = {
+ .sa_handler = sigterm_default,
+ .sa_mask = 0,
+ .sa_flags = 0,
+ .sa_sigaction = NULL
+ }
};
/*
diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c
index 17c6e54..a9a56ac 100644
--- a/sys/kern/kern_stub.c
+++ b/sys/kern/kern_stub.c
@@ -70,6 +70,18 @@ sigsegv_default(int signo)
exit1(td, 0);
}
+void
+sigterm_default(int signo)
+{
+ static struct proc *td;
+
+ td = this_td();
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Terminated (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ exit1(td, 0);
+}
+
int
dev_noread(void)
{
diff --git a/sys/kern/kern_vsr.c b/sys/kern/kern_vsr.c
new file mode 100644
index 0000000..c59be1e
--- /dev/null
+++ b/sys/kern/kern_vsr.c
@@ -0,0 +1,344 @@
+/*
+ * 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/vsr.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <sys/syslog.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("vsr: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static uint32_t
+fnv1_hash(const char *s)
+{
+ uint32_t hash = 2166136261UL;
+ const uint8_t *p = (uint8_t *)s;
+
+ while (*p != '\0') {
+ hash ^= *p;
+ hash = hash * 0x01000193;
+ ++p;
+ }
+
+ return hash;
+}
+
+/*
+ * Add a VSR capsule to a domain.
+ */
+static void
+vsr_domain_add(struct vsr_domain *vsp, struct vsr_capsule *cap)
+{
+ struct vsr_table *tab;
+ struct vsr_capsule **slot;
+ uint32_t hash;
+
+ if (vsp == NULL || cap == NULL) {
+ return;
+ }
+
+ if (cap->name == NULL) {
+ pr_error("vsr_domain_add: cap->name == NULL\n");
+ return;
+ }
+
+ tab = &vsp->table;
+ hash = fnv1_hash(cap->name);
+ slot = &tab->capsules[hash % VSR_MAX_CAPSULE];
+
+ /* If this slot is free, set it */
+ if (*slot == NULL) {
+ *slot = cap;
+ return;
+ }
+
+ /* Handle collision */
+ TAILQ_INSERT_TAIL(&(*slot)->buckets, cap, link);
+}
+
+/*
+ * Handle VSR domain hashmap collisions.
+ *
+ * @slot: Slot that we have collided with
+ * @name: Name to lookup
+ *
+ * Returns the pointer to the actual capsule if the
+ * collision has been resolved, otherwise, NULL if the
+ * entry to look up was not found.
+ */
+static struct vsr_capsule *
+vsr_domain_clash(struct vsr_capsule *slot, const char *name)
+{
+ struct vsr_capsule *cap_ent;
+
+ TAILQ_FOREACH(cap_ent, &slot->buckets, link) {
+ if (cap_ent == NULL) {
+ continue;
+ }
+
+ if (strcmp(cap_ent->name, name) == 0) {
+ return cap_ent;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Lookup a capsule within a VSR domain
+ * by name.
+ *
+ * @vsp: Domain to lookup within
+ * @name: Name to use as lookup key
+ *
+ * Returns NULL if no entry was found.
+ */
+static struct vsr_capsule *
+vfs_domain_lookup(struct vsr_domain *vsp, const char *name)
+{
+ uint32_t hash;
+ struct vsr_table *tab;
+ struct vsr_capsule **slot;
+
+ if (vsp == NULL || name == NULL) {
+ return NULL;
+ }
+
+ tab = &vsp->table;
+ hash = fnv1_hash(name);
+ slot = &tab->capsules[hash % VSR_MAX_CAPSULE];
+
+ if (*slot == NULL) {
+ return NULL;
+ }
+
+ if (strcmp((*slot)->name, name) != 0) {
+ return vsr_domain_clash(*slot, name);
+ }
+
+ return *slot;
+}
+
+/*
+ * Destroy a VSR capsule
+ *
+ * @capule: Capsule to destroy
+ */
+static void
+vsr_destroy_capsule(struct vsr_capsule *capsule)
+{
+ struct vsr_capsule *bucket;
+ struct capsule_ops *ops;
+
+ if (capsule->name != NULL) {
+ dynfree(capsule->name);
+ capsule->name = NULL;
+ }
+
+ ops = &capsule->ops;
+ if (ops->reclaim != NULL) {
+ ops->reclaim(capsule, 0);
+ }
+
+ TAILQ_FOREACH(bucket, &capsule->buckets, link) {
+ if (bucket == NULL) {
+ continue;
+ }
+ vsr_destroy_capsule(bucket);
+ }
+
+ /* Release any held locks */
+ mutex_release(&capsule->lock);
+}
+
+/*
+ * Destroy a VSR table
+ *
+ * @tab: Table to destroy.
+ */
+static void
+vsr_destroy_table(struct vsr_table *tab)
+{
+ struct vsr_capsule *capsule;
+
+ if (tab == NULL) {
+ pr_error("vsr_destroy_table: tab is NULL\n");
+ return;
+ }
+
+ for (int i = 0; i < VSR_MAX_CAPSULE; ++i) {
+ if ((capsule = tab->capsules[i]) == NULL) {
+ continue;
+ }
+
+ vsr_destroy_capsule(capsule);
+ }
+}
+
+/*
+ * Allocate a new VSR capsule and add it to
+ * VSR domain.
+ *
+ * @type: Domain type (e.g., VSR_FILE)
+ * @name: Capsule name (e.g., "mod0.data")
+ * @sz: Length of capsulized data
+ */
+struct vsr_capsule *
+vsr_new_capsule(struct proc *td, vsr_domain_t type, const char *name)
+{
+ struct vsr_capsule *capsule;
+ struct vsr_domain *domain;
+
+ /* Valid args? */
+ if (type >= VSR_MAX_DOMAIN || td == NULL) {
+ return NULL;
+ }
+
+ /*
+ * The VSR domain must be registered for
+ * us to add any capsules to it.
+ */
+ if ((domain = td->vsr_tab[type]) == NULL) {
+ pr_error("VSR domain %d not registered\n", type);
+ return NULL;
+ }
+
+ /* Allocate a new capsule */
+ capsule = dynalloc(sizeof(*capsule));
+ if (capsule == NULL) {
+ return NULL;
+ }
+
+ memset(capsule, 0, sizeof(*capsule));
+ capsule->name = strdup(name);
+
+ TAILQ_INIT(&capsule->buckets);
+ vsr_domain_add(domain, capsule);
+ return capsule;
+}
+
+/*
+ * Allocate a new VSR domain and add it to
+ * a specific process.
+ *
+ * @type: VSR type (e.g., VSR_FILE)
+ */
+struct vsr_domain *
+vsr_new_domain(struct proc *td, vsr_domain_t type)
+{
+ struct vsr_domain *domain;
+
+ /* Valid args? */
+ if (type >= VSR_MAX_DOMAIN || td == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Do not overwrite the entry if it is
+ * already allocated and log this anomalous
+ * activity.
+ */
+ if (td->vsr_tab[type] != NULL) {
+ pr_error("[security]: type %d already allocated\n", type);
+ return NULL;
+ }
+
+ domain = dynalloc(sizeof(*domain));
+ if (domain == NULL) {
+ return NULL;
+ }
+
+ /* Initialize the domain */
+ memset(domain, 0, sizeof(*domain));
+ domain->type = type;
+
+ td->vsr_tab[type] = domain;
+ return domain;
+}
+
+/*
+ * Lookup a capsule by name for the current
+ * process.
+ */
+struct vsr_capsule *
+vsr_lookup_capsule(struct proc *td, vsr_domain_t type, const char *name)
+{
+ struct vsr_domain *domain;
+
+ if (td == NULL) {
+ return NULL;
+ }
+
+ /*
+ * The VSR domain must be registered for
+ * us to lookup any capsules from it.
+ */
+ if ((domain = td->vsr_tab[type]) == NULL) {
+ pr_error("VSR domain %d not registered\n", type);
+ return NULL;
+ }
+
+ return vfs_domain_lookup(domain, name);
+}
+
+/*
+ * Initialize per-process domains
+ */
+void
+vsr_init_domains(struct proc *td)
+{
+ if (vsr_new_domain(td, VSR_FILE) == NULL) {
+ pr_error("failed to initialize VSR file domain\n");
+ }
+}
+
+/*
+ * Destroy per-process domains
+ */
+void
+vsr_destroy_domains(struct proc *td)
+{
+ struct vsr_domain *domain;
+
+ if (td == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < VSR_MAX_DOMAIN; ++i) {
+ if ((domain = td->vsr_tab[i]) == NULL) {
+ continue;
+ }
+
+ vsr_destroy_table(&domain->table);
+ }
+}
diff --git a/sys/lib/string/strdup.c b/sys/lib/string/strdup.c
new file mode 100644
index 0000000..9c101bc
--- /dev/null
+++ b/sys/lib/string/strdup.c
@@ -0,0 +1,52 @@
+/*
+ * 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 <string.h>
+#include <vm/dynalloc.h>
+
+char *
+strdup(const char *s)
+{
+ size_t s_len;
+ char *p;
+
+ /* Make sure size is not zero */
+ if ((s_len = strlen(s)) == 0) {
+ return NULL;
+ }
+
+ /* Allocate new memory for this string */
+ p = dynalloc(s_len + 1);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ memcpy(p, s, s_len);
+ return p;
+}