summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/aarch64/aarch64/exception.c128
-rw-r--r--sys/arch/aarch64/aarch64/intr.c37
-rw-r--r--sys/arch/aarch64/aarch64/locore.S (renamed from sys/arch/amd64/isa/i8042.S)12
-rw-r--r--sys/arch/aarch64/aarch64/machdep.c35
-rw-r--r--sys/arch/aarch64/aarch64/pmap.c280
-rw-r--r--sys/arch/aarch64/aarch64/proc_machdep.c6
-rw-r--r--sys/arch/aarch64/aarch64/reboot.c18
-rw-r--r--sys/arch/aarch64/aarch64/vector.S96
-rw-r--r--sys/arch/aarch64/conf/GENERIC6
-rw-r--r--sys/arch/aarch64/conf/link.ld6
-rw-r--r--sys/arch/aarch64/pci/pci_machdep.c14
-rw-r--r--sys/arch/amd64/amd64/gdt.c76
-rw-r--r--sys/arch/amd64/amd64/hpet.c17
-rw-r--r--sys/arch/amd64/amd64/intr.c72
-rw-r--r--sys/arch/amd64/amd64/ipi.c190
-rw-r--r--sys/arch/amd64/amd64/lapic.c3
-rw-r--r--sys/arch/amd64/amd64/lapic_intr.S2
-rw-r--r--sys/arch/amd64/amd64/machdep.c430
-rw-r--r--sys/arch/amd64/amd64/mp.c65
-rw-r--r--sys/arch/amd64/amd64/pmap.c75
-rw-r--r--sys/arch/amd64/amd64/proc_machdep.c132
-rw-r--r--sys/arch/amd64/amd64/reboot.c47
-rw-r--r--sys/arch/amd64/amd64/simd.S76
-rw-r--r--sys/arch/amd64/amd64/trap.c56
-rw-r--r--sys/arch/amd64/amd64/tsc.c109
-rw-r--r--sys/arch/amd64/amd64/uart.c1
-rw-r--r--sys/arch/amd64/amd64/vector.S221
-rw-r--r--sys/arch/amd64/conf/GENERIC22
-rw-r--r--sys/arch/amd64/conf/link.ld6
-rw-r--r--sys/arch/amd64/isa/i8042.c358
-rw-r--r--sys/arch/amd64/isa/mc1468.c281
-rw-r--r--sys/arch/amd64/isa/spkr.c53
-rw-r--r--sys/arch/amd64/pci/pci_machdep.c21
-rw-r--r--sys/conf/GENERIC10
-rw-r--r--sys/crypto/chacha20.c97
-rw-r--r--sys/crypto/siphash.c114
-rw-r--r--sys/dev/acpi/uacpi.c154
-rw-r--r--sys/dev/acpi/uacpi/event.c1
-rw-r--r--sys/dev/acpi/uacpi/resources.c1
-rw-r--r--sys/dev/acpi/uacpi/tables.c1
-rw-r--r--sys/dev/acpi/uacpi/utilities.c1
-rw-r--r--sys/dev/cons/cons.c426
-rw-r--r--sys/dev/cons/cons_ansi.c189
-rw-r--r--sys/dev/dcdr/cache.c14
-rw-r--r--sys/dev/dmi/dmi.c306
-rw-r--r--sys/dev/dmi/dmi_board.c104
-rw-r--r--sys/dev/ic/ahci.c429
-rw-r--r--sys/dev/ic/nvme.c60
-rw-r--r--sys/dev/pci/pci.c101
-rw-r--r--sys/dev/phy/e1000.c358
-rw-r--r--sys/dev/phy/et131x.c338
-rw-r--r--sys/dev/phy/rtl.c (renamed from sys/dev/phy/rt8139.c)178
-rw-r--r--sys/dev/random/entropy.c55
-rw-r--r--sys/dev/random/random.c88
-rw-r--r--sys/dev/usb/xhci.c93
-rw-r--r--sys/dev/video/fbdev.c84
-rw-r--r--sys/fs/ctlfs.c92
-rw-r--r--sys/fs/devfs.c59
-rw-r--r--sys/fs/initramfs.c11
-rw-r--r--sys/fs/tmpfs.c430
-rw-r--r--sys/include/arch/aarch64/board.h51
-rw-r--r--sys/include/arch/aarch64/cdefs.h1
-rw-r--r--sys/include/arch/aarch64/cpu.h1
-rw-r--r--sys/include/arch/aarch64/exception.h54
-rw-r--r--sys/include/arch/aarch64/frame.h80
-rw-r--r--sys/include/arch/aarch64/frameasm.h89
-rw-r--r--sys/include/arch/aarch64/intr.h57
-rw-r--r--sys/include/arch/aarch64/param.h35
-rw-r--r--sys/include/arch/aarch64/pci/pci.h40
-rw-r--r--sys/include/arch/aarch64/pio.h1
-rw-r--r--sys/include/arch/amd64/asm.h10
-rw-r--r--sys/include/arch/amd64/bus.h8
-rw-r--r--sys/include/arch/amd64/cdefs.h12
-rw-r--r--sys/include/arch/amd64/cpu.h53
-rw-r--r--sys/include/arch/amd64/frameasm.h2
-rw-r--r--sys/include/arch/amd64/gdt.h69
-rw-r--r--sys/include/arch/amd64/intr.h60
-rw-r--r--sys/include/arch/amd64/ipi.h67
-rw-r--r--sys/include/arch/amd64/isa/i8042var.h6
-rw-r--r--sys/include/arch/amd64/param.h35
-rw-r--r--sys/include/arch/amd64/pci/pci.h40
-rw-r--r--sys/include/arch/amd64/tsc.h55
-rw-r--r--sys/include/crypto/chacha20.h46
-rw-r--r--sys/include/crypto/siphash.h34
-rw-r--r--sys/include/dev/acpi/tables.h98
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h1
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/platform/config.h1
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/utilities.h1
-rw-r--r--sys/include/dev/cons/ansi.h75
-rw-r--r--sys/include/dev/cons/cons.h21
-rw-r--r--sys/include/dev/dmi/dmi.h42
-rw-r--r--sys/include/dev/dmi/dmivar.h41
-rw-r--r--sys/include/dev/ic/ahciregs.h7
-rw-r--r--sys/include/dev/ic/ahcivar.h6
-rw-r--r--sys/include/dev/ic/nvmevar.h85
-rw-r--r--sys/include/dev/mii/mii.h57
-rw-r--r--sys/include/dev/pci/pci.h3
-rw-r--r--sys/include/dev/phy/e1000regs.h119
-rw-r--r--sys/include/dev/phy/et131xregs.h275
-rw-r--r--sys/include/dev/phy/rtl.h (renamed from sys/include/dev/phy/rt8139.h)3
-rw-r--r--sys/include/dev/random/entropy.h40
-rw-r--r--sys/include/dev/timer.h6
-rw-r--r--sys/include/dev/usb/xhciregs.h8
-rw-r--r--sys/include/dev/video/fbdev.h1
-rw-r--r--sys/include/fs/ctlfs.h6
-rw-r--r--sys/include/fs/devfs.h2
-rw-r--r--sys/include/fs/tmpfs.h77
-rw-r--r--sys/include/lib/crc32.h37
-rw-r--r--sys/include/lib/string.h1
-rw-r--r--sys/include/net/ethertypes.h36
-rw-r--r--sys/include/net/if_arp.h51
-rw-r--r--sys/include/net/if_var.h82
-rw-r--r--sys/include/net/netbuf.h42
-rw-r--r--sys/include/netinet/if_ether.h56
-rw-r--r--sys/include/sys/atomic.h30
-rw-r--r--sys/include/sys/cdefs.h2
-rw-r--r--sys/include/sys/console.h57
-rw-r--r--sys/include/sys/device.h9
-rw-r--r--sys/include/sys/devstat.h46
-rw-r--r--sys/include/sys/disk.h211
-rw-r--r--sys/include/sys/disklabel.h48
-rw-r--r--sys/include/sys/dmi.h63
-rw-r--r--sys/include/sys/driver.h69
-rw-r--r--sys/include/sys/elf.h66
-rw-r--r--sys/include/sys/endian.h54
-rw-r--r--sys/include/sys/exec.h4
-rw-r--r--sys/include/sys/fbdev.h40
-rw-r--r--sys/include/sys/fcntl.h1
-rw-r--r--sys/include/sys/filedesc.h17
-rw-r--r--sys/include/sys/krq.h40
-rw-r--r--sys/include/sys/limits.h7
-rw-r--r--sys/include/sys/mman.h24
-rw-r--r--sys/include/sys/mmio.h1
-rw-r--r--sys/include/sys/mount.h2
-rw-r--r--sys/include/sys/mutex.h52
-rw-r--r--sys/include/sys/namei.h3
-rw-r--r--sys/include/sys/param.h16
-rw-r--r--sys/include/sys/proc.h61
-rw-r--r--sys/include/sys/queue.h6
-rw-r--r--sys/include/sys/sched.h34
-rw-r--r--sys/include/sys/schedvar.h6
-rw-r--r--sys/include/sys/signal.h2
-rw-r--r--sys/include/sys/socket.h202
-rw-r--r--sys/include/sys/socketvar.h53
-rw-r--r--sys/include/sys/spawn.h3
-rw-r--r--sys/include/sys/spinlock.h3
-rw-r--r--sys/include/sys/stat.h4
-rw-r--r--sys/include/sys/syscall.h20
-rw-r--r--sys/include/sys/sysctl.h22
-rw-r--r--sys/include/sys/syslog.h2
-rw-r--r--sys/include/sys/systm.h1
-rw-r--r--sys/include/sys/termios.h29
-rw-r--r--sys/include/sys/time.h60
-rw-r--r--sys/include/sys/tree.h1
-rw-r--r--sys/include/sys/types.h25
-rw-r--r--sys/include/sys/ucred.h57
-rw-r--r--sys/include/sys/uio.h58
-rw-r--r--sys/include/sys/vfs.h1
-rw-r--r--sys/include/sys/vmstat.h48
-rw-r--r--sys/include/sys/vnode.h39
-rw-r--r--sys/include/sys/vsr.h165
-rw-r--r--sys/include/sys/wait.h37
-rw-r--r--sys/include/sys/workqueue.h101
-rw-r--r--sys/include/vm/physmem.h4
-rw-r--r--sys/include/vm/pmap.h16
-rw-r--r--sys/include/vm/stat.h39
-rw-r--r--sys/include/vm/vm_device.h43
-rw-r--r--sys/include/vm/vm_pager.h1
-rw-r--r--sys/kern/disk_engine.c208
-rw-r--r--sys/kern/driver_blacklist.c170
-rw-r--r--sys/kern/driver_subr.c76
-rw-r--r--sys/kern/exec_elf64.c36
-rw-r--r--sys/kern/init_main.c40
-rw-r--r--sys/kern/kern_accnt.c128
-rw-r--r--sys/kern/kern_cpu.c61
-rw-r--r--sys/kern/kern_cred.c87
-rw-r--r--sys/kern/kern_descrip.c147
-rw-r--r--sys/kern/kern_disk.c475
-rw-r--r--sys/kern/kern_exec.c4
-rw-r--r--sys/kern/kern_exit.c113
-rw-r--r--sys/kern/kern_krq.c61
-rw-r--r--sys/kern/kern_panic.c54
-rw-r--r--sys/kern/kern_proc.c143
-rw-r--r--sys/kern/kern_sched.c247
-rw-r--r--sys/kern/kern_sig.c6
-rw-r--r--sys/kern/kern_socket.c1009
-rw-r--r--sys/kern/kern_spawn.c150
-rw-r--r--sys/kern/kern_stub.c36
-rw-r--r--sys/kern/kern_subr.c21
-rw-r--r--sys/kern/kern_synch.c69
-rw-r--r--sys/kern/kern_syscall.c26
-rw-r--r--sys/kern/kern_sysctl.c81
-rw-r--r--sys/kern/kern_syslog.c137
-rw-r--r--sys/kern/kern_time.c (renamed from sys/include/net/if_ether.h)77
-rw-r--r--sys/kern/kern_uio.c271
-rw-r--r--sys/kern/kern_vsr.c344
-rw-r--r--sys/kern/kern_work.c274
-rw-r--r--sys/kern/vfs_init.c3
-rw-r--r--sys/kern/vfs_lookup.c51
-rw-r--r--sys/kern/vfs_subr.c6
-rw-r--r--sys/kern/vfs_syscalls.c46
-rw-r--r--sys/kern/vfs_vcache.c2
-rw-r--r--sys/lib/crc32.c89
-rw-r--r--sys/lib/string/strdup.c52
-rw-r--r--sys/lib/string/vsnprintf.c4
-rw-r--r--sys/net/if.c85
-rw-r--r--sys/netinet/if_ether.c122
-rw-r--r--sys/vm/vm_device.c78
-rw-r--r--sys/vm/vm_init.c1
-rw-r--r--sys/vm/vm_map.c124
-rw-r--r--sys/vm/vm_physmem.c112
-rw-r--r--sys/vm/vm_stat.c95
-rw-r--r--sys/vm/vm_vnode.c3
213 files changed, 15316 insertions, 1044 deletions
diff --git a/sys/arch/aarch64/aarch64/exception.c b/sys/arch/aarch64/aarch64/exception.c
new file mode 100644
index 0000000..d6f1f97
--- /dev/null
+++ b/sys/arch/aarch64/aarch64/exception.c
@@ -0,0 +1,128 @@
+/*
+ * 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/syslog.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <machine/cdefs.h>
+#include <machine/exception.h>
+
+#define pr_trace(fmt, ...) kprintf("exception: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static inline void
+log_esr_class(uint8_t class)
+{
+ switch (class) {
+ case EC_WF:
+ pr_error("trapped WF\n");
+ break;
+ case EC_MCRMRC:
+ pr_error("trapped MCR/MRC\n");
+ break;
+ case EC_MCRRC:
+ pr_trace("trapped MCRR/MRRC\n");
+ break;
+ case EC_LDCSTC:
+ pr_error("trapped LDC/STC\n");
+ break;
+ case EC_SVE:
+ pr_trace("trapped SVE/SIMD/FP operation\n");
+ break;
+ case EC_BRE:
+ pr_error("ibt: bad branch target\n");
+ break;
+ case EC_ILLX:
+ pr_error("illegal execution state\n");
+ break;
+ case EC_SVC64:
+ /* TODO */
+ pr_error("supervisor call (TODO)!!\n");
+ break;
+ case EC_PCALIGN:
+ pr_error("PC alignment fault\n");
+ break;
+ case EC_DABORT:
+ case EC_EDABORT:
+ pr_error("data abort\n");
+ break;
+ case EC_SPALIGN:
+ pr_error("SP alignment fault\n");
+ break;
+ case EC_SERR:
+ pr_error("system error\n");
+ break;
+ default:
+ pr_error("unknown exception\n");
+ }
+}
+
+static void
+regdump(struct trapframe *tf, uint64_t elr)
+{
+ kprintf(OMIT_TIMESTAMP
+ "X0=%p X1=%p X2=%p\n"
+ "X3=%p X4=%p X5=%p\n"
+ "X6=%p X7=%p X8=%p\n"
+ "X9=%p X10=%p X11=%p\n"
+ "X12=%p X13=%p X14=%p\n"
+ "X15=%p X16=%p X17=%p\n"
+ "X18=%p X19=%p X20=%p\n"
+ "X21=%p X22=%p X23=%p\n"
+ "X24=%p X25=%p X26=%p\n"
+ "X27=%p X28=%p X29=%p\n"
+ "X30=%p\n"
+ "ELR=%p\n",
+ tf->x0, tf->x1, tf->x2, tf->x3,
+ tf->x4, tf->x5, tf->x6, tf->x7,
+ tf->x8, tf->x9, tf->x10, tf->x11,
+ tf->x12, tf->x13, tf->x14, tf->x15,
+ tf->x16, tf->x17, tf->x18, tf->x19,
+ tf->x20, tf->x21, tf->x22, tf->x23,
+ tf->x24, tf->x25, tf->x26, tf->x27,
+ tf->x28, tf->x29, tf->x30, elr);
+}
+
+/*
+ * Handle an exception
+ *
+ * @esr: Copy of the Exception Syndrome Register
+ */
+void
+handle_exception(struct trapframe *tf)
+{
+ uint8_t class;
+
+ class = (tf->esr >> 26) & 0x3F;
+ log_esr_class(class);
+ regdump(tf, tf->elr);
+ for (;;) {
+ md_hlt();
+ }
+}
diff --git a/sys/arch/aarch64/aarch64/intr.c b/sys/arch/aarch64/aarch64/intr.c
new file mode 100644
index 0000000..5fd2439
--- /dev/null
+++ b/sys/arch/aarch64/aarch64/intr.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 <machine/intr.h>
+
+void *
+intr_register(const char *name, const struct intr_hand *ih)
+{
+ /* TODO: Stub */
+ return NULL;
+}
diff --git a/sys/arch/amd64/isa/i8042.S b/sys/arch/aarch64/aarch64/locore.S
index 123d3a5..a95475c 100644
--- a/sys/arch/amd64/isa/i8042.S
+++ b/sys/arch/aarch64/aarch64/locore.S
@@ -27,11 +27,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <machine/frameasm.h>
-
.text
- .globl i8042_kb_isr
-INTRENTRY(i8042_kb_isr, handle_kb)
-handle_kb:
- call i8042_kb_event
- retq
+ .globl md_cpu_init
+md_cpu_init:
+ ldr x0, =__vectab
+ msr vbar_el1, x0
+ ret
diff --git a/sys/arch/aarch64/aarch64/machdep.c b/sys/arch/aarch64/aarch64/machdep.c
index 7c21e62..9a96cbb 100644
--- a/sys/arch/aarch64/aarch64/machdep.c
+++ b/sys/arch/aarch64/aarch64/machdep.c
@@ -31,15 +31,11 @@
#include <sys/panic.h>
#include <machine/cpu.h>
#include <machine/sync.h>
+#include <machine/board.h>
struct cpu_info g_bsp_ci = {0};
-void
-cpu_startup(struct cpu_info *ci)
-{
- /* TODO: STUB */
- return;
-}
+void md_cpu_init(void);
void
cpu_halt_others(void)
@@ -76,6 +72,13 @@ md_sync_all(void)
return 0;
}
+void
+cpu_halt_all(void)
+{
+ /* TODO: Stub */
+ for (;;);
+}
+
/*
* Get the descriptor for the currently
* running processor.
@@ -85,6 +88,24 @@ this_cpu(void)
{
struct cpu_info *ci;
- __ASMV("mrs %0, tpidr_el0" : "=r" (ci));
+ __ASMV("mrs %0, tpidr_el1" : "=r" (ci));
return ci;
}
+
+void
+cpu_startup(struct cpu_info *ci)
+{
+ ci->self = ci;
+ __ASMV("msr tpidr_el1, %0" :: "r" (ci));
+ md_cpu_init();
+}
+
+void
+md_get_board(struct board_info *res)
+{
+ uint64_t midr_el1;
+
+ __ASMV("mrs %0, midr_el1" : "=r" (midr_el1));
+ res->partno = (midr_el1 >> 4) & 0xFFF;
+ res->implementer = (midr_el1 >> 24) & 0xFF;
+}
diff --git a/sys/arch/aarch64/aarch64/pmap.c b/sys/arch/aarch64/aarch64/pmap.c
index b5ebda9..870ef80 100644
--- a/sys/arch/aarch64/aarch64/pmap.c
+++ b/sys/arch/aarch64/aarch64/pmap.c
@@ -28,35 +28,274 @@
*/
#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/panic.h>
#include <machine/vas.h>
#include <vm/pmap.h>
+#include <vm/physmem.h>
+#include <vm/vm.h>
+
+/* Memory types for MAIR_ELx */
+#define MT_NORMAL 0x00
+#define MT_NORMAL_UC 0x02
+#define MT_DEVICE 0x03
+
+/* Memory attributes */
+#define MEM_DEV_NGNRNE 0x00
+#define MEM_DEV_NVNRE 0x04
+#define MEM_NORMAL_UC 0x44
+#define MEM_NORMAL 0xFF
+
+#define MT_ATTR(idx, attr) ((attr) << (8 * (idx)))
+
+/*
+ * Descriptor bits for page table entries
+ *
+ * @PTE_VALID: Must be set to be valid
+ * @PTE_TABLE: Table (1), block (0)
+ * @PTE_USER: User access allowed
+ * @PTE_READONLY: Read-only
+ * @PTE_ISH: Inner sharable
+ * @PTE_AF: Accessed flag
+ * @PTE_XN: Execute never
+ */
+#define PTE_ADDR_MASK 0x0000FFFFFFFFF000
+#define PTE_VALID BIT(0)
+#define PTE_TABLE BIT(1)
+#define PTE_USER BIT(6)
+#define PTE_READONLY BIT(7)
+#define PTE_ISH (3 << 8)
+#define PTE_AF BIT(10)
+#define PTE_XN BIT(54)
+
+/*
+ * Write the EL1 Memory Attribute Indirection
+ * Register.
+ *
+ * @val: Value to write
+ *
+ * XXX: Refer to the ARMv8 Reference Manual section
+ * D7.2.70
+ */
+static inline void
+mair_el1_write(uint64_t val)
+{
+ __ASMV("msr mair_el1, %0"
+ :
+ : "r" (val)
+ : "memory"
+ );
+}
+
+static inline void
+tlb_flush(vaddr_t va)
+{
+ __ASMV(
+ "tlbi vaae1is, %0\n"
+ "dsb ish\n"
+ "isb\n"
+ :
+ : "r" (va >> 12)
+ : "memory"
+ );
+}
+
+static uint64_t
+pmap_prot_to_pte(vm_prot_t prot)
+{
+ uint64_t pte_flags = 0;
+
+ pte_flags |= (PTE_VALID | PTE_TABLE | PTE_AF);
+ pte_flags |= (PTE_XN | PTE_READONLY | PTE_ISH);
+
+ if (ISSET(prot, PROT_WRITE))
+ pte_flags &= ~PTE_READONLY;
+ if (ISSET(prot, PROT_EXEC))
+ pte_flags &= ~PTE_XN;
+ if (ISSET(prot, PROT_USER))
+ pte_flags |= PTE_USER;
+
+ return pte_flags;
+}
+
+/*
+ * Returns an index for a specific page map
+ * label based on an input address.
+ */
+static size_t
+pmap_level_idx(vaddr_t ia, uint8_t level)
+{
+ switch (level) {
+ case 0: return (ia >> 39) & 0x1FF;
+ case 1: return (ia >> 30) & 0x1FF;
+ case 2: return (ia >> 21) & 0x1FF;
+ case 3: return (ia >> 12) & 0x1FF;
+ default: panic("pmap_level_idx: bad index\n");
+ }
+
+ __builtin_unreachable();
+}
+
+/*
+ * Extract a level from a pagemap
+ *
+ * @level: Current pagemap level
+ * @ia: Input virtual address
+ * @pmap: Current level to extract from
+ * @alloc: Set to true to allocate new entries
+ *
+ * XXX: `level_idx' can be grabbed with pmap_level_idx().
+ */
+static uintptr_t *
+pmap_extract(uint8_t level, vaddr_t ia, vaddr_t *pmap, bool alloc)
+{
+ uintptr_t next, level_alloc;
+ uint8_t idx;
+
+ if (pmap == NULL) {
+ return NULL;
+ }
+
+ idx = pmap_level_idx(ia, level);
+ next = pmap[idx];
+
+ if (ISSET(next, PTE_VALID)) {
+ next = next & PTE_ADDR_MASK;
+ return PHYS_TO_VIRT(next);
+ }
+
+ /*
+ * Nothing to grab at this point, we'll need to
+ * allocate our own entry. However, if we are
+ * told not to allocate anything, just return
+ * NULL.
+ */
+ if (!alloc) {
+ return NULL;
+ }
+
+ level_alloc = vm_alloc_frame(1);
+ if (level_alloc == 0) {
+ return NULL;
+ }
+
+ pmap[idx] = (level_alloc | PTE_VALID | PTE_USER | PTE_TABLE);
+ return PHYS_TO_VIRT(level_alloc);
+}
+
+/*
+ * Get the lowest pagemap table referring to a 4 KiB
+ * frame.
+ *
+ * @ttrb: Translation table base to use
+ * @ia: Input virtual address
+ * @alloc: If true, allocate new pagemap entries as needed
+ * @res: Result goes here
+ */
+static int
+pmap_get_tbl(paddr_t ttbrn, vaddr_t ia, bool alloc, uintptr_t **res)
+{
+ vaddr_t *root;
+ uintptr_t *l1, *l2, *l3;
+
+ root = PHYS_TO_VIRT(ttbrn);
+
+ l1 = pmap_extract(0, ia, root, alloc);
+ if (l1 == NULL) {
+ return -1;
+ }
+
+ l2 = pmap_extract(1, ia, l1, alloc);
+ if (l2 == NULL) {
+ return -1;
+ }
+
+ l3 = pmap_extract(2, ia, l2, alloc);
+ if (l3 == NULL) {
+ return -1;
+ }
+
+ *res = l3;
+ return 0;
+}
struct vas
pmap_read_vas(void)
{
- /* TODO: STUB */
struct vas vas = {0};
+
+ __ASMV(
+ "mrs %0, ttbr0_el1\n"
+ "mrs %1, ttbr1_el1\n"
+ : "=r" (vas.ttbr0_el1),
+ "=r" (vas.ttbr1_el1)
+ :
+ : "memory"
+ );
+
return vas;
}
void
pmap_switch_vas(struct vas vas)
{
- /* TODO: STUB */
+ __ASMV(
+ "msr ttbr0_el1, %0\n"
+ "msr ttbr1_el1, %1\n"
+ :
+ : "r" (vas.ttbr0_el1),
+ "r" (vas.ttbr1_el1)
+ : "memory"
+ );
return;
}
int
pmap_map(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot)
{
- /* TODO: STUB */
+ paddr_t ttbrn = vas.ttbr0_el1;
+ uint64_t pte_flags;
+ uintptr_t *tbl;
+ int error;
+
+ if (va >= VM_HIGHER_HALF) {
+ ttbrn = vas.ttbr1_el1;
+ }
+
+ if ((error = pmap_get_tbl(ttbrn, va, true, &tbl)) < 0) {
+ return error;
+ }
+ if (__unlikely(tbl == NULL)) {
+ return -1;
+ }
+
+ pte_flags = pmap_prot_to_pte(prot);
+ tbl[pmap_level_idx(va, 3)] = pa | pte_flags;
+ tlb_flush(va);
return 0;
}
int
pmap_unmap(struct vas vas, vaddr_t va)
{
- /* TODO: STUB */
+ paddr_t ttbrn = vas.ttbr0_el1;
+ uintptr_t *tbl;
+ int error;
+
+ if (va >= VM_HIGHER_HALF) {
+ ttbrn = vas.ttbr1_el1;
+ }
+
+ if ((error = pmap_get_tbl(ttbrn, va, true, &tbl)) < 0) {
+ return error;
+ }
+ if (__unlikely(tbl == NULL)) {
+ return -1;
+ }
+
+ tbl[pmap_level_idx(va, 3)] = 0;
+ tlb_flush(va);
return 0;
}
@@ -66,3 +305,36 @@ pmap_destroy_vas(struct vas vas)
/* TODO: STUB */
return;
}
+
+bool
+pmap_is_clean(struct vas vas, vaddr_t va)
+{
+ /* TODO: STUB */
+ return false;
+}
+
+void
+pmap_mark_clean(struct vas vas, vaddr_t va)
+{
+ /* TODO: STUB */
+ return;
+}
+
+int
+pmap_set_cache(struct vas vas, vaddr_t va, int type)
+{
+ /* TODO: STUB */
+ return 0;
+}
+
+int
+pmap_init(void)
+{
+ uint64_t mair;
+
+ mair = MT_ATTR(MT_NORMAL, MEM_NORMAL) |
+ MT_ATTR(MT_NORMAL_UC, MEM_NORMAL_UC) |
+ MT_ATTR(MT_DEVICE, MEM_DEV_NGNRNE);
+ mair_el1_write(mair);
+ return 0;
+}
diff --git a/sys/arch/aarch64/aarch64/proc_machdep.c b/sys/arch/aarch64/aarch64/proc_machdep.c
index 97902e5..cc58af9 100644
--- a/sys/arch/aarch64/aarch64/proc_machdep.c
+++ b/sys/arch/aarch64/aarch64/proc_machdep.c
@@ -37,17 +37,17 @@
* @ip: Instruction pointer.
*/
int
-md_fork(struct proc *p, struct proc *parent, uintptr_t ip)
+md_spawn(struct proc *p, struct proc *parent, uintptr_t ip)
{
/* TODO: STUB */
return 0;
}
-void
+uintptr_t
md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog)
{
/* TODO: STUB */
- return;
+ return 0;
}
void
diff --git a/sys/arch/aarch64/aarch64/reboot.c b/sys/arch/aarch64/aarch64/reboot.c
index 6d82133..372012a 100644
--- a/sys/arch/aarch64/aarch64/reboot.c
+++ b/sys/arch/aarch64/aarch64/reboot.c
@@ -30,9 +30,25 @@
#include <sys/reboot.h>
#include <sys/param.h>
+/*
+ * Typically the reset vector is at address 0 but this can
+ * be remapped if the vendor is feeling silly.
+ */
+void(*g_cpu_reboot)(void) = NULL;
+
void
cpu_reboot(int method)
{
- /* TODO: STUB */
+ g_cpu_reboot();
for (;;);
}
+
+/*
+ * arg0: Method bits
+ */
+scret_t
+sys_reboot(struct syscall_args *scargs)
+{
+ cpu_reboot(scargs->arg0);
+ __builtin_unreachable();
+}
diff --git a/sys/arch/aarch64/aarch64/vector.S b/sys/arch/aarch64/aarch64/vector.S
new file mode 100644
index 0000000..c8f77ca
--- /dev/null
+++ b/sys/arch/aarch64/aarch64/vector.S
@@ -0,0 +1,96 @@
+/*
+ * 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 <machine/frameasm.h>
+
+// Vector table entries are aligned at 128 bytes
+// giving us 32 exception entries
+.macro ventry label
+ .align 7
+ b \label
+.endm
+
+ .text
+x_sync_elx:
+ PUSH_XFRAME(TRAPNO_XSYNC) // Synchronous: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0 // TODO
+ b 1b
+
+x_irq_elx:
+ PUSH_XFRAME(TRAPNO_XIRQ) // IRQ: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0 // TODO
+ b 1b
+
+x_fiq_elx:
+ PUSH_XFRAME(TRAPNO_XFIQ) // FIQ: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0
+ b 1b
+
+x_serr_elx:
+ PUSH_XFRAME(TRAPNO_XSERR) // SERR: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0 // TODO
+ b 1b
+
+x_unimpl:
+1: hlt #0
+ b 1b
+
+ .align 11 // Table aligned @ 2 KiB
+ .globl __vectab
+__vectab:
+ // From current EL (w/ SP_EL0)
+ ventry x_sync_elx
+ ventry x_irq_elx
+ ventry x_fiq_elx
+ ventry x_serr_elx
+
+ // From current EL (w/ SP_ELx > 0)
+ ventry x_sync_elx
+ ventry x_irq_elx
+ ventry x_fiq_elx
+ ventry x_serr_elx
+
+ // Lower EL with faulting code in AARCH64
+ ventry x_sync_elx
+ ventry x_irq_elx
+ ventry x_fiq_elx
+ ventry x_serr_elx
+
+ ventry x_unimpl
+ ventry x_unimpl
+ ventry x_unimpl
+ ventry x_unimpl
diff --git a/sys/arch/aarch64/conf/GENERIC b/sys/arch/aarch64/conf/GENERIC
index 5691685..702a248 100644
--- a/sys/arch/aarch64/conf/GENERIC
+++ b/sys/arch/aarch64/conf/GENERIC
@@ -1,5 +1,3 @@
// Kernel options
-option SERIAL_DEBUG yes
-
-// Kernel constants
-setval SCHED_NQUEUE 4
+option SERIAL_DEBUG yes // Enable kmsg serial logging
+option USER_KMSG yes // Show kmsg in user consoles
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/aarch64/pci/pci_machdep.c b/sys/arch/aarch64/pci/pci_machdep.c
index fa92165..8de6cc9 100644
--- a/sys/arch/aarch64/pci/pci_machdep.c
+++ b/sys/arch/aarch64/pci/pci_machdep.c
@@ -30,20 +30,6 @@
#include <sys/types.h>
#include <dev/pci/pci.h>
-pcireg_t
-pci_readl(struct pci_device *dev, uint32_t offset)
-{
- /* TODO: STUB */
- return 0;
-}
-
-void
-pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
-{
- /* TODO: STUB */
- return;
-}
-
/*
* Map a BAR into kernel memory.
*
diff --git a/sys/arch/amd64/amd64/gdt.c b/sys/arch/amd64/amd64/gdt.c
index a8fe54d..40d8f48 100644
--- a/sys/arch/amd64/amd64/gdt.c
+++ b/sys/arch/amd64/amd64/gdt.c
@@ -29,50 +29,70 @@
#include <machine/gdt.h>
-struct gdt_entry g_gdt_data[256] = {
+/*
+ * The GDT should be cache line aligned, since it is accessed every time a
+ * segment selector is reloaded
+ */
+__cacheline_aligned struct gdt_entry g_gdt_data[GDT_ENTRY_COUNT] = {
/* Null */
{0},
- /* Kernel code (0x8) */
+ /* Kernel code (0x08) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0x9A,
- .granularity = 0x20,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_64BIT_CODE | GDT_ATTRIBUTE_PRESENT |
+ GDT_ATTRIBUTE_DPL0 | GDT_ATTRIBUTE_NONSYSTEM |
+ GDT_ATTRIBUTE_EXECUTABLE | GDT_ATTRIBUTE_READABLE,
+ .base_hi = 0x00
},
/* Kernel data (0x10) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0x92,
- .granularity = 0x00,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_PRESENT | GDT_ATTRIBUTE_DPL0 |
+ GDT_ATTRIBUTE_NONSYSTEM | GDT_ATTRIBUTE_WRITABLE,
+ .base_hi = 0x00
},
/* User code (0x18) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0xFA,
- .granularity = 0xAF,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_64BIT_CODE | GDT_ATTRIBUTE_PRESENT |
+ GDT_ATTRIBUTE_DPL3 | GDT_ATTRIBUTE_NONSYSTEM |
+ GDT_ATTRIBUTE_EXECUTABLE | GDT_ATTRIBUTE_READABLE,
+ .base_hi = 0x00
},
/* User data (0x20) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0xF2,
- .granularity = 0x00,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_PRESENT | GDT_ATTRIBUTE_DPL3 |
+ GDT_ATTRIBUTE_NONSYSTEM | GDT_ATTRIBUTE_WRITABLE,
+ .base_hi = 0x00
},
- /* TSS segment (0x28) */
- {0}
+ /*
+ * TSS segment (0x28)
+ *
+ * NOTE: 64-bit TSS descriptors are 16 bytes, equivalent to the size of two
+ * regular descriptor entries.
+ * See Intel SPG 3/25 Section 9.2.3 - TSS Descriptor in 64-bit mode.
+ */
+ {0}, {0}
+};
+
+/* Verify that the GDT is of the correct size */
+__static_assert(sizeof(g_gdt_data) == (8 * GDT_ENTRY_COUNT));
+
+const struct gdtr g_gdtr = {
+ .limit = sizeof(g_gdt_data) - 1,
+ .offset = (uintptr_t)&g_gdt_data[0]
};
diff --git a/sys/arch/amd64/amd64/hpet.c b/sys/arch/amd64/amd64/hpet.c
index 1670546..9191bee 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,7 +195,9 @@ 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;
+ timer.flags = TIMER_MONOTONIC;
register_timer(TIMER_GP, &timer);
return 0;
}
diff --git a/sys/arch/amd64/amd64/intr.c b/sys/arch/amd64/amd64/intr.c
index c31ee3c..c44c88e 100644
--- a/sys/arch/amd64/amd64/intr.c
+++ b/sys/arch/amd64/amd64/intr.c
@@ -31,12 +31,19 @@
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/panic.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
#include <machine/intr.h>
#include <machine/cpu.h>
#include <machine/asm.h>
+#include <machine/ioapic.h>
#include <vm/dynalloc.h>
+#include <string.h>
-static struct intr_entry *intrs[256] = {0};
+#define pr_trace(fmt, ...) kprintf("intr: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+struct intr_hand *g_intrs[256] = {0};
int
splraise(uint8_t s)
@@ -67,35 +74,70 @@ splx(uint8_t s)
ci->ipl = s;
}
-int
-intr_alloc_vector(const char *name, uint8_t priority)
+void *
+intr_register(const char *name, const struct intr_hand *ih)
{
- size_t vec = MAX(priority << IPL_SHIFT, 0x20);
- struct intr_entry *intr;
+ uint32_t vec = MAX(ih->priority << IPL_SHIFT, 0x20);
+ struct intr_hand *ih_new;
+ struct intr_data *idp_new;
+ const struct intr_data *idp;
+ size_t name_len;
/* Sanity check */
- if (vec > NELEM(intrs)) {
- return -1;
+ if (vec > NELEM(g_intrs) || name == NULL) {
+ return NULL;
+ }
+
+ ih_new = dynalloc(sizeof(*ih_new));
+ if (ih_new == NULL) {
+ pr_error("could not allocate new interrupt handler\n");
+ return NULL;
}
/*
* Try to allocate an interrupt vector. An IPL is made up
* of 4 bits so there can be 16 vectors per IPL.
+ *
+ * XXX: Vector 0x20 is reserved for the Hyra scheduler and
+ * vectors 0x21 to 0x21 + N_IPIVEC are reserved for
+ * inter-processor interrupts.
*/
for (int i = vec; i < vec + 16; ++i) {
- if (intrs[i] != NULL) {
+ if (g_intrs[i] != NULL || i < 0x24) {
continue;
}
- intr = dynalloc(sizeof(*intr));
- if (intr == NULL) {
- return -ENOMEM;
+ /* Allocate memory for the name */
+ name_len = strlen(name) + 1;
+ ih_new->name = dynalloc(name_len);
+ if (ih_new->name == NULL) {
+ dynfree(ih_new);
+ pr_trace("could not allocate interrupt name\n");
+ return NULL;
}
- intr->priority = priority;
- intrs[i] = intr;
- return i;
+ memcpy(ih_new->name, name, name_len);
+ idp_new = &ih_new->data;
+ idp = &ih->data;
+
+ /* Pass the interrupt data */
+ idp_new->ihp = ih_new;
+ idp_new->data_u64 = idp->data_u64;
+
+ /* Setup the new intr_hand */
+ ih_new->func = ih->func;
+ ih_new->priority = ih->priority;
+ ih_new->irq = ih->irq;
+ ih_new->vector = i;
+ ih_new->nintr = 0;
+ g_intrs[i] = ih_new;
+
+ if (ih->irq >= 0) {
+ ioapic_set_vec(ih->irq, i);
+ ioapic_irq_unmask(ih->irq);
+ }
+ return ih_new;
}
- return -1;
+ return NULL;
}
diff --git a/sys/arch/amd64/amd64/ipi.c b/sys/arch/amd64/amd64/ipi.c
new file mode 100644
index 0000000..b44a9ac
--- /dev/null
+++ b/sys/arch/amd64/amd64/ipi.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <sys/panic.h>
+#include <sys/spinlock.h>
+#include <machine/cpu.h>
+#include <machine/idt.h>
+#include <machine/ipi.h>
+#include <machine/lapic.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+void ipi_isr(void);
+void halt_isr(void);
+
+void __ipi_handle_common(void);
+
+#define pr_trace(fmt, ...) kprintf("ipi: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define COOKIE 0x7E0A
+#define MAX_IPI 32
+
+/* For the global state of the subsystem */
+static uint32_t cookie = 0;
+
+static struct cpu_ipi ipi_list[MAX_IPI];
+static uint8_t ipi_count = 0;
+static struct spinlock lock;
+
+/*
+ * Allocate an IPI that can be sent to other
+ * cores on the CPU. This is the core logic
+ * and contains *no* locks. One should be
+ * using the md_ipi_alloc() function instead.
+ *
+ * Returns the allocated IPI identifier on succes,
+ * otherwise a less than zero value is returned.
+ */
+static int
+__ipi_alloc(struct cpu_ipi **res)
+{
+ struct cpu_ipi *ipip;
+
+ if (ipi_count >= MAX_IPI) {
+ return -EAGAIN;
+ }
+
+ ipip = &ipi_list[ipi_count];
+ ipip->cookie = COOKIE;
+ ipip->id = ipi_count++;
+ ipip->handler = NULL;
+ *res = ipip;
+ return ipip->id;
+}
+
+/*
+ * Common IPI routine, called from vector.S
+ *
+ * XXX: Internal usage only
+ */
+void
+__ipi_handle_common(void)
+{
+ struct cpu_ipi *ipip;
+ struct cpu_info *ci = this_cpu();
+ ipi_pend_t pending = 0;
+
+ if (cookie != COOKIE) {
+ pr_trace("[warn]: got spurious ipi\n");
+ return;
+ }
+
+ if (ci == NULL) {
+ pr_error("could not get current CPU\n");
+ return;
+ }
+
+ if (ipi_count == 0) {
+ pr_error("no registered IPIs\n");
+ return;
+ }
+
+ /* Attempt to find a handler */
+ pending = ci->ipi_pending;
+ for (int i = 0; i < ipi_count; ++i) {
+ ipip = &ipi_list[i];
+ if (ISSET(pending, BIT(i))) {
+ ipip->handler(ipip);
+ ci->ipi_pending &= ~BIT(i);
+ }
+ }
+
+ /* We are done dispatching IPIs */
+ ci->ipi_dispatch = 0;
+}
+
+/*
+ * Send one or more IPIs to a specific
+ * processor after caller sets bits in
+ * the `ci->ipi_pending' field
+ *
+ * @ci: Processor to send IPI(s) to
+ * @ipi: IPIs to send
+ */
+int
+md_ipi_send(struct cpu_info *ci, ipi_pend_t ipi)
+{
+ uint32_t apic_id = 0;
+
+ if (ci != NULL) {
+ /*
+ * We are already dispatching IPIs, we don't
+ * want to find ourselves in interrupt hell.
+ */
+ if (ci->ipi_dispatch) {
+ return -EAGAIN;
+ }
+
+ apic_id = ci->apicid;
+ }
+
+ ci->ipi_dispatch = 1;
+ ci->ipi_pending |= BIT(ipi);
+
+ /* Send it through on the bus */
+ lapic_send_ipi(
+ apic_id,
+ IPI_SHORTHAND_NONE,
+ IPI_VECTOR
+ );
+ return 0;
+}
+
+/*
+ * IPI allocation interface with
+ * locking.
+ */
+int
+md_ipi_alloc(struct cpu_ipi **res)
+{
+ int retval;
+
+ spinlock_acquire(&lock);
+ retval = __ipi_alloc(res);
+ spinlock_release(&lock);
+ return retval;
+}
+
+/*
+ * Initialize the IPI thunks
+ */
+void
+md_ipi_init(void)
+{
+ /* Initialize the IPI vectors */
+ idt_set_desc(IPI_VECTOR, IDT_INT_GATE, ISR(ipi_isr), 0);
+ idt_set_desc(HALT_VECTOR, IDT_INT_GATE, ISR(halt_isr), 0);
+ cookie = COOKIE;
+}
diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c
index 70d36a5..ceb5428 100644
--- a/sys/arch/amd64/amd64/lapic.c
+++ b/sys/arch/amd64/amd64/lapic.c
@@ -340,7 +340,7 @@ lapic_init(void)
/* Allocate a vector if needed */
if (lapic_timer_vec == 0) {
- lapic_timer_vec = intr_alloc_vector("lapictmr", IPL_CLOCK);
+ lapic_timer_vec = (IPL_CLOCK << IPL_SHIFT) | 0x20;
idt_set_desc(lapic_timer_vec, IDT_INT_GATE, ISR(lapic_tmr_isr),
IST_SCHED);
}
@@ -364,5 +364,6 @@ lapic_init(void)
lapic_timer.name = "LAPIC_INTEGRATED_TIMER";
lapic_timer.stop = lapic_timer_stop;
lapic_timer.oneshot_us = lapic_timer_oneshot_us;
+ lapic_timer.flags = 0;
register_timer(TIMER_SCHED, &lapic_timer);
}
diff --git a/sys/arch/amd64/amd64/lapic_intr.S b/sys/arch/amd64/amd64/lapic_intr.S
index 5ae8f39..1413660 100644
--- a/sys/arch/amd64/amd64/lapic_intr.S
+++ b/sys/arch/amd64/amd64/lapic_intr.S
@@ -33,6 +33,6 @@
.globl lapic_tmr_isr
INTRENTRY(lapic_tmr_isr, handle_lapic_tmr)
handle_lapic_tmr:
- call sched_switch // Context switch per every timer IRQ
+ call md_sched_switch // Context switch per every timer IRQ
call lapic_eoi // Done! Signal that we finished to the Local APIC
retq
diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c
index 4a885fa..60c37bf 100644
--- a/sys/arch/amd64/amd64/machdep.c
+++ b/sys/arch/amd64/amd64/machdep.c
@@ -42,7 +42,25 @@
#include <machine/uart.h>
#include <machine/sync.h>
#include <machine/intr.h>
+#include <machine/ipi.h>
+#include <machine/cdefs.h>
#include <machine/isa/i8042var.h>
+#include <dev/cons/cons.h>
+#include <string.h>
+
+/*
+ * This defines the max number of frames
+ * we will pass while walking the callstack
+ * in md_backtrace()
+ */
+#define MAX_FRAME_DEPTH 16
+
+#define pr_trace(fmt, ...) kprintf("cpu: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+#define pr_trace_bsp(...) \
+ if (!bsp_init) { \
+ pr_trace(__VA_ARGS__); \
+ }
#if defined(__SPECTRE_IBRS)
#define SPECTRE_IBRS __SPECTRE_IBRS
@@ -50,46 +68,86 @@
#define SPECTRE_IBRS 0
#endif
-static uint8_t halt_vector = 0;
+#if defined(__CPU_SMEP)
+#define CPU_SMEP __CPU_SMEP
+#else
+#define CPU_SMEP 0
+#endif
+
+#if defined(__CPU_UMIP)
+#define CPU_UMIP __CPU_UMIP
+#else
+#define CPU_UMIP 0
+#endif
int ibrs_enable(void);
+int simd_init(void);
void syscall_isr(void);
+void pin_isr_load(void);
struct cpu_info g_bsp_ci = {0};
-static struct gdtr bsp_gdtr = {
- .limit = sizeof(struct gdt_entry) * 256 - 1,
- .offset = (uintptr_t)&g_gdt_data[0]
-};
+static struct cpu_ipi *tlb_ipi;
+static struct spinlock ipi_lock = {0};
+static bool bsp_init = false;
-__attribute__((__interrupt__))
-static void
-cpu_halt_isr(void *p)
+static int
+tlb_shootdown_handler(struct cpu_ipi *ipi)
{
- __ASMV("cli; hlt");
- __builtin_unreachable();
+ struct cpu_info *ci;
+ int ipl;
+
+ /*
+ * Get the current CPU and check if we even
+ * need a shootdown. If `tlb_shootdown' is
+ * unset, this is not for us.
+ */
+ ci = this_cpu();
+ if (!ci->tlb_shootdown) {
+ return -1;
+ }
+
+ ipl = splraise(IPL_HIGH);
+ __invlpg(ci->shootdown_va);
+
+ ci->shootdown_va = 0;
+ ci->tlb_shootdown = 0;
+ splx(ipl);
+ return 0;
}
static void
-setup_vectors(void)
+setup_vectors(struct cpu_info *ci)
{
- if (halt_vector == 0) {
- halt_vector = intr_alloc_vector("cpu-halt", IPL_HIGH);
+ union tss_stack scstack;
+ union tss_stack dfstack;
+
+ /* Try to allocate a syscall stack */
+ if (tss_alloc_stack(&scstack, DEFAULT_PAGESIZE) != 0) {
+ panic("failed to allocate syscall stack\n");
}
+ /* Try to allocate a double fault stack */
+ if (tss_alloc_stack(&dfstack, DEFAULT_PAGESIZE) != 0) {
+ panic("failed to allocate double fault stack\n");
+ }
+
+ tss_update_ist(ci, scstack, IST_SYSCALL);
+ tss_update_ist(ci, dfstack, IST_DBFLT);
+
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);
idt_set_desc(0x4, IDT_TRAP_GATE, ISR(overflow), 0);
idt_set_desc(0x5, IDT_TRAP_GATE, ISR(bound_range), 0);
idt_set_desc(0x6, IDT_TRAP_GATE, ISR(invl_op), 0);
- idt_set_desc(0x8, IDT_TRAP_GATE, ISR(double_fault), 0);
+ idt_set_desc(0x8, IDT_TRAP_GATE, ISR(double_fault), IST_DBFLT);
idt_set_desc(0xA, IDT_TRAP_GATE, ISR(invl_tss), 0);
idt_set_desc(0xB, IDT_TRAP_GATE, ISR(segnp), 0);
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(halt_vector, IDT_INT_GATE, ISR(cpu_halt_isr), 0);
+ idt_set_desc(0x80, IDT_USER_INT_GATE, ISR(syscall_isr), IST_SYSCALL);
+ pin_isr_load();
}
static inline void
@@ -97,7 +155,7 @@ init_tss(struct cpu_info *ci)
{
struct tss_desc *desc;
- desc = (struct tss_desc *)&g_gdt_data[GDT_TSS];
+ desc = (struct tss_desc *)&g_gdt_data[GDT_TSS_INDEX];
write_tss(ci, desc);
tss_load();
}
@@ -133,45 +191,267 @@ backtrace_addr_to_name(uintptr_t addr, off_t *off)
return NULL;
}
+static void
+enable_simd(void)
+{
+ int retval;
+
+ if ((retval = simd_init()) < 0) {
+ pr_trace_bsp("SIMD not supported\n");
+ }
+
+ if (retval == 1) {
+ pr_trace_bsp("SSE enabled but not AVX\n");
+ }
+}
+
+static void
+init_ipis(void)
+{
+ int error;
+
+ if (bsp_init) {
+ return;
+ }
+
+ spinlock_acquire(&ipi_lock);
+ error = md_ipi_alloc(&tlb_ipi);
+ if (error < 0) {
+ pr_error("md_ipi_alloc: returned %d\n", error);
+ panic("failed to init TLB IPI\n");
+ }
+
+ tlb_ipi->handler = tlb_shootdown_handler;
+
+ /*
+ * Some IPIs must have very specific IDs
+ * so that they are standard and usable
+ * throughout the rest of the sytem.
+ */
+ if (tlb_ipi->id != IPI_TLB)
+ panic("expected IPI_TLB for TLB IPI\n");
+
+ spinlock_release(&ipi_lock);
+}
+
+static void
+cpu_get_vendor(struct cpu_info *ci)
+{
+ uint32_t unused, ebx, ecx, edx;
+ char vendor_str[13];
+
+ /*
+ * This CPUID returns a 12 byte CPU vendor string
+ * that we'll put together and use to detect the vendor.
+ */
+ CPUID(0, unused, ebx, ecx, edx);
+
+ /* Dword 0 */
+ vendor_str[0] = ebx & 0xFF;
+ vendor_str[1] = (ebx >> 8) & 0xFF;
+ vendor_str[2] = (ebx >> 16) & 0xFF;
+ vendor_str[3] = (ebx >> 24) & 0xFF;
+
+ /* Dword 1 */
+ vendor_str[4] = edx & 0xFF;
+ vendor_str[5] = (edx >> 8) & 0xFF;
+ vendor_str[6] = (edx >> 16) & 0xFF;
+ vendor_str[7] = (edx >> 24) & 0xFF;
+
+ /* Dword 2 */
+ vendor_str[8] = ecx & 0xFF;
+ vendor_str[9] = (ecx >> 8) & 0xFF;
+ vendor_str[10] = (ecx >> 16) & 0xFF;
+ vendor_str[11] = (ecx >> 24) & 0xFF;
+ vendor_str[12] = '\0';
+
+ /* Is this an AMD CPU? */
+ if (strcmp(vendor_str, "AuthenticAMD") == 0) {
+ ci->vendor = CPU_VENDOR_AMD;
+ return;
+ }
+
+ /* Is this an Intel CPU? */
+ if (strcmp(vendor_str, "GenuineIntel") == 0) {
+ ci->vendor = CPU_VENDOR_INTEL;
+ return;
+ }
+
+ /*
+ * Some buggy Intel CPUs report the string "GenuineIotel"
+ * instead of "GenuineIntel". This is rare but we should
+ * still handle it as it can happen. Probably a good idea
+ * to log it so the user can know about their rare CPU
+ * quirk and brag to their friends :~)
+ */
+ if (strcmp(vendor_str, "GenuineIotel") == 0) {
+ pr_trace_bsp("vendor_str=%s\n", vendor_str);
+ pr_trace_bsp("detected vendor string quirk\n");
+ ci->vendor = CPU_VENDOR_INTEL;
+ return;
+ }
+
+ ci->vendor = CPU_VENDOR_OTHER;
+}
+
+static void
+cpu_get_info(struct cpu_info *ci)
+{
+ uint32_t unused, eax, ebx, ecx, edx;
+ uint8_t ext_model, ext_family;
+
+ /* Get the vendor information */
+ cpu_get_vendor(ci);
+
+ /* Extended features */
+ CPUID(0x07, unused, ebx, ecx, unused);
+ if (ISSET(ebx, BIT(7)))
+ ci->feat |= CPU_FEAT_SMEP;
+ if (ISSET(ebx, BIT(20)))
+ ci->feat |= CPU_FEAT_SMAP;
+ if (ISSET(ecx, BIT(2)))
+ ci->feat |= CPU_FEAT_UMIP;
+
+ /*
+ * Processor power management information bits as well
+ * as bits describing RAS capabilities
+ */
+ CPUID(0x80000007, unused, unused, unused, edx);
+ if (ISSET(edx, BIT(8)))
+ ci->feat |= CPU_FEAT_TSCINV;
+
+ /*
+ * Processor info and feature bits
+ */
+ CPUID(0x01, eax, unused, unused, unused);
+ ci->model = (eax >> 4) & 0xF;
+ ci->family = (eax >> 8) & 0xF;
+
+ /*
+ * If the family ID is 15 then the actual family
+ * ID is the sum of the extended family and the
+ * family ID fields.
+ */
+ if (ci->family == 0xF) {
+ ext_family = (eax >> 20) & 0xFF;
+ ci->family += ext_family;
+ }
+
+ /*
+ * If the family has the value of either 6 or 15,
+ * then the extended model number would be used.
+ * Slap them together if this is the case.
+ */
+ if (ci->family == 6 || ci->family == 15) {
+ ext_model = (eax >> 16) & 0xF;
+ ci->model |= (ext_model << 4);
+ }
+}
+
+/*
+ * The CR4.UMIP bit prevents user programs from
+ * executing instructions related to accessing
+ * system memory structures. This should be enabled
+ * by default if supported.
+ */
+static void
+cpu_enable_umip(void)
+{
+ struct cpu_info *ci = this_cpu();
+ uint64_t cr4;
+
+ if (!CPU_UMIP) {
+ pr_trace_bsp("UMIP not configured\n");
+ return;
+ }
+
+ if (ISSET(ci->feat, CPU_FEAT_UMIP)) {
+ cr4 = amd64_read_cr4();
+ cr4 |= CR4_UMIP;
+ amd64_write_cr4(cr4);
+ }
+}
+
+void
+cpu_shootdown_tlb(vaddr_t va)
+{
+ uint32_t ncpu = cpu_count();
+ struct cpu_info *cip;
+
+ for (uint32_t i = 0; i < ncpu; ++i) {
+ cip = cpu_get(i);
+ if (cip == NULL) {
+ break;
+ }
+
+ spinlock_acquire(&cip->lock);
+ cip->shootdown_va = va;
+ cip->tlb_shootdown = 1;
+ md_ipi_send(cip, IPI_TLB);
+ spinlock_release(&cip->lock);
+ }
+}
+
void
md_backtrace(void)
{
- uintptr_t *rbp;
- uintptr_t rip;
+ uintptr_t *rbp = NULL;
+ uintptr_t rip, tmp;
off_t off;
const char *name;
+ char line[256];
+ uint8_t n = 0;
__ASMV("mov %%rbp, %0" : "=r" (rbp) :: "memory");
while (1) {
+ if (n >= MAX_FRAME_DEPTH) {
+ break;
+ }
+
+ /* End of callstack */
+ if (rbp == NULL) {
+ break;
+ }
+
rip = rbp[1];
rbp = (uintptr_t *)rbp[0];
- name = backtrace_addr_to_name(rip, &off);
- if (rbp == NULL)
+ /*
+ * RBP should be aligned on an 8-byte
+ * boundary... Don't trust this state
+ * anymore if it is not.
+ */
+ tmp = (uintptr_t)rbp;
+ if ((tmp & (8 - 1)) != 0) {
+ break;
+ }
+
+ /*
+ * This is not a valid value, get out
+ * of this loop!!
+ */
+ if (rip == 0) {
break;
- if (name == NULL)
- name = "???";
+ }
- kprintf(OMIT_TIMESTAMP "%p @ <%s+0x%x>\n", rip, name, off);
+ name = backtrace_addr_to_name(rip, &off);
+ snprintf(line, sizeof(line), "%p @ <%s+0x%x>\n", rip, name, off);
+ cons_putstr(&g_root_scr, line, strlen(line));
+ ++n;
}
}
void
cpu_halt_all(void)
{
- /*
- * If we have no current 'cpu_info' structure set,
- * we can't send IPIs, so just assume only the current
- * processor is the only one active, clear interrupts
- * then halt it.
- */
- if (rdmsr(IA32_GS_BASE) == 0) {
- __ASMV("cli; hlt");
- }
+ lapic_send_ipi(
+ 0,
+ IPI_SHORTHAND_ALL,
+ HALT_VECTOR
+ );
- /* Send IPI to all cores */
- lapic_send_ipi(0, IPI_SHORTHAND_ALL, halt_vector);
- for (;;);
+ __ASMV("cli; hlt");
+ __builtin_unreachable();
}
/*
@@ -181,12 +461,11 @@ cpu_halt_all(void)
void
cpu_halt_others(void)
{
- if (rdmsr(IA32_GS_BASE) == 0) {
- __ASMV("cli; hlt");
- }
-
- /* Send IPI to all cores */
- lapic_send_ipi(0, IPI_SHORTHAND_OTHERS, halt_vector);
+ lapic_send_ipi(
+ 0,
+ IPI_SHORTHAND_OTHERS,
+ HALT_VECTOR
+ );
}
void
@@ -210,6 +489,10 @@ this_cpu(void)
{
struct cpu_info *ci;
+ if (rdmsr(IA32_GS_BASE) == 0) {
+ return NULL;
+ }
+
/*
* This might look crazy but we are just leveraging the "m"
* constraint to add the offset of the self field within
@@ -236,17 +519,74 @@ md_sync_all(void)
}
void
+cpu_enable_smep(void)
+{
+ struct cpu_info *ci;
+ uint64_t cr4;
+
+ /* Don't bother if not enabled */
+ if (!CPU_SMEP) {
+ return;
+ }
+
+ ci = this_cpu();
+ if (!ISSET(ci->feat, CPU_FEAT_SMEP)) {
+ pr_trace_bsp("SMEP not supported\n");
+ return;
+ }
+
+ cr4 = amd64_read_cr4();
+ cr4 |= BIT(20); /* CR4.SMEP */
+ amd64_write_cr4(cr4);
+}
+
+void
+cpu_disable_smep(void)
+{
+ struct cpu_info *ci;
+ uint64_t cr4;
+
+ if (!CPU_SMEP) {
+ return;
+ }
+
+ ci = this_cpu();
+ if (!ISSET(ci->feat, CPU_FEAT_SMEP)) {
+ return;
+ }
+
+ cr4 = amd64_read_cr4();
+ cr4 &= ~BIT(20); /* CR4.SMEP */
+ amd64_write_cr4(cr4);
+}
+
+void
cpu_startup(struct cpu_info *ci)
{
ci->self = ci;
- gdt_load(&bsp_gdtr);
+ ci->feat = 0;
+ gdt_load();
idt_load();
- setup_vectors();
wrmsr(IA32_GS_BASE, (uintptr_t)ci);
-
init_tss(ci);
+
+ setup_vectors(ci);
+ md_ipi_init();
+ init_ipis();
+
try_mitigate_spectre();
+ ci->online = 1;
+ ci->preempt = 1;
+
+ cpu_get_info(ci);
+ cpu_enable_smep();
+ cpu_enable_umip();
+ enable_simd();
lapic_init();
+
+ if (!bsp_init) {
+ bsp_init = true;
+ }
}
diff --git a/sys/arch/amd64/amd64/mp.c b/sys/arch/amd64/amd64/mp.c
index 22561d7..43830ba 100644
--- a/sys/arch/amd64/amd64/mp.c
+++ b/sys/arch/amd64/amd64/mp.c
@@ -29,7 +29,10 @@
#include <sys/types.h>
#include <sys/limine.h>
+#include <sys/limits.h>
+#include <sys/systm.h>
#include <sys/syslog.h>
+#include <sys/proc.h>
#include <sys/spinlock.h>
#include <sys/sched.h>
#include <sys/atomic.h>
@@ -40,40 +43,95 @@
#define pr_trace(fmt, ...) kprintf("cpu_mp: " fmt, ##__VA_ARGS__)
+extern struct proc g_proc0;
static volatile struct limine_smp_request g_smp_req = {
.id = LIMINE_SMP_REQUEST,
.revision = 0
};
-static volatile uint32_t ncpu_up = 0;
+static volatile uint32_t ncpu_up = 1;
+static struct cpu_info *ci_list[CPU_MAX];
+static struct spinlock ci_list_lock = {0};
static void
ap_trampoline(struct limine_smp_info *si)
{
struct cpu_info *ci;
+ struct proc *idle;
ci = dynalloc(sizeof(*ci));
__assert(ci != NULL);
memset(ci, 0, sizeof(*ci));
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);
sched_enter();
while (1);
}
+struct cpu_info *
+cpu_get(uint32_t index)
+{
+ if (index >= ncpu_up) {
+ return NULL;
+ }
+
+ return ci_list[index];
+}
+
+/*
+ * Grab the CPU stat structured of a specified
+ * processor
+ *
+ * @cpu_index: CPU index number
+ */
+struct sched_cpu *
+cpu_get_stat(uint32_t cpu_index)
+{
+ struct cpu_info *ci;
+
+ if ((ci = cpu_get(cpu_index)) == NULL) {
+ return NULL;
+ }
+
+ return &ci->stat;
+}
+
+uint32_t
+cpu_count(void)
+{
+ return ncpu_up;
+}
+
void
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;
/* Should not happen */
__assert(resp != NULL);
cpus = resp->cpus;
- cpu_init_counter = resp->cpu_count - 1;
+ ncpu = resp->cpu_count;
+ 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");
@@ -91,5 +149,6 @@ mp_bootstrap_aps(struct cpu_info *ci)
}
/* Wait for all cores to be ready */
- while (ncpu_up < cpu_init_counter);
+ while ((ncpu_up - 1) < cpu_init_counter);
+ cpu_report_count(ncpu_up);
}
diff --git a/sys/arch/amd64/amd64/pmap.c b/sys/arch/amd64/amd64/pmap.c
index 2e62a4b..6c6bfcd 100644
--- a/sys/arch/amd64/amd64/pmap.c
+++ b/sys/arch/amd64/amd64/pmap.c
@@ -33,6 +33,8 @@
#include <sys/errno.h>
#include <machine/tlb.h>
#include <machine/vas.h>
+#include <machine/cpu.h>
+#include <machine/cdefs.h>
#include <vm/pmap.h>
#include <vm/physmem.h>
#include <vm/vm.h>
@@ -52,7 +54,7 @@
#define PTE_PCD BIT(4) /* Page-level cache disable */
#define PTE_ACC BIT(5) /* Accessed */
#define PTE_DIRTY BIT(6) /* Dirty (written-to page) */
-#define PTE_PAT BIT(7)
+#define PTE_PS BIT(7) /* Page size */
#define PTE_GLOBAL BIT(8)
#define PTE_NX BIT(63) /* Execute-disable */
@@ -112,6 +114,16 @@ pmap_extract(uint8_t level, vaddr_t va, vaddr_t *pmap, bool alloc)
return NULL;
}
+ /*
+ * TODO: Support huge pages... For now, don't let the
+ * bootloader fuck us up with their pre-kernel
+ * mappings and tell huge pages to get the fuck.
+ *
+ */
+ if (ISSET(pmap[idx], PTE_PS)) {
+ pmap[idx] = 0;
+ }
+
if (ISSET(pmap[idx], PTE_P)) {
next = (pmap[idx] & PTE_ADDR_MASK);
return PHYS_TO_VIRT(next);
@@ -176,14 +188,15 @@ done:
* @vas: Virtual address space.
* @va: Target virtual address.
* @val: Value to write.
+ * @alloc: True to alloc new paging entries.
*/
static int
-pmap_update_tbl(struct vas vas, vaddr_t va, uint64_t val)
+pmap_update_tbl(struct vas vas, vaddr_t va, uint64_t val, bool alloc)
{
uintptr_t *tbl;
int status;
- if ((status = pmap_get_tbl(vas, va, true, &tbl)) != 0) {
+ if ((status = pmap_get_tbl(vas, va, alloc, &tbl)) != 0) {
return status;
}
@@ -266,19 +279,21 @@ pmap_map(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot)
{
uint32_t flags = pmap_prot_to_pte(prot);
- return pmap_update_tbl(vas, va, (pa | flags));
+ return pmap_update_tbl(vas, va, (pa | flags), true);
}
int
pmap_unmap(struct vas vas, vaddr_t va)
{
- return pmap_update_tbl(vas, va, 0);
+ return pmap_update_tbl(vas, va, 0, false);
}
int
pmap_set_cache(struct vas vas, vaddr_t va, int type)
{
uintptr_t *tbl;
+ uint32_t flags;
+ paddr_t pa;
int status;
size_t idx;
@@ -286,20 +301,62 @@ pmap_set_cache(struct vas vas, vaddr_t va, int type)
return status;
idx = pmap_get_level_index(1, va);
+ pa = tbl[idx] & PTE_ADDR_MASK;
+ flags = tbl[idx] & ~PTE_ADDR_MASK;
/* Set the caching policy */
switch (type) {
case VM_CACHE_UC:
- tbl[idx] |= PTE_PCD;
- tbl[idx] &= ~PTE_PWT;
+ flags |= PTE_PCD;
+ flags &= ~PTE_PWT;
break;
case VM_CACHE_WT:
- tbl[idx] &= ~PTE_PCD;
- tbl[idx] |= PTE_PWT;
+ flags &= ~PTE_PCD;
+ flags |= PTE_PWT;
break;
default:
return -EINVAL;
}
+ return pmap_update_tbl(vas, va, (pa | flags), false);
+}
+
+bool
+pmap_is_clean(struct vas vas, vaddr_t va)
+{
+ uintptr_t *tbl;
+ int status;
+ size_t idx;
+
+ if ((status = pmap_get_tbl(vas, va, false, &tbl)) != 0)
+ return status;
+
+ idx = pmap_get_level_index(1, va);
+ return ISSET(tbl[idx], PTE_DIRTY) == 0;
+}
+
+void
+pmap_mark_clean(struct vas vas, vaddr_t va)
+{
+ uintptr_t *tbl;
+ int status;
+ size_t idx;
+
+ if ((status = pmap_get_tbl(vas, va, false, &tbl)) != 0)
+ return;
+
+ idx = pmap_get_level_index(1, va);
+ tbl[idx] &= ~PTE_DIRTY;
+
+ if (cpu_count() > 1) {
+ cpu_shootdown_tlb(va);
+ } else {
+ __invlpg(va);
+ }
+}
+
+int
+pmap_init(void)
+{
return 0;
}
diff --git a/sys/arch/amd64/amd64/proc_machdep.c b/sys/arch/amd64/amd64/proc_machdep.c
index 9579b7e..82b4e4f 100644
--- a/sys/arch/amd64/amd64/proc_machdep.c
+++ b/sys/arch/amd64/amd64/proc_machdep.c
@@ -32,6 +32,8 @@
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/exec.h>
+#include <sys/sched.h>
+#include <sys/schedvar.h>
#include <machine/frame.h>
#include <machine/gdt.h>
#include <machine/cpu.h>
@@ -40,7 +42,7 @@
#include <vm/map.h>
#include <string.h>
-void
+uintptr_t
md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog)
{
uintptr_t *sp = stack_top;
@@ -97,6 +99,7 @@ md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog)
STACK_PUSH(sp, argc);
tfp = &td->tf;
tfp->rsp = (uintptr_t)sp - VM_HIGHER_HALF;
+ return tfp->rsp;
}
void
@@ -123,24 +126,31 @@ md_td_kick(struct proc *td)
{
struct trapframe *tfp;
struct cpu_info *ci;
+ uint16_t ds = USER_DS | 3;
tfp = &td->tf;
ci = this_cpu();
ci->curtd = td;
+ td->flags &= ~PROC_KTD;
__ASMV(
- "push %0\n"
+ "mov %0, %%rax\n"
"push %1\n"
- "pushf\n"
"push %2\n"
"push %3\n"
+ "push %%rax\n"
+ "push %4\n"
+ "test $3, %%ax\n"
+ "jz 1f\n"
"lfence\n"
"swapgs\n"
- "iretq"
+ "1:\n"
+ " iretq"
:
- : "i" (USER_DS | 3),
+ : "r" (tfp->cs),
+ "r" (ds),
"r" (tfp->rsp),
- "i" (USER_CS | 3),
+ "m" (tfp->rflags),
"r" (tfp->rip)
);
@@ -162,6 +172,7 @@ md_spawn(struct proc *p, struct proc *parent, uintptr_t ip)
struct pcb *pcbp;
uint8_t rpl = 0;
int error;
+ vm_prot_t prot = PROT_READ | PROT_WRITE;
tfp = &p->tf;
@@ -201,12 +212,117 @@ 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);
+ prot |= PROT_USER;
+ vm_map(pcbp->addrsp, stack_base, stack_base, prot, PROC_STACK_PAGES);
}
p->stack_base = stack_base;
tfp->rsp = ALIGN_DOWN((stack_base + PROC_STACK_SIZE) - 1, 16);
return 0;
}
+
+/*
+ * Save thread state and enqueue it back into one
+ * of the ready queues.
+ */
+static void
+sched_save_td(struct proc *td, struct trapframe *tf)
+{
+ /*
+ * Save trapframe to process structure only
+ * if PROC_EXEC is not set.
+ */
+ if (!ISSET(td->flags, PROC_EXEC)) {
+ memcpy(&td->tf, tf, sizeof(td->tf));
+ }
+
+ sched_enqueue_td(td);
+}
+
+static void
+sched_switch_to(struct trapframe *tf, struct proc *td)
+{
+ struct cpu_info *ci;
+ struct sched_cpu *cpustat;
+ struct pcb *pcbp;
+
+ ci = this_cpu();
+
+ if (tf != NULL) {
+ memcpy(tf, &td->tf, sizeof(*tf));
+ }
+
+ /* Update stats */
+ cpustat = &ci->stat;
+ atomic_inc_64(&cpustat->nswitch);
+
+ ci->curtd = td;
+ pcbp = &td->pcb;
+ pmap_switch_vas(pcbp->addrsp);
+}
+
+/*
+ * Enable or disable preemption on the current
+ * processor
+ *
+ * @enable: Enable preemption if true
+ */
+void
+sched_preempt_set(bool enable)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci == NULL) {
+ return;
+ }
+
+ ci->preempt = enable;
+}
+
+bool
+sched_preemptable(void)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci == NULL) {
+ return false;
+ }
+
+ return ci->preempt;
+}
+
+/*
+ * Perform a context switch.
+ */
+void
+md_sched_switch(struct trapframe *tf)
+{
+ struct proc *next_td, *td;
+ struct cpu_info *ci;
+
+ ci = this_cpu();
+ if (!ci->preempt) {
+ sched_oneshot(false);
+ return;
+ }
+
+ td = ci->curtd;
+ mi_sched_switch(td);
+
+ if (td != NULL) {
+ if (td->pid == 0)
+ return;
+
+ sched_save_td(td, tf);
+ }
+
+ if ((next_td = sched_dequeue_td()) == NULL) {
+ sched_oneshot(false);
+ return;
+ }
+
+ sched_switch_to(tf, next_td);
+ sched_oneshot(false);
+}
diff --git a/sys/arch/amd64/amd64/reboot.c b/sys/arch/amd64/amd64/reboot.c
index d47a352..8ebe15e 100644
--- a/sys/arch/amd64/amd64/reboot.c
+++ b/sys/arch/amd64/amd64/reboot.c
@@ -34,9 +34,49 @@
#include <machine/cpu.h>
#include <dev/acpi/acpi.h>
+static void
+cpu_reset_intel(struct cpu_info *ci)
+{
+ /*
+ * Ivy bridge processors and their panther point chipsets
+ * (family 6) can be reset through special PCH reset control
+ * registers
+ */
+ if (ci->family == 6) {
+ outb(0xCF9, 3 << 1);
+ }
+}
+
+/*
+ * Attempt to reboot the system, we do this in many
+ * stages of escalation. If a reset via the i8042
+ * controller fails and we are on an Intel processor,
+ * attempt a chipset specific reset. If that somehow fails
+ * as well, just smack the cpu with a NULL IDTR as well
+ * as an INT $0x0
+ */
+static void
+__cpu_reset(struct cpu_info *ci)
+{
+ /* Try via the i8042 */
+ outb(0x64, 0xFE);
+
+ /* Something went wrong if we are here */
+ if (ci == NULL) {
+ return;
+ }
+
+ if (ci->vendor == CPU_VENDOR_INTEL) {
+ cpu_reset_intel(ci);
+ }
+}
+
void
cpu_reboot(int method)
{
+ struct cpu_info *ci = this_cpu();
+ uint32_t *__dmmy = NULL;
+
if (ISSET(method, REBOOT_POWEROFF)) {
acpi_sleep(ACPI_SLEEP_S5);
}
@@ -45,10 +85,9 @@ cpu_reboot(int method)
cpu_halt_all();
}
- /* Pulse the reset line until the machine goes down */
- for (;;) {
- outb(0x64, 0xFE);
- }
+ __cpu_reset(ci);
+ asm volatile("lgdt %0; int $0x0" :: "m" (__dmmy));
+ __builtin_unreachable();
}
/*
diff --git a/sys/arch/amd64/amd64/simd.S b/sys/arch/amd64/amd64/simd.S
new file mode 100644
index 0000000..23fe461
--- /dev/null
+++ b/sys/arch/amd64/amd64/simd.S
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+ .text
+ .globl simd_init
+simd_init:
+ /*
+ * Enable SIMD, if SSE and AVX is supported,
+ * a value of zero is returned. If SSE is
+ * supported yet AVX is not, a value of one
+ * is returned. However, if none are supported,
+ * this routine returns -1.
+ */
+
+ // Do we support SSE?
+ mov $1, %eax
+ cpuid
+ bt $25, %edx
+ jnc .sse_not_sup
+
+ mov %cr0, %rax // Old CR0 -> EAX
+ and $0xFFFB, %ax // Disable co-processor emulation
+ or $0x02, %ax // Enable co-processor monitoring
+ mov %rax, %cr0 // Update CR0 with new flags
+
+ mov %cr4, %rax // Old CR4 -> EAX
+ or $0x200, %ax // Enable FXSAVE/FXRSTOR
+ or $0x400, %ax // Enable SIMD FP exceptions
+ mov %rax, %cr4 // Update CR4 with new flags
+
+ mov $1, %eax // LEAF 1
+ cpuid // Bit 28 of ECX indicates AVX support
+ mov $3, %eax // We need to check two bits
+ shl $27, %eax // Which are ECX.OSXSAVE and ECX.AVX
+ test %eax, %ecx // Are XSAVE and AVX supported?
+ jnc .avx_not_sup // Nope, just continue
+
+ // Enable AVX
+ xor %rcx, %rcx // Select XCR0
+ xgetbv // Load extended control register
+ or $0x07, %eax // Set AVX + SSE bits
+ xsetbv // Store new flags
+ xor %rax, %rax // Everything is good
+ retq // Return back to caller (RETURN)
+.sse_not_sup:
+ mov $-1, %rax
+ retq
+.avx_not_sup:
+ mov $1, %rax
+ retq
diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c
index 6492a29..7d9df3f 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,7 +80,24 @@ pf_faultaddr(void)
}
static void
-regdump(struct trapframe *tf)
+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);
+}
+
+__dead static void
+trap_fatal(struct trapframe *tf)
{
uintptr_t cr3, cr2 = pf_faultaddr();
@@ -79,11 +107,17 @@ regdump(struct trapframe *tf)
: "memory"
);
- kprintf(OMIT_TIMESTAMP
+ if (tf->trapno == TRAP_PAGEFLT) {
+ pf_code(tf->error_code);
+ }
+
+ panic("got fatal trap (%s)\n\n"
+ "-- DUMPING PROCESSOR STATE --\n"
"RAX=%p RCX=%p RDX=%p\n"
"RBX=%p RSI=%p RDI=%p\n"
"RFL=%p CR2=%p CR3=%p\n"
- "RBP=%p RSP=%p RIP=%p\n",
+ "RBP=%p RSP=%p RIP=%p\n\n",
+ trap_type[tf->trapno],
tf->rax, tf->rcx, tf->rdx,
tf->rbx, tf->rsi, tf->rdi,
tf->rflags, cr2, cr3,
@@ -94,6 +128,7 @@ static void
trap_user(struct trapframe *tf)
{
struct proc *td = this_td();
+ uintptr_t fault_addr;
sigset_t sigset;
sigemptyset(&sigset);
@@ -101,17 +136,22 @@ 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:
sigaddset(&sigset, SIGFPE);
break;
default:
- kprintf("got unknown user trap %d\n", tf->trapno);
- sigaddset(&sigset, SIGKILL);
+ panic("got unknown user trap %d\n", tf->trapno);
break;
}
+ fault_addr = pf_faultaddr();
+ proc_coredump(td, fault_addr);
+
/*
* Send the signal then flush the signal queue right
* away as these types of events are critical.
@@ -141,8 +181,6 @@ trap_syscall(struct trapframe *tf)
void
trap_handler(struct trapframe *tf)
{
- splraise(IPL_HIGH);
-
if (tf->trapno >= NELEM(trap_type)) {
panic("got unknown trap %d\n", tf->trapno);
}
@@ -155,6 +193,6 @@ trap_handler(struct trapframe *tf)
return;
}
- regdump(tf);
- panic("fatal trap - halting\n");
+ trap_fatal(tf);
+ __builtin_unreachable();
}
diff --git a/sys/arch/amd64/amd64/tsc.c b/sys/arch/amd64/amd64/tsc.c
new file mode 100644
index 0000000..2111cd0
--- /dev/null
+++ b/sys/arch/amd64/amd64/tsc.c
@@ -0,0 +1,109 @@
+/*
+ * 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/types.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <sys/driver.h>
+#include <sys/syslog.h>
+#include <machine/tsc.h>
+#include <machine/asm.h>
+#include <machine/cpuid.h>
+
+/* See kconf(9) */
+#if defined(__USER_TSC)
+#define USER_TSC __USER_TSC
+#else
+#define USER_TSC 0
+#endif /* __USER_TSC */
+
+#define pr_trace(fmt, ...) kprintf("tsc: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static uint64_t tsc_i = 0;
+
+uint64_t
+rdtsc_rel(void)
+{
+ return rdtsc() - tsc_i;
+}
+
+/*
+ * Check if the TSC and RDTSC instruction is
+ * supported on the current CPU.
+ *
+ * Returns zero if supported, otherwise a less
+ * than zero value is returned.
+ */
+static int
+tsc_check(void)
+{
+ uint32_t edx, unused;
+
+ CPUID(1, unused, unused, unused, edx);
+ if (ISSET(edx, BIT(4))) {
+ return 0;
+ }
+
+ return -ENOTSUP;
+}
+
+static int
+tsc_init(void)
+{
+ uint64_t cr4;
+ int error;
+
+ /* Is the TSC even supported? */
+ if ((error = tsc_check()) != 0) {
+ pr_error("TSC not supported by machine\n");
+ return error;
+ }
+
+ cr4 = amd64_read_cr4();
+ tsc_i = rdtsc();
+ pr_trace("initial count @ %d\n", rdtsc_rel());
+
+ /*
+ * If we USER_TSC is configured to "yes" then
+ * we'll need to enable the 'rdtsc' instruction
+ * in user mode.
+ */
+ if (!USER_TSC) {
+ cr4 &= ~CR4_TSD;
+ } else {
+ cr4 |= CR4_TSD;
+ }
+
+ amd64_write_cr4(cr4);
+ return 0;
+}
+
+DRIVER_EXPORT(tsc_init, "x86-tsc");
diff --git a/sys/arch/amd64/amd64/uart.c b/sys/arch/amd64/amd64/uart.c
index 429aa57..ea88d3c 100644
--- a/sys/arch/amd64/amd64/uart.c
+++ b/sys/arch/amd64/amd64/uart.c
@@ -115,4 +115,3 @@ uart_init(void)
outb(UART_REG_MCR, UART_MCR_DTR);
return 0;
}
-
diff --git a/sys/arch/amd64/amd64/vector.S b/sys/arch/amd64/amd64/vector.S
new file mode 100644
index 0000000..62bed1b
--- /dev/null
+++ b/sys/arch/amd64/amd64/vector.S
@@ -0,0 +1,221 @@
+/*
+ * 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 <machine/frameasm.h>
+
+#define IDT_INT_GATE 0x8E
+
+.macro IDT_SET_VEC vec, sym
+ mov $\vec, %rdi
+ mov $IDT_INT_GATE, %rsi
+ lea \sym(%rip), %rdx
+ xor %rcx, %rcx
+ call idt_set_desc
+.endm
+
+ .text
+ ALIGN_TEXT
+ioapic_common_func:
+ xor %rcx, %rcx // Clear counter
+.walk: // Walk the handlers
+ lea g_intrs(%rip), %rbx // Grab table to RBX
+ lea (%rbx, %rcx, 8), %rbx // g_intrs + (8 * rcx)
+ mov (%rbx), %rdx // Grab the intr_hand
+ or %rdx, %rdx // No more?
+ jz 1f // Nope, return
+
+ mov (%rdx), %rbx // intr_hand.func
+ add $16, %rdx // Get interrupt data
+ mov %rdx, %rdi // Pass the interrupt data
+ push %rcx // Save our counter
+ push %rdx
+ call *%rbx // Call the handler
+ pop %rdx
+ pop %rcx // Restore our counter
+ or %rax, %rax // Was it theirs? (RET >= 1)
+ jnz handled // Yes, we are done.
+1: inc %rcx // Next
+ cmp $256, %rcx // Did we reach the end?
+ jl .walk // Nope, keep going
+ jmp done // Out of entries
+handled:
+ sub $8, %rdx
+ addq $1, (%rdx)
+done:
+ call lapic_eoi
+ retq
+
+ .globl pin_isr_load
+pin_isr_load:
+ IDT_SET_VEC 37, ioapic_edge_0
+ IDT_SET_VEC 38, ioapic_edge_1
+ IDT_SET_VEC 39, ioapic_edge_2
+ IDT_SET_VEC 40, ioapic_edge_3
+ IDT_SET_VEC 41, ioapic_edge_4
+ IDT_SET_VEC 42, ioapic_edge_5
+ IDT_SET_VEC 43, ioapic_edge_6
+ IDT_SET_VEC 44, ioapic_edge_7
+ IDT_SET_VEC 45, ioapic_edge_8
+ IDT_SET_VEC 46, ioapic_edge_9
+ IDT_SET_VEC 47, ioapic_edge_10
+ IDT_SET_VEC 48, ioapic_edge_11
+ IDT_SET_VEC 49, ioapic_edge_12
+ IDT_SET_VEC 50, ioapic_edge_13
+ IDT_SET_VEC 51, ioapic_edge_14
+ IDT_SET_VEC 52, ioapic_edge_15
+ IDT_SET_VEC 53, ioapic_edge_16
+ IDT_SET_VEC 54, ioapic_edge_17
+ IDT_SET_VEC 55, ioapic_edge_18
+ IDT_SET_VEC 56, ioapic_edge_19
+ IDT_SET_VEC 57, ioapic_edge_20
+ IDT_SET_VEC 58, ioapic_edge_21
+ IDT_SET_VEC 59, ioapic_edge_22
+ IDT_SET_VEC 60, ioapic_edge_23
+ IDT_SET_VEC 61, ioapic_edge_24
+ IDT_SET_VEC 62, ioapic_edge_25
+ IDT_SET_VEC 63, ioapic_edge_26
+ IDT_SET_VEC 64, ioapic_edge_27
+ IDT_SET_VEC 65, ioapic_edge_28
+ IDT_SET_VEC 66, ioapic_edge_29
+ IDT_SET_VEC 67, ioapic_edge_30
+ IDT_SET_VEC 68, ioapic_edge_31
+ IDT_SET_VEC 69, ioapic_edge_32
+ IDT_SET_VEC 70, ioapic_edge_33
+ IDT_SET_VEC 71, ioapic_edge_34
+ IDT_SET_VEC 72, ioapic_edge_35
+ IDT_SET_VEC 73, ioapic_edge_36
+ IDT_SET_VEC 74, ioapic_edge_37
+ IDT_SET_VEC 75, ioapic_edge_38
+ IDT_SET_VEC 76, ioapic_edge_39
+ IDT_SET_VEC 77, ioapic_edge_40
+ IDT_SET_VEC 78, ioapic_edge_41
+ IDT_SET_VEC 79, ioapic_edge_42
+ IDT_SET_VEC 80, ioapic_edge_43
+ IDT_SET_VEC 81, ioapic_edge_44
+ IDT_SET_VEC 82, ioapic_edge_45
+ IDT_SET_VEC 83, ioapic_edge_46
+ IDT_SET_VEC 84, ioapic_edge_47
+ IDT_SET_VEC 85, ioapic_edge_48
+ IDT_SET_VEC 86, ioapic_edge_49
+ IDT_SET_VEC 87, ioapic_edge_50
+ IDT_SET_VEC 88, ioapic_edge_51
+ IDT_SET_VEC 89, ioapic_edge_52
+ IDT_SET_VEC 90, ioapic_edge_53
+ IDT_SET_VEC 91, ioapic_edge_54
+ IDT_SET_VEC 92, ioapic_edge_55
+ IDT_SET_VEC 93, ioapic_edge_56
+ IDT_SET_VEC 94, ioapic_edge_57
+ IDT_SET_VEC 95, ioapic_edge_58
+ IDT_SET_VEC 96, ioapic_edge_59
+ IDT_SET_VEC 97, ioapic_edge_60
+ IDT_SET_VEC 98, ioapic_edge_61
+ IDT_SET_VEC 99, ioapic_edge_62
+ IDT_SET_VEC 100, ioapic_edge_63
+ ret
+
+ .globl ipi_isr
+INTRENTRY(ipi_isr, ipi_trampoline)
+ call ipi_trampoline
+ retq
+
+ .globl halt_isr
+INTRENTRY(halt_isr, halt_trampoline)
+halt_trampoline:
+ cli
+ hlt
+
+ipi_trampoline:
+ call __ipi_handle_common
+ retq
+
+/* I/O APIC edge ISRs */
+INTRENTRY(ioapic_edge_0, ioapic_common_func)
+INTRENTRY(ioapic_edge_1, ioapic_common_func)
+INTRENTRY(ioapic_edge_2, ioapic_common_func)
+INTRENTRY(ioapic_edge_3, ioapic_common_func)
+INTRENTRY(ioapic_edge_4, ioapic_common_func)
+INTRENTRY(ioapic_edge_5, ioapic_common_func)
+INTRENTRY(ioapic_edge_6, ioapic_common_func)
+INTRENTRY(ioapic_edge_7, ioapic_common_func)
+INTRENTRY(ioapic_edge_8, ioapic_common_func)
+INTRENTRY(ioapic_edge_9, ioapic_common_func)
+INTRENTRY(ioapic_edge_10, ioapic_common_func)
+INTRENTRY(ioapic_edge_11, ioapic_common_func)
+INTRENTRY(ioapic_edge_12, ioapic_common_func)
+INTRENTRY(ioapic_edge_13, ioapic_common_func)
+INTRENTRY(ioapic_edge_14, ioapic_common_func)
+INTRENTRY(ioapic_edge_15, ioapic_common_func)
+INTRENTRY(ioapic_edge_16, ioapic_common_func)
+INTRENTRY(ioapic_edge_17, ioapic_common_func)
+INTRENTRY(ioapic_edge_18, ioapic_common_func)
+INTRENTRY(ioapic_edge_19, ioapic_common_func)
+INTRENTRY(ioapic_edge_20, ioapic_common_func)
+INTRENTRY(ioapic_edge_21, ioapic_common_func)
+INTRENTRY(ioapic_edge_22, ioapic_common_func)
+INTRENTRY(ioapic_edge_23, ioapic_common_func)
+INTRENTRY(ioapic_edge_24, ioapic_common_func)
+INTRENTRY(ioapic_edge_25, ioapic_common_func)
+INTRENTRY(ioapic_edge_26, ioapic_common_func)
+INTRENTRY(ioapic_edge_27, ioapic_common_func)
+INTRENTRY(ioapic_edge_28, ioapic_common_func)
+INTRENTRY(ioapic_edge_29, ioapic_common_func)
+INTRENTRY(ioapic_edge_30, ioapic_common_func)
+INTRENTRY(ioapic_edge_31, ioapic_common_func)
+INTRENTRY(ioapic_edge_32, ioapic_common_func)
+INTRENTRY(ioapic_edge_33, ioapic_common_func)
+INTRENTRY(ioapic_edge_34, ioapic_common_func)
+INTRENTRY(ioapic_edge_35, ioapic_common_func)
+INTRENTRY(ioapic_edge_36, ioapic_common_func)
+INTRENTRY(ioapic_edge_37, ioapic_common_func)
+INTRENTRY(ioapic_edge_38, ioapic_common_func)
+INTRENTRY(ioapic_edge_39, ioapic_common_func)
+INTRENTRY(ioapic_edge_40, ioapic_common_func)
+INTRENTRY(ioapic_edge_41, ioapic_common_func)
+INTRENTRY(ioapic_edge_42, ioapic_common_func)
+INTRENTRY(ioapic_edge_43, ioapic_common_func)
+INTRENTRY(ioapic_edge_44, ioapic_common_func)
+INTRENTRY(ioapic_edge_45, ioapic_common_func)
+INTRENTRY(ioapic_edge_46, ioapic_common_func)
+INTRENTRY(ioapic_edge_47, ioapic_common_func)
+INTRENTRY(ioapic_edge_48, ioapic_common_func)
+INTRENTRY(ioapic_edge_49, ioapic_common_func)
+INTRENTRY(ioapic_edge_50, ioapic_common_func)
+INTRENTRY(ioapic_edge_51, ioapic_common_func)
+INTRENTRY(ioapic_edge_52, ioapic_common_func)
+INTRENTRY(ioapic_edge_53, ioapic_common_func)
+INTRENTRY(ioapic_edge_54, ioapic_common_func)
+INTRENTRY(ioapic_edge_55, ioapic_common_func)
+INTRENTRY(ioapic_edge_56, ioapic_common_func)
+INTRENTRY(ioapic_edge_57, ioapic_common_func)
+INTRENTRY(ioapic_edge_58, ioapic_common_func)
+INTRENTRY(ioapic_edge_59, ioapic_common_func)
+INTRENTRY(ioapic_edge_60, ioapic_common_func)
+INTRENTRY(ioapic_edge_61, ioapic_common_func)
+INTRENTRY(ioapic_edge_62, ioapic_common_func)
+INTRENTRY(ioapic_edge_63, ioapic_common_func)
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index db3ce4c..6bf3af5 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,10 +1,14 @@
+//
// Kernel options
-option SPECTRE_IBRS no
-option SERIAL_DEBUG yes
-
-// Kernel constants
-setval SCHED_NQUEUE 4
-
-// Console attributes
-setval CONSOLE_BG 0x000000
-setval CONSOLE_FG 0x009800
+//
+// 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 CPU_UMIP yes // Enable User-mode Instruction Prevention
+option USER_KMSG no // Show kmsg in user consoles
+option USER_TSC no // Enable 'rdtsc' in user mode
+option CPU_SMEP yes // Supervisor Memory Exec Protection
+option I8042_POLL yes // Use polling for the i8042
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/arch/amd64/isa/i8042.c b/sys/arch/amd64/isa/i8042.c
index 53679aa..095f1f4 100644
--- a/sys/arch/amd64/isa/i8042.c
+++ b/sys/arch/amd64/isa/i8042.c
@@ -33,12 +33,14 @@
#include <sys/syslog.h>
#include <sys/spinlock.h>
#include <sys/param.h>
+#include <sys/ascii.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/queue.h>
#include <dev/acpi/acpi.h>
#include <dev/timer.h>
#include <dev/cons/cons.h>
+#include <dev/dmi/dmi.h>
#include <machine/cpu.h>
#include <machine/pio.h>
#include <machine/isa/i8042var.h>
@@ -51,6 +53,13 @@
#include <string.h>
#include <assert.h>
+/* From kconf(9) */
+#if !defined(__I8042_POLL)
+#define I8042_POLL 0
+#else
+#define I8042_POLL __I8042_POLL
+#endif
+
#define KEY_REP_MAX 2
#define pr_trace(fmt, ...) kprintf("i8042: " fmt, ##__VA_ARGS__)
@@ -58,7 +67,28 @@
#define IO_NOP() inb(0x80)
-static struct spinlock data_lock;
+struct i8042_databuf {
+ uint8_t data[8];
+ size_t len;
+};
+
+/*
+ * This table allows the lookup of extended
+ * scancode bytes.
+ *
+ * XXX: Excludes the 0xE0 byte
+ */
+static struct i8042_databuf i8042_etab[] = {
+ [ I8042_XSC_ENDPR] = {
+ .data = { 0x4F },
+ .len = 1
+ },
+ [I8042_XSC_ENDRL] = {
+ .data = { 0xCF },
+ .len = 1
+ }
+};
+
static struct spinlock isr_lock;
static bool shift_key = false;
static bool capslock = false;
@@ -68,12 +98,13 @@ static struct proc polltd;
static struct timer tmr;
static bool is_init = false;
+static void i8042_ibuf_wait(void);
static int dev_send(bool aux, uint8_t data);
static int i8042_kb_getc(uint8_t sc, char *chr);
-static void i8042_drain(void);
+static void i8042_drain(struct i8042_databuf *res);
static char keytab[] = {
- '\0', '\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
+ '\0', '\x1B', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '\n', '\0', 'a', 's', 'd', 'f', 'g', 'h',
'j', 'k', 'l', ';', '\'', '`', '\0', '\\', 'z', 'x', 'c', 'v',
@@ -103,54 +134,56 @@ kbd_set_leds(uint8_t mask)
dev_send(false, mask);
}
-/*
- * Poll the i8042 status register
- *
- * @bits: Status bits.
- * @pollset: True to poll if set
- */
-static int
-i8042_statpoll(uint8_t bits, bool pollset)
+static void
+i8042_obuf_wait(void)
{
- size_t usec_start, usec;
- size_t elapsed_msec;
- uint8_t val;
- bool tmp;
+ uint8_t status;
- usec_start = tmr.get_time_usec();
for (;;) {
- val = inb(I8042_STATUS);
- tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits);
- usec = tmr.get_time_usec();
- elapsed_msec = (usec - usec_start) / 1000;
-
- IO_NOP();
-
- /* If tmp is set, the register updated in time */
- if (tmp) {
- break;
+ status = inb(I8042_STATUS);
+ if (ISSET(status, I8042_OBUFF)) {
+ return;
}
+ }
+}
- /* Exit with an error if we timeout */
- if (elapsed_msec > I8042_DELAY) {
- return -ETIME;
+static void
+i8042_ibuf_wait(void)
+{
+ uint8_t status;
+
+ for (;;) {
+ status = inb(I8042_STATUS);
+ if (!ISSET(status, I8042_IBUFF)) {
+ return;
}
}
-
- return val;
}
/*
* Drain i8042 internal data registers.
+ *
+ * @res: Pointer for read data to be buffered to
+ *
+ * XXX: The 'res' argument is NULLable
*/
static void
-i8042_drain(void)
+i8042_drain(struct i8042_databuf *res)
{
- spinlock_acquire(&data_lock);
while (ISSET(inb(I8042_STATUS), I8042_OBUFF)) {
- inb(I8042_DATA);
+ if (res == NULL) {
+ inb(I8042_DATA);
+ continue;
+ }
+
+ if (res->len >= sizeof(res->data)) {
+ pr_error("data recieved from i8042 is too big\n");
+ break;
+ }
+
+ res->data[res->len++] = inb(I8042_DATA);
+ tmr.msleep(10);
}
- spinlock_release(&data_lock);
}
/*
@@ -162,34 +195,47 @@ i8042_drain(void)
static void
i8042_write(uint16_t port, uint8_t val)
{
- i8042_statpoll(I8042_IBUFF, false);
+ i8042_ibuf_wait();
outb(port, val);
}
/*
- * Read the i8042 config register
+ * Read from an i8042 register.
+ *
+ * @port: I/O port
+ */
+static uint8_t
+i8042_read(uint16_t port)
+{
+ i8042_obuf_wait();
+ return inb(port);
+}
+
+/*
+ * Read the i8042 controller configuration
+ * byte.
*/
static uint8_t
i8042_read_conf(void)
{
- i8042_drain();
+ uint8_t conf;
+
i8042_write(I8042_CMD, I8042_GET_CONFB);
- i8042_statpoll(I8042_OBUFF, true);
- return inb(I8042_DATA);
+ i8042_obuf_wait();
+ conf = i8042_read(I8042_DATA);
+ return conf;
}
/*
- * Write the i8042 config register
+ * Write a new value to the i8042 controller
+ * configuration byte.
*/
static void
-i8042_write_conf(uint8_t value)
+i8042_write_conf(uint8_t conf)
{
- i8042_drain();
- i8042_statpoll(I8042_IBUFF, false);
i8042_write(I8042_CMD, I8042_SET_CONFB);
- i8042_statpoll(I8042_IBUFF, false);
- i8042_write(I8042_DATA, value);
- i8042_drain();
+ i8042_ibuf_wait();
+ i8042_write(I8042_DATA, conf);
}
/*
@@ -205,14 +251,13 @@ dev_send(bool aux, uint8_t data)
i8042_write(I8042_CMD, I8042_PORT1_SEND);
}
- i8042_statpoll(I8042_IBUFF, false);
i8042_write(I8042_DATA, data);
- i8042_statpoll(I8042_OBUFF, true);
+ i8042_obuf_wait();
return inb(I8042_DATA);
}
-void
-i8042_kb_event(void)
+static int
+i8042_kb_event(void *sp)
{
struct cpu_info *ci;
struct cons_input input;
@@ -232,45 +277,103 @@ i8042_kb_event(void)
input.chr = c;
cons_ibuf_push(&g_root_scr, input);
done:
- ci->irq_mask &= CPU_IRQ(1);
+ ci->irq_mask &= ~CPU_IRQ(1);
spinlock_release(&isr_lock);
- lapic_eoi();
+ return 1; /* handled */
}
static void
i8042_en_intr(void)
{
+ struct intr_hand ih;
uint8_t conf;
- int vec;
-
- i8042_write(I8042_CMD, I8042_DISABLE_PORT0);
- vec = intr_alloc_vector("i8042-kb", IPL_BIO);
- idt_set_desc(vec, IDT_INT_GATE, ISR(i8042_kb_isr), IST_HW_IRQ);
- ioapic_set_vec(KB_IRQ, vec);
- ioapic_irq_unmask(KB_IRQ);
+ ih.func = i8042_kb_event;
+ ih.priority = IPL_BIO;
+ ih.irq = KB_IRQ;
+ intr_register("i8042-kb", &ih);
- /* Setup config bits */
+ /*
+ * Enable the clock of PS/2 port 0 and tell
+ * the controller that we are accepting
+ * interrupts.
+ */
conf = i8042_read_conf();
+ conf &= ~I8042_PORT0_CLK;
conf |= I8042_PORT0_INTR;
- conf &= ~I8042_PORT1_INTR;
i8042_write_conf(conf);
+}
- i8042_write(I8042_CMD, I8042_ENABLE_PORT0);
+/*
+ * Toggle the capslock and LED
+ */
+static void
+capslock_toggle(void)
+{
+ /*
+ * In case we are holding the caps lock button down,
+ * we don't want it to be spam toggled as that would
+ * be pretty strange looking and probably annoying.
+ */
+ if (!capslock_released) {
+ return;
+ }
+
+ capslock_released = false;
+ capslock = !capslock;
+
+ if (!capslock) {
+ kbd_set_leds(0);
+ } else {
+ kbd_set_leds(I8042_LED_CAPS);
+ }
}
+/*
+ * Dump extended data buffer
+ *
+ * @buf: Data
+ */
static void
-esckey_reboot(void)
+i8042_ext_dump(struct i8042_databuf *buf)
{
- syslock();
- kprintf(OMIT_TIMESTAMP "** Machine going down for a reboot\f");
+ if (buf == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < buf->len; ++i) {
+ kprintf(OMIT_TIMESTAMP "%x", buf->data[i]);
+ }
+
+ kprintf(OMIT_TIMESTAMP "\n");
+}
- for (size_t i = 0; i < 3; ++i) {
- kprintf(OMIT_TIMESTAMP ".\f");
- tmr.msleep(1000);
+/*
+ * Used internally by i8042_kb_getc() to acquire
+ * a key from an extended scancode
+ *
+ * @buf: Scancode buf
+ * @chr: Char res
+ *
+ * Returns the extended scancode type on success,
+ * otherwise a less than zero value (see I8042_XSC_*)
+ */
+static int
+i8042_kb_getxc(struct i8042_databuf *buf, char *chr)
+{
+ size_t nelem = NELEM(i8042_etab);
+ struct i8042_databuf *buf_tmp;
+ size_t len;
+
+ for (int i = 0; i < nelem; ++i) {
+ buf_tmp = &i8042_etab[i];
+ len = buf_tmp->len;
+ if (memcmp(buf->data, buf_tmp->data, len) == 0) {
+ return i;
+ }
}
- cpu_reboot(0);
+ return -1;
}
/*
@@ -285,31 +388,16 @@ static int
i8042_kb_getc(uint8_t sc, char *chr)
{
bool release = ISSET(sc, BIT(7));
+ struct i8042_databuf buf = {0};
+ int x_type;
switch (sc) {
- /* Left alt [press] */
- case 0x38:
- esckey_reboot();
- break;
+ case 0x76:
+ *chr = ASCII_ESC;
+ return 0;
/* Caps lock [press] */
case 0x3A:
- /*
- * In case we are holding the caps lock button down,
- * we don't want it to be spam toggled as that would
- * be pretty strange looking and probably annoying.
- */
- if (!capslock_released) {
- return -EAGAIN;
- }
-
- capslock_released = false;
- capslock = !capslock;
-
- if (!capslock) {
- kbd_set_leds(0);
- } else {
- kbd_set_leds(I8042_LED_CAPS);
- }
+ capslock_toggle();
return -EAGAIN;
/* Caps lock [release] */
case 0xBA:
@@ -326,6 +414,26 @@ i8042_kb_getc(uint8_t sc, char *chr)
shift_key = false;
}
return -EAGAIN;
+ /* Extended byte */
+ case 0xE0:
+ /*
+ * Most keyboards have extended scancodes which
+ * consist of multiple bytes to represent certain
+ * special keys. We'll need to give the controller
+ * about 10 ms to refill its buffer.
+ */
+ tmr.msleep(10);
+ i8042_drain(&buf);
+ x_type = i8042_kb_getxc(&buf, chr);
+
+ /* Did we implement it? */
+ if (x_type < 0) {
+ pr_error("unknown xsc: ");
+ i8042_ext_dump(&buf);
+ return -EAGAIN;
+ }
+
+ return -1;
}
if (release) {
@@ -346,43 +454,30 @@ i8042_kb_getc(uint8_t sc, char *chr)
return 0;
}
-static void
-i8042_sync_loop(void)
-{
- /* Wake up the bus */
- outb(I8042_DATA, 0x00);
- i8042_drain();
-
- for (;;) {
- i8042_sync();
- md_pause();
- }
-}
-
/*
* Grabs a key from the keyboard, used typically
* for syncing the machine however can be used
- * to bypass IRQs in case of buggy EC.
+ * to bypass IRQs to prevent lost bytes.
*/
void
i8042_sync(void)
{
static struct spinlock lock;
struct cons_input input;
- uint8_t data;
+ uint8_t data, status;
char c;
if (spinlock_try_acquire(&lock)) {
return;
}
- if (ISSET(quirks, I8042_HOSTILE) && is_init) {
- if (i8042_statpoll(I8042_OBUFF, true) < 0) {
- /* No data ready */
+ if (is_init) {
+ status = inb(I8042_STATUS);
+ if (!ISSET(status, I8042_OBUFF)) {
goto done;
}
- data = inb(I8042_DATA);
+ data = inb(I8042_DATA);
if (i8042_kb_getc(data, &c) == 0) {
input.scancode = data;
input.chr = c;
@@ -399,9 +494,20 @@ i8042_quirk(int mask)
quirks |= mask;
}
+static void
+i8042_sync_loop(void)
+{
+ for (;;) {
+ i8042_obuf_wait();
+ i8042_sync();
+ }
+}
+
static int
i8042_init(void)
{
+ const char *prodver = NULL;
+
/* Try to request a general purpose timer */
if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
pr_error("failed to fetch general purpose timer\n");
@@ -420,6 +526,9 @@ i8042_init(void)
return -ENODEV;
}
+ i8042_write(I8042_CMD, I8042_DISABLE_PORT0);
+ i8042_write(I8042_CMD, I8042_DISABLE_PORT1);
+
/*
* On some thinkpads, e.g., the T420s, the EC implementing
* the i8042 logic likes to play cop and throw NMIs at us
@@ -427,26 +536,33 @@ i8042_init(void)
* etc... As of now, treat the i8042 like a fucking bomb
* if this bit is set.
*/
- if (strcmp(acpi_oemid(), "LENOVO") == 0) {
+ if ((prodver = dmi_prodver()) == NULL) {
+ prodver = "None";
+ }
+ if (strcmp(prodver, "ThinkPad T420s") == 0) {
quirks |= I8042_HOSTILE;
- pr_trace("lenovo device, assuming hostile\n");
+ pr_trace("ThinkPad T420s detected, assuming hostile\n");
pr_trace("disabling irq 1, polling as fallback\n");
- spawn(&polltd, i8042_sync_loop, NULL, 0, NULL);
}
- if (!ISSET(quirks, I8042_HOSTILE)) {
+ /*
+ * If the i8042 has the hostile quirk or we are
+ * configured to poll for events, spawn the polling
+ * thread.
+ */
+ if (!ISSET(quirks, I8042_HOSTILE) && !I8042_POLL) {
/* Enable interrupts */
- i8042_drain();
+ i8042_drain(NULL);
i8042_en_intr();
+ } else if (ISSET(quirks, I8042_HOSTILE) || I8042_POLL) {
+ spawn(&polltd, i8042_sync_loop, NULL, 0, NULL);
+ pr_trace("polling events\n");
}
- if (dev_send(false, 0xFF) == 0xFC) {
- pr_error("kbd self test failure\n");
- return -EIO;
- }
-
+ i8042_write(I8042_CMD, I8042_ENABLE_PORT0);
+ i8042_drain(NULL);
is_init = true;
return 0;
}
-DRIVER_EXPORT(i8042_init);
+DRIVER_EXPORT(i8042_init, "i8042");
diff --git a/sys/arch/amd64/isa/mc1468.c b/sys/arch/amd64/isa/mc1468.c
new file mode 100644
index 0000000..1f3ae1d
--- /dev/null
+++ b/sys/arch/amd64/isa/mc1468.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/driver.h>
+#include <sys/device.h>
+#include <sys/syslog.h>
+#include <fs/devfs.h>
+#include <machine/pio.h>
+#include <machine/cdefs.h>
+#include <string.h>
+
+#define MC1468_REGSEL 0x70
+#define MC1468_DATA 0x71
+
+/* Register A flags */
+#define MC1468_UPDATING BIT(7)
+
+/* Register B flags */
+#define MC1468_DAYSAVE BIT(1)
+#define MC1468_CLOCK24 BIT(2)
+
+static struct cdevsw mc1468_cdevsw;
+
+static uint8_t
+bin_dabble(uint8_t bin)
+{
+ uint8_t retval = 0;
+ uint8_t nibble;
+
+ for (int i = 7; i >= 0; --i) {
+ retval <<= 1;
+ if (bin & (1 << i)) {
+ retval |= 1;
+ }
+
+ for (int j = 0; j < 2; ++j) {
+ nibble = retval & (retval >> (4 * nibble)) & 0x0F;
+ if (nibble >= 5) {
+ retval += 0x03 << (4 * nibble);
+ }
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Read a byte from an MC1468XX register.
+ */
+static uint8_t
+mc1468_read(uint8_t reg)
+{
+ outb(MC1468_REGSEL, reg);
+ return inb(MC1468_DATA);
+}
+
+/*
+ * Write a byte to the MC1468XX register.
+ */
+static void
+mc1468_write(uint8_t reg, uint8_t val)
+{
+ outb(MC1468_REGSEL, reg);
+ outb(MC1468_DATA, val);
+}
+
+/*
+ * Returns true if the MC1468XX is updating
+ * its time registers.
+ */
+static bool
+mc1468_updating(void)
+{
+ uint8_t reg_b;
+
+ reg_b = mc1468_read(0xB);
+ return ISSET(reg_b, MC1468_UPDATING) != 0;
+}
+
+/*
+ * Check if date `a' and date `b' are synced.
+ * Used to make sure a bogus date caused by a
+ * read right before an MC1468XX register
+ * update doesn't occur.
+ */
+static bool
+mc1468_date_synced(struct date *a, struct date *b)
+{
+ if (a->year != b->year)
+ return false;
+ if (a->month != b->month)
+ return false;
+ if (a->day != b->day)
+ return false;
+ if (a->sec != b->sec)
+ return false;
+ if (a->min != b->min)
+ return false;
+ if (a->hour != b->hour)
+ return false;
+
+ return true;
+}
+
+/*
+ * Sometimes the clock chip may encode the
+ * date in binary-coded-decimal. This function
+ * converts a date in BCD format to plain binary.
+ */
+static void
+mc1468_bcd_conv(struct date *dp)
+{
+ dp->year = (dp->year & 0x0F) + ((dp->year / 16) * 10);
+ dp->month = (dp->month & 0x0F) + ((dp->month / 16) * 10);
+ dp->day = (dp->day & 0x0F) + ((dp->day / 16) * 10);
+ dp->sec = (dp->sec & 0x0F) + ((dp->sec / 16) * 10);
+ dp->min = (dp->min & 0x0F) + ((dp->min / 16) * 10);
+ dp->hour = (dp->hour & 0x0F) + (((dp->hour & 0x70) / 16) * 10);
+ dp->hour |= dp->hour & 0x80;
+}
+
+/*
+ * Read the time for the clock without syncing
+ * it up.
+ *
+ * XXX: Please use mc1468_get_date() instead as
+ * this function may return inconsistent
+ * values if not used correctly.
+ */
+static void
+__mc1468_get_time(struct date *dp)
+{
+ dp->year = mc1468_read(0x09);
+ dp->month = mc1468_read(0x08);
+ dp->day = mc1468_read(0x07);
+ dp->sec = mc1468_read(0x00);
+ dp->min = mc1468_read(0x02);
+ dp->hour = mc1468_read(0x04);
+}
+
+/*
+ * Write a new time/date to the chip.
+ */
+static void
+mc1468_set_date(const struct date *dp)
+{
+ while (mc1468_updating()) {
+ md_pause();
+ }
+
+ mc1468_write(0x08, bin_dabble(dp->month));
+ mc1468_write(0x07, bin_dabble(dp->day));
+ mc1468_write(0x04, bin_dabble(dp->hour));
+ mc1468_write(0x02, bin_dabble(dp->min));
+ mc1468_write(0x00, bin_dabble(dp->sec));
+}
+
+static int
+mc1468_get_date(struct date *dp)
+{
+ struct date date_cur, date_last;
+ uint8_t reg_b = mc1468_read(0x0B);
+
+ while (mc1468_updating()) {
+ __mc1468_get_time(&date_last);
+ }
+
+ /*
+ * Get the current date and time.
+ *
+ * XXX: The date and time returned by __mc1468_get_time()
+ * may at times be out of sync, read it twice to
+ * make sure everything is synced up.
+ */
+ do {
+ while (mc1468_updating()) {
+ md_pause();
+ }
+ __mc1468_get_time(&date_last);
+ date_cur.year = date_last.year;
+ date_cur.month = date_last.month;
+ date_cur.day = date_last.day;
+ date_cur.sec = date_last.sec;
+ date_cur.min = date_last.min;
+ date_cur.hour = date_last.hour;
+ } while (!mc1468_date_synced(&date_cur, &date_last));
+
+ /* Is this in BCD? */
+ if (!ISSET(reg_b, 0x04)) {
+ mc1468_bcd_conv(&date_cur);
+ }
+
+ /* 24-hour mode? */
+ if (ISSET(reg_b, MC1468_CLOCK24)) {
+ date_cur.hour = ((date_cur.hour & 0x7F) + 12) % 24;
+ }
+
+ date_cur.year += 2000;
+ *dp = date_cur;
+ return 0;
+}
+
+static int
+mc1468_dev_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ struct date d;
+ size_t len = sizeof(d);
+
+ if (sio->len > len) {
+ sio->len = len;
+ }
+
+ mc1468_get_date(&d);
+ memcpy(sio->buf, &d, sio->len);
+ return sio->len;
+}
+
+static int
+mc1468_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ struct date d;
+ size_t len = sizeof(d);
+
+ if (sio->len > len) {
+ sio->len = len;
+ }
+
+ memcpy(&d, sio->buf, sio->len);
+ mc1468_set_date(&d);
+ return sio->len;
+}
+
+static int
+mc1468_init(void)
+{
+ char devname[] = "rtc";
+ devmajor_t major;
+ dev_t dev;
+
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &mc1468_cdevsw);
+ devfs_create_entry(devname, major, dev, 0444);
+ return 0;
+}
+
+static struct cdevsw mc1468_cdevsw = {
+ .read = mc1468_dev_read,
+ .write = mc1468_dev_write,
+};
+
+DRIVER_EXPORT(mc1468_init, "mc1468");
diff --git a/sys/arch/amd64/isa/spkr.c b/sys/arch/amd64/isa/spkr.c
index b1bd2a2..c96e5f9 100644
--- a/sys/arch/amd64/isa/spkr.c
+++ b/sys/arch/amd64/isa/spkr.c
@@ -30,14 +30,60 @@
#include <sys/cdefs.h>
#include <sys/errno.h>
#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/driver.h>
+#include <fs/devfs.h>
#include <dev/timer.h>
#include <machine/isa/spkr.h>
#include <machine/isa/i8254.h>
#include <machine/pio.h>
+#include <string.h>
#define DIVIDEND 1193180
#define CTRL_PORT 0x61
+static struct cdevsw beep_cdevsw;
+
+/*
+ * Write to the pcspkr
+ *
+ * Bits 15:0 - frequency (hz)
+ * Bits 31:16 - duration (msec)
+ */
+static int
+dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ uint32_t payload = 0;
+ uint16_t hz;
+ uint16_t duration;
+ size_t len = sizeof(payload);
+
+ if (sio->len < len) {
+ return -EINVAL;
+ }
+
+ memcpy(&payload, sio->buf, len);
+ hz = payload & 0xFFFF;
+ duration = (payload >> 16) & 0xFFFF;
+ pcspkr_tone(hz, duration);
+ return sio->len;
+}
+
+static int
+beep_init(void)
+{
+ char devname[] = "beep";
+ devmajor_t major;
+ dev_t dev;
+
+ /* Register the device here */
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &beep_cdevsw);
+ devfs_create_entry(devname, major, dev, 0666);
+ return 0;
+}
+
int
pcspkr_tone(uint16_t freq, uint32_t msec)
{
@@ -67,3 +113,10 @@ pcspkr_tone(uint16_t freq, uint32_t msec)
outb(CTRL_PORT, tmp & ~3);
return 0;
}
+
+static struct cdevsw beep_cdevsw = {
+ .read = noread,
+ .write = dev_write
+};
+
+DRIVER_EXPORT(beep_init, "pcspkr");
diff --git a/sys/arch/amd64/pci/pci_machdep.c b/sys/arch/amd64/pci/pci_machdep.c
index 43065b0..5b49a78 100644
--- a/sys/arch/amd64/pci/pci_machdep.c
+++ b/sys/arch/amd64/pci/pci_machdep.c
@@ -33,6 +33,7 @@
#include <sys/mmio.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
+#include <machine/pci/pci.h>
#include <machine/pio.h>
#include <machine/bus.h>
#include <machine/cpu.h>
@@ -73,8 +74,8 @@ pci_get_barreg(struct pci_device *dev, uint8_t bar)
}
}
-pcireg_t
-pci_readl(struct pci_device *dev, uint32_t offset)
+__weak pcireg_t
+md_pci_readl(struct pci_device *dev, uint32_t offset)
{
uint32_t address;
@@ -83,8 +84,8 @@ pci_readl(struct pci_device *dev, uint32_t offset)
return inl(0xCFC) >> ((offset & 3) * 8);
}
-void
-pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+__weak void
+md_pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
{
uint32_t address;
@@ -163,6 +164,7 @@ pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr)
{
volatile uint64_t *tbl;
struct cpu_info *ci;
+ struct intr_hand ih, *ih_res;
uint32_t data, msg_ctl;
uint64_t msg_addr, tmp;
uint16_t tbl_off;
@@ -184,9 +186,14 @@ pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr)
tbl = (void *)((dev->bar[bir] & PCI_BAR_MEMMASK) + MMIO_OFFSET);
tbl = (void *)((char *)tbl + tbl_off);
- /* Get the vector and setup handler */
- vector = intr_alloc_vector(intr->name, IPL_BIO);
- idt_set_desc(vector, IDT_INT_GATE, ISR(intr->handler), 0);
+ ih.func = intr->handler;
+ ih.priority = IPL_BIO;
+ ih.irq = -1;
+ ih_res = intr_register(intr->name, &ih);
+ if (ih_res == NULL) {
+ return -EIO;
+ }
+ vector = ih_res->vector;
/*
* Setup the message data at bits 95:64 of the message
diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC
new file mode 100644
index 0000000..a8e4620
--- /dev/null
+++ b/sys/conf/GENERIC
@@ -0,0 +1,10 @@
+// Kernel options
+option PANIC_SCR no // Clear screen on panic
+
+// Kernel constants
+setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ)
+setval DISK_MAX 16 // Maximum disks to be registered
+
+// Console attributes
+setval CONSOLE_BG 0x000000
+setval CONSOLE_FG 0xB57614
diff --git a/sys/crypto/chacha20.c b/sys/crypto/chacha20.c
new file mode 100644
index 0000000..5c979a2
--- /dev/null
+++ b/sys/crypto/chacha20.c
@@ -0,0 +1,97 @@
+/*
+ * 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 <crypto/chacha20.h>
+
+static const char sigma[16] = "expand 32-byte k";
+
+void chacha20_init(uint32_t state[16], const uint8_t key[32],
+ const uint8_t nonce[12], uint32_t counter)
+{
+ state[0] = ((uint32_t *)sigma)[0];
+ state[1] = ((uint32_t *)sigma)[1];
+ state[2] = ((uint32_t *)sigma)[2];
+ state[3] = ((uint32_t *)sigma)[3];
+
+ for (int i = 0; i < 8; ++i) {
+ state[4 + i] = ((uint32_t *)key)[i];
+ }
+
+ state[12] = counter;
+ state[13] = ((uint32_t *)nonce)[0];
+ state[14] = ((uint32_t *)nonce)[1];
+ state[15] = ((uint32_t *)nonce)[2];
+}
+
+void
+chacha20_block(uint32_t state[16], uint8_t out[64])
+{
+ uint32_t x[16];
+ memcpy(x, state, sizeof(x));
+
+ for (int i = 0; i < 10; i++) {
+
+ QR(x[0], x[4], x[8], x[12]);
+ QR(x[1], x[5], x[9], x[13]);
+ QR(x[2], x[6], x[10], x[14]);
+ QR(x[3], x[7], x[11], x[15]);
+
+ QR(x[0], x[5], x[10], x[15]);
+ QR(x[1], x[6], x[11], x[12]);
+ QR(x[2], x[7], x[8], x[13]);
+ QR(x[3], x[4], x[9], x[14]);
+ }
+
+ for (int i = 0; i < 16; ++i) {
+ x[i] += state[i];
+ ((uint32_t *)out)[i] = x[i];
+ }
+
+ state[12]++;
+}
+
+void
+chacha20_encrypt(uint32_t state[16], uint8_t *in,
+ uint8_t *out, size_t len)
+{
+ uint8_t block[64];
+ size_t offset = 0;
+
+ while (len > 0) {
+ chacha20_block(state, block);
+ size_t n = len > 64 ? 64 : len;
+
+ for (size_t i = 0; i < n; ++i) {
+ out[offset + i] = in ? in[offset + i] ^ block[i] : block[i];
+ }
+
+ offset += n;
+ len -= n;
+ }
+}
diff --git a/sys/crypto/siphash.c b/sys/crypto/siphash.c
new file mode 100644
index 0000000..5df2ad2
--- /dev/null
+++ b/sys/crypto/siphash.c
@@ -0,0 +1,114 @@
+/* <MIT License>
+ Copyright (c) 2013 Marek Majkowski <marek@popcount.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ </MIT License>
+
+ Original location:
+ https://github.com/majek/csiphash/
+
+ Solution inspired by code from:
+ Samuel Neves (supercop/crypto_auth/siphash24/little)
+ djb (supercop/crypto_auth/siphash24/little2)
+ Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)
+*/
+
+#include <crypto/siphash.h>
+#include <stdint.h>
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define _le64toh(x) ((uint64_t)(x))
+#elif defined(_WIN32)
+/* Windows is always little endian, unless you're on xbox360
+ http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */
+# define _le64toh(x) ((uint64_t)(x))
+#elif defined(__APPLE__)
+# include <libkern/OSByteOrder.h>
+# define _le64toh(x) OSSwapLittleToHostInt64(x)
+#else
+
+/* See: http://sourceforge.net/p/predef/wiki/Endianness/ */
+# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# include <sys/endian.h>
+# else
+# include <endian.h>
+# endif
+# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN
+# define _le64toh(x) ((uint64_t)(x))
+# else
+# define _le64toh(x) le64toh(x)
+# endif
+
+#endif
+
+#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define HALF_ROUND(a,b,c,d,s,t) \
+ a += b; c += d; \
+ b = ROTATE(b, s) ^ a; \
+ d = ROTATE(d, t) ^ c; \
+ a = ROTATE(a, 32);
+
+#define DOUBLE_ROUND(v0,v1,v2,v3) \
+ HALF_ROUND(v0,v1,v2,v3,13,16); \
+ HALF_ROUND(v2,v1,v0,v3,17,21); \
+ HALF_ROUND(v0,v1,v2,v3,13,16); \
+ HALF_ROUND(v2,v1,v0,v3,17,21);
+
+uint64_t siphash24(const void *src, unsigned long src_sz, const char key[16]) {
+ const uint64_t *_key = (uint64_t *)key;
+ uint64_t k0 = _le64toh(_key[0]);
+ uint64_t k1 = _le64toh(_key[1]);
+ uint64_t b = (uint64_t)src_sz << 56;
+ const uint64_t *in = (uint64_t*)src;
+
+ uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
+ uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
+ uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
+ uint64_t v3 = k1 ^ 0x7465646279746573ULL;
+
+ while (src_sz >= 8) {
+ uint64_t mi = _le64toh(*in);
+ in += 1; src_sz -= 8;
+ v3 ^= mi;
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ v0 ^= mi;
+ }
+
+ uint64_t t = 0; uint8_t *pt = (uint8_t *)&t; uint8_t *m = (uint8_t *)in;
+ switch (src_sz) {
+ case 7: pt[6] = m[6];
+ case 6: pt[5] = m[5];
+ case 5: pt[4] = m[4];
+ case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break;
+ case 3: pt[2] = m[2];
+ case 2: pt[1] = m[1];
+ case 1: pt[0] = m[0];
+ }
+ b |= _le64toh(t);
+
+ v3 ^= b;
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ v0 ^= b; v2 ^= 0xff;
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ return (v0 ^ v1) ^ (v2 ^ v3);
+}
diff --git a/sys/dev/acpi/uacpi.c b/sys/dev/acpi/uacpi.c
index 7dbcb35..6c2bf50 100644
--- a/sys/dev/acpi/uacpi.c
+++ b/sys/dev/acpi/uacpi.c
@@ -32,6 +32,8 @@
#include <sys/param.h>
#include <sys/syslog.h>
#include <sys/panic.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
#include <dev/timer.h>
#include <uacpi/kernel_api.h>
#include <uacpi/platform/arch_helpers.h>
@@ -41,10 +43,10 @@
#include <machine/cdefs.h>
#include <machine/pio.h>
#include <machine/cpu.h>
+#include <machine/intr.h>
#if defined(__x86_64__)
#include <machine/idt.h>
#include <machine/ioapic.h>
-#include <machine/intr.h>
#endif /* __x86_64__ */
#include <dev/acpi/uacpi.h>
#include <dev/acpi/acpi.h>
@@ -53,22 +55,96 @@
#include <vm/vm.h>
#include <string.h>
+#define pr_trace(fmt, ...) kprintf("acpi: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
typedef struct {
uacpi_io_addr base;
uacpi_size length;
} io_range_t;
+struct uacpi_work {
+ uacpi_work_handler hand;
+ uacpi_handle ctx;
+ TAILQ_ENTRY(uacpi_work) link;
+};
+
+uacpi_status
+uacpi_kernel_schedule_work(uacpi_work_type type, uacpi_work_handler h, uacpi_handle ctx);
+
+extern struct proc g_proc0;
+
+static struct proc *event_td;
+static TAILQ_HEAD(, uacpi_work) acpi_gpe_eventq;
+static TAILQ_HEAD(, uacpi_work) acpi_notify_eventq;
+
/*
- * TODO: Schedule a system shutdown
+ * Dispatch ACPI general purpose events from
+ * hardware.
*/
-static uacpi_interrupt_ret
-power_button_handler(uacpi_handle ctx)
+static void
+uacpi_gpe_dispatch(void)
+{
+ struct uacpi_work *work;
+
+ work = TAILQ_FIRST(&acpi_gpe_eventq);
+ if (work == NULL) {
+ return;
+ }
+
+ work->hand(work->ctx);
+ TAILQ_REMOVE(&acpi_gpe_eventq, work, link);
+ dynfree(work);
+}
+
+/*
+ * Dispatch ACPI general notify events.
+ */
+static void
+uacpi_notify_dispatch(void)
+{
+ struct uacpi_work *work;
+
+ work = TAILQ_FIRST(&acpi_notify_eventq);
+ if (work == NULL) {
+ return;
+ }
+
+ work->hand(work->ctx);
+ TAILQ_REMOVE(&acpi_gpe_eventq, work, link);
+ dynfree(work);
+}
+
+static void
+uacpi_event_td(void)
+{
+ for (;;) {
+ uacpi_gpe_dispatch();
+ uacpi_notify_dispatch();
+ sched_yield();
+ }
+}
+
+static void
+shutdown(uacpi_handle ctx)
{
- md_intoff();
kprintf("power button pressed\n");
kprintf("halting machine...\n");
cpu_halt_all();
- return UACPI_INTERRUPT_HANDLED;
+}
+
+static uacpi_interrupt_ret
+power_button_handler(uacpi_handle ctx)
+{
+ md_intoff();
+ uacpi_kernel_schedule_work(UACPI_WORK_GPE_EXECUTION, shutdown, NULL);
+ md_inton();
+
+ for (;;) {
+ md_hlt();
+ }
+
+ __builtin_unreachable();
}
void *
@@ -259,17 +335,16 @@ uacpi_status
uacpi_kernel_install_interrupt_handler(uacpi_u32 irq, uacpi_interrupt_handler fn,
uacpi_handle ctx, uacpi_handle *out_irq_handle)
{
- int vec;
+ struct intr_hand ih;
+
+ ih.func = (void *)fn;
+ ih.priority = IPL_HIGH;
+ ih.irq = irq;
+ if (intr_register("acpi", &ih) == NULL) {
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
-#if defined(__x86_64__)
- vec = intr_alloc_vector("acpi", IPL_HIGH);
- idt_set_desc(vec, IDT_INT_GATE, ISR(fn), IST_HW_IRQ);
- ioapic_set_vec(irq, vec);
- ioapic_irq_unmask(irq);
return UACPI_STATUS_OK;
-#else
- return UACPI_STATUS_UNIMPLEMENTED;
-#endif /* __x86_64__ */
}
uacpi_status
@@ -279,9 +354,28 @@ uacpi_kernel_uninstall_interrupt_handler([[maybe_unused]] uacpi_interrupt_handle
}
uacpi_status
-uacpi_kernel_schedule_work(uacpi_work_type, uacpi_work_handler, uacpi_handle ctx)
+uacpi_kernel_schedule_work(uacpi_work_type type, uacpi_work_handler h, uacpi_handle ctx)
{
- return UACPI_STATUS_UNIMPLEMENTED;
+ struct uacpi_work *work;
+
+ work = dynalloc(sizeof(*work));
+ if (work == NULL) {
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ work->hand = h;
+ work->ctx = ctx;
+
+ switch (type) {
+ case UACPI_WORK_GPE_EXECUTION:
+ TAILQ_INSERT_TAIL(&acpi_gpe_eventq, work, link);
+ break;
+ case UACPI_WORK_NOTIFICATION:
+ TAILQ_INSERT_TAIL(&acpi_notify_eventq, work, link);
+ break;
+ }
+
+ return 0;
}
uacpi_status
@@ -514,9 +608,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;
}
@@ -533,25 +636,25 @@ uacpi_init(void)
ret = uacpi_initialize(0);
if (uacpi_unlikely_error(ret)) {
- kprintf("uacpi init error: %s\n", uacpi_status_to_string(ret));
+ pr_error("uacpi init error: %s\n", uacpi_status_to_string(ret));
return -1;
}
ret = uacpi_namespace_load();
if (uacpi_unlikely_error(ret)) {
- kprintf("uacpi namespace load error: %s\n", uacpi_status_to_string(ret));
+ pr_error("uacpi namespace load error: %s\n", uacpi_status_to_string(ret));
return -1;
}
ret = uacpi_namespace_initialize();
if (uacpi_unlikely_error(ret)) {
- kprintf("uacpi namespace init error: %s\n", uacpi_status_to_string(ret));
+ pr_error("uacpi namespace init error: %s\n", uacpi_status_to_string(ret));
return -1;
}
ret = uacpi_finalize_gpe_initialization();
if (uacpi_unlikely_error(ret)) {
- kprintf("uacpi GPE init error: %s\n", uacpi_status_to_string(ret));
+ pr_error("uacpi GPE init error: %s\n", uacpi_status_to_string(ret));
return -1;
}
@@ -561,11 +664,14 @@ uacpi_init(void)
);
if (uacpi_unlikely_error(ret)) {
- kprintf("failed to install power button event: %s\n",
+ pr_error("failed to install power button event: %s\n",
uacpi_status_to_string(ret)
);
return -1;
}
+ TAILQ_INIT(&acpi_gpe_eventq);
+ TAILQ_INIT(&acpi_notify_eventq);
+ spawn(&g_proc0, uacpi_event_td, NULL, 0, &event_td);
return 0;
}
diff --git a/sys/dev/acpi/uacpi/event.c b/sys/dev/acpi/uacpi/event.c
index 0c58372..62412ac 100644
--- a/sys/dev/acpi/uacpi/event.c
+++ b/sys/dev/acpi/uacpi/event.c
@@ -1054,7 +1054,6 @@ static uacpi_status create_gpe_block(
*/
reg->base_idx = base_idx + (i * EVENTS_PER_GPE_REGISTER);
-
tmp_gas.address = address + i;
ret = uacpi_map_gas_noalloc(&tmp_gas, &reg->status);
if (uacpi_unlikely_error(ret))
diff --git a/sys/dev/acpi/uacpi/resources.c b/sys/dev/acpi/uacpi/resources.c
index a9bcb82..f1a25ec 100644
--- a/sys/dev/acpi/uacpi/resources.c
+++ b/sys/dev/acpi/uacpi/resources.c
@@ -602,7 +602,6 @@ static uacpi_size aml_size_for_serial_connection(
#define ARG1(value) .f2.arg1 = (value)
#define ARG2(value) .f3.arg2 = (value)
-
static const struct uacpi_resource_convert_instruction convert_irq_to_native[] = {
OP(PACKED_ARRAY_16, AML_F(irq, irq_mask), NATIVE_F(irq, irqs),
ARG2(NATIVE_O(irq, num_irqs))),
diff --git a/sys/dev/acpi/uacpi/tables.c b/sys/dev/acpi/uacpi/tables.c
index df7d7b9..3314fd1 100644
--- a/sys/dev/acpi/uacpi/tables.c
+++ b/sys/dev/acpi/uacpi/tables.c
@@ -1211,7 +1211,6 @@ static void gas_init_system_io(
gas->access_size = 0;
}
-
struct register_description {
uacpi_size offset, xoffset;
uacpi_size length_offset;
diff --git a/sys/dev/acpi/uacpi/utilities.c b/sys/dev/acpi/uacpi/utilities.c
index c7ca20a..059c574 100644
--- a/sys/dev/acpi/uacpi/utilities.c
+++ b/sys/dev/acpi/uacpi/utilities.c
@@ -963,7 +963,6 @@ static uacpi_iteration_decision find_one_device(
return ctx->cb(ctx->user, node, depth);
}
-
uacpi_status uacpi_find_devices_at(
uacpi_namespace_node *parent, const uacpi_char *const *hids,
uacpi_iteration_callback cb, void *user
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c
index b89727f..0bb33b1 100644
--- a/sys/dev/cons/cons.c
+++ b/sys/dev/cons/cons.c
@@ -37,6 +37,7 @@
#include <dev/cons/font.h>
#include <dev/cons/cons.h>
#include <fs/devfs.h>
+#include <fs/ctlfs.h>
#include <vm/dynalloc.h>
#include <string.h>
@@ -44,7 +45,7 @@
cons_draw_cursor((SCR), (SCR)->bg)
#define SHOW_CURSOR(SCR) \
- cons_draw_cursor((SCR), (SCR)->fg)
+ cons_draw_cursor((SCR), rgb_invert((SCR)->bg))
/* Console background from kconf */
#if defined(__CONSOLE_BG)
@@ -62,10 +63,27 @@
struct cons_screen g_root_scr = {0};
static struct cdevsw cons_cdevsw;
+static struct ctlops cons_feat_ctl;
+static struct ctlops cons_attr_ctl;
static void cons_draw_cursor(struct cons_screen *scr, uint32_t color);
static int cons_handle_special(struct cons_screen *scr, char c);
-static void cons_clear_scr(struct cons_screen *scr, uint32_t bg);
+
+static uint32_t
+rgb_invert(uint32_t rgb)
+{
+ uint8_t r, g, b;
+ uint32_t ret;
+
+ r = (rgb >> 16) & 0xFF;
+ g = (rgb >> 8) & 0xFF;
+ b = rgb & 0xFF;
+
+ ret = (255 - r) << 16;
+ ret |= (255 - g) << 8;
+ ret |= 255 - b;
+ return ret;
+}
/*
* Render a character onto the screen.
@@ -91,9 +109,9 @@ cons_draw_char(struct cons_screen *scr, struct cons_char ch)
y = ch.y;
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1), y + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1) - cx, y + cy);
- scr->fb_mem[idx] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
+ scr->fb_mem[idx--] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
}
}
}
@@ -158,6 +176,17 @@ cons_handle_special(struct cons_screen *scr, char c)
}
switch (c) {
+ case ASCII_HT:
+ HIDE_CURSOR(scr);
+ scr->curs_col += 4;
+ scr->ch_col += 4;
+ if (scr->ch_col >= scr->ncols - 1) {
+ cons_handle_special(scr, '\n');
+ }
+ SHOW_CURSOR(scr);
+ return 0;
+ case ASCII_NUL:
+ return 0;
case ASCII_BS:
bp = scr->ob[scr->ch_row];
if (bp->head > bp->tail) {
@@ -165,27 +194,21 @@ cons_handle_special(struct cons_screen *scr, char c)
}
HIDE_CURSOR(scr);
- --scr->ch_col;
- --scr->curs_col;
+ if (scr->ch_col > 0 && scr->curs_col > 0) {
+ --scr->ch_col;
+ --scr->curs_col;
+ }
SHOW_CURSOR(scr);
return 0;
case ASCII_LF:
- HIDE_CURSOR(scr);
-
/* Are we past screen width? */
if (scr->ch_row >= scr->nrows - 1) {
cons_clear_scr(scr, scr->bg);
- cons_flush(scr);
- scr->ch_col = 0;
- scr->ch_row = 0;
-
- /* Update cursor */
- scr->curs_row = 0;
- scr->curs_col = 0;
- SHOW_CURSOR(scr);
return 0;
}
+ HIDE_CURSOR(scr);
+
/* Make a newline */
cons_flush(scr);
++scr->ch_row;
@@ -212,8 +235,14 @@ cons_handle_special(struct cons_screen *scr, char c)
static void
cons_draw_cursor(struct cons_screen *scr, uint32_t color)
{
+ struct console_feat *featp;
size_t idx;
+ featp = &scr->feat;
+ if (!featp->show_curs) {
+ color = scr->bg;
+ }
+
/* Past screen width? */
if (scr->curs_col >= scr->ncols) {
scr->curs_col = 0;
@@ -221,9 +250,9 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
}
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, scr->curs_col * FONT_WIDTH, (scr->curs_row * FONT_HEIGHT) + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, (scr->curs_col * FONT_WIDTH) + cx, (scr->curs_row * FONT_HEIGHT) + cy);
- scr->fb_mem[idx] = color;
+ scr->fb_mem[idx++] = color;
}
}
}
@@ -234,34 +263,88 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
* @scr: Screen to clear.
* @bg: Color to clear it to.
*/
-static void
+void
cons_clear_scr(struct cons_screen *scr, uint32_t bg)
{
struct fbdev fbdev = scr->fbdev;
- struct cons_buf *bp;
+
+ cons_flush(scr);
+ HIDE_CURSOR(scr);
+
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
for (size_t i = 0; i < fbdev.height * fbdev.pitch; ++i) {
scr->fb_mem[i] = bg;
}
- bp = scr->ob[scr->nrows - 1];
- bp->flags |= CONS_BUF_CLEAN;
+ SHOW_CURSOR(scr);
+
}
/*
- * Character device function.
+ * Quickly put a character on the screen.
+ * XXX: Does not acquire the screen's lock or show/hide the cursor.
+ *
+ * @scr: Screen.
+ * @c: Character to draw.
*/
-static int
-dev_write(dev_t dev, struct sio_txn *sio, int flags)
+static void
+cons_fast_putch(struct cons_screen *scr, char c)
{
- char *p;
+ struct cons_char cc;
+ struct cons_buf *bp;
+ int ansi;
+
+ ansi = ansi_feed(&scr->ansi_s, c);
+ if (ansi > 0) {
+ c = ASCII_NUL;
+ } else if (ansi < 0) {
+ c = ASCII_NUL;
+ }
+
+ /* Handle specials */
+ if (cons_handle_special(scr, c) == 0) {
+ return;
+ }
+
+ /* Create a new character */
+ cc.c = c;
+ cc.fg = scr->fg;
+ cc.bg = scr->bg;
+ cc.x = scr->ch_col * FONT_WIDTH;
+ cc.y = scr->ch_row * FONT_HEIGHT;
+
+ /* Push our new character */
+ bp = scr->ob[scr->ch_row];
+ bp->flags &= ~CONS_BUF_CLEAN;
+ cons_obuf_push(bp, cc);
+ ++scr->ch_col;
- p = sio->buf;
+ /* Check screen bounds */
+ if (cc.x >= (scr->ncols * FONT_WIDTH) - 1) {
+ scr->ch_col = 0;
+ ++scr->ch_row;
+ }
- for (size_t i = 0; i < sio->len; ++i) {
- cons_putch(&g_root_scr, p[i]);
+ ++scr->curs_col;
+ if (scr->curs_col > scr->ncols - 1) {
+ scr->curs_col = 0;
+ if (scr->curs_row < scr->nrows)
+ ++scr->curs_row;
}
+}
+/*
+ * Character device function.
+ */
+static int
+dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ cons_attach();
+ cons_putstr(&g_root_scr, sio->buf, sio->len);
cons_flush(&g_root_scr);
return sio->len;
}
@@ -272,6 +355,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;
@@ -285,12 +369,12 @@ 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;
}
- spinlock_acquire(&g_root_scr.lock);
+ cons_attach();
for (;;) {
/* Buffer too small */
if (n == 0) {
@@ -302,12 +386,11 @@ 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;
}
}
- spinlock_release(&g_root_scr.lock);
return sio->len;
}
@@ -338,6 +421,195 @@ cons_init_bufs(struct cons_screen *scr)
return 0;
}
+static int
+ctl_feat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ featp = &scr->feat;
+ if (sio->len > sizeof(*featp)) {
+ sio->len = sizeof(*featp);
+ }
+
+ memcpy(sio->buf, featp, sio->len);
+ return sio->len;
+}
+
+static int
+ctl_feat_write(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp, oldfeat;
+
+ featp = &scr->feat;
+ oldfeat = *featp;
+ if (sio->len > sizeof(*featp)) {
+ sio->len = sizeof(*featp);
+ }
+
+ memcpy(featp, sio->buf, sio->len);
+
+ /*
+ * If we are suddenly trying to reset the cursor
+ * status, redraw it.
+ */
+ if (featp->show_curs != oldfeat.show_curs) {
+ if (featp->show_curs == 0) {
+ HIDE_CURSOR(scr);
+ } else {
+ SHOW_CURSOR(scr);
+ }
+ }
+ return sio->len;
+}
+
+static int
+ctl_attr_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_attr *attrp;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ attrp = &scr->attr;
+ if (sio->len > sizeof(*attrp)) {
+ sio->len = sizeof(*attrp);
+ }
+
+ memcpy(sio->buf, attrp, sio->len);
+ return sio->len;
+}
+
+static int
+ctl_attr_write(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_attr *attrp;
+
+ attrp = &scr->attr;
+ if (sio->len > sizeof(*attrp)) {
+ sio->len = sizeof(*attrp);
+ }
+
+ spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
+ memcpy(attrp, sio->buf, sio->len);
+
+ /* Clip the x/y positions */
+ if (attrp->cursor_x >= scr->ncols)
+ attrp->cursor_x = scr->ncols - FONT_WIDTH;
+ if (attrp->cursor_y >= scr->nrows)
+ attrp->cursor_y = scr->nrows - FONT_HEIGHT;
+
+ /* Update cursor */
+ scr->curs_col = attrp->cursor_x;
+ scr->curs_row = attrp->cursor_y;
+ scr->ch_col = attrp->cursor_x;
+ scr->ch_row = attrp->cursor_y;
+ SHOW_CURSOR(scr);
+
+ spinlock_release(&scr->lock);
+ return sio->len;
+}
+
+/*
+ * Detach the currently running process from the
+ * console.
+ */
+int
+cons_detach(void)
+{
+ struct cons_screen *scr;
+
+ scr = &g_root_scr;
+ if (scr->atproc == NULL) {
+ return 0;
+ }
+ if (scr->atproc_lock == NULL) {
+ return 0;
+ }
+
+ scr = &g_root_scr;
+ scr->atproc = NULL;
+ mutex_release(scr->atproc_lock);
+ return 0;
+}
+
+/*
+ * Attach the current process to the
+ * console.
+ */
+int
+cons_attach(void)
+{
+ struct cons_screen *scr;
+ struct proc *td, *atproc;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+
+ scr = &g_root_scr;
+ if (scr->atproc_lock == NULL) {
+ return 0;
+ }
+
+ scr = &g_root_scr;
+ atproc = scr->atproc;
+
+ if (atproc != NULL) {
+ if (atproc->pid == td->pid) {
+ return 0;
+ }
+
+ /*
+ * Do not release this here as we want
+ * any other process that tries to attach
+ * to wait.
+ */
+ mutex_acquire(scr->atproc_lock, 0);
+ }
+
+ scr->atproc = td;
+ return 0;
+}
+
+/*
+ * Reset console color.
+ */
+void
+cons_reset_color(struct cons_screen *scr)
+{
+ g_root_scr.fg = CONSOLE_FG;
+ g_root_scr.bg = CONSOLE_BG;
+}
+
+void
+cons_update_color(struct cons_screen *scr, uint32_t fg, uint32_t bg)
+{
+ scr->fg = fg;
+ scr->bg = bg;
+}
+
+void
+cons_reset_cursor(struct cons_screen *scr)
+{
+ HIDE_CURSOR(scr);
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
+ SHOW_CURSOR(scr);
+}
+
/*
* Put a character on the screen.
*
@@ -347,47 +619,37 @@ cons_init_bufs(struct cons_screen *scr)
int
cons_putch(struct cons_screen *scr, char c)
{
- struct cons_buf *bp;
- struct cons_char cc;
- size_t max_width;
-
spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- /* Handle specials */
- if (cons_handle_special(scr, c) == 0) {
- goto done;
- }
+ cons_fast_putch(scr, c);
- HIDE_CURSOR(scr);
+ SHOW_CURSOR(scr);
+ spinlock_release(&scr->lock);
+ return 0;
+}
- /* Create a new character */
- cc.c = c;
- cc.fg = scr->fg;
- cc.bg = scr->bg;
- cc.x = scr->ch_col * FONT_WIDTH;
- cc.y = scr->ch_row * FONT_HEIGHT;
+/*
+ * Put a string on the screen.
+ *
+ * @scr: Screen.
+ * @s: String to draw.
+ * @l: Length of s.
+ */
+int
+cons_putstr(struct cons_screen *scr, const char *s, size_t len)
+{
+ const char *p = s;
- /* Push our new character */
- bp = scr->ob[scr->ch_row];
- bp->flags &= ~CONS_BUF_CLEAN;
- cons_obuf_push(bp, cc);
- ++scr->ch_col;
+ spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- /* Check screen bounds */
- max_width = scr->ncols * FONT_WIDTH;
- if (cc.x >= max_width - 1) {
- scr->ch_col = 0;
- ++scr->ch_row;
+ while (len--) {
+ cons_fast_putch(scr, *p);
+ ++p;
}
- ++scr->curs_col;
- if (scr->curs_col > scr->ncols - 1) {
- scr->curs_col = 0;
- if (scr->curs_row < scr->nrows)
- ++scr->curs_row;
- }
SHOW_CURSOR(scr);
-done:
spinlock_release(&scr->lock);
return 0;
}
@@ -396,7 +658,11 @@ void
cons_init(void)
{
struct fbdev fbdev = fbdev_get();
+ struct console_feat *featp;
+ featp = &g_root_scr.feat;
+ featp->ansi_esc = 1;
+ featp->show_curs = 1;
g_root_scr.ch_col = 0;
g_root_scr.ch_row = 0;
g_root_scr.fg = CONSOLE_FG;
@@ -405,6 +671,8 @@ cons_init(void)
g_root_scr.nrows = fbdev.height / FONT_HEIGHT;
g_root_scr.ncols = fbdev.width / FONT_WIDTH;
g_root_scr.fbdev = fbdev;
+ g_root_scr.atproc = NULL;
+ g_root_scr.atproc_lock = NULL;
memset(&g_root_scr.lock, 0, sizeof(g_root_scr.lock));
cons_init_bufs(&g_root_scr);
SHOW_CURSOR(&g_root_scr);
@@ -417,6 +685,7 @@ void
cons_expose(void)
{
static int once = 0;
+ struct ctlfs_dev ctl;
char devname[] = "console";
devmajor_t major;
dev_t dev;
@@ -426,11 +695,28 @@ cons_expose(void)
return;
}
+ /* Init the attached proc mutex lock */
+ g_root_scr.atproc_lock = mutex_new("console0");
+
/* Register the device here */
major = dev_alloc_major();
dev = dev_alloc(major);
dev_register(major, dev, &cons_cdevsw);
devfs_create_entry(devname, major, dev, 0444);
+
+ /* Register feat ctl */
+ ctl.mode = 0666;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &cons_feat_ctl;
+ ctlfs_create_entry("feat", &ctl);
+
+ /* Register attr ctl */
+ ctl.mode = 0666;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &cons_attr_ctl;
+ ctlfs_create_entry("attr", &ctl);
once ^= 1;
}
@@ -438,3 +724,13 @@ static struct cdevsw cons_cdevsw = {
.read = dev_read,
.write = dev_write
};
+
+static struct ctlops cons_feat_ctl = {
+ .read = ctl_feat_read,
+ .write = ctl_feat_write
+};
+
+static struct ctlops cons_attr_ctl = {
+ .read = ctl_attr_read,
+ .write = ctl_attr_write
+};
diff --git a/sys/dev/cons/cons_ansi.c b/sys/dev/cons/cons_ansi.c
new file mode 100644
index 0000000..bd78d9a
--- /dev/null
+++ b/sys/dev/cons/cons_ansi.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/console.h>
+#include <dev/cons/cons.h>
+#include <dev/cons/ansi.h>
+#include <string.h>
+
+__always_inline static inline bool
+is_valid_color(int c)
+{
+ return c >= '0' && c <= '7';
+}
+
+static inline void
+ansi_reset(struct ansi_state *statep)
+{
+ memset(statep, 0, sizeof(*statep));
+}
+
+/*
+ * Feed a byte into the ANSI escape sequence
+ * state machine.
+ *
+ * @statep: State machine pointer.
+ * @c: Byte to feed.
+ *
+ * On success, `c' is returned. On failure,
+ * 0 is returned. Values less than 0 indicate
+ * success with console attributes updated
+ * (ANSI_UPDATE_*).
+ */
+int
+ansi_feed(struct ansi_state *statep, char c)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp;
+
+ /* Standard colors */
+ static uint32_t colortab[] = {
+ ANSI_BLACK, ANSI_RED,
+ ANSI_GREEN, ANSI_YELLOW,
+ ANSI_BLUE, ANSI_MAGENTA,
+ ANSI_CYAN, ANSI_WHITE
+ };
+
+ featp = &scr->feat;
+ if (!featp->ansi_esc) {
+ return 0;
+ }
+
+ /*
+ * Handle the control sequence introducer
+ * bytes.
+ */
+ switch (statep->csi) {
+ case 0: /* '\033' */
+ if (c != '\033') {
+ return 0;
+ }
+ statep->csi = 1;
+ statep->prev = c;
+ return c;
+ case 1: /* '[' */
+ if (c != '[') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->csi = 2;
+ statep->prev = c;
+ return c;
+ case 2:
+ if (c == '2') {
+ statep->csi = 3;
+ statep->prev = c;
+ return c;
+ }
+ break;
+ case 3:
+ /* Did we get '\033[2J' ? */
+ if (statep->prev == '2' && c == 'J') {
+ cons_clear_scr(scr, g_root_scr.bg);
+ ansi_reset(statep);
+ return ANSI_UPDATE_CURSOR;
+ }
+ break;
+ }
+
+ if (!statep->set_fg && !statep->set_bg) {
+ /* Reset attributes? */
+ if (statep->reset_color) {
+ ansi_reset(statep);
+ cons_reset_color(scr);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Mark attributes to be reset? */
+ if (c == '0') {
+ statep->reset_color = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect foreground */
+ if (c != '3') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->set_fg = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_fg && c != ';') {
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the foreground */
+ statep->fg = colortab[c - '0'];
+ statep->set_bg = 1;
+ statep->set_fg = 0;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_bg) {
+ if (c == ';') {
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect '4' after ';' */
+ if (statep->prev == ';' && c != '4') {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ if (c == 'm') {
+ cons_update_color(scr, statep->fg, statep->bg);
+ ansi_reset(statep);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the background */
+ statep->bg = colortab[c - '0'];
+ statep->prev = c;
+ return c;
+ }
+
+ ansi_reset(statep);
+ return 0;
+}
diff --git a/sys/dev/dcdr/cache.c b/sys/dev/dcdr/cache.c
index c44c8ea..33f977e 100644
--- a/sys/dev/dcdr/cache.c
+++ b/sys/dev/dcdr/cache.c
@@ -126,6 +126,20 @@ struct dcd *
dcdr_cachein(struct dcdr *dcdr, void *block, off_t lba)
{
struct dcd *dcd, *tmp;
+ struct dcdr_lookup check;
+ int status;
+
+ /*
+ * If there is already a block within this
+ * DCDR, then we simply need to copy the
+ * new data into the old DCD.
+ */
+ status = dcdr_lookup(dcdr, lba, &check);
+ if (status == 0) {
+ dcd = check.dcd_res;
+ memcpy(dcd->block, block, dcdr->bsize);
+ return dcd;
+ }
dcd = dynalloc(sizeof(*dcd));
if (dcd == NULL) {
diff --git a/sys/dev/dmi/dmi.c b/sys/dev/dmi/dmi.c
new file mode 100644
index 0000000..73a9ab7
--- /dev/null
+++ b/sys/dev/dmi/dmi.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/limine.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/driver.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
+#include <dev/dmi/dmi.h>
+#include <dev/dmi/dmivar.h>
+#include <dev/acpi/tables.h>
+#include <fs/ctlfs.h>
+#include <string.h>
+
+#define DMI_BIOS_INFO 0
+#define DMI_SYSTEM_INFO 1
+#define DMI_PROCESSOR_INFO 4
+#define DMI_END_OF_TABLE 127
+
+/* String offsets */
+#define BIOSINFO_VENDOR 0x01
+#define SYSINFO_PRODUCT 0x02
+#define SYSINFO_VERSION 0x03
+#define SYSINFO_FAMILY 0x06
+#define PROCINFO_MANUFACT 0x02
+#define PROCINFO_VERSION 0x03
+#define PROCINFO_PARTNO 0x06
+
+static struct limine_smbios_request smbios_req = {
+ .id = LIMINE_SMBIOS_REQUEST,
+ .revision = 0
+};
+
+/* DMI/SMBIOS structure header */
+struct __packed dmi_shdr {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+} *hdrs[DMI_END_OF_TABLE + 1];
+
+/*
+ * Grab a structure header from a type
+ *
+ * @type: A DMI structure type to find
+ *
+ * Returns NULL if not found.
+ */
+static inline struct dmi_shdr *
+dmi_shdr(uint8_t type)
+{
+ struct dmi_shdr *hdr;
+
+ hdr = hdrs[type];
+ if (hdr == NULL) {
+ return NULL;
+ }
+
+ return hdr;
+}
+
+/*
+ * Grab a string from the DMI/SMBIOS formatted
+ * section.
+ *
+ * @hdr: DMI header to lookup string index
+ * @index: 1-based string index
+ *
+ * See section 6.1.3 of the DTMF SMBIOS Reference
+ * Specification
+ */
+static const char *
+dmi_str_index(struct dmi_shdr *hdr, uint8_t index)
+{
+ const char *strdata = PTR_OFFSET(hdr, hdr->length);
+
+ for (uint8_t i = 1; *strdata != '\0'; ++i) {
+ if (i == index) {
+ return strdata;
+ }
+
+ strdata += strlen(strdata) + 1;
+ }
+
+ return NULL;
+}
+
+/*
+ * Get the DMI/SMBIOS structure size from a
+ * header.
+ */
+static size_t
+dmi_struct_size(struct dmi_shdr *hdr)
+{
+ const char *strdata;
+ size_t i = 1;
+
+ strdata = PTR_OFFSET(hdr, hdr->length);
+ while (strdata[i - 1] != '\0' || strdata[i] != '\0') {
+ ++i;
+ }
+
+ return hdr->length + i + 1;
+}
+
+/*
+ * Get the vendor string from the DMI/SMBIOS BIOS
+ * info structure
+ *
+ * Returns NULL if not found.
+ */
+const char *
+dmi_vendor(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_BIOS_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, BIOSINFO_VENDOR);
+}
+
+/*
+ * Return the product string from the DMI/SMBIOS System
+ * Info structure
+ *
+ * Returns NULL if not found.
+ */
+const char *
+dmi_product(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_PRODUCT);
+}
+
+/*
+ * Return the product version from the DMI/SMBIOS
+ * System Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_prodver(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_VERSION);
+}
+
+/*
+ * Return the product family from the DMI/SMBIOS
+ * System Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_prodfam(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_FAMILY);
+}
+
+/*
+ * Return the CPU manufacturer string from the
+ * DMI/SMBIOS Processor Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_cpu_manufact(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_PROCESSOR_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, PROCINFO_MANUFACT);
+}
+
+/*
+ * Return the CPU version string from the
+ * DMI/SMBIOS Processor Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_cpu_version(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_PROCESSOR_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, PROCINFO_VERSION);
+}
+
+static void
+dmi_init_ctl(void)
+{
+ struct ctlfs_dev ctl;
+ char ctlname[] = "dmi";
+
+ /* Create '/ctl/dmi/board' */
+ ctl.mode = 0444;
+ ctlfs_create_node(ctlname, &ctl);
+ ctl.devname = ctlname;
+ ctl.ops = &g_ctl_board_ident;
+ ctlfs_create_entry("board", &ctl);
+}
+
+static int
+dmi_init(void)
+{
+ struct dmi_entry32 *entry32 = NULL;
+ struct limine_smbios_response *resp = smbios_req.response;
+ struct dmi_entry64 *entry64 = NULL;
+ struct dmi_shdr *hdr = NULL;
+ size_t scount = 0, smax_len = 0;
+ size_t nbytes = 0, cur_nbytes = 0;
+
+ if (resp == NULL) {
+ return -ENODEV;
+ }
+ if (resp->entry_32 == 0 && resp->entry_64 == 0) {
+ return -ENODEV;
+ }
+
+ if (resp->entry_64 != 0) {
+ entry64 = (void *)resp->entry_64;
+ hdr = PHYS_TO_VIRT(entry64->addr);
+ smax_len = entry64->max_size;
+ } else if (resp->entry_32 != 0) {
+ entry32 = (void *)(uint64_t)resp->entry_32;
+ hdr = PHYS_TO_VIRT((uint64_t)entry32->addr);
+ scount = entry32->nstruct;
+ } else {
+ return -ENODEV;
+ }
+
+ memset(hdrs, 0, sizeof(hdrs));
+ for (size_t i = 0; i < scount; ++i) {
+ if (hdr->type == DMI_END_OF_TABLE) {
+ break;
+ }
+
+ if (hdr->type < NELEM(hdrs)) {
+ hdrs[hdr->type] = hdr;
+ }
+ cur_nbytes = dmi_struct_size(hdr);
+ if (smax_len > 0 && (nbytes + cur_nbytes) >= smax_len) {
+ break;
+ }
+
+ nbytes += cur_nbytes;
+ hdr = PTR_OFFSET(hdr, cur_nbytes);
+ }
+
+ dmi_init_ctl();
+ return 0;
+}
+
+DRIVER_EXPORT(dmi_init, "dmi");
diff --git a/sys/dev/dmi/dmi_board.c b/sys/dev/dmi/dmi_board.c
new file mode 100644
index 0000000..23709bd
--- /dev/null
+++ b/sys/dev/dmi/dmi_board.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/dmi.h>
+#include <dev/dmi/dmi.h>
+#include <dev/dmi/dmivar.h>
+#include <fs/ctlfs.h>
+#include <string.h>
+
+extern struct ctlops ctl_cpu_ident;
+
+static int
+board_ctl_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct dmi_board board;
+ const char *cpu_manuf, *prodver;
+ const char *product, *vendor;
+ const char *cpu_ver, *p;
+ size_t len;
+
+ if (cdp == NULL || sio == NULL) {
+ return -EINVAL;
+ }
+ /* Cannot copy zero bytes */
+ if (sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* Check offset and clamp length */
+ if (sio->offset >= sizeof(board)) {
+ return 0;
+ }
+ if ((sio->offset + sio->len) > sizeof(board)) {
+ sio->len = sizeof(board);
+ }
+
+ memset(&board, 0, sizeof(board));
+ cpu_ver = dmi_cpu_version();
+ if (cpu_ver != NULL) {
+ len = strlen(cpu_ver);
+ memcpy(board.cpu_version, cpu_ver, len);
+ }
+
+ prodver = dmi_prodver();
+ if (prodver != NULL) {
+ len = strlen(prodver);
+ memcpy(board.version, prodver, len);
+ }
+
+ cpu_manuf = dmi_cpu_manufact();
+ if (cpu_manuf != NULL) {
+ len = strlen(cpu_manuf);
+ memcpy(board.cpu_manuf, cpu_manuf, len);
+ }
+
+ product = dmi_product();
+ if (product != NULL) {
+ len = strlen(product);
+ memcpy(board.product, product, len);
+ }
+
+ vendor = dmi_vendor();
+ if (vendor != NULL) {
+ len = strlen(vendor);
+ memcpy(board.vendor, vendor, len);
+ }
+
+ p = (char *)&board;
+ memcpy(sio->buf, &p[sio->offset], sio->len);
+ return sio->len;
+}
+
+struct ctlops g_ctl_board_ident = {
+ .read = board_ctl_read,
+ .write = NULL
+};
diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c
index 8b72cde..d994ef1 100644
--- a/sys/dev/ic/ahci.c
+++ b/sys/dev/ic/ahci.c
@@ -36,15 +36,18 @@
#include <sys/param.h>
#include <sys/bitops.h>
#include <sys/mmio.h>
+#include <sys/disk.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
#include <dev/timer.h>
#include <dev/ic/ahcivar.h>
#include <dev/ic/ahciregs.h>
+#include <dev/dcdr/cache.h>
#include <fs/devfs.h>
#include <fs/ctlfs.h>
#include <vm/dynalloc.h>
#include <vm/physmem.h>
+#include <machine/cdefs.h>
#include <string.h>
#define pr_trace(fmt, ...) kprintf("ahci: " fmt, ##__VA_ARGS__)
@@ -55,6 +58,20 @@ static struct bdevsw ahci_bdevsw;
static struct hba_device *devs;
static struct pci_device *ahci_dev;
static struct timer tmr;
+static struct ahci_hba g_hba;
+static struct driver_var __driver_var;
+
+#define MODEL_LEN 40 /* Model number length */
+#define SERIAL_LEN 20 /* Serial number length */
+
+/*
+ * Simplified structure containing certain
+ * information from device identity.
+ */
+struct dev_info {
+ char model[MODEL_LEN];
+ char serial[SERIAL_LEN];
+};
/*
* Poll register to have 'bits' set/unset.
@@ -94,6 +111,18 @@ ahci_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset)
return 0;
}
+static struct hba_device *
+ahci_get_dev(dev_t dev)
+{
+ for (int i = 0; i < devs_max; ++i) {
+ if (devs[i].dev == dev) {
+ return &devs[i];
+ }
+ }
+
+ return NULL;
+}
+
/*
* Allocate a command slot for a port on
* the HBA.
@@ -103,7 +132,6 @@ ahci_alloc_cmdslot(struct ahci_hba *hba, struct hba_port *port)
{
uint32_t slotlist;
- slotlist = port->ci | port->sact;
slotlist = mmio_read32(&port->ci);
slotlist |= mmio_read32(&port->sact);
@@ -166,35 +194,50 @@ ahci_hba_reset(struct ahci_hba *hba)
/*
* Dump identify structure for debugging
* purposes.
+ *
+ * Returns a pointer to a 'dev_info' structure
+ * on success, otherwise a value of NULL is returned
+ * on failure.
*/
-static void
-ahci_dump_identity(struct ata_identity *identity)
+static int
+ahci_dump_identity(struct ata_identity *identity, struct dev_info *res)
{
- char serial_number[20];
- char model_number[40];
char tmp;
- memcpy(serial_number, identity->serial_number, sizeof(serial_number));
- memcpy(model_number, identity->model_number, sizeof(model_number));
+ if (res == NULL) {
+ return -EINVAL;
+ }
- serial_number[sizeof(serial_number) - 1] = '\0';
- model_number[sizeof(model_number) - 1] = '\0';
+ /* Copy the data, might be big endian */
+ memcpy(
+ res->serial,
+ identity->serial_number,
+ SERIAL_LEN
+ );
+ memcpy(
+ res->model,
+ identity->model_number,
+ sizeof(res->model)
+ );
+
+ res->serial[SERIAL_LEN - 1] = '\0';
+ res->model[MODEL_LEN - 1] = '\0';
/* Fixup endianess for serial number */
- for (size_t i = 0; i < sizeof(serial_number); i += 2) {
- tmp = serial_number[i];
- serial_number[i] = serial_number[i + 1];
- serial_number[i + 1] = tmp;
+ for (size_t i = 0; i < SERIAL_LEN; i += 2) {
+ tmp = res->serial[i];
+ res->serial[i] = res->serial[i + 1];
+ res->serial[i + 1] = tmp;
}
/* Fixup endianess for model number */
- for (size_t i = 0; i < sizeof(model_number); i += 2) {
- tmp = model_number[i];
- model_number[i] = model_number[i + 1];
- model_number[i + 1] = tmp;
+ for (size_t i = 0; i < MODEL_LEN; i += 2) {
+ tmp = res->model[i];
+ res->model[i] = res->model[i + 1];
+ res->model[i + 1] = tmp;
}
- pr_trace("model number: %s\n", model_number);
+ return 0;
}
/*
@@ -315,59 +358,77 @@ hba_port_chkerr(struct hba_port *port)
static int
hba_port_reset(struct ahci_hba *hba, struct hba_port *port)
{
- uint32_t sctl, ssts;
- uint8_t det, ipm;
- int error;
+ uint32_t sctl, ssts, cmd;
+ uint8_t det, ipm, spd;
+ uint32_t elapsed = 0;
+
+ sctl = mmio_read32(&port->sctl);
/*
- * The port must not be in an idle state when a
- * COMRESET is sent over the interface as some
- * chipsets do not know how to handle this...
- *
- * After bringing up the port, send a COMRESET
- * over the interface for roughly ~2ms.
+ * Transmit a COMRESET to the device. If the HBA
+ * supports staggered spin-up, we'll need to set
+ * the PxCMD.SUD bit as well.
*/
- hba_port_start(port);
- sctl = mmio_read32(&port->sctl);
sctl = (sctl & ~0x0F) | AHCI_DET_COMRESET;
mmio_write32(&port->sctl, sctl);
+ if (hba->sss) {
+ cmd = mmio_read32(&port->cmd);
+ cmd |= AHCI_PXCMD_SUD;
+ mmio_write32(&port->cmd, cmd);
+ }
/*
* Wait for the link to become reestablished
* between the port and the HBA.
*/
- tmr.msleep(300);
+ tmr.msleep(8);
sctl &= ~AHCI_DET_COMRESET;
mmio_write32(&port->sctl, sctl);
- /*
- * Now we'll need to grab some power management
- * and detection flags as the port must have
- * a device present along with an active
- * interface.
- */
- ssts = mmio_read32(&port->ssts);
- det = AHCI_PXSCTL_DET(ssts);
- ipm = AHCI_PXSSTS_IPM(ssts);
+ for (;;) {
+ if (elapsed >= AHCI_TIMEOUT) {
+ break;
+ }
+ ssts = mmio_read32(&port->ssts);
+ det = AHCI_PXSSTS_DET(ssts);
+ if (det == AHCI_DET_COMM) {
+ break;
+ }
- /* If there is no device, fake success */
- if (det == AHCI_DET_NULL) {
- return 0;
+ tmr.msleep(10);
+ elapsed += 10;
}
+ ipm = AHCI_PXSSTS_IPM(ssts);
+ spd = AHCI_PXSSTS_SPD(ssts);
+
+ if (det == AHCI_DET_PRESENT) {
+ pr_error("SATA link timeout\n");
+ return -EAGAIN;
+ }
if (det != AHCI_DET_COMM) {
- pr_trace("failed to establish link\n");
return -EAGAIN;
}
+ /*
+ * Ensure the interface is in an active
+ * state.
+ */
if (ipm != AHCI_IPM_ACTIVE) {
- pr_trace("device interface not active\n");
+ pr_error("device interface not active\n");
return -EAGAIN;
}
- if ((error = hba_port_stop(port)) < 0) {
- pr_trace("failed to stop port\n");
- return error;
+ switch (spd) {
+ case AHCI_SPD_GEN1:
+ pr_trace("SATA link rate @ ~1.5 Gb/s\n");
+ break;
+ case AHCI_SPD_GEN2:
+ pr_trace("SATA link rate @ ~3 Gb/s\n");
+ break;
+ case AHCI_SPD_GEN3:
+ pr_trace("SATA link rate @ ~6 Gb/s\n");
+ break;
}
return 0;
@@ -416,12 +477,15 @@ ahci_submit_cmd(struct ahci_hba *hba, struct hba_port *port, uint8_t slot)
* SATA device.
*/
static int
-ahci_identify(struct ahci_hba *hba, struct hba_port *port)
+ahci_identify(struct ahci_hba *hba, struct hba_device *dp)
{
paddr_t base, buf;
+ struct dev_info dev_info;
+ struct hba_port *port;
struct ahci_cmd_hdr *cmdhdr;
struct ahci_cmdtab *cmdtbl;
struct ahci_fis_h2d *fis;
+ uint16_t *p;
int cmdslot, status;
buf = vm_alloc_frame(1);
@@ -430,6 +494,7 @@ ahci_identify(struct ahci_hba *hba, struct hba_port *port)
return -ENOMEM;
}
+ port = dp->io;
cmdslot = ahci_alloc_cmdslot(hba, port);
if (cmdslot < 0) {
pr_trace("failed to alloc cmdslot\n");
@@ -460,7 +525,13 @@ ahci_identify(struct ahci_hba *hba, struct hba_port *port)
goto done;
}
- ahci_dump_identity(PHYS_TO_VIRT(buf));
+ ahci_dump_identity(PHYS_TO_VIRT(buf), &dev_info);
+ p = (uint16_t *)PHYS_TO_VIRT(buf);
+ dp->nlba = (p[61] << 16) | p[60];
+
+ pr_trace("max block size: %d\n", dp->nlba);
+ pr_trace("model number: %s\n", dev_info.model);
+ pr_trace("serial number: %s\n", dev_info.serial);
done:
vm_free_frame(buf, 1);
return status;
@@ -470,7 +541,7 @@ done:
* Send a read/write command to a SATA drive
*
* @hba: Host bus adapter of target port
- * @port: Port to send over
+ * @dev: Device to send over
* @sio: System I/O descriptor
* @write: If true, data pointed to by `sio` will be written
*
@@ -480,14 +551,21 @@ done:
* - The `offset` field in `sio` is the LBA address.
*/
static int
-ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio,
+ahci_sata_rw(struct ahci_hba *hba, struct hba_device *dev, struct sio_txn *sio,
bool write)
{
paddr_t base, buf;
+ char *p, *dest;
+ bool dcdr_hit = false;
+ struct hba_port *port;
+ struct dcdr_lookup dcd_lookup;
+ struct dcd *dcd;
struct ahci_cmd_hdr *cmdhdr;
struct ahci_cmdtab *cmdtbl;
struct ahci_fis_h2d *fis;
int cmdslot, status;
+ size_t nblocks, cur_lba;
+ size_t len;
if (sio == NULL) {
return -EINVAL;
@@ -496,6 +574,60 @@ ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio,
return -EINVAL;
}
+ port = dev->io;
+
+ /*
+ * Compute how many blocks can be cached.
+ *
+ * XXX: We do not want to fill the entire DCDR
+ * with a single drive read to reduce the
+ * frequency of DCDR evictions.
+ *
+ * TODO: We should also take advantage of logical
+ * block coalescing.
+ */
+ nblocks = sio->len;
+ if (nblocks >= AHCI_DCDR_CAP) {
+ nblocks = AHCI_DCDR_CAP / 2;
+ }
+
+ /*
+ * If we are reading the drive, see if we have
+ * anything in the cache.
+ *
+ * XXX: If there is a break in the cache and we
+ * have a miss inbetween, other DCDs are
+ * ignored. Wonder how we can mitigate
+ * fragmentation.
+ */
+ cur_lba = sio->offset;
+ len = sio->len;
+ for (size_t i = 0; i < nblocks && !write; ++i) {
+ status = dcdr_lookup(dev->dcdr, cur_lba, &dcd_lookup);
+ if (status != 0) {
+ break;
+ }
+ if (len == 0) {
+ break;
+ }
+
+ dcdr_hit = true;
+ dcd = dcd_lookup.dcd_res;
+
+ /* Hit, copy the cached data */
+ dest = &((char *)sio->buf)[i * 512];
+ p = dcd->block;
+ memcpy(dest, p, 512);
+
+ ++cur_lba;
+ --len;
+ }
+
+ /* Did we get everything already? */
+ if (len == 0) {
+ return 0;
+ }
+
buf = VIRT_TO_PHYS(sio->buf);
cmdslot = ahci_alloc_cmdslot(hba, port);
if (cmdslot < 0) {
@@ -524,22 +656,32 @@ ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio,
fis->device = (1 << 6); /* LBA */
/* Setup LBA */
- fis->lba0 = sio->offset & 0xFF;
- fis->lba1 = (sio->offset >> 8) & 0xFF;
- fis->lba2 = (sio->offset >> 16) & 0xFF;
- fis->lba3 = (sio->offset >> 24) & 0xFF;
- fis->lba4 = (sio->offset >> 32) & 0xFF;
- fis->lba5 = (sio->offset >> 40) & 0xFF;
+ fis->lba0 = cur_lba & 0xFF;
+ fis->lba1 = (cur_lba >> 8) & 0xFF;
+ fis->lba2 = (cur_lba >> 16) & 0xFF;
+ fis->lba3 = (cur_lba >> 24) & 0xFF;
+ fis->lba4 = (cur_lba >> 32) & 0xFF;
+ fis->lba5 = (cur_lba >> 40) & 0xFF;
/* Setup count */
- fis->countl = sio->len & 0xFF;
- fis->counth = (sio->len >> 8) & 0xFF;
+ fis->countl = len & 0xFF;
+ fis->counth = (len >> 8) & 0xFF;
- pr_trace("SUBMIT: RIGHT!\n");
if ((status = ahci_submit_cmd(hba, port, cmdslot)) != 0) {
return status;
}
+ /* Don't cache again on hit */
+ if (!write && dcdr_hit) {
+ return 0;
+ }
+
+ /* Cache our read */
+ for (size_t i = 0; i < nblocks; ++i) {
+ cur_lba = sio->offset + i;
+ p = sio->buf;
+ dcdr_cachein(dev->dcdr, &p[i * 512], cur_lba);
+ }
return 0;
}
@@ -549,7 +691,6 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
const size_t BSIZE = 512;
struct sio_txn wr_sio;
struct hba_device *devp;
- struct ahci_hba *hba;
size_t block_count, len;
off_t block_off, read_off;
char *buf;
@@ -561,11 +702,11 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
if (sio->len == 0 || sio->buf == NULL) {
return -EINVAL;
}
- if (dev >= devs_max) {
+ if (dev > devs_max) {
return -ENODEV;
}
- devp = &devs[dev];
+ devp = ahci_get_dev(dev);
if (__unlikely(devp == NULL)) {
return -ENODEV;
}
@@ -595,14 +736,14 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
wr_sio.buf = buf;
wr_sio.len = block_count;
wr_sio.offset = block_off;
- status = ahci_sata_rw(hba, devp->io, &wr_sio, write);
+ status = ahci_sata_rw(&g_hba, devp, &wr_sio, write);
if (status == 0 && !write) {
read_off = sio->offset & (BSIZE - 1);
memcpy(sio->buf, buf + read_off, sio->len);
}
dynfree(buf);
- return status;
+ return sio->len;
}
/*
@@ -611,10 +752,99 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
static int
ahci_dev_read(dev_t dev, struct sio_txn *sio, int flags)
{
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
return sata_dev_rw(dev, sio, false);
}
/*
+ * Device interface write
+ */
+static int
+ahci_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
+ return sata_dev_rw(dev, sio, true);
+}
+
+/*
+ * Device interface number of blocks
+ */
+static int
+ahci_dev_bsize(dev_t dev)
+{
+ struct hba_device *dp;
+
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
+ if ((dp = ahci_get_dev(dev)) == NULL) {
+ return -ENODEV;
+ }
+
+ return dp->nlba;
+}
+
+/*
+ * Register a block device connected to an HBA port
+ * to the rest of the system.
+ *
+ * @dp: Device pointer
+ * @hba: HBA this device belongs to
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+static int
+ahci_register(struct hba_device *dp, struct ahci_hba *hba)
+{
+ struct ctlfs_dev dev;
+ char devname[128];
+ int error;
+
+ if (hba->major == 0) {
+ hba->major = dev_alloc_major();
+ }
+
+ dp->dev = dev_alloc(hba->major);
+ snprintf(devname, sizeof(devname), "sd%d", dp->dev);
+
+ /* Register the device */
+ dev_register(hba->major, dp->dev, &ahci_bdevsw);
+ pr_trace("drive @ /dev/%s\n", devname);
+
+ /* Register a control node */
+ dev.mode = 0444;
+ ctlfs_create_node(devname, &dev);
+ pr_trace("drive control @ /ctl/%s/\n", devname);
+
+ /* Register control files */
+ dev.devname = devname;
+ dev.ops = &g_sata_bsize_ops;
+ ctlfs_create_entry("bsize", &dev);
+
+ error = devfs_create_entry(devname, hba->major, dp->dev, 060444);
+ if (error < 0) {
+ pr_error("failed to create devfs entry\n");
+ return error;
+ }
+
+ snprintf(devname, sizeof(devname), "SATA drive %d", dp->dev);
+ error = disk_add(devname, dp->dev, &ahci_bdevsw, 0);
+ if (error < 0) {
+ pr_error("failed to add disk \"%s\"\n", devname);
+ return 1;
+ }
+ return 0;
+}
+
+/*
* Initialize a drive on an HBA port
*
* @hba: HBA descriptor
@@ -623,33 +853,23 @@ ahci_dev_read(dev_t dev, struct sio_txn *sio, int flags)
static int
ahci_init_port(struct ahci_hba *hba, uint32_t portno)
{
- char devname[128];
struct hba_memspace *abar = hba->io;
struct hba_port *port;
struct hba_device *dp;
- struct ctlfs_dev dev;
size_t clen, pagesz;
- uint32_t lo, hi, ssts;
- uint8_t det;
+ uint32_t lo, hi, sig;
paddr_t fra, cmdlist, tmp;
int error;
pagesz = DEFAULT_PAGESIZE;
port = &abar->ports[portno];
- /* Is anything on the port? */
- ssts = mmio_read32(&port->ssts);
- det = AHCI_PXSCTL_DET(ssts);
- switch (det) {
- case AHCI_DET_NULL:
- /* No device attached */
- return 0;
- case AHCI_DET_PRESENT:
- if ((error = hba_port_reset(hba, port)) < 0) {
- pr_trace("failed to reset port %d\n", portno);
- return error;
- }
- break;
+ if ((error = hba_port_reset(hba, port)) < 0) {
+ return error;
+ }
+ sig = mmio_read32(&port->sig);
+ if (sig == ATAPI_SIG) {
+ return -ENOTSUP;
}
pr_trace("found device @ port %d\n", portno);
@@ -658,6 +878,12 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
dp->hba = hba;
dp->dev = portno;
+ dp->dcdr = dcdr_alloc(512, AHCI_DCDR_CAP);
+ if (dp->dcdr == NULL) {
+ pr_error("failed to alloc dcdr\n");
+ return -ENOMEM;
+ }
+
/* Allocate a command list */
clen = ALIGN_UP(hba->nslots * AHCI_CMDENTRY_SIZE, pagesz);
clen /= pagesz;
@@ -677,8 +903,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
}
dp->fra = PHYS_TO_VIRT(fra);
- memset(dp->cmdlist, 0, clen * pagesz);
- memset(dp->fra, 0, pagesz);
/* Write the command list */
lo = cmdlist & 0xFFFFFFFF;
@@ -697,7 +921,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
tmp = vm_alloc_frame(1);
dp->cmdlist[i].prdtl = 1;
dp->cmdlist[i].ctba = tmp;
- memset(PHYS_TO_VIRT(tmp), 0, pagesz);
}
mmio_write32(&port->serr, 0xFFFFFFFF);
@@ -712,28 +935,8 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno)
return error;
}
- ahci_identify(hba, port);
-
- if (hba->major == 0) {
- hba->major = dev_alloc_major();
- }
- dp->dev = dev_alloc(hba->major);
- snprintf(devname, sizeof(devname), "sd%d", dp->dev);
-
- /* Register the device */
- dev_register(hba->major, dp->dev, &ahci_bdevsw);
- pr_trace("drive @ /dev/%s\n", devname);
-
- /* Register a control node */
- dev.mode = 0444;
- ctlfs_create_node(devname, &dev);
- pr_trace("drive control @ /ctl/%s/\n", devname);
-
- /* Register control files */
- dev.devname = devname;
- dev.ops = &g_sata_bsize_ops;
- ctlfs_create_entry("bsize", &dev);
- return devfs_create_entry(devname, hba->major, dp->dev, 0444);
+ ahci_identify(hba, dp);
+ return ahci_register(dp, hba);
}
/*
@@ -841,10 +1044,9 @@ ahci_init(void)
{
struct pci_lookup lookup;
int status;
- struct ahci_hba hba;
void *abar_vap = NULL;
- hba.major = 0;
+ g_hba.major = 0;
lookup.pci_class = 0x01;
lookup.pci_subclass = 0x06;
@@ -890,14 +1092,15 @@ ahci_init(void)
}
ahci_init_pci();
- hba.io = (struct hba_memspace*)abar_vap;
- ahci_hba_init(&hba);
+ g_hba.io = (struct hba_memspace*)abar_vap;
+ ahci_hba_init(&g_hba);
return 0;
}
static struct bdevsw ahci_bdevsw = {
.read = ahci_dev_read,
- .write = nowrite
+ .write = ahci_dev_write,
+ .bsize = ahci_dev_bsize
};
-DRIVER_EXPORT(ahci_init);
+DRIVER_EXPORT(ahci_init, "ahci");
diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c
index 704fdec..c65d7e0 100644
--- a/sys/dev/ic/nvme.c
+++ b/sys/dev/ic/nvme.c
@@ -309,6 +309,35 @@ nvme_poll_submit_cmd(struct nvme_queue *q, struct nvme_cmd cmd)
return 0;
}
+/*
+ * Get NVMe log page
+ *
+ * @ctrl: NVMe controller to target
+ * @buf: Data buffer
+ * @lid: Log identifier
+ * @len: Length (in bytes)
+ */
+static int
+nvme_get_logpage(struct nvme_ctrl *ctrl, void *buf, uint8_t lid, uint32_t len)
+{
+ struct nvme_cmd cmd = {0};
+ struct nvme_get_logpage_cmd *cmdp;
+
+ if (!is_4k_aligned(buf)) {
+ return -1;
+ }
+
+ cmdp = &cmd.get_logpage;
+ cmdp->opcode = NVME_OP_GET_LOGPAGE;
+ cmdp->nsid = 0xFFFFFFFF;
+ cmdp->lid = lid;
+ cmdp->numdl = len / 4;
+ cmdp->numdu = 0;
+ cmdp->prp1 = VIRT_TO_PHYS(buf);
+ cmdp->prp2 = 0;
+ return nvme_poll_submit_cmd(&ctrl->adminq, cmd);
+}
+
static int
nvme_identify(struct nvme_ctrl *ctrl, void *buf, uint32_t nsid, uint8_t cns)
{
@@ -470,6 +499,12 @@ nvme_dev_read(dev_t dev, struct sio_txn *sio, int flags)
return nvme_dev_rw(dev, sio, false);
}
+static int
+nvme_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ return nvme_dev_rw(dev, sio, true);
+}
+
/*
* Initializes an NVMe namespace.
*
@@ -543,6 +578,7 @@ nvme_init_ctrl(struct nvme_bar *bar)
uint16_t mqes;
uint8_t *nsids;
struct nvme_ctrl ctrl = { .bar = bar };
+ struct nvme_smart_data *smart;
struct nvme_queue *adminq;
struct nvme_id *id;
@@ -566,14 +602,21 @@ nvme_init_ctrl(struct nvme_bar *bar)
return error;
}
+ smart = dynalloc_memalign(sizeof(*smart), 0x1000);
+ if (smart == NULL) {
+ return -ENOMEM;
+ }
+
id = dynalloc_memalign(sizeof(*id), 0x1000);
if (id == NULL) {
+ dynfree(smart);
return -ENOMEM;
}
nsids = dynalloc_memalign(0x1000, 0x1000);
if (nsids == NULL) {
dynfree(id);
+ dynfree(smart);
return -ENOMEM;
}
@@ -581,6 +624,18 @@ nvme_init_ctrl(struct nvme_bar *bar)
nvme_log_ctrl_id(id);
nvme_identify(&ctrl, nsids, 0, ID_CNS_NSID_LIST);
+ /*
+ * Attempt to read some SMART data but don't bother
+ * if it fails in any way.
+ */
+ error = nvme_get_logpage(&ctrl, smart, NVME_LOGPAGE_SMART, sizeof(*smart));
+ if (error == 0) {
+ if (smart->temp != 0 && smart->temp > 283)
+ pr_trace("temp: %d K\n", smart->temp);
+
+ pr_trace("%d%% used\n", smart->percent_used);
+ }
+
ctrl.sqes = id->sqes >> 4;
ctrl.cqes = id->cqes >> 4;
@@ -607,6 +662,7 @@ nvme_init_ctrl(struct nvme_bar *bar)
dynfree(id);
dynfree(nsids);
+ dynfree(smart);
return 0;
}
@@ -659,7 +715,7 @@ nvme_init(void)
static struct bdevsw nvme_bdevsw = {
.read = nvme_dev_read,
- .write = nowrite
+ .write = nvme_dev_write
};
-DRIVER_EXPORT(nvme_init);
+DRIVER_EXPORT(nvme_init, "nvme");
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index d59f68f..140a363 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -32,15 +32,32 @@
#include <sys/syslog.h>
#include <sys/errno.h>
#include <sys/spinlock.h>
+#include <sys/mmio.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
+#include <dev/acpi/acpi.h>
+#include <dev/acpi/tables.h>
+#include <machine/pci/pci.h>
#include <vm/dynalloc.h>
+#include <vm/vm.h>
#include <lib/assert.h>
#define pr_trace(fmt, ...) kprintf("pci: " fmt, ##__VA_ARGS__)
static TAILQ_HEAD(, pci_device) device_list;
static struct spinlock devlist_lock = {0};
+static struct acpi_mcfg *mcfg;
+
+struct cam_hook {
+ /* PCI CAM */
+ pcireg_t(*cam_readl)(struct pci_device *dev, uint32_t off);
+ void(*cam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val);
+
+ /* PCIe ECAM */
+ pcireg_t(*ecam_readl)(struct pci_device *dev, uint32_t off);
+ void(*ecam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val);
+ void *ecam_base[1];
+} cam_hook = { NULL };
static bool
pci_dev_exists(uint8_t bus, uint8_t slot, uint8_t func)
@@ -123,6 +140,9 @@ pci_set_device_info(struct pci_device *dev)
dev->prog_if = PCIREG_PROGIF(classrev);
dev->hdr_type = (uint8_t)pci_readl(dev, PCIREG_HDRTYPE);
+ /* This is a PCIe device if it has CAP ID of 0x10 */
+ dev->pci_express = pci_get_cap(dev, 0x10) != 0;
+
/* Set type-specific data */
switch (dev->hdr_type & ~BIT(7)) {
case PCI_HDRTYPE_NORMAL:
@@ -151,6 +171,53 @@ pci_set_device_info(struct pci_device *dev)
static void
pci_scan_bus(uint8_t bus);
+static inline vaddr_t
+pcie_ecam_addr(struct pci_device *dev)
+{
+ vaddr_t base = (vaddr_t)cam_hook.ecam_base[0];
+
+ base += dev->bus << 20 |
+ dev->slot << 15 |
+ dev->func << 12;
+ return base;
+}
+
+static pcireg_t
+pcie_ecam_readl(struct pci_device *dev, uint32_t offset)
+{
+ vaddr_t address;
+
+ address = pcie_ecam_addr(dev);
+ address += (offset & ~3);
+ return mmio_read32((void *)address);
+}
+
+static void
+pcie_ecam_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+{
+ vaddr_t address;
+
+ address = pcie_ecam_addr(dev);
+ address += (offset & ~3);
+ mmio_write32((void *)address, val);
+}
+
+static int
+pcie_init(struct acpi_mcfg_base *base)
+{
+ void *iobase;
+
+ pr_trace("[group %02d] @ bus [%02d - %02d]\n", base->seg_grpno,
+ base->bus_start, base->bus_end);
+ pr_trace("ecam @ %p\n", base->base_pa);
+
+ iobase = PHYS_TO_VIRT(base->base_pa);
+ cam_hook.ecam_base[0] = iobase;
+ cam_hook.ecam_writel = pcie_ecam_writel;
+ cam_hook.ecam_readl = pcie_ecam_readl;
+ return 0;
+}
+
/*
* Attempt to register a device.
*
@@ -264,7 +331,6 @@ pci_get_device(struct pci_lookup lookup, uint16_t lookup_type)
return NULL;
}
-
void
pci_add_device(struct pci_device *dev)
{
@@ -273,12 +339,45 @@ pci_add_device(struct pci_device *dev)
spinlock_release(&devlist_lock);
}
+pcireg_t
+pci_readl(struct pci_device *dev, uint32_t offset)
+{
+ bool have_ecam = cam_hook.ecam_readl != NULL;
+
+ if (dev->pci_express && have_ecam) {
+ return cam_hook.ecam_readl(dev, offset);
+ }
+
+ return cam_hook.cam_readl(dev, offset);
+}
+
+void
+pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+{
+ bool have_ecam = cam_hook.ecam_writel != NULL;
+
+ if (dev->pci_express && have_ecam) {
+ cam_hook.ecam_writel(dev, offset, val);
+ return;
+ }
+
+ cam_hook.cam_writel(dev, offset, val);
+}
+
int
pci_init(void)
{
size_t ndev;
TAILQ_INIT(&device_list);
+ mcfg = acpi_query("MCFG");
+ if (mcfg != NULL) {
+ pcie_init(&mcfg->base[0]);
+ }
+
+ cam_hook.cam_readl = md_pci_readl;
+ cam_hook.cam_writel = md_pci_writel;
+
/* Recursively scan bus 0 */
pci_scan_bus(0);
ndev = TAILQ_NELEM(&device_list);
diff --git a/sys/dev/phy/e1000.c b/sys/dev/phy/e1000.c
new file mode 100644
index 0000000..41a2a27
--- /dev/null
+++ b/sys/dev/phy/e1000.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/driver.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/mmio.h>
+#include <dev/phy/e1000regs.h>
+#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
+#include <dev/timer.h>
+#include <net/if_var.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("e1000: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define E1000_VENDOR 0x8086
+#define E1000_DEVICE 0x100E
+#define E1000_TIMEOUT 500 /* In msec */
+
+static struct timer tmr;
+static struct pci_device *e1000;
+static struct netif netif;
+
+struct e1000_nic {
+ void *vap;
+ uint8_t has_eeprom : 1;
+ uint16_t eeprom_size;
+ uint16_t io_port;
+};
+
+static int
+e1000_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset)
+{
+ size_t usec_start, usec;
+ size_t elapsed_msec;
+ uint32_t val;
+ bool tmp;
+
+ usec_start = tmr.get_time_usec();
+
+ for (;;) {
+ val = mmio_read32(reg);
+ tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits);
+
+ usec = tmr.get_time_usec();
+ elapsed_msec = (usec - usec_start) / 1000;
+
+ /* If tmp is set, the register updated in time */
+ if (tmp) {
+ break;
+ }
+
+ /* Exit with an error if we timeout */
+ if (elapsed_msec > E1000_TIMEOUT) {
+ return -ETIME;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Query information about any EEPROMs for diagnostic
+ * purposes.
+ *
+ * TODO: Some wacky older chips don't show their presence
+ * too easily, we could fallback to microwire / SPI
+ * bit banging to see if it responds to us manually
+ * clocking a dummy read operation in.
+ */
+static void
+eeprom_query(struct e1000_nic *np)
+{
+ uint16_t size_bits = 1024;
+ uint32_t eecd, *eecd_p;
+ const char *typestr = "microwire";
+
+ eecd_p = PTR_OFFSET(np->vap, E1000_EECD);
+
+ /*
+ * First we should check if there is an EEPROM
+ * on-board as if not, there is nothing we can do
+ * here.
+ */
+ eecd = mmio_read32(eecd_p);
+ if (!ISSET(eecd, E1000_EECD_PRES)) {
+ return;
+ }
+
+ np->has_eeprom = 1;
+ if (ISSET(eecd, E1000_EECD_TYPE)) {
+ typestr = "SPI";
+ }
+ if (ISSET(eecd, E1000_EECD_SIZE)) {
+ size_bits = 4096;
+ }
+
+ np->eeprom_size = size_bits;
+ pr_trace("%d-bit %s EEPROM detected\n", size_bits, typestr);
+}
+
+/*
+ * If there is no EEPROM, we can still read
+ * the MAC address through the Receive address
+ * registers
+ *
+ * XXX: This is typically only used as a fallback.
+ *
+ * Returns a less than zero value if an ethernet
+ * address is not found, which would be kind of
+ * not good.
+ *
+ * @np: NIC descriptor
+ * @addr: Pointer to MAC address data
+ */
+static int
+e1000_read_recvaddr(struct e1000_nic *np, struct netif_addr *addr)
+{
+ const uint32_t RECVADDR_OFF = 0x5400;
+ uint32_t tmp;
+ uint32_t *dword_p;
+
+ dword_p = PTR_OFFSET(np->vap, RECVADDR_OFF);
+
+ if (dword_p[0] == 0) {
+ pr_error("bad hwaddr in recvaddr\n");
+ return -ENOTSUP;
+ }
+
+ /* DWORD 0 */
+ tmp = mmio_read32(&dword_p[0]);
+ addr->data[0] = tmp & 0xFF;
+ addr->data[1] = (tmp >> 8) & 0xFF;
+ addr->data[2] = (tmp >> 16) & 0xFF;
+ addr->data[3] = (tmp >> 24) & 0xFF;
+
+ /* DWORD 1 */
+ tmp = mmio_read32(&dword_p[1]);
+ addr->data[4] = tmp & 0xFF;
+ addr->data[5] = (tmp >> 8) & 0xFF;
+ return 0;
+}
+
+/*
+ * Read 16-bytes from the NIC's on-board EEPROM.
+ *
+ * XXX: This should only be used if the caller is
+ * certain that the NIC has an EEPROM
+ *
+ * @addr: EEPROM address to read from
+ *
+ * A returned value of 0xFFFF should be seen as invalid.
+ */
+static uint16_t
+eeprom_readw(struct e1000_nic *np, uint8_t addr)
+{
+ uint32_t eerd, *eerd_p;
+ int error;
+
+ if (!np->has_eeprom) {
+ pr_error("e1000_read_eeprom: EEPROM not present\n");
+ return 0xFFFF;
+ }
+
+ eerd_p = PTR_OFFSET(np->vap, E1000_EERD);
+ eerd = (addr << 8) | E1000_EERD_START;
+ mmio_write32(eerd_p, eerd);
+
+ error = e1000_poll_reg(eerd_p, E1000_EERD_DONE, true);
+ if (error < 0) {
+ pr_error("e1000_read_eeprom: timeout\n");
+ return 0xFFFF;
+ }
+
+ eerd = mmio_read32(eerd_p);
+ return (eerd >> 16) & 0xFFFF;
+}
+
+/*
+ * Read the MAC address from the NICs EEPROM.
+ *
+ * XXX: This should usually work, however if the NIC does
+ * not have an on-board EEPROM, this will fail. In such
+ * cases, e1000_read_recvaddr() can be called instead.
+ *
+ * @np: NIC descriptor
+ * @addr: Pointer to MAC address data
+ */
+static int
+e1000_read_macaddr(struct e1000_nic *np, struct netif_addr *addr)
+{
+ uint16_t eeprom_word;
+
+ if (!np->has_eeprom) {
+ pr_trace("EEPROM not present, trying recvaddr\n");
+ return e1000_read_recvaddr(np, addr);
+ }
+
+ /* Word 0 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR0);
+ addr->data[0] = (eeprom_word & 0xFF);
+ addr->data[1] = (eeprom_word >> 8) & 0xFF;
+
+ /* Word 1 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR1);
+ addr->data[2] = (eeprom_word & 0xFF);
+ addr->data[3] = (eeprom_word >> 8) & 0xFF;
+
+ /* Word 2 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR2);
+ addr->data[4] = (eeprom_word & 0xFF);
+ addr->data[5] = (eeprom_word >> 8) & 0xFF;
+ return 0;
+}
+
+/*
+ * Reset the entire E1000
+ */
+static int
+e1000_reset(struct e1000_nic *np)
+{
+ uint32_t ctl, *ctl_p;
+ int error;
+
+ ctl_p = PTR_OFFSET(np->vap, E1000_CTL);
+ ctl = mmio_read32(&ctl_p);
+ ctl |= E1000_CTL_RST;
+ mmio_write32(&ctl_p, ctl);
+
+ error = e1000_poll_reg(ctl_p, E1000_CTL_RST, false);
+ if (error < 0) {
+ pr_error("reset timeout\n");
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize an E1000(e) chip
+ */
+static int
+e1000_chip_init(struct e1000_nic *np)
+{
+ struct netif_addr *addr = &netif.addr;
+ int error;
+
+ /*
+ * To ensure that BIOS/UEFI or whatever firmware got us
+ * here didn't fuck anything up in the process or at the
+ * very least, put the controller in a seemingly alright
+ * state that gives us a suprise screwing in the future,
+ * we'll reset everything to its default startup state.
+ *
+ * Better safe than sorry...
+ */
+ if ((error = e1000_reset(np)) < 0) {
+ return error;
+ }
+
+ eeprom_query(np);
+ if ((error = e1000_read_macaddr(np, addr)) < 0) {
+ return error;
+ }
+
+ pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
+ (uint64_t)addr->data[0], (uint64_t)addr->data[1],
+ (uint64_t)addr->data[2], (uint64_t)addr->data[3],
+ (uint64_t)addr->data[4], (uint64_t)addr->data[5]);
+
+ return 0;
+}
+
+/*
+ * Enables PCI specific bits like bus mastering (for DMA)
+ * as well as MMIO.
+ */
+static void
+e1000_init_pci(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_readl(e1000, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(e1000, PCIREG_CMDSTATUS, tmp);
+}
+
+static int
+e1000_init(void)
+{
+ struct pci_lookup lookup;
+ struct e1000_nic nic;
+ int status;
+
+ lookup.vendor_id = E1000_VENDOR;
+ lookup.device_id = E1000_DEVICE;
+ e1000 = pci_get_device(lookup, PCI_DEVICE_ID | PCI_VENDOR_ID);
+ if (e1000 == NULL) {
+ return -ENODEV;
+ }
+
+ /* Get a GP timer */
+ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
+ pr_error("failed to fetch general purpose timer\n");
+ return -ENODEV;
+ }
+
+ /* We need msleep() */
+ if (tmr.msleep == NULL) {
+ pr_error("general purpose timer has no msleep()\n");
+ return -ENODEV;
+ }
+
+ memset(&nic, 0, sizeof(nic));
+ pr_trace("e1000 at pci%d:%x.%x.%d\n",
+ e1000->bus, e1000->device_id, e1000->func,
+ e1000->slot);
+
+ if ((status = pci_map_bar(e1000, 0, &nic.vap)) != 0) {
+ pr_error("failed to map BAR0\n");
+ return status;
+ }
+
+ e1000_init_pci();
+ e1000_chip_init(&nic);
+ return 0;
+}
+
+DRIVER_EXPORT(e1000_init, "e1000");
diff --git a/sys/dev/phy/et131x.c b/sys/dev/phy/et131x.c
new file mode 100644
index 0000000..d7764ae
--- /dev/null
+++ b/sys/dev/phy/et131x.c
@@ -0,0 +1,338 @@
+/*
+ * 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.
+ */
+
+/*
+ * This driver is the product of reverse engineering
+ * work done by Ian Marco Moffett and the OSMORA team.
+ *
+ * Please refer to share/docs/hw/et131x.txt
+ */
+
+#include <sys/types.h>
+#include <sys/driver.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/mmio.h>
+#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
+#include <dev/phy/et131xregs.h>
+#include <dev/timer.h>
+#include <net/if_var.h>
+
+#define VENDOR_ID 0x11C1 /* Agere */
+#define DEVICE_ID 0xED00
+
+#define pr_trace(fmt, ...) kprintf("et131x: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+/*
+ * The ET131X has 1024 words of internal RAM used to
+ * store/buffer packet data before reception or transmission.
+ * The card allows us to decide how large the TX/RX buffers would
+ * be split up. We split the RX/TX 50/50 as a nice balanced default.
+ * Might need to later adjust based on various system needs
+ * (e.g., heavy TX or RX) to avoid thrashing any of the buffers.
+ *
+ */
+#define INTERNAL_MEMSIZE 1024 /* In words */
+#define INTERNAL_MEM_RXOFF 0x1FF /* 50/50 split */
+
+/* Helpful constants */
+#define ETHERFRAME_LEN 1518 /* Length of ethernet frame */
+#define ETHER_FCS_LEN 4 /* Length of frame check seq */
+#define RX_MEM_END 0x2BC
+
+struct netcard {
+ struct et131x_iospace *io;
+};
+
+static struct pci_device *dev;
+static struct netcard g_card;
+static struct timer tmr;
+
+/*
+ * Software reset the ET131X
+ *
+ * @io: Register space
+ */
+static void
+et131x_soft_reset(struct netcard *card)
+{
+ struct et131x_iospace *io = card->io;
+ uint32_t tmp;
+
+ tmp = (
+ MAC_CFG1_RESET_TXMC |
+ MAC_CFG1_RESET_RXMC |
+ MAC_CFG1_RESET_TXFUNC |
+ MAC_CFG1_RESET_RXFUNC |
+ MAC_CFG1_SOFTRST |
+ MAC_CFG1_SIMRST
+ );
+
+ /*
+ * Reset the MAC core, bring it down. After that,
+ * we perform a global reset to bring the whole
+ * chip down.
+ */
+ mmio_write32(&io->mac.cfg1, tmp);
+ mmio_write32(&io->global.sw_reset, GBL_RESET_ALL);
+
+ /*
+ * Reset the MAC again for good measure, but
+ * this time a little softer. We already slammed
+ * the poor thing.
+ */
+ tmp &= ~(MAC_CFG1_SOFTRST | MAC_CFG1_SIMRST);
+ mmio_write32(&io->mac.cfg1, tmp);
+ mmio_write32(&io->mac.cfg1, 0);
+}
+
+/*
+ * Write to the PHY through MII
+ *
+ * @io: Register space
+ * @addr: PHY address
+ * @reg: PHY register
+ * @v: Value to write
+ */
+static int
+et131x_mii_write(struct netcard *card, uint8_t addr, uint8_t reg, uint16_t v)
+{
+ struct et131x_iospace *io = card->io;
+ uint16_t mii_addr;
+ uint32_t tmp, mgmt_addr_old;
+ uint32_t mgmt_cmd_old;
+ uint8_t ndelay = 0;
+ int retval = 0;
+
+ /* Save MII management regs state */
+ mgmt_cmd_old = mmio_read32(&io->mac.mii_mgmt_cmd);
+ mgmt_addr_old = mmio_read32(&io->mac.mii_mgmt_addr);
+ mii_addr = MAC_MII_ADDR(addr, reg);
+
+ /*
+ * Stop any transactions that are currently
+ * happening on the MDIO bus and prepare the
+ * write.
+ */
+ mmio_write32(&io->mac.mii_mgmt_cmd, 0);
+ mmio_write32(&io->mac.mii_mgmt_addr, mii_addr);
+ mmio_write32(&io->mac.mii_mgmt_ctrl, v);
+
+ for (;;) {
+ tmr.usleep(50);
+ ++ndelay;
+
+ tmp = mmio_read32(&io->mac.mii_mgmt_indicator);
+ if (!ISSET(tmp, MAC_MGMT_BUSY))
+ break;
+ if (ndelay >= 50)
+ break;
+ }
+
+ if (ndelay >= 50) {
+ pr_error("could not write PHY reg %x (status=%x)\n", reg, tmp);
+ retval = -EIO;
+ goto done;
+ }
+
+done:
+ /* Stop operations and restore state */
+ mmio_write32(&io->mac.mii_mgmt_cmd, 0);
+ mmio_write32(&io->mac.mii_mgmt_addr, mgmt_addr_old);
+ mmio_write32(&io->mac.mii_mgmt_cmd, mgmt_cmd_old);
+ return retval;
+}
+
+/*
+ * Initialize PCI related things for the
+ * chip.
+ */
+static void
+et131x_init_pci(void)
+{
+ uint32_t tmp;
+
+ /* Enable bus mastering and MMIO */
+ tmp = pci_readl(dev, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(dev, PCIREG_CMDSTATUS, tmp);
+}
+
+/*
+ * Blink both LEDs of the card
+ *
+ * @io: Register space
+ * @count: Number of times to blink
+ * @delay: Millisecond delay between blinks
+ */
+static void
+et131x_blink(struct netcard *card, uint32_t count, uint16_t delay)
+{
+ uint16_t on_val;
+
+ on_val = (LED_ON << LED_LINK_SHIFT);
+ on_val |= (LED_ON << LED_TXRX_SHIFT);
+ for (uint32_t i = 0; i < count; ++i) {
+ et131x_mii_write(card, 0, PHY_LED2, on_val);
+ tmr.msleep(delay);
+ et131x_mii_write(card, 0, PHY_LED2, LED_ALL_OFF);
+ tmr.msleep(delay);
+ }
+}
+
+/*
+ * Initialize the MAC into a functional
+ * state.
+ *
+ * @io: Register space.
+ */
+static void
+et131x_mac_init(struct netcard *card)
+{
+ struct et131x_iospace *io = card->io;
+ struct mac_regs *mac = &io->mac;
+ struct global_regs *global = &io->global;
+ struct netif_addr addr;
+ uint32_t ipg_tmp, tmp;
+
+ /*
+ * Okay so we need to reset the card so it doesn't
+ * do undefined bullshit. God forbid we get undefined
+ * behaviour without having a fucking official datasheet.
+ * Most would end themselves right then and there.
+ *
+ * Now, after we've done that, we must ensure that any
+ * packets larger than ETHERFRAME_LEN are truncated by
+ * the MAC. Again, something like an internal buffer
+ * overrun during TX/RX would be quite fucking horrible.
+ *
+ * We also want to clear the MAC interface control and MII
+ * clock to ensure it is in a known state.
+ */
+ et131x_soft_reset(card);
+ mmio_write32(&mac->max_fm_len, ETHERFRAME_LEN);
+ mmio_write32(&mac->if_ctrl, 0);
+ mmio_write32(&mac->mii_mgmt_cfg, MAC_MIIMGMT_CLK_RST);
+
+ /*
+ * Split the RX/TX memory 50/50, put the internal RX
+ * buffer right at the start into the first half, and
+ * the TX buffer right after the RX buffer.
+ */
+ mmio_write32(&global->rxq_start, 0);
+ mmio_write32(&global->rxq_end, RX_MEM_END);
+ mmio_write32(&global->txq_start, RX_MEM_END + 1);
+ mmio_write32(&global->txq_end, INTERNAL_MEMSIZE - 1);
+
+ /* Disable loopbacks, watchdog timer, clear MSI config */
+ mmio_write32(&global->loopback, 0);
+ mmio_write32(&global->msi_config, 0);
+ mmio_write32(&global->watchdog_timer, 0);
+
+ /*
+ * Set up half duplex config
+ *
+ * - BEB trunc (0xA)
+ * - Excess defer
+ * - Re-transmit (0xF)
+ * - Collision window
+ */
+ mmio_write32(&mac->hfdp, 0x00A1F037);
+
+ /*
+ * Setup the MAC interpacket gap register
+ *
+ * - IPG1 (0x38)
+ * - IPG2 (0x58)
+ * - B2B (0x60)
+ */
+ ipg_tmp = ((0x50 << 8) | 0x38005860);
+ mmio_write32(&mac->ipg, ipg_tmp);
+
+ /* MAC address dword 0 */
+ tmp = pci_readl(dev, PCI_MAC_ADDRESS);
+ addr.data[0] = tmp & 0xFF;
+ addr.data[1] = (tmp >> 8) & 0xFF;
+ addr.data[2] = (tmp >> 16) & 0xFF;
+ addr.data[3] = (tmp >> 24) & 0xFF;
+
+ /* MAC address word 1 */
+ tmp = pci_readl(dev, PCI_MAC_ADDRESS + 4);
+ addr.data[4] = tmp & 0xFF;
+ addr.data[5] = (tmp >> 8) & 0xFF;
+
+ /* Print out the MAC address */
+ pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
+ (uint64_t)addr.data[0], (uint64_t)addr.data[1],
+ (uint64_t)addr.data[2], (uint64_t)addr.data[3],
+ (uint64_t)addr.data[4], (uint64_t)addr.data[5]);
+}
+
+static int
+et131x_init(void)
+{
+ struct pci_lookup lookup;
+ int error;
+
+ lookup.vendor_id = VENDOR_ID;
+ lookup.device_id = DEVICE_ID;
+ dev = pci_get_device(lookup, PCI_VENDOR_ID | PCI_DEVICE_ID);
+ if (dev == NULL) {
+ return -ENODEV;
+ }
+
+ pr_trace("Agere ET1310 Ethernet ctl <phy? at pci%d:%x.%x.%d>\n",
+ dev->bus, dev->device_id, dev->func,
+ dev->slot);
+
+ /* Try to request a general purpose timer */
+ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
+ pr_error("failed to fetch general purpose timer\n");
+ return -ENODEV;
+ }
+
+ /* Ensure it has get_time_usec() */
+ if (tmr.usleep == NULL) {
+ pr_error("general purpose timer has no usleep()\n");
+ return -ENODEV;
+ }
+
+ if ((error = pci_map_bar(dev, 0, (void *)&g_card.io)) != 0) {
+ return error;
+ }
+
+ et131x_init_pci();
+ et131x_mac_init(&g_card);
+ et131x_blink(&g_card, 4, 150);
+ return 0;
+}
+
+DRIVER_DEFER(et131x_init, "et131x");
diff --git a/sys/dev/phy/rt8139.c b/sys/dev/phy/rtl.c
index ab8a6c0..d096d1a 100644
--- a/sys/dev/phy/rt8139.c
+++ b/sys/dev/phy/rtl.c
@@ -30,31 +30,30 @@
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/syslog.h>
+#include <sys/spinlock.h>
#include <sys/driver.h>
#include <sys/device.h>
#include <dev/pci/pci.h>
-#include <dev/phy/rt8139.h>
+#include <dev/phy/rtl.h>
#include <dev/timer.h>
#include <dev/pci/pciregs.h>
-#include <net/if_ether.h>
+#include <net/netbuf.h>
+#include <net/if_var.h>
#include <vm/physmem.h>
+#include <vm/dynalloc.h>
#include <vm/vm.h>
#include <machine/pio.h>
+#include <machine/intr.h>
#include <string.h>
-/* TODO: Make this smoother */
-#if defined(__x86_64__)
-#include <machine/intr.h>
-#include <machine/ioapic.h>
-#include <machine/lapic.h>
-#include <machine/idt.h>
-#endif
+#define IFNAME "rt0"
-#define pr_trace(fmt, ...) kprintf("rt8139: " fmt, ##__VA_ARGS__)
+#define pr_trace(fmt, ...) kprintf("rt81xx: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
#define RX_BUF_SIZE 3 /* In pages */
#define RX_REAL_BUF_SIZE 8192 /* In bytes */
+#define TXQ_ENTRIES 4
#define RX_PTR_MASK (~3)
@@ -65,12 +64,26 @@
#define HAVE_PIO 0
#endif /* _MACHINE_HAVE_PIO */
+static struct spinlock netif_lock;
+static struct netbuf netif_buf[TXQ_ENTRIES];
static struct pci_device *dev;
+static struct netif netif;
static struct timer tmr;
-static struct etherdev wire;
+static uint32_t tx_ptr = 0;
+static uint32_t netif_enq_ptr = 0;
static uint16_t ioport;
static paddr_t rxbuf, txbuf;
+/* TXAD regs */
+static uint16_t tsads[TXQ_ENTRIES] = {
+ RT_TXAD_N(0), RT_TXAD_N(4),
+ RT_TXAD_N(8), RT_TXAD_N(12)
+};
+static uint16_t tsds[TXQ_ENTRIES] = {
+ RT_TXSTATUS_N(0), RT_TXSTATUS_N(4),
+ RT_TXSTATUS_N(8), RT_TXSTATUS_N(8)
+};
+
/*
* Write to an RTL8139 register
*
@@ -159,53 +172,112 @@ rt_poll(uint8_t reg, uint8_t size, uint32_t bits, bool pollset)
return val;
}
-#if defined(__x86_64__)
-__isr static void
-rt8139_pin_irq(void *sp)
+static int
+rt_tx(void *packet, size_t len)
+{
+ static uint32_t tx_ptr = 0;
+ void *tx_data;
+ paddr_t tx_pa;
+
+ tx_data = dynalloc(len);
+ if (tx_data == NULL) {
+ return -ENOMEM;
+ }
+
+ memcpy(tx_data, packet, len);
+ tx_pa = VIRT_TO_PHYS(tx_data);
+ rt_write(tsads[tx_ptr], 4, tx_pa);
+ rt_write(tsds[tx_ptr++], 4, len);
+ if (tx_ptr > TXQ_ENTRIES - 1) {
+ tx_ptr = 0;
+ }
+ return 0;
+}
+
+static void
+__rt81xx_tx_start(struct netif *nifp)
+{
+ struct netbuf *dest;
+ int error;
+
+ for (int i = 0; i < netif_enq_ptr; ++i) {
+ dest = &netif_buf[i];
+ error = rt_tx(dest->data, dest->len);
+ if (error < 0) {
+ pr_error("tx_start fail @queue %d (errno=%d)\n", i, error);
+ }
+ }
+}
+
+static void
+rt81xx_tx_start(struct netif *nifp)
+{
+ spinlock_acquire(&netif_lock);
+ __rt81xx_tx_start(nifp);
+ spinlock_release(&netif_lock);
+}
+
+static int
+rt81xx_tx_enq(struct netif *nifp, struct netbuf *nbp, void *data)
+{
+ struct netbuf *dest;
+
+ spinlock_acquire(&netif_lock);
+ dest = &netif_buf[netif_enq_ptr++];
+ memcpy(dest, nbp, sizeof(*dest));
+
+ if (netif_enq_ptr > TXQ_ENTRIES - 1) {
+ __rt81xx_tx_start(nifp);
+ netif_enq_ptr = 0;
+ }
+ spinlock_release(&netif_lock);
+ return 0;
+}
+
+static int
+rt81xx_intr(void *sp)
{
- static uint32_t packet_ptr = 0;
uint16_t len;
uint16_t *p;
uint16_t status;
status = rt_read(RT_INTRSTATUS, 2);
- p = (uint16_t *)(rxbuf + packet_ptr);
+ p = (uint16_t *)(rxbuf + tx_ptr);
len = *(p + 1); /* Length after header */
p += 2; /* Points to data now */
- if (status & RT_TOK) {
- return;
+ if (!ISSET(status, RT_TOK | RT_ROK)) {
+ return 0;
+ }
+
+ if (ISSET(status, RT_TOK)) {
+ pr_trace("sent packet\n");
+ return 1;
}
/* Update rxbuf offset in CAPR */
- packet_ptr = (packet_ptr + len + 4 + 3) & RX_PTR_MASK;
- if (packet_ptr > RX_REAL_BUF_SIZE) {
- packet_ptr -= RX_REAL_BUF_SIZE;
+ tx_ptr = (tx_ptr + len + 4 + 3) & RX_PTR_MASK;
+ if (tx_ptr > RX_REAL_BUF_SIZE) {
+ tx_ptr -= RX_REAL_BUF_SIZE;
}
- rt_write(RT_RXBUFTAIL, 2, packet_ptr - 0x10);
+ rt_write(RT_RXBUFTAIL, 2, tx_ptr - 0x10);
rt_write(RT_INTRSTATUS, 2, RT_ACKW);
- lapic_eoi();
+ return 1; /* handled */
}
static int
-rtl8139_irq_init(void)
+rt81xx_irq_init(void)
{
- int vec;
+ struct intr_hand ih;
- vec = intr_alloc_vector("rt8139", IPL_BIO);
- if (vec < 0) {
- return vec;
+ ih.func = rt81xx_intr;
+ ih.priority = IPL_BIO;
+ ih.irq = dev->irq_line;
+ if (intr_register("rt81xx", &ih) == NULL) {
+ return -EIO;
}
-
- /* Map interrupt vector to IRQ */
- idt_set_desc(vec, IDT_INT_GATE, ISR(rt8139_pin_irq), 0);
- ioapic_set_vec(dev->irq_line, vec);
- ioapic_irq_unmask(dev->irq_line);
return 0;
}
-#else
-#define rtl8139_irq_init(...) -ENOTSUP
-#endif
static void
rt_init_pci(void)
@@ -221,6 +293,7 @@ rt_init_pci(void)
static int
rt_init_mac(void)
{
+ struct netif_addr *addr = &netif.addr;
uint8_t conf;
uint32_t tmp;
int error;
@@ -256,20 +329,20 @@ rt_init_mac(void)
/* MAC address dword 0 */
tmp = rt_read(RT_IDR0, 4);
- wire.mac_addr[0] = tmp & 0xFF;
- wire.mac_addr[1] = (tmp >> 8) & 0xFF;
- wire.mac_addr[2] = (tmp >> 16) & 0xFF;
- wire.mac_addr[3] = (tmp >> 24) & 0xFF;
+ addr->data[0] = tmp & 0xFF;
+ addr->data[1] = (tmp >> 8) & 0xFF;
+ addr->data[2] = (tmp >> 16) & 0xFF;
+ addr->data[3] = (tmp >> 24) & 0xFF;
/* MAC address word 1 */
tmp = rt_read(RT_IDR2, 4);
- wire.mac_addr[4] = (tmp >> 16) & 0xFF;
- wire.mac_addr[5] = (tmp >> 24) & 0xFF;
+ addr->data[4] = (tmp >> 16) & 0xFF;
+ addr->data[5] = (tmp >> 24) & 0xFF;
pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
- (uint64_t)wire.mac_addr[0], (uint64_t)wire.mac_addr[1],
- (uint64_t)wire.mac_addr[2], (uint64_t)wire.mac_addr[3],
- (uint64_t)wire.mac_addr[4], (uint64_t)wire.mac_addr[5]);
+ (uint64_t)addr->data[0], (uint64_t)addr->data[1],
+ (uint64_t)addr->data[2], (uint64_t)addr->data[3],
+ (uint64_t)addr->data[4], (uint64_t)addr->data[5]);
/*
* Alright, now we don't want those EEM bits
@@ -293,6 +366,11 @@ rt_init_mac(void)
return -ENOMEM;
}
+ memcpy(netif.name, IFNAME, strlen(IFNAME) + 1);
+ netif.tx_enq = rt81xx_tx_enq;
+ netif.tx_start = rt81xx_tx_start;
+ netif_add(&netif);
+
/*
* Configure the chip:
*
@@ -310,19 +388,17 @@ rt_init_mac(void)
* - Enable interrupts through ROK/TOK
* - Enable RX state machines
*
- * TODO: Support TX
- *
*/
- rtl8139_irq_init();
+ rt81xx_irq_init();
rt_write(RT_RXBUF, 4, rxbuf);
rt_write(RT_RXCONFIG, 4, RT_AB | RT_AM | RT_APM | RT_AAP);
rt_write(RT_INTRMASK, 2, RT_ROK | RT_TOK);
- rt_write(RT_CHIPCMD, 1, RT_RE);
+ rt_write(RT_CHIPCMD, 1, RT_RE | RT_TE);
return 0;
}
static int
-rt813l_init(void)
+rt81xx_init(void)
{
struct pci_lookup lookup;
@@ -364,4 +440,4 @@ rt813l_init(void)
return rt_init_mac();
}
-DRIVER_EXPORT(rt813l_init);
+DRIVER_DEFER(rt81xx_init, "rtl81xx");
diff --git a/sys/dev/random/entropy.c b/sys/dev/random/entropy.c
new file mode 100644
index 0000000..4e723a4
--- /dev/null
+++ b/sys/dev/random/entropy.c
@@ -0,0 +1,55 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <dev/random/entropy.h>
+#include <crypto/siphash.h>
+
+void
+mix_entropy(struct entropy_pool *ep, const uint8_t *input,
+ size_t input_len, uint32_t input_entropy_bits)
+{
+ char key[16] = {0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf};
+ uint64_t hash_result;
+ uint8_t buffer[ENTROPY_POOL_SIZE + input_len];
+ memcpy(buffer, ep->pool, ENTROPY_POOL_SIZE);
+ memcpy(buffer + ENTROPY_POOL_SIZE, input, input_len);
+
+ hash_result = siphash24(buffer, sizeof(buffer), key);
+
+ for (int i = 0; i < 8; ++i) {
+ ep->pool[i] ^= (hash_result >> (i * 8)) & 0xFF;
+ }
+
+ ep->entropy_bits += input_entropy_bits;
+ if (ep->entropy_bits > ENTROPY_POOL_SIZE * 8) {
+ ep->entropy_bits = ENTROPY_POOL_SIZE * 8;
+ }
+}
diff --git a/sys/dev/random/random.c b/sys/dev/random/random.c
new file mode 100644
index 0000000..9bca719
--- /dev/null
+++ b/sys/dev/random/random.c
@@ -0,0 +1,88 @@
+/*
+ * 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/sio.h>
+#include <sys/device.h>
+#include <sys/driver.h>
+#include <dev/random/entropy.h>
+#include <crypto/chacha20.h>
+#include <crypto/siphash.h>
+#include <fs/devfs.h>
+#include <string.h>
+
+static struct cdevsw random_cdevsw;
+static struct entropy_pool entropy;
+
+uint8_t key[32] = {0};
+uint8_t nonce[12] = {0};
+uint32_t state[16];
+uint32_t tsc;
+
+static inline uint64_t
+read_tsc(void)
+{
+ uint32_t lo, hi;
+ __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi));
+ return ((uint64_t)hi << 32) | lo;
+}
+
+static int
+random_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ tsc = read_tsc();
+ mix_entropy(&entropy, (uint8_t *)&tsc, sizeof(tsc), 1);
+
+ chacha20_init(state, entropy.pool, nonce, 0);
+ chacha20_encrypt(state, NULL, sio->buf, sio->len);
+
+ return sio->len;
+}
+
+static int
+random_init(void)
+{
+ char devname[] = "random";
+ devmajor_t major;
+ dev_t dev;
+
+ /* Register the device here */
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &random_cdevsw);
+ devfs_create_entry(devname, major, dev, 0444);
+
+ return 0;
+}
+
+static struct cdevsw random_cdevsw = {
+ .read = random_read,
+ .write = nowrite
+};
+
+DRIVER_EXPORT(random_init, "random");
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index d68f98e..e14cb44 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -37,6 +37,7 @@
#include <dev/usb/xhciregs.h>
#include <dev/usb/xhcivar.h>
#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
#include <dev/acpi/acpi.h>
#include <vm/physmem.h>
#include <vm/dynalloc.h>
@@ -56,10 +57,11 @@
static struct pci_device *hci_dev;
static struct timer tmr;
-__attribute__((__interrupt__)) static void
-xhci_common_isr(void *sf)
+static int
+xhci_intr(void *sf)
{
pr_trace("received xHCI interrupt (via PCI MSI-X)\n");
+ return 1; /* handled */
}
/*
@@ -69,11 +71,16 @@ xhci_common_isr(void *sf)
static inline uint32_t *
xhci_get_portsc(struct xhci_hc *hc, uint8_t portno)
{
- if (portno > hc->maxports) {
- portno = hc->maxports;
+ if (portno >= hc->maxports) {
+ return NULL;
+ }
+
+ /* Zero based */
+ if (portno > 0) {
+ --portno;
}
- return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * (portno - 1)));
+ return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * portno));
}
static int
@@ -174,6 +181,7 @@ xhci_init_scratchpads(struct xhci_hc *hc)
struct xhci_caps *caps = XHCI_CAPS(hc->base);
uint16_t max_bufs_lo, max_bufs_hi;
uint16_t max_bufs;
+ size_t len;
uintptr_t *bufarr, tmp;
max_bufs_lo = XHCI_MAX_SP_LO(caps->hcsparams1);
@@ -187,8 +195,9 @@ xhci_init_scratchpads(struct xhci_hc *hc)
return 0;
}
- pr_trace("using %d pages for xHC scratchpads\n");
- bufarr = dynalloc_memalign(sizeof(uintptr_t)*max_bufs, 0x1000);
+ len = sizeof(uint64_t) * max_bufs;
+ pr_trace("using %d bytes for xHC scratchpads\n", len);
+ bufarr = dynalloc_memalign(len, 0x1000);
if (bufarr == NULL) {
pr_error("failed to allocate scratchpad buffer array\n");
return -1;
@@ -196,12 +205,12 @@ xhci_init_scratchpads(struct xhci_hc *hc)
for (size_t i = 0; i < max_bufs; ++i) {
tmp = vm_alloc_frame(1);
- memset(PHYS_TO_VIRT(tmp), 0, 0x1000);
if (tmp == 0) {
/* TODO: Shutdown, free memory */
pr_error("failed to fill scratchpad buffer array\n");
return -1;
}
+ memset(PHYS_TO_VIRT(tmp), 0, 0x1000);
bufarr[i] = tmp;
}
@@ -217,7 +226,7 @@ xhci_alloc_dcbaa(struct xhci_hc *hc)
{
size_t size;
- size = sizeof(uintptr_t) * hc->maxslots;
+ size = sizeof(uintptr_t) * (hc->maxslots + 1);
hc->dcbaap = dynalloc_memalign(size, 0x1000);
__assert(hc->dcbaap != NULL);
return VIRT_TO_PHYS(hc->dcbaap);
@@ -232,7 +241,7 @@ xhci_init_msix(struct xhci_hc *hc)
struct msi_intr intr;
intr.name = "xHCI MSI-X";
- intr.handler = xhci_common_isr;
+ intr.handler = xhci_intr;
return pci_enable_msix(hci_dev, &intr);
}
@@ -254,7 +263,7 @@ xhci_init_evring(struct xhci_hc *hc)
memset(segtab, 0, DEFAULT_PAGESIZE);
/* Set the size of the event ring segment table */
- erst_size = PTR_OFFSET(runtime, 0x28);
+ erst_size = PTR_OFFSET(runtime, XHCI_RT_ERSTSZ);
mmio_write32(erst_size, 1);
/* Allocate the event ring segment */
@@ -264,24 +273,24 @@ xhci_init_evring(struct xhci_hc *hc)
/* setup the event ring segment */
segtab->base = VIRT_TO_PHYS(tmp_p);
- segtab->base = ((uintptr_t)segtab->base) + (2 * 4096) & ~0xF;
+ segtab->base = ((uintptr_t)segtab->base);
segtab->size = XHCI_EVRING_LEN;
/* Setup the event ring dequeue pointer */
- erdp = PTR_OFFSET(runtime, 0x38);
+ erdp = PTR_OFFSET(runtime, XHCI_RT_ERDP);
mmio_write64(erdp, segtab->base);
/* Point ERSTBA to our event ring segment */
- erstba = PTR_OFFSET(runtime, 0x30);
+ erstba = PTR_OFFSET(runtime, XHCI_RT_ERSTBA);
mmio_write64(erstba, VIRT_TO_PHYS(segtab));
hc->evring = PHYS_TO_VIRT(segtab->base);
/* Setup interrupt moderation */
- imod = PTR_OFFSET(runtime, 0x24);
+ imod = PTR_OFFSET(runtime, XHCI_RT_IMOD);
mmio_write32(imod, XHCI_IMOD_DEFAULT);
/* Enable interrupts */
- iman = PTR_OFFSET(runtime, 0x20);
+ iman = PTR_OFFSET(runtime, XHCI_RT_IMAN);
tmp = mmio_read32(iman);
mmio_write32(iman, tmp | XHCI_IMAN_IE);
}
@@ -331,6 +340,13 @@ xhci_reset(struct xhci_hc *hc)
return error;
}
+ /* Wait longer if the xHC is not ready */
+ error = xhci_poll32(&opregs->usbsts, USBSTS_CNR, false);
+ if (error < 0) {
+ pr_error("xhci_reset: xHC ready wait timeout\n");
+ return error;
+ }
+
return 0;
}
@@ -368,13 +384,33 @@ xhci_start_hc(struct xhci_hc *hc)
/* Don't start up if we are already running */
usbcmd = mmio_read32(&opregs->usbcmd);
if (ISSET(usbcmd, USBCMD_RUN))
- return -EBUSY;
+ return 0;
usbcmd |= USBCMD_RUN;
mmio_write32(&opregs->usbcmd, usbcmd);
return 0;
}
+/*
+ * Stop and bring down the host controller.
+ * Returns 0 on success.
+ */
+static int
+xhci_stop_hc(struct xhci_hc *hc)
+{
+ struct xhci_opregs *opregs = hc->opregs;
+ uint32_t usbcmd;
+
+ /* Don't continue if we aren't running */
+ usbcmd = mmio_read32(&opregs->usbcmd);
+ if (!ISSET(usbcmd, USBCMD_RUN))
+ return 0;
+
+ usbcmd &= ~USBCMD_RUN;
+ mmio_write32(&opregs->usbcmd, usbcmd);
+ return 0;
+}
+
static int
xhci_init_ports(struct xhci_hc *hc)
{
@@ -384,6 +420,9 @@ xhci_init_ports(struct xhci_hc *hc)
for (size_t i = 1; i < hc->maxports; ++i) {
portsc_p = xhci_get_portsc(hc, i);
+ if (portsc_p == NULL) {
+ continue;
+ }
portsc = mmio_read32(portsc_p);
/*
@@ -457,8 +496,15 @@ xhci_init_hc(struct xhci_hc *hc)
return -1;
}
+ pr_trace("stopping xHC chip...\n");
+ if ((error = xhci_stop_hc(hc)) != 0) {
+ pr_error("run/stop timeout\n");
+ return error;
+ }
+
pr_trace("resetting xHC chip...\n");
if ((error = xhci_reset(hc)) != 0) {
+ pr_error("reset timeout\n");
return error;
}
@@ -495,6 +541,16 @@ xhci_init_hc(struct xhci_hc *hc)
return 0;
}
+static void
+xhci_init_pci(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_readl(hci_dev, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(hci_dev, PCIREG_CMDSTATUS, tmp);
+}
+
static int
xhci_init(void)
{
@@ -527,7 +583,8 @@ xhci_init(void)
return -ENODEV;
}
+ xhci_init_pci();
return xhci_init_hc(&xhc);
}
-DRIVER_EXPORT(xhci_init);
+DRIVER_EXPORT(xhci_init, "xhci");
diff --git a/sys/dev/video/fbdev.c b/sys/dev/video/fbdev.c
index f21c769..f58a813 100644
--- a/sys/dev/video/fbdev.c
+++ b/sys/dev/video/fbdev.c
@@ -28,17 +28,65 @@
*/
#include <sys/types.h>
+#include <sys/errno.h>
#include <sys/limine.h>
+#include <sys/device.h>
+#include <sys/driver.h>
+#include <sys/fbdev.h>
#include <dev/video/fbdev.h>
+#include <fs/devfs.h>
+#include <fs/ctlfs.h>
+#include <vm/vm.h>
+#include <string.h>
#define FRAMEBUFFER \
framebuffer_req.response->framebuffers[0]
+static struct cdevsw fb_cdevsw;
+static const struct ctlops fb_size_ctl;
static volatile struct limine_framebuffer_request framebuffer_req = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
};
+static paddr_t
+fbdev_mmap(dev_t dev, size_t size, off_t off, int flags)
+{
+ size_t max_bounds;
+
+ max_bounds = FRAMEBUFFER->pitch * FRAMEBUFFER->height;
+ if ((off + size) > max_bounds) {
+ return 0;
+ }
+
+ return VIRT_TO_PHYS(FRAMEBUFFER->address);
+}
+
+static int
+ctl_attr_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct fbattr attr;
+ size_t len = sizeof(attr);
+
+ if (sio == NULL) {
+ return -EINVAL;
+ }
+ if (sio->buf == NULL) {
+ return -EINVAL;
+ }
+
+ if (len > sio->len) {
+ len = sio->len;
+ }
+
+ attr.width = FRAMEBUFFER->width;
+ attr.height = FRAMEBUFFER->height;
+ attr.pitch = FRAMEBUFFER->pitch;
+ attr.bpp = FRAMEBUFFER->bpp;
+ memcpy(sio->buf, &attr, len);
+ return len;
+}
+
struct fbdev
fbdev_get(void)
{
@@ -51,3 +99,39 @@ fbdev_get(void)
ret.bpp = FRAMEBUFFER->bpp;
return ret;
}
+
+static int
+fbdev_init(void)
+{
+ struct ctlfs_dev ctl;
+ char devname[] = "fb0";
+ devmajor_t major;
+ dev_t dev;
+
+ /* Register the device here */
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &fb_cdevsw);
+ devfs_create_entry(devname, major, dev, 0444);
+
+ /* Register control files */
+ ctl.mode = 0444;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &fb_size_ctl;
+ ctlfs_create_entry("attr", &ctl);
+ return 0;
+}
+
+static struct cdevsw fb_cdevsw = {
+ .read = noread,
+ .write = nowrite,
+ .mmap = fbdev_mmap
+};
+
+static const struct ctlops fb_size_ctl = {
+ .read = ctl_attr_read,
+ .write = NULL,
+};
+
+DRIVER_EXPORT(fbdev_init, "fbdev");
diff --git a/sys/fs/ctlfs.c b/sys/fs/ctlfs.c
index efbf448..b86fa0a 100644
--- a/sys/fs/ctlfs.c
+++ b/sys/fs/ctlfs.c
@@ -234,6 +234,35 @@ ctlfs_lookup(struct vop_lookup_args *args)
return 0;
}
+static int
+ctlfs_get_ops(struct vnode *vp, struct ctlfs_entry **enpres,
+ const struct ctlops **iopres)
+{
+ const struct ctlops *iop;
+ struct ctlfs_entry *enp;
+
+ if (enpres == NULL || iopres == NULL) {
+ return -EINVAL;
+ }
+
+ if ((enp = vp->data) == NULL) {
+ pr_error("no vnode data for ctlfs entry\n");
+ return -EIO;
+ }
+ if (enp->magic != CTLFS_ENTRY_MAG) {
+ pr_error("ctlfs entry has bad magic\n");
+ return -EIO;
+ }
+ if ((iop = enp->io) == NULL) {
+ pr_error("no i/o ops for ctlfs entry\n");
+ return -EIO;
+ }
+
+ *enpres = enp;
+ *iopres = iop;
+ return 0;
+}
+
/*
* Create a ctlfs node (directory) within the
* root fs.
@@ -344,18 +373,10 @@ ctlfs_read(struct vnode *vp, struct sio_txn *sio)
const struct ctlops *iop;
struct ctlfs_entry *enp;
struct ctlfs_dev dev;
+ int error;
- if ((enp = vp->data) == NULL) {
- pr_error("no vnode data for ctlfs entry\n");
- return -EIO;
- }
- if (enp->magic != CTLFS_ENTRY_MAG) {
- pr_error("ctlfs entry has bad magic\n");
- return -EIO;
- }
- if ((iop = enp->io) == NULL) {
- pr_error("no i/o ops for ctlfs entry\n");
- return -EIO;
+ if ((error = ctlfs_get_ops(vp, &enp, &iop)) < 0) {
+ return error;
}
if (iop->read == NULL) {
pr_trace("no read op for ctlfs entry\n");
@@ -368,28 +389,40 @@ ctlfs_read(struct vnode *vp, struct sio_txn *sio)
return iop->read(&dev, sio);
}
+/*
+ * Write a control file
+ *
+ * Args passed to driver:
+ * - ctlfs_dev.ctlname
+ * - ctlfs_dev.iop
+ * - ctlfs_dev.mode
+ */
static int
-ctlfs_reclaim(struct vnode *vp)
+ctlfs_write(struct vnode *vp, struct sio_txn *sio)
{
- struct ctlfs_hdr *hp;
+ const struct ctlops *iop;
+ struct ctlfs_entry *enp;
+ struct ctlfs_dev dev;
+ int error;
- if (vp->data == NULL) {
- return 0;
+ if ((error = ctlfs_get_ops(vp, &enp, &iop)) < 0) {
+ return error;
}
-
- /* Ensure the magic is correct */
- hp = vp->data;
- switch (hp->magic) {
- case CTLFS_NODE_MAG:
- case CTLFS_ENTRY_MAG:
- dynfree(hp->name);
- break;
- default:
- pr_error("reclaim: bad magic in vp\n");
- break;
+ if (iop->write == NULL) {
+ pr_trace("no write op for ctlfs entry\n");
+ return -EIO;
}
- dynfree(vp->data);
+ dev.ctlname = enp->name;
+ dev.ops = iop;
+ dev.mode = enp->mode;
+ return iop->write(&dev, sio);
+}
+
+static int
+ctlfs_reclaim(struct vnode *vp)
+{
+ vp->data = NULL;
return 0;
}
@@ -397,8 +430,9 @@ static const struct vops ctlfs_vops = {
.lookup = ctlfs_lookup,
.read = ctlfs_read,
.getattr = NULL,
- .write = NULL,
- .reclaim = ctlfs_reclaim
+ .write = ctlfs_write,
+ .reclaim = ctlfs_reclaim,
+ .create = NULL
};
const struct vfsops g_ctlfs_vfsops = {
diff --git a/sys/fs/devfs.c b/sys/fs/devfs.c
index 024239d..3bd1b11 100644
--- a/sys/fs/devfs.c
+++ b/sys/fs/devfs.c
@@ -30,6 +30,8 @@
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
#include <sys/mount.h>
#include <sys/device.h>
#include <fs/devfs.h>
@@ -37,6 +39,7 @@
#include <string.h>
struct devfs_node {
+ struct devstat stat;
char *name;
uint8_t is_block : 1;
mode_t mode;
@@ -126,6 +129,8 @@ devfs_lookup(struct vop_lookup_args *args)
vp->data = dnp;
vp->vops = &g_devfs_vops;
+ vp->major = dnp->major;
+ vp->dev = dnp->dev;
*args->vpp = vp;
return 0;
}
@@ -136,6 +141,8 @@ devfs_getattr(struct vop_getattr_args *args)
struct vnode *vp;
struct vattr *attr;
struct devfs_node *dnp;
+ struct bdevsw *bdev;
+ size_t size = 0;
vp = args->vp;
if ((dnp = vp->data) == NULL) {
@@ -145,6 +152,13 @@ devfs_getattr(struct vop_getattr_args *args)
return -EIO;
}
+ if (dnp->is_block) {
+ bdev = dev_get(dnp->major, dnp->dev);
+ if (bdev->bsize != NULL) {
+ size = bdev->bsize(dnp->dev);
+ }
+ }
+
/*
* Set stat attributes from device node structure
* found within vnode data.
@@ -153,20 +167,13 @@ devfs_getattr(struct vop_getattr_args *args)
* size is hardwired to 0.
*/
attr->mode = dnp->mode;
- attr->size = 0;
+ attr->size = size;
return 0;
}
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;
}
@@ -175,6 +182,7 @@ static int
devfs_read(struct vnode *vp, struct sio_txn *sio)
{
struct devfs_node *dnp;
+ struct devstat *statp;
void *devsw;
if ((dnp = vp->data) == NULL)
@@ -185,6 +193,9 @@ devfs_read(struct vnode *vp, struct sio_txn *sio)
if (!dnp->is_block)
return cdevsw_read(devsw, dnp->dev, sio);
+ statp = &dnp->stat;
+ ++statp->nreads;
+
/* Block device */
return bdevsw_read(devsw, dnp->dev, sio);
}
@@ -193,6 +204,7 @@ static int
devfs_write(struct vnode *vp, struct sio_txn *sio)
{
struct devfs_node *dnp;
+ struct devstat *statp;
void *devsw;
if ((dnp = vp->data) == NULL)
@@ -204,6 +216,9 @@ devfs_write(struct vnode *vp, struct sio_txn *sio)
return cdevsw_write(devsw, dnp->dev, sio);
}
+ statp = &dnp->stat;
+ ++statp->nwrites;
+
/* Block device */
return bdevsw_write(devsw, dnp->dev, sio);
}
@@ -228,6 +243,24 @@ devfs_init(struct fs_info *fip)
return 0;
}
+int
+devfs_devstat(struct vnode *vp, struct devstat *res)
+{
+ struct devfs_node *dnp;
+
+ if ((dnp = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Not supported on char devices */
+ if (!dnp->is_block) {
+ return -ENOTSUP;
+ }
+
+ *res = dnp->stat;
+ return 0;
+}
+
/*
* Create an entry within devfs.
*
@@ -240,6 +273,7 @@ int
devfs_create_entry(const char *name, devmajor_t major, dev_t dev, mode_t mode)
{
struct devfs_node *dnp;
+ struct devstat *statp;
size_t name_len;
dnp = dynalloc(sizeof(*dnp));
@@ -253,9 +287,13 @@ devfs_create_entry(const char *name, devmajor_t major, dev_t dev, mode_t mode)
return -ENOMEM;
}
+ statp = &dnp->stat;
+ statp->nwrites = 0;
+ statp->nreads = 0;
+
memcpy(dnp->name, name, name_len);
dnp->name[name_len] = '\0';
-
+ dnp->is_block = ISSET(mode, S_IFBLK) ? 1 : 0;
dnp->major = major;
dnp->dev = dev;
dnp->mode = mode;
@@ -268,7 +306,8 @@ const struct vops g_devfs_vops = {
.reclaim = devfs_reclaim,
.read = devfs_read,
.write = devfs_write,
- .getattr = devfs_getattr
+ .getattr = devfs_getattr,
+ .create = NULL
};
const struct vfsops g_devfs_vfsops = {
diff --git a/sys/fs/initramfs.c b/sys/fs/initramfs.c
index b12a64b..beb2e84 100644
--- a/sys/fs/initramfs.c
+++ b/sys/fs/initramfs.c
@@ -61,12 +61,16 @@ struct initramfs_node {
* @magic: Header magic ("OMAR")
* @len: Length of the file
* @namelen: Length of the filename
+ * @rev: OMAR revision
+ * @mode: File permissions
*/
struct __packed omar_hdr {
char magic[4];
uint8_t type;
uint8_t namelen;
uint32_t len;
+ uint8_t rev;
+ uint32_t mode;
};
static volatile struct limine_module_request mod_req = {
@@ -140,7 +144,7 @@ initramfs_get_file(const char *path, struct initramfs_node *res)
p += hdr->namelen;
if (strcmp(namebuf, path) == 0) {
- node.mode = 0700;
+ node.mode = hdr->mode;
node.size = hdr->len;
node.data = (void *)p;
*res = node;
@@ -223,6 +227,8 @@ initramfs_read(struct vnode *vp, struct sio_txn *sio)
return -EIO;
if (sio->buf == NULL)
return -EIO;
+ if (sio->len > n->size)
+ sio->len = n->size;
src = n->data;
dest = sio->buf;
@@ -277,7 +283,8 @@ const struct vops g_initramfs_vops = {
.read = initramfs_read,
.write = NULL,
.reclaim = initramfs_reclaim,
- .getattr = initramfs_getattr
+ .getattr = initramfs_getattr,
+ .create = NULL,
};
const struct vfsops g_initramfs_vfsops = {
diff --git a/sys/fs/tmpfs.c b/sys/fs/tmpfs.c
new file mode 100644
index 0000000..4fd9e85
--- /dev/null
+++ b/sys/fs/tmpfs.c
@@ -0,0 +1,430 @@
+/*
+ * 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/mount.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/panic.h>
+#include <sys/vnode.h>
+#include <vm/dynalloc.h>
+#include <vm/vm_obj.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <fs/tmpfs.h>
+#include <string.h>
+
+#define ROOT_RPATH "/tmp"
+#define TMPFS_BSIZE DEFAULT_PAGESIZE
+
+#define pr_trace(fmt, ...) kprintf("tmpfs: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static TAILQ_HEAD(, tmpfs_node) root;
+
+/*
+ * Generate a vnode for a specific tmpfs
+ * node.
+ */
+static int
+tmpfs_ref(struct tmpfs_node *np)
+{
+ struct vnode *vp = NULL;
+ int retval = 0;
+
+ if (np->vp == NULL) {
+ spinlock_acquire(&np->lock);
+ retval = vfs_alloc_vnode(&vp, np->type);
+ np->vp = vp;
+ spinlock_release(&np->lock);
+ }
+
+ if (vp != NULL) {
+ vp->data = np;
+ vp->vops = &g_tmpfs_vops;
+ }
+
+ return retval;
+}
+
+/*
+ * Perform lookup within the tmpfs namespace
+ *
+ * XXX: This operations is serialized
+ * TODO: Support multiple directories (only fs root now)
+ *
+ * @rpath: /tmp/ relative path to lookup
+ * @res: The result is written here (must NOT be NULL)
+ */
+static int
+tmpfs_do_lookup(const char *rpath, struct tmpfs_node **res)
+{
+ struct tmpfs_node *cnp;
+ struct tmpfs_node *dirent;
+ int error = 0;
+
+ /*
+ * If the directory is the node that we are
+ * looking for, return it. But if it is not
+ * and it is empty then there is nothing
+ * we can do.
+ */
+ cnp = TAILQ_FIRST(&root);
+ if (strcmp(cnp->rpath, rpath) == 0) {
+ *res = cnp;
+ return 0;
+ }
+ if (TAILQ_NELEM(&cnp->dirents) == 0) {
+ return -ENOENT;
+ }
+
+ /*
+ * Go through each tmpfs dirent to see if we can
+ * find the file we are looking for.
+ */
+ spinlock_acquire(&cnp->lock);
+ dirent = TAILQ_FIRST(&cnp->dirents);
+ while (dirent != NULL) {
+ if (strcmp(dirent->rpath, rpath) == 0) {
+ break;
+ }
+
+ dirent = TAILQ_NEXT(dirent, link);
+ }
+
+ spinlock_release(&cnp->lock);
+ if (dirent == NULL) {
+ return -ENOENT;
+ }
+
+ if ((error = tmpfs_ref(dirent)) != 0) {
+ return error;
+ }
+
+ *res = dirent;
+ return 0;
+}
+
+/*
+ * TMPFS lookup callback for the VFS
+ *
+ * Takes some arguments and returns a vnode
+ * in args->vpp
+ */
+static int
+tmpfs_lookup(struct vop_lookup_args *args)
+{
+ struct tmpfs_node *np;
+ int error;
+
+ if (args == NULL) {
+ return -EINVAL;
+ }
+ if (args->name == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Attempt to find the node we want, if it already
+ * has a vnode attached to it then that's something we
+ * want. However we should allocate a new vnode if we
+ * need to.
+ */
+ error = tmpfs_do_lookup(args->name, &np);
+ if (error != 0) {
+ return error;
+ }
+
+ *args->vpp = np->vp;
+ return 0;
+}
+
+/*
+ * TMPFS create callback for the VFS
+ *
+ * Creates a new TMPFS node
+ */
+static int
+tmpfs_create(struct vop_create_args *args)
+{
+ const char *pcp = args->path; /* Stay away from boat, kids */
+ struct vnode *dirvp;
+ struct tmpfs_node *np;
+ struct tmpfs_node *root_np;
+ int error;
+
+ /* Validate inputs */
+ if (args == NULL)
+ return -EINVAL;
+ if (pcp == NULL)
+ return -EIO;
+ if ((dirvp = args->dirvp) == NULL)
+ return -EIO;
+
+ /* Remove the leading "/tmp/" */
+ pcp += sizeof(ROOT_RPATH);
+ if (*pcp == '\0') {
+ return -ENOENT;
+ }
+
+ np = dynalloc(sizeof(*np));
+ if (np == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(np, 0, sizeof(*np));
+
+ /*
+ * TODO: Support multiple directories.
+ *
+ * XXX: We currently only create a TMPFS_REG node as
+ * to keep things initially simple.
+ */
+ root_np = TAILQ_FIRST(&root);
+ np->dirvp = dirvp;
+ np->type = TMPFS_REG;
+ np->real_size = 0;
+ np->mode = 0700;
+ memcpy(np->rpath, pcp, strlen(pcp) + 1);
+ TAILQ_INSERT_TAIL(&root_np->dirents, np, link);
+
+ if ((error = tmpfs_ref(np)) != 0) {
+ return error;
+ }
+
+ *args->vpp = np->vp;
+ return 0;
+}
+
+/*
+ * TMPFS write callback for VFS
+ *
+ * Node buffers are orthogonally managed. That is, each
+ * node has their own respective data buffers. When
+ * writing to a node, we need to take into account of the
+ * length of the buffer. This value may need to expanded as
+ * well as more pages allocated if the amount of bytes to
+ * be written exceeds it.
+ */
+static int
+tmpfs_write(struct vnode *vp, struct sio_txn *sio)
+{
+ struct tmpfs_node *np;
+ off_t res_off;
+ uint8_t *buf;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* This should not happen but you never know */
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Is this even a regular file? */
+ if (np->type != VREG) {
+ return -EISDIR;
+ }
+
+ spinlock_acquire(&np->lock);
+
+ /*
+ * If the residual byte count is zero, we need to
+ * allocate a new page to be used. However if this
+ * fails we'll throw back an -ENOMEM.
+ */
+ if (np->len == 0) {
+ np->data = dynalloc(TMPFS_BSIZE);
+ if (np->data == NULL) {
+ spinlock_release(&np->lock);
+ return -ENOMEM;
+ }
+ np->len += TMPFS_BSIZE;
+ }
+
+ /*
+ * Bring up the real size if we are writing
+ * more bytes.
+ */
+ res_off = sio->offset + sio->len;
+ if (res_off > np->real_size) {
+ np->real_size = res_off;
+ }
+
+ /*
+ * If the length to be written exceeds the residual byte
+ * count. We will try to expand the buffer by the page
+ * size. However, if this fails, we will split the write
+ * into a suitable size that does not overflow what we
+ * have left.
+ */
+ if (res_off > np->len) {
+ np->data = dynrealloc(np->data, (sio->offset + sio->len));
+ if (np->data == NULL) {
+ sio->len = np->len;
+ } else {
+ np->len = sio->offset + sio->len;
+ }
+ }
+
+ buf = np->data;
+ memcpy(&buf[sio->offset], sio->buf, sio->len);
+ spinlock_release(&np->lock);
+ return sio->len;
+}
+
+/*
+ * TMPFS read callback for VFS
+ */
+static int
+tmpfs_read(struct vnode *vp, struct sio_txn *sio)
+{
+ struct tmpfs_node *np;
+ uint8_t *buf;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* This should not happen but you never know */
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /*
+ * The node data is only allocated during writes, if
+ * we read this file before a write was ever done to it,
+ * np->data will be NULL. We must handle this.
+ */
+ if (np->data == NULL) {
+ return 0;
+ }
+
+ /* Is this even a regular file? */
+ if (np->type != VREG) {
+ return -EISDIR;
+ }
+
+ spinlock_acquire(&np->lock);
+
+ if (sio->offset > np->real_size) {
+ return -EINVAL;
+ }
+
+ buf = np->data;
+ memcpy(sio->buf, &buf[sio->offset], sio->len);
+ spinlock_release(&np->lock);
+ return sio->len;
+}
+
+/*
+ * TMPFS get attribute callback for VFS
+ */
+static int
+tmpfs_getattr(struct vop_getattr_args *args)
+{
+ struct vnode *vp;
+ struct tmpfs_node *np;
+ struct vattr attr;
+
+ if ((vp = args->vp) == NULL) {
+ return -EIO;
+ }
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ memset(&attr, VNOVAL, sizeof(attr));
+ attr.size = np->real_size;
+ attr.mode = np->mode;
+ *args->res = attr;
+ return 0;
+}
+
+static int
+tmpfs_reclaim(struct vnode *vp)
+{
+ struct tmpfs_node *np;
+
+ if ((np = vp->data) == NULL) {
+ return 0;
+ }
+
+ np->vp = NULL;
+ return 0;
+}
+
+static int
+tmpfs_init(struct fs_info *fip)
+{
+ struct tmpfs_node *np;
+ struct vnode *vp;
+ struct mount *mp;
+ int error;
+
+ /* Grab ourselves a new vnode for /tmp */
+ if ((error = vfs_alloc_vnode(&vp, VDIR)) != 0) {
+ return error;
+ }
+
+ vp->vops = &g_tmpfs_vops;
+ mp = vfs_alloc_mount(vp, fip);
+ vfs_name_mount(mp, "tmp");
+ TAILQ_INSERT_TAIL(&g_mountlist, mp, mnt_list);
+
+ /* Pre-allocate the first entry */
+ if ((np = dynalloc(sizeof(*np))) == NULL) {
+ return -ENOMEM;
+ }
+
+ TAILQ_INIT(&root);
+ memset(np, 0, sizeof(*np));
+
+ memcpy(np->rpath, ROOT_RPATH, sizeof(ROOT_RPATH));
+ np->type = TMPFS_DIR;
+ TAILQ_INIT(&np->dirents);
+ TAILQ_INSERT_TAIL(&root, np, link);
+ return 0;
+}
+
+const struct vops g_tmpfs_vops = {
+ .lookup = tmpfs_lookup,
+ .getattr = tmpfs_getattr,
+ .read = tmpfs_read,
+ .write = tmpfs_write,
+ .reclaim = tmpfs_reclaim,
+ .create = tmpfs_create,
+};
+
+const struct vfsops g_tmpfs_vfsops = {
+ .init = tmpfs_init
+};
diff --git a/sys/include/arch/aarch64/board.h b/sys/include/arch/aarch64/board.h
new file mode 100644
index 0000000..bba421f
--- /dev/null
+++ b/sys/include/arch/aarch64/board.h
@@ -0,0 +1,51 @@
+/*
+ * 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 _MACHINE_BOARD_H_
+#define _MACHINE_BOARD_H_
+
+/* Board implementer */
+#define BOARD_ARM_LIMITED 0x41 /* ARM Limited */
+#define BOARD_BROADCOM 0x42 /* Broadcom corp */
+#define BOARD_CAVIUM 0x43 /* Calvium Inc */
+#define BOARD_DIGITAL_EQUIP 0x44 /* Digital Equipment Corporation */
+#define BOARD_FUJITSU 0x46 /* Fujitsu Ltd */
+
+/*
+ * Board information, contains a part number
+ * and an implementer number.
+ */
+struct board_info {
+ uint8_t implementer;
+ uint16_t partno : 12;
+};
+
+void md_get_board(struct board_info *res);
+
+#endif /* !_MACHINE_BOARD_H_ */
diff --git a/sys/include/arch/aarch64/cdefs.h b/sys/include/arch/aarch64/cdefs.h
index a22c436..aaf8649 100644
--- a/sys/include/arch/aarch64/cdefs.h
+++ b/sys/include/arch/aarch64/cdefs.h
@@ -36,5 +36,6 @@
#define md_pause() __ASMV("yield")
#define md_intoff() __ASMV("msr daifset, #2")
#define md_inton() __ASMV("msr daifclr, #2")
+#define md_hlt() __ASMV("hlt #0")
#endif /* !_AARCH64_CDEFS_H_ */
diff --git a/sys/include/arch/aarch64/cpu.h b/sys/include/arch/aarch64/cpu.h
index 2f62d95..8c2d837 100644
--- a/sys/include/arch/aarch64/cpu.h
+++ b/sys/include/arch/aarch64/cpu.h
@@ -39,6 +39,7 @@ struct cpu_info {
struct cpu_info *self;
};
+__dead void cpu_halt_all(void);
void cpu_startup(struct cpu_info *ci);
void cpu_halt_others(void);
diff --git a/sys/include/arch/aarch64/exception.h b/sys/include/arch/aarch64/exception.h
new file mode 100644
index 0000000..9e89c81
--- /dev/null
+++ b/sys/include/arch/aarch64/exception.h
@@ -0,0 +1,54 @@
+/*
+ * 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 _MACHINE_EXCEPTION_H_
+#define _MACHINE_EXCEPTION_H_
+
+#include <sys/types.h>
+#include <machine/frame.h>
+
+/* Exception class */
+#define EC_UNKNOWN 0x00 /* Unknown type */
+#define EC_WF 0x01 /* Trapped WF instruction */
+#define EC_MCRMRC 0x03 /* Trapped MCR/MRC */
+#define EC_MCRRC 0x04 /* Trapped MCRR/MRRC */
+#define EC_LDCSTC 0x06 /* Trapped LDC/STC */
+#define EC_SVE 0x07 /* Trapped SVE/SIMD/FP op */
+#define EC_BRE 0x0D /* Branch target exception */
+#define EC_ILLX 0x0E /* Illegal execution state */
+#define EC_SVC64 0x15 /* AARCH64 SVC */
+#define EC_PCALIGN 0x22 /* PC alignment fault */
+#define EC_DABORT 0x24 /* Data abort (w/o ELx change) */
+#define EC_EDABORT 0x25 /* Data abort (w/ ELx change) */
+#define EC_SPALIGN 0x26 /* SP alignment fault */
+#define EC_SERR 0x2F /* System error (what the fuck!) */
+
+void handle_exception(struct trapframe *tf);
+
+#endif /* !_MACHINE_EXCEPTION_H_ */
diff --git a/sys/include/arch/aarch64/frame.h b/sys/include/arch/aarch64/frame.h
index fa4d33d..143f4d0 100644
--- a/sys/include/arch/aarch64/frame.h
+++ b/sys/include/arch/aarch64/frame.h
@@ -31,43 +31,10 @@
#define _MACHINE_FRAME_H_
#include <sys/types.h>
+#include <sys/cdefs.h>
typedef uint64_t lreg_t;
-
-/* General purpose registers */
-struct gpregs {
- lreg_t x0;
- lreg_t x1;
- lreg_t x2;
- lreg_t x3;
- lreg_t x4;
- lreg_t x5;
- lreg_t x6;
- lreg_t x7;
- lreg_t x8;
- lreg_t x9;
- lreg_t x10;
- lreg_t x11;
- lreg_t x12;
- lreg_t x13;
- lreg_t x14;
- lreg_t x15;
- lreg_t x16;
- lreg_t x17;
- lreg_t x18;
- lreg_t x19;
- lreg_t x20;
- lreg_t x21;
- lreg_t x22;
- lreg_t x23;
- lreg_t x24;
- lreg_t x25;
- lreg_t x26;
- lreg_t x27;
- lreg_t x28;
- lreg_t x29;
- lreg_t x30;
-};
+typedef uint64_t frament_t;
/* Stack regs */
struct sregs {
@@ -83,14 +50,41 @@ struct pstat {
lreg_t spsr_el3;
};
-struct trapframe {
- struct gpregs gp;
- struct sregs stack;
- struct pstat status;
- lreg_t elr_el1;
- lreg_t elr_el2;
- lreg_t elr_el3;
- lreg_t pc;
+struct __aligned(16) trapframe {
+ lreg_t x30;
+ lreg_t x29;
+ lreg_t x28;
+ lreg_t x27;
+ lreg_t x26;
+ lreg_t x25;
+ lreg_t x24;
+ lreg_t x23;
+ lreg_t x22;
+ lreg_t x21;
+ lreg_t x20;
+ lreg_t x19;
+ lreg_t x18;
+ lreg_t x17;
+ lreg_t x16;
+ lreg_t x15;
+ lreg_t x14;
+ lreg_t x13;
+ lreg_t x12;
+ lreg_t x11;
+ lreg_t x10;
+ lreg_t x9;
+ lreg_t x8;
+ lreg_t x7;
+ lreg_t x6;
+ lreg_t x5;
+ lreg_t x4;
+ lreg_t x3;
+ lreg_t x2;
+ lreg_t x1;
+ lreg_t x0;
+ lreg_t elr;
+ lreg_t esr;
+ frament_t trapno;
};
#define TF_IP(TFP) ((TFP)->pc)
diff --git a/sys/include/arch/aarch64/frameasm.h b/sys/include/arch/aarch64/frameasm.h
new file mode 100644
index 0000000..ca7f81a
--- /dev/null
+++ b/sys/include/arch/aarch64/frameasm.h
@@ -0,0 +1,89 @@
+/*
+ * 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 _MACHINE_FRAMEASM_H_
+#define _MACHINE_FRAMEASM_H_
+
+/* XXX: Must be 16-byte aligned!!! */
+#define XFRAME_STACK_SIZE (38 * 8)
+
+/* Trap numbers */
+#define TRAPNO_UNKNOWN #0
+#define TRAPNO_XSYNC #1 /* Synchronous */
+#define TRAPNO_XIRQ #2 /* IRQ */
+#define TRAPNO_XFIQ #3 /* FIQ */
+#define TRAPNO_XSERR #4 /* System error */
+
+#define PUSH_XFRAME(TRAPNO) \
+ sub sp, sp, #XFRAME_STACK_SIZE ; \
+ stp x30, x29, [sp, #(0 * 8)] ; \
+ stp x28, x27, [sp, #(2 * 8)] ; \
+ stp x26, x25, [sp, #(4 * 8)] ; \
+ stp x24, x23, [sp, #(6 * 8)] ; \
+ stp x22, x21, [sp, #(8 * 8)] ; \
+ stp x20, x19, [sp, #(10 * 8)] ; \
+ stp x18, x17, [sp, #(12 * 8)] ; \
+ stp x16, x15, [sp, #(14 * 8)] ; \
+ stp x14, x13, [sp, #(16 * 8)] ; \
+ stp x12, x11, [sp, #(18 * 8)] ; \
+ stp x10, x9, [sp, #(20 * 8)] ; \
+ stp x8, x7, [sp, #(22 * 8)] ; \
+ stp x6, x5, [sp, #(24 * 8)] ; \
+ stp x4, x3, [sp, #(26 * 8)] ; \
+ stp x2, x1, [sp, #(28 * 8)] ; \
+ str x0, [sp, #(30 * 8)] ; \
+ ; \
+ mrs x0, elr_el1 ; \
+ str x0, [sp, #(31 * 8)] ; \
+ mrs x0, esr_el1 ; \
+ str x0, [sp, #(32 * 8)] ; \
+ mov x0, TRAPNO ; \
+ str x0, [sp, #(33 * 8)] ; \
+ mov x0, sp
+
+#define POP_XFRAME() \
+ ldr x0, [sp, #(30 * 8)] ; \
+ ldp x2, x1, [sp, #(28 * 8)] ; \
+ ldp x4, x3, [sp, #(26 * 8)] ; \
+ ldp x6, x5, [sp, #(24 * 8)] ; \
+ ldp x8, x7, [sp, #(22 * 8)] ; \
+ ldp x10, x9, [sp, #(20 * 8)] ; \
+ ldp x12, x11, [sp, #(18 * 8)] ; \
+ ldp x14, x13, [sp, #(16 * 8)] ; \
+ ldp x16, x15, [sp, #(14 * 8)] ; \
+ ldp x18, x17, [sp, #(12 * 8)] ; \
+ ldp x20, x19, [sp, #(10 * 8)] ; \
+ ldp x22, x21, [sp, #(8 * 8)] ; \
+ ldp x24, x23, [sp, #(6 * 8)] ; \
+ ldp x26, x25, [sp, #(4 * 8)] ; \
+ ldp x28, x27, [sp, #(2 * 8)] ; \
+ ldp x30, x29, [sp, #(0 * 8)] ; \
+ add sp, sp, #XFRAME_STACK_SIZE
+
+#endif /* !_MACHINE_FRAMEASM_H_ */
diff --git a/sys/include/arch/aarch64/intr.h b/sys/include/arch/aarch64/intr.h
new file mode 100644
index 0000000..b85564f
--- /dev/null
+++ b/sys/include/arch/aarch64/intr.h
@@ -0,0 +1,57 @@
+/*
+ * 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 _MACHINE_INTR_H_
+#define _MACHINE_INTR_H_
+
+#include <sys/types.h>
+
+/*
+ * Interrupt priority levels
+ */
+#define IPL_NONE 0 /* Don't defer anything */
+#define IPL_BIO 1 /* Block I/O */
+#define IPL_CLOCK 2 /* Clock */
+#define IPL_HIGH 3 /* Defer everything */
+
+struct intr_entry {
+ int priority;
+};
+
+struct intr_hand {
+ int(*func)(void *);
+ char *name;
+ int priority;
+ int irq;
+ int vector;
+};
+
+void *intr_register(const char *name, const struct intr_hand *ih);
+
+#endif /* !_MACHINE_INTR_H_ */
diff --git a/sys/include/arch/aarch64/param.h b/sys/include/arch/aarch64/param.h
new file mode 100644
index 0000000..c074ffb
--- /dev/null
+++ b/sys/include/arch/aarch64/param.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _AARCH64_PARAM_H_
+#define _AARCH64_PARAM_H_
+
+#define M_WORD_SIZE 4
+
+#endif /* !_AARCH64_PARAM_H_ */
diff --git a/sys/include/arch/aarch64/pci/pci.h b/sys/include/arch/aarch64/pci/pci.h
new file mode 100644
index 0000000..189a423
--- /dev/null
+++ b/sys/include/arch/aarch64/pci/pci.h
@@ -0,0 +1,40 @@
+/*
+ * 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 _MACHINE_PCI_H_
+#define _MACHINE_PCI_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <dev/pci/pci.h>
+
+__weak pcireg_t md_pci_readl(struct pci_device *dev, uint32_t off);
+__weak void md_pci_writel(struct pci_device *dev, uint32_t off, pcireg_t val);
+
+#endif /* !_MACHINE_PCI_H_ */
diff --git a/sys/include/arch/aarch64/pio.h b/sys/include/arch/aarch64/pio.h
index 4aaeece..41f07fb 100644
--- a/sys/include/arch/aarch64/pio.h
+++ b/sys/include/arch/aarch64/pio.h
@@ -39,5 +39,4 @@
#define outw(...) __nothing
#define outl(...) __nothing
-
#endif /* _MACHINE_PIO_H_ */
diff --git a/sys/include/arch/amd64/asm.h b/sys/include/arch/amd64/asm.h
index 8d2c812..aca49d2 100644
--- a/sys/include/arch/amd64/asm.h
+++ b/sys/include/arch/amd64/asm.h
@@ -34,6 +34,16 @@
#include <sys/param.h>
#include <machine/msr.h>
+/* CR4 bits */
+#define CR4_TSD BIT(2) /* Timestamp disable */
+#define CR4_DE BIT(3) /* Debugging extensions */
+#define CR4_PSE BIT(4) /* Page size extensions */
+#define CR4_PCE BIT(8) /* Performance monitoring counter enable */
+#define CR4_UMIP BIT(11) /* User mode instruction prevention */
+#define CR4_LA57 BIT(12) /* Level 5 paging enable */
+#define CR4_VMXE BIT(13) /* Virtual machine extensions enable */
+#define CR4_SMXE BIT(14) /* Safer mode extensions enable */
+
/*
* Contains information for the current
* core. Stored in %GS.
diff --git a/sys/include/arch/amd64/bus.h b/sys/include/arch/amd64/bus.h
index 00cb3ba..25088b4 100644
--- a/sys/include/arch/amd64/bus.h
+++ b/sys/include/arch/amd64/bus.h
@@ -36,13 +36,7 @@
struct bus_resource;
-/*
- * Hyra assumes that the bootloader uses PDE[256] for some
- * higher half mappings. To avoid conflicts with those mappings,
- * this offset is used to start device memory at PDE[257]. This
- * will give us more than enough space.
- */
-#define MMIO_OFFSET (VM_HIGHER_HALF + 0x8000000000)
+#define MMIO_OFFSET VM_HIGHER_HALF
/* Resource signature size max */
#define RSIG_MAX 16
diff --git a/sys/include/arch/amd64/cdefs.h b/sys/include/arch/amd64/cdefs.h
index 256fd8b..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,5 +41,15 @@
#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() cpu_halt() /* Halt the processor */
+
+/*
+ * AMD64 specific defines
+ */
+#define __invlpg(VA) \
+ __ASMV("invlpg %0" \
+ : \
+ : "m" ((VA)) \
+ : "memory")
#endif /* !_AMD64_CDEFS_H_ */
diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h
index dd753b7..5adff29 100644
--- a/sys/include/arch/amd64/cpu.h
+++ b/sys/include/arch/amd64/cpu.h
@@ -33,18 +33,48 @@
#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/spinlock.h>
#include <machine/tss.h>
+#include <machine/cdefs.h>
+#include <machine/intr.h>
#define CPU_IRQ(IRQ_N) (BIT((IRQ_N)) & 0xFF)
+/* Feature bits */
+#define CPU_FEAT_SMAP BIT(0)
+#define CPU_FEAT_SMEP BIT(1)
+#define CPU_FEAT_UMIP BIT(2)
+#define CPU_FEAT_TSCINV BIT(3) /* TSC invariant */
+
+/* CPU vendors */
+#define CPU_VENDOR_OTHER 0x00000000
+#define CPU_VENDOR_INTEL 0x00000001
+#define CPU_VENDOR_AMD 0x00000002
+
+typedef uint32_t ipi_pend_t;
+
struct cpu_info {
uint32_t apicid;
+ uint32_t feat;
+ uint32_t vendor; /* Vendor (see CPU_VENDOR_*) */
+ uint8_t preempt : 1; /* CPU is preemptable */
+ uint8_t ipi_dispatch : 1; /* 1: IPIs being dispatched */
+ ipi_pend_t ipi_pending;
+ 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;
+ uint8_t tlb_shootdown : 1;
+ uint8_t online : 1; /* CPU online */
uint8_t ipl;
size_t lapic_tmr_freq;
uint8_t irq_mask;
+ vaddr_t shootdown_va;
+ struct sched_cpu stat;
struct tss_entry *tss;
struct proc *curtd;
+ struct spinlock lock;
struct cpu_info *self;
};
@@ -52,9 +82,32 @@ __dead void cpu_halt_all(void);
void cpu_halt_others(void);
void cpu_startup(struct cpu_info *ci);
+void cpu_enable_smep(void);
+void cpu_disable_smep(void);
+
+struct cpu_info *cpu_get(uint32_t index);
+struct sched_cpu *cpu_get_stat(uint32_t cpu_index);
+
+uint32_t cpu_count(void);
+void cpu_shootdown_tlb(vaddr_t va);
+
struct cpu_info *this_cpu(void);
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/arch/amd64/frameasm.h b/sys/include/arch/amd64/frameasm.h
index c6316a5..4dc075e 100644
--- a/sys/include/arch/amd64/frameasm.h
+++ b/sys/include/arch/amd64/frameasm.h
@@ -30,6 +30,8 @@
#ifndef _MACHINE_FRAMEASM_H_
#define _MACHINE_FRAMEASM_H_
+#define ALIGN_TEXT .align 8, 0x90
+
/*
* If the interrupt has an error code, this macro shall
* be used to create the trapframe.
diff --git a/sys/include/arch/amd64/gdt.h b/sys/include/arch/amd64/gdt.h
index 55666a7..0c5faf1 100644
--- a/sys/include/arch/amd64/gdt.h
+++ b/sys/include/arch/amd64/gdt.h
@@ -4,18 +4,48 @@
#include <sys/types.h>
#include <sys/cdefs.h>
+#define GDT_TSS_INDEX 5
+#define GDT_ENTRY_COUNT 7
+
+/* Segment selectors */
#define KERNEL_CS 0x08
#define KERNEL_DS 0x10
-#define USER_CS 0x18
-#define USER_DS 0x20
-#define GDT_TSS 5
+#define USER_CS 0x18
+#define USER_DS 0x20
+
+/*
+ * Bit definitions for regular segment descriptors
+ *
+ * See Intel SPG 3/25 Section 3.4.5 - Segment Descriptors
+ */
+
+#define GDT_ATTRIBUTE_ACCESSED BIT(0) /* Accessed */
+#define GDT_ATTRIBUTE_EXECUTABLE BIT(3) /* Executable */
+#define GDT_ATTRIBUTE_NONSYSTEM BIT(4) /* Code/data */
+#define GDT_ATTRIBUTE_PRESENT BIT(7) /* Present */
+#define GDT_ATTRIBUTE_64BIT_CODE BIT(13) /* 64-bit code */
+#define GDT_ATTRIBUTE_32BIT BIT(14) /* 32-bit code/data */
+#define GDT_ATTRIBUTE_GRANULARITY BIT(15) /* 4KiB limit granularity */
+
+/* Attributes for executable segments */
+#define GDT_ATTRIBUTE_READABLE BIT(1) /* Readable */
+#define GDT_ATTRIBUTE_CONFORMING BIT(2) /* Conforming */
+
+/* Attributes for non-executable segments */
+#define GDT_ATTRIBUTE_WRITABLE BIT(1) /* Writable */
+#define GDT_ATTRIBUTE_EXPANDS_DOWN BIT(2) /* See SPG 3/25 Section 6.8.1 */
+
+/* DPL (Descriptor Privilege Level) specifier */
+#define GDT_ATTRIBUTE_DPL0 0
+#define GDT_ATTRIBUTE_DPL1 (1 << 5)
+#define GDT_ATTRIBUTE_DPL2 (2 << 5)
+#define GDT_ATTRIBUTE_DPL3 (3 << 5)
struct __packed gdt_entry {
uint16_t limit;
uint16_t base_low;
uint8_t base_mid;
- uint8_t access;
- uint8_t granularity;
+ uint16_t attributes;
uint8_t base_hi;
};
@@ -24,27 +54,28 @@ struct __packed gdtr {
uintptr_t offset;
};
+extern struct gdt_entry g_gdt_data[GDT_ENTRY_COUNT];
+extern const struct gdtr g_gdtr;
+
__always_inline static inline void
-gdt_load(struct gdtr *gdtr)
+gdt_load(void)
{
__ASMV("lgdt %0\n"
- "push $8\n" /* Push CS */
- "lea 1f(%%rip), %%rax\n" /* Load 1 label address into RAX */
- "push %%rax\n" /* Push the return address (label 1) */
- "lretq\n" /* Far return to update CS */
+ "push %1\n" /* Push code segment selector */
+ "lea 1f(%%rip), %%rax\n" /* Load label 1 address into RAX */
+ "push %%rax\n" /* Push return address (label 1) */
+ "lretq\n" /* Far return to update CS */
"1:\n"
- " mov $0x10, %%eax\n"
- " mov %%eax, %%ds\n"
- " mov %%eax, %%es\n"
- " mov %%eax, %%fs\n"
- " mov %%eax, %%gs\n"
- " mov %%eax, %%ss\n"
+ " mov %2, %%ax\n" /* Load data segment selectors */
+ " mov %%ax, %%ds\n"
+ " mov %%ax, %%es\n"
+ " mov %%ax, %%fs\n"
+ " mov %%ax, %%gs\n"
+ " mov %%ax, %%ss\n"
:
- : "m" (*gdtr)
+ : "m" (g_gdtr), "i"(KERNEL_CS), "i"(KERNEL_DS)
: "rax", "memory"
);
}
-extern struct gdt_entry g_gdt_data[256];
-
#endif /* !AMD64_GDT_H_ */
diff --git a/sys/include/arch/amd64/intr.h b/sys/include/arch/amd64/intr.h
index c643945..6e011fa 100644
--- a/sys/include/arch/amd64/intr.h
+++ b/sys/include/arch/amd64/intr.h
@@ -35,6 +35,8 @@
#define IST_SCHED 1U
#define IST_HW_IRQ 2U
#define IST_SW_INT 3U
+#define IST_SYSCALL 4U
+#define IST_DBFLT 5U
/* Upper 4 bits of interrupt vector */
#define IPL_SHIFT 4
@@ -47,11 +49,65 @@
#define IPL_CLOCK 2 /* Clock */
#define IPL_HIGH 3 /* Defer everything */
-struct intr_entry {
+#define N_IPIVEC 4 /* Number of vectors reserved for IPIs */
+#define IPI_PER_VEC 16 /* Max IPIs per vector */
+
+struct intr_hand;
+
+/*
+ * Contains information passed to driver
+ *
+ * @ihp: Interrupt handler
+ * @data: Driver specific data
+ */
+struct intr_data {
+ struct intr_hand *ihp;
+ union {
+ void *data;
+ uint64_t data_u64;
+ };
+};
+
+/*
+ * Interrupt handler
+ *
+ * [r]: Required for intr_register()
+ * [o]: Not required for intr_register()
+ * [v]: Returned by intr_register()
+ * [i]: Internal
+ *
+ * @func: The actual handler [r]
+ * @data: Interrupt data [o/v]
+ * @nintr: Number of times it fired [o]
+ * @name: Interrupt name [v]
+ * @priority: Interrupt priority [r]
+ * @irq: Interrupt request number [o]
+ * @vector: Interrupt vector [v]
+ *
+ * XXX: `name' must be null terminated ('\0')
+ *
+ * XXX: `irq` can be set to -1 for MSI/MSI-X
+ * interrupts.
+ *
+ * XXX: `func` must be the first field in this
+ * structure so that it may be called through
+ * assembly.
+ *
+ * XXX: `ist' should usually be set to -1 but can be
+ * used if an interrupt requires its own stack.
+ */
+struct intr_hand {
+ int(*func)(void *);
+ size_t nintr;
+ struct intr_data data;
+ char *name;
int priority;
+ int irq;
+ int vector;
};
-int intr_alloc_vector(const char *name, uint8_t priority);
+void *intr_register(const char *name, const struct intr_hand *ih);
+
int splraise(uint8_t s);
void splx(uint8_t s);
diff --git a/sys/include/arch/amd64/ipi.h b/sys/include/arch/amd64/ipi.h
new file mode 100644
index 0000000..48243e7
--- /dev/null
+++ b/sys/include/arch/amd64/ipi.h
@@ -0,0 +1,67 @@
+/*
+ * 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 _MACHINE_IPI_H_
+#define _MACHINE_IPI_H_
+
+#include <sys/types.h>
+#include <machine/cpu.h>
+#include <machine/lapic.h>
+
+/*
+ * IPI_VECTOR is the main vector used for all misc
+ * IPIs, the HALT_VECTOR is used to slam the system
+ * to a screetching halt.
+ */
+#define IPI_VECTOR 0x21
+#define HALT_VECTOR 0x22
+
+/* Fixed IPI IDs */
+#define IPI_TLB 0
+
+/*
+ * Represents an interprocessor interrupt
+ * handler.
+ *
+ * @cookie: Used to verifying an instance
+ * @id: IPI ID (identifies the IPI)
+ * @mask: If set, IPIs are ignored
+ * @handler: Handler routine
+ */
+struct cpu_ipi {
+ uint16_t cookie;
+ uint8_t id;
+ int(*handler)(struct cpu_ipi *ipi);
+};
+
+int md_ipi_alloc(struct cpu_ipi **res);
+int md_ipi_send(struct cpu_info *ci, ipi_pend_t ipi);
+void md_ipi_init(void);
+
+#endif /* !_MACHINE_IPI_H_ */
diff --git a/sys/include/arch/amd64/isa/i8042var.h b/sys/include/arch/amd64/isa/i8042var.h
index ebd96ad..9619920 100644
--- a/sys/include/arch/amd64/isa/i8042var.h
+++ b/sys/include/arch/amd64/isa/i8042var.h
@@ -74,6 +74,10 @@
#define I8042_LED_NUM BIT(1)
#define I8042_LED_CAPS BIT(2)
+/* Extended scancode types */
+#define I8042_XSC_ENDPR 0 /* End pressed */
+#define I8042_XSC_ENDRL 1 /* End released */
+
/* Quirks */
#define I8042_HOSTILE BIT(0) /* If EC likes throwing NMIs */
@@ -82,7 +86,5 @@ void i8042_quirk(int mask);
/* Internal - do not use */
void i8042_sync(void);
-void i8042_kb_isr(void);
-void i8042_kb_event(void);
#endif /* _I8042VAR_H_ */
diff --git a/sys/include/arch/amd64/param.h b/sys/include/arch/amd64/param.h
new file mode 100644
index 0000000..6ea3fca
--- /dev/null
+++ b/sys/include/arch/amd64/param.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _AMD64_PARAM_H_
+#define _AMD64_PARAM_H_
+
+#define M_WORD_SIZE 8
+
+#endif /* !_AMD64_PARAM_H_ */
diff --git a/sys/include/arch/amd64/pci/pci.h b/sys/include/arch/amd64/pci/pci.h
new file mode 100644
index 0000000..189a423
--- /dev/null
+++ b/sys/include/arch/amd64/pci/pci.h
@@ -0,0 +1,40 @@
+/*
+ * 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 _MACHINE_PCI_H_
+#define _MACHINE_PCI_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <dev/pci/pci.h>
+
+__weak pcireg_t md_pci_readl(struct pci_device *dev, uint32_t off);
+__weak void md_pci_writel(struct pci_device *dev, uint32_t off, pcireg_t val);
+
+#endif /* !_MACHINE_PCI_H_ */
diff --git a/sys/include/arch/amd64/tsc.h b/sys/include/arch/amd64/tsc.h
new file mode 100644
index 0000000..d9eed4f
--- /dev/null
+++ b/sys/include/arch/amd64/tsc.h
@@ -0,0 +1,55 @@
+/*
+ * 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 _MACHINE_TSC_H_
+#define _MACHINE_TSC_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+
+uint64_t rdtsc_rel(void);
+
+__always_inline static inline uint64_t
+rdtsc(void)
+{
+ uint32_t lo, hi;
+
+ __ASMV(
+ "rdtsc"
+ : "=d" (hi),
+ "=a" (lo)
+ :
+ : "memory"
+ );
+
+ return COMBINE32(hi, lo);
+}
+
+#endif /* !_MACHINE_TSC_H_ */
diff --git a/sys/include/crypto/chacha20.h b/sys/include/crypto/chacha20.h
new file mode 100644
index 0000000..267b13c
--- /dev/null
+++ b/sys/include/crypto/chacha20.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+
+#define QR(a,b,c,d) \
+ a += b; d ^= a; d = ROTL(d, 16); \
+ c += d; b ^= c; b = ROTL(b, 12); \
+ a += b; d ^= a; d = ROTL(d, 8); \
+ c += d; b ^= c; b = ROTL(b, 7);
+
+void chacha20_init(uint32_t state[16], const uint8_t key[32],
+ const uint8_t nonce[12], uint32_t counter);
+
+void chacha20_block(uint32_t state[16], uint8_t out[64]);
+void chacha20_encrypt(uint32_t state[16], uint8_t *in, uint8_t *out, size_t len);
diff --git a/sys/include/crypto/siphash.h b/sys/include/crypto/siphash.h
new file mode 100644
index 0000000..ecabb4a
--- /dev/null
+++ b/sys/include/crypto/siphash.h
@@ -0,0 +1,34 @@
+/* <MIT License>
+ Copyright (c) 2013 Marek Majkowski <marek@popcount.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ </MIT License>
+
+ Original location:
+ https://github.com/majek/csiphash/
+
+ Solution inspired by code from:
+ Samuel Neves (supercop/crypto_auth/siphash24/little)
+ djb (supercop/crypto_auth/siphash24/little2)
+ Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)
+*/
+
+#include <stdint.h>
+
+uint64_t siphash24(const void *src, unsigned long src_sz, const char k[16]);
diff --git a/sys/include/dev/acpi/tables.h b/sys/include/dev/acpi/tables.h
index 5215c86..d31cbe0 100644
--- a/sys/include/dev/acpi/tables.h
+++ b/sys/include/dev/acpi/tables.h
@@ -118,6 +118,43 @@ struct __packed acpi_gas {
uint64_t address;
};
+/*
+ * ACPI Address Space ID definitions for GAS
+ *
+ * See section 5.2.3.2 of the ACPI software programming
+ * manual.
+ *
+ * XXX: 0x0B->0x7E is reserved as well as 0x80->0xBF
+ * and 0xC0->0xFF is OEM defined. Values other than
+ * the ones specified below are either garbage or
+ * OEM specific values.
+ */
+#define ACPI_GAS_SYSMEM 0x00 /* System memory space */
+#define ACPI_GAS_SYSIO 0x01 /* System I/O space */
+#define ACPI_GAS_PCICONF 0x02 /* PCI configuration space */
+#define ACPI_GAS_EC 0x03 /* Embedded controller */
+#define ACPI_GAS_SMBUS 0x04 /* System management bus */
+#define ACPI_GAS_CMOS 0x05 /* System CMOS */
+#define ACPI_GAS_PCIBAR 0x06 /* PCI BAR target */
+#define ACPI_GAS_IPMI 0x07 /* IPMI (sensor monitoring) */
+#define ACPI_GAS_GPIO 0x08 /* General Purpose I/O */
+#define ACPI_GAS_GSBUS 0x09 /* GenericSerialBus */
+#define ACPI_GAS_PLATCOM 0x0A /* Platform Communications Channel */
+
+/*
+ * ACPI address size definitions for GAS
+ *
+ * See section 5.2.3.2 of the ACPI software programming
+ * manual.
+ *
+ * This is really retarded Intel and Microsoft, thank you.
+ */
+#define ACPI_GAS_UNDEF 0 /* Undefined (legacy reasons) */
+#define ACPI_GAS_BYTE 1 /* Byte access */
+#define ACPI_GAS_WORD 2 /* Word access */
+#define ACPI_GAS_DWORD 3 /* Dword access */
+#define ACPI_GAS_QWORD 4 /* Qword access */
+
struct __packed acpi_hpet {
struct acpi_header hdr;
uint8_t hardware_rev_id;
@@ -132,4 +169,65 @@ struct __packed acpi_hpet {
uint8_t page_protection;
};
+/*
+ * PCIe / ACPI MCFG base address description
+ * table.
+ *
+ * @base_pa: Enhanced configuration base [physical]
+ * @seg_grpno: PCI segment group number
+ * @bus_start: Host bridge bus start
+ * @bus_end: Host bridge bus end
+ */
+struct __packed acpi_mcfg_base {
+ uint64_t base_pa;
+ uint16_t seg_grpno;
+ uint8_t bus_start;
+ uint8_t bus_end;
+ uint32_t reserved;
+};
+
+/*
+ * PCIe / ACPI MCFG structure
+ *
+ * @hdr: ACPI header
+ * @reserved: Do not use
+ * @base: ECAM MMIO address list
+ */
+struct __packed acpi_mcfg {
+ struct acpi_header hdr;
+ uint32_t reserved[2];
+ struct acpi_mcfg_base base[1];
+};
+
+struct __packed dmi_entry32 {
+ char signature[4]; /* _SM_ */
+ uint8_t checksum; /* Sum of table bytes */
+ uint8_t length; /* Length of entry table */
+ uint8_t major; /* DMI major */
+ uint8_t minor; /* DMI minor */
+ uint16_t max_size; /* Max structure size */
+ uint8_t rev; /* Entry revision */
+ char fmt_area[5]; /* Formatted area */
+ char isignature[5]; /* Intermediate signature */
+ uint8_t ichecksum; /* Intermediate checksum */
+ uint16_t table_len; /* Length of SMBIOS structure table */
+ uint32_t addr; /* 32-bit physical start of SMBIOS structure table */
+ uint16_t nstruct; /* Total number of structures */
+ uint8_t bcd_rev;
+};
+
+struct __packed dmi_entry64 {
+ char signature[5]; /* _SM_ */
+ uint8_t checksum; /* Sum of table bytes */
+ uint8_t length; /* Length of entry table */
+ uint8_t major; /* DMI major */
+ uint8_t minor; /* DMI minor */
+ uint8_t docrev;
+ uint8_t entry_rev;
+ uint8_t reserved;
+ uint16_t max_size; /* Max structure size */
+ uint16_t padding;
+ uint64_t addr; /* 64-bit physical address */
+};
+
#endif /* _ACPI_TABLES_H_ */
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h b/sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h
index 369c5a4..045e402 100644
--- a/sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h
@@ -42,7 +42,6 @@ void uacpi_deinitialize_namespace(void);
uacpi_namespace_node *uacpi_namespace_node_alloc(uacpi_object_name name);
void uacpi_namespace_node_unref(uacpi_namespace_node *node);
-
uacpi_status uacpi_namespace_node_type_unlocked(
const uacpi_namespace_node *node, uacpi_object_type *out_type
);
diff --git a/sys/include/dev/acpi/uacpi/uacpi/platform/config.h b/sys/include/dev/acpi/uacpi/uacpi/platform/config.h
index dff043f..b594338 100644
--- a/sys/include/dev/acpi/uacpi/uacpi/platform/config.h
+++ b/sys/include/dev/acpi/uacpi/uacpi/platform/config.h
@@ -67,7 +67,6 @@ UACPI_BUILD_BUG_ON_WITH_MSG(
*/
// #define UACPI_SIZED_FREES
-
/*
* Makes uacpi_kernel_alloc_zeroed mandatory to implement by the host, uACPI
* will not provide a default implementation if this is enabled.
diff --git a/sys/include/dev/acpi/uacpi/uacpi/utilities.h b/sys/include/dev/acpi/uacpi/uacpi/utilities.h
index dfc41c3..d7042e9 100644
--- a/sys/include/dev/acpi/uacpi/uacpi/utilities.h
+++ b/sys/include/dev/acpi/uacpi/uacpi/utilities.h
@@ -128,7 +128,6 @@ uacpi_status uacpi_eval_cls(uacpi_namespace_node*, uacpi_id_string **out_id);
*/
uacpi_status uacpi_eval_uid(uacpi_namespace_node*, uacpi_id_string **out_uid);
-
// uacpi_namespace_node_info->flags
#define UACPI_NS_NODE_INFO_HAS_ADR (1 << 0)
#define UACPI_NS_NODE_INFO_HAS_HID (1 << 1)
diff --git a/sys/include/dev/cons/ansi.h b/sys/include/dev/cons/ansi.h
new file mode 100644
index 0000000..7a336d1
--- /dev/null
+++ b/sys/include/dev/cons/ansi.h
@@ -0,0 +1,75 @@
+/*
+ * 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 _CONS_ANSI_H_
+#define _CONS_ANSI_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+
+/* ANSI colors */
+#define ANSI_BLACK 0x000000
+#define ANSI_RED 0xAA0000
+#define ANSI_GREEN 0x00AA00
+#define ANSI_BLUE 0x00007F
+#define ANSI_YELLOW 0xAA5500
+#define ANSI_MAGENTA 0xAA00AA
+#define ANSI_CYAN 0x00AAAA
+#define ANSI_WHITE 0xAAAAAA
+
+/* ANSI_FEED update codes */
+#define ANSI_UPDATE_COLOR -1
+#define ANSI_UPDATE_CURSOR -2
+
+/*
+ * ANSI parser state machine.
+ *
+ * @prev: Previous char
+ * @csi: Encountered control seq introducer
+ * @reset_color: 1 if color is to be reset
+ * @set_fg: 1 if fg is being set
+ * @set_bg: 1 if bg is being set
+ * @fg: Foreground color
+ * @bg: Background color
+ * @flags: State flags
+ */
+struct ansi_state {
+ char prev;
+ uint8_t csi : 2;
+ uint8_t reset_color : 1;
+ uint8_t set_fg : 1;
+ uint8_t set_bg : 1;
+ uint32_t fg;
+ uint32_t bg;
+};
+
+int ansi_feed(struct ansi_state *statep, char c);
+
+#endif /* !_CONS_ANSI_H_ */
diff --git a/sys/include/dev/cons/cons.h b/sys/include/dev/cons/cons.h
index 3569c52..7c4e41a 100644
--- a/sys/include/dev/cons/cons.h
+++ b/sys/include/dev/cons/cons.h
@@ -32,8 +32,12 @@
#include <sys/types.h>
#include <sys/spinlock.h>
+#include <sys/proc.h>
+#include <sys/mutex.h>
+#include <sys/console.h>
#include <dev/video/fbdev.h>
#include <dev/cons/consvar.h>
+#include <dev/cons/ansi.h>
struct cons_char {
char c;
@@ -45,6 +49,11 @@ struct cons_char {
struct cons_screen {
struct fbdev fbdev;
+ struct ansi_state ansi_s;
+ struct console_feat feat; /* Features */
+ struct console_attr attr; /* Attributes */
+ struct proc *atproc; /* Attached proc */
+ struct mutex *atproc_lock;
uint32_t fg;
uint32_t bg;
@@ -54,17 +63,25 @@ struct cons_screen {
uint32_t ncols;
uint32_t ch_col; /* Current col */
uint32_t ch_row; /* Current row */
- uint32_t curs_col; /* Cursor col */
- uint32_t curs_row; /* Cursor row */
struct cons_buf *ib; /* Input buffer */
struct cons_buf **ob; /* Output buffers */
struct cons_char last_chr;
struct spinlock lock;
};
+#define curs_col attr.cursor_x
+#define curs_row attr.cursor_y
+
void cons_init(void);
void cons_expose(void);
+void cons_update_color(struct cons_screen *scr, uint32_t fg, uint32_t bg);
+void cons_clear_scr(struct cons_screen *scr, uint32_t bg);
+void cons_reset_color(struct cons_screen *scr);
+void cons_reset_cursor(struct cons_screen *scr);
+int cons_attach(void);
+int cons_detach(void);
int cons_putch(struct cons_screen *scr, char c);
+int cons_putstr(struct cons_screen *scr, const char *s, size_t len);
extern struct cons_screen g_root_scr;
diff --git a/sys/include/dev/dmi/dmi.h b/sys/include/dev/dmi/dmi.h
new file mode 100644
index 0000000..8b7030c
--- /dev/null
+++ b/sys/include/dev/dmi/dmi.h
@@ -0,0 +1,42 @@
+/*
+ * 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 _DMI_DMI_H_
+#define _DMI_DMI_H_
+
+#include <sys/types.h>
+
+const char *dmi_vendor(void);
+const char *dmi_prodver(void);
+const char *dmi_prodfam(void);
+const char *dmi_product(void);
+const char *dmi_cpu_manufact(void);
+const char *dmi_cpu_version(void);
+
+#endif /* !_DMI_DMI_H_ */
diff --git a/sys/include/dev/dmi/dmivar.h b/sys/include/dev/dmi/dmivar.h
new file mode 100644
index 0000000..e5da92f
--- /dev/null
+++ b/sys/include/dev/dmi/dmivar.h
@@ -0,0 +1,41 @@
+/*
+ * 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 _DEV_DMIVAR_H_
+#define _DEV_DMIVAR_H_
+
+#include <sys/types.h>
+#include <sys/sio.h>
+#include <fs/ctlfs.h>
+
+extern struct ctlops g_ctl_board_ident;
+
+int dmi_board_ctl_read(struct ctlfs_dev *cdp, struct sio_txn *sio);
+
+#endif /* !_DEV_DMIVAR_H_ */
diff --git a/sys/include/dev/ic/ahciregs.h b/sys/include/dev/ic/ahciregs.h
index f959a1e..232b41e 100644
--- a/sys/include/dev/ic/ahciregs.h
+++ b/sys/include/dev/ic/ahciregs.h
@@ -88,6 +88,7 @@ struct hba_memspace {
*/
#define AHCI_PXSSTS_DET(SSTS) (SSTS & 0xF)
#define AHCI_PXSSTS_IPM(SSTS) ((SSTS >> 8) & 0xF)
+#define AHCI_PXSSTS_SPD(SSTS) ((SSTS >> 4) & 0xF)
/*
* Port SATA control bits
@@ -100,6 +101,7 @@ struct hba_memspace {
* See section 3.3.7 of the AHCI spec.
*/
#define AHCI_PXCMD_ST BIT(0) /* Start */
+#define AHCI_PXCMD_SUD BIT(1) /* Spin-up device */
#define AHCI_PXCMD_FRE BIT(4) /* FIS Receive Enable */
#define AHCI_PXCMD_FR BIT(14) /* FIS Receive Running */
#define AHCI_PXCMD_CR BIT(15) /* Command List Running */
@@ -137,6 +139,9 @@ struct hba_memspace {
#define AHCI_DET_PRESENT 1 /* Device present (no PHY comm) */
#define AHCI_DET_COMM 3 /* Device present and phy comm established */
#define AHCI_IPM_ACTIVE 1
+#define AHCI_SPD_GEN1 1 /* 1.5 Gb/s */
+#define AHCI_SPD_GEN2 2 /* 3 Gb/s */
+#define AHCI_SPD_GEN3 3 /* 6 Gb/s */
/*
* PxSERR bits
@@ -158,6 +163,8 @@ struct hba_memspace {
#define AHCI_DIAG_T BIT(24) /* Transport state transition error */
#define AHCI_DIAG_F BIT(25) /* Unknown FIS type */
+#define ATAPI_SIG 0xEB140101
+
/*
* Device detection initialization values
* See section 3.3.11 of the AHCI spec.
diff --git a/sys/include/dev/ic/ahcivar.h b/sys/include/dev/ic/ahcivar.h
index fa24812..67f2efe 100644
--- a/sys/include/dev/ic/ahcivar.h
+++ b/sys/include/dev/ic/ahcivar.h
@@ -33,9 +33,12 @@
#include <sys/param.h>
#include <sys/types.h>
#include <sys/device.h>
+#include <dev/dcdr/cache.h>
#include <dev/ic/ahciregs.h>
#include <fs/ctlfs.h>
+#define AHCI_DCDR_CAP 16
+
struct ahci_cmd_hdr;
extern const struct ctlops g_sata_bsize_ops;
@@ -93,6 +96,7 @@ struct ahci_hba {
* @io: Memory mapped port registers
* @hba: HBA descriptor
* @cmdlist: Command list [p]
+ * @nlba: Max number of addressable blocks
* @fra: FIS receive area [p]
* @dev: Device minor number.
*/
@@ -100,6 +104,8 @@ struct hba_device {
struct hba_port *io;
struct ahci_hba *hba;
struct ahci_cmd_hdr *cmdlist;
+ struct dcdr *dcdr;
+ uint32_t nlba;
void *fra;
dev_t dev;
};
diff --git a/sys/include/dev/ic/nvmevar.h b/sys/include/dev/ic/nvmevar.h
index eab8b52..f361d0a 100644
--- a/sys/include/dev/ic/nvmevar.h
+++ b/sys/include/dev/ic/nvmevar.h
@@ -31,9 +31,11 @@
#define _IC_NVMEVAR_H_
#include <sys/types.h>
+#include <sys/cdefs.h>
/* Admin commands */
#define NVME_OP_CREATE_IOSQ 0x01
+#define NVME_OP_GET_LOGPAGE 0x02
#define NVME_OP_CREATE_IOCQ 0x05
#define NVME_OP_IDENTIFY 0x06
@@ -45,6 +47,67 @@
#define NVME_OP_WRITE 0x01
#define NVME_OP_READ 0x02
+/* Log page identifiers */
+#define NVME_LOGPAGE_SMART 0x02
+
+/*
+ * S.M.A.R.T health / information log
+ *
+ * See section 5.16.1.3, figure 207 of the
+ * NVMe base spec (rev 2.0a)
+ *
+ * @cwarn: Critical warning
+ * @temp: Composite tempature (kelvin)
+ * @avail_spare: Available spare (in percentage)
+ * @avail_spare_thr: Available spare threshold
+ * @percent_used: Estimate NVMe life used percentage
+ * @end_cwarn: Endurance group critical warning summary
+ * @data_units_read: Number of 512 byte data units read
+ * @data_units_written: Number of 512 byte data units written
+ * @host_reads: Number of host read commands completed
+ * @host_writes: Number of host write commands completed
+ * @ctrl_busy_time: Controller busy time
+ * @power_cycles: Number of power cycles
+ * @power_on_hours: Number of power on hours
+ * @unsafe_shutdowns: Number of unsafe shutdowns
+ * @media_errors: Media and data integrity errors
+ * @n_errlog_entries: Number of error log info entries
+ * @warning_temp_time: Warning composite tempature time
+ * @critical_comp_time: Critical composite tempature time
+ * @temp_sensor: Tempature sensor <n> data
+ * @temp1_trans_cnt: Tempature 1 transition count
+ * @temp2_trans_cnt: Tempature 2 transition count
+ * @temp1_total_time: Total time for tempature 1
+ * @temp2_total_time: Total time for tempature 2
+ */
+struct __packed nvme_smart_data {
+ uint8_t cwarn;
+ uint16_t temp;
+ uint8_t avail_spare;
+ uint8_t avail_spare_thr;
+ uint8_t percent_used;
+ uint8_t end_cwarn;
+ uint8_t reserved[25];
+ uint8_t data_units_read[16];
+ uint8_t data_units_written[16];
+ uint8_t host_reads[16];
+ uint8_t host_writes[16];
+ uint8_t ctrl_busy_time[16];
+ uint8_t power_cycles[16];
+ uint8_t power_on_hours[16];
+ uint8_t unsafe_shutdowns[16];
+ uint8_t media_errors[16];
+ uint8_t n_errlog_entries[16];
+ uint32_t warning_temp_time;
+ uint32_t critical_comp_time;
+ uint16_t temp_sensor[8];
+ uint32_t temp1_trans_cnt;
+ uint32_t temp2_trans_cnt;
+ uint32_t temp1_total_time;
+ uint32_t temp2_total_time;
+ uint8_t reserved1[280];
+};
+
struct nvme_identify_cmd {
uint8_t opcode;
uint8_t flags;
@@ -98,6 +161,26 @@ struct nvme_create_iosq_cmd {
uint64_t unused3[2];
};
+/* Get log page */
+struct nvme_get_logpage_cmd {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t nsid;
+ uint64_t unused[2];
+ uint64_t prp1;
+ uint64_t prp2;
+ uint8_t lid;
+ uint8_t lsp;
+ uint16_t numdl;
+ uint16_t numdu;
+ uint16_t lsi;
+ uint64_t lpo;
+ uint8_t unused1[3];
+ uint8_t csi;
+ uint32_t unused2;
+};
+
/* Read/write */
struct nvme_rw_cmd {
uint8_t opcode;
@@ -117,12 +200,12 @@ struct nvme_rw_cmd {
uint16_t appmask;
};
-
struct nvme_cmd {
union {
struct nvme_identify_cmd identify;
struct nvme_create_iocq_cmd create_iocq;
struct nvme_create_iosq_cmd create_iosq;
+ struct nvme_get_logpage_cmd get_logpage;
struct nvme_rw_cmd rw;
};
};
diff --git a/sys/include/dev/mii/mii.h b/sys/include/dev/mii/mii.h
new file mode 100644
index 0000000..5d77281
--- /dev/null
+++ b/sys/include/dev/mii/mii.h
@@ -0,0 +1,57 @@
+/*
+ * 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 _DEV_MII_H_
+#define _DEV_MII_H_
+
+#include <sys/param.h>
+
+/*
+ * MII registers
+ */
+#define MII_BMCR 0x00 /* Basic Mode Config */
+#define MII_BMSR 0x01 /* Basic Mode Status */
+#define MII_PHYID 0x02 /* MII PHY identifier 1 */
+#define MII_PHYID2 0x03 /* MII PHY identifier 2 */
+#define MII_ADVER 0x04 /* Auto-negotiation advertisement */
+#define MII_LPA 0x05 /* Link parter abilities */
+#define MII_EXPAN 0x06 /* Auto-negotiation expansion */
+#define MII_ESTATUS 0x0F /* Extended status register */
+#define MII_IRQ 0x1B /* Interrupt control/status */
+
+/*
+ * MII BMCR bits
+ */
+#define MII_BMCR_RST BIT(15) /* PHY reset */
+#define MII_BCMR_LOOP BIT(14) /* Loopback mode enable */
+#define MII_BMCR_ANEN BIT(12) /* Auto-negotiation enable */
+#define MII_PWR_DOWN BIT(11) /* Power down PHY */
+#define MII_ISOLATE BIT(10) /* Electrically isolate PHY from MII */
+
+#endif /* !_DEV_MII_H_ */
diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h
index de6d8fb..144b500 100644
--- a/sys/include/dev/pci/pci.h
+++ b/sys/include/dev/pci/pci.h
@@ -62,6 +62,7 @@ struct pci_device {
uint8_t pci_subclass;
uint8_t prog_if;
uint8_t hdr_type;
+ uint8_t pci_express : 1;
uint8_t pri_bus;
uint8_t sec_bus;
@@ -75,7 +76,7 @@ struct pci_device {
struct msi_intr {
const char *name;
- void(*handler)(void *);
+ int(*handler)(void *);
};
pcireg_t pci_readl(struct pci_device *dev, uint32_t offset);
diff --git a/sys/include/dev/phy/e1000regs.h b/sys/include/dev/phy/e1000regs.h
new file mode 100644
index 0000000..7caceee
--- /dev/null
+++ b/sys/include/dev/phy/e1000regs.h
@@ -0,0 +1,119 @@
+/*
+ * 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 _PHY_E1000_REGS_H_
+#define _PHY_E1000_REGS_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+/*
+ * E1000 register offsets
+ *
+ * XXX: Notes about reserve fields:
+ *
+ * - The `EERD' register is reserved and should NOT be touched
+ * for the 82544GC/EI card.
+ *
+ * - The `FLA' register is only usable for the 82541xx and
+ * 82547GI/EI cards, this is reserved and should NOT be
+ * touched on any other cards.
+ *
+ * - The `TXCW' and `RXCW' registers are reserved and should NOT
+ * be touched for the 82540EP/EM, 82541xx and 82547GI/EI cards.
+ *
+ * - The `LEDCTL' register is reserved and should NOT be touched
+ * for the 82544GC/EI card.
+ */
+#define E1000_CTL 0x00000 /* Control register */
+#define E1000_STATUS 0x00008 /* Status register */
+#define E1000_EECD 0x00010 /* EEPROM/flash control and data register */
+#define E1000_EERD 0x00014 /* EEPROM/flash read register */
+#define E1000_FLA 0x0001C /* EEPROM/flash read register */
+#define E1000_CTRL_EXT 0x00018 /* Extended device control register */
+#define E1000_MDIC 0x00020 /* PHY management data interface control register */
+#define E1000_FCAL 0x00028 /* Flow control low register */
+#define E1000_FCAH 0x0002C /* Flow control high register */
+#define E1000_FCT 0x00030 /* Flow control type register */
+#define E1000_VET 0x00038 /* VLAN ethertype register */
+#define E1000_FCTTV 0x00170 /* Flow control transmit timer value register */
+#define E1000_TXCW 0x00178 /* Transmit config word register */
+#define E1000_RXCW 0x00180 /* Receive config word register */
+#define E1000_LEDCTL 0x00E00 /* LED control register */
+
+/*
+ * Device control register (`ctl') bits
+ *
+ * See section 13.4.1 of the PCI/PCI-X Intel Gigabit
+ * Ethernet Controllers spec
+ *
+ * XXX: Notes about reserved bits:
+ *
+ * - The CTL.LRST bit is reserved and should NOT be touched
+ * for the 82540EP/EM, 82541xx, or 82547GI/EI cards.
+ */
+#define E1000_CTL_FD BIT(0) /* Full-duplex */
+#define E1000_CTL_LRST BIT(3) /* Link-reset */
+#define E1000_CTL_RST BIT(26) /* Device reset */
+
+/*
+ * EEPROM/flash control and data register (`eecd')
+ * bits
+ *
+ * See section 13.4.3 of the PCI/PCI-X Intel Gigabit
+ * Ethernet controller spec
+ */
+#define E1000_EECD_SK BIT(0) /* EEPROM clock input */
+#define E1000_EECD_CS BIT(1) /* EEPROM chip select */
+#define E1000_EECD_DI BIT(2) /* EEPROM data input */
+#define E1000_EECD_DO BIT(3) /* EEPROM data output */
+#define E1000_EECD_FWE BIT(4) /* EEPROM flash write enable ctl (4:5) */
+#define E1000_EECD_REQ BIT(6) /* Request EEPROM access */
+#define E1000_EECD_GNT BIT(7) /* Grant EEPROM access */
+#define E1000_EECD_PRES BIT(8) /* EEPROM present */
+#define E1000_EECD_SIZE BIT(9) /* EEPROM size (1024-bit [0], 4096-bit [1]) */
+#define E1000_EECD_TYPE BIT(13) /* EEPROM type (microwire [0], SPI [1]) */
+
+/*
+ * EEPROM read (`eerd') register bits
+ *
+ * See section 13.4.4 of the PCI/PCI-X Intel Gigabit
+ * Ethernet controller spec
+ */
+#define E1000_EERD_START BIT(0) /* Start read */
+#define E1000_EERD_DONE BIT(4) /* EEPROM read finished */
+
+/*
+ * EEPROM word addresses
+ */
+#define E1000_HWADDR0 0x00 /* Word 0 */
+#define E1000_HWADDR1 0x01 /* Word 1 */
+#define E1000_HWADDR2 0x02 /* Word 2 */
+
+#endif /* !_PHY_E1000_REGS_H_ */
diff --git a/sys/include/dev/phy/et131xregs.h b/sys/include/dev/phy/et131xregs.h
new file mode 100644
index 0000000..1f8bfcb
--- /dev/null
+++ b/sys/include/dev/phy/et131xregs.h
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+/*
+ * Please refer to share/docs/hw/et131x.txt
+ */
+
+#ifndef _PHYS_ET131XREGS_H_
+#define _PHYS_ET131XREGS_H_
+
+#include <sys/types.h>
+
+#define MAC_CFG1_SOFTRST 0x80000000 /* Soft reset */
+#define MAC_CFG1_SIMRST 0x40000000 /* SIM reset */
+#define MAC_CFG1_RESET_RXMC 0x00080000 /* RX MC reset */
+#define MAC_CFG1_RESET_TXMC 0x00040000 /* TX MC reset */
+#define MAC_CFG1_RESET_RXFUNC 0x00020000 /* RX func reset */
+#define MAC_CFG1_RESET_TXFUNC 0x00010000 /* TX func reset */
+
+#define PAD_N(N, NAME) uint8_t (NAME)[(N)]
+
+/*
+ * ET131X global registers
+ */
+struct global_regs {
+ uint32_t txq_start;
+ uint32_t txq_end;
+ uint32_t rxq_start;
+ uint32_t rxq_end;
+ uint32_t pm_csr;
+ uint32_t unused;
+ uint32_t istat;
+ uint32_t imask;
+ uint32_t ialias_clr_en;
+ uint32_t istat_alias;
+ uint32_t sw_reset;
+ uint32_t slv_timer;
+ uint32_t msi_config;
+ uint32_t loopback;
+ uint32_t watchdog_timer;
+};
+
+/*
+ * ET131X TX DMA registers
+ */
+struct txdma_regs {
+ uint32_t csr;
+ uint32_t pr_base_hi;
+ uint32_t pr_base_lo;
+ uint32_t pr_num_des;
+ uint32_t txq_wr_addr;
+ uint32_t txq_wr_addr_ext;
+ uint32_t txq_rd_addr;
+ uint32_t dma_wb_base_hi;
+ uint32_t dma_wb_base_lo;
+ uint32_t service_request;
+ uint32_t service_complete;
+ uint32_t cache_rd_index;
+ uint32_t cache_wr_index;
+ uint32_t tx_dma_error;
+ uint32_t des_abort_cnt;
+ uint32_t payload_abort_cnt;
+ uint32_t wb_abort_cnt;
+ uint32_t des_timeout_cnt;
+ uint32_t payload_timeout_cnt;
+ uint32_t wb_timeout_cnt;
+ uint32_t des_error_cnt;
+ uint32_t payload_err_cnt;
+ uint32_t wb_error_cnt;
+ uint32_t dropped_tlp_cnt;
+ uint32_t new_service_complete;
+ uint32_t ether_pkt_cnt;
+};
+
+/*
+ * ET131X RX DMA registers
+ */
+struct rxdma_regs {
+ uint32_t csr;
+ uint32_t dma_wb_base_lo;
+ uint32_t dma_wb_base_hi;
+ uint32_t num_pkt_done;
+ uint32_t max_pkt_time;
+ uint32_t rxq_rd_addr;
+ uint32_t rxq_rd_addr_ext;
+ uint32_t rxq_wr_addr;
+ uint32_t psr_base_lo;
+ uint32_t psr_base_hi;
+ uint32_t psr_num_des;
+ uint32_t psr_avail_offset;
+ uint32_t psr_full_offset;
+ uint32_t psr_access_index;
+ uint32_t psr_min_des;
+ uint32_t fbr0_base_lo;
+ uint32_t fbr0_base_hi;
+ uint32_t fbr0_num_des;
+ uint32_t fbr0_avail_offset;
+ uint32_t fbr0_full_offset;
+ uint32_t fbr0_rd_index;
+ uint32_t fbr0_min_des;
+ uint32_t fbr1_base_lo;
+ uint32_t fbr1_base_hi;
+ uint32_t fbr1_num_des;
+ uint32_t fbr1_avail_offset;
+ uint32_t fbr1_full_offset;
+ uint32_t fbr1_rd_index;
+ uint32_t fbr1_min_des;
+};
+
+/*
+ * ET131X TX MAC registers
+ */
+struct txmac_regs {
+ uint32_t ctl;
+ uint32_t shadow_ptr;
+ uint32_t err_cnt;
+ uint32_t max_fill;
+ uint32_t cf_param;
+ uint32_t tx_test;
+ uint32_t err;
+ uint32_t err_int;
+ uint32_t bp_ctrl;
+};
+
+/*
+ * ET131X RX MAC registers
+ */
+struct rxmac_regs {
+ uint32_t ctrl;
+ uint32_t crc0;
+ uint32_t crc12;
+ uint32_t crc34;
+ uint32_t sa_lo;
+ uint32_t sa_hi;
+ uint32_t mask0_word0;
+ uint32_t mask0_word1;
+ uint32_t mask0_word2;
+ uint32_t mask0_word3;
+ uint32_t mask1_word0;
+ uint32_t mask1_word1;
+ uint32_t mask1_word2;
+ uint32_t mask1_word3;
+ uint32_t mask2_word0;
+ uint32_t mask2_word1;
+ uint32_t mask2_word2;
+ uint32_t mask2_word3;
+ uint32_t mask3_word0;
+ uint32_t mask3_word1;
+ uint32_t mask3_word2;
+ uint32_t mask3_word3;
+ uint32_t mask4_word0;
+ uint32_t mask4_word1;
+ uint32_t mask4_word2;
+ uint32_t mask4_word3;
+ uint32_t uni_pf_addr1;
+ uint32_t uni_pf_addr2;
+ uint32_t uni_pf_addr3;
+ uint32_t multi_hash1;
+ uint32_t multi_hash2;
+ uint32_t multi_hash3;
+ uint32_t multi_hash4;
+ uint32_t pf_ctrl;
+ uint32_t mcif_ctrl_max_seg;
+ uint32_t mcif_water_mark;
+ uint32_t rxq_diag;
+ uint32_t space_avail;
+ uint32_t mif_ctrl;
+ uint32_t err_reg;
+};
+
+struct mac_regs {
+ uint32_t cfg1;
+ uint32_t cfg2;
+ uint32_t ipg;
+ uint32_t hfdp;
+ uint32_t max_fm_len;
+ uint32_t rsv1;
+ uint32_t rsv2;
+ uint32_t mac_test;
+ uint32_t mii_mgmt_cfg;
+ uint32_t mii_mgmt_cmd;
+ uint32_t mii_mgmt_addr;
+ uint32_t mii_mgmt_ctrl;
+ uint32_t mii_mgmt_stat;
+ uint32_t mii_mgmt_indicator;
+ uint32_t if_ctrl;
+ uint32_t if_stat;
+ uint32_t station_addr_1;
+ uint32_t station_addr_2;
+};
+
+/* Global reset */
+#define GBL_RESET_ALL 0x007F
+
+/* MII management address */
+#define MAC_MII_ADDR(PHY, REG) ((PHY) << 8 | (REG))
+
+/* MAC management indications */
+#define MAC_MGMT_BUSY 0x00000001
+#define MAC_MGMT_WAIT 0x00000005
+
+/* MAC management config values */
+#define MAC_MIIMGMT_CLK_RST 0x00007
+
+/* LED register defines */
+#define PHY_LED2 0x1C
+
+/* PCI config space offsets */
+#define PCI_EEPROM_STATUS 0xB2
+#define PCI_MAC_ADDRESS 0xA4
+
+/*
+ * LED control register 2 values
+ */
+#define LED_BLINK 0xD
+#define LED_ON 0xE
+#define LED_OFF 0xF
+#define LED_ALL_OFF 0xFFFF
+
+/*
+ * LED register bit-shift constants
+ *
+ * Bits [3:0]: 100BASE-T LED
+ * Bits [7:4]: 100BASE-TX LED
+ * Bits [11:8]: TX/RX LED
+ * Bits [15:12]: Link LED
+ */
+#define LED_TXRX_SHIFT 8
+#define LED_LINK_SHIFT 12
+
+struct et131x_iospace {
+#define _IO_PAD(NAME, REGSET) uint8_t NAME[4096 - sizeof(struct REGSET)]
+ struct global_regs global;
+ _IO_PAD(global_pad, global_regs);
+ struct txdma_regs txdma;
+ _IO_PAD(txdma_pad, txdma_regs);
+ struct rxdma_regs rxdma;
+ _IO_PAD(rxdma_pad, rxdma_regs);
+ struct txmac_regs txmac;
+ _IO_PAD(txmac_pad, txmac_regs);
+ struct rxmac_regs rxmac;
+ _IO_PAD(rxmac_pad, rxmac_regs);
+ struct mac_regs mac;
+ _IO_PAD(mac_pad, mac_regs);
+ /* ... TODO - add more */
+#undef _IO_PAD
+};
+
+#endif /* !_PHYS_ET131XREGS_H_ */
diff --git a/sys/include/dev/phy/rt8139.h b/sys/include/dev/phy/rtl.h
index 21c7d54..f3178d0 100644
--- a/sys/include/dev/phy/rt8139.h
+++ b/sys/include/dev/phy/rtl.h
@@ -71,6 +71,9 @@
#define RT_AS_LPAR 0x68 /* Auto-negotiation link partner reg (16 bits) */
#define RT_AS_EXPANSION 0x6A /* Auto-negotiation expansion reg (16 bits) */
+#define RT_TXAD_N(N) (RT_TXADDR0 + (N))
+#define RT_TXSTATUS_N(N) (RT_TXSTATUS0 + ((N)))
+
/* Command register bits */
#define RT_BUFEN BIT(0) /* Buffer empty */
#define RT_TE BIT(2) /* Transmitter enable */
diff --git a/sys/include/dev/random/entropy.h b/sys/include/dev/random/entropy.h
new file mode 100644
index 0000000..34d86df
--- /dev/null
+++ b/sys/include/dev/random/entropy.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <stdint.h>
+
+#define ENTROPY_POOL_SIZE 32
+
+struct entropy_pool {
+ uint8_t pool[ENTROPY_POOL_SIZE];
+ uint32_t entropy_bits;
+};
+
+void mix_entropy(struct entropy_pool *ep, const uint8_t *input,
+ size_t input_len, uint32_t input_entropy_bits);
diff --git a/sys/include/dev/timer.h b/sys/include/dev/timer.h
index e54adcc..2ca6d62 100644
--- a/sys/include/dev/timer.h
+++ b/sys/include/dev/timer.h
@@ -31,11 +31,15 @@
#define _DEV_TIMER_H_
#include <sys/types.h>
+#include <sys/param.h>
/* Timer IDs */
#define TIMER_SCHED 0x00000000U /* Scheduler reserved timer */
#define TIMER_GP 0x00000001U /* General purpose timer */
+/* Timer flags */
+#define TIMER_MONOTONIC BIT(0)
+
/* Number of timer IDs, adjust when adding timer IDs */
#define TIMER_ID_COUNT 2
@@ -69,6 +73,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);
@@ -78,6 +83,7 @@ struct timer {
void(*oneshot_ms)(size_t ms);
void(*oneshot_us)(size_t ms);
void(*stop)(void);
+ uint8_t flags;
};
tmrr_status_t register_timer(timer_id_t id, const struct timer *tmr);
diff --git a/sys/include/dev/usb/xhciregs.h b/sys/include/dev/usb/xhciregs.h
index 69515e4..cafd7c9 100644
--- a/sys/include/dev/usb/xhciregs.h
+++ b/sys/include/dev/usb/xhciregs.h
@@ -77,6 +77,7 @@ struct xhci_opregs {
/* USBSTS bits */
#define USBSTS_HCH BIT(0) /* HC halted */
+#define USBSTS_CNR BIT(11) /* Controller not ready */
/* CAPS.HCSPARAMS1 fields */
#define XHCI_MAXSLOTS(HCSPARAMS1) (HCSPARAMS1 & 0xFF)
@@ -98,6 +99,13 @@ struct xhci_opregs {
#define XHCI_RTS(BASE, RTSOFF) PTR_OFFSET(BASE, RTSOFF)
#define XHCI_CMD_DB(BASE, DBOFF) PTR_OFFSET(BASE, DBOFF)
+/* Runtime register offsets */
+#define XHCI_RT_IMAN 0x20
+#define XHCI_RT_IMOD 0x24
+#define XHCI_RT_ERSTSZ 0x28
+#define XHCI_RT_ERSTBA 0x30
+#define XHCI_RT_ERDP 0x38
+
/* Support protocol cap fields */
#define XHCI_PROTO_ID(PROTO) (PROTO & 0xFF)
#define XHCI_PROTO_MINOR(PROTO) ((PROTO >> 16) & 0xFF)
diff --git a/sys/include/dev/video/fbdev.h b/sys/include/dev/video/fbdev.h
index c80fd92..c9fec94 100644
--- a/sys/include/dev/video/fbdev.h
+++ b/sys/include/dev/video/fbdev.h
@@ -52,5 +52,6 @@ fbdev_get_index(const struct fbdev *fbdev, uint32_t x, uint32_t y)
}
struct fbdev fbdev_get(void);
+void fbdev_init_dev(void);
#endif /* !_DEV_FBDEV_H_ */
diff --git a/sys/include/fs/ctlfs.h b/sys/include/fs/ctlfs.h
index 90f42f0..29ae358 100644
--- a/sys/include/fs/ctlfs.h
+++ b/sys/include/fs/ctlfs.h
@@ -42,12 +42,10 @@ struct ctlops {
/*
* Ctlfs op arguments
*
- * @devname: Device name.
- * @major: Device major
- * @minor: Device minor.
- * @mode: Access flags.
* @devname [1]: Device name (node name)
* @ctlname: [1]: Control name (node entry name)
+ * @ops: Callbacks / fs hooks
+ * @mode: Access flags.
*/
struct ctlfs_dev {
union {
diff --git a/sys/include/fs/devfs.h b/sys/include/fs/devfs.h
index 012c2eb..51b0b45 100644
--- a/sys/include/fs/devfs.h
+++ b/sys/include/fs/devfs.h
@@ -33,9 +33,11 @@
#include <sys/vnode.h>
#include <sys/types.h>
#include <sys/device.h>
+#include <sys/devstat.h>
extern const struct vops g_devfs_vops;
int devfs_create_entry(const char *name, devmajor_t major, dev_t dev, mode_t mode);
+int devfs_devstat(struct vnode *vp, struct devstat *res);
#endif /* !_FS_DEVFS_H_ */
diff --git a/sys/include/fs/tmpfs.h b/sys/include/fs/tmpfs.h
new file mode 100644
index 0000000..acb5256
--- /dev/null
+++ b/sys/include/fs/tmpfs.h
@@ -0,0 +1,77 @@
+/*
+ * 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 _FS_TMPFS_H_
+#define _FS_TMPFS_H_
+
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <sys/vnode.h>
+#include <sys/queue.h>
+#include <sys/spinlock.h>
+#include <vm/vm_obj.h>
+
+extern const struct vops g_tmpfs_vops;
+
+/* Tmpfs node types */
+#define TMPFS_NONE (VNON) /* No type */
+#define TMPFS_REG (VREG) /* Regular file [f] */
+#define TMPFS_DIR (VDIR) /* Directory [d] */
+
+struct tmpfs_node;
+
+/*
+ * A tmpfs node represents an object within the
+ * tmpfs namespace such as a file, directory, etc.
+ *
+ * @rpath: /tmp/ relative path (for lookups)
+ * @type: The tmpfs node type [one-to-one to vtype]
+ * @len: Length of buffer
+ * @real_size: Actual size of file
+ * @data: The backing file data
+ * @mode: File permissions
+ * @dirvp: Vnode of the parent node
+ * @vp: Vnode of the current node
+ * @lock: Lock protecting this node
+ */
+struct tmpfs_node {
+ char rpath[PATH_MAX];
+ uint8_t type;
+ size_t len;
+ size_t real_size;
+ void *data;
+ mode_t mode;
+ struct vnode *dirvp;
+ struct vnode *vp;
+ struct spinlock lock;
+ TAILQ_HEAD(, tmpfs_node) dirents;
+ TAILQ_ENTRY(tmpfs_node) link;
+};
+
+#endif /* !_FS_TMPFS_H_ */
diff --git a/sys/include/lib/crc32.h b/sys/include/lib/crc32.h
new file mode 100644
index 0000000..a7e5eeb
--- /dev/null
+++ b/sys/include/lib/crc32.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 _LIB_CRC32_H_
+#define _LIB_CRC32_H_
+
+#include <sys/types.h>
+
+uint32_t crc32(const void *data, size_t len);
+
+#endif
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/net/ethertypes.h b/sys/include/net/ethertypes.h
new file mode 100644
index 0000000..753ea10
--- /dev/null
+++ b/sys/include/net/ethertypes.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _NET_ETHERTYPES_H_
+#define _NET_ETHERTYPES_H_
+
+#define ETHERTYPE_IPV4 0x0800
+#define ETHERTYPE_ARP 0x0806
+
+#endif /* !_NET_ETHERTYPES_H_ */
diff --git a/sys/include/net/if_arp.h b/sys/include/net/if_arp.h
new file mode 100644
index 0000000..cbfb2fe
--- /dev/null
+++ b/sys/include/net/if_arp.h
@@ -0,0 +1,51 @@
+/*
+ * 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 _NETINET_IF_ARP_H_
+#define _NETINET_IF_ARP_H_
+
+#include <sys/types.h>
+#include <net/ethertypes.h>
+
+/* ARP hardware types */
+#define ARP_HWTYPE_ETHER 1
+
+/* ARP operation types */
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+
+struct arp_hdr {
+ uint16_t hw_type; /* See ARP_HWTYPE_* */
+ uint16_t proto_type; /* See ETHERTYPE_* */
+ uint8_t hw_len; /* See ETHER_ADDR_LEN */
+ uint8_t proto_len; /* Protocol address length */
+ uint16_t op_type; /* See operation types above */
+};
+
+#endif /* !_NETINET_IF_ARP_H_ */
diff --git a/sys/include/net/if_var.h b/sys/include/net/if_var.h
new file mode 100644
index 0000000..e032ff4
--- /dev/null
+++ b/sys/include/net/if_var.h
@@ -0,0 +1,82 @@
+/*
+ * 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 _NET_IF_VAR_H_
+#define _NET_IF_VAR_H_
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <net/netbuf.h>
+
+#define NETIF_ADDR_LEN 32 /* In bytes */
+
+/* Return values for netif hooks */
+#define NETIF_ENQ_OK 0 /* Enqueued */
+#define NETIF_ENQ_FLUSHED 1 /* Internal queue flushed */
+
+/* Interface types */
+#define NETIF_TYPE_ANY 0 /* Any type */
+#define NETIF_TYPE_WIRE 1 /* Ethernet */
+
+/*
+ * Represents the address of a network
+ * interface.
+ *
+ * @data: Raw address bytes
+ */
+struct netif_addr {
+ uint8_t data[NETIF_ADDR_LEN];
+};
+
+/*
+ * Represents a network interface
+ *
+ * @name: Interface name
+ * @type: Interface type (see NETIF_TYPE*)
+ * @tx_enq: Enqueue a packet
+ * @tx_start: Start a packet
+ *
+ * XXX: tx_enq() returns 0 on success and 1 if a flush was needed
+ * and the packets have been transmitted. Less than zero values
+ * indicate failure.
+ */
+struct netif {
+ char name[IFNAMESIZ];
+ uint8_t type;
+ TAILQ_ENTRY(netif) link;
+ struct netif_addr addr;
+ int(*tx_enq)(struct netif *nifp, struct netbuf *nbp, void *data);
+ void(*tx_start)(struct netif *nifp);
+};
+
+void netif_add(struct netif *nifp);
+int netif_lookup(const char *name, uint8_t type, struct netif **res);
+
+#endif /* !_NET_IF_VAR_H_ */
diff --git a/sys/include/net/netbuf.h b/sys/include/net/netbuf.h
new file mode 100644
index 0000000..7067370
--- /dev/null
+++ b/sys/include/net/netbuf.h
@@ -0,0 +1,42 @@
+/*
+ * 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 _NET_NETBUF_H_
+#define _NET_NETBUF_H_
+
+#include <sys/types.h>
+
+#define NETBUF_LEN 256
+
+struct netbuf {
+ char data[NETBUF_LEN];
+ size_t len;
+};
+
+#endif /* !_NET_NETBUF_H_ */
diff --git a/sys/include/netinet/if_ether.h b/sys/include/netinet/if_ether.h
new file mode 100644
index 0000000..d3dc9b7
--- /dev/null
+++ b/sys/include/netinet/if_ether.h
@@ -0,0 +1,56 @@
+/*
+ * 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 _NETINET_IF_ETHER_H_
+#define _NETINET_IF_ETHER_H_
+
+#include <sys/types.h>
+#include <net/if_arp.h>
+#include <net/if_var.h>
+
+#define ETHER_ADDR_LEN 6
+
+struct ether_arp {
+ struct arp_hdr hdr;
+ uint8_t sha[ETHER_ADDR_LEN];
+ uint8_t spa[4];
+ uint8_t tha[ETHER_ADDR_LEN];
+ uint8_t tpa[4];
+};
+
+struct ether_frame {
+ uint8_t ether_daddr[ETHER_ADDR_LEN];
+ uint8_t ether_saddr[ETHER_ADDR_LEN];
+ uint16_t ether_type;
+};
+
+int arp_request(struct netif *nifp, uint8_t *sproto, uint8_t *tproto);
+int arp_reply(struct netif *netif, uint8_t *sproto, uint8_t *tproto);
+
+#endif /* !_NETINET_IF_ETHER_H_ */
diff --git a/sys/include/sys/atomic.h b/sys/include/sys/atomic.h
index f61bf62..d9b3bde 100644
--- a/sys/include/sys/atomic.h
+++ b/sys/include/sys/atomic.h
@@ -30,6 +30,8 @@
#ifndef _SYS_ATOMIC_H_
#define _SYS_ATOMIC_H_
+#include <sys/types.h>
+
static inline unsigned long
atomic_add_long_nv(volatile unsigned long *p, unsigned long v)
{
@@ -42,6 +44,12 @@ atomic_add_int_nv(volatile unsigned int *p, unsigned int v)
return __sync_add_and_fetch(p, v);
}
+static inline unsigned int
+atomic_add_64_nv(volatile uint64_t *p, unsigned int v)
+{
+ return __sync_add_and_fetch(p, v);
+}
+
static inline unsigned long
atomic_sub_long_nv(volatile unsigned long *p, unsigned long v)
{
@@ -55,6 +63,12 @@ atomic_sub_int_nv(volatile unsigned int *p, unsigned int v)
}
static inline unsigned int
+atomic_sub_64_nv(volatile uint64_t *p, unsigned int v)
+{
+ return __sync_sub_and_fetch(p, v);
+}
+
+static inline unsigned int
atomic_load_int_nv(volatile unsigned int *p, unsigned int v)
{
return __atomic_load_n(p, v);
@@ -66,6 +80,12 @@ atomic_load_long_nv(volatile unsigned long *p, unsigned int v)
return __atomic_load_n(p, v);
}
+static inline unsigned int
+atomic_load_64_nv(volatile uint64_t *p, unsigned int v)
+{
+ return __atomic_load_n(p, v);
+}
+
static inline void
atomic_store_int_nv(volatile unsigned int *p, int nv, unsigned int v)
{
@@ -78,20 +98,30 @@ atomic_store_long_nv(volatile unsigned long *p, long nv, unsigned int v)
__atomic_store_n(p, nv, v);
}
+static inline void
+atomic_store_64_nv(volatile uint64_t *p, long nv, unsigned int v)
+{
+ __atomic_store_n(p, nv, v);
+}
+
/* Atomic increment (and fetch) operations */
#define atomic_inc_long(P) atomic_add_long_nv((P), 1)
#define atomic_inc_int(P) atomic_add_int_nv((P), 1)
+#define atomic_inc_64(P) atomic_add_64_nv((P), 1)
/* Atomic decrement (and fetch) operations */
#define atomic_dec_long(P) atomic_sub_long_nv((P), 1)
#define atomic_dec_int(P) atomic_sub_int_nv((P), 1)
+#define atomic_dec_64(P) atomic_sub_64_nv((P), 1)
/* Atomic load operations */
#define atomic_load_int(P) atomic_load_int_nv((P), __ATOMIC_SEQ_CST)
#define atomic_load_long(P) atomic_load_long_nv((P), __ATOMIC_SEQ_CST)
+#define atomic_load_64(P) atomic_load_64_nv((P), __ATOMIC_SEQ_CST)
/* Atomic store operations */
#define atomic_store_int(P, NV) atomic_store_int_nv((P), (NV), __ATOMIC_SEQ_CST)
#define atomic_store_long(P, NV) atomic_store_long_nv((P), (NV), __ATOMIC_SEQ_CST)
+#define atomic_store_64(P, NV) atomic_store_64_nv((P), (NV), __ATOMIC_SEQ_CST)
#endif /* !_SYS_ATOMIC_H_ */
diff --git a/sys/include/sys/cdefs.h b/sys/include/sys/cdefs.h
index 61106fa..725193e 100644
--- a/sys/include/sys/cdefs.h
+++ b/sys/include/sys/cdefs.h
@@ -42,7 +42,9 @@
#define __dead __attribute__((__noreturn__))
#define __cold __attribute__((__cold__))
#define __dead_cold __attribute__((__noreturn__, __cold__))
+#define __aligned(n) __attribute__((__aligned__((n))))
#define __unused __attribute__((__unused__))
+#define __used __attribute__((__used__))
#define __nothing ((void)0)
#define __likely(exp) __builtin_expect(((exp) != 0), 1)
#define __unlikely(exp) __builtin_expect(((exp) != 0), 0)
diff --git a/sys/include/sys/console.h b/sys/include/sys/console.h
new file mode 100644
index 0000000..d0b89a8
--- /dev/null
+++ b/sys/include/sys/console.h
@@ -0,0 +1,57 @@
+/*
+ * 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_CONSOLE_H_
+#define _SYS_CONSOLE_H_
+
+#include <sys/types.h>
+
+/*
+ * Console features
+ *
+ * @ansi_esc: If 1, ANSI escape codes are enabled
+ * @show_curs: If 1, show the cursor
+ */
+struct console_feat {
+ uint8_t ansi_esc : 1;
+ uint8_t show_curs : 1;
+};
+
+/*
+ * Console attributes
+ *
+ * @cursor_x: Cursor x position
+ * @cursor_y: Cursor y position
+ */
+struct console_attr {
+ uint32_t cursor_x;
+ uint32_t cursor_y;
+};
+
+#endif /* !_SYS_CONSOLE_H_ */
diff --git a/sys/include/sys/device.h b/sys/include/sys/device.h
index f5f92ad..04b66fc 100644
--- a/sys/include/sys/device.h
+++ b/sys/include/sys/device.h
@@ -36,21 +36,28 @@
#include <sys/queue.h>
#include <sys/proc.h>
#include <sys/sio.h>
+#include <vm/vm_obj.h>
typedef uint8_t devmajor_t;
/* Device operation typedefs */
typedef int(*dev_read_t)(dev_t, struct sio_txn *, int);
typedef int(*dev_write_t)(dev_t, struct sio_txn *, int);
+typedef int(*dev_bsize_t)(dev_t);
struct cdevsw {
int(*read)(dev_t dev, struct sio_txn *sio, int flags);
int(*write)(dev_t dev, struct sio_txn *sio, int flags);
+ paddr_t(*mmap)(dev_t dev, size_t size, off_t off, int flags);
+
+ /* Private */
+ struct vm_object vmobj;
};
struct bdevsw {
int(*read)(dev_t dev, struct sio_txn *sio, int flags);
int(*write)(dev_t dev, struct sio_txn *sio, int flags);
+ int(*bsize)(dev_t dev);
};
void *dev_get(devmajor_t major, dev_t dev);
@@ -61,10 +68,12 @@ int dev_register(devmajor_t major, dev_t dev, void *devsw);
int dev_noread(void);
int dev_nowrite(void);
+int dev_nobsize(void);
/* Device operation stubs */
#define noread ((dev_read_t)dev_noread)
#define nowrite ((dev_write_t)dev_nowrite)
+#define nobsize ((dev_bsize_t)dev_nobsize)
#endif /* _KERNEL */
#endif /* !_SYS_DEVICE_H_ */
diff --git a/sys/include/sys/devstat.h b/sys/include/sys/devstat.h
new file mode 100644
index 0000000..91af30f
--- /dev/null
+++ b/sys/include/sys/devstat.h
@@ -0,0 +1,46 @@
+/*
+ * 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_DEVSTAT_H_
+#define _SYS_DEVSTAT_H_
+
+#include <sys/types.h>
+
+/*
+ * Stats for block devices
+ *
+ * @nwrites: Number of writes total
+ * @nreads: Number of reads total
+ */
+struct devstat {
+ size_t nwrites;
+ size_t nreads;
+};
+
+#endif /* !_SYS_DEVSTAT_H_ */
diff --git a/sys/include/sys/disk.h b/sys/include/sys/disk.h
new file mode 100644
index 0000000..4ad068b
--- /dev/null
+++ b/sys/include/sys/disk.h
@@ -0,0 +1,211 @@
+/*
+ * 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_DISK_H_
+#define _SYS_DISK_H_
+
+#include <sys/syscall.h>
+#include <sys/queue.h>
+#include <sys/device.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <sys/cdefs.h>
+#if defined(_KERNEL)
+#include <dev/dcdr/cache.h>
+#endif /* _KERNEL */
+
+#define DISK_NAME_MAX 64
+
+/*
+ * V_BSIZE is the virtual block size in bytes used
+ * by the disk framework. The virtual block size is a
+ * multiple of the hardware block size and defines
+ * how many bytes a virtual block is made up of.
+ *
+ * A virtual block is simply a unit specific to
+ * the disk framework that represents multiple
+ * hardware disk blocks.
+ */
+#if defined(__V_BSIZE)
+#define V_BSIZE __V_BSIZE
+#else
+#define V_BSIZE 4096
+#endif /* __V_BSIZE */
+
+/* Sanitize the silly human's input */
+_Static_assert(V_BSIZE > 512, "V_BSIZE must be > 512");
+_Static_assert((V_BSIZE & 1) == 0, "V_BSIZE must be a power of two");
+
+#define DISK_PRIMARY 0 /* ID of primary disk */
+
+/*
+ * To prevent unlikely cases of unintended disk
+ * operations (e.g., read, write, etc), we store
+ * a cookie within each set of parameters.
+ *
+ * Requests whose bundle of parameters have no valid
+ * cookie shall be rejected by us.
+ */
+#define DISK_PARAM_COOKIE 0xD1531001
+
+/* Valid disk operations */
+#define DISK_IO_READ 0x00 /* Read data from the disk */
+#define DISK_IO_WRITE 0x01 /* Write data to disk */
+#define DISK_IO_QUERY 0x02 /* Query disk information */
+
+/*
+ * A disk identifier is a zero-based index into
+ * the disk registry.
+ */
+typedef uint16_t diskid_t;
+
+/*
+ * Block offset / LBA
+ */
+typedef off_t blkoff_t;
+
+/*
+ * Disk operations may be requested by user
+ * programs by using a disk operation code.
+ */
+typedef uint8_t diskop_t;
+
+/*
+ * Describes basic disk information
+ *
+ * @block_size: Hardware block size
+ * @vblock_size: Virtual block size
+ * @n_block: Number of blocks total
+ */
+struct disk_info {
+ uint32_t block_size;
+ uint32_t vblock_size;
+ size_t n_block;
+};
+
+/*
+ * The disk metadata structure contains information
+ * describing the disk. It is used for Hyra's pbuf
+ * (persistent buffers / sls) support. This structure
+ * is to be stored at the very last sector of the drive.
+ *
+ * @canary: Boot canary to verify persistent instance
+ * @info: Disk attributes
+ */
+struct disk_root {
+ uint32_t canary;
+ struct disk_info info;
+};
+
+/*
+ * A disk I/O parameter contains information
+ * that is passed from a user application to
+ * the kernel for specific operations.
+ *
+ * @buf: User-side pointer to data buffer
+ * @size: Size of data buffer in bytes
+ * @cookie: Used to prevent unintended operations
+ * @blk: Disk block offset
+ * @u_buf: Used by the kernel to keep track of user buffer
+ */
+struct disk_param {
+ void *buf;
+ size_t size;
+ uint32_t cookie;
+ blkoff_t blk;
+#if defined(_KERNEL)
+ void *u_buf;
+#endif
+};
+
+/*
+ * Helper used to initialize disk I/O parameters.
+ * This is used by the user to initialize a declared
+ * set of parameters.
+ *
+ * @buf: Buffer to operate on
+ * @blk: Disk block to operate on
+ * @size: Operation size in bytes (block-aligned)
+ * @res: Pointer to params to be initialized
+ */
+__always_inline static inline void
+disk_param_init(void *buf, blkoff_t blk, size_t size, struct disk_param *res)
+{
+ if (res != NULL) {
+ res->buf = buf;
+ res->blk = blk;
+ res->size = size;
+ res->cookie = DISK_PARAM_COOKIE;
+ }
+}
+
+/*
+ * User side disk API
+ */
+#if !defined(_KERNEL)
+ssize_t __disk_io(diskid_t id, diskop_t op, const struct disk_param *param);
+#endif /* !_KERNEL */
+
+/* Common disk operations */
+int disk_query(diskid_t id, struct disk_info *res);
+ssize_t disk_read(diskid_t id, blkoff_t blk, void *buf, size_t len);
+ssize_t disk_write(diskid_t id, blkoff_t blk, const void *buf, size_t len);
+
+#if defined(_KERNEL)
+/*
+ * Represents a block storage device
+ *
+ * @name: Name of disk
+ * @cookie: Used internally to ensure validity
+ * @bsize: Hardware block size (defaults to 512 bytes)
+ * @dev: Device minor
+ * @id: Disk ID (zero-based index)
+ * @bdev: Block device operations
+ * @link: TAILQ link
+ */
+struct disk {
+ char name[DISK_NAME_MAX];
+ uint32_t cookie;
+ uint16_t bsize;
+ dev_t dev;
+ diskid_t id;
+ const struct bdevsw *bdev;
+ TAILQ_ENTRY(disk) link;
+};
+
+void *disk_buf_alloc(diskid_t id, size_t len);
+void disk_buf_free(void *p);
+
+int disk_add(const char *name, dev_t dev, const struct bdevsw *bdev, int flags);
+int disk_get_id(diskid_t id, struct disk **res);
+
+scret_t sys_disk(struct syscall_args *scargs);
+#endif /* _KERNEL */
+#endif /* !_SYS_DISK_H_ */
diff --git a/sys/include/sys/disklabel.h b/sys/include/sys/disklabel.h
new file mode 100644
index 0000000..895c35e
--- /dev/null
+++ b/sys/include/sys/disklabel.h
@@ -0,0 +1,48 @@
+/*
+ * 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_DISKLABEL_H_
+#define _SYS_DISKLABEL_H_
+
+#include <sys/types.h>
+
+#define DISK_MAG 0x4F445421UL /* "ODT!" */
+
+/*
+ * Represents a disk table.
+ *
+ * @magic: Magic number (`DISK_MAG')
+ * @sect_size: Disk sector size
+ */
+struct disklabel {
+ uint32_t magic;
+ uint32_t sect_size;
+};
+
+#endif /* !_SYS_DISKLABEL_H_ */
diff --git a/sys/include/sys/dmi.h b/sys/include/sys/dmi.h
new file mode 100644
index 0000000..a21cff6
--- /dev/null
+++ b/sys/include/sys/dmi.h
@@ -0,0 +1,63 @@
+/*
+ * 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_DMI_H_
+#define _SYS_DMI_H_
+
+#if defined(_KERNEL)
+#include <sys/types.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif /* _KERNEL */
+
+/*
+ * Provides board information through
+ * DMI.
+ *
+ * @cpu_version: CPU version string
+ * @cpu_manuf: CPU manufacturer string
+ * @product: Board product string
+ * @vendor: Board vendor string
+ * @version: Product version string
+ *
+ * If index 0 of any of the strings contain
+ * '\0', then they are unsupported/unused.
+ *
+ * XXX: Strings are null terminated
+ */
+struct dmi_board {
+ char cpu_version[64];
+ char cpu_manuf[32];
+ char product[32];
+ char vendor[32];
+ char version[32];
+};
+
+#endif /* !_SYS_DMI_H_ */
diff --git a/sys/include/sys/driver.h b/sys/include/sys/driver.h
index 05c40fa..e10021a 100644
--- a/sys/include/sys/driver.h
+++ b/sys/include/sys/driver.h
@@ -31,27 +31,94 @@
#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);
+ const char *name;
+ struct driver_var *data;
};
+extern struct proc g_proc0;
+
+/* Early (high priority) drivers */
extern char __drivers_init_start[];
extern char __drivers_init_end[];
-#define DRIVER_EXPORT(INIT) \
+/* Deferred (low priority) drivers */
+extern char __driversd_init_start[];
+extern char __driversd_init_end[];
+
+#define DRIVER_EXPORT(INIT, NAME) \
+ static struct driver_var __driver_var = { \
+ .deferred = 0 \
+ }; \
+ \
__attribute__((used, section(".drivers"))) \
static struct driver __driver_desc = { \
.init = INIT, \
+ .data = &__driver_var, \
+ .name = NAME \
}
+/*
+ * 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.
+ */
+#define DRIVER_DEFER(INIT, NAME) \
+ static struct driver_var __driver_var = { \
+ .deferred = 1 \
+ }; \
+ \
+ __attribute__((used, section(".drivers.defer"))) \
+ static struct driver __driver_desc = { \
+ .init = INIT, \
+ .data = &__driver_var, \
+ .name = NAME \
+ }
+
+#define DRIVER_DEFERRED() __driver_var.deferred
+
#define DRIVERS_INIT() \
for (struct driver *__d = (struct driver *)__drivers_init_start; \
(uintptr_t)__d < (uintptr_t)__drivers_init_end; ++__d) \
{ \
+ if (driver_blacklist_check((__d)->name)) { \
+ continue; \
+ } \
__d->init(); \
}
+
+#define DRIVERS_SCHED() \
+ spawn(&g_proc0, __driver_init_td, NULL, 0, NULL)
+
+/* Driver blacklist framework */
+int driver_blacklist(const char *name);
+int driver_blacklist_check(const char *name);
+void driver_blacklist_init(void);
+
+void __driver_init_td(void);
+
#endif /* _KERNEL */
#endif /* !_SYS_DRIVER_H_ */
diff --git a/sys/include/sys/elf.h b/sys/include/sys/elf.h
index af5f6d6..76c6d43 100644
--- a/sys/include/sys/elf.h
+++ b/sys/include/sys/elf.h
@@ -496,4 +496,70 @@ typedef struct {
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
+/* Special section indices. */
+
+#define SHN_UNDEF 0 /* Undefined section */
+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
+#define SHN_LOPROC 0xff00 /* Start of processor-specific */
+#define SHN_BEFORE 0xff00 /* Order section before all others
+ (Solaris). */
+#define SHN_AFTER 0xff01 /* Order section after all others
+ (Solaris). */
+#define SHN_HIPROC 0xff1f /* End of processor-specific */
+#define SHN_LOOS 0xff20 /* Start of OS-specific */
+#define SHN_HIOS 0xff3f /* End of OS-specific */
+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
+#define SHN_COMMON 0xfff2 /* Associated symbol is common */
+#define SHN_XINDEX 0xffff /* Index is in extra table. */
+#define SHN_HIRESERVE 0xffff /* End of reserved indices */
+
+/* Legal values for sh_type (section type). */
+
+#define SHT_NULL 0 /* Section header table entry unused */
+#define SHT_PROGBITS 1 /* Program data */
+#define SHT_SYMTAB 2 /* Symbol table */
+#define SHT_STRTAB 3 /* String table */
+#define SHT_RELA 4 /* Relocation entries with addends */
+#define SHT_HASH 5 /* Symbol hash table */
+#define SHT_DYNAMIC 6 /* Dynamic linking information */
+#define SHT_NOTE 7 /* Notes */
+#define SHT_NOBITS 8 /* Program space with no data (bss) */
+#define SHT_REL 9 /* Relocation entries, no addends */
+#define SHT_SHLIB 10 /* Reserved */
+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
+#define SHT_INIT_ARRAY 14 /* Array of constructors */
+#define SHT_FINI_ARRAY 15 /* Array of destructors */
+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
+#define SHT_GROUP 17 /* Section group */
+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
+#define SHT_NUM 19 /* Number of defined types. */
+#define SHT_LOOS 0x60000000 /* Start OS-specific. */
+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
+#define SHT_SUNW_move 0x6ffffffa
+#define SHT_SUNW_COMDAT 0x6ffffffb
+#define SHT_SUNW_syminfo 0x6ffffffc
+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
+#define SHT_HIOS 0x6fffffff /* End OS-specific type */
+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
+#define SHT_LOUSER 0x80000000 /* Start of application-specific */
+#define SHT_HIUSER 0x8fffffff /* End of application-specific */
+
+/* Legal values for sh_flags (section flags). */
+
+#define SHF_WRITE (1 << 0) /* Writable */
+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
+#define SHF_EXECINSTR (1 << 2) /* Executable */
+#define SHF_MERGE (1 << 4) /* Might be merged */
+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
+ required */
+#define SHF_GROUP (1 << 9) /* Section is member of a group. */
+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
+#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */
+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
#endif /* _SYS_ELF_H_ */
diff --git a/sys/include/sys/endian.h b/sys/include/sys/endian.h
new file mode 100644
index 0000000..5cbc94a
--- /dev/null
+++ b/sys/include/sys/endian.h
@@ -0,0 +1,54 @@
+/*
+ * 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_ENDIAN_H_
+#define _SYS_ENDIAN_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#define swap16(x) __swap16((x))
+#define swap32(x) __swap32((x))
+
+__always_inline static inline uint16_t
+__swap16(uint16_t x)
+{
+ return ((x << 8) & 0xFF00) | ((x >> 8) & 0x00FF);
+}
+
+__always_inline static inline uint32_t
+__swap32(uint32_t x)
+{
+ return ((x << 24) & 0xFF000000) |
+ ((x << 8) & 0x00FF0000) |
+ ((x >> 8) & 0x0000FF00) |
+ ((x >> 24) & 0x000000FF);
+}
+
+#endif /* !_SYS_ENDIAN_H_ */
diff --git a/sys/include/sys/exec.h b/sys/include/sys/exec.h
index 7e720fc..43df59f 100644
--- a/sys/include/sys/exec.h
+++ b/sys/include/sys/exec.h
@@ -32,8 +32,6 @@
#include <sys/types.h>
-#if defined(_KERNEL)
-
/* Danger: Do not change these !! */
#define AT_NULL 0
#define AT_ENTRY 1
@@ -45,7 +43,9 @@
#define AT_RANDOM 7
#define AT_EXECFN 8
#define AT_PAGESIZE 9
+#define _AT_MAX 16
+#if defined(_KERNEL)
#define MAX_PHDRS 32
#define STACK_PUSH(PTR, VAL) *(--(PTR)) = VAL
#define AUXVAL(PTR, TAG, VAL) \
diff --git a/sys/include/sys/fbdev.h b/sys/include/sys/fbdev.h
new file mode 100644
index 0000000..e206889
--- /dev/null
+++ b/sys/include/sys/fbdev.h
@@ -0,0 +1,40 @@
+/*
+ * 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_FBDEV_H_
+#define _SYS_FBDEV_H_
+
+struct fbattr {
+ uint32_t width;
+ uint32_t height;
+ uint32_t pitch;
+ uint32_t bpp;
+};
+
+#endif /* !_SYS_FBDEV_H_ */
diff --git a/sys/include/sys/fcntl.h b/sys/include/sys/fcntl.h
index 122a378..83d38af 100644
--- a/sys/include/sys/fcntl.h
+++ b/sys/include/sys/fcntl.h
@@ -33,6 +33,7 @@
#define O_RDONLY 0x0000
#define O_WRONLY 0x0001
#define O_RDWR 0x0002
+#define O_CREAT 0x0004
/* Makes seal checking easier */
#if defined(_KERNEL)
diff --git a/sys/include/sys/filedesc.h b/sys/include/sys/filedesc.h
index a544811..adbcfa8 100644
--- a/sys/include/sys/filedesc.h
+++ b/sys/include/sys/filedesc.h
@@ -31,8 +31,15 @@
#define _SYS_FILEDESC_H_
#include <sys/types.h>
+#if defined(_KERNEL)
#include <sys/vnode.h>
+#include <sys/syscall.h>
#include <sys/spinlock.h>
+#include <sys/syscall.h>
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
struct filedesc {
int fdno;
@@ -48,10 +55,14 @@ int fd_close(unsigned int fd);
int fd_read(unsigned int fd, void *buf, size_t count);
int fd_write(unsigned int fd, void *buf, size_t count);
-int fd_alloc(struct filedesc **fd_out);
+int fd_alloc(struct proc *td, struct filedesc **fd_out);
int fd_open(const char *pathname, int flags);
+off_t fd_seek(int fildes, off_t offset, int whence);
+
+int fd_dup(struct proc *td, int fd);
+struct filedesc *fd_get(struct proc *td, unsigned int fdno);
-int fd_dup(int fd);
-struct filedesc *fd_get(unsigned int fdno);
+scret_t sys_lseek(struct syscall_args *scargs);
+#endif /* _KERNEL */
#endif /* !_SYS_FILEDESC_H_ */
diff --git a/sys/include/sys/krq.h b/sys/include/sys/krq.h
new file mode 100644
index 0000000..9cb6ec6
--- /dev/null
+++ b/sys/include/sys/krq.h
@@ -0,0 +1,40 @@
+/*
+ * 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_KRQ_H_
+#define _SYS_KRQ_H_
+
+#include <sys/syscall.h>
+
+#if defined(_KERNEL)
+scret_t sys_inject(struct syscall_args *scargs);
+#else
+int inject(const char *path);
+#endif /* _KERNEL */
+#endif /* !_SYS_KRQ_H_ */
diff --git a/sys/include/sys/limits.h b/sys/include/sys/limits.h
index 6185719..c0ce5af 100644
--- a/sys/include/sys/limits.h
+++ b/sys/include/sys/limits.h
@@ -31,7 +31,12 @@
#define _SYS_LIMITS_H_
#define PATH_MAX 1024
+#define NAME_MAX 256
#define SSIZE_MAX 32767
+#define ARG_MAX 4096
#define CHAR_BIT 8
-
+#define CPU_MAX 256
+#define VSR_MAX_DOMAIN 16
+#define VSR_MAX_CAPSULE 16
+#define IOVEC_MAX 512
#endif /* !_SYS_LIMITS_H_ */
diff --git a/sys/include/sys/mman.h b/sys/include/sys/mman.h
index 4ead9ba..de360e4 100644
--- a/sys/include/sys/mman.h
+++ b/sys/include/sys/mman.h
@@ -35,6 +35,8 @@
#if defined(_KERNEL)
#include <sys/tree.h>
#include <vm/vm_obj.h>
+#else
+#include <stddef.h>
#endif /* _KERNEL */
/*
@@ -49,10 +51,10 @@
#endif /* !_KERNEL */
/* mmap() flags */
+#define MAP_ANON 0x0000
#define MAP_SHARED 0x0001
#define MAP_PRIVATE 0x0002
#define MAP_FIXED 0x0004
-#define MAP_ANON 0x0008
#if defined(_KERNEL)
/*
@@ -80,19 +82,19 @@ struct mmap_lgdr {
size_t nbytes;
};
-/* Kernel munmap() routine */
-int munmap_at(void *addr, size_t len);
-
-/* Kernel mmap() routine */
-void *mmap_at(void *addr, size_t len, int prot, int flags,
- int fildes, off_t off);
-
int mmap_entrycmp(const struct mmap_entry *a, const struct mmap_entry *b);
RBT_PROTOTYPE(lgdr_entries, mmap_entry, hd, mmap_entrycmp)
-#endif /* _KERNEL */
/* Syscall layer */
-scret_t mmap(struct syscall_args *scargs);
-scret_t munmap(struct syscall_args *scargs);
+scret_t sys_mmap(struct syscall_args *scargs);
+scret_t sys_munmap(struct syscall_args *scargs);
+#endif /* _KERNEL */
+
+/* Kernel munmap() routine */
+int munmap(void *addr, size_t len);
+
+/* Kernel mmap() routine */
+void *mmap(void *addr, size_t len, int prot, int flags,
+ int fildes, off_t off);
#endif /* !_SYS_MMAN_H_ */
diff --git a/sys/include/sys/mmio.h b/sys/include/sys/mmio.h
index 9f6e4e2..0fa9e36 100644
--- a/sys/include/sys/mmio.h
+++ b/sys/include/sys/mmio.h
@@ -42,7 +42,6 @@
#if defined(_KERNEL)
-
/*
* mmio_write<n> - Writes to MMIO address with specific size
*
diff --git a/sys/include/sys/mount.h b/sys/include/sys/mount.h
index 1fcdbfa..636c7bf 100644
--- a/sys/include/sys/mount.h
+++ b/sys/include/sys/mount.h
@@ -47,6 +47,7 @@
#define MOUNT_RAMFS "initramfs"
#define MOUNT_DEVFS "devfs"
#define MOUNT_CTLFS "ctlfs"
+#define MOUNT_TMPFS "tmpfs"
struct vfsops;
struct mount;
@@ -59,6 +60,7 @@ extern mountlist_t g_mountlist;
extern const struct vfsops g_initramfs_vfsops;
extern const struct vfsops g_devfs_vfsops;
extern const struct vfsops g_ctlfs_vfsops;
+extern const struct vfsops g_tmpfs_vfsops;
struct mount {
char *name;
diff --git a/sys/include/sys/mutex.h b/sys/include/sys/mutex.h
new file mode 100644
index 0000000..8a4d50a
--- /dev/null
+++ b/sys/include/sys/mutex.h
@@ -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.
+ */
+
+#ifndef _SYS_MUTEX_H_
+#define _SYS_MUTEX_H_
+
+#include <sys/types.h>
+#include <vm/dynalloc.h>
+
+#define MUTEX_NAME_LEN 32
+
+#if defined(_KERNEL)
+
+struct mutex {
+ char name[MUTEX_NAME_LEN];
+ volatile uint8_t lock;
+};
+
+struct mutex *mutex_new(const char *name);
+void mutex_free(struct mutex *mtx);
+
+int mutex_acquire(struct mutex *mtx, int flags);
+void mutex_release(struct mutex *mtx);
+
+#endif /* _KERNEL */
+#endif /* !_SYS_MUTEX_H_ */
diff --git a/sys/include/sys/namei.h b/sys/include/sys/namei.h
index f81f905..ccd7f35 100644
--- a/sys/include/sys/namei.h
+++ b/sys/include/sys/namei.h
@@ -32,6 +32,9 @@
#include <sys/types.h>
#include <sys/vnode.h>
+#include <sys/param.h>
+
+#define NAMEI_WANTPARENT BIT(0) /* Request parent only */
struct nameidata {
const char *path; /* Pathname */
diff --git a/sys/include/sys/param.h b/sys/include/sys/param.h
index c0a5686..2bbbabd 100644
--- a/sys/include/sys/param.h
+++ b/sys/include/sys/param.h
@@ -30,11 +30,20 @@
#ifndef _SYS_PARAM_H_
#define _SYS_PARAM_H_
+#if defined(_KERNEL)
+#include <machine/param.h>
+#endif
+
/* Assumed cache line size */
#ifndef COHERENCY_UNIT
#define COHERENCY_UNIT 64
#endif
+/* Assumed machine word size */
+#ifndef M_WORD_SIZE
+#define M_WORD_SIZE 4
+#endif
+
/* Bit related macros */
#define ISSET(v, f) ((v) & (f))
#define BIT(n) (1ULL << (n))
@@ -47,6 +56,7 @@
/* Align up/down a value */
#define ALIGN_DOWN(value, align) ((value) & ~((align)-1))
#define ALIGN_UP(value, align) (((value) + (align)-1) & ~((align)-1))
+#define MALIGN(value) ALIGN_UP((value), M_WORD_SIZE)
/* Bitmap helper macros */
#define setbit(a, b) ((a)[(b) >> 3] |= BIT(b % 8))
@@ -67,8 +77,12 @@
/* Gives 1 if pointer is aligned */
#define PTR_ALIGNED(PTR, ALIGN) (!((uintptr_t)PTR & (ALIGN - 1)))
-/* Adds a value to a pointer */
+/*
+ * PTR_OFFSET: Adds an offset to the pointer
+ * PTR_NOFFSET: Subtracts a negative offset from the pointer
+ */
#define PTR_OFFSET(PTR, OFF) ((void *)((uintptr_t)PTR + OFF))
+#define PTR_NOFFSET(PTR, NOFF) ((void *)((uintptr_t)PTR - NOFF))
#define NELEM(a) (sizeof(a) / sizeof(a[0]))
diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h
index 1b59de9..809ee23 100644
--- a/sys/include/sys/proc.h
+++ b/sys/include/sys/proc.h
@@ -38,6 +38,9 @@
#include <sys/cdefs.h>
#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>
@@ -53,22 +56,53 @@
#define PROC_MAX_FILEDES 256
#define PROC_SIGMAX 64
+/*
+ * The coredump structure, contains information
+ * about crashes.
+ *
+ * @pid: PID of process that has crashed
+ * @fault_addr: Address of faulting memory access
+ * @tf: Copy of the programs trapframe
+ * @checksum: CRC32 checksum of entire coredump
+ *
+ * XXX: DO NOT REORDER (always add to the end before 'checksum')
+ */
+struct __packed coredump {
+ pid_t pid;
+ uintptr_t fault_addr;
+ struct trapframe tf;
+
+ /* XXX: Add entries above the checksum */
+ 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;
+ 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;
struct trapframe tf;
struct pcb pcb;
struct proc *parent;
- void *spawn_data;
+ affinity_t affinity;
+ void *data;
size_t priority;
int exit_status;
bool rested;
- uint32_t flags;
+ volatile uint32_t flags;
uint32_t nleaves;
uintptr_t stack_base;
struct spinlock ksigq_lock;
@@ -83,19 +117,38 @@ 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 */
+#define PROC_SLEEP BIT(6) /* Thread execution paused */
+#define PROC_PINNED BIT(7) /* Pinned to CPU */
struct proc *this_td(void);
+struct proc *td_copy(struct proc *td);
struct proc *get_child(struct proc *cur, pid_t pid);
+
+int proc_init(struct proc *td, struct proc *parent);
+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);
+
+pid_t getpid(void);
+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);
scret_t sys_spawn(struct syscall_args *scargs);
pid_t spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **newprocp);
-void md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog);
+uintptr_t 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/queue.h b/sys/include/sys/queue.h
index e5d607d..2226ccc 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_
@@ -79,7 +84,6 @@ struct { \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
-
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
diff --git a/sys/include/sys/sched.h b/sys/include/sys/sched.h
index 80f4d1c..7bba9df 100644
--- a/sys/include/sys/sched.h
+++ b/sys/include/sys/sched.h
@@ -32,11 +32,45 @@
#include <sys/proc.h>
#include <sys/cdefs.h>
+#include <sys/limits.h>
+#include <sys/time.h>
+
+/*
+ * Scheduler CPU information
+ *
+ * @nswitch: Number of context switches
+ * @idle: Number of milliseconds idle
+ */
+struct sched_cpu {
+ uint64_t nswitch;
+};
+
+/*
+ * Scheduler statistics
+ *
+ * @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];
+};
#if defined(_KERNEL)
+void sched_stat(struct sched_stat *statp);
void sched_init(void);
+
+void sched_preempt_set(bool enable);
+bool sched_preemptable(void);
+
void sched_yield(void);
+void sched_suspend(struct proc *td, const struct timeval *tv);
void sched_detach(struct proc *td);
__dead void sched_enter(void);
diff --git a/sys/include/sys/schedvar.h b/sys/include/sys/schedvar.h
index 5ed9f5f..017fcb7 100644
--- a/sys/include/sys/schedvar.h
+++ b/sys/include/sys/schedvar.h
@@ -60,5 +60,11 @@ struct sched_queue {
size_t nthread;
};
+struct proc *sched_dequeue_td(void);
+void mi_sched_switch(struct proc *from);
+
+void md_sched_switch(struct trapframe *tf);
+void sched_oneshot(bool now);
+
#endif /* _KERNEL */
#endif /* !_SYS_SCHEDVAR_H_ */
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/socket.h b/sys/include/sys/socket.h
new file mode 100644
index 0000000..1a33108
--- /dev/null
+++ b/sys/include/sys/socket.h
@@ -0,0 +1,202 @@
+/*
+ * 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_SOCKET_H_
+#define _SYS_SOCKET_H_
+
+#include <sys/socketvar.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#if defined(_KERNEL)
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/syscall.h>
+#include <sys/mutex.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif /* _KERNEL */
+
+#ifndef _SA_FAMILY_T_DEFINED_
+#define _SA_FAMILY_T_DEFINED_
+typedef uint32_t sa_family_t;
+#endif /* _SA_FAMILY_T_DEFINED_ */
+
+#ifndef _SOCKLEN_T_DEFINED_
+#define _SOCKLEN_T_DEFINED_
+typedef uint32_t socklen_t;
+#endif /* !_SOCKLEN_T_DEFINED_ */
+
+/*
+ * Socket level number
+ */
+#define SOL_SOCKET 0xFFFF
+
+/*
+ * Address family defines
+ */
+#define AF_UNSPEC 0
+#define AF_UNIX 1
+#define AF_LOCAL AF_UNIX
+
+/* Socket types */
+#define SOCK_STREAM 1
+
+/* Socket option names */
+#define SO_RCVTIMEO 0 /* Max time recv(2) waits */
+#define _SO_MAX 1 /* Max socket options */
+
+struct sockaddr_un {
+ sa_family_t sun_family;
+ char sun_path[108];
+};
+
+struct sockaddr {
+ sa_family_t sa_family;
+ char sa_data[14];
+};
+
+/*
+ * POSIX message header for recvmsg()
+ * and sendmsg() calls.
+ */
+struct msghdr {
+ void *msg_name; /* Optional address */
+ socklen_t msg_namelen; /* Size of address */
+ struct iovec *msg_iov; /* Scatter/gather array */
+ int msg_iovlen; /* Members in msg_iov */
+ void *msg_control; /* Ancillary data, see below */
+ socklen_t msg_controllen; /* Ancillary data buffer len */
+ int msg_flags; /* Message flags */
+};
+
+/*
+ * POSIX control message header for
+ * ancillary data objects.
+ */
+struct cmsghdr {
+ socklen_t cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+};
+
+#define CMSG_SPACE(len) (MALIGN(sizeof(struct cmsghdr)) + MALIGN(len))
+
+/* Return pointer to cmsg data */
+#define CMSG_DATA(cmsg) PTR_OFFSET(cmsg, sizeof(struct cmsghdr))
+
+/* Return length of control message */
+#define CMSG_LEN(len) (MALIGN(sizeof(struct cmsghdr)) + MALIGN(len))
+
+/* Return pointer to next cmsghdr */
+#define CMSG_NXTHDR(mhdr, cmsg) \
+ PTR_OFFSET(cmsg, MALIGN((cmsg)>cmsg_len)) + \
+ MALIGN(sizeof(struct cmsghdr)) > \
+ PTR_OFFSET((mhdr)->msg_control, (mhdr)->msg_controllen) ? \
+ (struct cmsghdr *)NULL : \
+ (struct cmsghdr *)PTR_OFFSET(cmsg, MALIGN((cmsg)->cmsg_len))
+
+/* Return pointer to first header */
+#define CMSG_FIRSTHDR(mhdr) \
+ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \
+ (struct cmsghdr *)(mhdr)->msg_control : \
+ (struct cmsghdr *)NULL);
+
+/* Socket level control messages */
+#define SCM_RIGHTS 0x01
+
+#if defined(_KERNEL)
+
+struct cmsg {
+ union {
+ struct cmsghdr hdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ };
+
+ size_t control_len;
+ TAILQ_ENTRY(cmsg) link;
+};
+
+/*
+ * List of cmsg headers and data, queued up
+ * during sendmsg()
+ */
+struct cmsg_list {
+ TAILQ_HEAD(, cmsg) list;
+ uint8_t is_init : 1;
+};
+
+/*
+ * Socket option that may be applied to
+ * sockets on the system.
+ */
+struct sockopt {
+ socklen_t len;
+ char data[];
+};
+
+struct ksocket {
+ int sockfd;
+ union {
+ struct sockaddr sockaddr;
+ struct sockaddr_un un;
+ };
+ struct sockopt *opt[_SO_MAX];
+ struct proc *owner;
+ struct cmsg_list cmsg_list;
+ struct sockbuf buf;
+ struct mutex *mtx;
+};
+
+scret_t sys_socket(struct syscall_args *scargs);
+scret_t sys_bind(struct syscall_args *scargs);
+scret_t sys_connect(struct syscall_args *scargs);
+
+scret_t sys_recv(struct syscall_args *scargs);
+scret_t sys_send(struct syscall_args *scargs);
+
+scret_t sys_recvmsg(struct syscall_args *scargs);
+scret_t sys_sendmsg(struct syscall_args *scargs);
+scret_t sys_setsockopt(struct syscall_args *scargs);
+#endif /* _KERNEL */
+
+int socket(int domain, int type, int protocol);
+int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
+
+int setsockopt(int sockfd, int level, int name, const void *v, socklen_t len);
+int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
+
+ssize_t send(int sockfd, const void *buf, size_t size, int flags);
+ssize_t recv(int sockfd, void *buf, size_t len, int flags);
+
+ssize_t sendmsg(int socket, const struct msghdr *msg, int flags);
+ssize_t recvmsg(int socket, struct msghdr *msg, int flags);
+
+#endif /* !_SYS_SOCKET_H_ */
diff --git a/sys/include/sys/socketvar.h b/sys/include/sys/socketvar.h
new file mode 100644
index 0000000..e090a70
--- /dev/null
+++ b/sys/include/sys/socketvar.h
@@ -0,0 +1,53 @@
+/*
+ * 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_SOCKETVAR_H_
+#define _SYS_SOCKETVAR_H_
+
+#include <sys/types.h>
+#if defined(_KERNEL)
+#include <net/netbuf.h>
+
+/*
+ * Socket buffer
+ *
+ * @buf: Actual data buffer
+ * @head: Buffer head
+ * @tail: Buffer tail
+ * @watermark: Max length
+ */
+struct sockbuf {
+ struct netbuf buf;
+ size_t head;
+ size_t tail;
+ size_t watermark;
+};
+
+#endif /* _KERNEL */
+#endif /* !_SYS_SOCKETVAR_H_ */
diff --git a/sys/include/sys/spawn.h b/sys/include/sys/spawn.h
index 3828d5c..28dbe5b 100644
--- a/sys/include/sys/spawn.h
+++ b/sys/include/sys/spawn.h
@@ -31,8 +31,9 @@
#define _SYS_SPAWN_H_
#include <sys/types.h>
+#include <sys/param.h>
#if !defined(_KERNEL)
-pid_t spawn(const char *pathname, int flags);
+pid_t spawn(const char *pathname, char **argv, char **envp, int flags);
#endif /* _KERNEL */
#endif /* !_SYS_SPAWN_H_ */
diff --git a/sys/include/sys/spinlock.h b/sys/include/sys/spinlock.h
index 140addc..b416152 100644
--- a/sys/include/sys/spinlock.h
+++ b/sys/include/sys/spinlock.h
@@ -44,9 +44,6 @@ void spinlock_release(struct spinlock *lock);
int spinlock_try_acquire(struct spinlock *lock);
int spinlock_usleep(struct spinlock *lock, size_t usec_max);
-/* System-wide locking (be careful!!) */
-int syslock(void);
-void sysrel(void);
#endif
#endif /* !_SYS_SPINLOCK_H_ */
diff --git a/sys/include/sys/stat.h b/sys/include/sys/stat.h
index 6303630..5409f2c 100644
--- a/sys/include/sys/stat.h
+++ b/sys/include/sys/stat.h
@@ -32,6 +32,8 @@
#include <sys/types.h>
+#define S_IFBLK 0060000
+
struct stat {
dev_t st_dev;
ino_t st_ino;
@@ -46,4 +48,6 @@ struct stat {
time_t st_ctime;
};
+int stat(const char *path, struct stat *buf);
+
#endif /* _SYS_STAT_H_ */
diff --git a/sys/include/sys/syscall.h b/sys/include/sys/syscall.h
index 2223a96..604f937 100644
--- a/sys/include/sys/syscall.h
+++ b/sys/include/sys/syscall.h
@@ -48,6 +48,26 @@
#define SYS_write 7
#define SYS_spawn 8
#define SYS_reboot 9
+#define SYS_mmap 10
+#define SYS_munmap 11
+#define SYS_access 12
+#define SYS_lseek 13
+#define SYS_sleep 14
+#define SYS_inject 15
+#define SYS_getpid 16
+#define SYS_getppid 17
+#define SYS_setuid 18
+#define SYS_getuid 19
+#define SYS_waitpid 20
+#define SYS_socket 21
+#define SYS_bind 22
+#define SYS_recv 23
+#define SYS_send 24
+#define SYS_sendmsg 25
+#define SYS_recvmsg 26
+#define SYS_connect 27
+#define SYS_setsockopt 28
+#define SYS_disk 29
#if defined(_KERNEL)
/* Syscall return value and arg type */
diff --git a/sys/include/sys/sysctl.h b/sys/include/sys/sysctl.h
index 078135b..ce7510d 100644
--- a/sys/include/sys/sysctl.h
+++ b/sys/include/sys/sysctl.h
@@ -30,16 +30,35 @@
#ifndef _SYS_SYSCTL_H_
#define _SYS_SYSCTL_H_
-#include <sys/types.h>
#if defined(_KERNEL)
+#include <sys/types.h>
#include <sys/syscall.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
#endif
#include <sys/param.h>
+/*
+ * List of 'kern.* ' identifiers
+ */
#define KERN_OSTYPE 0
#define KERN_OSRELEASE 1
#define KERN_VERSION 2
#define KERN_VCACHE_TYPE 3
+#define KERN_HOSTNAME 4
+
+/*
+ * List of 'hw.* ' identifiers
+ */
+#define HW_PAGESIZE 5
+#define HW_NCPU 6
+#define HW_MACHINE 7
+
+/*
+ * List of 'proc.*' identifiers
+ */
+#define PROC_COUNT 8
/*
* Option types (i.e., int, string, etc) for
@@ -61,6 +80,7 @@ struct sysctl_entry {
};
scret_t sys_sysctl(struct syscall_args *scargs);
+int sysctl_clearstr(int name);
#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..2f69175 100644
--- a/sys/include/sys/systm.h
+++ b/sys/include/sys/systm.h
@@ -39,6 +39,7 @@
int copyin(const void *uaddr, void *kaddr, size_t len);
int copyout(const void *kaddr, void *uaddr, size_t len);
int copyinstr(const void *uaddr, char *kaddr, size_t len);
+int cpu_report_count(uint32_t count);
__always_inline static inline void
__sigraise(int signo)
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/time.h b/sys/include/sys/time.h
new file mode 100644
index 0000000..ce66885
--- /dev/null
+++ b/sys/include/sys/time.h
@@ -0,0 +1,60 @@
+/*
+ * 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_TIME_H_
+#define _SYS_TIME_H_
+
+#include <sys/types.h>
+#if defined(_KERNEL)
+#include <sys/syscall.h>
+#endif /* _KERNEL */
+
+struct timeval {
+ time_t tv_sec;
+ time_t tv_usec;
+};
+
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+
+struct date {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t sec;
+ uint8_t min;
+ uint8_t hour;
+};
+
+#if defined(_KERNEL)
+scret_t sys_sleep(struct syscall_args *scargs);
+#endif
+#endif /* !_SYS_TIME_H_ */
diff --git a/sys/include/sys/tree.h b/sys/include/sys/tree.h
index 1054ade..6954185 100644
--- a/sys/include/sys/tree.h
+++ b/sys/include/sys/tree.h
@@ -746,7 +746,6 @@ name##_RB_MINMAX(struct name *head, int val) \
((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \
(x) = (y))
-
/*
* Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
*
diff --git a/sys/include/sys/types.h b/sys/include/sys/types.h
index 5cb2fc7..223f455 100644
--- a/sys/include/sys/types.h
+++ b/sys/include/sys/types.h
@@ -82,11 +82,11 @@ typedef __uint64_t uint64_t;
#endif
#if __SIZEOF_SIZE_T__ == 8
-typedef uint64_t __size_t;
-typedef int64_t __ssize_t; /* Byte count or error */
+typedef __uint64_t __size_t;
+typedef __int64_t __ssize_t; /* Byte count or error */
#elif __SIZEOF_SIZE_T__ == 4
-typedef uint32_t __size_t;
-typedef int32_t __ssize_t; /* Byte count or error */
+typedef __uint32_t __size_t;
+typedef __int32_t __ssize_t; /* Byte count or error */
#else
#error "Unsupported size_t size"
#endif
@@ -100,14 +100,15 @@ typedef __size_t uintptr_t;
typedef __size_t off_t;
typedef int pid_t;
typedef int dev_t;
-typedef uint32_t mode_t;
-typedef uint32_t ino_t;
-typedef uint32_t nlink_t;
-typedef uint32_t uid_t;
-typedef uint32_t gid_t;
-typedef uint32_t blksize_t;
-typedef uint32_t blkcnt_t;
-typedef uint64_t time_t;
+typedef __uint32_t uid_t;
+typedef __uint32_t mode_t;
+typedef __uint32_t ino_t;
+typedef __uint32_t nlink_t;
+typedef __uint32_t uid_t;
+typedef __uint32_t gid_t;
+typedef __uint32_t blksize_t;
+typedef __uint32_t blkcnt_t;
+typedef __uint64_t time_t;
#if defined(_HAVE_PTRDIFF_T)
typedef __ptrdiff_t ptrdiff_t;
#endif /* _HAVE_PTRDIFF_T */
diff --git a/sys/include/sys/ucred.h b/sys/include/sys/ucred.h
new file mode 100644
index 0000000..f8cbbe0
--- /dev/null
+++ b/sys/include/sys/ucred.h
@@ -0,0 +1,57 @@
+/*
+ * 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_UCRED_H_
+#define _SYS_UCRED_H_
+
+#include <sys/types.h>
+#if defined(_KERNEL)
+#include <sys/spinlock.h>
+#include <sys/syscall.h>
+#endif
+
+/*
+ * Kernel structure for user credentials
+ */
+struct ucred {
+ uid_t euid;
+ uid_t ruid;
+#if defined(_KERNEL)
+ struct spinlock lock;
+#endif /* _KERNEL */
+};
+
+int setuid(uid_t new);
+uid_t getuid(void);
+
+#if defined(_KERNEL)
+scret_t sys_setuid(struct syscall_args *scargs);
+scret_t sys_getuid(struct syscall_args *scargs);
+#endif
+#endif /* !_SYS_UCRED_H_ */
diff --git a/sys/include/sys/uio.h b/sys/include/sys/uio.h
new file mode 100644
index 0000000..4318a53
--- /dev/null
+++ b/sys/include/sys/uio.h
@@ -0,0 +1,58 @@
+/*
+ * 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_UIO_H_
+#define _SYS_UIO_H_
+
+#if defined(_KERNEL)
+#include <sys/types.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif /* _KERNEL */
+
+/*
+ * POSIX I/O vector
+ */
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+
+ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
+ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
+
+#if defined(_KERNEL)
+
+int uio_copyin(const struct iovec *u_iov, struct iovec *k_iov, int iovcnt);
+int uio_copyout(const struct iovec *k_iov, struct iovec *u_iov, int iovcnt);
+void uio_copyin_clean(struct iovec *copy, int iovcnt);
+
+#endif /* _KERNEL */
+#endif /* !_SYS_UIO_H_ */
diff --git a/sys/include/sys/vfs.h b/sys/include/sys/vfs.h
index 1ff722a..fcb7391 100644
--- a/sys/include/sys/vfs.h
+++ b/sys/include/sys/vfs.h
@@ -40,6 +40,7 @@ scret_t sys_close(struct syscall_args *args);
scret_t sys_read(struct syscall_args *scargs);
scret_t sys_write(struct syscall_args *sargs);
scret_t sys_stat(struct syscall_args *scargs);
+scret_t sys_access(struct syscall_args *scargs);
#endif /* _KERNEL */
#endif /* !_SYS_VFS_H_ */
diff --git a/sys/include/sys/vmstat.h b/sys/include/sys/vmstat.h
new file mode 100644
index 0000000..b7faeb2
--- /dev/null
+++ b/sys/include/sys/vmstat.h
@@ -0,0 +1,48 @@
+/*
+ * 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_VMSTAT_H_
+#define _SYS_VMSTAT_H_
+
+#include <sys/types.h>
+
+/*
+ * Virtual memory statistics
+ *
+ * @mem_avail: Available memory in MiB
+ * @mem_used: Allocated memory in MiB
+ * @mem_total: Total system memory in MiB
+ */
+struct vm_stat {
+ uint32_t mem_avail;
+ uint32_t mem_used;
+ size_t mem_total;
+};
+
+#endif /* !_VM_STAT_H_ */
diff --git a/sys/include/sys/vnode.h b/sys/include/sys/vnode.h
index 33092f9..ff6f995 100644
--- a/sys/include/sys/vnode.h
+++ b/sys/include/sys/vnode.h
@@ -32,11 +32,11 @@
#include <sys/types.h>
#include <sys/queue.h>
+#include <sys/vnode.h>
#include <sys/atomic.h>
#include <sys/sio.h>
-#include <vm/vm_obj.h>
-
#if defined(_KERNEL)
+#include <vm/vm_obj.h>
struct vops;
@@ -47,6 +47,8 @@ struct vnode {
const struct vops *vops;
struct vm_object vobj;
uint32_t refcount;
+ dev_t major;
+ dev_t dev;
TAILQ_ENTRY(vnode) vcache_link;
};
@@ -74,6 +76,7 @@ struct vcache {
#define VDIR 0x02 /* Directory */
#define VCHR 0x03 /* Character device */
#define VBLK 0x04 /* Block device */
+#define VSOCK 0x05 /* Socket */
#define VNOVAL -1
@@ -83,6 +86,23 @@ struct vop_lookup_args {
struct vnode **vpp; /* Result vnode */
};
+struct vop_create_args {
+ const char *path; /* Full path */
+ const char *ppath; /* Parent path */
+ struct vnode *dirvp; /* Directory vnode */
+ struct vnode **vpp; /* Result vnode */
+};
+
+struct vop_getattr_args {
+ struct vnode *vp; /* Target vnode */
+ struct vattr *res; /* Result vattr */
+};
+
+struct vop_readdir_args {
+ struct vnode *vp; /* Target vnode */
+ struct sio_txn *sio; /* SIO data to read into */
+};
+
/*
* A field in this structure is unavailable
* if it has a value of VNOVAL.
@@ -92,34 +112,33 @@ struct vattr {
size_t size;
};
-struct vop_getattr_args {
- struct vnode *vp;
- struct vattr *res;
-};
-
struct vops {
int(*lookup)(struct vop_lookup_args *args);
int(*getattr)(struct vop_getattr_args *args);
+ int(*readdir)(struct vop_readdir_args *args);
int(*read)(struct vnode *vp, struct sio_txn *sio);
int(*write)(struct vnode *vp, struct sio_txn *sio);
int(*reclaim)(struct vnode *vp);
+ int(*create)(struct vop_create_args *args);
};
extern struct vnode *g_root_vnode;
+/* Vnode cache operations */
int vfs_vcache_type(void);
int vfs_vcache_migrate(int newtype);
-
int vfs_vcache_enter(struct vnode *vp);
struct vnode *vfs_recycle_vnode(void);
+/* Vnode operations */
int vfs_alloc_vnode(struct vnode **res, int type);
int vfs_release_vnode(struct vnode *vp);
-int vfs_vop_lookup(struct vnode *vp, struct vop_lookup_args *args);
+/* Vnode operation wrappers */
+int vfs_vop_lookup(struct vop_lookup_args *args);
+int vfs_vop_getattr(struct vop_getattr_args *args);
int vfs_vop_read(struct vnode *vp, struct sio_txn *sio);
int vfs_vop_write(struct vnode *vp, struct sio_txn *sio);
-int vfs_vop_getattr(struct vnode *vp, struct vop_getattr_args *args);
#endif /* _KERNEL */
#endif /* !_SYS_VNODE_H_ */
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/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/include/sys/workqueue.h b/sys/include/sys/workqueue.h
new file mode 100644
index 0000000..9925f79
--- /dev/null
+++ b/sys/include/sys/workqueue.h
@@ -0,0 +1,101 @@
+/*
+ * 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_WORKQUEUE_H_
+#define _SYS_WORKQUEUE_H_
+
+#if defined(_KERNEL)
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+
+struct workqueue;
+struct work;
+
+/*
+ * A work function can either refer to a work thread
+ * entry (or actual work to be done
+ */
+typedef void(*workfunc_t)(struct workqueue *wqp, struct work *wp);
+
+/*
+ * Represents work that may be added to a
+ * workqueue.
+ *
+ * @name: Name of this work/task [i]
+ * @data: Optional data to be passed with work [p]
+ * @func: Function with work to be done [p]
+ * @cookie: Used for validating the work structure [i]
+ *
+ * Field attributes:
+ * - [i]: Used internally
+ * - [p]: Used as parameter
+ */
+struct work {
+ char *name;
+ void *data;
+ workfunc_t func;
+ TAILQ_ENTRY(work) link;
+};
+
+/*
+ * A workqueue contains tasks that are
+ * queued up to be completed in their own
+ * thread context.
+ *
+ * @name: Name of workqueue.
+ * @work: Start of the workqueue
+ * @ipl: IPL that work here must run with
+ * @max_work: Max number of jobs that can be queued
+ * @nwork: Number of tasks to be done
+ * @cookie: For validating workqueues
+ * @worktd: Thread associated with the workqueue
+ * @lock: Protects the workqueue
+ */
+struct workqueue {
+ char *name;
+ TAILQ_HEAD(, work) work;
+ uint8_t ipl;
+ size_t max_work;
+ ssize_t nwork;
+ uint16_t cookie;
+ struct proc *worktd;
+ struct mutex *lock;
+};
+
+struct workqueue *workqueue_new(const char *name, size_t max_work, int ipl);
+
+int workqueue_enq(struct workqueue *wqp, const char *name, struct work *wp);
+int workqueue_destroy(struct workqueue *wqp);
+int work_destroy(struct work *wp);
+
+#endif /* !_KERNEL */
+#endif /* !_SYS_WORKQUEUE_H_ */
diff --git a/sys/include/vm/physmem.h b/sys/include/vm/physmem.h
index ae11530..3f1da61 100644
--- a/sys/include/vm/physmem.h
+++ b/sys/include/vm/physmem.h
@@ -32,6 +32,10 @@
#include <sys/types.h>
+uint32_t vm_mem_used(void);
+uint32_t vm_mem_free(void);
+size_t vm_mem_total(void);
+
void vm_physmem_init(void);
uintptr_t vm_alloc_frame(size_t count);
void vm_free_frame(uintptr_t base, size_t count);
diff --git a/sys/include/vm/pmap.h b/sys/include/vm/pmap.h
index 9eed184..e0549d4 100644
--- a/sys/include/vm/pmap.h
+++ b/sys/include/vm/pmap.h
@@ -76,9 +76,25 @@ int pmap_map(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot);
int pmap_unmap(struct vas vas, vaddr_t va);
/*
+ * Returns true if the page is clean (modified), otherwise
+ * returns false.
+ */
+bool pmap_is_clean(struct vas vas, vaddr_t va);
+
+/*
+ * Marks a page as clean (unmodified)
+ */
+void pmap_mark_clean(struct vas vas, vaddr_t va);
+
+/*
* Mark a virtual address with a specific
* caching type.
*/
int pmap_set_cache(struct vas vas, vaddr_t va, int type);
+/*
+ * Machine dependent pmap init code.
+ */
+int pmap_init(void);
+
#endif /* !_VM_PMAP_H_ */
diff --git a/sys/include/vm/stat.h b/sys/include/vm/stat.h
new file mode 100644
index 0000000..7e9a4a9
--- /dev/null
+++ b/sys/include/vm/stat.h
@@ -0,0 +1,39 @@
+/*
+ * 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 _VM_STAT_H_
+#define _VM_STAT_H_
+
+#include <sys/types.h>
+#include <sys/vmstat.h>
+
+int vm_stat_get(struct vm_stat *vmstat);
+void vm_stat_init(void);
+
+#endif /* !_VM_STAT_H_ */
diff --git a/sys/include/vm/vm_device.h b/sys/include/vm/vm_device.h
new file mode 100644
index 0000000..da476e2
--- /dev/null
+++ b/sys/include/vm/vm_device.h
@@ -0,0 +1,43 @@
+/*
+ * 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 _VM_DEVICE_H_
+#define _VM_DEVICE_H_
+
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/device.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_obj.h>
+
+extern const struct vm_pagerops vm_vnops;
+
+struct vm_object *dv_attach(devmajor_t major, dev_t dev, vm_prot_t prot);
+
+#endif /* !_VM_DEVICE_H_ */
diff --git a/sys/include/vm/vm_pager.h b/sys/include/vm/vm_pager.h
index e0503e0..4732234 100644
--- a/sys/include/vm/vm_pager.h
+++ b/sys/include/vm/vm_pager.h
@@ -40,7 +40,6 @@ struct vm_pagerops;
extern const struct vm_pagerops vm_vnops;
extern const struct vm_pagerops vm_anonops;
-
/*
* Pager operations.
*
diff --git a/sys/kern/disk_engine.c b/sys/kern/disk_engine.c
new file mode 100644
index 0000000..1061165
--- /dev/null
+++ b/sys/kern/disk_engine.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/syscall.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/disk.h>
+#include <vm/dynalloc.h>
+
+#define pr_trace(fmt, ...) kprintf("disk: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+/*
+ * Clones a disk parameter structure passed
+ * by a user. The structure returned is safe
+ * to be accessed freely by the kernel.
+ *
+ * @u_param: Contains user-side pointer
+ * @res: Resulting safe data
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+static int
+disk_param_clone(struct disk_param *u_param, struct disk_param *res)
+{
+ void *data;
+ int error;
+
+ if (u_param == NULL) {
+ pr_error("disk_param_clone: got NULL u_param\n");
+ return -EINVAL;
+ }
+
+ error = copyin(u_param, res, sizeof(*res));
+ if (error < 0) {
+ return error;
+ }
+
+ /*
+ * If these parameters do not have a valid cookie, fuck
+ * that object, something is not right with it...
+ */
+ if (res->cookie != DISK_PARAM_COOKIE) {
+ pr_error("disk_param_clone: erroneous params (bad cookie)\n");
+ return -EACCES;
+ }
+
+ data = dynalloc(res->size);
+ if (data == NULL) {
+ pr_error("disk_param_clone: out of memory\n");
+ return -ENOMEM;
+ }
+
+ error = copyin(res->buf, data, res->size);
+ if (error < 0) {
+ pr_error("failed to copy in param data\n");
+ dynfree(data);
+ return error;
+ }
+
+ res->u_buf = res->buf;
+ res->buf = data;
+ return 0;
+}
+
+/*
+ * Deallocate a kernel managed disk parameter
+ * structure created by disk_param_clone()
+ *
+ * @param: Params to free
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+static int
+disk_param_free(struct disk_param *param)
+{
+ if (param == NULL) {
+ return -EINVAL;
+ }
+
+ if (param->cookie != DISK_PARAM_COOKIE) {
+ return -EACCES;
+ }
+
+ dynfree(param->buf);
+ return 0;
+}
+
+/*
+ * Perform an operation on a disk.
+ *
+ * @id: ID of disk to operate on
+ * @opcode: Operation to perform (see DISK_IO_*)
+ * @u_param: User side disk parameters
+ *
+ * Returns a less than zero value on error
+ */
+static ssize_t
+disk_mux_io(diskid_t id, diskop_t opcode, struct disk_param *u_param)
+{
+ struct disk_param param;
+ struct disk *dp;
+ ssize_t retval = -EIO;
+ int error;
+
+ if (u_param == NULL) {
+ return -EINVAL;
+ }
+
+ error = disk_param_clone(u_param, &param);
+ if (error < 0) {
+ return error;
+ }
+
+ /* First, attempt to acquire the disk */
+ error = disk_get_id(id, &dp);
+ if (error < 0) {
+ pr_error("disk_mux_io: no such device (id=%d)\n", id);
+ return error;
+ }
+
+ switch (opcode) {
+ case DISK_IO_READ:
+ retval = disk_read(
+ id,
+ param.blk,
+ param.buf,
+ param.size
+ );
+
+ /* Write back the data to the user program */
+ error = copyout(param.buf, param.u_buf, param.size);
+ if (error < 0) {
+ retval = error;
+ }
+ break;
+ case DISK_IO_WRITE:
+ retval = disk_write(
+ id,
+ param.blk,
+ param.buf,
+ param.size
+ );
+ break;
+ case DISK_IO_QUERY:
+ retval = disk_query(
+ id,
+ param.buf
+ );
+
+ /* Write back info to user program */
+ error = copyout(param.buf, param.u_buf, param.size);
+ if (error < 0) {
+ retval = error;
+ }
+ break;
+ }
+
+ disk_param_free(&param);
+ return retval;
+}
+
+/*
+ * Disk I/O multiplexer syscall
+ *
+ * arg0: disk id
+ * arg1: opcode
+ * arg2: disk params
+ */
+scret_t
+sys_disk(struct syscall_args *scargs)
+{
+ struct disk_param *u_param = (void *)scargs->arg2;
+ diskid_t id = scargs->arg0;
+ diskop_t opcode = scargs->arg1;
+
+ return disk_mux_io(id, opcode, u_param);
+}
diff --git a/sys/kern/driver_blacklist.c b/sys/kern/driver_blacklist.c
new file mode 100644
index 0000000..982d5c9
--- /dev/null
+++ b/sys/kern/driver_blacklist.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <sys/driver.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define BLACKLIST_SIZE 64
+
+/*
+ * A driver blacklist entry
+ *
+ * @name: Name of driver to be blacklisted
+ * @buckets: To handle collisions
+ */
+struct blacklist_entry {
+ char *name;
+ TAILQ_ENTRY(blacklist_entry) link;
+ TAILQ_HEAD(, blacklist_entry) buckets;
+};
+
+static struct blacklist_entry blacklist[BLACKLIST_SIZE];
+
+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;
+}
+
+/*
+ * Returns a bucket in case of collision
+ */
+static struct blacklist_entry *
+blacklist_collide(struct blacklist_entry *entp, const char *name)
+{
+ struct blacklist_entry *tmp;
+
+ if (entp->name == NULL) {
+ return NULL;
+ }
+
+ TAILQ_FOREACH(tmp, &entp->buckets, link) {
+ if (strcmp(name, tmp->name) == 0) {
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Mark a driver to be ignored during startup.
+ * Blacklisted drivers will not be ran.
+ *
+ * @name: Name of driver (e.g., 'ahci')
+ */
+int
+driver_blacklist(const char *name)
+{
+ struct blacklist_entry *ent;
+ struct blacklist_entry *bucket;
+ size_t name_len;
+ uint32_t hash;
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ hash = fnv1_hash(name);
+ ent = &blacklist[hash % BLACKLIST_SIZE];
+ if (ent->name != NULL) {
+ bucket = dynalloc(sizeof(*bucket));
+ if (bucket == NULL) {
+ return -EINVAL;
+ }
+ TAILQ_INSERT_TAIL(&ent->buckets, bucket, link);
+ return 0;
+ }
+
+ name_len = strlen(name);
+ ent->name = dynalloc(name_len + 1);
+ if (ent->name == NULL) {
+ return -ENOMEM;
+ }
+ memcpy(ent->name, name, name_len + 1);
+ return 0;
+}
+
+/*
+ * Checks if a driver name is in the blacklist.
+ * Returns 0 if not, otherwise 1.
+ */
+int
+driver_blacklist_check(const char *name)
+{
+ struct blacklist_entry *ent;
+ uint32_t hash;
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ hash = fnv1_hash(name);
+ ent = &blacklist[hash % BLACKLIST_SIZE];
+ if (ent->name == NULL) {
+ return 0;
+ }
+
+ if (strcmp(ent->name, name) == 0) {
+ return 1;
+ }
+
+ ent = blacklist_collide(ent, name);
+ if (ent != NULL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize each entry in the driver
+ * blacklist
+ */
+void
+driver_blacklist_init(void)
+{
+ for (size_t i = 0; i < BLACKLIST_SIZE; ++i) {
+ blacklist[i].name = NULL;
+ TAILQ_INIT(&blacklist[i].buckets);
+ }
+}
diff --git a/sys/kern/driver_subr.c b/sys/kern/driver_subr.c
new file mode 100644
index 0000000..a0f9f73
--- /dev/null
+++ b/sys/kern/driver_subr.c
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+ /*
+ * Check the blacklist to see if this driver
+ * is marked to be ignored. If so, just continue
+ * to the next.
+ */
+ if (driver_blacklist_check(dp->name)) {
+ continue;
+ }
+
+ if (var->deferred) {
+ dp->init();
+ var->deferred = 0;
+ }
+ }
+
+ exit1(td, 0);
+ __builtin_unreachable();
+}
diff --git a/sys/kern/exec_elf64.c b/sys/kern/exec_elf64.c
index 3767b0b..8dc87dc 100644
--- a/sys/kern/exec_elf64.c
+++ b/sys/kern/exec_elf64.c
@@ -49,11 +49,43 @@
#define PHDR(HDRP, IDX) \
(void *)((uintptr_t)HDRP + (HDRP)->e_phoff + (HDRP->e_phentsize * IDX))
+#define SHDR(HDRP, IDX) \
+ (void *)((uintptr_t)HDRP + (HDRP)->e_shoff + (HDRP->e_shentsize * IDX))
+
struct elf_file {
char *data;
size_t size;
};
+static int
+elf_parse_shdrs(Elf64_Ehdr *eh)
+{
+ Elf64_Shdr *shp;
+ uint32_t nshdr;
+
+ if (eh == NULL) {
+ return -EINVAL;
+ }
+
+ nshdr = eh->e_shnum;
+ for (uint32_t i = 0; i < nshdr; ++i) {
+ shp = SHDR(eh, i);
+
+ /* Drop null entries */
+ if (shp->sh_type == SHT_NULL) {
+ continue;
+ }
+
+ switch (shp->sh_type) {
+ case SHT_NOBITS:
+ memset((void *)shp->sh_addr, 0x0, shp->sh_size);
+ break;
+ }
+ }
+
+ return 0;
+}
+
/*
* Load the file and give back an "elf_file"
* structure.
@@ -80,7 +112,7 @@ elf_get_file(const char *pathname, struct elf_file *res)
getattr_args.res = &vattr;
getattr_args.vp = vp;
- status = vfs_vop_getattr(vp, &getattr_args);
+ status = vfs_vop_getattr(&getattr_args);
if (status != 0)
goto done;
@@ -192,6 +224,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog)
if ((status = elf64_verify(hdr)) != 0)
goto done;
+ memset(loadmap, 0, sizeof(loadmap));
pcbp = &td->pcb;
start = -1;
end = 0;
@@ -242,6 +275,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog)
}
}
+ elf_parse_shdrs(hdr);
memcpy(prog->loadmap, loadmap, sizeof(loadmap));
prog->start = start;
prog->end = end;
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index 146c4a9..4a0f7a8 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -35,15 +35,26 @@
#include <sys/exec.h>
#include <sys/driver.h>
#include <sys/panic.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
#include <dev/acpi/uacpi.h>
#include <dev/cons/cons.h>
#include <dev/acpi/acpi.h>
#include <machine/cpu.h>
#include <machine/cdefs.h>
#include <vm/vm.h>
+#include <vm/stat.h>
#include <string.h>
-static struct proc proc0;
+#define _START_PATH "/usr/sbin/init"
+#if defined(_INSTALL_MEDIA)
+#define _START_ARG "/usr/sbin/install"
+#else
+#define _START_ARG NULL
+#endif /* _INSTALL_MEDIA */
+
+struct proc g_proc0;
+struct proc *g_init;
static void
copyright(void)
@@ -57,9 +68,10 @@ start_init(void)
{
struct proc *td = this_td();
struct execve_args execve_args;
- char *argv[] = { "/usr/bin/osh", NULL };
+ char *argv[] = { _START_PATH, _START_ARG, NULL };
char *envp[] = { NULL };
+ kprintf("starting init...\n");
execve_args.pathname = argv[0];
execve_args.argv = argv;
execve_args.envp = envp;
@@ -93,23 +105,35 @@ main(void)
/* Init the virtual file system */
vfs_init();
+ /* Init vmstats */
+ vm_stat_init();
+
/* Expose the console to devfs */
cons_expose();
- uacpi_init();
-
/* Start scheduler and bootstrap APs */
md_intoff();
sched_init();
+ memset(&g_proc0, 0, sizeof(g_proc0));
+ sysctl_clearstr(KERN_HOSTNAME);
+
/* Startup pid 1 */
- memset(&proc0, 0, sizeof(proc0.tf));
- spawn(&proc0, start_init, NULL, 0, NULL);
+ spawn(&g_proc0, start_init, NULL, 0, &g_init);
+ md_inton();
+
+ uacpi_init();
- /* Load all drivers */
+ /* Load all early drivers */
DRIVERS_INIT();
- /* Bootstrap APs and here we go! */
+ /* Only log to kmsg from here */
+ syslog_silence(true);
+
+ /*
+ * Bootstrap APs, schedule all other drivers
+ * and here we go!
+ */
mp_bootstrap_aps(&g_bsp_ci);
sched_enter();
__builtin_unreachable();
diff --git a/sys/kern/kern_accnt.c b/sys/kern/kern_accnt.c
new file mode 100644
index 0000000..51905e7
--- /dev/null
+++ b/sys/kern/kern_accnt.c
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+/*
+ * System Accounting
+ */
+
+#include <sys/sched.h>
+#include <sys/schedvar.h>
+#include <sys/proc.h>
+#include <fs/ctlfs.h>
+#include <machine/cpu.h>
+#include <string.h>
+
+/* Called within kern_sched.c */
+void sched_accnt_init(void);
+
+static struct ctlops sched_stat_ctl;
+volatile size_t g_nthreads;
+
+static int
+ctl_stat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct sched_stat stat;
+
+ if (sio->len > sizeof(stat)) {
+ sio->len = sizeof(stat);
+ }
+
+ sched_stat(&stat);
+ memcpy(sio->buf, &stat, sio->len);
+ 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
+ *
+ * @statp: Info gets copied here
+ */
+void
+sched_stat(struct sched_stat *statp)
+{
+ struct sched_cpu *cpustat;
+
+ 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
+ */
+ for (int i = 0; i < CPU_MAX; ++i) {
+ cpustat = cpu_get_stat(i);
+ if (cpustat == NULL) {
+ break;
+ }
+
+ statp->cpus[i] = *cpustat;
+ }
+}
+
+void
+sched_accnt_init(void)
+{
+ char devname[] = "sched";
+ struct ctlfs_dev ctl;
+
+ /*
+ * Register some accounting information in
+ * '/ctl/sched/stat'
+ */
+ ctl.mode = 0444;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &sched_stat_ctl;
+ ctlfs_create_entry("stat", &ctl);
+}
+
+static struct ctlops sched_stat_ctl = {
+ .read = ctl_stat_read,
+ .write = NULL
+};
diff --git a/sys/kern/kern_cpu.c b/sys/kern/kern_cpu.c
new file mode 100644
index 0000000..69d44c4
--- /dev/null
+++ b/sys/kern/kern_cpu.c
@@ -0,0 +1,61 @@
+/*
+ * 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/sysctl.h>
+#include <sys/types.h>
+
+/*
+ * Report the number of processors that are online
+ * in the machine.
+ *
+ * @count: Number of processors active
+ *
+ * Returns zero on success, otherwise a less
+ * than zero value is returned.
+ */
+int
+cpu_report_count(uint32_t count)
+{
+ struct sysctl_args args;
+ int error, name = HW_NCPU;
+
+ args.name = &name;
+ args.nlen = 1;
+ args.oldlenp = 0;
+ args.oldp = NULL;
+ args.newp = &count;
+ args.newlen = sizeof(count);
+
+ if ((error = sysctl(&args)) != 0) {
+ return error;
+ }
+
+ return 0;
+}
diff --git a/sys/kern/kern_cred.c b/sys/kern/kern_cred.c
new file mode 100644
index 0000000..017b22a
--- /dev/null
+++ b/sys/kern/kern_cred.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/ucred.h>
+#include <sys/proc.h>
+
+int
+setuid(uid_t new)
+{
+ struct proc *td;
+ struct ucred *cur_cred;
+
+ td = this_td();
+ cur_cred = &td->cred;
+
+ /*
+ * Only root can become other users. If you are not
+ * root, fuck off.
+ */
+ if (cur_cred->ruid != 0) {
+ return -EPERM;
+ }
+
+ spinlock_acquire(&cur_cred->lock);
+ cur_cred->euid = new;
+ cur_cred->ruid = new;
+ spinlock_release(&cur_cred->lock);
+ return 0;
+}
+
+uid_t
+getuid(void)
+{
+ struct proc *td;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+
+ return td->cred.ruid;
+}
+
+/*
+ * setuid() syscall
+ *
+ * arg0: `new'
+ */
+scret_t
+sys_setuid(struct syscall_args *scargs)
+{
+ return setuid(scargs->arg0);
+}
+
+scret_t
+sys_getuid(struct syscall_args *scargs)
+{
+ return getuid();
+}
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index d122e89..83845f6 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -41,6 +41,7 @@
/*
* Allocate a file descriptor.
*
+ * @td: Process to allocate from (null for CURRENT)
* @fd_out: Pointer to allocated file descriptor output.
*
* This routine will create a new file descriptor
@@ -49,10 +50,13 @@
* Returns 0 on success.
*/
int
-fd_alloc(struct filedesc **fd_out)
+fd_alloc(struct proc *td, struct filedesc **fd_out)
{
struct filedesc *fd;
- struct proc *td = this_td();
+
+ if (td == NULL) {
+ td = this_td();
+ }
/* Find free fd table entry */
for (size_t i = 3; i < PROC_MAX_FILEDES; ++i) {
@@ -85,12 +89,15 @@ fd_alloc(struct filedesc **fd_out)
* Fetch a file descriptor from a file descriptor
* number.
*
+ * @td: Process to get fd from (NULL for current)
* @fdno: File descriptor to fetch
*/
struct filedesc *
-fd_get(unsigned int fdno)
+fd_get(struct proc *td, unsigned int fdno)
{
- struct proc *td = this_td();
+ if (td == NULL) {
+ td = this_td();
+ }
if (fdno > PROC_MAX_FILEDES) {
return NULL;
@@ -111,7 +118,7 @@ fd_close(unsigned int fd)
struct filedesc *filedes;
struct proc *td;
- if ((filedes = fd_get(fd)) == NULL) {
+ if ((filedes = fd_get(NULL, fd)) == NULL) {
return -EBADF;
}
@@ -149,18 +156,32 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
{
char *kbuf = NULL;
ssize_t n;
+ uint32_t seal;
struct filedesc *filedes;
struct sio_txn sio;
scret_t retval = 0;
+ if (fd > PROC_MAX_FILEDES) {
+ return -EBADF;
+ }
+
if (count > SSIZE_MAX) {
retval = -EINVAL;
goto done;
}
- filedes = fd_get(fd);
- kbuf = dynalloc(count);
+ filedes = fd_get(NULL, fd);
+ seal = filedes->flags;
+ /* Check the seal */
+ if (write && !ISSET(seal, O_ALLOW_WR)) {
+ return -EPERM;
+ }
+ if (!write && ISSET(seal, O_WRONLY)) {
+ return -EPERM;
+ }
+
+ kbuf = dynalloc(count);
if (kbuf == NULL) {
retval = -ENOMEM;
goto done;
@@ -187,6 +208,7 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
sio.buf = kbuf;
sio.offset = filedes->offset;
+ spinlock_acquire(&filedes->lock);
if (write) {
/* Copy in user buffer */
if (copyin(buf, kbuf, count) < 0) {
@@ -205,19 +227,52 @@ 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;
}
}
- retval = count;
+
+ /* Increment the offset per read */
+ filedes->offset += n;
+ retval = n;
done:
if (kbuf != NULL) {
dynfree(kbuf);
}
+ spinlock_release(&filedes->lock);
return retval;
}
+static int
+fd_do_create(const char *path, struct nameidata *ndp)
+{
+ struct vop_create_args cargs;
+ struct vnode *dirvp = ndp->vp;
+ const struct vops *vops = dirvp->vops;
+ int error;
+
+ if (vops->create == NULL) {
+ return -EINVAL;
+ }
+
+ cargs.path = path;
+ cargs.ppath = ndp->path;
+ cargs.dirvp = dirvp;
+ cargs.vpp = &ndp->vp;
+ if ((error = vops->create(&cargs)) < 0) {
+ return error;
+ }
+
+ return 0;
+}
+
int
fd_read(unsigned int fd, void *buf, size_t count)
{
@@ -236,28 +291,35 @@ fd_write(unsigned int fd, void *buf, size_t count)
*
* @pathname: Path of file to open.
* @flags: Flags to use.
- *
- * TODO: Use of flags.
*/
int
fd_open(const char *pathname, int flags)
{
int error;
+ const struct vops *vops;
struct filedesc *filedes;
struct nameidata nd;
nd.path = pathname;
- nd.flags = 0;
+ nd.flags = ISSET(flags, O_CREAT) ? NAMEI_WANTPARENT : 0;
if ((error = namei(&nd)) < 0) {
return error;
}
- if ((error = fd_alloc(&filedes)) != 0) {
+ if ((error = fd_alloc(NULL, &filedes)) != 0) {
vfs_release_vnode(nd.vp);
return error;
}
+ vops = nd.vp->vops;
+ if (ISSET(flags, O_CREAT) && vops->create != NULL) {
+ error = fd_do_create(pathname, &nd);
+ }
+ if (error < 0) {
+ return error;
+ }
+
filedes->vp = nd.vp;
filedes->flags = flags;
return filedes->fdno;
@@ -266,18 +328,25 @@ fd_open(const char *pathname, int flags)
/*
* Duplicate a file descriptor. New file descriptor
* points to the same vnode.
+ *
+ * @td: Process of fd to dup (NULL for current)
+ * @fd: File descriptor to dup
*/
int
-fd_dup(int fd)
+fd_dup(struct proc *td, int fd)
{
int error;
struct filedesc *new_desc, *tmp;
- tmp = fd_get(fd);
+ if (td == NULL) {
+ td = this_td();
+ }
+
+ tmp = fd_get(td, fd);
if (tmp == NULL)
return -EBADF;
- if ((error = fd_alloc(&new_desc)) != 0)
+ if ((error = fd_alloc(td, &new_desc)) != 0)
return error;
/* Ref that vnode before we point to it */
@@ -285,3 +354,51 @@ fd_dup(int fd)
new_desc->vp = tmp->vp;
return new_desc->fdno;
}
+
+off_t
+fd_seek(int fildes, off_t offset, int whence)
+{
+ struct filedesc *tmp;
+ struct vattr attr;
+ struct vop_getattr_args getattr_args;
+
+ tmp = fd_get(NULL, fildes);
+ if (tmp == NULL) {
+ return -EBADF;
+ }
+
+ getattr_args.vp = tmp->vp;
+ getattr_args.res = &attr;
+ if ((vfs_vop_getattr(&getattr_args)) < 0) {
+ return -EPIPE;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ tmp->offset = offset;
+ break;
+ case SEEK_CUR:
+ tmp->offset += offset;
+ break;
+ case SEEK_END:
+ tmp->offset = attr.size + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return tmp->offset;
+}
+
+/*
+ * Update file offset
+ *
+ * arg0: `filedes'
+ * arg1: `offset'
+ * arg2: `whence'
+ */
+scret_t
+sys_lseek(struct syscall_args *scargs)
+{
+ return fd_seek(scargs->arg0, scargs->arg1, scargs->arg2);
+}
diff --git a/sys/kern/kern_disk.c b/sys/kern/kern_disk.c
new file mode 100644
index 0000000..a3fa05e
--- /dev/null
+++ b/sys/kern/kern_disk.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/sio.h>
+#include <sys/param.h>
+#include <sys/panic.h>
+#include <sys/spinlock.h>
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <vm/dynalloc.h>
+#include <assert.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("disk: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define DEFAULT_BSIZE 512 /* Default block size in bytes */
+#define DISKQ_COOKIE 0xD9EA /* Verification cookie */
+
+/*
+ * The maximum disks supported by the kernel
+ * is defined by the `DISK_MAX' kconf(9) option.
+ *
+ * We define a default of 16 if that option is not
+ * specified.
+ */
+#if defined(__DISK_MAX)
+#define DISK_MAX __DISK_MAX
+#else
+#define DISK_MAX 16 /* Maximum disks */
+#endif
+
+/*
+ * We set a hard limit at 64 disks to prevent misconfiguration as
+ * it is unlikely that one would ever have that many on a single
+ * instance. Though of course, anything is possible, so one may
+ * patch the hard limit defined below to a higher value if needed.
+ */
+__static_assert(DISK_MAX < 64, "DISK_MAX exceeds hard limit");
+
+/*
+ * The disk queue stores descriptors of disks that
+ * are registered with the system. This allows for
+ * easy and simplified access of the storage medium.
+ *
+ * XXX: An array would be more efficent, however disks
+ * could be detached or swapped during runtime thus
+ * making the usage of queues a more sane design.
+ *
+ * This also provides the added benefit of lazy-allocation
+ * so memory isn't wasted and only allocated when we actually
+ * have a disk descriptor that it would be used to store.
+ */
+static struct spinlock diskq_lock;
+static TAILQ_HEAD(, disk) diskq;
+static uint16_t disk_count = 0;
+static uint16_t diskq_cookie = 0;
+
+/*
+ * Verify that a disk descriptor has been properly
+ * initialized by comparing against the cookie field.
+ *
+ * Returns a value of zero if valid, otherwise a less
+ * than zero value is returned.
+ */
+__always_inline static inline int
+check_disk_cookie(struct disk *dp)
+{
+ __assert(dp != NULL);
+ return (dp->cookie == DISKQ_COOKIE) ? 0 : -1;
+}
+
+/*
+ * Verify if the disk queue is initialized and
+ * ready for descriptors to be added.
+ *
+ * Returns a value of zero if it has already been
+ * initialized, otherwise a value less than zero
+ * is returned after check_diskq() initializes
+ * the disk queue.
+ */
+static inline int
+check_diskq(void)
+{
+ if (diskq_cookie != DISKQ_COOKIE) {
+ TAILQ_INIT(&diskq);
+ diskq_cookie = DISKQ_COOKIE;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Acquire a disk descriptor through a zero-based
+ * disk index. Returns a pointer to the disk descriptor
+ * on success, otherwise a less than zero value is returned.
+ *
+ * @id: Disk index
+ *
+ * XXX: This is the lockless internal implementation,
+ * please use disk_get_id() instead.
+ */
+static struct disk *
+__disk_get_id(diskid_t id)
+{
+ struct disk *dp;
+
+ if (id >= disk_count) {
+ return NULL;
+ }
+
+ dp = TAILQ_FIRST(&diskq);
+ if (dp == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Now, we start at the first disk entry and
+ * traverse the list. If the ID of a disk matches
+ * the ID we are looking for, return it.
+ */
+ while (dp != NULL) {
+ if (dp->id == id) {
+ return dp;
+ }
+
+ dp = TAILQ_NEXT(dp, link);
+ }
+
+ /* Nothing found :( */
+ return NULL;
+}
+
+/*
+ * Attempt to perform a read/write operation on
+ * a disk.
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to read at
+ * @buf: Buffer to read data into
+ * @len: Number of bytes to read
+ * @write: If true, do a write
+ *
+ * XXX: The size in which blocks are read at is in
+ * virtual blocks which is defined by V_BSIZE
+ * in sys/disk.h
+ */
+static ssize_t
+disk_rw(diskid_t id, blkoff_t blk, void *buf, size_t len, bool write)
+{
+ const struct bdevsw *bdev;
+ struct sio_txn sio;
+ struct disk *dp;
+ int error;
+
+ len = ALIGN_UP(len, V_BSIZE);
+
+ /* Attempt to grab the disk object */
+ error = disk_get_id(id, &dp);
+ if (error < 0) {
+ return error;
+ }
+
+ /* Sanity check, should not happen */
+ bdev = dp->bdev;
+ if (__unlikely(bdev == NULL)) {
+ return -EIO;
+ }
+
+ /* Prepare the buffer */
+ sio.buf = buf;
+ sio.offset = blk * dp->bsize;
+ sio.len = len;
+
+ /* Handle writes */
+ if (write) {
+ if (bdev->write == NULL) {
+ return -ENOTSUP;
+ }
+
+ return bdev->write(dp->dev, &sio, 0);
+ }
+
+ /* Do we support this operation? */
+ if (bdev->read == NULL) {
+ return -ENOTSUP;
+ }
+
+ return bdev->read(dp->dev, &sio, 0);
+}
+
+/*
+ * Register a disk with the system so that it may
+ * be accessible independently of its device major
+ * and minor numbers
+ *
+ * @name: Name of the disk
+ * @dev: Device minor
+ * @bdev: Block device operations associated with device
+ *
+ * Returns zero on success, otherwise a less than zero
+ * value is returned.
+ */
+int
+disk_add(const char *name, dev_t dev, const struct bdevsw *bdev, int flags)
+{
+ struct disk *dp;
+ size_t name_len;
+
+ if (name == NULL || bdev == NULL) {
+ return -EINVAL;
+ }
+
+ /* Disk queue must be initialized */
+ check_diskq();
+
+ /* There is a limit to how many can be added */
+ if (disk_count >= DISK_MAX) {
+ pr_error("disk_add: disk limit %d/%d reached\n",
+ disk_count, DISK_MAX);
+ return -EAGAIN;
+ }
+
+ /* Is the disk name of correct length? */
+ name_len = strlen(name);
+ if (name_len >= sizeof(dp->name) - 1) {
+ pr_error("disk_add: name too big (len=%d)\n", name_len);
+ return -E2BIG;
+ }
+
+ dp = dynalloc(sizeof(*dp));
+ if (dp == NULL) {
+ pr_error("failed to allocate disk\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the descriptor */
+ memset(dp, 0, sizeof(*dp));
+ memcpy(dp->name, name, name_len);
+ dp->cookie = DISKQ_COOKIE;
+ dp->bdev = bdev;
+ dp->dev = dev;
+ dp->id = disk_count++;
+ dp->bsize = DEFAULT_BSIZE;
+
+ /*
+ * We are to panic if the virtual blocksize
+ * defined is not a multiple of any hardware
+ * block size
+ */
+ if ((V_BSIZE & (dp->bsize - 1)) != 0) {
+ panic("virtual block size not hw bsize aligned\n");
+ }
+
+ /* Now we can add it to the queue */
+ spinlock_acquire(&diskq_lock);
+ TAILQ_INSERT_TAIL(&diskq, dp, link);
+ spinlock_release(&diskq_lock);
+ return 0;
+}
+
+/*
+ * Acquire a disk descriptor by using a zero-based
+ * index.
+ *
+ * @id: Disk index (0: primary)
+ * @res: Resulting disk descriptor
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+int
+disk_get_id(diskid_t id, struct disk **res)
+{
+ int error;
+ struct disk *dp;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ if (id >= disk_count) {
+ return -ENODEV;
+ }
+
+ /* Grab the disk */
+ spinlock_acquire(&diskq_lock);
+ dp = __disk_get_id(id);
+ spinlock_release(&diskq_lock);
+
+ /* Did it even exist? */
+ if (dp == NULL) {
+ return -ENODEV;
+ }
+
+ /* Should not fail but make sure */
+ error = check_disk_cookie(dp);
+ if (__unlikely(error < 0)) {
+ panic("disk_get_id: got bad disk object\n");
+ }
+
+ *res = dp;
+ return 0;
+}
+
+/*
+ * Allocate a memory buffer that may be used for
+ * disk I/O.
+ *
+ * @id: ID of disk buffer will be used for
+ * @len: Length to allocate
+ */
+void *
+disk_buf_alloc(diskid_t id, size_t len)
+{
+ struct disk *dp;
+ void *buf;
+
+ if (len == 0) {
+ return NULL;
+ }
+
+ /* Attempt to acquire the disk */
+ if (disk_get_id(id, &dp) < 0) {
+ return NULL;
+ }
+
+ /*
+ * Here we will align the buffer size by the
+ * virtual block size to ensure it is big enough.
+ */
+ len = ALIGN_UP(len, V_BSIZE);
+ buf = dynalloc(len);
+ return buf;
+}
+
+/*
+ * Free a memory buffer that was allocated by
+ * disk_buf_alloc()
+ */
+void
+disk_buf_free(void *p)
+{
+ if (p != NULL) {
+ dynfree(p);
+ }
+}
+
+/*
+ * Attempt to perform a read operation on
+ * a disk.
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to read at
+ * @buf: Buffer to read data into
+ * @len: Number of bytes to read
+ */
+ssize_t
+disk_read(diskid_t id, blkoff_t blk, void *buf, size_t len)
+{
+ ssize_t retval;
+ char *tmp;
+
+ tmp = disk_buf_alloc(id, len);
+ if (tmp == NULL) {
+ return -ENOMEM;
+ }
+
+ retval = disk_rw(id, blk, tmp, len, false);
+ if (retval < 0) {
+ disk_buf_free(tmp);
+ return retval;
+ }
+
+ memcpy(buf, tmp, len);
+ disk_buf_free(tmp);
+ return retval;
+}
+
+/*
+ * Attempt to perform a write operation on
+ * a disk.
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to read at
+ * @buf: Buffer containing data to write
+ * @len: Number of bytes to read
+ */
+ssize_t
+disk_write(diskid_t id, blkoff_t blk, const void *buf, size_t len)
+{
+ ssize_t retval;
+ char *tmp;
+
+ tmp = disk_buf_alloc(id, len);
+ if (tmp == NULL) {
+ return -ENOMEM;
+ }
+
+ memcpy(tmp, buf, len);
+ retval = disk_rw(id, blk, tmp, len, true);
+ disk_buf_free(tmp);
+ return retval;
+}
+
+/*
+ * Attempt to request attributes from a specific
+ * device.
+ *
+ * @id: ID of disk to query
+ * @res: Resulting information goes here
+ *
+ * This function returns zero on success, otherwise
+ * a less than zero value is returned.
+ */
+int
+disk_query(diskid_t id, struct disk_info *res)
+{
+ const struct bdevsw *bdev;
+ struct disk *dp;
+ int error;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ /* Attempt to grab the disk */
+ error = disk_get_id(id, &dp);
+ if (error < 0) {
+ pr_error("disk_query: bad disk ID %d\n", id);
+ return error;
+ }
+
+ bdev = dp->bdev;
+ if (__unlikely(bdev == NULL)) {
+ pr_error("disk_query: no bdev for disk %d\n", id);
+ return -EIO;
+ }
+
+ res->block_size = dp->bsize;
+ res->vblock_size = V_BSIZE;
+ res->n_block = bdev->bsize(dp->dev);
+ return 0;
+}
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index bf6a26e..2a53b8a 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -37,6 +37,7 @@
#include <vm/map.h>
#include <vm/physmem.h>
#include <machine/pcb.h>
+#include <machine/cdefs.h>
#include <string.h>
/*
@@ -87,6 +88,7 @@ execve(struct proc *td, const struct execve_args *args)
release_stack(td);
/* Save program state */
+ md_intoff();
memcpy(&td->exec, &prog, sizeof(td->exec));
/* Set new stack and map it to userspace */
@@ -99,7 +101,7 @@ execve(struct proc *td, const struct execve_args *args)
stack_top = td->stack_base + (PROC_STACK_SIZE - 1);
/* Setup registers, signals and stack */
- md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog);
+ stack_top = md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog);
setregs(td, &prog, stack_top);
signals_init(td);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index d6c9168..af697d7 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -30,15 +30,24 @@
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/syslog.h>
+#include <sys/atomic.h>
+#include <sys/panic.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <dev/cons/cons.h>
#include <vm/physmem.h>
#include <vm/dynalloc.h>
#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__)
+extern volatile size_t g_nthreads;
+extern struct proc g_init;
+
static void
unload_td(struct proc *td)
{
@@ -49,6 +58,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;
@@ -73,47 +86,92 @@ unload_td(struct proc *td)
}
}
+void
+proc_reap(struct proc *td)
+{
+ struct pcb *pcbp;
+ struct filedesc *fdp;
+ vaddr_t stack_va;
+ paddr_t stack_pa;
+
+ cons_detach();
+
+ /* Clear out all fds */
+ for (size_t i = 4; i < PROC_MAX_FILEDES; ++i) {
+ fdp = td->fds[i];
+ if (fdp == NULL) {
+ continue;
+ }
+ if (fdp->refcnt == 1) {
+ vfs_release_vnode(fdp->vp);
+ dynfree(fdp);
+ fdp = NULL;
+ }
+ }
+
+ pcbp = &td->pcb;
+ unload_td(td);
+
+ /*
+ * User space stacks are identity mapped and
+ * kernel space stacks are not.
+ */
+ if (ISSET(td->flags, PROC_KTD)) {
+ stack_va = td->stack_base;
+ stack_pa = td->stack_base - VM_HIGHER_HALF;
+ } else {
+ stack_va = td->stack_base;
+ stack_pa = td->stack_base;
+ vm_unmap(pcbp->addrsp, stack_va, PROC_STACK_SIZE);
+ }
+
+ vm_free_frame(stack_pa, PROC_STACK_PAGES);
+ pmap_destroy_vas(pcbp->addrsp);
+}
+
/*
* Kill a thread and deallocate its resources.
*
* @td: Thread to exit
*/
int
-exit1(struct proc *td)
+exit1(struct proc *td, int flags)
{
- struct pcb *pcbp;
struct proc *curtd, *procp;
- uintptr_t stack;
+ struct proc *parent;
+ struct cpu_info *ci;
pid_t target_pid, curpid;
+ if (td->pid == 1) {
+ panic("init died\n");
+ }
+
+ ci = this_cpu();
target_pid = td->pid;
curtd = this_td();
- pcbp = &td->pcb;
curpid = curtd->pid;
- stack = td->stack_base;
+
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) {
- exit1(procp);
+ procp->parent = &g_init;
}
}
- /*
- * If this is on the higher half, it is kernel
- * mapped and we need to convert it to a physical
- * address.
- */
- if (stack >= VM_HIGHER_HALF) {
- stack -= VM_HIGHER_HALF;
+ if (target_pid != curpid) {
+ proc_reap(td);
}
- unload_td(td);
- vm_unmap(pcbp->addrsp, td->stack_base, PROC_STACK_SIZE);
- vm_free_frame(stack, PROC_STACK_PAGES);
- pmap_destroy_vas(pcbp->addrsp);
+ if (td->data != NULL) {
+ dynfree(td->data);
+ }
/*
* Only free the process structure if we aren't
@@ -124,14 +182,29 @@ exit1(struct proc *td)
dynfree(td);
} else {
td->flags |= PROC_ZOMB;
+ td->flags &= ~PROC_WAITED;
}
/*
* If we are the thread exiting, reenter the scheduler
* and do not return.
*/
- if (target_pid == curpid)
+ if (target_pid == curpid) {
+ /*
+ * If the thread is exiting on a core that is not
+ * preemptable, something is not right.
+ */
+ if (__unlikely(!sched_preemptable())) {
+ panic("exit1: cpu %d not preemptable\n", ci->id);
+ }
+
+ ci->curtd = NULL;
+ if (parent->pid == 0)
+ sched_enter();
+
+ parent->flags &= ~PROC_SLEEP;
sched_enter();
+ }
return 0;
}
@@ -145,6 +218,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_krq.c b/sys/kern/kern_krq.c
new file mode 100644
index 0000000..c12a98c
--- /dev/null
+++ b/sys/kern/kern_krq.c
@@ -0,0 +1,61 @@
+/*
+ * 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/krq.h>
+#include <sys/errno.h>
+#include <sys/spinlock.h>
+#include <sys/driver.h>
+#include <sys/syslog.h>
+
+static struct spinlock krq_lock = {0};
+
+/*
+ * Load a kernel runtime quantum (KRQ)
+ *
+ * @arg0: path
+ *
+ * XXX: If the 'path' argument is NULL, all deferrable
+ * drivers are loaded.
+ *
+ * TODO: Handle non-null paths where a completly seperate
+ * module/krq can be loaded.
+ */
+scret_t
+sys_inject(struct syscall_args *scargs)
+{
+ if (scargs->arg0 != 0) {
+ return -EINVAL;
+ }
+
+ spinlock_acquire(&krq_lock);
+ DRIVERS_SCHED();
+ spinlock_release(&krq_lock);
+ return 0;
+}
diff --git a/sys/kern/kern_panic.c b/sys/kern/kern_panic.c
index 7660fff..13b4964 100644
--- a/sys/kern/kern_panic.c
+++ b/sys/kern/kern_panic.c
@@ -31,8 +31,25 @@
#include <sys/spinlock.h>
#include <sys/syslog.h>
#include <sys/reboot.h>
+#include <dev/cons/cons.h>
#include <machine/cdefs.h>
#include <machine/cpu.h>
+#include <string.h>
+
+#if defined(__PANIC_SCR)
+#define PANIC_SCR __PANIC_SCR
+#else
+#define PANIC_SCR 0
+#endif
+
+static void
+panic_puts(const char *str)
+{
+ size_t len;
+
+ len = strlen(str);
+ cons_putstr(&g_root_scr, str, len);
+}
/*
* Burn and sizzle - the core logic that really ends
@@ -49,14 +66,40 @@ bas(bool do_trace, int reboot_type)
spinlock_acquire(&lock); /* Never released */
if (do_trace) {
- kprintf(OMIT_TIMESTAMP "** backtrace\n");
+ panic_puts(" ** backtrace\n");
md_backtrace();
}
+ panic_puts("\n-- ALL CORES HAVE BEEN HALTED --\n");
cpu_reboot(reboot_type);
__builtin_unreachable();
}
+static void
+panic_screen(void)
+{
+ struct cons_screen *scr = &g_root_scr;
+
+ if (scr->fb_mem != NULL) {
+ scr->bg = 0x8B0000;
+ scr->fg = 0xAABBAA;
+ cons_reset_cursor(scr);
+ cons_clear_scr(scr, 0x393B39);
+ }
+}
+
+static void
+do_panic(const char *fmt, va_list *ap)
+{
+ syslog_silence(false);
+ spinlock_release(&g_root_scr.lock);
+ panic_puts("panic: ");
+ vkprintf(fmt, ap);
+ bas(true, REBOOT_HALT);
+
+ __builtin_unreachable();
+}
+
/*
* Tells the user something terribly wrong happened then
* halting the system as soon as possible.
@@ -75,11 +118,11 @@ panic(const char *fmt, ...)
md_intoff();
cpu_halt_others();
+ if (PANIC_SCR) {
+ panic_screen();
+ }
va_start(ap, fmt);
- kprintf(OMIT_TIMESTAMP "\npanic: ");
- vkprintf(fmt, &ap);
- bas(true, REBOOT_HALT);
-
+ do_panic(fmt, &ap);
__builtin_unreachable();
}
@@ -95,7 +138,6 @@ hcf(const char *fmt, ...)
{
va_list ap;
-
if (fmt != NULL) {
va_start(ap, fmt);
kprintf(OMIT_TIMESTAMP);
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
new file mode 100644
index 0000000..8bc5680
--- /dev/null
+++ b/sys/kern/kern_proc.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/cdefs.h>
+#include <sys/vnode.h>
+#include <sys/tree.h>
+#include <sys/syscall.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <string.h>
+#include <crc32.h>
+
+extern volatile size_t g_nthreads;
+
+pid_t
+getpid(void)
+{
+ struct proc *td;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+
+ return td->pid;
+}
+
+pid_t
+getppid(void)
+{
+ struct proc *td;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+ if (td->parent == NULL) {
+ return -1;
+ }
+
+ return td->parent->pid;
+}
+
+void
+proc_coredump(struct proc *td, uintptr_t fault_addr)
+{
+ struct coredump core;
+ struct sio_txn sio;
+ struct vnode *vp;
+ char pathname[128];
+ int fd;
+
+ snprintf(pathname, sizeof(pathname), "/tmp/core.%d", td->pid);
+ fd = fd_open(pathname, O_RDWR | O_CREAT);
+
+ /* ... Hopefully not */
+ if (__unlikely(fd < 0)) {
+ return;
+ }
+
+ core.pid = td->pid;
+ core.fault_addr = fault_addr;
+ memcpy(&core.tf, &td->tf, sizeof(td->tf));
+
+ core.checksum = crc32(&core, sizeof(core) - sizeof(core.checksum));
+ vp = fd_get(NULL, fd)->vp;
+
+ sio.buf = &core;
+ sio.len = sizeof(core);
+ sio.offset = 0;
+
+ /* Write the core file */
+ vfs_vop_write(vp, &sio);
+ fd_close(fd);
+}
+
+int
+proc_init(struct proc *td, struct proc *parent)
+{
+ struct mmap_lgdr *mlgdr;
+
+ mlgdr = dynalloc(sizeof(*mlgdr));
+ if (mlgdr == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Add to parent leafq */
+ TAILQ_INSERT_TAIL(&parent->leafq, td, leaf_link);
+ atomic_inc_int(&parent->nleaves);
+ atomic_inc_64(&g_nthreads);
+ td->parent = parent;
+ td->exit_status = -1;
+ td->cred = parent->cred;
+
+ /* Initialize the mmap ledger */
+ mlgdr->nbytes = 0;
+ RBT_INIT(lgdr_entries, &mlgdr->hd);
+ td->mlgdr = mlgdr;
+ td->flags |= PROC_WAITED;
+ signals_init(td);
+ return 0;
+}
+
+scret_t
+sys_getpid(struct syscall_args *scargs)
+{
+ return getpid();
+}
+
+scret_t
+sys_getppid(struct syscall_args *scargs)
+{
+ return getppid();
+}
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
index c22ea25..9c5e215 100644
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -34,6 +34,7 @@
#include <sys/param.h>
#include <sys/syslog.h>
#include <sys/atomic.h>
+#include <dev/cons/cons.h>
#include <machine/frame.h>
#include <machine/cpu.h>
#include <machine/cdefs.h>
@@ -44,7 +45,8 @@
#define pr_trace(fmt, ...) kprintf("ksched: " fmt, ##__VA_ARGS__)
-void sched_switch(struct trapframe *tf);
+void md_sched_switch(struct trapframe *tf);
+void sched_accnt_init(void);
static sched_policy_t policy = SCHED_POLICY_MLFQ;
@@ -63,7 +65,7 @@ __cacheline_aligned static struct spinlock tdq_lock = {0};
/*
* Perform timer oneshot
*/
-static inline void
+void
sched_oneshot(bool now)
{
struct timer timer;
@@ -77,39 +79,75 @@ sched_oneshot(bool now)
}
/*
- * Save thread state and enqueue it back into one
- * of the ready queues.
+ * Returns true if a processor is associated
+ * with a specific thread
+ *
+ * @ci: CPU that wants to take 'td'
+ * @td: Thread to check against
*/
-static void
-sched_save_td(struct proc *td, struct trapframe *tf)
+static bool
+cpu_is_assoc(struct cpu_info *ci, struct proc *td)
{
/*
- * Save trapframe to process structure only
- * if PROC_EXEC is not set.
+ * If we are not pinned, any processor is
+ * associated.
*/
- if (!ISSET(td->flags, PROC_EXEC)) {
- memcpy(&td->tf, tf, sizeof(td->tf));
+ if (!ISSET(td->flags, PROC_PINNED)) {
+ return true;
}
- sched_enqueue_td(td);
+ return ci->id == td->affinity;
}
-static struct proc *
+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];
- if (!TAILQ_EMPTY(&queue->q)) {
- td = TAILQ_FIRST(&queue->q);
- TAILQ_REMOVE(&queue->q, td, link);
- spinlock_release(&tdq_lock);
- return td;
+ if (TAILQ_EMPTY(&queue->q)) {
+ continue;
+ }
+
+ td = TAILQ_FIRST(&queue->q);
+ if (td == NULL) {
+ continue;
+ }
+
+ while (ISSET(td->flags, PROC_SLEEP)) {
+ td = TAILQ_NEXT(td, link);
+ if (td == NULL) {
+ break;
+ }
}
+
+ /*
+ * 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;
+ }
+
+ TAILQ_REMOVE(&queue->q, td, link);
+ spinlock_release(&tdq_lock);
+ return td;
}
/* We got nothing */
@@ -141,6 +179,9 @@ this_td(void)
struct cpu_info *ci;
ci = this_cpu();
+ if (ci == NULL) {
+ return NULL;
+ }
return ci->curtd;
}
@@ -177,35 +218,21 @@ td_pri_update(struct proc *td)
}
/*
- * Perform a context switch.
+ * MI work to be done during a context
+ * switch. Called by md_sched_switch()
*/
void
-sched_switch(struct trapframe *tf)
+mi_sched_switch(struct proc *from)
{
- struct cpu_info *ci;
- struct pcb *pcbp;
- struct proc *next_td, *td;
-
- ci = this_cpu();
- td = ci->curtd;
-
- if (td != NULL) {
- dispatch_signals(td);
- td_pri_update(td);
- sched_save_td(td, tf);
- }
+ if (from != NULL) {
+ if (from->pid == 0)
+ return;
- if ((next_td = sched_dequeue_td()) == NULL) {
- sched_oneshot(false);
- return;
+ dispatch_signals(from);
+ td_pri_update(from);
}
- memcpy(tf, &next_td->tf, sizeof(*tf));
- ci->curtd = next_td;
- pcbp = &next_td->pcb;
-
- pmap_switch_vas(pcbp->addrsp);
- sched_oneshot(false);
+ cons_detach();
}
/*
@@ -224,12 +251,27 @@ sched_enter(void)
void
sched_yield(void)
{
- struct proc *td = this_td();
+ struct proc *td;
+ struct cpu_info *ci = this_cpu();
+
+ if ((td = ci->curtd) == NULL) {
+ return;
+ }
+
+ td->rested = true;
- if (td != NULL) {
- td->rested = true;
- sched_switch(&td->tf);
+ /* FIXME: Hang yielding when waited on */
+ if (ISSET(td->flags, PROC_WAITED)) {
+ return;
}
+
+ ci->curtd = NULL;
+ md_inton();
+ sched_oneshot(false);
+
+ md_hlt();
+ md_intoff();
+ ci->curtd = td;
}
void
@@ -244,6 +286,121 @@ 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;
+}
+
+/*
+ * Suspend a process for a specified amount
+ * of time. This calling process will yield for
+ * the amount of time specified in 'tv'
+ *
+ * @td: Process to suspend (NULL for current)
+ * @tv: Time value to use
+ *
+ * XXX: 'tv' being NULL is equivalent to calling
+ * sched_detach()
+ */
+void
+sched_suspend(struct proc *td, const struct timeval *tv)
+{
+ struct timer tmr;
+ const time_t USEC_PER_SEC = 1000000;
+ ssize_t usec;
+ time_t usec_cur, usec_tmp;
+ bool have_timer = true;
+ tmrr_status_t tmr_status;
+
+ if (td == NULL)
+ td = this_td();
+ if (__unlikely(td == NULL))
+ return;
+
+ if (tv == NULL) {
+ sched_detach(td);
+ return;
+ }
+
+ /*
+ * Now, we need a generic timer so that we can compute
+ * how much time has elapsed since this process has
+ * requested to be suspended. However, we cannot assume
+ * that it would be present. If the lookup fails, all we
+ * can do is try to estimate how much time went by which
+ * works fine too, just not as accurate.
+ */
+ tmr_status = req_timer(TIMER_GP, &tmr);
+ if (tmr_status != TMRR_SUCCESS) {
+ have_timer = false;
+ }
+
+ /* We need microsecond precision */
+ if (tmr.get_time_sec == NULL) {
+ have_timer = false;
+ }
+
+ /*
+ * Compute the max time in microseconds that
+ * we will wait. We are using both tv->tv_sec
+ * and tv->tv_usec
+ */
+ usec = tv->tv_usec;
+ usec += tv->tv_sec * USEC_PER_SEC;
+ usec_cur = (have_timer) ? tmr.get_time_usec() : 0;
+
+ for (;;) {
+ sched_yield();
+
+ /*
+ * If we have a timer in our paws, compute how much
+ * time went by. Otherwise we estimate by subtracting
+ * the scheduler quantum.
+ *
+ * XXX: The timing here works decently as intended. However,
+ * it would be nice to smoothen out any jitter. Such can
+ * probably be done by subtracting 'usec' by the exponential
+ * moving average of 'usec_tmp' rather than the raw original
+ * value.
+ */
+ if (have_timer) {
+ usec_tmp = (tmr.get_time_usec() - usec_cur);
+ } else {
+ usec_tmp = DEFAULT_TIMESLICE_USEC;
+ }
+
+ /* We are done here! */
+ usec -= usec_tmp;
+ if (usec <= 0) {
+ break;
+ }
+ }
+}
+
void
sched_init(void)
{
@@ -254,4 +411,6 @@ sched_init(void)
pr_trace("prepared %d queues (policy=0x%x)\n",
SCHED_NQUEUE, policy);
+
+ sched_accnt_init();
}
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_socket.c b/sys/kern/kern_socket.c
new file mode 100644
index 0000000..d0fbe19
--- /dev/null
+++ b/sys/kern/kern_socket.c
@@ -0,0 +1,1009 @@
+/*
+ * 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/socket.h>
+#include <sys/sio.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/namei.h>
+#include <sys/sched.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <sys/vnode.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("socket: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static struct vops socket_vops;
+
+/*
+ * This table maps socket option names to
+ * lengths of their underlying structure.
+ *
+ * This is used for bounds/length checking within
+ * setsockopt()
+ */
+static size_t sockopt_lentab[_SO_MAX] = {
+ [ SO_RCVTIMEO ] = sizeof(struct timeval)
+};
+
+/*
+ * Get a kernel socket structure from a
+ * file descriptor.
+ *
+ * @sockfd: File descriptor to lookup
+ * @res: Result pointer
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+static int
+get_ksock(int sockfd, struct ksocket **res)
+{
+ struct ksocket *ksock;
+ struct filedesc *fdesc;
+ struct vnode *vp;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the file descriptor */
+ fdesc = fd_get(NULL, sockfd);
+ if (fdesc == NULL) {
+ return -EBADF;
+ }
+
+ /* Is this even a socket? */
+ if ((vp = fdesc->vp) == NULL) {
+ return -ENOTSOCK;
+ }
+ if (vp->type != VSOCK) {
+ return -ENOTSOCK;
+ }
+
+ ksock = vp->data;
+ if (__unlikely(ksock == NULL)) {
+ return -EIO;
+ }
+
+ *res = ksock;
+ return 0;
+}
+
+/*
+ * VFS reclaim callback for the socket
+ * layer
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+static int
+socket_reclaim(struct vnode *vp)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+
+ /* Is this even a socket? */
+ if (vp->type != VSOCK) {
+ return -ENOTSOCK;
+ }
+
+ /* Is there any data attached? */
+ if ((ksock = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Free up any used options */
+ for (int i = 0; i < _SO_MAX; ++i) {
+ opt = ksock->opt[i];
+ if (opt != NULL) {
+ dynfree(opt);
+ ksock->opt[i] = NULL;
+ }
+ }
+
+ fd_close(ksock->sockfd);
+ mutex_free(ksock->mtx);
+ dynfree(ksock);
+ return 0;
+}
+
+/*
+ * Create a socket file from the sockaddr
+ * structure
+ *
+ * @ksock: Socket to create a file for
+ * @sockaddr_un: domain sockaddr
+ */
+static int
+socket_mkfile(struct ksocket *ksock, struct sockaddr_un *un)
+{
+ struct filedesc *fdesc;
+ struct vnode *vp;
+ int fd;
+
+ fd = fd_open(un->sun_path, O_CREAT | O_RDONLY);
+ if (fd < 0) {
+ return fd;
+ }
+
+ /* Grab the actual handle now */
+ fdesc = fd_get(NULL, fd);
+ if (fdesc == NULL) {
+ fd_close(fd);
+ return -EIO;
+ }
+
+ /* Hijack the vnode */
+ vp = fdesc->vp;
+ vp->type = VSOCK;
+ vp->vops = &socket_vops;
+ vp->data = ksock;
+ return fd;
+}
+
+/*
+ * Connect to a domain socket - used by connect()
+ *
+ * @sockfd: Socket file descriptor
+ * @ksock: Current ksock
+ * @un: Current sockaddr_un
+ */
+static int
+connect_domain(int sockfd, struct ksocket *ksock, struct sockaddr_un *un)
+{
+ int error;
+ struct nameidata ndp;
+ struct filedesc *filedesc;
+ struct vnode *vp;
+
+ ndp.path = un->sun_path;
+ ndp.flags = 0;
+ if ((error = namei(&ndp)) < 0) {
+ return error;
+ }
+
+ vp = ndp.vp;
+ filedesc = fd_get(NULL, sockfd);
+ if (filedesc == NULL) {
+ pr_error("connect: no filedesc for current\n");
+ return -EIO;
+ }
+
+ filedesc->vp = vp;
+ return 0;
+}
+
+/*
+ * Wait until data is received for the
+ * recv() function.
+ *
+ * @sockfd: Socket we are waiting on
+ *
+ * Returns zero on success, otherwise a less
+ * than zero value is returned.
+ */
+static int
+socket_rx_wait(int sockfd)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+ struct timeval tv;
+ int error;
+
+ if (ksock == NULL) {
+ return -EINVAL;
+ }
+
+ error = get_ksock(sockfd, &ksock);
+ if (error < 0) {
+ return error;
+ }
+
+ /*
+ * If the socket does not have this option set,
+ * we will assume that there is no timeout value.
+ */
+ opt = ksock->opt[SO_RCVTIMEO];
+ if (opt == NULL) {
+ return 0;
+ }
+
+ memcpy(&tv, opt->data, opt->len);
+ sched_suspend(NULL, &tv);
+ return 0;
+}
+
+/*
+ * Send data to socket - POSIX send(2) core
+ *
+ * @sockfd: File descriptor that backs this socket
+ * @buf: Buffer containing data to transmit
+ * @size: Size of the buffer
+ * @flags: Optional flags
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+send(int sockfd, const void *buf, size_t size, int flags)
+{
+ struct ksocket *ksock;
+ struct sockbuf *sbuf;
+ struct netbuf *netbuf;
+ size_t tail;
+ int error;
+
+ /* Size cannot be zero */
+ if (size == 0) {
+ return -EINVAL;
+ }
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ sbuf = &ksock->buf;
+ netbuf = &sbuf->buf;
+ mutex_acquire(ksock->mtx, 0);
+
+ /* Make sure we dont overflow */
+ if (netbuf->len > sbuf->watermark) {
+ mutex_release(ksock->mtx);
+ return -ENOBUFS;
+ }
+
+ if (netbuf->len == 0) {
+ sbuf->head = 0;
+ sbuf->tail = 0;
+ }
+
+ /* Clamp the size if needed */
+ if ((netbuf->len + size) > sbuf->watermark) {
+ size = sbuf->watermark - netbuf->len;
+ }
+ if (size == 0) {
+ return -ENOBUFS;
+ }
+
+ /* Copy the new data */
+ tail = sbuf->tail;
+ memcpy(&netbuf->data[tail], buf, size);
+
+ sbuf->tail += size;
+ netbuf->len += size;
+ mutex_release(ksock->mtx);
+ return size;
+}
+
+/*
+ * Recv data from socket - POSIX recv(2) core
+ *
+ * @sockfd: File descriptor that backs this socket
+ * @buf: RX buffer
+ * @size: Size of the buffer
+ * @flags: Optional flags
+ *
+ * Returns length on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+recv(int sockfd, void *buf, size_t len, int flags)
+{
+ struct ksocket *ksock;
+ struct sockbuf *sbuf;
+ struct netbuf *netbuf;
+ size_t head;
+ ssize_t retval = len;
+ int error;
+
+ /* Length cannot be zero */
+ if (len == 0) {
+ return -EINVAL;
+ }
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ sbuf = &ksock->buf;
+ netbuf = &sbuf->buf;
+ mutex_acquire(ksock->mtx, 0);
+
+ /* Is it empty? */
+ if (netbuf->len == 0) {
+ sbuf->head = 0;
+ sbuf->tail = 0;
+ retval = -EAGAIN;
+ goto done;
+ }
+
+ if (len > netbuf->len) {
+ len = netbuf->len;
+ }
+
+ head = sbuf->head;
+ memcpy(buf, &netbuf->data[head], len);
+ sbuf->head = (sbuf->head + len) % NETBUF_LEN;
+done:
+ mutex_release(ksock->mtx);
+ return retval;
+}
+
+/*
+ * POSIX socket(7) core
+ *
+ * @domain: Address family (see AF_*)
+ * @type: Socket type
+ * @protocol: Socket protocol
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+int
+socket(int domain, int type, int protocol)
+{
+ struct ksocket *ksock = NULL;
+ struct sockbuf *sbuf = NULL;
+ struct proc *td = this_td();
+ int fd, error = -1;
+
+ ksock = dynalloc(sizeof(*ksock));
+ if (ksock == NULL) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ memset(ksock, 0, sizeof(*ksock));
+ sbuf = &ksock->buf;
+ sbuf->head = 0;
+ sbuf->tail = 0;
+
+ switch (domain) {
+ case AF_UNIX:
+ {
+ struct sockaddr_un *un;
+
+ un = &ksock->un;
+ sbuf->watermark = NETBUF_LEN;
+
+ /* Set up a path and create a socket file */
+ un->sun_family = domain;
+ snprintf(un->sun_path, sizeof(un->sun_path), "/tmp/%d-sock0", td->pid);
+ fd = socket_mkfile(ksock, un);
+ }
+ return fd;
+ default:
+ error = -EINVAL;
+ break;
+ }
+
+fail:
+ if (ksock != NULL)
+ dynfree(ksock);
+
+ fd_close(fd);
+ return error;
+}
+
+/*
+ * Bind address to socket - POSIX bind(2) core
+ *
+ * @sockfd: File descriptor
+ * @addr: Address to bind
+ * @len: Sockaddr len
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+int
+bind(int sockfd, const struct sockaddr *addr, socklen_t len)
+{
+ struct proc *td;
+ struct ksocket *ksock;
+ struct cmsg_list *clp;
+ int error;
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ kprintf("error=%d\n", error);
+ return error;
+ }
+
+ /* Create the new mutex lock */
+ ksock->mtx = mutex_new("ksocket");
+ if (ksock->mtx == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Mark ourselves as the owner */
+ td = this_td();
+ ksock->owner = td;
+
+ /* Initialize the cmsg list queue */
+ clp = &ksock->cmsg_list;
+ TAILQ_INIT(&clp->list);
+ clp->is_init = 1;
+ return 0;
+}
+
+/*
+ * Set socket options - POSIX setsockopt(3) core
+ *
+ * @sockfd: File descriptor of socket
+ * @level: Protocol level
+ * @v: Options value
+ * @len: Length of data pointed to by 'v'
+ */
+int
+setsockopt(int sockfd, int level, int name, const void *v, socklen_t len)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+ size_t exp_len;
+ int error;
+
+ /* Must have a valid fd */
+ if (sockfd < 0) {
+ return -EBADF;
+ }
+
+ /* Ensure value and length are valid */
+ if (v == NULL || len == 0) {
+ return -EINVAL;
+ }
+
+ /* Verify the name */
+ if (name >= _SO_MAX) {
+ return -EINVAL;
+ }
+
+ /* Grab a new socket */
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ /* Clamp the input length as needed */
+ exp_len = sockopt_lentab[name];
+ if (len > exp_len) {
+ len = exp_len;
+ }
+
+ /*
+ * Here we will grab the socket options. If it is
+ * NULL, we'll need to allocate one.
+ */
+ if ((opt = ksock->opt[name]) == NULL) {
+ opt = dynalloc(sizeof(*opt) + len);
+
+ if (opt == NULL) {
+ return -ENOMEM;
+ }
+
+ opt->len = len;
+ ksock->opt[name] = opt;
+ }
+
+ memcpy(opt->data, v, len);
+ opt->len = len;
+ return 0;
+}
+
+/*
+ * Connect to a socket
+ *
+ * @sockfd: File descriptor to connect
+ * @addr: Address to connect to
+ * @len: Length of address
+ */
+int
+connect(int sockfd, const struct sockaddr *addr, socklen_t len)
+{
+ struct ksocket *ksock;
+ int error = -1;
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ switch (addr->sa_family) {
+ case AF_UNIX:
+ {
+ struct sockaddr_un *un;
+
+ un = (struct sockaddr_un *)addr;
+ if (un->sun_path[0] == '\0') {
+ pr_error("connect: bad socket path\n");
+ return -1;
+ }
+
+ /* Wait for the connection to be established */
+ do {
+ error = connect_domain(sockfd, ksock, un);
+ if (error != 0) {
+ sched_yield();
+ }
+ } while (error != 0);
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Send socket control message - POSIX.1-2008
+ *
+ * @socket: Socket to transmit on
+ * @msg: Further arguments
+ * @flags: Optional flags
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+sendmsg(int socket, const struct msghdr *msg, int flags)
+{
+ struct ksocket *ksock;
+ struct cmsg *cmsg;
+ struct sockaddr_un *un;
+ struct cmsg_list *clp;
+ size_t control_len = 0;
+ int error;
+
+ if ((error = get_ksock(socket, &ksock)) < 0) {
+ return error;
+ }
+
+ /* We cannot do sendmsg() non domain sockets */
+ un = &ksock->un;
+ if (un->sun_family != AF_UNIX) {
+ return -EBADF;
+ }
+
+ control_len = MALIGN(msg->msg_controllen);
+
+ /* Allocate a new cmsg */
+ cmsg = dynalloc(control_len + sizeof(struct cmsg));
+ if (cmsg == NULL) {
+ return -EINVAL;
+ }
+
+ memcpy(cmsg->buf, msg->msg_control, control_len);
+ clp = &ksock->cmsg_list;
+ cmsg->control_len = control_len;
+ TAILQ_INSERT_TAIL(&clp->list, cmsg, link);
+ return 0;
+}
+
+/*
+ * Receive socket control message - POSIX.1‐2017
+ *
+ * @socket: Socket to receive on
+ * @msg: Further arguments
+ * @flags: Optional flags
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+recvmsg(int socket, struct msghdr *msg, int flags)
+{
+ struct ksocket *ksock;
+ struct sockaddr_un *un;
+ struct cmsg *cmsg, *tmp;
+ struct cmsghdr *cmsghdr;
+ struct cmsg_list *clp;
+ uint8_t *fds;
+ int error;
+
+ if (socket < 0) {
+ return -EINVAL;
+ }
+
+ /* Grab the socket descriptor */
+ if ((error = get_ksock(socket, &ksock)) < 0) {
+ return error;
+ }
+
+ /* Must be a unix domain socket */
+ un = &ksock->un;
+ if (un->sun_family != AF_UNIX) {
+ return -EBADF;
+ }
+
+ /* Grab the control message list */
+ clp = &ksock->cmsg_list;
+ cmsg = TAILQ_FIRST(&clp->list);
+
+ /* Empty? */
+ while (cmsg == NULL) {
+ sched_yield();
+ cmsg = TAILQ_FIRST(&clp->list);
+ }
+
+ while (cmsg != NULL) {
+ cmsghdr = &cmsg->hdr;
+
+ /* Check the control message type */
+ switch (cmsghdr->cmsg_type) {
+ case SCM_RIGHTS:
+ {
+ fds = (uint8_t *)CMSG_DATA(cmsghdr);
+ pr_trace("SCM_RIGHTS -> fd %d (from pid %d)\n", fds[0],
+ ksock->owner->pid);
+
+ break;
+ }
+ }
+
+ tmp = cmsg;
+ cmsg = TAILQ_NEXT(cmsg, link);
+
+ TAILQ_REMOVE(&clp->list, tmp, link);
+ dynfree(tmp);
+ }
+
+ return 0;
+}
+
+/*
+ * socket(7) syscall
+ *
+ * arg0: domain
+ * arg1: type
+ * arg2: protocol
+ */
+scret_t
+sys_socket(struct syscall_args *scargs)
+{
+ int domain = scargs->arg0;
+ int type = scargs->arg1;
+ int protocol = scargs->arg2;
+
+ return socket(domain, type, protocol);
+}
+
+/*
+ * bind(2) syscall
+ *
+ * arg0: sockfd
+ * arg1: addr
+ * arg2: len
+ */
+scret_t
+sys_bind(struct syscall_args *scargs)
+{
+ const struct sockaddr *u_addr = (void *)scargs->arg1;
+ struct sockaddr addr_copy;
+ int sockfd = scargs->arg0;
+ int len = scargs->arg2;
+ int error;
+
+ error = copyin(u_addr, &addr_copy, sizeof(addr_copy));
+ if (error < 0) {
+ return error;
+ }
+
+ return bind(sockfd, &addr_copy, len);
+}
+
+/*
+ * recv(2) syscall
+ *
+ * arg0: sockfd
+ * arg1: buf
+ * arg2: size
+ * arg3: flags
+ */
+scret_t
+sys_recv(struct syscall_args *scargs)
+{
+ char buf[NETBUF_LEN];
+ void *u_buf = (void *)scargs->arg1;
+ int sockfd = scargs->arg0;
+ size_t len = scargs->arg2;
+ int error, flags = scargs->arg3;
+
+ if (len > sizeof(buf)) {
+ return -ENOBUFS;
+ }
+
+ for (;;) {
+ error = recv(sockfd, buf, len, flags);
+ if (error <= 0 && error != -EAGAIN) {
+ break;
+ }
+
+ /*
+ * Wait for data to be ready on the socket.
+ * If a less than zero value is returned, don't
+ * handle timeouts.
+ */
+ error = socket_rx_wait(sockfd);
+ if (error < 0) {
+ continue;
+ }
+
+ /* Try one more time, obey timeout */
+ error = recv(sockfd, buf, len, flags);
+ if (error == -EAGAIN) {
+ return error;
+ }
+ break;
+ }
+
+ if (error < 0) {
+ pr_error("sys_recv: recv() fail (fd=%d)\n", sockfd);
+ return error;
+ }
+
+ error = copyout(buf, u_buf, len);
+ return (error == 0) ? len : error;
+}
+
+/*
+ * send(2) syscall
+ *
+ * arg0: sockfd
+ * arg1: buf
+ * arg2: size
+ * arg3: flags
+ */
+scret_t
+sys_send(struct syscall_args *scargs)
+{
+ char buf[NETBUF_LEN];
+ const void *u_buf = (void *)scargs->arg1;
+ int sockfd = scargs->arg0;
+ size_t len = scargs->arg2;
+ int error, flags = scargs->arg3;
+
+ if (len > sizeof(buf)) {
+ return -ENOBUFS;
+ }
+
+ error = copyin(u_buf, buf, len);
+ if (error < 0) {
+ pr_error("sys_send: copyin() failure (fd=%d)\n", sockfd);
+ return error;
+ }
+
+ return send(sockfd, buf, len, flags);
+}
+
+/*
+ * recvmsg(3) syscall
+ *
+ * arg0: socket
+ * arg1: msg
+ * arg2: flags
+ */
+scret_t
+sys_recvmsg(struct syscall_args *scargs)
+{
+ struct msghdr *u_msg = (void *)scargs->arg1;
+ void *u_control, *control = NULL;
+ size_t controllen;
+ struct iovec msg_iov;
+ struct msghdr msg;
+ ssize_t retval;
+ int socket = scargs->arg0;
+ int flags = scargs->arg2;
+ int error;
+
+ /* Read the message header */
+ error = copyin(u_msg, &msg, sizeof(msg));
+ if (error < 0) {
+ pr_error("sys_recvmsg: bad msg\n");
+ return error;
+ }
+
+ /* Grab the message I/O vector */
+ error = uio_copyin(msg.msg_iov, &msg_iov, msg.msg_iovlen);
+ if (error < 0) {
+ return error;
+ }
+
+ /* Save control fields */
+ u_control = msg.msg_control;
+ controllen = msg.msg_controllen;
+
+ /* Allocate a new control field to copy in */
+ control = dynalloc(controllen);
+ msg.msg_control = control;
+ if (msg.msg_control == NULL) {
+ uio_copyin_clean(&msg_iov, msg.msg_iovlen);
+ return -ENOMEM;
+ }
+
+ memset(msg.msg_control, 0, controllen);
+ error = copyin(u_control, msg.msg_control, controllen);
+ if (error < 0) {
+ retval = error;
+ goto done;
+ }
+
+ /*
+ * Now wait until we get a control
+ * message
+ */
+ msg.msg_iov = &msg_iov;
+ for (;;) {
+ retval = recvmsg(socket, &msg, flags);
+ if (retval == 0) {
+ break;
+ }
+
+ sched_yield();
+ }
+done:
+ uio_copyin_clean(&msg_iov, msg.msg_iovlen);
+ dynfree(control);
+ return retval;
+}
+
+/*
+ * sendmsg(3) syscall
+ *
+ * arg0: socket
+ * arg1: msg
+ * arg2: flags
+ */
+scret_t
+sys_sendmsg(struct syscall_args *scargs)
+{
+ struct iovec msg_iov;
+ struct msghdr *u_msg = (void *)scargs->arg1;
+ struct msghdr msg;
+ ssize_t retval;
+ int socket = scargs->arg0;
+ int flags = scargs->arg2;
+ int error;
+
+ /* Read the message header */
+ error = copyin(u_msg, &msg, sizeof(msg));
+ if (error < 0) {
+ pr_error("sys_sendmsg: bad msg\n");
+ return error;
+ }
+
+ /* Grab the message I/O vector */
+ error = uio_copyin(msg.msg_iov, &msg_iov, msg.msg_iovlen);
+ if (error < 0) {
+ return error;
+ }
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &msg_iov;
+
+ for (;;) {
+ retval = sendmsg(socket, &msg, flags);
+ if (retval == 0) {
+ break;
+ }
+ sched_yield();
+ }
+ uio_copyin_clean(&msg_iov, msg.msg_iovlen);
+ return retval;
+}
+
+/*
+ * connect(3) syscall
+ *
+ * arg0: sockfd
+ * arg1: address
+ * arg2: len
+ */
+scret_t
+sys_connect(struct syscall_args *scargs)
+{
+ char buf[256];
+ struct sockaddr *u_addr = (void *)scargs->arg1;
+ struct sockaddr *sockaddr;
+ int error;
+ int sockfd = scargs->arg0;
+ socklen_t len = scargs->arg2;
+
+ if (len >= sizeof(buf)) {
+ pr_error("sys_connect: address too big\n");
+ return -E2BIG;
+ }
+
+ error = copyin(u_addr, buf, len);
+ if (error < 0) {
+ pr_error("sys_connect: bad 'address'\n");
+ return error;
+ }
+
+ sockaddr = (struct sockaddr *)buf;
+ return connect(sockfd, sockaddr, len);
+}
+
+/*
+ * POSIX setsockopt(3) syscall
+ *
+ * arg0: sockfd
+ * arg1: level
+ * arg2: name
+ * arg3: data
+ * arg4: len
+ */
+scret_t
+sys_setsockopt(struct syscall_args *scargs)
+{
+ int sockfd = scargs->arg0;
+ int level = scargs->arg1;
+ int name = scargs->arg2;
+ void *u_data = (void *)scargs->arg3;
+ socklen_t len = scargs->arg4;
+ void *data;
+ size_t exp_len;
+ int retval;
+
+ /* Verify that the name is correct */
+ if (name >= _SO_MAX) {
+ return -EINVAL;
+ }
+
+ /* Clamp length as needed */
+ exp_len = sockopt_lentab[name];
+ if (len > exp_len) {
+ len = exp_len;
+ }
+
+ data = dynalloc(len);
+ if (data == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Grab data from userland */
+ retval = copyin(u_data, data, len);
+ if (retval < 0) {
+ dynfree(data);
+ return retval;
+ }
+
+ retval = setsockopt(sockfd, level, name, data, len);
+ dynfree(data);
+ return retval;
+}
+
+static struct vops socket_vops = {
+ .read = NULL,
+ .write = NULL,
+ .reclaim = socket_reclaim,
+};
diff --git a/sys/kern/kern_spawn.c b/sys/kern/kern_spawn.c
index cb898dc..12f3d16 100644
--- a/sys/kern/kern_spawn.c
+++ b/sys/kern/kern_spawn.c
@@ -27,6 +27,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include <sys/spawn.h>
+#include <sys/wait.h>
#include <sys/proc.h>
#include <sys/exec.h>
#include <sys/mman.h>
@@ -34,7 +36,6 @@
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/syscall.h>
-#include <sys/atomic.h>
#include <sys/signal.h>
#include <sys/limits.h>
#include <sys/sched.h>
@@ -44,10 +45,17 @@
#define pr_trace(fmt, ...) kprintf("spawn: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
-static volatile size_t nthreads = 0;
+#define ARGVP_MAX (ARG_MAX / sizeof(void *))
+static size_t next_pid = 1;
+
+/*
+ * TODO: envp
+ */
struct spawn_args {
char path[PATH_MAX];
+ char argv_blk[ARG_MAX];
+ char *argv[ARGVP_MAX];
};
static inline void
@@ -66,29 +74,54 @@ spawn_thunk(void)
struct proc *cur;
struct execve_args execve_args;
struct spawn_args *args;
- char *argv[] = { NULL, NULL };
char *envp[] = { NULL };
cur = this_td();
- args = cur->spawn_data;
+ args = cur->data;
path = args->path;
+ memset(pathbuf, 0, sizeof(pathbuf));
memcpy(pathbuf, path, strlen(path));
- argv[0] = (char *)pathbuf;
- execve_args.pathname = argv[0];
- execve_args.argv = argv;
+ execve_args.pathname = pathbuf;
+ execve_args.argv = (char **)&args->argv[0];
execve_args.envp = envp;
-
path = NULL;
- dynfree(args);
if (execve(cur, &execve_args) != 0) {
pr_error("execve failed, aborting\n");
- exit1(this_td());
+ exit1(this_td(), 0);
}
__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
*
@@ -108,8 +141,8 @@ pid_t
spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **newprocp)
{
struct proc *newproc;
- struct mmap_lgdr *mlgdr;
int error;
+ pid_t pid;
newproc = dynalloc(sizeof(*newproc));
if (newproc == NULL) {
@@ -118,19 +151,10 @@ spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **new
return -ENOMEM;
}
- mlgdr = dynalloc(sizeof(*mlgdr));
- if (mlgdr == NULL) {
- dynfree(newproc);
- try_free_data(p);
- pr_error("could not alloc proc mlgdr (-ENOMEM)\n");
- return -ENOMEM;
- }
-
memset(newproc, 0, sizeof(*newproc));
error = md_spawn(newproc, cur, (uintptr_t)func);
if (error < 0) {
dynfree(newproc);
- dynfree(mlgdr);
try_free_data(p);
pr_error("error initializing proc\n");
return error;
@@ -146,21 +170,19 @@ spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **new
cur->flags |= PROC_LEAFQ;
}
- /* Add to parent leafq */
- TAILQ_INSERT_TAIL(&cur->leafq, newproc, leaf_link);
- atomic_inc_int(&cur->nleaves);
- newproc->parent = cur;
- newproc->spawn_data = p;
-
- /* Initialize the mmap ledger */
- mlgdr->nbytes = 0;
- RBT_INIT(lgdr_entries, &mlgdr->hd);
- newproc->mlgdr = mlgdr;
+ error = proc_init(newproc, cur);
+ if (error < 0) {
+ dynfree(newproc);
+ try_free_data(p);
+ pr_error("error initializing proc\n");
+ return error;
+ }
- newproc->pid = ++nthreads;
- signals_init(newproc);
+ newproc->data = p;
+ newproc->pid = next_pid++;
sched_enqueue_td(newproc);
- return newproc->pid;
+ pid = newproc->pid;
+ return pid;
}
/*
@@ -177,6 +199,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;
}
@@ -186,20 +211,48 @@ 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: Optional flags (`flags')
+ * arg1: Argv
+ * arg2: Envp (TODO)
+ * arg3: Optional flags (`flags')
*/
scret_t
sys_spawn(struct syscall_args *scargs)
{
struct spawn_args *args;
- const char *u_path;
+ char *path;
+ const char *u_path, **u_argv;
+ const char *u_p = NULL;
struct proc *td;
int flags, error;
+ size_t len, bytes_copied = 0;
+ size_t argv_i = 0;
td = this_td();
- flags = scargs->arg1;
u_path = (const char *)scargs->arg0;
+ u_argv = (const char **)scargs->arg1;
+ flags = scargs->arg3;
args = dynalloc(sizeof(*args));
if (args == NULL) {
@@ -212,5 +265,30 @@ sys_spawn(struct syscall_args *scargs)
return error;
}
+ memset(args->argv, 0, ARG_MAX);
+ for (size_t i = 0; i < ARG_MAX - 1; ++i) {
+ error = copyin(&u_argv[argv_i], &u_p, sizeof(u_p));
+ if (error < 0) {
+ dynfree(args);
+ return error;
+ }
+ if (u_p == NULL) {
+ args->argv[argv_i++] = NULL;
+ break;
+ }
+
+ path = &args->argv_blk[i];
+ error = copyinstr(u_p, path, ARG_MAX - bytes_copied);
+ if (error < 0) {
+ dynfree(args);
+ return error;
+ }
+
+ args->argv[argv_i++] = &args->argv_blk[i];
+ len = strlen(path);
+ bytes_copied += (len + 1);
+ i += len;
+ }
+
return spawn(td, spawn_thunk, args, flags, NULL);
}
diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c
index 8603fd5..a9a56ac 100644
--- a/sys/kern/kern_stub.c
+++ b/sys/kern/kern_stub.c
@@ -40,8 +40,10 @@ sigfpe_default(int signo)
static struct proc *td;
td = this_td();
- kprintf("Floating point exception (pid=%d)\n", td->pid);
- exit1(td);
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Floating point exception (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ exit1(td, 0);
}
void
@@ -50,8 +52,10 @@ sigkill_default(int signo)
static struct proc *td;
td = this_td();
- kprintf("Terminated (pid=%d)\n", td->pid);
- exit1(td);
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Terminated (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ exit1(td, 0);
}
void
@@ -60,8 +64,22 @@ sigsegv_default(int signo)
static struct proc *td;
td = this_td();
- kprintf("Segmentation fault (pid=%d)\n", td->pid);
- exit1(td);
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Segmentation fault (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ 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
@@ -75,3 +93,9 @@ dev_nowrite(void)
{
return -ENOTSUP;
}
+
+int
+dev_nobsize(void)
+{
+ return -ENOTSUP;
+}
diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c
index f437ec7..8a08f33 100644
--- a/sys/kern/kern_subr.c
+++ b/sys/kern/kern_subr.c
@@ -29,9 +29,12 @@
#include <sys/proc.h>
#include <sys/types.h>
+#include <sys/param.h>
#include <sys/errno.h>
+#include <sys/mman.h>
#include <sys/exec.h>
#include <sys/systm.h>
+#include <vm/vm.h>
#include <string.h>
/*
@@ -45,6 +48,8 @@ static bool
check_uaddr(const void *uaddr)
{
vaddr_t stack_start, stack_end;
+ struct mmap_lgdr *lp;
+ struct mmap_entry find, *res;
struct exec_prog exec;
struct proc *td;
uintptr_t addr;
@@ -61,6 +66,22 @@ check_uaddr(const void *uaddr)
if (addr >= stack_start && addr <= stack_end)
return true;
+ /* Try to grab the mmap ledger */
+ if ((lp = td->mlgdr) == NULL) {
+ return false;
+ }
+
+ /*
+ * Now give an attempt at looking through the
+ * mmap ledger. Perhaps this memory was allocated
+ * in the user heap?
+ */
+ find.va_start = ALIGN_DOWN(addr, DEFAULT_PAGESIZE);
+ res = RBT_FIND(lgdr_entries, &lp->hd, &find);
+ if (res != NULL) {
+ return true;
+ }
+
return false;
}
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index 57b27d0..7660f1f 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -28,19 +28,20 @@
*/
#include <sys/types.h>
+#include <sys/mutex.h>
#include <sys/systm.h>
#include <sys/errno.h>
+#include <sys/sched.h>
#include <sys/atomic.h>
#include <sys/syslog.h>
#include <sys/spinlock.h>
+#include <machine/cdefs.h>
#include <dev/timer.h>
+#include <string.h>
#define pr_trace(fmt, ...) kprintf("synch: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
-/* XXX: Be very careful with this */
-static struct spinlock __syslock;
-
/*
* Returns 0 on success, returns non-zero value
* on timeout/failure.
@@ -80,7 +81,10 @@ spinlock_usleep(struct spinlock *lock, size_t usec_max)
void
spinlock_acquire(struct spinlock *lock)
{
- while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE));
+ sched_preempt_set(false);
+ while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE)) {
+ md_pause();
+ }
}
/*
@@ -104,35 +108,66 @@ spinlock_try_acquire(struct spinlock *lock)
return 1;
}
- while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE));
- return 0;
+ return __atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE);
}
void
spinlock_release(struct spinlock *lock)
{
__atomic_clear(&lock->lock, __ATOMIC_RELEASE);
+ sched_preempt_set(true);
}
/*
- * Attempt to hold the system-wide lock, returns 1
- * if already held.
- *
- * XXX: Only use for CRITICAL code sections.
+ * Create a new mutex lock object
*/
-int
-syslock(void)
+struct mutex *
+mutex_new(const char *name)
{
- return spinlock_try_acquire(&__syslock);
+ struct mutex *mtx;
+ size_t namelen;
+
+ mtx = dynalloc(sizeof(*mtx));
+ if (mtx == NULL) {
+ return NULL;
+ }
+
+ mtx->lock = 0;
+ namelen = strlen(name);
+
+ /* Don't overflow the name buffer */
+ if (namelen >= MUTEX_NAME_LEN) {
+ namelen = MUTEX_NAME_LEN - 1;
+ }
+
+ memcpy(mtx->name, name, namelen);
+ return mtx;
}
/*
- * Release the system-wide lock
+ * Acquire a mutex
*
- * XXX: Only use for CRITICAL code sections.
+ * @mtx: Mutex to acquire
+ * @flags: Optional flags
*/
+int
+mutex_acquire(struct mutex *mtx, int flags)
+{
+ while (__atomic_test_and_set(&mtx->lock, __ATOMIC_ACQUIRE)) {
+ sched_yield();
+ }
+
+ return 0;
+}
+
+void
+mutex_release(struct mutex *mtx)
+{
+ __atomic_clear(&mtx->lock, __ATOMIC_RELEASE);
+}
+
void
-sysrel(void)
+mutex_free(struct mutex *mtx)
{
- spinlock_release(&__syslock);
+ dynfree(mtx);
}
diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c
index 249a04a..c352b9c 100644
--- a/sys/kern/kern_syscall.c
+++ b/sys/kern/kern_syscall.c
@@ -29,10 +29,16 @@
#include <sys/syscall.h>
#include <sys/sysctl.h>
+#include <sys/socket.h>
#include <sys/reboot.h>
#include <sys/types.h>
+#include <sys/ucred.h>
+#include <sys/disk.h>
+#include <sys/time.h>
+#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/vfs.h>
+#include <sys/krq.h>
scret_t(*g_sctab[])(struct syscall_args *) = {
NULL, /* SYS_none */
@@ -45,6 +51,26 @@ scret_t(*g_sctab[])(struct syscall_args *) = {
sys_write, /* SYS_write */
sys_spawn, /* SYS_spawn */
sys_reboot, /* SYS_reboot */
+ sys_mmap, /* SYS_mmap */
+ sys_munmap, /* SYS_munap */
+ sys_access, /* SYS_access */
+ sys_lseek, /* SYS_lseek */
+ sys_sleep, /* SYS_sleep */
+ sys_inject, /* SYS_inject */
+ sys_getpid, /* SYS_getpid */
+ sys_getppid, /* SYS_getppid */
+ sys_setuid, /* SYS_setuid */
+ sys_getuid, /* SYS_getuid */
+ sys_waitpid, /* SYS_waitpid */
+ sys_socket, /* SYS_socket */
+ sys_bind, /* SYS_bind */
+ sys_recv, /* SYS_recv */
+ sys_send, /* SYS_send */
+ sys_sendmsg, /* SYS_sendmsg */
+ sys_recvmsg, /* SYS_recvmsg */
+ sys_connect, /* SYS_connect */
+ sys_setsockopt, /* SYS_setsockopt */
+ sys_disk, /* SYS_disk */
};
const size_t MAX_SYSCALLS = NELEM(g_sctab);
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 7679aa1..1f5e578 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -33,12 +33,16 @@
#include <sys/errno.h>
#include <sys/systm.h>
#include <vm/dynalloc.h>
+#include <vm/vm.h>
#include <string.h>
#define HYRA_RELEASE "Hyra/" HYRA_ARCH " " \
HYRA_VERSION " " \
HYRA_BUILDDATE
+extern size_t g_nthreads;
+static uint32_t pagesize = DEFAULT_PAGESIZE;
+static char machine[] = HYRA_ARCH;
static char hyra[] = "Hyra";
static char hyra_version[] = HYRA_VERSION;
static char osrelease[] = HYRA_RELEASE;
@@ -49,10 +53,20 @@ static char osrelease[] = HYRA_RELEASE;
* allocated through dynalloc(9).
*/
static struct sysctl_entry common_optab[] = {
+ /* 'kern.*' */
[KERN_OSTYPE] = { KERN_OSTYPE, SYSCTL_OPTYPE_STR_RO, hyra },
[KERN_OSRELEASE] = { KERN_OSRELEASE, SYSCTL_OPTYPE_STR_RO, &osrelease },
[KERN_VERSION] = { KERN_VERSION, SYSCTL_OPTYPE_STR_RO, &hyra_version },
- [KERN_VCACHE_TYPE] = { KERN_VCACHE_TYPE, SYSCTL_OPTYPE_STR, NULL }
+ [KERN_VCACHE_TYPE] = { KERN_VCACHE_TYPE, SYSCTL_OPTYPE_STR, NULL },
+ [KERN_HOSTNAME] = { KERN_HOSTNAME, SYSCTL_OPTYPE_STR, NULL },
+
+ /* 'hw.*' */
+ [HW_PAGESIZE] = { HW_PAGESIZE, SYSCTL_OPTYPE_INT_RO, &pagesize },
+ [HW_NCPU] = { HW_NCPU, SYSCTL_OPTYPE_INT, NULL },
+ [HW_MACHINE] = {HW_MACHINE, SYSCTL_OPTYPE_STR_RO, &machine },
+
+ /* 'proc.*' */
+ [PROC_COUNT] = { PROC_COUNT, SYSCTL_OPTYPE_INT_RO, &g_nthreads }
};
static int
@@ -91,19 +105,18 @@ static int
do_sysctl(struct sysctl_args *args)
{
struct sysctl_args new_args;
- size_t name_len, oldlenp;
+ size_t name_len = 1, oldlenp = 0;
int *name = NULL;
void *oldp = NULL, *newp = NULL;
- int retval = 0;
-
- if (args->oldlenp == NULL) {
- return -EINVAL;
- }
-
- name_len = args->nlen;
- retval = copyin(args->oldlenp, &oldlenp, sizeof(oldlenp));
- if (retval != 0) {
- goto done;
+ int retval = 0, have_oldlen = 0;
+
+ if (args->oldlenp != NULL) {
+ have_oldlen = 1;
+ name_len = args->nlen;
+ retval = copyin(args->oldlenp, &oldlenp, sizeof(oldlenp));
+ if (retval != 0) {
+ goto done;
+ }
}
/* Copy in newp if it is set */
@@ -124,25 +137,30 @@ do_sysctl(struct sysctl_args *args)
return retval;
}
- oldp = dynalloc(oldlenp);
- retval = copyin(args->oldp, oldp, oldlenp);
- if (retval != 0) {
- return retval;
+ if (oldlenp != 0) {
+ oldp = dynalloc(oldlenp);
+ retval = copyin(args->oldp, oldp, oldlenp);
+ if (retval != 0) {
+ return retval;
+ }
}
/* Prepare the arguments for the sysctl call */
new_args.name = name;
new_args.nlen = name_len;
new_args.oldp = oldp;
- new_args.oldlenp = &oldlenp;
+ new_args.oldlenp = (have_oldlen) ? &oldlenp : NULL;
new_args.newp = newp;
+ new_args.newlen = args->newlen;
retval = sysctl(&new_args);
if (retval != 0) {
goto done;
}
- copyout(oldp, args->oldp, oldlenp);
+ if (oldlenp != 0) {
+ copyout(oldp, args->oldp, oldlenp);
+ }
done:
if (name != NULL)
dynfree(name);
@@ -154,6 +172,33 @@ done:
return retval;
}
+/*
+ * Clear a writable sysctl string variable to the
+ * value of "(undef)"
+ *
+ * @name: Name to clear
+ */
+int
+sysctl_clearstr(int name)
+{
+ struct sysctl_args args;
+ char val[] = "(undef)";
+ int error;
+
+ args.name = &name;
+ args.nlen = 1;
+ args.oldlenp = 0;
+ args.oldp = NULL;
+ args.newp = val;
+ args.newlen = sizeof(val);
+
+ if ((error = sysctl(&args)) != 0) {
+ return error;
+ }
+
+ return 0;
+}
+
int
sysctl(struct sysctl_args *args)
{
diff --git a/sys/kern/kern_syslog.c b/sys/kern/kern_syslog.c
index 665734d..c7f51f7 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,21 +45,105 @@
#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)
{
- const char *p = s;
+ const char *p;
+ size_t l;
- while (len--) {
- cons_putch(&g_root_scr, *p);
- if (SERIAL_DEBUG) {
+ if (SERIAL_DEBUG) {
+ p = s;
+ l = len;
+ while (l--) {
serial_putc(*p);
+ ++p;
}
- ++p;
}
+
+ 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);
}
/*
@@ -101,7 +190,6 @@ kprintf(const char *fmt, ...)
}
}
- spinlock_acquire(&lock);
if (use_timestamp) {
syslog_write(timestamp, strlen(timestamp));
}
@@ -110,5 +198,38 @@ kprintf(const char *fmt, ...)
vkprintf(fmt_p, &ap);
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/sys/include/net/if_ether.h b/sys/kern/kern_time.c
index a8fcfa5..e741157 100644
--- a/sys/include/net/if_ether.h
+++ b/sys/kern/kern_time.c
@@ -27,48 +27,47 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef _IF_ETHER_H_
-#define _IF_ETHER_H_
-
-#include <sys/cdefs.h>
#include <sys/types.h>
-#include <net/if.h>
-
-#define MACADDR_LEN 6
-
-struct __packed ether_frame {
- uint8_t sync[7]; /* Preamble (sync stuff) */
- uint8_t spd; /* Start frame delimiter */
- uint8_t macd[6]; /* MAC destination */
- uint8_t macs[6]; /* MAC source */
- uint16_t type; /* Protocol type */
- char payload[1]; /* sized @ 1+n */
-};
+#include <sys/time.h>
+#include <sys/syscall.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/cdefs.h>
+#include <dev/timer.h>
+#include <machine/cdefs.h>
/*
- * Used by the driver to buffer packets.
+ * arg0: Timespec
+ * arg1: Remaining timeval
*/
-struct etherbuf {
- struct ether_frame *frp;
- off_t head;
- off_t tail;
- size_t cap; /* In entries */
-};
+scret_t
+sys_sleep(struct syscall_args *scargs)
+{
+ struct timespec ts;
+ struct timer tmr;
+ size_t timeout_msec;
+ tmrr_status_t status;
+ int error;
-/*
- * Ethernet device
- *
- * if_ether: E
- * driver: D
- *
- * @if_name: Interface name.
- * @tx: Transmit packets (D->E)
- */
-struct etherdev {
- char if_name[IFNAMESIZ];
- struct etherbuf *buf;
- ssize_t(*tx)(struct etherdev *ep, const void *buf, size_t len);
- char mac_addr[MACADDR_LEN];
-};
+ error = copyin((void *)scargs->arg0, &ts, sizeof(ts));
+ if (error < 0) {
+ return error;
+ }
+
+ if (ts.tv_nsec >= 1000000000) {
+ return -EINVAL;
+ }
+
+ status = req_timer(TIMER_GP, &tmr);
+ if (__unlikely(status != TMRR_SUCCESS)) {
+ return -ENOTSUP;
+ }
+ if (__unlikely(tmr.msleep == NULL)) {
+ return -ENOTSUP;
+ }
-#endif /* !_IF_ETHER_H_ */
+ timeout_msec = ts.tv_nsec / 1000000;
+ timeout_msec += ts.tv_sec * 1000;
+ tmr.msleep(timeout_msec);
+ return 0;
+}
diff --git a/sys/kern/kern_uio.c b/sys/kern/kern_uio.c
new file mode 100644
index 0000000..5cdd8f6
--- /dev/null
+++ b/sys/kern/kern_uio.c
@@ -0,0 +1,271 @@
+/*
+ * 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/limits.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/filedesc.h>
+
+/*
+ * Clean up after a UIO copyin() operation
+ *
+ * @iov: iovec copy to clean up
+ * @iovcnt: Number of iovec entries
+ */
+void
+uio_copyin_clean(struct iovec *iov, int iovcnt)
+{
+ for (int i = 0; i < iovcnt; ++i) {
+ if (iov[i].iov_base == NULL) {
+ continue;
+ }
+
+ dynfree(iov[i].iov_base);
+ iov[i].iov_base = NULL;
+ }
+}
+
+/*
+ * Read data into POSIX.1‐2017 iovec
+ *
+ * @filedes: File descriptor number
+ * @iov: I/O vector to read file into
+ * @iovnt: Number of I/O vectors
+ */
+ssize_t
+readv(int filedes, const struct iovec *iov, int iovcnt)
+{
+ void *base;
+ size_t len;
+ ssize_t tmp, bytes_read = 0;
+
+ if (filedes < 0) {
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure that this conforms to our max
+ * iovec limit.
+ */
+ if (iovcnt > IOVEC_MAX) {
+ return -EINVAL;
+ }
+
+ /*
+ * Go through each I/O vector and read a chunk
+ * of data into one.
+ */
+ for (int i = 0; i < iovcnt; ++i) {
+ base = iov[i].iov_base;
+ len = iov[i].iov_len;
+
+ /*
+ * If we encounter a base that is NULL,
+ * or if the length to read is an invalid
+ * value of zero. We can just assume this
+ * is some sort of weird list termination?
+ */
+ if (base == NULL || len == 0) {
+ break;
+ }
+
+ /* Read the file into this base */
+ tmp = fd_read(filedes, base, len);
+
+ /* Did anything go wrong? */
+ if (tmp < 0) {
+ return tmp;
+ }
+
+ /* No more data */
+ if (tmp == 0) {
+ break;
+ }
+
+ /* Read more bytes */
+ bytes_read += tmp;
+ }
+
+ return bytes_read;
+}
+
+/*
+ * Write data from POSIX.1‐2017 iovec
+ *
+ * @filedes: File descriptor number
+ * @iov: I/O vector to write to file
+ * @iovnt: Number of I/O vectors
+ */
+ssize_t
+writev(int filedes, const struct iovec *iov, int iovcnt)
+{
+ void *base;
+ size_t len;
+ ssize_t bytes_written = 0;
+ ssize_t tmp;
+
+ if (filedes < 0) {
+ return -EINVAL;
+ }
+
+ /*
+ * Are we within the limits? Return an
+ * error if not.
+ */
+ if (iovcnt > IOVEC_MAX) {
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < iovcnt; ++i) {
+ base = iov[i].iov_base;
+ len = iov[i].iov_len;
+
+ /*
+ * These are invalid, whatever these are,
+ * terminate our walk through.
+ */
+ if (base == NULL || len == 0) {
+ break;
+ }
+
+ /* Write the data from the iovec */
+ tmp = fd_write(filedes, base, len);
+
+ /* Was there an error? */
+ if (tmp < 0) {
+ return tmp;
+ }
+
+ /* No more data to read? */
+ if (tmp == 0) {
+ break;
+ }
+
+ bytes_written += tmp;
+ }
+
+ return bytes_written;
+}
+
+/*
+ * Validate iovecs coming in from userland
+ * and copy it to a kernel buffer.
+ *
+ * XXX: A new buffer is allocated in k_iov[i]->iov_base
+ * and must be freed with dynfree() after use.
+ *
+ * @u_iov: Userspace source iovecs
+ * @k_iov: Kernel destination iovec
+ * @iovcnt: Number of iovecs to copy
+ */
+int
+uio_copyin(const struct iovec *u_iov, struct iovec *k_iov, int iovcnt)
+{
+ struct iovec *iov_dest;
+ const struct iovec *iov_src;
+ size_t len;
+ void *old_base;
+ int error;
+
+ if (u_iov == NULL || k_iov == NULL) {
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < iovcnt; ++i) {
+ iov_dest = &k_iov[i];
+ iov_src = &u_iov[i];
+ error = copyin(iov_src, iov_dest, sizeof(*iov_dest));
+
+ if (error < 0) {
+ uio_copyin_clean(iov_dest, i + 1);
+ return error;
+ }
+
+ /*
+ * Save the old base so that we may copy the data to
+ * the new kernel buffer. First we'd need to allocate
+ * one of course.
+ */
+ old_base = iov_dest->iov_base;
+ len = iov_dest->iov_len;
+ iov_dest->iov_base = dynalloc(len);
+
+ /* Did it fail? */
+ if (iov_dest->iov_base == NULL) {
+ uio_copyin_clean(iov_dest, i + 1);
+ return -ENOMEM;
+ }
+
+ /* Copy actual data in */
+ error = copyin(old_base, iov_dest->iov_base, len);
+ if (error < 0) {
+ uio_copyin_clean(iov_dest, i + 1);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Validate iovecs going out from kernel space (us)
+ * before actually sending it out.
+ *
+ * @k_iov: Kernel iovec to copyout
+ * @u_iov: Userspace destination
+ * @iovcnt: Number of iovecs
+ */
+int
+uio_copyout(const struct iovec *k_iov, struct iovec *u_iov, int iovcnt)
+{
+ struct iovec iov_shadow, *iov_dest;
+ const struct iovec *iov_src;
+ int error;
+
+ for (int i = 0; i < iovcnt; ++i) {
+ iov_dest = &u_iov[i];
+ iov_src = &k_iov[i];
+
+ /* Grab a shadow copy */
+ error = copyin(iov_src, &iov_shadow, sizeof(iov_shadow));
+ if (error < 0) {
+ return error;
+ }
+
+ /* Copy out actual data */
+ error = copyout(iov_src->iov_base, iov_dest->iov_base, iov_dest->iov_len);
+ if (error < 0) {
+ return error;
+ }
+ }
+
+ return 0;
+}
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/kern/kern_work.c b/sys/kern/kern_work.c
new file mode 100644
index 0000000..918af89
--- /dev/null
+++ b/sys/kern/kern_work.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/panic.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/syslog.h>
+#include <sys/workqueue.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("workq: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+extern struct proc g_proc0;
+
+/*
+ * The workqueue cookie value that is used for
+ * verifying if a workqueue object is properly
+ * set up or not.
+ */
+#define WQ_COOKIE 0xFC0B
+
+/*
+ * A worker services work in the queue
+ * and there is one per workqueue.
+ */
+static void
+workqueue_worker(void)
+{
+ struct proc *td;
+ struct workqueue *wqp;
+ struct work *wp;
+
+ td = this_td();
+ if ((wqp = td->data) == NULL) {
+ panic("no workqueue in thread\n");
+ }
+
+ /*
+ * Weird things can happen, just be careful
+ * here...
+ */
+ if (wqp->cookie != WQ_COOKIE) {
+ panic("bad WQ_COOKIE in worker\n");
+ }
+
+ for (;;) {
+ mutex_acquire(wqp->lock, 0);
+ wp = TAILQ_FIRST(&wqp->work);
+
+ /* Try again later if empty */
+ if (wp == NULL) {
+ mutex_release(wqp->lock);
+ sched_yield();
+ continue;
+ }
+
+ wp->func(wqp, wp);
+ TAILQ_REMOVE(&wqp->work, wp, link);
+
+ /*
+ * Decrement the amount of work that is
+ * left to get done. Check for underflows
+ * which should not happen unless something
+ * clobbers the fields.
+ */
+ if ((--wqp->nwork) < 0) {
+ panic("wqp nwork underflow\n");
+ }
+
+ mutex_release(wqp->lock);
+ sched_yield();
+ }
+}
+
+/*
+ * Allocates a new work queue that may be used
+ * to hold queued up tasks.
+ *
+ * @name: Name to give the workqueue
+ * @max_work: Maximum number of jobs to be added
+ * @ipl: IPL that the work must operate in
+ *
+ * Returns a pointer to the new workqueue on success,
+ * otherwise a value of NULL is returned.
+ */
+struct workqueue *
+workqueue_new(const char *name, size_t max_work, int ipl)
+{
+ struct workqueue *wqp;
+ struct proc *td;
+
+ td = this_td();
+ if (__unlikely(td == NULL)) {
+ pr_error("no thread in workqueue_new()\n");
+ return NULL;
+ }
+
+ wqp = dynalloc(sizeof(*wqp));
+ if (wqp == NULL) {
+ return NULL;
+ }
+
+ wqp->name = strdup(name);
+ TAILQ_INIT(&wqp->work);
+ wqp->ipl = ipl;
+ wqp->max_work = max_work;
+ wqp->nwork = 0;
+ wqp->cookie = WQ_COOKIE;
+ wqp->lock = mutex_new(wqp->name);
+
+ /*
+ * We need to spawn the work thread which
+ * is behind the management of this specific
+ * workqueue. It typically does something like
+ * dequeuing at the head of the workqueue, performing
+ * the work, cleaning up as needed and dequeuing the
+ * next and waiting if there are none yet.
+ */
+ spawn(
+ &g_proc0, workqueue_worker,
+ wqp, 0,
+ &wqp->worktd
+ );
+
+ return wqp;
+}
+
+/*
+ * Enqueue a work item onto a specific
+ * workqueue.
+ *
+ * @wqp: Pointer to specific workqueue
+ * @name: Name to set for work unit
+ * @wp: Pointer to work that should be enqueued
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+int
+workqueue_enq(struct workqueue *wqp, const char *name, struct work *wp)
+{
+ if (wqp == NULL || wp == NULL) {
+ return -EINVAL;
+ }
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ /* Verify that we have a valid workqueue */
+ if (__unlikely(wqp->cookie != WQ_COOKIE)) {
+ panic("workq: bad cookie on work enqueue\n");
+ }
+
+ wp->name = strdup(name);
+ mutex_acquire(wqp->lock, 0);
+
+ /*
+ * If we have reached the max amount of jobs
+ * that we can enqueue here, just log it and
+ * bail.
+ */
+ if (wqp->nwork >= wqp->max_work) {
+ pr_error("max jobs reached for '%s'\n", wqp->name);
+ mutex_release(wqp->lock);
+ return -EAGAIN;
+ }
+
+ TAILQ_INSERT_TAIL(&wqp->work, wp, link);
+ ++wqp->nwork;
+ mutex_release(wqp->lock);
+ return 0;
+}
+
+/*
+ * Destroy a workqueue and free resources
+ * associated with it.
+ *
+ * @wqp: Pointer to workqueue to destroy
+ *
+ * Returns zero on success, otherwise a less
+ * than zero value is returned.
+ */
+int
+workqueue_destroy(struct workqueue *wqp)
+{
+ if (wqp == NULL) {
+ return -EINVAL;
+ }
+
+ /* Should not happen but just make sure */
+ if (__unlikely(wqp->cookie != WQ_COOKIE)) {
+ panic("workq: bad cookie on destroy\n");
+ }
+
+ /* Free the name if we have it */
+ if (wqp->name != NULL) {
+ dynfree(wqp->name);
+ }
+
+ if (wqp->lock != NULL) {
+ mutex_free(wqp->lock);
+ }
+
+ /* Brutally murder any workthreads */
+ if (wqp->worktd != NULL) {
+ exit1(wqp->worktd, 0);
+ wqp->worktd = NULL;
+ }
+
+ /*
+ * Zero before we free for security reasons, we
+ * don't really know what will be queued up but
+ * for certain things, it is best if we make it
+ * as if it never existed in the first place.
+ *
+ * XXX: There is no need to free the workqueue here as
+ * we had to pass it to spawn() to run the worker.
+ *
+ * During an exit, spawn() will free the thread data
+ * meaning this is already cleaned up.
+ */
+ memset(wqp, 0, sizeof(*wqp));
+ return 0;
+}
+
+/*
+ * Cleanup after work
+ *
+ * @wp: Work to clean up
+ */
+int
+work_destroy(struct work *wp)
+{
+ if (wp == NULL) {
+ return -EINVAL;
+ }
+
+ if (wp->name != NULL) {
+ dynfree(wp->name);
+ }
+
+ return 0;
+}
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c
index 8c1bc74..bc7f8b0 100644
--- a/sys/kern/vfs_init.c
+++ b/sys/kern/vfs_init.c
@@ -37,7 +37,8 @@ struct vnode *g_root_vnode = NULL;
static struct fs_info fs_list[] = {
{MOUNT_RAMFS, &g_initramfs_vfsops, 0, 0},
{MOUNT_DEVFS, &g_devfs_vfsops, 0, 0},
- {MOUNT_CTLFS, &g_ctlfs_vfsops, 0, 0}
+ {MOUNT_CTLFS, &g_ctlfs_vfsops, 0, 0},
+ {MOUNT_TMPFS, &g_tmpfs_vfsops, 0, 0}
};
void
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index d04b812..7320102 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -29,6 +29,7 @@
#include <sys/namei.h>
#include <sys/vnode.h>
+#include <sys/param.h>
#include <sys/mount.h>
#include <sys/errno.h>
#include <vm/dynalloc.h>
@@ -118,20 +119,60 @@ vfs_get_fname_at(const char *path, size_t idx)
}
/*
+ * Count the number of components that exist within
+ * a path minus the delimiter as well as any redundant
+ * delimiters.
+ *
+ * @path: Path to count
+ */
+static uint8_t
+namei_num_cnp(const char *path)
+{
+ const char *p = path;
+ uint8_t count = 0;
+
+ while (*p != '\0') {
+ /* Skip redundant delimiters */
+ if (p[0] == '/' && p[1] == '/') {
+ ++p;
+ continue;
+ }
+
+ if (*p == '/') {
+ ++count;
+ }
+ ++p;
+ }
+
+ /* Don't count leading slash */
+ if (*(p - 1) == '/') {
+ --count;
+ }
+
+ return count;
+}
+
+/*
* Search for a path within a mountpoint.
*
* @mp: Mountpoint to search in.
* @path: Path to search for.
+ * @ndp: Namei data pointer
*/
static struct vnode *
-namei_mp_search(struct mount *mp, const char *path)
+namei_mp_search(struct mount *mp, const char *path, struct nameidata *ndp)
{
struct vop_lookup_args lookup_args;
struct vnode *vp = mp->vp;
+ uint8_t n_cnp = 0;
char *name;
int status;
- for (size_t i = 1;; ++i) {
+ n_cnp = namei_num_cnp(path);
+ if (ISSET(ndp->flags, NAMEI_WANTPARENT)) {
+ --n_cnp;
+ }
+ for (size_t i = 1; i < n_cnp; ++i) {
name = vfs_get_fname_at(path, i);
if (name == NULL)
break;
@@ -140,7 +181,7 @@ namei_mp_search(struct mount *mp, const char *path)
lookup_args.dirvp = vp;
lookup_args.vpp = &vp;
- status = vfs_vop_lookup(vp, &lookup_args);
+ status = vfs_vop_lookup(&lookup_args);
dynfree(name);
if (status != 0) {
@@ -193,7 +234,7 @@ namei(struct nameidata *ndp)
lookup_args.name = path;
lookup_args.dirvp = g_root_vnode;
lookup_args.vpp = &vp;
- status = vfs_vop_lookup(lookup_args.dirvp, &lookup_args);
+ status = vfs_vop_lookup(&lookup_args);
/* Did we find it in the root */
if (status == 0) {
@@ -212,7 +253,7 @@ namei(struct nameidata *ndp)
/* If the name matches, search within */
if (strcmp(mp->name, name) == 0)
- vp = namei_mp_search(mp, path);
+ vp = namei_mp_search(mp, path, ndp);
/* Did we find it at this mountpoint? */
if (vp != NULL) {
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index da0a4f9..69417d0 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -141,8 +141,9 @@ vfs_release_vnode(struct vnode *vp)
}
int
-vfs_vop_lookup(struct vnode *vp, struct vop_lookup_args *args)
+vfs_vop_lookup(struct vop_lookup_args *args)
{
+ const struct vnode *vp = args->dirvp;
const struct vops *vops = vp->vops;
if (vops == NULL)
@@ -180,8 +181,9 @@ vfs_vop_write(struct vnode *vp, struct sio_txn *sio)
}
int
-vfs_vop_getattr(struct vnode *vp, struct vop_getattr_args *args)
+vfs_vop_getattr(struct vop_getattr_args *args)
{
+ const struct vnode *vp = args->vp;
const struct vops *vops = vp->vops;
if (vops == NULL)
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 990d722..d15ecf1 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -43,7 +43,7 @@ static int
vfs_dostat(const char *path, struct stat *sbuf)
{
char pathbuf[PATH_MAX];
- struct vattr *attr;
+ struct vattr attr;
struct stat st;
struct vnode *vp;
struct vop_getattr_args gattr;
@@ -58,7 +58,7 @@ vfs_dostat(const char *path, struct stat *sbuf)
return -EFAULT;
}
- nd.path = path;
+ nd.path = pathbuf;
nd.flags = 0;
if ((error = namei(&nd)) != 0) {
@@ -67,19 +67,42 @@ vfs_dostat(const char *path, struct stat *sbuf)
vp = nd.vp;
gattr.vp = vp;
- error = vfs_vop_getattr(vp, &gattr);
+ gattr.res = &attr;
+ error = vfs_vop_getattr(&gattr);
if (error != 0) {
return error;
}
- attr = gattr.res;
memset(&st, VNOVAL, sizeof(st));
/* Copy stat data to userspace statbuf */
- st.st_mode = attr->mode;
- st.st_size = attr->size;
+ st.st_mode = attr.mode;
+ st.st_size = attr.size;
copyout(&st, sbuf, sizeof(*sbuf));
+ vfs_release_vnode(vp);
+ return 0;
+}
+
+static int
+vfs_doaccess(const char *path)
+{
+ struct nameidata nd;
+ char pathbuf[PATH_MAX];
+ int error;
+
+ if ((copyinstr(path, pathbuf, sizeof(pathbuf))) < 0) {
+ return -EFAULT;
+ }
+
+ nd.path = pathbuf;
+ nd.flags = 0;
+
+ if ((error = namei(&nd)) != 0) {
+ return error;
+ }
+
+ vfs_release_vnode(nd.vp);
return 0;
}
@@ -149,3 +172,14 @@ sys_stat(struct syscall_args *scargs)
{
return vfs_dostat((const char *)scargs->arg0, (void *)scargs->arg1);
}
+
+/*
+ * Check if a file can be accessed.
+ *
+ * @arg0: path
+ */
+scret_t
+sys_access(struct syscall_args *scargs)
+{
+ return vfs_doaccess((const char *)scargs->arg0);
+}
diff --git a/sys/kern/vfs_vcache.c b/sys/kern/vfs_vcache.c
index 25e244c..6c08caf 100644
--- a/sys/kern/vfs_vcache.c
+++ b/sys/kern/vfs_vcache.c
@@ -161,7 +161,7 @@ vfs_vcache_migrate(int newtype)
args.oldp = NULL;
args.oldlenp = NULL;
args.newp = sysctl_val;
- args.newlen = strlen(sysctl_val);
+ args.newlen = strlen(sysctl_val) + 1;
if ((retval = sysctl(&args)) != 0) {
return retval;
diff --git a/sys/lib/crc32.c b/sys/lib/crc32.c
new file mode 100644
index 0000000..dda4428
--- /dev/null
+++ b/sys/lib/crc32.c
@@ -0,0 +1,89 @@
+/*
+ * 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 <crc32.h>
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+uint32_t
+crc32(const void *data, size_t len)
+{
+ const uint8_t *p = data;
+ uint32_t val = 0xFFFFFFFF;
+
+ for (size_t i = 0; i < len; ++i) {
+ val = (val >> 8) ^ crc32_tab[(val ^ p[i]) & 0xFF];
+ }
+
+ return val ^ 0xFFFFFFFF;
+}
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;
+}
diff --git a/sys/lib/string/vsnprintf.c b/sys/lib/string/vsnprintf.c
index e9e391f..489514f 100644
--- a/sys/lib/string/vsnprintf.c
+++ b/sys/lib/string/vsnprintf.c
@@ -96,6 +96,9 @@ vsnprintf(char *s, size_t size, const char *fmt, va_list ap)
c1 = (char )va_arg(ap, int);
printc(s, size, &off, c1);
break;
+ case '%':
+ printc(s, size, &off, c);
+ break;
case 'd':
num = va_arg(ap, int);
itoa(num, num_buf, 10);
@@ -104,6 +107,7 @@ vsnprintf(char *s, size_t size, const char *fmt, va_list ap)
num_len = strlen(num_buf);
for (size_t i = num_len; i < pad_width; ++i)
printc(s, size, &off, '0');
+ pad_width = 0;
}
printstr(s, size, &off, num_buf);
break;
diff --git a/sys/net/if.c b/sys/net/if.c
new file mode 100644
index 0000000..5c9bc01
--- /dev/null
+++ b/sys/net/if.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/spinlock.h>
+#include <sys/errno.h>
+#include <net/if_var.h>
+#include <string.h>
+
+static TAILQ_HEAD(, netif) netif_list;
+static bool netif_init = false;
+
+/*
+ * Expose a network interface to the rest of the
+ * system.
+ */
+void
+netif_add(struct netif *nifp)
+{
+ if (!netif_init) {
+ TAILQ_INIT(&netif_list);
+ netif_init = true;
+ }
+
+ TAILQ_INSERT_TAIL(&netif_list, nifp, link);
+}
+
+/*
+ * Lookup a network interface by name or type.
+ *
+ * @name: Name to lookup (use `type' if NULL)
+ * @type: Type to lookup (use if `name' is NULL)
+ */
+int
+netif_lookup(const char *name, uint8_t type, struct netif **res)
+{
+ struct netif *netif;
+
+ if (!netif_init) {
+ return -EAGAIN;
+ }
+
+ TAILQ_FOREACH(netif, &netif_list, link) {
+ if (name != NULL) {
+ if (strcmp(netif->name, name) == 0) {
+ *res = netif;
+ return 0;
+ }
+ }
+
+ if (name == NULL && netif->type == type) {
+ *res = netif;
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
new file mode 100644
index 0000000..db1d6d4
--- /dev/null
+++ b/sys/netinet/if_ether.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <vm/dynalloc.h>
+#include <net/ethertypes.h>
+#include <netinet/if_ether.h>
+#include <string.h>
+
+struct arp_pkt {
+ struct ether_frame ehfr;
+ struct ether_arp payload;
+};
+
+static struct arp_pkt *
+arp_create(struct netif *nifp, uint32_t *sproto, uint32_t *tproto, uint16_t op)
+{
+ struct arp_pkt *packet;
+ struct arp_hdr *hdrp;
+ struct ether_frame *frp;
+ struct ether_arp *payload;
+
+ packet = dynalloc(sizeof(*packet));
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ frp = &packet->ehfr;
+ payload = &packet->payload;
+ hdrp = &payload->hdr;
+
+ /* Ethernet frame, from source to all */
+ memcpy(frp->ether_saddr, &nifp->addr, ETHER_ADDR_LEN);
+ memset(frp->ether_daddr, 0xFF, ETHER_ADDR_LEN);
+ frp->ether_type = swap16(ETHERTYPE_ARP);
+
+ /* Now for the ARP header */
+ hdrp->hw_type = swap16(ARP_HWTYPE_ETHER);
+ hdrp->proto_type = swap16(ETHERTYPE_IPV4);
+ hdrp->hw_len = ETHER_ADDR_LEN;
+ hdrp->proto_len = 4;
+ hdrp->op_type = swap16(op);
+
+ memcpy(payload->sha, frp->ether_saddr, ETHER_ADDR_LEN);
+ memset(payload->tha, 0xFF, ETHER_ADDR_LEN);
+
+ /* Protocol source address */
+ *((uint32_t *)payload->spa) = *sproto;
+ *((uint32_t *)payload->tpa) = *tproto;
+ return packet;
+}
+
+static int
+arp_send(struct netif *nifp, uint8_t *sproto, uint8_t *tproto, uint16_t op)
+{
+ struct arp_pkt *packet;
+ struct netbuf nb;
+ uint32_t *src_tmp, *targ_tmp;
+
+ if (nifp->tx_enq == NULL) {
+ return -ENOTSUP;
+ }
+ if (nifp->tx_start == NULL) {
+ return -ENOTSUP;
+ }
+
+ src_tmp = (uint32_t *)sproto;
+ targ_tmp = (uint32_t *)tproto;
+
+ packet = arp_create(nifp, src_tmp, targ_tmp, op);
+ if (packet == NULL) {
+ return -ENOMEM;
+ }
+
+ nb.len = sizeof(*packet);
+ memcpy(nb.data, packet, nb.len);
+
+ nifp->tx_enq(nifp, &nb, NULL);
+ nifp->tx_start(nifp);
+ dynfree(packet);
+ return 0;
+}
+
+int
+arp_request(struct netif *nifp, uint8_t *sproto, uint8_t *tproto)
+{
+ return arp_send(nifp, sproto, tproto, ARP_REQUEST);
+}
+
+int
+arp_reply(struct netif *nifp, uint8_t *sproto, uint8_t *tproto)
+{
+ return arp_send(nifp, sproto, tproto, ARP_REPLY);
+}
diff --git a/sys/vm/vm_device.c b/sys/vm/vm_device.c
new file mode 100644
index 0000000..e990b47
--- /dev/null
+++ b/sys/vm/vm_device.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/syslog.h>
+#include <vm/vm_device.h>
+
+#define pr_trace(fmt, ...) kprintf("vm_device: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+const struct vm_pagerops dv_vnops;
+
+/*
+ * Attach a cdev to a vm_object
+ *
+ * @major: Char device major
+ * @minor: Char device minor.
+ */
+struct vm_object *
+dv_attach(devmajor_t major, dev_t dev, vm_prot_t prot)
+{
+ int error;
+ struct cdevsw *cdevp;
+ struct vm_object *vmobj;
+
+ if ((cdevp = dev_get(major, dev)) == NULL) {
+ pr_error("bad attach (major=%d, dev=%d)\n", major, dev);
+ return NULL;
+ }
+
+ if (cdevp->mmap == NULL) {
+ pr_error("cdev lacks mmap() (major=%d, dev=%d)\n", major, dev);
+ return NULL;
+ }
+
+ error = vm_obj_init(&cdevp->vmobj, &dv_vnops, 1);
+ if (error != 0) {
+ return NULL;
+ }
+
+ vmobj = &cdevp->vmobj;
+ vmobj->prot = prot;
+ vmobj->data = cdevp;
+ vmobj->pgops = &dv_vnops;
+ return vmobj;
+}
+
+/* TODO */
+const struct vm_pagerops dv_vnops = {
+ .get = NULL,
+};
diff --git a/sys/vm/vm_init.c b/sys/vm/vm_init.c
index 2846a69..7518838 100644
--- a/sys/vm/vm_init.c
+++ b/sys/vm/vm_init.c
@@ -56,6 +56,7 @@ vm_init(void)
void *pool;
vm_physmem_init();
+ pmap_init();
g_kvas = pmap_read_vas();
vm_ctx.dynalloc_pool_sz = DYNALLOC_POOL_SZ;
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index b56e896..bb9df83 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -35,8 +35,10 @@
#include <sys/syscall.h>
#include <sys/syslog.h>
#include <sys/mman.h>
+#include <sys/filedesc.h>
#include <vm/dynalloc.h>
#include <vm/vm_pager.h>
+#include <vm/vm_device.h>
#include <vm/pmap.h>
#include <vm/map.h>
#include <vm/vm.h>
@@ -157,51 +159,113 @@ vm_map_modify(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot, bool unmap
* crashes.
*/
void *
-mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
{
- struct vm_object *map_obj;
+ struct vm_object *map_obj = NULL;
+ struct cdevsw *cdevp;
struct vm_page *pg;
struct mmap_entry *ep;
+ struct vnode *vp;
+ struct filedesc *fdp;
struct proc *td;
struct vas vas;
int error, npgs;
paddr_t pa;
vaddr_t va;
size_t misalign;
+ off_t page_off;
misalign = len & (DEFAULT_PAGESIZE - 1);
len = ALIGN_UP(len + misalign, DEFAULT_PAGESIZE);
npgs = len / DEFAULT_PAGESIZE;
-
- if (addr == NULL) {
- pr_error("mmap: NULL addr not supported\n");
- return NULL;
- }
+ vas = pmap_read_vas();
/* Validate flags */
- if (ISSET(flags, MAP_FIXED | MAP_SHARED)) {
- pr_error("mmap: fixed/shared mappings not yet supported\n");
+ if (ISSET(flags, MAP_FIXED)) {
+ pr_error("mmap: fixed mappings not yet supported\n");
mmap_dbg(addr, len, prot, flags, fildes, off);
return NULL;
}
- map_obj = dynalloc(sizeof(*map_obj));
- if (map_obj == NULL) {
- kprintf("mmap: failed to allocate map object\n");
- return NULL;
+
+ /*
+ * Attempt to open the file if mapping
+ * is shared.
+ */
+ if (ISSET(flags, MAP_SHARED)) {
+ fdp = fd_get(NULL, fildes);
+ if (fdp == NULL) {
+ pr_error("mmap: no such fd (fd=%d)\n", fildes);
+ return NULL;
+ }
+
+ vp = fdp->vp;
+ if (vp->type != VCHR) {
+ /* TODO */
+ pr_error("mmap: only device files supported\n");
+ return NULL;
+ }
+
+ map_obj = dv_attach(vp->major, vp->dev, prot);
+ if (map_obj == NULL) {
+ kprintf("mmap: dv_attach() failure\n");
+ return NULL;
+ }
+
+ cdevp = map_obj->data;
+ if ((pa = cdevp->mmap(vp->dev, len, off, 0)) == 0) {
+ kprintf("mmap: dev mmap() gave 0\n");
+ return NULL;
+ }
+
+ /*
+ * If the address passed is NULL, just identity
+ * map everything.
+ *
+ * XXX: This is why the bounds check done in the
+ * cdev mmap() *must* be correct.
+ *
+ * TODO: Use copy-on-write for this instead. Since mapping
+ * certain devices may required a lot of memory to
+ * be referenced anyways, we could use a buffered
+ * copy-on-write technique where only a window of
+ * pages can be mapped on-demand and other pages
+ * freed when that window is exceeded.
+ */
+ if (addr == NULL) {
+ addr = (void *)pa;
+ }
+
+ va = ALIGN_DOWN((vaddr_t)addr, DEFAULT_PAGESIZE);
+ error = vm_map(vas, va, pa, prot, len);
+ if (error != 0) {
+ kprintf("mmap: map failed (error=%d)\n", error);
+ return NULL;
+ }
+
+ goto done;
}
- error = vm_obj_init(map_obj, &vm_anonops, 1);
- if (error < 0) {
- kprintf("mmap: vm_obj_init() returned %d\n", error);
- kprintf("mmap: failed to init object\n");
- return NULL;
+
+ /* Only allocate new obj if needed */
+ if (map_obj == NULL) {
+ map_obj = dynalloc(sizeof(*map_obj));
+ if (map_obj == NULL) {
+ kprintf("mmap: failed to allocate map object\n");
+ return NULL;
+ }
+ error = vm_obj_init(map_obj, &vm_anonops, 1);
+ if (error < 0) {
+ kprintf("mmap: vm_obj_init() returned %d\n", error);
+ kprintf("mmap: failed to init object\n");
+ return NULL;
+ }
}
/* XXX: Assuming private */
- vas = pmap_read_vas();
va = ALIGN_DOWN((vaddr_t)addr, DEFAULT_PAGESIZE);
for (int i = 0; i < npgs; ++i) {
pg = vm_pagealloc(map_obj, PALLOC_ZERO);
+ page_off = i * DEFAULT_PAGESIZE;
if (pg == NULL) {
/* TODO */
@@ -209,15 +273,21 @@ mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
return NULL;
}
+ /* TODO: copy-on-write */
+ if (addr == NULL) {
+ va = pg->phys_addr;
+ addr = (void *)va;
+ }
+
pa = pg->phys_addr;
- error = vm_map(vas, va, pa, prot, len);
- pr_trace("va=%p, len=%d\n", va, len);
+ error = vm_map(vas, va + page_off, pa, prot, len);
if (error < 0) {
pr_error("mmap: failed to map page (retval=%x)\n", error);
return NULL;
}
}
+done:
/* Add entry to ledger */
td = this_td();
ep = dynalloc(sizeof(*ep));
@@ -243,7 +313,7 @@ mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
* multiple of the machine page size.
*/
int
-munmap_at(void *addr, size_t len)
+munmap(void *addr, size_t len)
{
int pgno;
vaddr_t va;
@@ -299,7 +369,7 @@ munmap_at(void *addr, size_t len)
* arg5 -> off
*/
scret_t
-mmap(struct syscall_args *scargs)
+sys_mmap(struct syscall_args *scargs)
{
void *addr;
size_t len;
@@ -308,11 +378,11 @@ mmap(struct syscall_args *scargs)
addr = (void *)scargs->arg0;
len = scargs->arg1;
- prot = scargs->arg2;
+ prot = scargs->arg2 | PROT_USER;
flags = scargs->arg3;
fildes = scargs->arg4;
off = scargs->arg5;
- return (scret_t)mmap_at(addr, len, prot, flags, fildes, off);
+ return (scret_t)mmap(addr, len, prot, flags, fildes, off);
}
/*
@@ -322,14 +392,14 @@ mmap(struct syscall_args *scargs)
* arg1 -> len
*/
scret_t
-munmap(struct syscall_args *scargs)
+sys_munmap(struct syscall_args *scargs)
{
void *addr;
size_t len;
addr = (void *)scargs->arg0;
len = scargs->arg1;
- return (scret_t)munmap_at(addr, len);
+ return (scret_t)munmap(addr, len);
}
/*
diff --git a/sys/vm/vm_physmem.c b/sys/vm/vm_physmem.c
index c7fcedb..42b7a31 100644
--- a/sys/vm/vm_physmem.c
+++ b/sys/vm/vm_physmem.c
@@ -32,15 +32,22 @@
#include <sys/limine.h>
#include <sys/syslog.h>
#include <sys/spinlock.h>
+#include <sys/panic.h>
#include <vm/physmem.h>
#include <vm/vm.h>
#include <string.h>
-size_t highest_frame_idx = 0;
-size_t bitmap_size = 0;
-size_t bitmap_free_start = 0;
+#define BYTES_PER_MIB 8388608
-uint8_t *bitmap;
+static size_t pages_free = 0;
+static size_t pages_used = 0;
+static size_t pages_total = 0;
+static size_t highest_frame_idx = 0;
+static size_t bitmap_size = 0;
+static size_t bitmap_free_start = 0;
+static ssize_t last_idx = 0;
+
+static uint8_t *bitmap;
static struct limine_memmap_response *resp = NULL;
static struct spinlock lock = {0};
@@ -59,9 +66,11 @@ physmem_populate_bitmap(void)
for (size_t i = 0; i < resp->entry_count; ++i) {
ent = resp->entries[i];
+ pages_total += ent->length / DEFAULT_PAGESIZE;
if (ent->type != LIMINE_MEMMAP_USABLE) {
/* This memory is not usable */
+ pages_used += ent->length / DEFAULT_PAGESIZE;
continue;
}
@@ -72,6 +81,8 @@ physmem_populate_bitmap(void)
for (size_t j = 0; j < ent->length; j += DEFAULT_PAGESIZE) {
clrbit(bitmap, (ent->base + j) / DEFAULT_PAGESIZE);
}
+
+ pages_free += ent->length / DEFAULT_PAGESIZE;
}
}
@@ -105,7 +116,6 @@ physmem_alloc_bitmap(void)
}
}
-
/*
* Init the physical memory bitmap.
*/
@@ -137,29 +147,59 @@ physmem_init_bitmap(void)
*
* @count: Number of frames to allocate.
*/
-uintptr_t
-vm_alloc_frame(size_t count)
+static uintptr_t
+__vm_alloc_frame(size_t count)
{
size_t frames = 0;
+ ssize_t idx = -1;
uintptr_t ret = 0;
- spinlock_acquire(&lock);
- for (size_t i = 0; i < highest_frame_idx; ++i) {
+ for (size_t i = last_idx; i < highest_frame_idx; ++i) {
if (!testbit(bitmap, i)) {
- /* We have a free page */
- if (++frames != count) {
- continue;
- }
-
- for (size_t j = i; j < i + count; ++j) {
- setbit(bitmap, j);
- }
+ if (idx < 0)
+ idx = i;
+ if (++frames >= count)
+ break;
- ret = i * DEFAULT_PAGESIZE;
- break;
+ continue;
}
+
+ idx = -1;
+ frames = 0;
+ }
+
+ if (idx < 0 || frames != count) {
+ ret = 0;
+ goto done;
+ }
+
+ for (size_t i = idx; i < idx + count; ++i) {
+ setbit(bitmap, i);
+ }
+ ret = idx * DEFAULT_PAGESIZE;
+ last_idx = idx;
+ memset(PHYS_TO_VIRT(ret), 0, count * DEFAULT_PAGESIZE);
+done:
+ return ret;
+}
+
+uintptr_t
+vm_alloc_frame(size_t count)
+{
+ uintptr_t ret;
+
+ spinlock_acquire(&lock);
+ if ((ret = __vm_alloc_frame(count)) == 0) {
+ last_idx = 0;
+ ret = __vm_alloc_frame(count);
}
+ if (ret == 0) {
+ panic("out of memory\n");
+ }
+
+ pages_used += count;
+ pages_free -= count;
spinlock_release(&lock);
return ret;
}
@@ -169,13 +209,47 @@ vm_free_frame(uintptr_t base, size_t count)
{
size_t stop_at = base + (count * DEFAULT_PAGESIZE);
+ base = ALIGN_UP(base, DEFAULT_PAGESIZE);
+
spinlock_acquire(&lock);
for (uintptr_t p = base; p < stop_at; p += DEFAULT_PAGESIZE) {
clrbit(bitmap, p / DEFAULT_PAGESIZE);
}
+ pages_used -= count;
+ pages_free += count;
spinlock_release(&lock);
}
+/*
+ * Return the amount of memory in MiB that is
+ * currently allocated.
+ */
+uint32_t
+vm_mem_used(void)
+{
+ return (pages_used * DEFAULT_PAGESIZE) / BYTES_PER_MIB;
+}
+
+/*
+ * Return the amount of memory in MiB that is
+ * currently free.
+ */
+uint32_t
+vm_mem_free(void)
+{
+ return (pages_free * DEFAULT_PAGESIZE) / BYTES_PER_MIB;
+}
+
+/*
+ * Return the total amount of memory supported
+ * by the machine.
+ */
+size_t
+vm_mem_total(void)
+{
+ return (pages_total * DEFAULT_PAGESIZE) / BYTES_PER_MIB;
+}
+
void
vm_physmem_init(void)
{
diff --git a/sys/vm/vm_stat.c b/sys/vm/vm_stat.c
new file mode 100644
index 0000000..3e39047
--- /dev/null
+++ b/sys/vm/vm_stat.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <fs/ctlfs.h>
+#include <vm/physmem.h>
+#include <vm/vm.h>
+#include <vm/stat.h>
+#include <string.h>
+
+#include <sys/syslog.h>
+
+static struct ctlops vm_stat_ctl;
+
+/*
+ * ctlfs hook to read the virtual memory
+ * statistics.
+ */
+static int
+vm_stat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct vm_stat stat;
+ int error;
+
+ if (sio->len > sizeof(stat)) {
+ sio->len = sizeof(stat);
+ }
+
+ error = vm_stat_get(&stat);
+ if (error < 0) {
+ return error;
+ }
+
+ memcpy(sio->buf, &stat, sio->len);
+ return sio->len;
+}
+
+int
+vm_stat_get(struct vm_stat *vmstat)
+{
+ if (vmstat == NULL) {
+ return -EINVAL;
+ }
+
+ vmstat->mem_avail = vm_mem_free();
+ vmstat->mem_used = vm_mem_used();
+ vmstat->mem_total = vm_mem_total();
+ return 0;
+}
+
+void
+vm_stat_init(void)
+{
+ char devname[] = "vm";
+ struct ctlfs_dev ctl;
+
+ /* Register a stat control file */
+ ctl.mode = 0444;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &vm_stat_ctl;
+ ctlfs_create_entry("stat", &ctl);
+}
+
+static struct ctlops vm_stat_ctl = {
+ .read = vm_stat_read,
+ .write = NULL
+};
diff --git a/sys/vm/vm_vnode.c b/sys/vm/vm_vnode.c
index 2457c97..777b382 100644
--- a/sys/vm/vm_vnode.c
+++ b/sys/vm/vm_vnode.c
@@ -73,7 +73,7 @@ vn_io(struct vnode *vp, struct vm_page **pgs, unsigned int npages, int rw)
args.res = &vattr;
c = MAX(vattr.size / DEFAULT_PAGESIZE, 1);
- if ((err = vfs_vop_getattr(vp, &args)) != 0) {
+ if ((err = vfs_vop_getattr(&args)) != 0) {
return err;
}
@@ -162,7 +162,6 @@ vn_attach(struct vnode *vp, vm_prot_t prot)
if (vp->type != VREG) {
pr_error("vn_attach: vp=%p, prot=%x\n", vp, prot);
- pr_error("vn_attach: Special files not supported yet!\n");
return NULL;
}