diff options
29 files changed, 1392 insertions, 129 deletions
@@ -79,6 +79,35 @@ Documentation will be in the form of comments throughout the codebase and can al - ``share/docs/kernel``: Kernel documentation - ``share/docs/lib``: Library documentation +# Maintainers (by author) +-------------- +| Maintainer | Component | +|--------------------|--------------------| +| <ian@osmora.org> | Hyra AMD64 Kernel | +| <ian@osmora.org> | User C Library | +| <ian@osmora.org> | NVMe Driver | +| <ian@osmora.org> | AHCI Driver | +| <ian@osmora.org> | xHCI Driver | +| <ian@osmora.org> | RTL8139 Driver | +| <ian@osmora.org> | E1000E Driver | +| <ian@osmora.org> | ET131X Driver | +| <ian@osmora.org> | PCI Driver | +| <ian@osmora.org> | PCIe Driver | +| <quinn@osmora.org> | PCI Driver | +| <quinn@osmora.org> | User C Library | +| <quinn@osmora.org> | Killing MS | + +-------------- +# To-do + +``` +[ ] kern: dev: AHCI DCDR cache (<ian@osmora.org>) +[ ] kern: Worker threads (<ian@osmora.org>) +[ ] kern: Multithreaded driver startup (<quinn@osmora.org>) +[ ] libc: Slab allocator (<quinn@osmora.org>) +... +``` + Hyra running on bare metal: --------------  diff --git a/lib/libc/include/ctype.h b/lib/libc/include/ctype.h index 0ff9a43..2a827e3 100644 --- a/lib/libc/include/ctype.h +++ b/lib/libc/include/ctype.h @@ -36,6 +36,12 @@ __BEGIN_DECLS __always_inline static inline int +__isascii(int c) +{ + return c >= 0 && c <= 127; +} + +__always_inline static inline int __tolower(int c) { return c | 0x20; @@ -92,4 +98,7 @@ __END_DECLS /* Is a space? */ #define isspace(C) __isspace((C)) +/* Is ascii? */ +#define isascii(C) __isascii((C)) + #endif /* _CTYPE_H */ diff --git a/lib/libc/include/unistd.h b/lib/libc/include/unistd.h index 21206ad..40ebee2 100644 --- a/lib/libc/include/unistd.h +++ b/lib/libc/include/unistd.h @@ -53,15 +53,28 @@ int sethostname(const char *name, size_t size); uid_t getuid(void); char *getlogin(void); +char *getcwd(char *buf, size_t size); +char *getwd(char *pathname); + +int symlink(const char *target, const char *linkpath); +int synlinkat(const char *target, int newdirfd, const char *linkpath); + ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int close(int fd); int access(const char *path, int mode); + off_t lseek(int fildes, off_t offset, int whence); +int unlinkat(int dirfd, const char *pathname, int flags); +int unlink(const char *path); + +int dup(int fd); +int dup2(int fd, int fd1); pid_t getpid(void); pid_t getppid(void); +pid_t fork(void); extern char *optarg; extern int optind, opterr, optopt; diff --git a/lib/libc/src/stdio/fopen.c b/lib/libc/src/stdio/fopen.c index c15dace..efeb577 100644 --- a/lib/libc/src/stdio/fopen.c +++ b/lib/libc/src/stdio/fopen.c @@ -51,6 +51,8 @@ fopen(const char *__restrict path, const char *__restrict mode) seal |= (O_WRONLY | O_CREAT); } else if (strcmp(mode, "r+") == 0) { seal |= O_RDWR; + } else if (strcmp(mode, "rb") == 0) { + seal |= O_RDONLY; } else { return NULL; } diff --git a/lib/libc/src/unistd/dup.c b/lib/libc/src/unistd/dup.c new file mode 100644 index 0000000..887fdc6 --- /dev/null +++ b/lib/libc/src/unistd/dup.c @@ -0,0 +1,44 @@ +/* + * 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 <unistd.h> + +int +dup(int fd) +{ + /* TODO: STUB */ + return -1; +} + +int +dup2(int fd, int fd1) +{ + /* TODO: STUB */ + return -1; +} diff --git a/lib/libc/src/unistd/fork.c b/lib/libc/src/unistd/fork.c new file mode 100644 index 0000000..970072e --- /dev/null +++ b/lib/libc/src/unistd/fork.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 <unistd.h> + +pid_t +fork(void) +{ + /* TODO */ + return -1; +} diff --git a/lib/libc/src/unistd/getcwd.c b/lib/libc/src/unistd/getcwd.c new file mode 100644 index 0000000..641a49b --- /dev/null +++ b/lib/libc/src/unistd/getcwd.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <unistd.h> + +char * +getcwd(char *buf, size_t size) +{ + if (buf == NULL) { + return NULL; + } + + /* TODO: STUB */ + return NULL; +} + +char * +getwd(char *pathname) +{ + if (pathname == NULL) { + return NULL; + } + + /* TODO: STUB */ + return NULL; +} diff --git a/lib/libc/src/unistd/symlink.c b/lib/libc/src/unistd/symlink.c new file mode 100644 index 0000000..2ad24ca --- /dev/null +++ b/lib/libc/src/unistd/symlink.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/errno.h> +#include <unistd.h> + +int +symlink(const char *target, const char *linkpath) +{ + if (target == NULL || linkpath == NULL) { + return -EINVAL; + } + + /* TODO: STUB */ + return -1; +} + +int +synlinkat(const char *target, int newdirfd, const char *linkpath) +{ + if (target == NULL || linkpath == NULL) { + return -EINVAL; + } + + /* TODO: STUB */ + return -1; +} diff --git a/lib/libc/src/unistd/unlink.c b/lib/libc/src/unistd/unlink.c new file mode 100644 index 0000000..3de0796 --- /dev/null +++ b/lib/libc/src/unistd/unlink.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/errno.h> +#include <unistd.h> + +int +unlink(const char *path) +{ + if (path == NULL) { + return -EINVAL; + } + + /* TODO: STUB */ + return -1; +} + +int +unlinkat(int dirfd, const char *pathname, int flags) +{ + if (pathname == NULL || dirfd < 0) { + return -EINVAL; + } + + /* TODO: STUB */ + return -1; +} diff --git a/sys/arch/amd64/amd64/intr.c b/sys/arch/amd64/amd64/intr.c index a545788..c44c88e 100644 --- a/sys/arch/amd64/amd64/intr.c +++ b/sys/arch/amd64/amd64/intr.c @@ -98,12 +98,12 @@ intr_register(const char *name, const struct intr_hand *ih) * 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. + * XXX: Vector 0x20 is reserved for the Hyra scheduler and + * vectors 0x21 to 0x21 + N_IPIVEC are reserved for + * inter-processor interrupts. */ for (int i = vec; i < vec + 16; ++i) { - if (g_intrs[i] != NULL || i < 0x23) { + if (g_intrs[i] != NULL || i < 0x24) { continue; } diff --git a/sys/arch/amd64/amd64/ipi.c b/sys/arch/amd64/amd64/ipi.c new file mode 100644 index 0000000..ffa291f --- /dev/null +++ b/sys/arch/amd64/amd64/ipi.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/param.h> +#include <sys/panic.h> +#include <sys/spinlock.h> +#include <machine/cpu.h> +#include <machine/idt.h> +#include <machine/ipi.h> +#include <machine/lapic.h> +#include <string.h> + +void ipi_isr0(void); +void ipi_isr1(void); +void ipi_isr2(void); +void ipi_isr3(void); + +void __ipi_handle_common(void); + +#define pr_trace(fmt, ...) kprintf("ipi: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +#define BASE_VECTOR 0x21 +#define COOKIE 0x7E0A + +/* For the global state of the subsystem */ +static uint32_t cookie = 0; + +/* + * The next vector that will be used for an IPI to + * be allocated. It starts at 0x21 because interrupt + * vector 0x20 is used for the Hyra scheduler and `N_IPIVEC' + * vectors up are reserved for inter-processor interrupts. + * + * XXX: This must not go beyond N_IPIVEC !! + */ +static uint8_t next_vec = BASE_VECTOR; +static uint8_t vec_entries = 0; + +/* + * In order to get an index into the 'vectors' array, + * one can pass an `ipi_bitmap' bit index into the + * ipi_vector() function. The index into the `ipi` + * field within may be acquired with the ipi_index() + * function. + */ +static uint64_t ipi_bitmap = 0; +static struct ipi_vector vectors[N_IPIVEC]; +static struct spinlock lock; + +/* + * Allocate a bit from the `ipi_bitmap' and + * return the index. + * + * Returns a less than zero value upon error. + */ +static ssize_t +alloc_ipi_bit(void) +{ + const size_t MAX = sizeof(ipi_bitmap) * 8; + off_t i; + + for (i = 0; i < MAX; ++i) { + if (!ISSET(ipi_bitmap, BIT(i))) { + ipi_bitmap |= BIT(i); + return i; + } + } + + return -1; +} + +/* + * Allocate an IPI that can be sent to other + * cores on the CPU. This is the core logic + * and contains *no* locks. One should be + * using the md_ipi_alloc() function instead. + * + * Returns the allocated IPI identifier on succes, + * otherwise a less than zero value is returned. + */ +static int +__ipi_alloc(struct cpu_ipi **res) +{ + struct ipi_vector *vp; + struct cpu_ipi *ipip; + ssize_t bit; + uint8_t idx; + + if (res == NULL) { + return -EINVAL; + } + + if (next_vec >= BASE_VECTOR + N_IPIVEC) { + return -EAGAIN; + } + + /* + * Attempt to allocate a bit index from + * the bitmap. + */ + if ((bit = alloc_ipi_bit()) < 0) { + return -EAGAIN; + } + + idx = ipi_vector(bit); + vp = &vectors[idx]; + + /* Initialize the vector if not already */ + if (vp->cookie != COOKIE) { + vp->cookie = COOKIE; + vp->nipi = 0; + vp->vec = next_vec; + memset(vp->ipi, 0, sizeof(vp->ipi)); + } + + /* + * Just a sanity check here, the number of + * IPIs per vector should never exceed the + * maximum, and if it does, that gives us more + * than enough grounds to panic the system as + * it would not be wise to trust it. + */ + if (__unlikely(vp->nipi >= IPI_PER_VEC)) { + panic("too many IPIs in vector %x\n", vp->vec); + } + + idx = ipi_index(bit); + ipip = &vp->ipi[idx]; + + /* We are allocating, not clobbering */ + if (ipip->cookie == COOKIE) { + panic("ipi table corruption\n"); + } + + if ((++vec_entries) >= IPI_PER_VEC) { + vec_entries = 0; + ++next_vec; + } + + /* Set up the initial state */ + ipip->cookie = COOKIE; + ipip->handler = NULL; + ipip->id = bit; + *res = ipip; + return bit; +} + +/* + * Dispatch pending IPIs for the current + * processor. + * + * @vector: Backing interrupt vector + * @ci: Current processor + */ +static void +ipi_dispatch_pending(struct ipi_vector *vec, struct cpu_info *ci) +{ + uint8_t bit_i; + uint8_t n_bit; + uint8_t index; + struct cpu_ipi *ipip = NULL; + ipi_pend_t pending; + + if (vec == NULL || ci == NULL) { + return; + } + + n_bit = sizeof(pending) * 8; + for (bit_i = 0; bit_i < n_bit; ++bit_i) { + index = ipi_vector(bit_i); + pending = ci->ipi_pending[index]; + + vec = &vectors[index]; + index = ipi_index(bit_i); + ipip = &vec->ipi[index]; + + /* Is this pending or not? */ + if (!ISSET(pending, BIT(bit_i))) { + continue; + } + + /* Handle and mark as no longer pending */ + ipip->handler(ipip); + ci->ipi_pending[vec->vec] &= ~BIT(bit_i); + } +} + +/* + * Check an IPI pending bitmap for a + * vector and send IPIs as needed + * + * @ci: Target processor + * @pending: Pending IPIs + */ +static void +ipi_send_vector(struct cpu_info *ci, ipi_pend_t pending) +{ + struct ipi_vector *vp; + struct cpu_ipi *ipip; + uint8_t n_bits = sizeof(pending) * 8; + uint8_t bit_i; + uint8_t vector, index; + uint32_t apic_id = 0; + + if (ci != NULL) { + /* + * We are already dispatching IPIs, we don't + * want to find ourselves in interrupt hell. + */ + if (ci->ipi_dispatch) { + return; + } + + apic_id = ci->apicid; + } + + ci->ipi_dispatch = 1; + for (bit_i = 0; bit_i < n_bits; ++bit_i) { + if (ISSET(pending, BIT(bit_i))) { + vector = ipi_vector(bit_i); + index = ipi_index(bit_i); + + if (ci != NULL) + ci->ipi_id = bit_i; + + vp = &vectors[vector]; + ipip = &vp->ipi[index]; + + /* Ignore if cookie does match */ + if (ipip->cookie != COOKIE) + continue; + + /* Ignore if there is no handler */ + if (ipip->handler == NULL) + continue; + + /* Send that IPI through */ + lapic_send_ipi( + apic_id, + IPI_SHORTHAND_NONE, + BASE_VECTOR + vector + ); + } + } +} + +/* + * Common IPI routine, called from vector.S + * + * XXX: Internal usage only + */ +void +__ipi_handle_common(void) +{ + struct ipi_vector *vp; + struct cpu_info *ci = this_cpu(); + uint8_t vector; + + if (cookie != COOKIE) { + pr_trace("[warn]: got spurious ipi\n"); + return; + } + + /* Grab the vector */ + vector = ipi_vector(ci->ipi_id); + vp = &vectors[vector]; + if (vp->cookie != COOKIE) { + pr_error("got IPI for uninitialized vector\n"); + return; + } + + if ((ci = this_cpu()) == NULL) { + pr_error("could not get current CPU\n"); + return; + } + + ipi_dispatch_pending(vp, ci); + + /* We are done dispatching IPIs */ + ci->ipi_dispatch = 0; + ci->ipi_id = 0; +} + +/* + * Send one or more IPIs to a specific + * processor after caller sets bits in + * the `ci->ipi_pending' field + * + * @ci: Processor to send IPI(s) to + */ +int +md_ipi_send(struct cpu_info *ci) +{ + if (ci == NULL) { + return -EINVAL; + } + + spinlock_acquire(&lock); + for (int i = 0; i < N_IPIVEC; ++i) { + ipi_send_vector(ci, ci->ipi_pending[i]); + } + + spinlock_release(&lock); + return 0; +} + + +/* + * IPI allocation interface with + * locking. + */ +int +md_ipi_alloc(struct cpu_ipi **res) +{ + int retval; + + spinlock_acquire(&lock); + retval = __ipi_alloc(res); + spinlock_release(&lock); + return retval; +} + +/* + * Initialize the IPI thunks + */ +void +md_ipi_init(void) +{ + /* Initialize the IPI vectors */ + idt_set_desc(0x21, IDT_INT_GATE, ISR(ipi_isr0), 0); + idt_set_desc(0x22, IDT_INT_GATE, ISR(ipi_isr1), 0); + idt_set_desc(0x23, IDT_INT_GATE, ISR(ipi_isr2), 0); + idt_set_desc(0x24, IDT_INT_GATE, ISR(ipi_isr3), 0); + cookie = COOKIE; +} diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index 9ff96e1..3f8580a 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -42,6 +42,7 @@ #include <machine/uart.h> #include <machine/sync.h> #include <machine/intr.h> +#include <machine/ipi.h> #include <machine/cdefs.h> #include <machine/isa/i8042var.h> #include <dev/cons/cons.h> @@ -61,9 +62,6 @@ pr_trace(__VA_ARGS__); \ } -#define HALT_VECTOR 0x21 -#define TLB_VECTOR 0x22 - #if defined(__SPECTRE_IBRS) #define SPECTRE_IBRS __SPECTRE_IBRS #else @@ -82,19 +80,20 @@ void syscall_isr(void); void pin_isr_load(void); struct cpu_info g_bsp_ci = {0}; +static struct cpu_ipi *halt_ipi; +static struct cpu_ipi *tlb_ipi; +static struct spinlock ipi_lock = {0}; static bool bsp_init = false; -__attribute__((__interrupt__)) -static void -cpu_halt_isr(void *p) +static int +cpu_halt_handler(struct cpu_ipi *ipi) { __ASMV("cli; hlt"); __builtin_unreachable(); } -__attribute__((__interrupt__)) -static void -tlb_shootdown_isr(void *p) +static int +tlb_shootdown_handler(struct cpu_ipi *ipi) { struct cpu_info *ci; int ipl; @@ -106,7 +105,7 @@ tlb_shootdown_isr(void *p) */ ci = this_cpu(); if (!ci->tlb_shootdown) { - return; + return -1; } ipl = splraise(IPL_HIGH); @@ -115,6 +114,7 @@ tlb_shootdown_isr(void *p) ci->shootdown_va = 0; ci->tlb_shootdown = 0; splx(ipl); + return 0; } static void @@ -141,8 +141,6 @@ setup_vectors(struct cpu_info *ci) 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), IST_SYSCALL); - 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(); } @@ -202,6 +200,44 @@ enable_simd(void) } static void +init_ipis(void) +{ + int error; + + if (bsp_init) { + return; + } + + spinlock_acquire(&ipi_lock); + error = md_ipi_alloc(&halt_ipi); + if (error < 0) { + pr_error("md_ipi_alloc: returned %d\n", error); + panic("failed to init halt IPI\n"); + } + + halt_ipi->handler = cpu_halt_handler; + error = md_ipi_alloc(&tlb_ipi); + if (error < 0) { + pr_error("md_ipi_alloc: returned %d\n", error); + panic("failed to init TLB IPI\n"); + } + + tlb_ipi->handler = tlb_shootdown_handler; + + /* + * Some IPIs must have very specific IDs + * so that they are standard and usable + * throughout the rest of the sytem. + */ + if (halt_ipi->id != IPI_HALT) + panic("expected IPI_HALT for halt IPI\n"); + if (tlb_ipi->id != IPI_TLB) + panic("expected IPI_TLB for TLB IPI\n"); + + spinlock_release(&ipi_lock); +} + +static void cpu_get_info(struct cpu_info *ci) { uint32_t eax, ebx, unused; @@ -257,7 +293,7 @@ cpu_shootdown_tlb(vaddr_t va) spinlock_acquire(&cip->lock); cip->shootdown_va = va; cip->tlb_shootdown = 1; - lapic_send_ipi(cip->apicid, IPI_SHORTHAND_NONE, TLB_VECTOR); + cpu_ipi_send(cip, IPI_TLB); spinlock_release(&cip->lock); } } @@ -309,6 +345,9 @@ md_backtrace(void) void cpu_halt_all(void) { + struct cpu_info *ci; + uint32_t ncpu; + /* * If we have no current 'cpu_info' structure set, * we can't send IPIs, so just assume only the current @@ -319,8 +358,15 @@ cpu_halt_all(void) __ASMV("cli; hlt"); } - /* Send IPI to all cores */ - lapic_send_ipi(0, IPI_SHORTHAND_ALL, HALT_VECTOR); + for (int i = 0; i < ncpu; ++i) { + ci = cpu_get(i); + if (ci == NULL) { + continue; + } + + cpu_ipi_send(ci, IPI_HALT); + } + for (;;); } @@ -331,12 +377,24 @@ cpu_halt_all(void) void cpu_halt_others(void) { + struct cpu_info *curcpu, *ci; + uint32_t ncpu; + if (rdmsr(IA32_GS_BASE) == 0) { __ASMV("cli; hlt"); } - /* Send IPI to all cores */ - lapic_send_ipi(0, IPI_SHORTHAND_OTHERS, HALT_VECTOR); + curcpu = this_cpu(); + ncpu = cpu_count(); + + for (int i = 0; i < ncpu; ++i) { + if ((ci = cpu_get(i)) == NULL) + continue; + if (ci->id == curcpu->id) + continue; + + cpu_ipi_send(ci, IPI_HALT); + } } void @@ -441,7 +499,10 @@ cpu_startup(struct cpu_info *ci) wrmsr(IA32_GS_BASE, (uintptr_t)ci); init_tss(ci); + setup_vectors(ci); + md_ipi_init(); + init_ipis(); try_mitigate_spectre(); ci->online = 1; diff --git a/sys/arch/amd64/amd64/mp.c b/sys/arch/amd64/amd64/mp.c index 20f550f..43830ba 100644 --- a/sys/arch/amd64/amd64/mp.c +++ b/sys/arch/amd64/amd64/mp.c @@ -30,6 +30,7 @@ #include <sys/types.h> #include <sys/limine.h> #include <sys/limits.h> +#include <sys/systm.h> #include <sys/syslog.h> #include <sys/proc.h> #include <sys/spinlock.h> @@ -149,4 +150,5 @@ mp_bootstrap_aps(struct cpu_info *ci) /* Wait for all cores to be ready */ while ((ncpu_up - 1) < cpu_init_counter); + cpu_report_count(ncpu_up); } diff --git a/sys/arch/amd64/amd64/vector.S b/sys/arch/amd64/amd64/vector.S index 890b314..19c68d5 100644 --- a/sys/arch/amd64/amd64/vector.S +++ b/sys/arch/amd64/amd64/vector.S @@ -73,72 +73,101 @@ done: .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 + IDT_SET_VEC 37, ioapic_edge_0 + IDT_SET_VEC 38, ioapic_edge_1 + IDT_SET_VEC 39, ioapic_edge_2 + IDT_SET_VEC 40, ioapic_edge_3 + IDT_SET_VEC 41, ioapic_edge_4 + IDT_SET_VEC 42, ioapic_edge_5 + IDT_SET_VEC 43, ioapic_edge_6 + IDT_SET_VEC 44, ioapic_edge_7 + IDT_SET_VEC 45, ioapic_edge_8 + IDT_SET_VEC 46, ioapic_edge_9 + IDT_SET_VEC 47, ioapic_edge_10 + IDT_SET_VEC 48, ioapic_edge_11 + IDT_SET_VEC 49, ioapic_edge_12 + IDT_SET_VEC 50, ioapic_edge_13 + IDT_SET_VEC 51, ioapic_edge_14 + IDT_SET_VEC 52, ioapic_edge_15 + IDT_SET_VEC 53, ioapic_edge_16 + IDT_SET_VEC 54, ioapic_edge_17 + IDT_SET_VEC 55, ioapic_edge_18 + IDT_SET_VEC 56, ioapic_edge_19 + IDT_SET_VEC 57, ioapic_edge_20 + IDT_SET_VEC 58, ioapic_edge_21 + IDT_SET_VEC 59, ioapic_edge_22 + IDT_SET_VEC 60, ioapic_edge_23 + IDT_SET_VEC 61, ioapic_edge_24 + IDT_SET_VEC 62, ioapic_edge_25 + IDT_SET_VEC 63, ioapic_edge_26 + IDT_SET_VEC 64, ioapic_edge_27 + IDT_SET_VEC 65, ioapic_edge_28 + IDT_SET_VEC 66, ioapic_edge_29 + IDT_SET_VEC 67, ioapic_edge_30 + IDT_SET_VEC 68, ioapic_edge_31 + IDT_SET_VEC 69, ioapic_edge_32 + IDT_SET_VEC 70, ioapic_edge_33 + IDT_SET_VEC 71, ioapic_edge_34 + IDT_SET_VEC 72, ioapic_edge_35 + IDT_SET_VEC 73, ioapic_edge_36 + IDT_SET_VEC 74, ioapic_edge_37 + IDT_SET_VEC 75, ioapic_edge_38 + IDT_SET_VEC 76, ioapic_edge_39 + IDT_SET_VEC 77, ioapic_edge_40 + IDT_SET_VEC 78, ioapic_edge_41 + IDT_SET_VEC 79, ioapic_edge_42 + IDT_SET_VEC 80, ioapic_edge_43 + IDT_SET_VEC 81, ioapic_edge_44 + IDT_SET_VEC 82, ioapic_edge_45 + IDT_SET_VEC 83, ioapic_edge_46 + IDT_SET_VEC 84, ioapic_edge_47 + IDT_SET_VEC 85, ioapic_edge_48 + IDT_SET_VEC 86, ioapic_edge_49 + IDT_SET_VEC 87, ioapic_edge_50 + IDT_SET_VEC 88, ioapic_edge_51 + IDT_SET_VEC 89, ioapic_edge_52 + IDT_SET_VEC 90, ioapic_edge_53 + IDT_SET_VEC 91, ioapic_edge_54 + IDT_SET_VEC 92, ioapic_edge_55 + IDT_SET_VEC 93, ioapic_edge_56 + IDT_SET_VEC 94, ioapic_edge_57 + IDT_SET_VEC 95, ioapic_edge_58 + IDT_SET_VEC 96, ioapic_edge_59 + IDT_SET_VEC 97, ioapic_edge_60 + IDT_SET_VEC 98, ioapic_edge_61 + IDT_SET_VEC 99, ioapic_edge_62 + IDT_SET_VEC 100, ioapic_edge_63 ret + .globl ipi_isr0 +INTRENTRY(ipi_isr0, ipi_trampoline) + call ipi_trampoline + retq + + .globl ipi_isr1 +INTRENTRY(ipi_isr1, ipi_trampoline) + call ipi_trampoline + retq + + .globl ipi_isr2 +INTRENTRY(ipi_isr2, ipi_trampoline) + call ipi_trampoline + retq + + .globl ipi_isr3 +INTRENTRY(ipi_isr3, ipi_trampoline) + call ipi_trampoline + retq + +/* + * Hyra supports 16 IPI handlers per 4 reserved + * IDT vectors. That allows for a maximum of + * 64 IPIs. + */ +ipi_trampoline: + call __ipi_handle_common + retq + /* I/O APIC edge ISRs */ INTRENTRY(ioapic_edge_0, ioapic_common_func) INTRENTRY(ioapic_edge_1, ioapic_common_func) diff --git a/sys/dev/acpi/uacpi.c b/sys/dev/acpi/uacpi.c index b133288..ffec436 100644 --- a/sys/dev/acpi/uacpi.c +++ b/sys/dev/acpi/uacpi.c @@ -32,6 +32,8 @@ #include <sys/param.h> #include <sys/syslog.h> #include <sys/panic.h> +#include <sys/proc.h> +#include <sys/queue.h> #include <dev/timer.h> #include <uacpi/kernel_api.h> #include <uacpi/platform/arch_helpers.h> @@ -58,17 +60,88 @@ typedef struct { uacpi_size length; } io_range_t; +struct uacpi_work { + uacpi_work_handler hand; + uacpi_handle ctx; + TAILQ_ENTRY(uacpi_work) link; +}; + +uacpi_status +uacpi_kernel_schedule_work(uacpi_work_type type, uacpi_work_handler h, uacpi_handle ctx); + +extern struct proc g_proc0; + +static struct proc *event_td; +static TAILQ_HEAD(, uacpi_work) acpi_gpe_eventq; +static TAILQ_HEAD(, uacpi_work) acpi_notify_eventq; + /* - * TODO: Schedule a system shutdown + * Dispatch ACPI general purpose events from + * hardware. */ -static uacpi_interrupt_ret -power_button_handler(uacpi_handle ctx) +static void +uacpi_gpe_dispatch(void) +{ + struct uacpi_work *work; + + work = TAILQ_FIRST(&acpi_gpe_eventq); + if (work == NULL) { + return; + } + + work->hand(work->ctx); + TAILQ_REMOVE(&acpi_gpe_eventq, work, link); + dynfree(work); +} + +/* + * Dispatch ACPI general notify events. + */ +static void +uacpi_notify_dispatch(void) +{ + struct uacpi_work *work; + + work = TAILQ_FIRST(&acpi_notify_eventq); + if (work == NULL) { + return; + } + + work->hand(work->ctx); + TAILQ_REMOVE(&acpi_gpe_eventq, work, link); + dynfree(work); +} + +static void +uacpi_event_td(void) +{ + for (;;) { + uacpi_gpe_dispatch(); + uacpi_notify_dispatch(); + sched_yield(); + } +} + +static void +shutdown(uacpi_handle ctx) { - md_intoff(); kprintf("power button pressed\n"); kprintf("halting machine...\n"); cpu_halt_all(); - return UACPI_INTERRUPT_HANDLED; +} + +static uacpi_interrupt_ret +power_button_handler(uacpi_handle ctx) +{ + md_intoff(); + uacpi_kernel_schedule_work(UACPI_WORK_GPE_EXECUTION, shutdown, NULL); + md_inton(); + + for (;;) { + md_hlt(); + } + + __builtin_unreachable(); } void * @@ -278,9 +351,28 @@ uacpi_kernel_uninstall_interrupt_handler([[maybe_unused]] uacpi_interrupt_handle } uacpi_status -uacpi_kernel_schedule_work(uacpi_work_type, uacpi_work_handler, uacpi_handle ctx) +uacpi_kernel_schedule_work(uacpi_work_type type, uacpi_work_handler h, uacpi_handle ctx) { - return UACPI_STATUS_UNIMPLEMENTED; + struct uacpi_work *work; + + work = dynalloc(sizeof(*work)); + if (work == NULL) { + return UACPI_STATUS_OUT_OF_MEMORY; + } + + work->hand = h; + work->ctx = ctx; + + switch (type) { + case UACPI_WORK_GPE_EXECUTION: + TAILQ_INSERT_TAIL(&acpi_gpe_eventq, work, link); + break; + case UACPI_WORK_NOTIFICATION: + TAILQ_INSERT_TAIL(&acpi_notify_eventq, work, link); + break; + } + + return 0; } uacpi_status @@ -575,5 +667,8 @@ uacpi_init(void) return -1; } + TAILQ_INIT(&acpi_gpe_eventq); + TAILQ_INIT(&acpi_notify_eventq); + spawn(&g_proc0, uacpi_event_td, NULL, 0, &event_td); return 0; } diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index 0ccb7a0..e14cb44 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -71,11 +71,16 @@ xhci_intr(void *sf) static inline uint32_t * xhci_get_portsc(struct xhci_hc *hc, uint8_t portno) { - if (portno > hc->maxports) { - portno = hc->maxports; + if (portno >= hc->maxports) { + return NULL; } - return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * (portno - 1))); + /* Zero based */ + if (portno > 0) { + --portno; + } + + return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * portno)); } static int @@ -200,12 +205,12 @@ xhci_init_scratchpads(struct xhci_hc *hc) for (size_t i = 0; i < max_bufs; ++i) { tmp = vm_alloc_frame(1); - memset(PHYS_TO_VIRT(tmp), 0, 0x1000); if (tmp == 0) { /* TODO: Shutdown, free memory */ pr_error("failed to fill scratchpad buffer array\n"); return -1; } + memset(PHYS_TO_VIRT(tmp), 0, 0x1000); bufarr[i] = tmp; } @@ -221,7 +226,7 @@ xhci_alloc_dcbaa(struct xhci_hc *hc) { size_t size; - size = sizeof(uintptr_t) * hc->maxslots; + size = sizeof(uintptr_t) * (hc->maxslots + 1); hc->dcbaap = dynalloc_memalign(size, 0x1000); __assert(hc->dcbaap != NULL); return VIRT_TO_PHYS(hc->dcbaap); @@ -268,7 +273,7 @@ xhci_init_evring(struct xhci_hc *hc) /* setup the event ring segment */ segtab->base = VIRT_TO_PHYS(tmp_p); - segtab->base = ((uintptr_t)segtab->base) + (2 * 4096) & ~0xF; + segtab->base = ((uintptr_t)segtab->base); segtab->size = XHCI_EVRING_LEN; /* Setup the event ring dequeue pointer */ @@ -335,6 +340,13 @@ xhci_reset(struct xhci_hc *hc) return error; } + /* Wait longer if the xHC is not ready */ + error = xhci_poll32(&opregs->usbsts, USBSTS_CNR, false); + if (error < 0) { + pr_error("xhci_reset: xHC ready wait timeout\n"); + return error; + } + return 0; } @@ -372,13 +384,33 @@ xhci_start_hc(struct xhci_hc *hc) /* Don't start up if we are already running */ usbcmd = mmio_read32(&opregs->usbcmd); if (ISSET(usbcmd, USBCMD_RUN)) - return -EBUSY; + return 0; usbcmd |= USBCMD_RUN; mmio_write32(&opregs->usbcmd, usbcmd); return 0; } +/* + * Stop and bring down the host controller. + * Returns 0 on success. + */ +static int +xhci_stop_hc(struct xhci_hc *hc) +{ + struct xhci_opregs *opregs = hc->opregs; + uint32_t usbcmd; + + /* Don't continue if we aren't running */ + usbcmd = mmio_read32(&opregs->usbcmd); + if (!ISSET(usbcmd, USBCMD_RUN)) + return 0; + + usbcmd &= ~USBCMD_RUN; + mmio_write32(&opregs->usbcmd, usbcmd); + return 0; +} + static int xhci_init_ports(struct xhci_hc *hc) { @@ -388,6 +420,9 @@ xhci_init_ports(struct xhci_hc *hc) for (size_t i = 1; i < hc->maxports; ++i) { portsc_p = xhci_get_portsc(hc, i); + if (portsc_p == NULL) { + continue; + } portsc = mmio_read32(portsc_p); /* @@ -461,8 +496,15 @@ xhci_init_hc(struct xhci_hc *hc) return -1; } + pr_trace("stopping xHC chip...\n"); + if ((error = xhci_stop_hc(hc)) != 0) { + pr_error("run/stop timeout\n"); + return error; + } + pr_trace("resetting xHC chip...\n"); if ((error = xhci_reset(hc)) != 0) { + pr_error("reset timeout\n"); return error; } diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h index 116661b..4586163 100644 --- a/sys/include/arch/amd64/cpu.h +++ b/sys/include/arch/amd64/cpu.h @@ -37,6 +37,7 @@ #include <sys/spinlock.h> #include <machine/tss.h> #include <machine/cdefs.h> +#include <machine/intr.h> #define CPU_IRQ(IRQ_N) (BIT((IRQ_N)) & 0xFF) @@ -44,9 +45,14 @@ #define CPU_FEAT_SMAP BIT(0) #define CPU_FEAT_SMEP BIT(1) +typedef uint16_t ipi_pend_t; + struct cpu_info { uint32_t apicid; uint32_t feat; + uint8_t ipi_dispatch : 1; /* 1: IPIs being dispatched */ + uint8_t ipi_id; + ipi_pend_t ipi_pending[N_IPIVEC]; uint8_t id; /* MI Logical ID */ uint8_t model : 4; /* CPU model number */ uint8_t family : 4; /* CPU family ID */ diff --git a/sys/include/arch/amd64/intr.h b/sys/include/arch/amd64/intr.h index 3870f18..6d9bb09 100644 --- a/sys/include/arch/amd64/intr.h +++ b/sys/include/arch/amd64/intr.h @@ -48,6 +48,9 @@ #define IPL_CLOCK 2 /* Clock */ #define IPL_HIGH 3 /* Defer everything */ +#define N_IPIVEC 4 /* Number of vectors reserved for IPIs */ +#define IPI_PER_VEC 16 /* Max IPIs per vector */ + struct intr_hand; /* diff --git a/sys/include/arch/amd64/ipi.h b/sys/include/arch/amd64/ipi.h new file mode 100644 index 0000000..1a3b51c --- /dev/null +++ b/sys/include/arch/amd64/ipi.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MACHINE_IPI_H_ +#define _MACHINE_IPI_H_ + +#include <sys/types.h> +#include <machine/cpu.h> +#include <machine/lapic.h> + +/* Fixed IPI IDs */ +#define IPI_HALT 0 +#define IPI_TLB 1 + +/* + * Represents an interprocessor interrupt + * handler. + * + * @cookie: Used to verifying an instance + * @id: IPI ID (identifies the IPI) + * @mask: If set, IPIs are ignored + * @handler: Handler routine + */ +struct cpu_ipi { + uint16_t cookie; + uint8_t id; + int(*handler)(struct cpu_ipi *ipi); +}; + +/* + * Represents an interrupt vector for a + * specific IPI + * + * @ipi: IPIs associated with this vector + * @cookie: Used to verify an instance + * @nipi: Number of IPIs associated + * @vec: System interrupt vector number + */ +struct ipi_vector { + struct cpu_ipi ipi[IPI_PER_VEC]; + uint16_t cookie; + uint8_t nipi; + uint8_t vec; +}; + +int md_ipi_alloc(struct cpu_ipi **res); +int md_ipi_send(struct cpu_info *ci); +void md_ipi_init(void); + +/* + * Get the vector an IPI belongs to + * + * @ipi: IPI to check + */ +__always_inline static inline uint8_t +ipi_vector(uint8_t ipi) +{ + return ipi / N_IPIVEC; +} + +/* + * Get the handler index an IPI belongs + * to + * + * @ipi: IPI to check + */ +__always_inline static inline uint8_t +ipi_index(uint8_t ipi) +{ + return ipi % (sizeof(ipi_pend_t) * 8); +} + +__always_inline static inline int +cpu_ipi_send(struct cpu_info *ci, uint8_t ipi) +{ + uint8_t vec = ipi_vector(ipi); + uint8_t idx = ipi_index(ipi); + + ci->ipi_pending[vec] |= BIT(idx); + return md_ipi_send(ci); +} + +#endif /* !_MACHINE_IPI_H_ */ diff --git a/sys/include/dev/acpi/tables.h b/sys/include/dev/acpi/tables.h index 5340c7f..d31cbe0 100644 --- a/sys/include/dev/acpi/tables.h +++ b/sys/include/dev/acpi/tables.h @@ -118,6 +118,43 @@ struct __packed acpi_gas { uint64_t address; }; +/* + * ACPI Address Space ID definitions for GAS + * + * See section 5.2.3.2 of the ACPI software programming + * manual. + * + * XXX: 0x0B->0x7E is reserved as well as 0x80->0xBF + * and 0xC0->0xFF is OEM defined. Values other than + * the ones specified below are either garbage or + * OEM specific values. + */ +#define ACPI_GAS_SYSMEM 0x00 /* System memory space */ +#define ACPI_GAS_SYSIO 0x01 /* System I/O space */ +#define ACPI_GAS_PCICONF 0x02 /* PCI configuration space */ +#define ACPI_GAS_EC 0x03 /* Embedded controller */ +#define ACPI_GAS_SMBUS 0x04 /* System management bus */ +#define ACPI_GAS_CMOS 0x05 /* System CMOS */ +#define ACPI_GAS_PCIBAR 0x06 /* PCI BAR target */ +#define ACPI_GAS_IPMI 0x07 /* IPMI (sensor monitoring) */ +#define ACPI_GAS_GPIO 0x08 /* General Purpose I/O */ +#define ACPI_GAS_GSBUS 0x09 /* GenericSerialBus */ +#define ACPI_GAS_PLATCOM 0x0A /* Platform Communications Channel */ + +/* + * ACPI address size definitions for GAS + * + * See section 5.2.3.2 of the ACPI software programming + * manual. + * + * This is really retarded Intel and Microsoft, thank you. + */ +#define ACPI_GAS_UNDEF 0 /* Undefined (legacy reasons) */ +#define ACPI_GAS_BYTE 1 /* Byte access */ +#define ACPI_GAS_WORD 2 /* Word access */ +#define ACPI_GAS_DWORD 3 /* Dword access */ +#define ACPI_GAS_QWORD 4 /* Qword access */ + struct __packed acpi_hpet { struct acpi_header hdr; uint8_t hardware_rev_id; diff --git a/sys/include/dev/usb/xhciregs.h b/sys/include/dev/usb/xhciregs.h index 1cbfd14..cafd7c9 100644 --- a/sys/include/dev/usb/xhciregs.h +++ b/sys/include/dev/usb/xhciregs.h @@ -77,6 +77,7 @@ struct xhci_opregs { /* USBSTS bits */ #define USBSTS_HCH BIT(0) /* HC halted */ +#define USBSTS_CNR BIT(11) /* Controller not ready */ /* CAPS.HCSPARAMS1 fields */ #define XHCI_MAXSLOTS(HCSPARAMS1) (HCSPARAMS1 & 0xFF) diff --git a/sys/include/sys/sysctl.h b/sys/include/sys/sysctl.h index 2b1ebb6..3b8d3c7 100644 --- a/sys/include/sys/sysctl.h +++ b/sys/include/sys/sysctl.h @@ -39,6 +39,9 @@ #endif #include <sys/param.h> +/* + * List of 'kern.* ' identifiers + */ #define KERN_OSTYPE 0 #define KERN_OSRELEASE 1 #define KERN_VERSION 2 @@ -46,6 +49,13 @@ #define KERN_HOSTNAME 4 /* + * List of 'hw.* ' identifiers + */ +#define HW_PAGESIZE 5 +#define HW_NCPU 6 +#define HW_MACHINE 7 + +/* * Option types (i.e., int, string, etc) for * sysctl entries. * diff --git a/sys/include/sys/systm.h b/sys/include/sys/systm.h index 42e1723..2f69175 100644 --- a/sys/include/sys/systm.h +++ b/sys/include/sys/systm.h @@ -39,6 +39,7 @@ int copyin(const void *uaddr, void *kaddr, size_t len); int copyout(const void *kaddr, void *uaddr, size_t len); int copyinstr(const void *uaddr, char *kaddr, size_t len); +int cpu_report_count(uint32_t count); __always_inline static inline void __sigraise(int signo) diff --git a/sys/kern/kern_cpu.c b/sys/kern/kern_cpu.c new file mode 100644 index 0000000..69d44c4 --- /dev/null +++ b/sys/kern/kern_cpu.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/systm.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +/* + * Report the number of processors that are online + * in the machine. + * + * @count: Number of processors active + * + * Returns zero on success, otherwise a less + * than zero value is returned. + */ +int +cpu_report_count(uint32_t count) +{ + struct sysctl_args args; + int error, name = HW_NCPU; + + args.name = &name; + args.nlen = 1; + args.oldlenp = 0; + args.oldp = NULL; + args.newp = &count; + args.newlen = sizeof(count); + + if ((error = sysctl(&args)) != 0) { + return error; + } + + return 0; +} diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index c8f8357..83845f6 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -241,7 +241,7 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) /* Increment the offset per read */ filedes->offset += n; - retval = count; + retval = n; done: if (kbuf != NULL) { dynfree(kbuf); diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index 6df5a8d..a4c16bb 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -33,12 +33,15 @@ #include <sys/errno.h> #include <sys/systm.h> #include <vm/dynalloc.h> +#include <vm/vm.h> #include <string.h> #define HYRA_RELEASE "Hyra/" HYRA_ARCH " " \ HYRA_VERSION " " \ HYRA_BUILDDATE +static uint32_t pagesize = DEFAULT_PAGESIZE; +static char machine[] = HYRA_ARCH; static char hyra[] = "Hyra"; static char hyra_version[] = HYRA_VERSION; static char osrelease[] = HYRA_RELEASE; @@ -49,11 +52,17 @@ static char osrelease[] = HYRA_RELEASE; * allocated through dynalloc(9). */ static struct sysctl_entry common_optab[] = { + /* 'kern.*' */ [KERN_OSTYPE] = { KERN_OSTYPE, SYSCTL_OPTYPE_STR_RO, hyra }, [KERN_OSRELEASE] = { KERN_OSRELEASE, SYSCTL_OPTYPE_STR_RO, &osrelease }, [KERN_VERSION] = { KERN_VERSION, SYSCTL_OPTYPE_STR_RO, &hyra_version }, [KERN_VCACHE_TYPE] = { KERN_VCACHE_TYPE, SYSCTL_OPTYPE_STR, NULL }, - [KERN_HOSTNAME] = { KERN_HOSTNAME, SYSCTL_OPTYPE_STR, NULL } + [KERN_HOSTNAME] = { KERN_HOSTNAME, SYSCTL_OPTYPE_STR, NULL }, + + /* 'hw.*' */ + [HW_PAGESIZE] = { HW_PAGESIZE, SYSCTL_OPTYPE_INT_RO, &pagesize }, + [HW_NCPU] = { HW_NCPU, SYSCTL_OPTYPE_INT, NULL }, + [HW_MACHINE] = {HW_MACHINE, SYSCTL_OPTYPE_STR_RO, &machine } }; static int diff --git a/usr.bin/fetch/fetch.c b/usr.bin/fetch/fetch.c index c81ee84..1e8ef92 100644 --- a/usr.bin/fetch/fetch.c +++ b/usr.bin/fetch/fetch.c @@ -33,8 +33,6 @@ #include <stdlib.h> #include <stdio.h> -static const char *user = "unknown"; - #define CPUID(level, a, b, c, d) \ __ASMV("cpuid\n\t" \ : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ diff --git a/usr.bin/sysctl/sysctl.c b/usr.bin/sysctl/sysctl.c index a23bfe8..d4275a7 100644 --- a/usr.bin/sysctl/sysctl.c +++ b/usr.bin/sysctl/sysctl.c @@ -31,14 +31,51 @@ #include <stdint.h> #include <stdio.h> #include <string.h> +#include <stdbool.h> +#define BUF_SIZE 128 + +/* Kern var string constants */ #define NAME_OSTYPE "ostype" #define NAME_OSRELEASE "osrelease" #define NAME_VERSION "version" #define NAME_VCACHE_TYPE "vcache_type" +/* Hw var string constants */ +#define NAME_PAGESIZE "pagesize" +#define NAME_NCPU "ncpu" +#define NAME_MACHINE "machine" + +/* Name start string constants */ +#define NAME_KERN "kern" +#define NAME_HW "hw" + +/* Name start int constants */ +#define NAME_DEF_KERN 0 +#define NAME_DEF_HW 1 + +/* + * Print the contents read from a sysctl + * variable depending on its type. + * + * @data: Data to print + * @is_str: True if a string + */ +static inline void +varbuf_print(char data[BUF_SIZE], bool is_str) +{ + uint32_t *val; + + if (is_str) { + printf("%s\n", data); + } else { + val = (uint32_t *)data; + printf("%d\n", *val); + } +} + /* - * Convert string name to a sysctl name + * Convert string name to a internal name * definition. * * @name: Name to convert @@ -46,7 +83,7 @@ * Convert to int def * / * kern.ostype - * ^^ name + * ^^ * * -- * Returns a less than zero value on failure @@ -56,26 +93,128 @@ static int name_to_def(const char *name) { switch (*name) { + case 'k': + if (strcmp(name, NAME_KERN) == 0) { + return NAME_DEF_KERN; + } + + return -1; + case 'h': + if (strcmp(name, NAME_HW) == 0) { + return NAME_DEF_HW; + } + + return -1; + } + + return -1; +} + +/* + * Handle parsing of 'kern.*' node names + * + * @node: Node name to parse + * @is_str: Set to true if string + */ +static int +kern_node(const char *node, bool *is_str) +{ + switch (*node) { case 'v': - if (strcmp(name, NAME_VERSION) == 0) { + if (strcmp(node, NAME_VERSION) == 0) { return KERN_VERSION; } - if (strcmp(name, NAME_VCACHE_TYPE) == 0) { + if (strcmp(node, NAME_VCACHE_TYPE) == 0) { return KERN_VCACHE_TYPE; } - return -1; case 'o': - if (strcmp(name, NAME_OSTYPE) == 0) { + if (strcmp(node, NAME_OSTYPE) == 0) { return KERN_OSTYPE; } - if (strcmp(name, NAME_OSRELEASE) == 0) { + if (strcmp(node, NAME_OSRELEASE) == 0) { return KERN_OSRELEASE; } + return -1; + } + + return -1; +} + +/* + * Handle parsing of 'hw.*' node names + * + * @node: Node name to parse + * @is_str: Set to true if string + */ +static int +hw_node(const char *node, bool *is_str) +{ + switch (*node) { + case 'p': + if (strcmp(node, NAME_PAGESIZE) == 0) { + *is_str = false; + return HW_PAGESIZE; + } + + return -1; + case 'n': + if (strcmp(node, NAME_NCPU) == 0) { + *is_str = false; + return HW_NCPU; + } return -1; + case 'm': + if (strcmp(node, NAME_MACHINE) == 0) { + return HW_MACHINE; + } + return -1; + } + + return -1; +} + +/* + * Convert string node to a sysctl name + * definition. + * + * @name: Name to convert + * @is_str: Set to true if string + * + * Convert to int def + * / + * kern.ostype + * ^^ name + * + * -- + * Returns a less than zero value on failure + * (e.g., entry not found). + */ +static int +node_to_def(int name, const char *node, bool *is_str) +{ + int retval; + bool dmmy; + + /* + * If the caller did not set `is_str' just + * set it to a dummy value. Otherwise, we will + * make it *default* to a 'true' value. + */ + if (is_str == NULL) { + is_str = &dmmy; + } else { + *is_str = true; + } + + switch (name) { + case NAME_DEF_KERN: + return kern_node(node, is_str); + case NAME_DEF_HW: + return hw_node(node, is_str); } return -1; @@ -86,9 +225,11 @@ main(int argc, char **argv) { struct sysctl_args args; char *var, *p; - int type, name, error; + int type, error; + int root, name; size_t oldlen; - char buf[128]; + bool is_str; + char buf[BUF_SIZE]; if (argc < 2) { printf("sysctl: usage: sysctl <var>\n"); @@ -103,10 +244,9 @@ main(int argc, char **argv) return -1; } - /* TODO: Non kern.* vars */ - if (strcmp(p, "kern") != 0) { - printf("unknown var \"%s\"\n", p); - return -1; + if ((root = name_to_def(p)) < 0) { + printf("sysctl: bad var \"%s\"", p); + return root; } p = strtok(NULL, "."); @@ -115,12 +255,12 @@ main(int argc, char **argv) return -1; } - if ((name = name_to_def(p)) < 0) { + if ((name = node_to_def(root, p, &is_str)) < 0) { printf("sysctl: bad var \"%s\"\n", p); return name; } - name = name; + memset(buf, 0, sizeof(buf)); oldlen = sizeof(buf); args.name = &name; args.nlen = 1; @@ -134,6 +274,6 @@ main(int argc, char **argv) return error; } - printf("%s\n", buf); + varbuf_print(buf, is_str); return 0; } diff --git a/usr.sbin/init/main.c b/usr.sbin/init/main.c index 5da33c6..12bb98c 100644 --- a/usr.sbin/init/main.c +++ b/usr.sbin/init/main.c @@ -33,6 +33,9 @@ #include <stdio.h> #include <string.h> +#define log_trace(fmt, ...) printf("[init]: " fmt, ##__VA_ARGS__) +#define log_error(fmt, ...) printf("[error]: " fmt, ##__VA_ARGS__) + #define SHELL_PATH "/usr/bin/osh" #define LOGIN_PATH "/usr/bin/login" #define INIT_RC_PATH "/usr/rc/init.rc" @@ -41,33 +44,31 @@ static void init_hostname(void) { char hostname[128]; - int error; size_t len; FILE *fp; fp = fopen("/etc/hostname", "r"); if (fp == NULL) { - printf("[init]: error opening /etc/hostname\n"); + log_error("[init]: error opening /etc/hostname\n"); return; } - error = fread(hostname, sizeof(char), sizeof(hostname), fp); - if (error <= 0) { - printf("[init]: error reading /etc/hostname\n"); + len = fread(hostname, sizeof(char), sizeof(hostname), fp); + if (len == 0) { + log_error("[init]: error reading /etc/hostname\n"); fclose(fp); return; } - len = strlen(hostname); - hostname[len - 2] = '\0'; - + hostname[len - 1] = '\0'; if (sethostname(hostname, len) < 0) { - printf("[init]: error setting hostname\n"); - printf("[init]: tried to set %s (len=%d)\n", hostname, len); + log_error("[init]: error setting hostname\n"); + log_error("[init]: tried to set %s (len=%d)\n", hostname, len); fclose(fp); return; } + log_trace("hostname -> %s\n", hostname); fclose(fp); } @@ -82,6 +83,7 @@ main(int argc, char **argv) init_hostname(); /* Start the init.rc */ + log_trace("init.rc up\n"); spawn(SHELL_PATH, start_argv, envp, 0); start_argv[1] = NULL; |