diff options
Diffstat (limited to 'sys')
209 files changed, 14864 insertions, 911 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/aarch64/aarch64/locore.S b/sys/arch/aarch64/aarch64/locore.S new file mode 100644 index 0000000..a95475c --- /dev/null +++ b/sys/arch/aarch64/aarch64/locore.S @@ -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. + */ + + .text + .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/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 1529b1c..c44c88e 100644 --- a/sys/arch/amd64/amd64/intr.c +++ b/sys/arch/amd64/amd64/intr.c @@ -79,6 +79,8 @@ intr_register(const char *name, const struct intr_hand *ih) { 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 */ @@ -97,15 +99,16 @@ intr_register(const char *name, const struct intr_hand *ih) * of 4 bits so there can be 16 vectors per IPL. * * XXX: Vector 0x20 is reserved for the Hyra scheduler and - * vector 0x21 is reserved for the CPU halt IPI. + * vectors 0x21 to 0x21 + N_IPIVEC are reserved for + * inter-processor interrupts. */ for (int i = vec; i < vec + 16; ++i) { - if (g_intrs[i] != NULL || i < 0x22) { + if (g_intrs[i] != NULL || i < 0x24) { continue; } /* Allocate memory for the name */ - name_len = strlen(name); + name_len = strlen(name) + 1; ih_new->name = dynalloc(name_len); if (ih_new->name == NULL) { dynfree(ih_new); @@ -114,10 +117,19 @@ intr_register(const char *name, const struct intr_hand *ih) } 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) { 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 022592c..ceb5428 100644 --- a/sys/arch/amd64/amd64/lapic.c +++ b/sys/arch/amd64/amd64/lapic.c @@ -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 3381437..60c37bf 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -42,9 +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> -#define HALT_VECTOR 0x21 +/* + * 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 @@ -52,39 +68,85 @@ #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 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) { + 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(); } @@ -129,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(); } /* @@ -177,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 @@ -206,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 @@ -232,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; + 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 index 32ccd34..62bed1b 100644 --- a/sys/arch/amd64/amd64/vector.S +++ b/sys/arch/amd64/amd64/vector.S @@ -51,85 +51,109 @@ ioapic_common_func: jz 1f // Nope, return mov (%rdx), %rbx // intr_hand.func - xor %rdi, %rdi // No data + 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 done // Yes, we are done. + 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 34, ioapic_edge_0 - IDT_SET_VEC 35, ioapic_edge_1 - IDT_SET_VEC 36, ioapic_edge_2 - IDT_SET_VEC 37, ioapic_edge_3 - IDT_SET_VEC 38, ioapic_edge_4 - IDT_SET_VEC 39, ioapic_edge_5 - IDT_SET_VEC 40, ioapic_edge_6 - IDT_SET_VEC 41, ioapic_edge_7 - IDT_SET_VEC 42, ioapic_edge_8 - IDT_SET_VEC 43, ioapic_edge_9 - IDT_SET_VEC 44, ioapic_edge_10 - IDT_SET_VEC 45, ioapic_edge_11 - IDT_SET_VEC 46, ioapic_edge_12 - IDT_SET_VEC 47, ioapic_edge_13 - IDT_SET_VEC 48, ioapic_edge_14 - IDT_SET_VEC 49, ioapic_edge_15 - IDT_SET_VEC 50, ioapic_edge_16 - IDT_SET_VEC 51, ioapic_edge_17 - IDT_SET_VEC 52, ioapic_edge_18 - IDT_SET_VEC 53, ioapic_edge_19 - IDT_SET_VEC 54, ioapic_edge_20 - IDT_SET_VEC 55, ioapic_edge_21 - IDT_SET_VEC 56, ioapic_edge_22 - IDT_SET_VEC 57, ioapic_edge_23 - IDT_SET_VEC 58, ioapic_edge_24 - IDT_SET_VEC 59, ioapic_edge_25 - IDT_SET_VEC 60, ioapic_edge_26 - IDT_SET_VEC 61, ioapic_edge_27 - IDT_SET_VEC 62, ioapic_edge_28 - IDT_SET_VEC 63, ioapic_edge_29 - IDT_SET_VEC 64, ioapic_edge_30 - IDT_SET_VEC 65, ioapic_edge_31 - IDT_SET_VEC 66, ioapic_edge_32 - IDT_SET_VEC 67, ioapic_edge_33 - IDT_SET_VEC 68, ioapic_edge_34 - IDT_SET_VEC 69, ioapic_edge_35 - IDT_SET_VEC 70, ioapic_edge_36 - IDT_SET_VEC 71, ioapic_edge_37 - IDT_SET_VEC 72, ioapic_edge_38 - IDT_SET_VEC 73, ioapic_edge_39 - IDT_SET_VEC 74, ioapic_edge_40 - IDT_SET_VEC 75, ioapic_edge_41 - IDT_SET_VEC 76, ioapic_edge_42 - IDT_SET_VEC 77, ioapic_edge_43 - IDT_SET_VEC 78, ioapic_edge_44 - IDT_SET_VEC 79, ioapic_edge_45 - IDT_SET_VEC 80, ioapic_edge_46 - IDT_SET_VEC 81, ioapic_edge_47 - IDT_SET_VEC 82, ioapic_edge_48 - IDT_SET_VEC 83, ioapic_edge_49 - IDT_SET_VEC 84, ioapic_edge_50 - IDT_SET_VEC 85, ioapic_edge_51 - IDT_SET_VEC 86, ioapic_edge_52 - IDT_SET_VEC 87, ioapic_edge_53 - IDT_SET_VEC 88, ioapic_edge_54 - IDT_SET_VEC 89, ioapic_edge_55 - IDT_SET_VEC 90, ioapic_edge_56 - IDT_SET_VEC 91, ioapic_edge_57 - IDT_SET_VEC 92, ioapic_edge_58 - IDT_SET_VEC 93, ioapic_edge_59 - IDT_SET_VEC 94, ioapic_edge_60 - IDT_SET_VEC 95, ioapic_edge_61 - IDT_SET_VEC 96, ioapic_edge_62 - IDT_SET_VEC 97, ioapic_edge_63 + 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) 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 b32ff5b..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; @@ -71,10 +101,10 @@ 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', @@ -132,15 +162,28 @@ i8042_ibuf_wait(void) /* * 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); } /* @@ -157,6 +200,45 @@ i8042_write(uint16_t port, uint8_t val) } /* + * 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) +{ + uint8_t conf; + + i8042_write(I8042_CMD, I8042_GET_CONFB); + i8042_obuf_wait(); + conf = i8042_read(I8042_DATA); + return conf; +} + +/* + * Write a new value to the i8042 controller + * configuration byte. + */ +static void +i8042_write_conf(uint8_t conf) +{ + i8042_write(I8042_CMD, I8042_SET_CONFB); + i8042_ibuf_wait(); + i8042_write(I8042_DATA, conf); +} + +/* * Send a data to a device * * @aux: If true, send to aux device (mouse) @@ -204,25 +286,94 @@ static void i8042_en_intr(void) { struct intr_hand ih; + uint8_t conf; ih.func = i8042_kb_event; ih.priority = IPL_BIO; ih.irq = KB_IRQ; intr_register("i8042-kb", &ih); + + /* + * 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; + i8042_write_conf(conf); +} + +/* + * 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 (size_t i = 0; i < 3; ++i) { - kprintf(OMIT_TIMESTAMP ".\f"); - tmr.msleep(1000); + for (int i = 0; i < buf->len; ++i) { + kprintf(OMIT_TIMESTAMP "%x", buf->data[i]); } - cpu_reboot(0); + kprintf(OMIT_TIMESTAMP "\n"); +} + +/* + * 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; + } + } + + return -1; } /* @@ -237,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: @@ -278,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) { @@ -350,6 +506,8 @@ i8042_sync_loop(void) 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"); @@ -378,23 +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"); } i8042_write(I8042_CMD, I8042_ENABLE_PORT0); - i8042_drain(); + 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 7eaee6a..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; 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 03d0ecf..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 * @@ -278,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 @@ -513,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; } @@ -532,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; } @@ -560,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, ®->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/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 23c68a2..d994ef1 100644 --- a/sys/dev/ic/ahci.c +++ b/sys/dev/ic/ahci.c @@ -36,6 +36,7 @@ #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> @@ -46,6 +47,7 @@ #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__) @@ -56,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. @@ -95,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. @@ -104,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); @@ -167,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; } /* @@ -316,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; @@ -417,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); @@ -431,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"); @@ -461,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; @@ -621,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; @@ -633,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; } @@ -667,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, &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; } /* @@ -683,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 @@ -695,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); @@ -755,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; @@ -775,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); @@ -790,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); } /* @@ -919,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; @@ -968,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 bf71463..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 * @@ -160,41 +173,107 @@ rt_poll(uint8_t reg, uint8_t size, uint32_t bits, bool pollset) } static int -rt8139_intr(void *sp) +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 -EIO; + 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); return 1; /* handled */ } static int -rtl8139_irq_init(void) +rt81xx_irq_init(void) { struct intr_hand ih; - ih.func = rt8139_intr; + ih.func = rt81xx_intr; ih.priority = IPL_BIO; ih.irq = dev->irq_line; - if (intr_register("rt8139", &ih) == NULL) { + if (intr_register("rt81xx", &ih) == NULL) { return -EIO; } return 0; @@ -214,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; @@ -249,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 @@ -286,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: * @@ -303,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; @@ -357,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 9ab109a..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> @@ -70,11 +71,16 @@ xhci_intr(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; } - return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * (portno - 1))); + /* Zero based */ + if (portno > 0) { + --portno; + } + + return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * portno)); } static int @@ -175,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); @@ -188,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; @@ -197,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; } @@ -218,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); @@ -265,7 +273,7 @@ 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 */ @@ -332,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; } @@ -369,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) { @@ -385,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); /* @@ -458,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; } @@ -496,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) { @@ -528,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/intr.h b/sys/include/arch/amd64/intr.h index 2a8d487..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,8 +49,23 @@ #define IPL_CLOCK 2 /* Clock */ #define IPL_HIGH 3 /* Defer everything */ -struct intr_entry { - int priority; +#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; + }; }; /* @@ -57,13 +74,18 @@ struct intr_entry { * [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. * @@ -76,6 +98,8 @@ struct intr_entry { */ struct intr_hand { int(*func)(void *); + size_t nintr; + struct intr_data data; char *name; int priority; int irq; 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 13c3095..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 */ 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 a23a654..67f2efe 100644 --- a/sys/include/dev/ic/ahcivar.h +++ b/sys/include/dev/ic/ahcivar.h @@ -96,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. */ @@ -104,6 +105,7 @@ struct hba_device { 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 a4de162..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; 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 1cbfd14..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) 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, ¶m); + 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(¶m); + 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 5845c1c..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,24 +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(); - /* Startup pid 1 */ - memset(&proc0, 0, sizeof(proc0.tf)); - spawn(&proc0, start_init, NULL, 0, NULL); + memset(&g_proc0, 0, sizeof(g_proc0)); + sysctl_clearstr(KERN_HOSTNAME); - /* Load all drivers */ + /* Startup pid 1 */ + spawn(&g_proc0, start_init, NULL, 0, &g_init); md_inton(); + + uacpi_init(); + + /* 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 756b44b..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; + if (from != NULL) { + if (from->pid == 0) + return; - ci = this_cpu(); - td = ci->curtd; - - if (td != NULL) { - dispatch_signals(td); - td_pri_update(td); - sched_save_td(td, tf); + dispatch_signals(from); + td_pri_update(from); } - if ((next_td = sched_dequeue_td()) == NULL) { - sched_oneshot(false); - return; - } - - 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(); } /* @@ -214,6 +241,7 @@ sched_switch(struct trapframe *tf) void sched_enter(void) { + md_inton(); sched_oneshot(false); for (;;) { md_pause(); @@ -223,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 @@ -243,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) { @@ -253,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; } |