diff options
Diffstat (limited to 'sys')
139 files changed, 7568 insertions, 751 deletions
diff --git a/sys/arch/aarch64/aarch64/exception.c b/sys/arch/aarch64/aarch64/exception.c new file mode 100644 index 0000000..d6f1f97 --- /dev/null +++ b/sys/arch/aarch64/aarch64/exception.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/syslog.h> +#include <sys/param.h> +#include <sys/cdefs.h> +#include <machine/cdefs.h> +#include <machine/exception.h> + +#define pr_trace(fmt, ...) kprintf("exception: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +static inline void +log_esr_class(uint8_t class) +{ + switch (class) { + case EC_WF: + pr_error("trapped WF\n"); + break; + case EC_MCRMRC: + pr_error("trapped MCR/MRC\n"); + break; + case EC_MCRRC: + pr_trace("trapped MCRR/MRRC\n"); + break; + case EC_LDCSTC: + pr_error("trapped LDC/STC\n"); + break; + case EC_SVE: + pr_trace("trapped SVE/SIMD/FP operation\n"); + break; + case EC_BRE: + pr_error("ibt: bad branch target\n"); + break; + case EC_ILLX: + pr_error("illegal execution state\n"); + break; + case EC_SVC64: + /* TODO */ + pr_error("supervisor call (TODO)!!\n"); + break; + case EC_PCALIGN: + pr_error("PC alignment fault\n"); + break; + case EC_DABORT: + case EC_EDABORT: + pr_error("data abort\n"); + break; + case EC_SPALIGN: + pr_error("SP alignment fault\n"); + break; + case EC_SERR: + pr_error("system error\n"); + break; + default: + pr_error("unknown exception\n"); + } +} + +static void +regdump(struct trapframe *tf, uint64_t elr) +{ + kprintf(OMIT_TIMESTAMP + "X0=%p X1=%p X2=%p\n" + "X3=%p X4=%p X5=%p\n" + "X6=%p X7=%p X8=%p\n" + "X9=%p X10=%p X11=%p\n" + "X12=%p X13=%p X14=%p\n" + "X15=%p X16=%p X17=%p\n" + "X18=%p X19=%p X20=%p\n" + "X21=%p X22=%p X23=%p\n" + "X24=%p X25=%p X26=%p\n" + "X27=%p X28=%p X29=%p\n" + "X30=%p\n" + "ELR=%p\n", + tf->x0, tf->x1, tf->x2, tf->x3, + tf->x4, tf->x5, tf->x6, tf->x7, + tf->x8, tf->x9, tf->x10, tf->x11, + tf->x12, tf->x13, tf->x14, tf->x15, + tf->x16, tf->x17, tf->x18, tf->x19, + tf->x20, tf->x21, tf->x22, tf->x23, + tf->x24, tf->x25, tf->x26, tf->x27, + tf->x28, tf->x29, tf->x30, elr); +} + +/* + * Handle an exception + * + * @esr: Copy of the Exception Syndrome Register + */ +void +handle_exception(struct trapframe *tf) +{ + uint8_t class; + + class = (tf->esr >> 26) & 0x3F; + log_esr_class(class); + regdump(tf, tf->elr); + for (;;) { + md_hlt(); + } +} diff --git a/sys/arch/aarch64/aarch64/intr.c b/sys/arch/aarch64/aarch64/intr.c new file mode 100644 index 0000000..5fd2439 --- /dev/null +++ b/sys/arch/aarch64/aarch64/intr.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <machine/intr.h> + +void * +intr_register(const char *name, const struct intr_hand *ih) +{ + /* TODO: Stub */ + return NULL; +} diff --git a/sys/arch/amd64/isa/i8042.S b/sys/arch/aarch64/aarch64/locore.S index 123d3a5..2155991 100644 --- a/sys/arch/amd64/isa/i8042.S +++ b/sys/arch/aarch64/aarch64/locore.S @@ -27,11 +27,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <machine/frameasm.h> - .text - .globl i8042_kb_isr -INTRENTRY(i8042_kb_isr, handle_kb) -handle_kb: - call i8042_kb_event - retq + .globl md_cpu_init +md_cpu_init: + ldr x0, =__vectab + msr vbar_el1, x0 + ret + diff --git a/sys/arch/aarch64/aarch64/machdep.c b/sys/arch/aarch64/aarch64/machdep.c index 7c21e62..9a96cbb 100644 --- a/sys/arch/aarch64/aarch64/machdep.c +++ b/sys/arch/aarch64/aarch64/machdep.c @@ -31,15 +31,11 @@ #include <sys/panic.h> #include <machine/cpu.h> #include <machine/sync.h> +#include <machine/board.h> struct cpu_info g_bsp_ci = {0}; -void -cpu_startup(struct cpu_info *ci) -{ - /* TODO: STUB */ - return; -} +void md_cpu_init(void); void cpu_halt_others(void) @@ -76,6 +72,13 @@ md_sync_all(void) return 0; } +void +cpu_halt_all(void) +{ + /* TODO: Stub */ + for (;;); +} + /* * Get the descriptor for the currently * running processor. @@ -85,6 +88,24 @@ this_cpu(void) { struct cpu_info *ci; - __ASMV("mrs %0, tpidr_el0" : "=r" (ci)); + __ASMV("mrs %0, tpidr_el1" : "=r" (ci)); return ci; } + +void +cpu_startup(struct cpu_info *ci) +{ + ci->self = ci; + __ASMV("msr tpidr_el1, %0" :: "r" (ci)); + md_cpu_init(); +} + +void +md_get_board(struct board_info *res) +{ + uint64_t midr_el1; + + __ASMV("mrs %0, midr_el1" : "=r" (midr_el1)); + res->partno = (midr_el1 >> 4) & 0xFFF; + res->implementer = (midr_el1 >> 24) & 0xFF; +} diff --git a/sys/arch/aarch64/aarch64/pmap.c b/sys/arch/aarch64/aarch64/pmap.c index b5ebda9..870ef80 100644 --- a/sys/arch/aarch64/aarch64/pmap.c +++ b/sys/arch/aarch64/aarch64/pmap.c @@ -28,35 +28,274 @@ */ #include <sys/types.h> +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/panic.h> #include <machine/vas.h> #include <vm/pmap.h> +#include <vm/physmem.h> +#include <vm/vm.h> + +/* Memory types for MAIR_ELx */ +#define MT_NORMAL 0x00 +#define MT_NORMAL_UC 0x02 +#define MT_DEVICE 0x03 + +/* Memory attributes */ +#define MEM_DEV_NGNRNE 0x00 +#define MEM_DEV_NVNRE 0x04 +#define MEM_NORMAL_UC 0x44 +#define MEM_NORMAL 0xFF + +#define MT_ATTR(idx, attr) ((attr) << (8 * (idx))) + +/* + * Descriptor bits for page table entries + * + * @PTE_VALID: Must be set to be valid + * @PTE_TABLE: Table (1), block (0) + * @PTE_USER: User access allowed + * @PTE_READONLY: Read-only + * @PTE_ISH: Inner sharable + * @PTE_AF: Accessed flag + * @PTE_XN: Execute never + */ +#define PTE_ADDR_MASK 0x0000FFFFFFFFF000 +#define PTE_VALID BIT(0) +#define PTE_TABLE BIT(1) +#define PTE_USER BIT(6) +#define PTE_READONLY BIT(7) +#define PTE_ISH (3 << 8) +#define PTE_AF BIT(10) +#define PTE_XN BIT(54) + +/* + * Write the EL1 Memory Attribute Indirection + * Register. + * + * @val: Value to write + * + * XXX: Refer to the ARMv8 Reference Manual section + * D7.2.70 + */ +static inline void +mair_el1_write(uint64_t val) +{ + __ASMV("msr mair_el1, %0" + : + : "r" (val) + : "memory" + ); +} + +static inline void +tlb_flush(vaddr_t va) +{ + __ASMV( + "tlbi vaae1is, %0\n" + "dsb ish\n" + "isb\n" + : + : "r" (va >> 12) + : "memory" + ); +} + +static uint64_t +pmap_prot_to_pte(vm_prot_t prot) +{ + uint64_t pte_flags = 0; + + pte_flags |= (PTE_VALID | PTE_TABLE | PTE_AF); + pte_flags |= (PTE_XN | PTE_READONLY | PTE_ISH); + + if (ISSET(prot, PROT_WRITE)) + pte_flags &= ~PTE_READONLY; + if (ISSET(prot, PROT_EXEC)) + pte_flags &= ~PTE_XN; + if (ISSET(prot, PROT_USER)) + pte_flags |= PTE_USER; + + return pte_flags; +} + +/* + * Returns an index for a specific page map + * label based on an input address. + */ +static size_t +pmap_level_idx(vaddr_t ia, uint8_t level) +{ + switch (level) { + case 0: return (ia >> 39) & 0x1FF; + case 1: return (ia >> 30) & 0x1FF; + case 2: return (ia >> 21) & 0x1FF; + case 3: return (ia >> 12) & 0x1FF; + default: panic("pmap_level_idx: bad index\n"); + } + + __builtin_unreachable(); +} + +/* + * Extract a level from a pagemap + * + * @level: Current pagemap level + * @ia: Input virtual address + * @pmap: Current level to extract from + * @alloc: Set to true to allocate new entries + * + * XXX: `level_idx' can be grabbed with pmap_level_idx(). + */ +static uintptr_t * +pmap_extract(uint8_t level, vaddr_t ia, vaddr_t *pmap, bool alloc) +{ + uintptr_t next, level_alloc; + uint8_t idx; + + if (pmap == NULL) { + return NULL; + } + + idx = pmap_level_idx(ia, level); + next = pmap[idx]; + + if (ISSET(next, PTE_VALID)) { + next = next & PTE_ADDR_MASK; + return PHYS_TO_VIRT(next); + } + + /* + * Nothing to grab at this point, we'll need to + * allocate our own entry. However, if we are + * told not to allocate anything, just return + * NULL. + */ + if (!alloc) { + return NULL; + } + + level_alloc = vm_alloc_frame(1); + if (level_alloc == 0) { + return NULL; + } + + pmap[idx] = (level_alloc | PTE_VALID | PTE_USER | PTE_TABLE); + return PHYS_TO_VIRT(level_alloc); +} + +/* + * Get the lowest pagemap table referring to a 4 KiB + * frame. + * + * @ttrb: Translation table base to use + * @ia: Input virtual address + * @alloc: If true, allocate new pagemap entries as needed + * @res: Result goes here + */ +static int +pmap_get_tbl(paddr_t ttbrn, vaddr_t ia, bool alloc, uintptr_t **res) +{ + vaddr_t *root; + uintptr_t *l1, *l2, *l3; + + root = PHYS_TO_VIRT(ttbrn); + + l1 = pmap_extract(0, ia, root, alloc); + if (l1 == NULL) { + return -1; + } + + l2 = pmap_extract(1, ia, l1, alloc); + if (l2 == NULL) { + return -1; + } + + l3 = pmap_extract(2, ia, l2, alloc); + if (l3 == NULL) { + return -1; + } + + *res = l3; + return 0; +} struct vas pmap_read_vas(void) { - /* TODO: STUB */ struct vas vas = {0}; + + __ASMV( + "mrs %0, ttbr0_el1\n" + "mrs %1, ttbr1_el1\n" + : "=r" (vas.ttbr0_el1), + "=r" (vas.ttbr1_el1) + : + : "memory" + ); + return vas; } void pmap_switch_vas(struct vas vas) { - /* TODO: STUB */ + __ASMV( + "msr ttbr0_el1, %0\n" + "msr ttbr1_el1, %1\n" + : + : "r" (vas.ttbr0_el1), + "r" (vas.ttbr1_el1) + : "memory" + ); return; } int pmap_map(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot) { - /* TODO: STUB */ + paddr_t ttbrn = vas.ttbr0_el1; + uint64_t pte_flags; + uintptr_t *tbl; + int error; + + if (va >= VM_HIGHER_HALF) { + ttbrn = vas.ttbr1_el1; + } + + if ((error = pmap_get_tbl(ttbrn, va, true, &tbl)) < 0) { + return error; + } + if (__unlikely(tbl == NULL)) { + return -1; + } + + pte_flags = pmap_prot_to_pte(prot); + tbl[pmap_level_idx(va, 3)] = pa | pte_flags; + tlb_flush(va); return 0; } int pmap_unmap(struct vas vas, vaddr_t va) { - /* TODO: STUB */ + paddr_t ttbrn = vas.ttbr0_el1; + uintptr_t *tbl; + int error; + + if (va >= VM_HIGHER_HALF) { + ttbrn = vas.ttbr1_el1; + } + + if ((error = pmap_get_tbl(ttbrn, va, true, &tbl)) < 0) { + return error; + } + if (__unlikely(tbl == NULL)) { + return -1; + } + + tbl[pmap_level_idx(va, 3)] = 0; + tlb_flush(va); return 0; } @@ -66,3 +305,36 @@ pmap_destroy_vas(struct vas vas) /* TODO: STUB */ return; } + +bool +pmap_is_clean(struct vas vas, vaddr_t va) +{ + /* TODO: STUB */ + return false; +} + +void +pmap_mark_clean(struct vas vas, vaddr_t va) +{ + /* TODO: STUB */ + return; +} + +int +pmap_set_cache(struct vas vas, vaddr_t va, int type) +{ + /* TODO: STUB */ + return 0; +} + +int +pmap_init(void) +{ + uint64_t mair; + + mair = MT_ATTR(MT_NORMAL, MEM_NORMAL) | + MT_ATTR(MT_NORMAL_UC, MEM_NORMAL_UC) | + MT_ATTR(MT_DEVICE, MEM_DEV_NGNRNE); + mair_el1_write(mair); + return 0; +} diff --git a/sys/arch/aarch64/aarch64/proc_machdep.c b/sys/arch/aarch64/aarch64/proc_machdep.c index 97902e5..cc58af9 100644 --- a/sys/arch/aarch64/aarch64/proc_machdep.c +++ b/sys/arch/aarch64/aarch64/proc_machdep.c @@ -37,17 +37,17 @@ * @ip: Instruction pointer. */ int -md_fork(struct proc *p, struct proc *parent, uintptr_t ip) +md_spawn(struct proc *p, struct proc *parent, uintptr_t ip) { /* TODO: STUB */ return 0; } -void +uintptr_t md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog) { /* TODO: STUB */ - return; + return 0; } void diff --git a/sys/arch/aarch64/aarch64/reboot.c b/sys/arch/aarch64/aarch64/reboot.c index 6d82133..372012a 100644 --- a/sys/arch/aarch64/aarch64/reboot.c +++ b/sys/arch/aarch64/aarch64/reboot.c @@ -30,9 +30,25 @@ #include <sys/reboot.h> #include <sys/param.h> +/* + * Typically the reset vector is at address 0 but this can + * be remapped if the vendor is feeling silly. + */ +void(*g_cpu_reboot)(void) = NULL; + void cpu_reboot(int method) { - /* TODO: STUB */ + g_cpu_reboot(); for (;;); } + +/* + * arg0: Method bits + */ +scret_t +sys_reboot(struct syscall_args *scargs) +{ + cpu_reboot(scargs->arg0); + __builtin_unreachable(); +} diff --git a/sys/arch/aarch64/aarch64/vector.S b/sys/arch/aarch64/aarch64/vector.S new file mode 100644 index 0000000..c8f77ca --- /dev/null +++ b/sys/arch/aarch64/aarch64/vector.S @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <machine/frameasm.h> + +// Vector table entries are aligned at 128 bytes +// giving us 32 exception entries +.macro ventry label + .align 7 + b \label +.endm + + .text +x_sync_elx: + PUSH_XFRAME(TRAPNO_XSYNC) // Synchronous: sp+top @ X0 + bl handle_exception // Handle the exception + POP_XFRAME() // Pop the trapframe +1: hlt #0 // TODO + b 1b + +x_irq_elx: + PUSH_XFRAME(TRAPNO_XIRQ) // IRQ: sp+top @ X0 + bl handle_exception // Handle the exception + POP_XFRAME() // Pop the trapframe +1: hlt #0 // TODO + b 1b + +x_fiq_elx: + PUSH_XFRAME(TRAPNO_XFIQ) // FIQ: sp+top @ X0 + bl handle_exception // Handle the exception + POP_XFRAME() // Pop the trapframe +1: hlt #0 + b 1b + +x_serr_elx: + PUSH_XFRAME(TRAPNO_XSERR) // SERR: sp+top @ X0 + bl handle_exception // Handle the exception + POP_XFRAME() // Pop the trapframe +1: hlt #0 // TODO + b 1b + +x_unimpl: +1: hlt #0 + b 1b + + .align 11 // Table aligned @ 2 KiB + .globl __vectab +__vectab: + // From current EL (w/ SP_EL0) + ventry x_sync_elx + ventry x_irq_elx + ventry x_fiq_elx + ventry x_serr_elx + + // From current EL (w/ SP_ELx > 0) + ventry x_sync_elx + ventry x_irq_elx + ventry x_fiq_elx + ventry x_serr_elx + + // Lower EL with faulting code in AARCH64 + ventry x_sync_elx + ventry x_irq_elx + ventry x_fiq_elx + ventry x_serr_elx + + ventry x_unimpl + ventry x_unimpl + ventry x_unimpl + ventry x_unimpl diff --git a/sys/arch/aarch64/conf/GENERIC b/sys/arch/aarch64/conf/GENERIC index 5691685..eeb9d9d 100644 --- a/sys/arch/aarch64/conf/GENERIC +++ b/sys/arch/aarch64/conf/GENERIC @@ -1,5 +1,10 @@ // Kernel options -option SERIAL_DEBUG yes +option SERIAL_DEBUG yes // Enable kmsg serial logging +option USER_KMSG yes // Show kmsg in user consoles // Kernel constants -setval SCHED_NQUEUE 4 +setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ) + +// Console attributes +setval CONSOLE_BG 0x000000 +setval CONSOLE_FG 0xB57614 diff --git a/sys/arch/aarch64/conf/link.ld b/sys/arch/aarch64/conf/link.ld index c64cec3..2aa8c93 100644 --- a/sys/arch/aarch64/conf/link.ld +++ b/sys/arch/aarch64/conf/link.ld @@ -40,6 +40,12 @@ SECTIONS __drivers_init_end = .; } :rodata + .drivers.defer : { + __driversd_init_start = .; + *(.drivers.defer .drivers.defer) + __driversd_init_end = .; + } :rodata + /* Move to the next memory page for .data */ . += CONSTANT(MAXPAGESIZE); diff --git a/sys/arch/aarch64/pci/pci_machdep.c b/sys/arch/aarch64/pci/pci_machdep.c index fa92165..8de6cc9 100644 --- a/sys/arch/aarch64/pci/pci_machdep.c +++ b/sys/arch/aarch64/pci/pci_machdep.c @@ -30,20 +30,6 @@ #include <sys/types.h> #include <dev/pci/pci.h> -pcireg_t -pci_readl(struct pci_device *dev, uint32_t offset) -{ - /* TODO: STUB */ - return 0; -} - -void -pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val) -{ - /* TODO: STUB */ - return; -} - /* * Map a BAR into kernel memory. * diff --git a/sys/arch/amd64/amd64/gdt.c b/sys/arch/amd64/amd64/gdt.c index a8fe54d..40d8f48 100644 --- a/sys/arch/amd64/amd64/gdt.c +++ b/sys/arch/amd64/amd64/gdt.c @@ -29,50 +29,70 @@ #include <machine/gdt.h> -struct gdt_entry g_gdt_data[256] = { +/* + * The GDT should be cache line aligned, since it is accessed every time a + * segment selector is reloaded + */ +__cacheline_aligned struct gdt_entry g_gdt_data[GDT_ENTRY_COUNT] = { /* Null */ {0}, - /* Kernel code (0x8) */ + /* Kernel code (0x08) */ { - .limit = 0x0000, - .base_low = 0x0000, - .base_mid = 0x00, - .access = 0x9A, - .granularity = 0x20, - .base_hi = 0x00 + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .attributes = GDT_ATTRIBUTE_64BIT_CODE | GDT_ATTRIBUTE_PRESENT | + GDT_ATTRIBUTE_DPL0 | GDT_ATTRIBUTE_NONSYSTEM | + GDT_ATTRIBUTE_EXECUTABLE | GDT_ATTRIBUTE_READABLE, + .base_hi = 0x00 }, /* Kernel data (0x10) */ { - .limit = 0x0000, - .base_low = 0x0000, - .base_mid = 0x00, - .access = 0x92, - .granularity = 0x00, - .base_hi = 0x00 + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .attributes = GDT_ATTRIBUTE_PRESENT | GDT_ATTRIBUTE_DPL0 | + GDT_ATTRIBUTE_NONSYSTEM | GDT_ATTRIBUTE_WRITABLE, + .base_hi = 0x00 }, /* User code (0x18) */ { - .limit = 0x0000, - .base_low = 0x0000, - .base_mid = 0x00, - .access = 0xFA, - .granularity = 0xAF, - .base_hi = 0x00 + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .attributes = GDT_ATTRIBUTE_64BIT_CODE | GDT_ATTRIBUTE_PRESENT | + GDT_ATTRIBUTE_DPL3 | GDT_ATTRIBUTE_NONSYSTEM | + GDT_ATTRIBUTE_EXECUTABLE | GDT_ATTRIBUTE_READABLE, + .base_hi = 0x00 }, /* User data (0x20) */ { - .limit = 0x0000, - .base_low = 0x0000, - .base_mid = 0x00, - .access = 0xF2, - .granularity = 0x00, - .base_hi = 0x00 + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .attributes = GDT_ATTRIBUTE_PRESENT | GDT_ATTRIBUTE_DPL3 | + GDT_ATTRIBUTE_NONSYSTEM | GDT_ATTRIBUTE_WRITABLE, + .base_hi = 0x00 }, - /* TSS segment (0x28) */ - {0} + /* + * TSS segment (0x28) + * + * NOTE: 64-bit TSS descriptors are 16 bytes, equivalent to the size of two + * regular descriptor entries. + * See Intel SPG 3/25 Section 9.2.3 - TSS Descriptor in 64-bit mode. + */ + {0}, {0} +}; + +/* Verify that the GDT is of the correct size */ +__static_assert(sizeof(g_gdt_data) == (8 * GDT_ENTRY_COUNT)); + +const struct gdtr g_gdtr = { + .limit = sizeof(g_gdt_data) - 1, + .offset = (uintptr_t)&g_gdt_data[0] }; diff --git a/sys/arch/amd64/amd64/hpet.c b/sys/arch/amd64/amd64/hpet.c index 1670546..3b0ca46 100644 --- a/sys/arch/amd64/amd64/hpet.c +++ b/sys/arch/amd64/amd64/hpet.c @@ -47,6 +47,7 @@ #define CAP_CLK_PERIOD(caps) (caps >> 32) #define FSEC_PER_SECOND 1000000000000000ULL +#define NSEC_PER_SECOND 1000000000ULL #define USEC_PER_SECOND 1000000ULL static void *hpet_base = NULL; @@ -135,6 +136,20 @@ hpet_time_usec(void) } static size_t +hpet_time_nsec(void) +{ + uint64_t period, freq, caps; + uint64_t counter; + + caps = hpet_read(HPET_REG_CAPS); + period = CAP_CLK_PERIOD(caps); + freq = FSEC_PER_SECOND / period; + + counter = hpet_read(HPET_REG_MAIN_COUNTER); + return (counter * NSEC_PER_SECOND) / freq; +} + +static size_t hpet_time_sec(void) { return hpet_time_usec() / USEC_PER_SECOND; @@ -180,6 +195,7 @@ hpet_init(void) timer.usleep = hpet_usleep; timer.nsleep = hpet_nsleep; timer.get_time_usec = hpet_time_usec; + timer.get_time_nsec = hpet_time_nsec; timer.get_time_sec = hpet_time_sec; register_timer(TIMER_GP, &timer); return 0; diff --git a/sys/arch/amd64/amd64/intr.c b/sys/arch/amd64/amd64/intr.c index c31ee3c..685a16d 100644 --- a/sys/arch/amd64/amd64/intr.c +++ b/sys/arch/amd64/amd64/intr.c @@ -31,12 +31,19 @@ #include <sys/param.h> #include <sys/errno.h> #include <sys/panic.h> +#include <sys/cdefs.h> +#include <sys/syslog.h> #include <machine/intr.h> #include <machine/cpu.h> #include <machine/asm.h> +#include <machine/ioapic.h> #include <vm/dynalloc.h> +#include <string.h> -static struct intr_entry *intrs[256] = {0}; +#define pr_trace(fmt, ...) kprintf("intr: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +struct intr_hand *g_intrs[256] = {0}; int splraise(uint8_t s) @@ -67,35 +74,69 @@ splx(uint8_t s) ci->ipl = s; } -int -intr_alloc_vector(const char *name, uint8_t priority) +void * +intr_register(const char *name, const struct intr_hand *ih) { - size_t vec = MAX(priority << IPL_SHIFT, 0x20); - struct intr_entry *intr; + uint32_t vec = MAX(ih->priority << IPL_SHIFT, 0x20); + struct intr_hand *ih_new; + struct intr_data *idp_new; + const struct intr_data *idp; + size_t name_len; /* Sanity check */ - if (vec > NELEM(intrs)) { - return -1; + if (vec > NELEM(g_intrs) || name == NULL) { + return NULL; + } + + ih_new = dynalloc(sizeof(*ih_new)); + if (ih_new == NULL) { + pr_error("could not allocate new interrupt handler\n"); + return NULL; } /* * Try to allocate an interrupt vector. An IPL is made up * of 4 bits so there can be 16 vectors per IPL. + * + * XXX: Vector 0x20 is reserved for the Hyra scheduler, + * vector 0x21 is reserved for the CPU halt IPI, + * and vector 0x22 is reserved for TLB shootdowns. */ for (int i = vec; i < vec + 16; ++i) { - if (intrs[i] != NULL) { + if (g_intrs[i] != NULL || i < 0x23) { continue; } - intr = dynalloc(sizeof(*intr)); - if (intr == NULL) { - return -ENOMEM; + /* Allocate memory for the name */ + name_len = strlen(name) + 1; + ih_new->name = dynalloc(name_len); + if (ih_new->name == NULL) { + dynfree(ih_new); + pr_trace("could not allocate interrupt name\n"); + return NULL; } - intr->priority = priority; - intrs[i] = intr; - return i; + memcpy(ih_new->name, name, name_len); + idp_new = &ih_new->data; + idp = &ih->data; + + /* Pass the interrupt data */ + idp_new->ihp = ih_new; + idp_new->data_u64 = idp->data_u64; + + /* Setup the new intr_hand */ + ih_new->func = ih->func; + ih_new->priority = ih->priority; + ih_new->irq = ih->irq; + ih_new->vector = i; + g_intrs[i] = ih_new; + + if (ih->irq >= 0) { + ioapic_set_vec(ih->irq, i); + ioapic_irq_unmask(ih->irq); + } + return ih_new; } - return -1; + return NULL; } diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c index 70d36a5..022592c 100644 --- a/sys/arch/amd64/amd64/lapic.c +++ b/sys/arch/amd64/amd64/lapic.c @@ -340,7 +340,7 @@ lapic_init(void) /* Allocate a vector if needed */ if (lapic_timer_vec == 0) { - lapic_timer_vec = intr_alloc_vector("lapictmr", IPL_CLOCK); + lapic_timer_vec = (IPL_CLOCK << IPL_SHIFT) | 0x20; idt_set_desc(lapic_timer_vec, IDT_INT_GATE, ISR(lapic_tmr_isr), IST_SCHED); } diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index 4a885fa..9b65d6e 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -42,24 +42,38 @@ #include <machine/uart.h> #include <machine/sync.h> #include <machine/intr.h> +#include <machine/cdefs.h> #include <machine/isa/i8042var.h> +#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__); \ + } + +#define HALT_VECTOR 0x21 +#define TLB_VECTOR 0x22 + #if defined(__SPECTRE_IBRS) #define SPECTRE_IBRS __SPECTRE_IBRS #else #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 int ibrs_enable(void); +int simd_init(void); void syscall_isr(void); +void pin_isr_load(void); struct cpu_info g_bsp_ci = {0}; -static struct gdtr bsp_gdtr = { - .limit = sizeof(struct gdt_entry) * 256 - 1, - .offset = (uintptr_t)&g_gdt_data[0] -}; +static bool bsp_init = false; __attribute__((__interrupt__)) static void @@ -69,13 +83,34 @@ cpu_halt_isr(void *p) __builtin_unreachable(); } +__attribute__((__interrupt__)) static void -setup_vectors(void) +tlb_shootdown_isr(void *p) { - if (halt_vector == 0) { - halt_vector = intr_alloc_vector("cpu-halt", IPL_HIGH); + 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; } + ipl = splraise(IPL_HIGH); + __invlpg(ci->shootdown_va); + + ci->shootdown_va = 0; + ci->tlb_shootdown = 0; + splx(ipl); +} + +static void +setup_vectors(void) +{ 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); @@ -89,7 +124,9 @@ setup_vectors(void) 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(HALT_VECTOR, IDT_INT_GATE, ISR(cpu_halt_isr), 0); + idt_set_desc(TLB_VECTOR, IDT_INT_GATE, ISR(tlb_shootdown_isr), 0); + pin_isr_load(); } static inline void @@ -97,7 +134,7 @@ init_tss(struct cpu_info *ci) { struct tss_desc *desc; - desc = (struct tss_desc *)&g_gdt_data[GDT_TSS]; + desc = (struct tss_desc *)&g_gdt_data[GDT_TSS_INDEX]; write_tss(ci, desc); tss_load(); } @@ -133,6 +170,53 @@ 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 +cpu_check_feat(struct cpu_info *ci) +{ + uint32_t unused, ebx; + + /* Extended features */ + CPUID(0x07, unused, ebx, unused, unused); + if (ISSET(ebx, BIT(7))) + ci->feat |= CPU_FEAT_SMEP; + if (ISSET(ebx, BIT(20))) + ci->feat |= CPU_FEAT_SMAP; +} + +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; + lapic_send_ipi(cip->apicid, IPI_SHORTHAND_NONE, TLB_VECTOR); + spinlock_release(&cip->lock); + } +} + void md_backtrace(void) { @@ -170,7 +254,7 @@ cpu_halt_all(void) } /* Send IPI to all cores */ - lapic_send_ipi(0, IPI_SHORTHAND_ALL, halt_vector); + lapic_send_ipi(0, IPI_SHORTHAND_ALL, HALT_VECTOR); for (;;); } @@ -186,7 +270,7 @@ cpu_halt_others(void) } /* Send IPI to all cores */ - lapic_send_ipi(0, IPI_SHORTHAND_OTHERS, halt_vector); + lapic_send_ipi(0, IPI_SHORTHAND_OTHERS, HALT_VECTOR); } void @@ -236,10 +320,53 @@ md_sync_all(void) } void +cpu_enable_smep(void) +{ + struct cpu_info *ci; + uint64_t cr4; + + /* Don't bother if not enabled */ + if (!CPU_SMEP) { + return; + } + + ci = this_cpu(); + if (!ISSET(ci->feat, CPU_FEAT_SMEP)) { + pr_trace_bsp("SMEP not supported\n"); + return; + } + + cr4 = amd64_read_cr4(); + cr4 |= BIT(20); /* CR4.SMEP */ + amd64_write_cr4(cr4); +} + +void +cpu_disable_smep(void) +{ + struct cpu_info *ci; + uint64_t cr4; + + if (!CPU_SMEP) { + return; + } + + ci = this_cpu(); + if (!ISSET(ci->feat, CPU_FEAT_SMEP)) { + return; + } + + cr4 = amd64_read_cr4(); + cr4 &= ~BIT(20); /* CR4.SMEP */ + amd64_write_cr4(cr4); +} + +void cpu_startup(struct cpu_info *ci) { ci->self = ci; - gdt_load(&bsp_gdtr); + ci->feat = 0; + gdt_load(); idt_load(); setup_vectors(); @@ -248,5 +375,13 @@ cpu_startup(struct cpu_info *ci) init_tss(ci); try_mitigate_spectre(); + cpu_check_feat(ci); + cpu_enable_smep(); + + 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..dbee32c 100644 --- a/sys/arch/amd64/amd64/mp.c +++ b/sys/arch/amd64/amd64/mp.c @@ -29,7 +29,9 @@ #include <sys/types.h> #include <sys/limine.h> +#include <sys/limits.h> #include <sys/syslog.h> +#include <sys/proc.h> #include <sys/spinlock.h> #include <sys/sched.h> #include <sys/atomic.h> @@ -40,12 +42,15 @@ #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) @@ -57,23 +62,46 @@ ap_trampoline(struct limine_smp_info *si) memset(ci, 0, sizeof(*ci)); cpu_startup(ci); + spinlock_acquire(&ci_list_lock); + ci_list[ncpu_up] = ci; + 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]; +} + +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; 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; if (resp->cpu_count == 1) { pr_trace("CPU has 1 core, no APs to bootstrap...\n"); @@ -90,6 +118,12 @@ mp_bootstrap_aps(struct cpu_info *ci) cpus[i]->goto_address = ap_trampoline; } + /* Start up idle threads */ + pr_trace("kicking %d idle threads...\n", ncpu); + for (uint32_t i = 0; i < ncpu; ++i) { + spawn(&g_proc0, sched_enter, NULL, 0, NULL); + } + /* Wait for all cores to be ready */ - while (ncpu_up < cpu_init_counter); + while ((ncpu_up - 1) < cpu_init_counter); } 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..63604a4 100644 --- a/sys/arch/amd64/amd64/proc_machdep.c +++ b/sys/arch/amd64/amd64/proc_machdep.c @@ -40,7 +40,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 +97,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 +124,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 +170,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,9 +210,10 @@ 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; 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..37efad4 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,16 @@ regdump(struct trapframe *tf) : "memory" ); - kprintf(OMIT_TIMESTAMP + if (tf->trapno == TRAP_PAGEFLT) { + pf_code(tf->error_code); + } + + panic("got fatal trap\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", tf->rax, tf->rcx, tf->rdx, tf->rbx, tf->rsi, tf->rdi, tf->rflags, cr2, cr3, @@ -94,6 +127,7 @@ static void trap_user(struct trapframe *tf) { struct proc *td = this_td(); + uintptr_t fault_addr; sigset_t sigset; sigemptyset(&sigset); @@ -101,6 +135,9 @@ trap_user(struct trapframe *tf) switch (tf->trapno) { case TRAP_PROTFLT: case TRAP_PAGEFLT: + if (tf->trapno == TRAP_PAGEFLT) { + pf_code(tf->error_code); + } sigaddset(&sigset, SIGSEGV); break; case TRAP_ARITH_ERR: @@ -112,6 +149,9 @@ trap_user(struct trapframe *tf) 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,9 @@ trap_syscall(struct trapframe *tf) void trap_handler(struct trapframe *tf) { - splraise(IPL_HIGH); + int ipl; + ipl = splraise(IPL_HIGH); if (tf->trapno >= NELEM(trap_type)) { panic("got unknown trap %d\n", tf->trapno); } @@ -151,10 +192,11 @@ trap_handler(struct trapframe *tf) /* Handle traps from userland */ if (ISSET(tf->cs, 3)) { + splx(ipl); trap_user(tf); return; } - regdump(tf); - panic("fatal trap - halting\n"); + trap_fatal(tf); + __builtin_unreachable(); } diff --git a/sys/arch/amd64/amd64/vector.S b/sys/arch/amd64/amd64/vector.S new file mode 100644 index 0000000..c820a41 --- /dev/null +++ b/sys/arch/amd64/amd64/vector.S @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <machine/frameasm.h> + +#define IDT_INT_GATE 0x8E + +.macro IDT_SET_VEC vec, sym + mov $\vec, %rdi + mov $IDT_INT_GATE, %rsi + lea \sym(%rip), %rdx + xor %rcx, %rcx + call idt_set_desc +.endm + + .text + ALIGN_TEXT +ioapic_common_func: + xor %rcx, %rcx // Clear counter +.walk: // Walk the handlers + lea g_intrs(%rip), %rbx // Grab table to RBX + lea (%rbx, %rcx, 8), %rbx // g_intrs + (8 * rcx) + mov (%rbx), %rdx // Grab the intr_hand + or %rdx, %rdx // No more? + jz 1f // Nope, return + + mov (%rdx), %rbx // intr_hand.func + add $8, %rdx // Get interrupt data + mov %rdx, %rdi // Pass the interrupt data + push %rcx // Save our counter + call *%rbx // Call the handler + pop %rcx // Restore our counter + or %rax, %rax // Was it theirs? (RET >= 1) + jnz done // Yes, we are done. +1: inc %rcx // Next + cmp $256, %rcx // Did we reach the end? + jl .walk // Nope, keep going +done: + call lapic_eoi + retq + + .globl pin_isr_load +pin_isr_load: + IDT_SET_VEC 35, ioapic_edge_0 + IDT_SET_VEC 36, ioapic_edge_1 + IDT_SET_VEC 37, ioapic_edge_2 + IDT_SET_VEC 38, ioapic_edge_3 + IDT_SET_VEC 39, ioapic_edge_4 + IDT_SET_VEC 40, ioapic_edge_5 + IDT_SET_VEC 41, ioapic_edge_6 + IDT_SET_VEC 42, ioapic_edge_7 + IDT_SET_VEC 43, ioapic_edge_8 + IDT_SET_VEC 44, ioapic_edge_9 + IDT_SET_VEC 45, ioapic_edge_10 + IDT_SET_VEC 46, ioapic_edge_11 + IDT_SET_VEC 47, ioapic_edge_12 + IDT_SET_VEC 48, ioapic_edge_13 + IDT_SET_VEC 49, ioapic_edge_14 + IDT_SET_VEC 50, ioapic_edge_15 + IDT_SET_VEC 51, ioapic_edge_16 + IDT_SET_VEC 52, ioapic_edge_17 + IDT_SET_VEC 53, ioapic_edge_18 + IDT_SET_VEC 54, ioapic_edge_19 + IDT_SET_VEC 55, ioapic_edge_20 + IDT_SET_VEC 56, ioapic_edge_21 + IDT_SET_VEC 57, ioapic_edge_22 + IDT_SET_VEC 58, ioapic_edge_23 + IDT_SET_VEC 59, ioapic_edge_24 + IDT_SET_VEC 60, ioapic_edge_25 + IDT_SET_VEC 61, ioapic_edge_26 + IDT_SET_VEC 62, ioapic_edge_27 + IDT_SET_VEC 63, ioapic_edge_28 + IDT_SET_VEC 64, ioapic_edge_29 + IDT_SET_VEC 65, ioapic_edge_30 + IDT_SET_VEC 66, ioapic_edge_31 + IDT_SET_VEC 67, ioapic_edge_32 + IDT_SET_VEC 68, ioapic_edge_33 + IDT_SET_VEC 69, ioapic_edge_34 + IDT_SET_VEC 70, ioapic_edge_35 + IDT_SET_VEC 71, ioapic_edge_36 + IDT_SET_VEC 72, ioapic_edge_37 + IDT_SET_VEC 73, ioapic_edge_38 + IDT_SET_VEC 74, ioapic_edge_39 + IDT_SET_VEC 75, ioapic_edge_40 + IDT_SET_VEC 76, ioapic_edge_41 + IDT_SET_VEC 77, ioapic_edge_42 + IDT_SET_VEC 78, ioapic_edge_43 + IDT_SET_VEC 79, ioapic_edge_44 + IDT_SET_VEC 80, ioapic_edge_45 + IDT_SET_VEC 81, ioapic_edge_46 + IDT_SET_VEC 82, ioapic_edge_47 + IDT_SET_VEC 83, ioapic_edge_48 + IDT_SET_VEC 84, ioapic_edge_49 + IDT_SET_VEC 85, ioapic_edge_50 + IDT_SET_VEC 86, ioapic_edge_51 + IDT_SET_VEC 87, ioapic_edge_52 + IDT_SET_VEC 88, ioapic_edge_53 + IDT_SET_VEC 89, ioapic_edge_54 + IDT_SET_VEC 90, ioapic_edge_55 + IDT_SET_VEC 91, ioapic_edge_56 + IDT_SET_VEC 92, ioapic_edge_57 + IDT_SET_VEC 93, ioapic_edge_58 + IDT_SET_VEC 94, ioapic_edge_59 + IDT_SET_VEC 95, ioapic_edge_60 + IDT_SET_VEC 96, ioapic_edge_61 + IDT_SET_VEC 97, ioapic_edge_62 + IDT_SET_VEC 97, ioapic_edge_63 + ret + +/* I/O APIC edge ISRs */ +INTRENTRY(ioapic_edge_0, ioapic_common_func) +INTRENTRY(ioapic_edge_1, ioapic_common_func) +INTRENTRY(ioapic_edge_2, ioapic_common_func) +INTRENTRY(ioapic_edge_3, ioapic_common_func) +INTRENTRY(ioapic_edge_4, ioapic_common_func) +INTRENTRY(ioapic_edge_5, ioapic_common_func) +INTRENTRY(ioapic_edge_6, ioapic_common_func) +INTRENTRY(ioapic_edge_7, ioapic_common_func) +INTRENTRY(ioapic_edge_8, ioapic_common_func) +INTRENTRY(ioapic_edge_9, ioapic_common_func) +INTRENTRY(ioapic_edge_10, ioapic_common_func) +INTRENTRY(ioapic_edge_11, ioapic_common_func) +INTRENTRY(ioapic_edge_12, ioapic_common_func) +INTRENTRY(ioapic_edge_13, ioapic_common_func) +INTRENTRY(ioapic_edge_14, ioapic_common_func) +INTRENTRY(ioapic_edge_15, ioapic_common_func) +INTRENTRY(ioapic_edge_16, ioapic_common_func) +INTRENTRY(ioapic_edge_17, ioapic_common_func) +INTRENTRY(ioapic_edge_18, ioapic_common_func) +INTRENTRY(ioapic_edge_19, ioapic_common_func) +INTRENTRY(ioapic_edge_20, ioapic_common_func) +INTRENTRY(ioapic_edge_21, ioapic_common_func) +INTRENTRY(ioapic_edge_22, ioapic_common_func) +INTRENTRY(ioapic_edge_23, ioapic_common_func) +INTRENTRY(ioapic_edge_24, ioapic_common_func) +INTRENTRY(ioapic_edge_25, ioapic_common_func) +INTRENTRY(ioapic_edge_26, ioapic_common_func) +INTRENTRY(ioapic_edge_27, ioapic_common_func) +INTRENTRY(ioapic_edge_28, ioapic_common_func) +INTRENTRY(ioapic_edge_29, ioapic_common_func) +INTRENTRY(ioapic_edge_30, ioapic_common_func) +INTRENTRY(ioapic_edge_31, ioapic_common_func) +INTRENTRY(ioapic_edge_32, ioapic_common_func) +INTRENTRY(ioapic_edge_33, ioapic_common_func) +INTRENTRY(ioapic_edge_34, ioapic_common_func) +INTRENTRY(ioapic_edge_35, ioapic_common_func) +INTRENTRY(ioapic_edge_36, ioapic_common_func) +INTRENTRY(ioapic_edge_37, ioapic_common_func) +INTRENTRY(ioapic_edge_38, ioapic_common_func) +INTRENTRY(ioapic_edge_39, ioapic_common_func) +INTRENTRY(ioapic_edge_40, ioapic_common_func) +INTRENTRY(ioapic_edge_41, ioapic_common_func) +INTRENTRY(ioapic_edge_42, ioapic_common_func) +INTRENTRY(ioapic_edge_43, ioapic_common_func) +INTRENTRY(ioapic_edge_44, ioapic_common_func) +INTRENTRY(ioapic_edge_45, ioapic_common_func) +INTRENTRY(ioapic_edge_46, ioapic_common_func) +INTRENTRY(ioapic_edge_47, ioapic_common_func) +INTRENTRY(ioapic_edge_48, ioapic_common_func) +INTRENTRY(ioapic_edge_49, ioapic_common_func) +INTRENTRY(ioapic_edge_50, ioapic_common_func) +INTRENTRY(ioapic_edge_51, ioapic_common_func) +INTRENTRY(ioapic_edge_52, ioapic_common_func) +INTRENTRY(ioapic_edge_53, ioapic_common_func) +INTRENTRY(ioapic_edge_54, ioapic_common_func) +INTRENTRY(ioapic_edge_55, ioapic_common_func) +INTRENTRY(ioapic_edge_56, ioapic_common_func) +INTRENTRY(ioapic_edge_57, ioapic_common_func) +INTRENTRY(ioapic_edge_58, ioapic_common_func) +INTRENTRY(ioapic_edge_59, ioapic_common_func) +INTRENTRY(ioapic_edge_60, ioapic_common_func) +INTRENTRY(ioapic_edge_61, ioapic_common_func) +INTRENTRY(ioapic_edge_62, ioapic_common_func) +INTRENTRY(ioapic_edge_63, ioapic_common_func) diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index db3ce4c..44ab8b5 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,10 +1,18 @@ +// // Kernel options -option SPECTRE_IBRS no -option SERIAL_DEBUG yes +// +// XXX: Indirect branch restricted speculation (SPECTRE_IBRS) +// is disabled by default as it can lead to significant +// performance degradation. +// +option SPECTRE_IBRS no // Enable the IBRS CPU feature +option SERIAL_DEBUG yes // Enable kmsg serial logging +option USER_KMSG no // Show kmsg in user consoles +option CPU_SMEP yes // Supervisor Memory Exec Protection // Kernel constants -setval SCHED_NQUEUE 4 +setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ) // Console attributes setval CONSOLE_BG 0x000000 -setval CONSOLE_FG 0x009800 +setval CONSOLE_FG 0xB57614 diff --git a/sys/arch/amd64/conf/link.ld b/sys/arch/amd64/conf/link.ld index 9c47a81..a43824f 100644 --- a/sys/arch/amd64/conf/link.ld +++ b/sys/arch/amd64/conf/link.ld @@ -29,6 +29,12 @@ SECTIONS __drivers_init_end = .; } :rodata + .drivers.defer : { + __driversd_init_start = .; + *(.drivers.defer .drivers.defer) + __driversd_init_end = .; + } :rodata + . += CONSTANT(MAXPAGESIZE); .data : { diff --git a/sys/arch/amd64/isa/i8042.c b/sys/arch/amd64/isa/i8042.c index 53679aa..69d9f92 100644 --- a/sys/arch/amd64/isa/i8042.c +++ b/sys/arch/amd64/isa/i8042.c @@ -39,6 +39,7 @@ #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> @@ -68,6 +69,7 @@ static struct proc polltd; static struct timer tmr; static bool is_init = false; +static void i8042_ibuf_wait(void); static int dev_send(bool aux, uint8_t data); static int i8042_kb_getc(uint8_t sc, char *chr); static void i8042_drain(void); @@ -103,41 +105,30 @@ kbd_set_leds(uint8_t mask) dev_send(false, mask); } -/* - * Poll the i8042 status register - * - * @bits: Status bits. - * @pollset: True to poll if set - */ -static int -i8042_statpoll(uint8_t bits, bool pollset) +static void +i8042_obuf_wait(void) { - size_t usec_start, usec; - size_t elapsed_msec; - uint8_t val; - bool tmp; + uint8_t status; - usec_start = tmr.get_time_usec(); for (;;) { - val = inb(I8042_STATUS); - tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits); - usec = tmr.get_time_usec(); - elapsed_msec = (usec - usec_start) / 1000; - - IO_NOP(); - - /* If tmp is set, the register updated in time */ - if (tmp) { - break; + status = inb(I8042_STATUS); + if (ISSET(status, I8042_OBUFF)) { + return; } + } +} - /* Exit with an error if we timeout */ - if (elapsed_msec > I8042_DELAY) { - return -ETIME; +static void +i8042_ibuf_wait(void) +{ + uint8_t status; + + for (;;) { + status = inb(I8042_STATUS); + if (!ISSET(status, I8042_IBUFF)) { + return; } } - - return val; } /* @@ -162,34 +153,47 @@ i8042_drain(void) static void i8042_write(uint16_t port, uint8_t val) { - i8042_statpoll(I8042_IBUFF, false); + i8042_ibuf_wait(); outb(port, val); } /* - * Read the i8042 config register + * Read from an i8042 register. + * + * @port: I/O port + */ +static uint8_t +i8042_read(uint16_t port) +{ + i8042_obuf_wait(); + return inb(port); +} + +/* + * Read the i8042 controller configuration + * byte. */ static uint8_t i8042_read_conf(void) { - i8042_drain(); + uint8_t conf; + i8042_write(I8042_CMD, I8042_GET_CONFB); - i8042_statpoll(I8042_OBUFF, true); - return inb(I8042_DATA); + i8042_obuf_wait(); + conf = i8042_read(I8042_DATA); + return conf; } /* - * Write the i8042 config register + * Write a new value to the i8042 controller + * configuration byte. */ static void -i8042_write_conf(uint8_t value) +i8042_write_conf(uint8_t conf) { - i8042_drain(); - i8042_statpoll(I8042_IBUFF, false); i8042_write(I8042_CMD, I8042_SET_CONFB); - i8042_statpoll(I8042_IBUFF, false); - i8042_write(I8042_DATA, value); - i8042_drain(); + i8042_ibuf_wait(); + i8042_write(I8042_DATA, conf); } /* @@ -205,14 +209,13 @@ dev_send(bool aux, uint8_t data) i8042_write(I8042_CMD, I8042_PORT1_SEND); } - i8042_statpoll(I8042_IBUFF, false); i8042_write(I8042_DATA, data); - i8042_statpoll(I8042_OBUFF, true); + i8042_obuf_wait(); return inb(I8042_DATA); } -void -i8042_kb_event(void) +static int +i8042_kb_event(void *sp) { struct cpu_info *ci; struct cons_input input; @@ -232,45 +235,31 @@ i8042_kb_event(void) input.chr = c; cons_ibuf_push(&g_root_scr, input); done: - ci->irq_mask &= CPU_IRQ(1); + ci->irq_mask &= ~CPU_IRQ(1); spinlock_release(&isr_lock); - lapic_eoi(); + return 1; /* handled */ } static void i8042_en_intr(void) { + struct intr_hand ih; uint8_t conf; - int vec; - - i8042_write(I8042_CMD, I8042_DISABLE_PORT0); - vec = intr_alloc_vector("i8042-kb", IPL_BIO); - idt_set_desc(vec, IDT_INT_GATE, ISR(i8042_kb_isr), IST_HW_IRQ); - ioapic_set_vec(KB_IRQ, vec); - ioapic_irq_unmask(KB_IRQ); + ih.func = i8042_kb_event; + ih.priority = IPL_BIO; + ih.irq = KB_IRQ; + intr_register("i8042-kb", &ih); - /* Setup config bits */ + /* + * Enable the clock of PS/2 port 0 and tell + * the controller that we are accepting + * interrupts. + */ conf = i8042_read_conf(); + conf &= ~I8042_PORT0_CLK; conf |= I8042_PORT0_INTR; - conf &= ~I8042_PORT1_INTR; i8042_write_conf(conf); - - i8042_write(I8042_CMD, I8042_ENABLE_PORT0); -} - -static void -esckey_reboot(void) -{ - syslock(); - kprintf(OMIT_TIMESTAMP "** Machine going down for a reboot\f"); - - for (size_t i = 0; i < 3; ++i) { - kprintf(OMIT_TIMESTAMP ".\f"); - tmr.msleep(1000); - } - - cpu_reboot(0); } /* @@ -287,10 +276,6 @@ i8042_kb_getc(uint8_t sc, char *chr) bool release = ISSET(sc, BIT(7)); switch (sc) { - /* Left alt [press] */ - case 0x38: - esckey_reboot(); - break; /* Caps lock [press] */ case 0x3A: /* @@ -346,43 +331,30 @@ i8042_kb_getc(uint8_t sc, char *chr) return 0; } -static void -i8042_sync_loop(void) -{ - /* Wake up the bus */ - outb(I8042_DATA, 0x00); - i8042_drain(); - - for (;;) { - i8042_sync(); - md_pause(); - } -} - /* * Grabs a key from the keyboard, used typically * for syncing the machine however can be used - * to bypass IRQs in case of buggy EC. + * to bypass IRQs to prevent lost bytes. */ void i8042_sync(void) { static struct spinlock lock; struct cons_input input; - uint8_t data; + uint8_t data, status; char c; if (spinlock_try_acquire(&lock)) { return; } - if (ISSET(quirks, I8042_HOSTILE) && is_init) { - if (i8042_statpoll(I8042_OBUFF, true) < 0) { - /* No data ready */ + if (is_init) { + status = inb(I8042_STATUS); + if (!ISSET(status, I8042_OBUFF)) { goto done; } - data = inb(I8042_DATA); + data = inb(I8042_DATA); if (i8042_kb_getc(data, &c) == 0) { input.scancode = data; input.chr = c; @@ -399,9 +371,20 @@ i8042_quirk(int mask) quirks |= mask; } +static void +i8042_sync_loop(void) +{ + for (;;) { + i8042_obuf_wait(); + i8042_sync(); + } +} + static int i8042_init(void) { + const char *prodver = NULL; + /* Try to request a general purpose timer */ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) { pr_error("failed to fetch general purpose timer\n"); @@ -420,6 +403,9 @@ i8042_init(void) return -ENODEV; } + i8042_write(I8042_CMD, I8042_DISABLE_PORT0); + i8042_write(I8042_CMD, I8042_DISABLE_PORT1); + /* * On some thinkpads, e.g., the T420s, the EC implementing * the i8042 logic likes to play cop and throw NMIs at us @@ -427,9 +413,12 @@ 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); } @@ -440,13 +429,10 @@ i8042_init(void) i8042_en_intr(); } - if (dev_send(false, 0xFF) == 0xFC) { - pr_error("kbd self test failure\n"); - return -EIO; - } - + i8042_write(I8042_CMD, I8042_ENABLE_PORT0); + i8042_drain(); is_init = true; return 0; } -DRIVER_EXPORT(i8042_init); +DRIVER_EXPORT(i8042_init, "i8042"); diff --git a/sys/arch/amd64/isa/mc1468.c b/sys/arch/amd64/isa/mc1468.c new file mode 100644 index 0000000..1f3ae1d --- /dev/null +++ b/sys/arch/amd64/isa/mc1468.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/driver.h> +#include <sys/device.h> +#include <sys/syslog.h> +#include <fs/devfs.h> +#include <machine/pio.h> +#include <machine/cdefs.h> +#include <string.h> + +#define MC1468_REGSEL 0x70 +#define MC1468_DATA 0x71 + +/* Register A flags */ +#define MC1468_UPDATING BIT(7) + +/* Register B flags */ +#define MC1468_DAYSAVE BIT(1) +#define MC1468_CLOCK24 BIT(2) + +static struct cdevsw mc1468_cdevsw; + +static uint8_t +bin_dabble(uint8_t bin) +{ + uint8_t retval = 0; + uint8_t nibble; + + for (int i = 7; i >= 0; --i) { + retval <<= 1; + if (bin & (1 << i)) { + retval |= 1; + } + + for (int j = 0; j < 2; ++j) { + nibble = retval & (retval >> (4 * nibble)) & 0x0F; + if (nibble >= 5) { + retval += 0x03 << (4 * nibble); + } + } + } + + return retval; +} + +/* + * Read a byte from an MC1468XX register. + */ +static uint8_t +mc1468_read(uint8_t reg) +{ + outb(MC1468_REGSEL, reg); + return inb(MC1468_DATA); +} + +/* + * Write a byte to the MC1468XX register. + */ +static void +mc1468_write(uint8_t reg, uint8_t val) +{ + outb(MC1468_REGSEL, reg); + outb(MC1468_DATA, val); +} + +/* + * Returns true if the MC1468XX is updating + * its time registers. + */ +static bool +mc1468_updating(void) +{ + uint8_t reg_b; + + reg_b = mc1468_read(0xB); + return ISSET(reg_b, MC1468_UPDATING) != 0; +} + +/* + * Check if date `a' and date `b' are synced. + * Used to make sure a bogus date caused by a + * read right before an MC1468XX register + * update doesn't occur. + */ +static bool +mc1468_date_synced(struct date *a, struct date *b) +{ + if (a->year != b->year) + return false; + if (a->month != b->month) + return false; + if (a->day != b->day) + return false; + if (a->sec != b->sec) + return false; + if (a->min != b->min) + return false; + if (a->hour != b->hour) + return false; + + return true; +} + +/* + * Sometimes the clock chip may encode the + * date in binary-coded-decimal. This function + * converts a date in BCD format to plain binary. + */ +static void +mc1468_bcd_conv(struct date *dp) +{ + dp->year = (dp->year & 0x0F) + ((dp->year / 16) * 10); + dp->month = (dp->month & 0x0F) + ((dp->month / 16) * 10); + dp->day = (dp->day & 0x0F) + ((dp->day / 16) * 10); + dp->sec = (dp->sec & 0x0F) + ((dp->sec / 16) * 10); + dp->min = (dp->min & 0x0F) + ((dp->min / 16) * 10); + dp->hour = (dp->hour & 0x0F) + (((dp->hour & 0x70) / 16) * 10); + dp->hour |= dp->hour & 0x80; +} + +/* + * Read the time for the clock without syncing + * it up. + * + * XXX: Please use mc1468_get_date() instead as + * this function may return inconsistent + * values if not used correctly. + */ +static void +__mc1468_get_time(struct date *dp) +{ + dp->year = mc1468_read(0x09); + dp->month = mc1468_read(0x08); + dp->day = mc1468_read(0x07); + dp->sec = mc1468_read(0x00); + dp->min = mc1468_read(0x02); + dp->hour = mc1468_read(0x04); +} + +/* + * Write a new time/date to the chip. + */ +static void +mc1468_set_date(const struct date *dp) +{ + while (mc1468_updating()) { + md_pause(); + } + + mc1468_write(0x08, bin_dabble(dp->month)); + mc1468_write(0x07, bin_dabble(dp->day)); + mc1468_write(0x04, bin_dabble(dp->hour)); + mc1468_write(0x02, bin_dabble(dp->min)); + mc1468_write(0x00, bin_dabble(dp->sec)); +} + +static int +mc1468_get_date(struct date *dp) +{ + struct date date_cur, date_last; + uint8_t reg_b = mc1468_read(0x0B); + + while (mc1468_updating()) { + __mc1468_get_time(&date_last); + } + + /* + * Get the current date and time. + * + * XXX: The date and time returned by __mc1468_get_time() + * may at times be out of sync, read it twice to + * make sure everything is synced up. + */ + do { + while (mc1468_updating()) { + md_pause(); + } + __mc1468_get_time(&date_last); + date_cur.year = date_last.year; + date_cur.month = date_last.month; + date_cur.day = date_last.day; + date_cur.sec = date_last.sec; + date_cur.min = date_last.min; + date_cur.hour = date_last.hour; + } while (!mc1468_date_synced(&date_cur, &date_last)); + + /* Is this in BCD? */ + if (!ISSET(reg_b, 0x04)) { + mc1468_bcd_conv(&date_cur); + } + + /* 24-hour mode? */ + if (ISSET(reg_b, MC1468_CLOCK24)) { + date_cur.hour = ((date_cur.hour & 0x7F) + 12) % 24; + } + + date_cur.year += 2000; + *dp = date_cur; + return 0; +} + +static int +mc1468_dev_read(dev_t dev, struct sio_txn *sio, int flags) +{ + struct date d; + size_t len = sizeof(d); + + if (sio->len > len) { + sio->len = len; + } + + mc1468_get_date(&d); + memcpy(sio->buf, &d, sio->len); + return sio->len; +} + +static int +mc1468_dev_write(dev_t dev, struct sio_txn *sio, int flags) +{ + struct date d; + size_t len = sizeof(d); + + if (sio->len > len) { + sio->len = len; + } + + memcpy(&d, sio->buf, sio->len); + mc1468_set_date(&d); + return sio->len; +} + +static int +mc1468_init(void) +{ + char devname[] = "rtc"; + devmajor_t major; + dev_t dev; + + major = dev_alloc_major(); + dev = dev_alloc(major); + dev_register(major, dev, &mc1468_cdevsw); + devfs_create_entry(devname, major, dev, 0444); + return 0; +} + +static struct cdevsw mc1468_cdevsw = { + .read = mc1468_dev_read, + .write = mc1468_dev_write, +}; + +DRIVER_EXPORT(mc1468_init, "mc1468"); diff --git a/sys/arch/amd64/isa/spkr.c b/sys/arch/amd64/isa/spkr.c index b1bd2a2..c96e5f9 100644 --- a/sys/arch/amd64/isa/spkr.c +++ b/sys/arch/amd64/isa/spkr.c @@ -30,14 +30,60 @@ #include <sys/cdefs.h> #include <sys/errno.h> #include <sys/param.h> +#include <sys/device.h> +#include <sys/driver.h> +#include <fs/devfs.h> #include <dev/timer.h> #include <machine/isa/spkr.h> #include <machine/isa/i8254.h> #include <machine/pio.h> +#include <string.h> #define DIVIDEND 1193180 #define CTRL_PORT 0x61 +static struct cdevsw beep_cdevsw; + +/* + * Write to the pcspkr + * + * Bits 15:0 - frequency (hz) + * Bits 31:16 - duration (msec) + */ +static int +dev_write(dev_t dev, struct sio_txn *sio, int flags) +{ + uint32_t payload = 0; + uint16_t hz; + uint16_t duration; + size_t len = sizeof(payload); + + if (sio->len < len) { + return -EINVAL; + } + + memcpy(&payload, sio->buf, len); + hz = payload & 0xFFFF; + duration = (payload >> 16) & 0xFFFF; + pcspkr_tone(hz, duration); + return sio->len; +} + +static int +beep_init(void) +{ + char devname[] = "beep"; + devmajor_t major; + dev_t dev; + + /* Register the device here */ + major = dev_alloc_major(); + dev = dev_alloc(major); + dev_register(major, dev, &beep_cdevsw); + devfs_create_entry(devname, major, dev, 0666); + return 0; +} + int pcspkr_tone(uint16_t freq, uint32_t msec) { @@ -67,3 +113,10 @@ pcspkr_tone(uint16_t freq, uint32_t msec) outb(CTRL_PORT, tmp & ~3); return 0; } + +static struct cdevsw beep_cdevsw = { + .read = noread, + .write = dev_write +}; + +DRIVER_EXPORT(beep_init, "pcspkr"); diff --git a/sys/arch/amd64/pci/pci_machdep.c b/sys/arch/amd64/pci/pci_machdep.c index 43065b0..5b49a78 100644 --- a/sys/arch/amd64/pci/pci_machdep.c +++ b/sys/arch/amd64/pci/pci_machdep.c @@ -33,6 +33,7 @@ #include <sys/mmio.h> #include <dev/pci/pci.h> #include <dev/pci/pciregs.h> +#include <machine/pci/pci.h> #include <machine/pio.h> #include <machine/bus.h> #include <machine/cpu.h> @@ -73,8 +74,8 @@ pci_get_barreg(struct pci_device *dev, uint8_t bar) } } -pcireg_t -pci_readl(struct pci_device *dev, uint32_t offset) +__weak pcireg_t +md_pci_readl(struct pci_device *dev, uint32_t offset) { uint32_t address; @@ -83,8 +84,8 @@ pci_readl(struct pci_device *dev, uint32_t offset) return inl(0xCFC) >> ((offset & 3) * 8); } -void -pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val) +__weak void +md_pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val) { uint32_t address; @@ -163,6 +164,7 @@ pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr) { volatile uint64_t *tbl; struct cpu_info *ci; + struct intr_hand ih, *ih_res; uint32_t data, msg_ctl; uint64_t msg_addr, tmp; uint16_t tbl_off; @@ -184,9 +186,14 @@ pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr) tbl = (void *)((dev->bar[bir] & PCI_BAR_MEMMASK) + MMIO_OFFSET); tbl = (void *)((char *)tbl + tbl_off); - /* Get the vector and setup handler */ - vector = intr_alloc_vector(intr->name, IPL_BIO); - idt_set_desc(vector, IDT_INT_GATE, ISR(intr->handler), 0); + ih.func = intr->handler; + ih.priority = IPL_BIO; + ih.irq = -1; + ih_res = intr_register(intr->name, &ih); + if (ih_res == NULL) { + return -EIO; + } + vector = ih_res->vector; /* * Setup the message data at bits 95:64 of the message diff --git a/sys/dev/acpi/uacpi.c b/sys/dev/acpi/uacpi.c index 7dbcb35..b133288 100644 --- a/sys/dev/acpi/uacpi.c +++ b/sys/dev/acpi/uacpi.c @@ -41,10 +41,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> @@ -259,17 +259,16 @@ uacpi_status uacpi_kernel_install_interrupt_handler(uacpi_u32 irq, uacpi_interrupt_handler fn, uacpi_handle ctx, uacpi_handle *out_irq_handle) { - int vec; + struct intr_hand ih; + + ih.func = (void *)fn; + ih.priority = IPL_HIGH; + ih.irq = irq; + if (intr_register("acpi", &ih) == NULL) { + return UACPI_STATUS_INTERNAL_ERROR; + } -#if defined(__x86_64__) - vec = intr_alloc_vector("acpi", IPL_HIGH); - idt_set_desc(vec, IDT_INT_GATE, ISR(fn), IST_HW_IRQ); - ioapic_set_vec(irq, vec); - ioapic_irq_unmask(irq); return UACPI_STATUS_OK; -#else - return UACPI_STATUS_UNIMPLEMENTED; -#endif /* __x86_64__ */ } uacpi_status @@ -514,9 +513,18 @@ uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot(void) { static uacpi_u64 time = 0; + static struct timer tmr = {0}; + tmrr_status_t tmr_error; + + if (time == 0) { + tmr_error = req_timer(TIMER_GP, &tmr); + if (tmr_error != TMRR_SUCCESS) { + time += 1000000; + return time; + } + } - /* TODO */ - time += 1000000; + time = tmr.get_time_nsec(); return time; } diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c index b89727f..9921ff8 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 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; } @@ -290,6 +373,7 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags) return -EAGAIN; } + cons_attach(); spinlock_acquire(&g_root_scr.lock); for (;;) { /* Buffer too small */ @@ -338,6 +422,144 @@ 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; +} + +/* + * 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 +569,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 +608,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 +621,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 +635,7 @@ void cons_expose(void) { static int once = 0; + struct ctlfs_dev ctl; char devname[] = "console"; devmajor_t major; dev_t dev; @@ -426,11 +645,21 @@ 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 the control file */ + ctl.mode = 0666; + ctlfs_create_node(devname, &ctl); + ctl.devname = devname; + ctl.ops = &cons_feat_ctl; + ctlfs_create_entry("feat", &ctl); once ^= 1; } @@ -438,3 +667,8 @@ 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 +}; diff --git a/sys/dev/cons/cons_ansi.c b/sys/dev/cons/cons_ansi.c new file mode 100644 index 0000000..e00bdc9 --- /dev/null +++ b/sys/dev/cons/cons_ansi.c @@ -0,0 +1,181 @@ +/* + * 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 == 'H') { + cons_clear_scr(scr, g_root_scr.bg); + return ANSI_UPDATE_CURSOR; + } + break; + } + + if (!statep->set_fg && !statep->set_bg) { + /* Reset attributes? */ + if (statep->reset_color) { + ansi_reset(statep); + cons_reset_color(scr); + return ANSI_UPDATE_COLOR; + } + + /* Mark attributes to be reset? */ + if (c == '0') { + statep->reset_color = 1; + statep->prev = c; + return c; + } + + /* Expect foreground */ + if (c != '3') { + ansi_reset(statep); + return 0; + } + statep->set_fg = 1; + statep->prev = c; + return c; + } + + if (statep->set_fg && c != ';') { + /* Make sure this is valid */ + if (!is_valid_color(c)) { + ansi_reset(statep); + return 0; + } + + /* Set the foreground */ + statep->fg = colortab[c - '0']; + statep->set_bg = 1; + statep->set_fg = 0; + statep->prev = c; + return c; + } + + if (statep->set_bg) { + if (c == ';') { + statep->prev = c; + return c; + } + + /* Expect '4' after ';' */ + if (statep->prev == ';' && c != '4') { + ansi_reset(statep); + return 0; + } + + if (c == 'm') { + cons_update_color(scr, statep->fg, statep->bg); + ansi_reset(statep); + return ANSI_UPDATE_COLOR; + } + + /* Make sure this is valid */ + if (!is_valid_color(c)) { + ansi_reset(statep); + return 0; + } + + /* Set the background */ + statep->bg = colortab[c - '0']; + statep->prev = c; + return c; + } + + ansi_reset(statep); + return 0; +} diff --git a/sys/dev/dcdr/cache.c b/sys/dev/dcdr/cache.c index c44c8ea..33f977e 100644 --- a/sys/dev/dcdr/cache.c +++ b/sys/dev/dcdr/cache.c @@ -126,6 +126,20 @@ struct dcd * dcdr_cachein(struct dcdr *dcdr, void *block, off_t lba) { struct dcd *dcd, *tmp; + struct dcdr_lookup check; + int status; + + /* + * If there is already a block within this + * DCDR, then we simply need to copy the + * new data into the old DCD. + */ + status = dcdr_lookup(dcdr, lba, &check); + if (status == 0) { + dcd = check.dcd_res; + memcpy(dcd->block, block, dcdr->bsize); + return dcd; + } dcd = dynalloc(sizeof(*dcd)); if (dcd == NULL) { diff --git a/sys/dev/dmi/dmi.c b/sys/dev/dmi/dmi.c new file mode 100644 index 0000000..59946ad --- /dev/null +++ b/sys/dev/dmi/dmi.c @@ -0,0 +1,251 @@ +/* + * 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/acpi/tables.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_FAMILY 0x03 +#define PROCINFO_MANUFACT 0x02 +#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_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); +} + +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 = (void *)entry64->addr; + smax_len = entry64->max_size; + } else if (resp->entry_32 != 0) { + entry32 = (void *)(uint64_t)resp->entry_32; + hdr = (void *)(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); + } + + return 0; +} + +DRIVER_EXPORT(dmi_init, "dmi"); diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c index 8b72cde..d17c6a3 100644 --- a/sys/dev/ic/ahci.c +++ b/sys/dev/ic/ahci.c @@ -41,10 +41,12 @@ #include <dev/timer.h> #include <dev/ic/ahcivar.h> #include <dev/ic/ahciregs.h> +#include <dev/dcdr/cache.h> #include <fs/devfs.h> #include <fs/ctlfs.h> #include <vm/dynalloc.h> #include <vm/physmem.h> +#include <machine/cdefs.h> #include <string.h> #define pr_trace(fmt, ...) kprintf("ahci: " fmt, ##__VA_ARGS__) @@ -55,6 +57,8 @@ 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; /* * Poll register to have 'bits' set/unset. @@ -94,6 +98,18 @@ ahci_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset) return 0; } +static struct hba_device * +ahci_get_dev(dev_t dev) +{ + for (int i = 0; i < devs_max; ++i) { + if (devs[i].dev == dev) { + return &devs[i]; + } + } + + return NULL; +} + /* * Allocate a command slot for a port on * the HBA. @@ -103,7 +119,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); @@ -315,22 +330,24 @@ 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 @@ -340,34 +357,50 @@ hba_port_reset(struct ahci_hba *hba, struct hba_port *port) sctl &= ~AHCI_DET_COMRESET; mmio_write32(&port->sctl, sctl); - /* - * Now we'll need to grab some power management - * and detection flags as the port must have - * a device present along with an active - * interface. - */ - ssts = mmio_read32(&port->ssts); - det = AHCI_PXSCTL_DET(ssts); - ipm = AHCI_PXSSTS_IPM(ssts); + for (;;) { + if (elapsed >= AHCI_TIMEOUT) { + break; + } + ssts = mmio_read32(&port->ssts); + det = AHCI_PXSSTS_DET(ssts); + if (det == AHCI_DET_COMM) { + break; + } - /* If there is no device, fake success */ - if (det == AHCI_DET_NULL) { - return 0; + tmr.msleep(10); + elapsed += 10; } + ipm = AHCI_PXSSTS_IPM(ssts); + spd = AHCI_PXSSTS_SPD(ssts); + + if (det == AHCI_DET_PRESENT) { + pr_error("SATA link timeout\n"); + return -EAGAIN; + } if (det != AHCI_DET_COMM) { - pr_trace("failed to establish link\n"); return -EAGAIN; } + /* + * Ensure the interface is in an active + * state. + */ if (ipm != AHCI_IPM_ACTIVE) { - pr_trace("device interface not active\n"); + pr_error("device interface not active\n"); return -EAGAIN; } - if ((error = hba_port_stop(port)) < 0) { - pr_trace("failed to stop port\n"); - return error; + switch (spd) { + case AHCI_SPD_GEN1: + pr_trace("SATA link rate @ ~1.5 Gb/s\n"); + break; + case AHCI_SPD_GEN2: + pr_trace("SATA link rate @ ~3 Gb/s\n"); + break; + case AHCI_SPD_GEN3: + pr_trace("SATA link rate @ ~6 Gb/s\n"); + break; } return 0; @@ -416,12 +449,14 @@ 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 hba_port *port; struct ahci_cmd_hdr *cmdhdr; struct ahci_cmdtab *cmdtbl; struct ahci_fis_h2d *fis; + uint16_t *p; int cmdslot, status; buf = vm_alloc_frame(1); @@ -430,6 +465,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,6 +497,9 @@ ahci_identify(struct ahci_hba *hba, struct hba_port *port) } ahci_dump_identity(PHYS_TO_VIRT(buf)); + p = (uint16_t *)PHYS_TO_VIRT(buf); + dp->nlba = (p[61] << 16) | p[60]; + pr_trace("max block size: %d\n", dp->nlba); done: vm_free_frame(buf, 1); return status; @@ -470,7 +509,7 @@ done: * Send a read/write command to a SATA drive * * @hba: Host bus adapter of target port - * @port: Port to send over + * @dev: Device to send over * @sio: System I/O descriptor * @write: If true, data pointed to by `sio` will be written * @@ -480,14 +519,21 @@ done: * - The `offset` field in `sio` is the LBA address. */ static int -ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio, +ahci_sata_rw(struct ahci_hba *hba, struct hba_device *dev, struct sio_txn *sio, bool write) { paddr_t base, buf; + char *p, *dest; + bool dcdr_hit = false; + struct hba_port *port; + struct dcdr_lookup dcd_lookup; + struct dcd *dcd; struct ahci_cmd_hdr *cmdhdr; struct ahci_cmdtab *cmdtbl; struct ahci_fis_h2d *fis; int cmdslot, status; + size_t nblocks, cur_lba; + size_t len; if (sio == NULL) { return -EINVAL; @@ -496,6 +542,60 @@ ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio, return -EINVAL; } + port = dev->io; + + /* + * Compute how many blocks can be cached. + * + * XXX: We do not want to fill the entire DCDR + * with a single drive read to reduce the + * frequency of DCDR evictions. + * + * TODO: We should also take advantage of logical + * block coalescing. + */ + nblocks = sio->len; + if (nblocks >= AHCI_DCDR_CAP) { + nblocks = AHCI_DCDR_CAP / 2; + } + + /* + * If we are reading the drive, see if we have + * anything in the cache. + * + * XXX: If there is a break in the cache and we + * have a miss inbetween, other DCDs are + * ignored. Wonder how we can mitigate + * fragmentation. + */ + cur_lba = sio->offset; + len = sio->len; + for (size_t i = 0; i < nblocks && !write; ++i) { + status = dcdr_lookup(dev->dcdr, cur_lba, &dcd_lookup); + if (status != 0) { + break; + } + if (len == 0) { + break; + } + + dcdr_hit = true; + dcd = dcd_lookup.dcd_res; + + /* Hit, copy the cached data */ + dest = &((char *)sio->buf)[i * 512]; + p = dcd->block; + memcpy(dest, p, 512); + + ++cur_lba; + --len; + } + + /* Did we get everything already? */ + if (len == 0) { + return 0; + } + buf = VIRT_TO_PHYS(sio->buf); cmdslot = ahci_alloc_cmdslot(hba, port); if (cmdslot < 0) { @@ -524,22 +624,32 @@ ahci_sata_rw(struct ahci_hba *hba, struct hba_port *port, struct sio_txn *sio, fis->device = (1 << 6); /* LBA */ /* Setup LBA */ - fis->lba0 = sio->offset & 0xFF; - fis->lba1 = (sio->offset >> 8) & 0xFF; - fis->lba2 = (sio->offset >> 16) & 0xFF; - fis->lba3 = (sio->offset >> 24) & 0xFF; - fis->lba4 = (sio->offset >> 32) & 0xFF; - fis->lba5 = (sio->offset >> 40) & 0xFF; + fis->lba0 = cur_lba & 0xFF; + fis->lba1 = (cur_lba >> 8) & 0xFF; + fis->lba2 = (cur_lba >> 16) & 0xFF; + fis->lba3 = (cur_lba >> 24) & 0xFF; + fis->lba4 = (cur_lba >> 32) & 0xFF; + fis->lba5 = (cur_lba >> 40) & 0xFF; /* Setup count */ - fis->countl = sio->len & 0xFF; - fis->counth = (sio->len >> 8) & 0xFF; + fis->countl = len & 0xFF; + fis->counth = (len >> 8) & 0xFF; - pr_trace("SUBMIT: RIGHT!\n"); if ((status = ahci_submit_cmd(hba, port, cmdslot)) != 0) { return status; } + /* Don't cache again on hit */ + if (!write && dcdr_hit) { + return 0; + } + + /* Cache our read */ + for (size_t i = 0; i < nblocks; ++i) { + cur_lba = sio->offset + i; + p = sio->buf; + dcdr_cachein(dev->dcdr, &p[i * 512], cur_lba); + } return 0; } @@ -549,7 +659,6 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write) const size_t BSIZE = 512; struct sio_txn wr_sio; struct hba_device *devp; - struct ahci_hba *hba; size_t block_count, len; off_t block_off, read_off; char *buf; @@ -561,11 +670,11 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write) if (sio->len == 0 || sio->buf == NULL) { return -EINVAL; } - if (dev >= devs_max) { + if (dev > devs_max) { return -ENODEV; } - devp = &devs[dev]; + devp = ahci_get_dev(dev); if (__unlikely(devp == NULL)) { return -ENODEV; } @@ -595,14 +704,14 @@ sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write) wr_sio.buf = buf; wr_sio.len = block_count; wr_sio.offset = block_off; - status = ahci_sata_rw(hba, devp->io, &wr_sio, write); + status = ahci_sata_rw(&g_hba, devp, &wr_sio, write); if (status == 0 && !write) { read_off = sio->offset & (BSIZE - 1); memcpy(sio->buf, buf + read_off, sio->len); } dynfree(buf); - return status; + return sio->len; } /* @@ -611,10 +720,46 @@ 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; +} + +/* * Initialize a drive on an HBA port * * @hba: HBA descriptor @@ -629,27 +774,19 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) struct hba_device *dp; struct ctlfs_dev dev; size_t clen, pagesz; - uint32_t lo, hi, ssts; - uint8_t det; + uint32_t lo, hi, sig; paddr_t fra, cmdlist, tmp; int error; pagesz = DEFAULT_PAGESIZE; port = &abar->ports[portno]; - /* Is anything on the port? */ - ssts = mmio_read32(&port->ssts); - det = AHCI_PXSCTL_DET(ssts); - switch (det) { - case AHCI_DET_NULL: - /* No device attached */ - return 0; - case AHCI_DET_PRESENT: - if ((error = hba_port_reset(hba, port)) < 0) { - pr_trace("failed to reset port %d\n", portno); - return error; - } - break; + if ((error = hba_port_reset(hba, port)) < 0) { + return error; + } + sig = mmio_read32(&port->sig); + if (sig == ATAPI_SIG) { + return -ENOTSUP; } pr_trace("found device @ port %d\n", portno); @@ -658,6 +795,12 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) dp->hba = hba; dp->dev = portno; + dp->dcdr = dcdr_alloc(512, AHCI_DCDR_CAP); + if (dp->dcdr == NULL) { + pr_error("failed to alloc dcdr\n"); + return -ENOMEM; + } + /* Allocate a command list */ clen = ALIGN_UP(hba->nslots * AHCI_CMDENTRY_SIZE, pagesz); clen /= pagesz; @@ -677,8 +820,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) } dp->fra = PHYS_TO_VIRT(fra); - memset(dp->cmdlist, 0, clen * pagesz); - memset(dp->fra, 0, pagesz); /* Write the command list */ lo = cmdlist & 0xFFFFFFFF; @@ -697,7 +838,6 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) tmp = vm_alloc_frame(1); dp->cmdlist[i].prdtl = 1; dp->cmdlist[i].ctba = tmp; - memset(PHYS_TO_VIRT(tmp), 0, pagesz); } mmio_write32(&port->serr, 0xFFFFFFFF); @@ -712,7 +852,7 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) return error; } - ahci_identify(hba, port); + ahci_identify(hba, dp); if (hba->major == 0) { hba->major = dev_alloc_major(); @@ -733,7 +873,7 @@ ahci_init_port(struct ahci_hba *hba, uint32_t portno) dev.devname = devname; dev.ops = &g_sata_bsize_ops; ctlfs_create_entry("bsize", &dev); - return devfs_create_entry(devname, hba->major, dp->dev, 0444); + return devfs_create_entry(devname, hba->major, dp->dev, 060444); } /* @@ -841,10 +981,9 @@ ahci_init(void) { struct pci_lookup lookup; int status; - struct ahci_hba hba; void *abar_vap = NULL; - hba.major = 0; + g_hba.major = 0; lookup.pci_class = 0x01; lookup.pci_subclass = 0x06; @@ -890,14 +1029,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_DEFER(ahci_init, "ahci"); diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c index 704fdec..147ab4f 100644 --- a/sys/dev/ic/nvme.c +++ b/sys/dev/ic/nvme.c @@ -662,4 +662,4 @@ static struct bdevsw nvme_bdevsw = { .write = nowrite }; -DRIVER_EXPORT(nvme_init); +DRIVER_DEFER(nvme_init, "nvme"); diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index d59f68f..9dfb90e 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. * @@ -273,12 +340,46 @@ 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/rt8139.c b/sys/dev/phy/rtl.c index ab8a6c0..d096d1a 100644 --- a/sys/dev/phy/rt8139.c +++ b/sys/dev/phy/rtl.c @@ -30,31 +30,30 @@ #include <sys/types.h> #include <sys/errno.h> #include <sys/syslog.h> +#include <sys/spinlock.h> #include <sys/driver.h> #include <sys/device.h> #include <dev/pci/pci.h> -#include <dev/phy/rt8139.h> +#include <dev/phy/rtl.h> #include <dev/timer.h> #include <dev/pci/pciregs.h> -#include <net/if_ether.h> +#include <net/netbuf.h> +#include <net/if_var.h> #include <vm/physmem.h> +#include <vm/dynalloc.h> #include <vm/vm.h> #include <machine/pio.h> +#include <machine/intr.h> #include <string.h> -/* TODO: Make this smoother */ -#if defined(__x86_64__) -#include <machine/intr.h> -#include <machine/ioapic.h> -#include <machine/lapic.h> -#include <machine/idt.h> -#endif +#define IFNAME "rt0" -#define pr_trace(fmt, ...) kprintf("rt8139: " fmt, ##__VA_ARGS__) +#define pr_trace(fmt, ...) kprintf("rt81xx: " fmt, ##__VA_ARGS__) #define pr_error(...) pr_trace(__VA_ARGS__) #define RX_BUF_SIZE 3 /* In pages */ #define RX_REAL_BUF_SIZE 8192 /* In bytes */ +#define TXQ_ENTRIES 4 #define RX_PTR_MASK (~3) @@ -65,12 +64,26 @@ #define HAVE_PIO 0 #endif /* _MACHINE_HAVE_PIO */ +static struct spinlock netif_lock; +static struct netbuf netif_buf[TXQ_ENTRIES]; static struct pci_device *dev; +static struct netif netif; static struct timer tmr; -static struct etherdev wire; +static uint32_t tx_ptr = 0; +static uint32_t netif_enq_ptr = 0; static uint16_t ioport; static paddr_t rxbuf, txbuf; +/* TXAD regs */ +static uint16_t tsads[TXQ_ENTRIES] = { + RT_TXAD_N(0), RT_TXAD_N(4), + RT_TXAD_N(8), RT_TXAD_N(12) +}; +static uint16_t tsds[TXQ_ENTRIES] = { + RT_TXSTATUS_N(0), RT_TXSTATUS_N(4), + RT_TXSTATUS_N(8), RT_TXSTATUS_N(8) +}; + /* * Write to an RTL8139 register * @@ -159,53 +172,112 @@ rt_poll(uint8_t reg, uint8_t size, uint32_t bits, bool pollset) return val; } -#if defined(__x86_64__) -__isr static void -rt8139_pin_irq(void *sp) +static int +rt_tx(void *packet, size_t len) +{ + static uint32_t tx_ptr = 0; + void *tx_data; + paddr_t tx_pa; + + tx_data = dynalloc(len); + if (tx_data == NULL) { + return -ENOMEM; + } + + memcpy(tx_data, packet, len); + tx_pa = VIRT_TO_PHYS(tx_data); + rt_write(tsads[tx_ptr], 4, tx_pa); + rt_write(tsds[tx_ptr++], 4, len); + if (tx_ptr > TXQ_ENTRIES - 1) { + tx_ptr = 0; + } + return 0; +} + +static void +__rt81xx_tx_start(struct netif *nifp) +{ + struct netbuf *dest; + int error; + + for (int i = 0; i < netif_enq_ptr; ++i) { + dest = &netif_buf[i]; + error = rt_tx(dest->data, dest->len); + if (error < 0) { + pr_error("tx_start fail @queue %d (errno=%d)\n", i, error); + } + } +} + +static void +rt81xx_tx_start(struct netif *nifp) +{ + spinlock_acquire(&netif_lock); + __rt81xx_tx_start(nifp); + spinlock_release(&netif_lock); +} + +static int +rt81xx_tx_enq(struct netif *nifp, struct netbuf *nbp, void *data) +{ + struct netbuf *dest; + + spinlock_acquire(&netif_lock); + dest = &netif_buf[netif_enq_ptr++]; + memcpy(dest, nbp, sizeof(*dest)); + + if (netif_enq_ptr > TXQ_ENTRIES - 1) { + __rt81xx_tx_start(nifp); + netif_enq_ptr = 0; + } + spinlock_release(&netif_lock); + return 0; +} + +static int +rt81xx_intr(void *sp) { - static uint32_t packet_ptr = 0; uint16_t len; uint16_t *p; uint16_t status; status = rt_read(RT_INTRSTATUS, 2); - p = (uint16_t *)(rxbuf + packet_ptr); + p = (uint16_t *)(rxbuf + tx_ptr); len = *(p + 1); /* Length after header */ p += 2; /* Points to data now */ - if (status & RT_TOK) { - return; + if (!ISSET(status, RT_TOK | RT_ROK)) { + return 0; + } + + if (ISSET(status, RT_TOK)) { + pr_trace("sent packet\n"); + return 1; } /* Update rxbuf offset in CAPR */ - packet_ptr = (packet_ptr + len + 4 + 3) & RX_PTR_MASK; - if (packet_ptr > RX_REAL_BUF_SIZE) { - packet_ptr -= RX_REAL_BUF_SIZE; + tx_ptr = (tx_ptr + len + 4 + 3) & RX_PTR_MASK; + if (tx_ptr > RX_REAL_BUF_SIZE) { + tx_ptr -= RX_REAL_BUF_SIZE; } - rt_write(RT_RXBUFTAIL, 2, packet_ptr - 0x10); + rt_write(RT_RXBUFTAIL, 2, tx_ptr - 0x10); rt_write(RT_INTRSTATUS, 2, RT_ACKW); - lapic_eoi(); + return 1; /* handled */ } static int -rtl8139_irq_init(void) +rt81xx_irq_init(void) { - int vec; + struct intr_hand ih; - vec = intr_alloc_vector("rt8139", IPL_BIO); - if (vec < 0) { - return vec; + ih.func = rt81xx_intr; + ih.priority = IPL_BIO; + ih.irq = dev->irq_line; + if (intr_register("rt81xx", &ih) == NULL) { + return -EIO; } - - /* Map interrupt vector to IRQ */ - idt_set_desc(vec, IDT_INT_GATE, ISR(rt8139_pin_irq), 0); - ioapic_set_vec(dev->irq_line, vec); - ioapic_irq_unmask(dev->irq_line); return 0; } -#else -#define rtl8139_irq_init(...) -ENOTSUP -#endif static void rt_init_pci(void) @@ -221,6 +293,7 @@ rt_init_pci(void) static int rt_init_mac(void) { + struct netif_addr *addr = &netif.addr; uint8_t conf; uint32_t tmp; int error; @@ -256,20 +329,20 @@ rt_init_mac(void) /* MAC address dword 0 */ tmp = rt_read(RT_IDR0, 4); - wire.mac_addr[0] = tmp & 0xFF; - wire.mac_addr[1] = (tmp >> 8) & 0xFF; - wire.mac_addr[2] = (tmp >> 16) & 0xFF; - wire.mac_addr[3] = (tmp >> 24) & 0xFF; + addr->data[0] = tmp & 0xFF; + addr->data[1] = (tmp >> 8) & 0xFF; + addr->data[2] = (tmp >> 16) & 0xFF; + addr->data[3] = (tmp >> 24) & 0xFF; /* MAC address word 1 */ tmp = rt_read(RT_IDR2, 4); - wire.mac_addr[4] = (tmp >> 16) & 0xFF; - wire.mac_addr[5] = (tmp >> 24) & 0xFF; + addr->data[4] = (tmp >> 16) & 0xFF; + addr->data[5] = (tmp >> 24) & 0xFF; pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n", - (uint64_t)wire.mac_addr[0], (uint64_t)wire.mac_addr[1], - (uint64_t)wire.mac_addr[2], (uint64_t)wire.mac_addr[3], - (uint64_t)wire.mac_addr[4], (uint64_t)wire.mac_addr[5]); + (uint64_t)addr->data[0], (uint64_t)addr->data[1], + (uint64_t)addr->data[2], (uint64_t)addr->data[3], + (uint64_t)addr->data[4], (uint64_t)addr->data[5]); /* * Alright, now we don't want those EEM bits @@ -293,6 +366,11 @@ rt_init_mac(void) return -ENOMEM; } + memcpy(netif.name, IFNAME, strlen(IFNAME) + 1); + netif.tx_enq = rt81xx_tx_enq; + netif.tx_start = rt81xx_tx_start; + netif_add(&netif); + /* * Configure the chip: * @@ -310,19 +388,17 @@ rt_init_mac(void) * - Enable interrupts through ROK/TOK * - Enable RX state machines * - * TODO: Support TX - * */ - rtl8139_irq_init(); + rt81xx_irq_init(); rt_write(RT_RXBUF, 4, rxbuf); rt_write(RT_RXCONFIG, 4, RT_AB | RT_AM | RT_APM | RT_AAP); rt_write(RT_INTRMASK, 2, RT_ROK | RT_TOK); - rt_write(RT_CHIPCMD, 1, RT_RE); + rt_write(RT_CHIPCMD, 1, RT_RE | RT_TE); return 0; } static int -rt813l_init(void) +rt81xx_init(void) { struct pci_lookup lookup; @@ -364,4 +440,4 @@ rt813l_init(void) return rt_init_mac(); } -DRIVER_EXPORT(rt813l_init); +DRIVER_DEFER(rt81xx_init, "rtl81xx"); diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index d68f98e..0ccb7a0 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -37,6 +37,7 @@ #include <dev/usb/xhciregs.h> #include <dev/usb/xhcivar.h> #include <dev/pci/pci.h> +#include <dev/pci/pciregs.h> #include <dev/acpi/acpi.h> #include <vm/physmem.h> #include <vm/dynalloc.h> @@ -56,10 +57,11 @@ static struct pci_device *hci_dev; static struct timer tmr; -__attribute__((__interrupt__)) static void -xhci_common_isr(void *sf) +static int +xhci_intr(void *sf) { pr_trace("received xHCI interrupt (via PCI MSI-X)\n"); + return 1; /* handled */ } /* @@ -174,6 +176,7 @@ xhci_init_scratchpads(struct xhci_hc *hc) struct xhci_caps *caps = XHCI_CAPS(hc->base); uint16_t max_bufs_lo, max_bufs_hi; uint16_t max_bufs; + size_t len; uintptr_t *bufarr, tmp; max_bufs_lo = XHCI_MAX_SP_LO(caps->hcsparams1); @@ -187,8 +190,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; @@ -232,7 +236,7 @@ xhci_init_msix(struct xhci_hc *hc) struct msi_intr intr; intr.name = "xHCI MSI-X"; - intr.handler = xhci_common_isr; + intr.handler = xhci_intr; return pci_enable_msix(hci_dev, &intr); } @@ -254,7 +258,7 @@ xhci_init_evring(struct xhci_hc *hc) memset(segtab, 0, DEFAULT_PAGESIZE); /* Set the size of the event ring segment table */ - erst_size = PTR_OFFSET(runtime, 0x28); + erst_size = PTR_OFFSET(runtime, XHCI_RT_ERSTSZ); mmio_write32(erst_size, 1); /* Allocate the event ring segment */ @@ -268,20 +272,20 @@ xhci_init_evring(struct xhci_hc *hc) segtab->size = XHCI_EVRING_LEN; /* Setup the event ring dequeue pointer */ - erdp = PTR_OFFSET(runtime, 0x38); + erdp = PTR_OFFSET(runtime, XHCI_RT_ERDP); mmio_write64(erdp, segtab->base); /* Point ERSTBA to our event ring segment */ - erstba = PTR_OFFSET(runtime, 0x30); + erstba = PTR_OFFSET(runtime, XHCI_RT_ERSTBA); mmio_write64(erstba, VIRT_TO_PHYS(segtab)); hc->evring = PHYS_TO_VIRT(segtab->base); /* Setup interrupt moderation */ - imod = PTR_OFFSET(runtime, 0x24); + imod = PTR_OFFSET(runtime, XHCI_RT_IMOD); mmio_write32(imod, XHCI_IMOD_DEFAULT); /* Enable interrupts */ - iman = PTR_OFFSET(runtime, 0x20); + iman = PTR_OFFSET(runtime, XHCI_RT_IMAN); tmp = mmio_read32(iman); mmio_write32(iman, tmp | XHCI_IMAN_IE); } @@ -495,6 +499,16 @@ xhci_init_hc(struct xhci_hc *hc) return 0; } +static void +xhci_init_pci(void) +{ + uint32_t tmp; + + tmp = pci_readl(hci_dev, PCIREG_CMDSTATUS); + tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE); + pci_writel(hci_dev, PCIREG_CMDSTATUS, tmp); +} + static int xhci_init(void) { @@ -527,7 +541,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..6b1c6c8 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,40 @@ 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..c41deb4 100644 --- a/sys/fs/initramfs.c +++ b/sys/fs/initramfs.c @@ -223,6 +223,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 +279,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..6ae1c38 --- /dev/null +++ b/sys/fs/tmpfs.c @@ -0,0 +1,428 @@ +/* + * 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 (res == 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; + 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. + */ + if (sio->offset >= np->real_size) { + np->real_size = sio->offset; + } + + /* + * 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 ((sio->offset + sio->len) > 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/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/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..0a20324 100644 --- a/sys/include/arch/amd64/cdefs.h +++ b/sys/include/arch/amd64/cdefs.h @@ -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() __ASMV("hlt") /* 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..46e5df7 100644 --- a/sys/include/arch/amd64/cpu.h +++ b/sys/include/arch/amd64/cpu.h @@ -33,18 +33,27 @@ #include <sys/types.h> #include <sys/cdefs.h> #include <sys/proc.h> +#include <sys/spinlock.h> #include <machine/tss.h> #define CPU_IRQ(IRQ_N) (BIT((IRQ_N)) & 0xFF) +/* Feature bits */ +#define CPU_FEAT_SMAP BIT(0) +#define CPU_FEAT_SMEP BIT(1) + struct cpu_info { uint32_t apicid; + uint32_t feat; uint8_t has_x2apic : 1; + uint8_t tlb_shootdown : 1; uint8_t ipl; size_t lapic_tmr_freq; uint8_t irq_mask; + vaddr_t shootdown_va; struct tss_entry *tss; struct proc *curtd; + struct spinlock lock; struct cpu_info *self; }; @@ -52,6 +61,13 @@ __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); +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); diff --git a/sys/include/arch/amd64/frameasm.h b/sys/include/arch/amd64/frameasm.h index c6316a5..4dc075e 100644 --- a/sys/include/arch/amd64/frameasm.h +++ b/sys/include/arch/amd64/frameasm.h @@ -30,6 +30,8 @@ #ifndef _MACHINE_FRAMEASM_H_ #define _MACHINE_FRAMEASM_H_ +#define ALIGN_TEXT .align 8, 0x90 + /* * If the interrupt has an error code, this macro shall * be used to create the trapframe. diff --git a/sys/include/arch/amd64/gdt.h b/sys/include/arch/amd64/gdt.h index 55666a7..0c5faf1 100644 --- a/sys/include/arch/amd64/gdt.h +++ b/sys/include/arch/amd64/gdt.h @@ -4,18 +4,48 @@ #include <sys/types.h> #include <sys/cdefs.h> +#define GDT_TSS_INDEX 5 +#define GDT_ENTRY_COUNT 7 + +/* Segment selectors */ #define KERNEL_CS 0x08 #define KERNEL_DS 0x10 -#define USER_CS 0x18 -#define USER_DS 0x20 -#define GDT_TSS 5 +#define USER_CS 0x18 +#define USER_DS 0x20 + +/* + * Bit definitions for regular segment descriptors + * + * See Intel SPG 3/25 Section 3.4.5 - Segment Descriptors + */ + +#define GDT_ATTRIBUTE_ACCESSED BIT(0) /* Accessed */ +#define GDT_ATTRIBUTE_EXECUTABLE BIT(3) /* Executable */ +#define GDT_ATTRIBUTE_NONSYSTEM BIT(4) /* Code/data */ +#define GDT_ATTRIBUTE_PRESENT BIT(7) /* Present */ +#define GDT_ATTRIBUTE_64BIT_CODE BIT(13) /* 64-bit code */ +#define GDT_ATTRIBUTE_32BIT BIT(14) /* 32-bit code/data */ +#define GDT_ATTRIBUTE_GRANULARITY BIT(15) /* 4KiB limit granularity */ + +/* Attributes for executable segments */ +#define GDT_ATTRIBUTE_READABLE BIT(1) /* Readable */ +#define GDT_ATTRIBUTE_CONFORMING BIT(2) /* Conforming */ + +/* Attributes for non-executable segments */ +#define GDT_ATTRIBUTE_WRITABLE BIT(1) /* Writable */ +#define GDT_ATTRIBUTE_EXPANDS_DOWN BIT(2) /* See SPG 3/25 Section 6.8.1 */ + +/* DPL (Descriptor Privilege Level) specifier */ +#define GDT_ATTRIBUTE_DPL0 0 +#define GDT_ATTRIBUTE_DPL1 (1 << 5) +#define GDT_ATTRIBUTE_DPL2 (2 << 5) +#define GDT_ATTRIBUTE_DPL3 (3 << 5) struct __packed gdt_entry { uint16_t limit; uint16_t base_low; uint8_t base_mid; - uint8_t access; - uint8_t granularity; + uint16_t attributes; uint8_t base_hi; }; @@ -24,27 +54,28 @@ struct __packed gdtr { uintptr_t offset; }; +extern struct gdt_entry g_gdt_data[GDT_ENTRY_COUNT]; +extern const struct gdtr g_gdtr; + __always_inline static inline void -gdt_load(struct gdtr *gdtr) +gdt_load(void) { __ASMV("lgdt %0\n" - "push $8\n" /* Push CS */ - "lea 1f(%%rip), %%rax\n" /* Load 1 label address into RAX */ - "push %%rax\n" /* Push the return address (label 1) */ - "lretq\n" /* Far return to update CS */ + "push %1\n" /* Push code segment selector */ + "lea 1f(%%rip), %%rax\n" /* Load label 1 address into RAX */ + "push %%rax\n" /* Push return address (label 1) */ + "lretq\n" /* Far return to update CS */ "1:\n" - " mov $0x10, %%eax\n" - " mov %%eax, %%ds\n" - " mov %%eax, %%es\n" - " mov %%eax, %%fs\n" - " mov %%eax, %%gs\n" - " mov %%eax, %%ss\n" + " mov %2, %%ax\n" /* Load data segment selectors */ + " mov %%ax, %%ds\n" + " mov %%ax, %%es\n" + " mov %%ax, %%fs\n" + " mov %%ax, %%gs\n" + " mov %%ax, %%ss\n" : - : "m" (*gdtr) + : "m" (g_gdtr), "i"(KERNEL_CS), "i"(KERNEL_DS) : "rax", "memory" ); } -extern struct gdt_entry g_gdt_data[256]; - #endif /* !AMD64_GDT_H_ */ diff --git a/sys/include/arch/amd64/intr.h b/sys/include/arch/amd64/intr.h index c643945..c848b6f 100644 --- a/sys/include/arch/amd64/intr.h +++ b/sys/include/arch/amd64/intr.h @@ -47,11 +47,59 @@ #define IPL_CLOCK 2 /* Clock */ #define IPL_HIGH 3 /* Defer everything */ -struct intr_entry { +struct intr_hand; + +/* + * Contains information passed to driver + * + * @ihp: Interrupt handler + * @data: Driver specific data + */ +struct intr_data { + struct intr_hand *ihp; + union { + void *data; + uint64_t data_u64; + }; +}; + +/* + * Interrupt handler + * + * [r]: Required for intr_register() + * [o]: Not required for intr_register() + * [v]: Returned by intr_register() + * + * @func: The actual handler [r] + * @data: Interrupt data [o/v] + * @name: Interrupt name [v] + * @priority: Interrupt priority [r] + * @irq: Interrupt request number [o] + * @vector: Interrupt vector [v] + * + * XXX: `name' must be null terminated ('\0') + * + * XXX: `irq` can be set to -1 for MSI/MSI-X + * interrupts. + * + * XXX: `func` must be the first field in this + * structure so that it may be called through + * assembly. + * + * XXX: `ist' should usually be set to -1 but can be + * used if an interrupt requires its own stack. + */ +struct intr_hand { + int(*func)(void *); + struct intr_data data; + char *name; int priority; + int irq; + int vector; }; -int intr_alloc_vector(const char *name, uint8_t priority); +void *intr_register(const char *name, const struct intr_hand *ih); + int splraise(uint8_t s); void splx(uint8_t s); diff --git a/sys/include/arch/amd64/isa/i8042var.h b/sys/include/arch/amd64/isa/i8042var.h index ebd96ad..13c3095 100644 --- a/sys/include/arch/amd64/isa/i8042var.h +++ b/sys/include/arch/amd64/isa/i8042var.h @@ -82,7 +82,5 @@ void i8042_quirk(int mask); /* Internal - do not use */ void i8042_sync(void); -void i8042_kb_isr(void); -void i8042_kb_event(void); #endif /* _I8042VAR_H_ */ diff --git a/sys/include/arch/amd64/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/dev/acpi/tables.h b/sys/include/dev/acpi/tables.h index 5215c86..5340c7f 100644 --- a/sys/include/dev/acpi/tables.h +++ b/sys/include/dev/acpi/tables.h @@ -132,4 +132,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/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..c82c3c5 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,10 @@ struct cons_char { struct cons_screen { struct fbdev fbdev; + struct ansi_state ansi_s; + struct console_feat feat; /* Features */ + struct proc *atproc; /* Attached proc */ + struct mutex *atproc_lock; uint32_t fg; uint32_t bg; @@ -64,7 +72,14 @@ struct cons_screen { 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..d24397a --- /dev/null +++ b/sys/include/dev/dmi/dmi.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 _DMI_DMI_H_ +#define _DMI_DMI_H_ + +#include <sys/types.h> + +const char *dmi_vendor(void); +const char *dmi_prodver(void); +const char *dmi_product(void); +const char *dmi_cpu_manufact(void); + +#endif /* !_DMI_DMI_H_ */ diff --git a/sys/include/dev/ic/ahciregs.h b/sys/include/dev/ic/ahciregs.h index f959a1e..232b41e 100644 --- a/sys/include/dev/ic/ahciregs.h +++ b/sys/include/dev/ic/ahciregs.h @@ -88,6 +88,7 @@ struct hba_memspace { */ #define AHCI_PXSSTS_DET(SSTS) (SSTS & 0xF) #define AHCI_PXSSTS_IPM(SSTS) ((SSTS >> 8) & 0xF) +#define AHCI_PXSSTS_SPD(SSTS) ((SSTS >> 4) & 0xF) /* * Port SATA control bits @@ -100,6 +101,7 @@ struct hba_memspace { * See section 3.3.7 of the AHCI spec. */ #define AHCI_PXCMD_ST BIT(0) /* Start */ +#define AHCI_PXCMD_SUD BIT(1) /* Spin-up device */ #define AHCI_PXCMD_FRE BIT(4) /* FIS Receive Enable */ #define AHCI_PXCMD_FR BIT(14) /* FIS Receive Running */ #define AHCI_PXCMD_CR BIT(15) /* Command List Running */ @@ -137,6 +139,9 @@ struct hba_memspace { #define AHCI_DET_PRESENT 1 /* Device present (no PHY comm) */ #define AHCI_DET_COMM 3 /* Device present and phy comm established */ #define AHCI_IPM_ACTIVE 1 +#define AHCI_SPD_GEN1 1 /* 1.5 Gb/s */ +#define AHCI_SPD_GEN2 2 /* 3 Gb/s */ +#define AHCI_SPD_GEN3 3 /* 6 Gb/s */ /* * PxSERR bits @@ -158,6 +163,8 @@ struct hba_memspace { #define AHCI_DIAG_T BIT(24) /* Transport state transition error */ #define AHCI_DIAG_F BIT(25) /* Unknown FIS type */ +#define ATAPI_SIG 0xEB140101 + /* * Device detection initialization values * See section 3.3.11 of the AHCI spec. diff --git a/sys/include/dev/ic/ahcivar.h b/sys/include/dev/ic/ahcivar.h index fa24812..67f2efe 100644 --- a/sys/include/dev/ic/ahcivar.h +++ b/sys/include/dev/ic/ahcivar.h @@ -33,9 +33,12 @@ #include <sys/param.h> #include <sys/types.h> #include <sys/device.h> +#include <dev/dcdr/cache.h> #include <dev/ic/ahciregs.h> #include <fs/ctlfs.h> +#define AHCI_DCDR_CAP 16 + struct ahci_cmd_hdr; extern const struct ctlops g_sata_bsize_ops; @@ -93,6 +96,7 @@ struct ahci_hba { * @io: Memory mapped port registers * @hba: HBA descriptor * @cmdlist: Command list [p] + * @nlba: Max number of addressable blocks * @fra: FIS receive area [p] * @dev: Device minor number. */ @@ -100,6 +104,8 @@ struct hba_device { struct hba_port *io; struct ahci_hba *hba; struct ahci_cmd_hdr *cmdlist; + struct dcdr *dcdr; + uint32_t nlba; void *fra; dev_t dev; }; diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h index de6d8fb..144b500 100644 --- a/sys/include/dev/pci/pci.h +++ b/sys/include/dev/pci/pci.h @@ -62,6 +62,7 @@ struct pci_device { uint8_t pci_subclass; uint8_t prog_if; uint8_t hdr_type; + uint8_t pci_express : 1; uint8_t pri_bus; uint8_t sec_bus; @@ -75,7 +76,7 @@ struct pci_device { struct msi_intr { const char *name; - void(*handler)(void *); + int(*handler)(void *); }; pcireg_t pci_readl(struct pci_device *dev, uint32_t offset); diff --git a/sys/include/dev/phy/e1000regs.h b/sys/include/dev/phy/e1000regs.h new file mode 100644 index 0000000..7caceee --- /dev/null +++ b/sys/include/dev/phy/e1000regs.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PHY_E1000_REGS_H_ +#define _PHY_E1000_REGS_H_ + +#include <sys/types.h> +#include <sys/param.h> + +/* + * E1000 register offsets + * + * XXX: Notes about reserve fields: + * + * - The `EERD' register is reserved and should NOT be touched + * for the 82544GC/EI card. + * + * - The `FLA' register is only usable for the 82541xx and + * 82547GI/EI cards, this is reserved and should NOT be + * touched on any other cards. + * + * - The `TXCW' and `RXCW' registers are reserved and should NOT + * be touched for the 82540EP/EM, 82541xx and 82547GI/EI cards. + * + * - The `LEDCTL' register is reserved and should NOT be touched + * for the 82544GC/EI card. + */ +#define E1000_CTL 0x00000 /* Control register */ +#define E1000_STATUS 0x00008 /* Status register */ +#define E1000_EECD 0x00010 /* EEPROM/flash control and data register */ +#define E1000_EERD 0x00014 /* EEPROM/flash read register */ +#define E1000_FLA 0x0001C /* EEPROM/flash read register */ +#define E1000_CTRL_EXT 0x00018 /* Extended device control register */ +#define E1000_MDIC 0x00020 /* PHY management data interface control register */ +#define E1000_FCAL 0x00028 /* Flow control low register */ +#define E1000_FCAH 0x0002C /* Flow control high register */ +#define E1000_FCT 0x00030 /* Flow control type register */ +#define E1000_VET 0x00038 /* VLAN ethertype register */ +#define E1000_FCTTV 0x00170 /* Flow control transmit timer value register */ +#define E1000_TXCW 0x00178 /* Transmit config word register */ +#define E1000_RXCW 0x00180 /* Receive config word register */ +#define E1000_LEDCTL 0x00E00 /* LED control register */ + +/* + * Device control register (`ctl') bits + * + * See section 13.4.1 of the PCI/PCI-X Intel Gigabit + * Ethernet Controllers spec + * + * XXX: Notes about reserved bits: + * + * - The CTL.LRST bit is reserved and should NOT be touched + * for the 82540EP/EM, 82541xx, or 82547GI/EI cards. + */ +#define E1000_CTL_FD BIT(0) /* Full-duplex */ +#define E1000_CTL_LRST BIT(3) /* Link-reset */ +#define E1000_CTL_RST BIT(26) /* Device reset */ + +/* + * EEPROM/flash control and data register (`eecd') + * bits + * + * See section 13.4.3 of the PCI/PCI-X Intel Gigabit + * Ethernet controller spec + */ +#define E1000_EECD_SK BIT(0) /* EEPROM clock input */ +#define E1000_EECD_CS BIT(1) /* EEPROM chip select */ +#define E1000_EECD_DI BIT(2) /* EEPROM data input */ +#define E1000_EECD_DO BIT(3) /* EEPROM data output */ +#define E1000_EECD_FWE BIT(4) /* EEPROM flash write enable ctl (4:5) */ +#define E1000_EECD_REQ BIT(6) /* Request EEPROM access */ +#define E1000_EECD_GNT BIT(7) /* Grant EEPROM access */ +#define E1000_EECD_PRES BIT(8) /* EEPROM present */ +#define E1000_EECD_SIZE BIT(9) /* EEPROM size (1024-bit [0], 4096-bit [1]) */ +#define E1000_EECD_TYPE BIT(13) /* EEPROM type (microwire [0], SPI [1]) */ + +/* + * EEPROM read (`eerd') register bits + * + * See section 13.4.4 of the PCI/PCI-X Intel Gigabit + * Ethernet controller spec + */ +#define E1000_EERD_START BIT(0) /* Start read */ +#define E1000_EERD_DONE BIT(4) /* EEPROM read finished */ + +/* + * EEPROM word addresses + */ +#define E1000_HWADDR0 0x00 /* Word 0 */ +#define E1000_HWADDR1 0x01 /* Word 1 */ +#define E1000_HWADDR2 0x02 /* Word 2 */ + +#endif /* !_PHY_E1000_REGS_H_ */ diff --git a/sys/include/dev/phy/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/timer.h b/sys/include/dev/timer.h index e54adcc..fe91323 100644 --- a/sys/include/dev/timer.h +++ b/sys/include/dev/timer.h @@ -69,6 +69,7 @@ struct timer { const char *name; /* e.g "HPET" */ size_t(*calibrate)(void); /* Returns frequency, 0 for unspecified */ size_t(*get_time_usec)(void); /* Time since init (microseconds) */ + size_t(*get_time_nsec)(void); /* Time since init (nanoseconds) */ size_t(*get_time_sec)(void); /* Time since init (seconds) */ int(*msleep)(size_t ms); int(*usleep)(size_t us); diff --git a/sys/include/dev/usb/xhciregs.h b/sys/include/dev/usb/xhciregs.h index 69515e4..1cbfd14 100644 --- a/sys/include/dev/usb/xhciregs.h +++ b/sys/include/dev/usb/xhciregs.h @@ -98,6 +98,13 @@ struct xhci_opregs { #define XHCI_RTS(BASE, RTSOFF) PTR_OFFSET(BASE, RTSOFF) #define XHCI_CMD_DB(BASE, DBOFF) PTR_OFFSET(BASE, DBOFF) +/* Runtime register offsets */ +#define XHCI_RT_IMAN 0x20 +#define XHCI_RT_IMOD 0x24 +#define XHCI_RT_ERSTSZ 0x28 +#define XHCI_RT_ERSTBA 0x30 +#define XHCI_RT_ERDP 0x38 + /* Support protocol cap fields */ #define XHCI_PROTO_ID(PROTO) (PROTO & 0xFF) #define XHCI_PROTO_MINOR(PROTO) ((PROTO >> 16) & 0xFF) diff --git a/sys/include/dev/video/fbdev.h b/sys/include/dev/video/fbdev.h index c80fd92..c9fec94 100644 --- a/sys/include/dev/video/fbdev.h +++ b/sys/include/dev/video/fbdev.h @@ -52,5 +52,6 @@ fbdev_get_index(const struct fbdev *fbdev, uint32_t x, uint32_t y) } struct fbdev fbdev_get(void); +void fbdev_init_dev(void); #endif /* !_DEV_FBDEV_H_ */ diff --git a/sys/include/fs/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/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..33ba06f --- /dev/null +++ b/sys/include/net/netbuf.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 _NET_NETBUF_H_ +#define _NET_NETBUF_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/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..c912d28 --- /dev/null +++ b/sys/include/sys/console.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_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; +}; + +#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/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/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..aa2a729 100644 --- a/sys/include/sys/exec.h +++ b/sys/include/sys/exec.h @@ -32,7 +32,6 @@ #include <sys/types.h> -#if defined(_KERNEL) /* Danger: Do not change these !! */ #define AT_NULL 0 @@ -45,7 +44,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..4ce2db2 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; @@ -50,8 +57,12 @@ int fd_write(unsigned int fd, void *buf, size_t count); int fd_alloc(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(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..f56958e 100644 --- a/sys/include/sys/limits.h +++ b/sys/include/sys/limits.h @@ -32,6 +32,9 @@ #define PATH_MAX 1024 #define SSIZE_MAX 32767 +#define ARG_MAX 4096 #define CHAR_BIT 8 - +#if defined(_KERNEL) +#define CPU_MAX 256 +#endif /* _KERNEL */ #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/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..7331d5f 100644 --- a/sys/include/sys/param.h +++ b/sys/include/sys/param.h @@ -67,8 +67,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..54764d0 100644 --- a/sys/include/sys/proc.h +++ b/sys/include/sys/proc.h @@ -53,6 +53,26 @@ #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; +}; + struct proc { pid_t pid; struct exec_prog exec; @@ -64,11 +84,11 @@ struct proc { struct trapframe tf; struct pcb pcb; struct proc *parent; - void *spawn_data; + 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 +103,31 @@ 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 */ struct proc *this_td(void); struct proc *get_child(struct proc *cur, pid_t pid); + +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); + 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/sched.h b/sys/include/sys/sched.h index 80f4d1c..7d17607 100644 --- a/sys/include/sys/sched.h +++ b/sys/include/sys/sched.h @@ -37,6 +37,8 @@ void sched_init(void); void sched_yield(void); + +void sched_switch_to(struct trapframe *tf, struct proc *td); void sched_detach(struct proc *td); __dead void sched_enter(void); diff --git a/sys/include/sys/spawn.h b/sys/include/sys/spawn.h index 3828d5c..0c54e4c 100644 --- a/sys/include/sys/spawn.h +++ b/sys/include/sys/spawn.h @@ -31,8 +31,11 @@ #define _SYS_SPAWN_H_ #include <sys/types.h> +#include <sys/param.h> + +#define SPAWN_WAIT BIT(0) #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/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..3650e7a 100644 --- a/sys/include/sys/syscall.h +++ b/sys/include/sys/syscall.h @@ -48,6 +48,14 @@ #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 #if defined(_KERNEL) /* Syscall return value and arg type */ 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/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/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/vnode.h b/sys/include/sys/vnode.h index 33092f9..b135433 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; }; @@ -83,6 +85,13 @@ 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 */ +}; + /* * A field in this structure is unavailable * if it has a value of VNOVAL. @@ -103,6 +112,7 @@ struct vops { 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; 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/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/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..9706e77 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. @@ -192,6 +224,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog) if ((status = elf64_verify(hdr)) != 0) goto done; + memset(loadmap, 0, sizeof(loadmap)); pcbp = &td->pcb; start = -1; end = 0; @@ -242,6 +275,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog) } } + elf_parse_shdrs(hdr); memcpy(prog->loadmap, loadmap, sizeof(loadmap)); prog->start = start; prog->end = end; diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 146c4a9..6b3e09b 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -35,6 +35,7 @@ #include <sys/exec.h> #include <sys/driver.h> #include <sys/panic.h> +#include <sys/systm.h> #include <dev/acpi/uacpi.h> #include <dev/cons/cons.h> #include <dev/acpi/acpi.h> @@ -43,7 +44,14 @@ #include <vm/vm.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; static void copyright(void) @@ -57,9 +65,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; @@ -102,14 +111,22 @@ main(void) md_intoff(); sched_init(); + memset(&g_proc0, 0, sizeof(g_proc0)); + /* Startup pid 1 */ - memset(&proc0, 0, sizeof(proc0.tf)); - spawn(&proc0, start_init, NULL, 0, NULL); + spawn(&g_proc0, start_init, NULL, 0, NULL); + md_inton(); - /* Load all drivers */ + /* Load all early drivers */ DRIVERS_INIT(); - /* Bootstrap APs and here we go! */ + /* Only log to kmsg from here */ + syslog_silence(true); + + /* + * Bootstrap APs, schedule all other drivers + * and here we go! + */ mp_bootstrap_aps(&g_bsp_ci); sched_enter(); __builtin_unreachable(); diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index d122e89..0fb026f 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -149,6 +149,7 @@ 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; @@ -159,8 +160,17 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) } filedes = fd_get(fd); - kbuf = dynalloc(count); + 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 +197,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 +216,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; } } + + /* Increment the offset per read */ + filedes->offset += n; retval = count; 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,18 +280,17 @@ 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; @@ -258,6 +301,14 @@ fd_open(const char *pathname, int flags) 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; @@ -285,3 +336,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(fildes); + if (tmp == NULL) { + return -EBADF; + } + + getattr_args.vp = tmp->vp; + getattr_args.res = &attr; + if ((vfs_vop_getattr(tmp->vp, &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_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..c00f39b 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -35,6 +35,7 @@ #include <vm/vm.h> #include <vm/map.h> #include <machine/pcb.h> +#include <machine/cpu.h> #define pr_trace(fmt, ...) kprintf("exit: " fmt, ##__VA_ARGS__) #define pr_error(...) pr_trace(__VA_ARGS__) @@ -49,6 +50,10 @@ unload_td(struct proc *td) size_t len; sched_detach(td); + if (ISSET(td->flags, PROC_KTD)) { + return; + } + execp = &td->exec; auxvalp = &execp->auxval; pcbp = &td->pcb; @@ -73,47 +78,69 @@ unload_td(struct proc *td) } } +void +proc_reap(struct proc *td) +{ + struct pcb *pcbp; + vaddr_t stack_va; + paddr_t stack_pa; + + 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; + 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; /* If we have any children, kill them too */ if (td->nleaves > 0) { TAILQ_FOREACH(procp, &td->leafq, leaf_link) { - exit1(procp); + if (!ISSET(procp->flags, PROC_EXITING)) + exit1(procp, flags); } } - /* - * 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 +151,21 @@ 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) { + ci->curtd = NULL; + if (parent->pid == 0) + sched_enter(); + + parent->flags &= ~PROC_SLEEP; sched_enter(); + } return 0; } @@ -145,6 +179,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..099f620 100644 --- a/sys/kern/kern_panic.c +++ b/sys/kern/kern_panic.c @@ -31,6 +31,7 @@ #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> @@ -53,10 +54,35 @@ bas(bool do_trace, int reboot_type) md_backtrace(); } + kprintf(OMIT_TIMESTAMP "\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); + kprintf(OMIT_TIMESTAMP "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 +101,9 @@ panic(const char *fmt, ...) md_intoff(); cpu_halt_others(); + 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 +119,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..16cd4b2 --- /dev/null +++ b/sys/kern/kern_proc.c @@ -0,0 +1,113 @@ +/* + * 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/cdefs.h> +#include <sys/vnode.h> +#include <sys/syscall.h> +#include <sys/filedesc.h> +#include <sys/fcntl.h> +#include <string.h> +#include <crc32.h> + +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(fd)->vp; + + sio.buf = &core; + sio.len = sizeof(core); + sio.offset = 0; + + /* Write the core file */ + vfs_vop_write(vp, &sio); + fd_close(fd); +} + +scret_t +sys_getpid(struct syscall_args *scargs) +{ + return getpid(); +} + +scret_t +sys_getppid(struct syscall_args *scargs) +{ + return getppid(); +} diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index c22ea25..774ba71 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> @@ -104,12 +105,29 @@ sched_dequeue_td(void) 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 (td == NULL) { + continue; + } + + TAILQ_REMOVE(&queue->q, td, link); + spinlock_release(&tdq_lock); + return td; } /* We got nothing */ @@ -176,20 +194,40 @@ td_pri_update(struct proc *td) } } +void +sched_switch_to(struct trapframe *tf, struct proc *td) +{ + struct cpu_info *ci; + struct pcb *pcbp; + + ci = this_cpu(); + + if (tf != NULL) { + memcpy(tf, &td->tf, sizeof(*tf)); + } + + ci->curtd = td; + pcbp = &td->pcb; + pmap_switch_vas(pcbp->addrsp); +} + /* * Perform a context switch. */ void sched_switch(struct trapframe *tf) { - struct cpu_info *ci; - struct pcb *pcbp; struct proc *next_td, *td; + struct cpu_info *ci; ci = this_cpu(); td = ci->curtd; + cons_detach(); if (td != NULL) { + if (td->pid == 0) + return; + dispatch_signals(td); td_pri_update(td); sched_save_td(td, tf); @@ -200,11 +238,7 @@ sched_switch(struct trapframe *tf) return; } - memcpy(tf, &next_td->tf, sizeof(*tf)); - ci->curtd = next_td; - pcbp = &next_td->pcb; - - pmap_switch_vas(pcbp->addrsp); + sched_switch_to(tf, next_td); sched_oneshot(false); } @@ -224,12 +258,27 @@ sched_enter(void) void sched_yield(void) { - struct proc *td = this_td(); + struct proc *td; + struct cpu_info *ci = this_cpu(); - if (td != NULL) { - td->rested = true; - sched_switch(&td->tf); + if ((td = ci->curtd) == NULL) { + return; } + + td->rested = true; + + /* 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 diff --git a/sys/kern/kern_spawn.c b/sys/kern/kern_spawn.c index cb898dc..a953a6e 100644 --- a/sys/kern/kern_spawn.c +++ b/sys/kern/kern_spawn.c @@ -27,6 +27,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <sys/spawn.h> #include <sys/proc.h> #include <sys/exec.h> #include <sys/mman.h> @@ -44,10 +45,17 @@ #define pr_trace(fmt, ...) kprintf("spawn: " fmt, ##__VA_ARGS__) #define pr_error(...) pr_trace(__VA_ARGS__) +#define ARGVP_MAX (ARG_MAX / sizeof(void *)) + static volatile size_t nthreads = 0; +/* + * TODO: envp + */ struct spawn_args { char path[PATH_MAX]; + char argv_blk[ARG_MAX]; + char *argv[ARGVP_MAX]; }; static inline void @@ -66,25 +74,22 @@ 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(); } @@ -110,6 +115,7 @@ spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **new struct proc *newproc; struct mmap_lgdr *mlgdr; int error; + pid_t pid; newproc = dynalloc(sizeof(*newproc)); if (newproc == NULL) { @@ -150,17 +156,38 @@ spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **new TAILQ_INSERT_TAIL(&cur->leafq, newproc, leaf_link); atomic_inc_int(&cur->nleaves); newproc->parent = cur; - newproc->spawn_data = p; + newproc->data = p; + newproc->exit_status = -1; /* Initialize the mmap ledger */ mlgdr->nbytes = 0; RBT_INIT(lgdr_entries, &mlgdr->hd); newproc->mlgdr = mlgdr; + newproc->flags |= PROC_WAITED; newproc->pid = ++nthreads; signals_init(newproc); sched_enqueue_td(newproc); - return newproc->pid; + pid = newproc->pid; + + if (ISSET(flags, SPAWN_WAIT)) { + cur->flags |= PROC_SLEEP; + + while (ISSET(cur->flags, PROC_SLEEP)) { + sched_yield(); + } + while (!ISSET(newproc->flags, PROC_ZOMB)) { + sched_yield(); + } + + if (newproc->exit_status < 0) { + pid = newproc->exit_status; + } + + proc_reap(newproc); + } + + return pid; } /* @@ -187,19 +214,26 @@ get_child(struct proc *cur, pid_t pid) /* * 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 +246,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..17c6e54 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,10 @@ 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); } int @@ -75,3 +81,9 @@ dev_nowrite(void) { return -ENOTSUP; } + +int +dev_nobsize(void) +{ + return -ENOTSUP; +} diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index 57b27d0..497aff7 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -28,12 +28,16 @@ */ #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__) @@ -80,7 +84,9 @@ spinlock_usleep(struct spinlock *lock, size_t usec_max) void spinlock_acquire(struct spinlock *lock) { - while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE)); + while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE)) { + md_pause(); + } } /* @@ -136,3 +142,57 @@ sysrel(void) { spinlock_release(&__syslock); } + +/* + * Create a new mutex lock object + */ +struct mutex * +mutex_new(const char *name) +{ + 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; +} + +/* + * Acquire a mutex + * + * @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 +mutex_free(struct mutex *mtx) +{ + dynfree(mtx); +} diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c index 249a04a..739dd7f 100644 --- a/sys/kern/kern_syscall.c +++ b/sys/kern/kern_syscall.c @@ -31,8 +31,11 @@ #include <sys/sysctl.h> #include <sys/reboot.h> #include <sys/types.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 +48,14 @@ 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 */ }; const size_t MAX_SYSCALLS = NELEM(g_sctab); 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..102648c 100644 --- a/sys/include/net/if_ether.h +++ b/sys/kern/kern_time.c @@ -27,48 +27,50 @@ * 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; + } + + timeout_msec = ts.tv_nsec / 1000000; + timeout_msec += ts.tv_sec * 1000; -#endif /* !_IF_ETHER_H_ */ + md_inton(); + tmr.msleep(timeout_msec); + md_intoff(); + 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..d88c447 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; @@ -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_syscalls.c b/sys/kern/vfs_syscalls.c index 990d722..0d51331 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; + gattr.res = &attr; error = vfs_vop_getattr(vp, &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/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/vsnprintf.c b/sys/lib/string/vsnprintf.c index e9e391f..a3b7e91 100644 --- a/sys/lib/string/vsnprintf.c +++ b/sys/lib/string/vsnprintf.c @@ -104,6 +104,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..26effdb 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(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..89f9ee6 100644 --- a/sys/vm/vm_physmem.c +++ b/sys/vm/vm_physmem.c @@ -36,11 +36,12 @@ #include <vm/vm.h> #include <string.h> -size_t highest_frame_idx = 0; -size_t bitmap_size = 0; -size_t bitmap_free_start = 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; -uint8_t *bitmap; +static uint8_t *bitmap; static struct limine_memmap_response *resp = NULL; static struct spinlock lock = {0}; @@ -137,27 +138,51 @@ 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; - } + if (idx < 0) + idx = i; + if (++frames >= count) + break; - for (size_t j = i; j < i + count; ++j) { - setbit(bitmap, j); - } - - 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); } spinlock_release(&lock); @@ -169,6 +194,8 @@ 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); diff --git a/sys/vm/vm_vnode.c b/sys/vm/vm_vnode.c index 2457c97..27defc9 100644 --- a/sys/vm/vm_vnode.c +++ b/sys/vm/vm_vnode.c @@ -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; } |