diff options
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r-- | sys/arch/amd64/amd64/acpi_machdep.c | 79 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/gdt.c | 78 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/hpet.c | 186 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/idt.c | 70 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/intr.c | 98 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/ioapic.c | 230 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/lapic.c | 343 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/lapic_intr.S | 11 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/machdep.c | 114 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/mp.c | 93 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/pio.c | 74 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/pmap.c | 270 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/proc_machdep.c | 107 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/reboot.c | 52 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/spectre.S | 53 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/trap.S | 143 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/trap.c | 95 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/tss.c | 177 | ||||
-rw-r--r-- | sys/arch/amd64/conf/GENERIC | 5 | ||||
-rw-r--r-- | sys/arch/amd64/conf/link.ld | 53 | ||||
-rw-r--r-- | sys/arch/amd64/isa/i8254.c | 73 |
21 files changed, 2404 insertions, 0 deletions
diff --git a/sys/arch/amd64/amd64/acpi_machdep.c b/sys/arch/amd64/amd64/acpi_machdep.c new file mode 100644 index 0000000..3bcdc9d --- /dev/null +++ b/sys/arch/amd64/amd64/acpi_machdep.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023-2024 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/panic.h> +#include <sys/syslog.h> +#include <dev/acpi/acpi.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/tables.h> +#include <machine/ioapic.h> +#include <machine/lapic.h> + +#define pr_trace(fmt, ...) kprintf("acpi: " fmt, ##__VA_ARGS__) + +int +acpi_init_madt(void) +{ + struct acpi_madt *madt = acpi_query("APIC"); + struct apic_header *apichdr; + struct ioapic *ioapic = NULL; + uint8_t *cur, *end; + + if (madt == NULL) { + panic("Could not find MADT!\n"); + } + + cur = (uint8_t *)(madt + 1); + end = (uint8_t *)madt + madt->hdr.length; + g_lapic_base = madt->lapic_addr; + + while (cur < end) { + apichdr = (void *)cur; + if (apichdr->type == APIC_TYPE_IO_APIC) { + /* + * TODO: Figure out how to use multiple I/O APICs + */ + if (ioapic != NULL) { + pr_trace("Skipping I/O APIC with ID %d\n", ioapic->ioapic_id); + break; + } + + ioapic = (struct ioapic *)cur; + pr_trace("Detected I/O APIC (id=%d, gsi_base=%d)\n", + ioapic->ioapic_id, ioapic->gsi_base); + + ioapic_init((void *)(uintptr_t)ioapic->ioapic_addr); + } + + cur += apichdr->length; + } + + return 0; + +} diff --git a/sys/arch/amd64/amd64/gdt.c b/sys/arch/amd64/amd64/gdt.c new file mode 100644 index 0000000..d9b7bf2 --- /dev/null +++ b/sys/arch/amd64/amd64/gdt.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023-2024 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/gdt.h> + +struct gdt_entry g_gdt_data[256] = { + /* Null */ + {0}, + + /* Kernel code (0x8) */ + { + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x9A, + .granularity = 0x20, + .base_hi = 0x00 + }, + + /* Kernel data (0x10) */ + { + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0x92, + .granularity = 0x00, + .base_hi = 0x00 + }, + + /* User code (0x18) */ + { + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0xFA, + .granularity = 0xAF, + .base_hi = 0x00 + }, + + /* User data (0x20) */ + { + .limit = 0x0000, + .base_low = 0x0000, + .base_mid = 0x00, + .access = 0xF2, + .granularity = 0x00, + .base_hi = 0x00 + }, + + /* TSS segment (0x28) */ + {0} +}; diff --git a/sys/arch/amd64/amd64/hpet.c b/sys/arch/amd64/amd64/hpet.c new file mode 100644 index 0000000..860e610 --- /dev/null +++ b/sys/arch/amd64/amd64/hpet.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2023-2024 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/mmio.h> +#include <sys/syslog.h> +#include <machine/hpet.h> +#include <dev/acpi/acpi.h> +#include <dev/acpi/tables.h> +#include <dev/timer.h> + +#define pr_trace(fmt, ...) kprintf("hpet: " fmt, ##__VA_ARGS__) +#define pr_error(fmt, ...) pr_trace(__VA_ARGS__) + +#define HPET_REG_CAPS 0x00 +#define HPET_GENERAL_CONFIG 0x10 +#define HPET_REG_MAIN_COUNTER 0xF0 + +#define CAP_REV_ID(caps) (caps & 0xFF) +#define CAP_NUM_TIM(caps) (caps >> 8) & 0x1F +#define CAP_CLK_PERIOD(caps) (caps >> 32) + +#define FSEC_PER_SECOND 1000000000000000ULL +#define USEC_PER_SECOND 1000000ULL + +static void *hpet_base = NULL; +static struct timer timer = {0}; + +/* + * Read from HPET register space. + * + * @reg: Register to read from. + */ +static inline uint64_t +hpet_read(uint32_t reg) +{ + void *addr; + + addr = (void *)((uintptr_t)hpet_base + reg); + return mmio_read64(addr); +} + +/* + * Write to HPET register space. + * + * @reg: Register to write to. + * @val: Value to write. + */ +static inline void +hpet_write(uint32_t reg, uint64_t val) +{ + void *addr; + + addr = (void *)((uintptr_t)hpet_base + reg); + mmio_write64(addr, val); +} + +static int +hpet_sleep(uint64_t n, uint64_t units) +{ + uint64_t caps; + uint32_t period; + uint64_t counter_val; + volatile size_t ticks; + + caps = hpet_read(HPET_REG_CAPS); + period = CAP_CLK_PERIOD(caps); + counter_val = hpet_read(HPET_REG_MAIN_COUNTER); + + ticks = counter_val + (n * (units / period)); + + while (hpet_read(HPET_REG_MAIN_COUNTER) < ticks) { + __ASMV("rep; nop"); + } + + return 0; +} + +static int +hpet_msleep(size_t ms) +{ + return hpet_sleep(ms, 1000000000000); +} + +static int +hpet_usleep(size_t us) +{ + return hpet_sleep(us, 1000000000); +} + +static int +hpet_nsleep(size_t ns) +{ + return hpet_sleep(ns, 1000000); +} + +static size_t +hpet_time_usec(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 * USEC_PER_SECOND) / freq; +} + +static size_t +hpet_time_sec(void) +{ + return hpet_time_usec() / USEC_PER_SECOND; +} + +int +hpet_init(void) +{ + struct acpi_gas *gas; + struct acpi_hpet *hpet; + uint64_t caps; + + hpet = acpi_query("HPET"); + if (hpet == NULL) + return -1; + + gas = &hpet->gas; + hpet_base = (void *)gas->address; + + /* Ensure caps aren't bogus */ + caps = hpet_read(HPET_REG_CAPS); + if (CAP_REV_ID(caps) == 0) { + pr_error("Found bogus revision, assuming faulty\n"); + return -1; + } + if (CAP_CLK_PERIOD(caps) > 0x05F5E100) { + /* + * The spec states this counter clk period must + * be <= 0x05F5E100. So we'll consider it as bogus + * if it exceeds this value + */ + pr_error("Found bogus COUNTER_CLK_PERIOD, assuming faulty\n"); + return 1; + } + + pr_trace("HPET integrity verified\n"); + hpet_write(HPET_REG_MAIN_COUNTER, 0); + hpet_write(HPET_GENERAL_CONFIG, 1); + + /* Setup timer descriptor */ + timer.name = "HIGH_PRECISION_EVENT_TIMER"; + timer.msleep = hpet_msleep; + timer.usleep = hpet_usleep; + timer.nsleep = hpet_nsleep; + timer.get_time_usec = hpet_time_usec; + timer.get_time_sec = hpet_time_sec; + register_timer(TIMER_GP, &timer); + return 0; +} diff --git a/sys/arch/amd64/amd64/idt.c b/sys/arch/amd64/amd64/idt.c new file mode 100644 index 0000000..a9507fc --- /dev/null +++ b/sys/arch/amd64/amd64/idt.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023-2024 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 <machine/idt.h> +#include <machine/gdt.h> + +#define LIDT(idtr) __ASMV("lidt %0" :: "m" (idtr)) + +static struct idt_entry idt[256]; +static struct idtr idtr = { + .limit = sizeof(struct idt_entry) * 256 - 1, + .offset = (uintptr_t)&idt[0] +}; + +void +idt_set_desc(uint8_t vector, uint8_t type, uintptr_t isr, uint8_t ist) +{ + struct idt_entry *desc; + + if (vector >= 255) { + /* Invalid vector */ + return; + } + + desc = &idt[vector]; + desc->off_lo = isr & 0xFFFF; + desc->off_mid = (isr >> 16) & 0xFFFF; + desc->off_hi = (isr >> 32) & 0xFFFFFFFF; + desc->segsel = KERNEL_CS; + desc->type = type; + desc->dpl = (type == IDT_USER_INT_GATE) ? 3 : 0; + desc->p = 1; + desc->zero = 0; + desc->zero1 = 0; + desc->reserved = 0; + desc->ist = 0; +} + +void +idt_load(void) +{ + LIDT(idtr); +} diff --git a/sys/arch/amd64/amd64/intr.c b/sys/arch/amd64/amd64/intr.c new file mode 100644 index 0000000..3e3f309 --- /dev/null +++ b/sys/arch/amd64/amd64/intr.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023-2024 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/errno.h> +#include <sys/panic.h> +#include <machine/intr.h> +#include <machine/cpu.h> +#include <machine/asm.h> +#include <vm/dynalloc.h> + +static struct intr_entry *intrs[256] = {0}; + +void +splraise(uint8_t s) +{ + struct cpu_info *ci = this_cpu(); + + if (s < ci->ipl) { + panic("splraise IPL less than current IPL\n"); + } + + amd64_write_cr8(s); + ci->ipl = s; +} + +void +splx(uint8_t s) +{ + struct cpu_info *ci = this_cpu(); + + if (s > ci->ipl) { + panic("splx IPL greater than current IPL\n"); + } + + amd64_write_cr8(s); + ci->ipl = s; +} + +int +intr_alloc_vector(const char *name, uint8_t priority) +{ + size_t vec = MAX(priority << IPL_SHIFT, 0x20); + struct intr_entry *intr; + + /* Sanity check */ + if (vec > NELEM(intrs)) { + return -1; + } + + /* + * Try to allocate an interrupt vector. An IPL is made up + * of 4 bits so there can be 16 vectors per IPL. + */ + for (int i = vec; i < vec + 16; ++i) { + if (intrs[i] != NULL) { + continue; + } + + intr = dynalloc(sizeof(*intr)); + if (intr == NULL) { + return -ENOMEM; + } + + intr->priority = priority; + intrs[i] = intr; + return i; + } + + return -1; +} diff --git a/sys/arch/amd64/amd64/ioapic.c b/sys/arch/amd64/amd64/ioapic.c new file mode 100644 index 0000000..39bf8f8 --- /dev/null +++ b/sys/arch/amd64/amd64/ioapic.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2023-2024 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/syslog.h> +#include <sys/panic.h> +#include <sys/mmio.h> +#include <machine/ioapicvar.h> +#include <machine/ioapic.h> +#include <dev/acpi/tables.h> +#include <dev/acpi/acpi.h> + +#define pr_trace(fmt, ...) kprintf("ioapic: " fmt, ##__VA_ARGS__) +#define IOAPIC_BASE_OFF(off) ((void *)((uintptr_t)ioapic_base + off)) + +static void *ioapic_base = NULL; +static struct acpi_madt *madt = NULL; + +/* + * Converts IRQ numbers to its corresponding + * Global System Interrupt (GSI) number. + * + * @irq: IRQ number. + */ +static uint32_t +irq_to_gsi(uint8_t irq) +{ + struct apic_header *hdr = NULL; + struct interrupt_override *override = NULL; + uint8_t *cur = NULL; + uint8_t *end = NULL; + + if (madt == NULL) + madt = acpi_query("APIC"); + if (madt == NULL) + panic("Failed to fetch MADT\n"); + + cur = (uint8_t *)(madt + 1); + end = (uint8_t *)madt + madt->hdr.length; + + while (cur < end) { + hdr = (void *)cur; + + if (hdr->type == APIC_TYPE_INTERRUPT_OVERRIDE) { + override = (struct interrupt_override *)cur; + if (override->source == irq) { + return override->interrupt; + } + } + + cur += hdr->length; + } + + return irq; +} + +/* + * Reads a 32 bit value from the IOAPIC + * register space. + * + * @reg: Register to read from. + */ +static uint32_t +ioapic_readl(uint16_t reg) +{ + mmio_write32(IOAPIC_BASE_OFF(IOREGSEL), reg); + return mmio_read32(IOAPIC_BASE_OFF(IOWIN)); +} + +/* + * Writes a 32 bit value to the IOAPIC + * register space. + * + * @reg: Register to write to. + * @val: Value to write. + */ +static void +ioapic_writel(uint16_t reg, uint32_t val) +{ + mmio_write32(IOAPIC_BASE_OFF(IOREGSEL), reg); + mmio_write32(IOAPIC_BASE_OFF(IOWIN), val); +} + +/* + * Reads an I/O APIC redirection entry. + * + * @entry: Entry variable to read into. + * @index: Index to read. + */ +static void +ioapic_read_redentry(union ioapic_redentry *entry, uint8_t index) +{ + uint32_t lo, hi; + + lo = ioapic_readl(IOREDTBL + index * 2); + hi = ioapic_readl(IOREDTBL + index * 2 + 1); + entry->value = ((uint64_t)hi << 32) | lo; +} + +/* + * Writes an I/O APIC redirection entry. + * + * @entry: Entry variable to write. + * @index: Index to write to. + */ +static void +ioapic_write_redentry(const union ioapic_redentry *entry, uint8_t index) +{ + ioapic_writel(IOREDTBL + index * 2, (uint32_t)entry->value); + ioapic_writel(IOREDTBL + index * 2 + 1, (uint32_t)(entry->value >> 32)); +} + +/* + * Mask I/O APIC pin with "raw" pin number + * (Global System Interrupt) + * + * @gsi: Global System Interrupt number + */ +void +ioapic_gsi_mask(uint8_t gsi) +{ + union ioapic_redentry redentry; + + ioapic_read_redentry(&redentry, gsi); + redentry.interrupt_mask = 1; + ioapic_write_redentry(&redentry, gsi); +} + +/* + * Unmask I/O APIC pin with "raw" pin number + * (Global System Interrupt) + * + * @gsi: Global System Interrupt number + */ +void +ioapic_gsi_unmask(uint8_t gsi) +{ + union ioapic_redentry redentry; + + ioapic_read_redentry(&redentry, gsi); + redentry.interrupt_mask = 0; + ioapic_write_redentry(&redentry, gsi); +} + +/* + * Masks I/O APIC pin via IRQ number + * + * @irq: Interrupt Request number + */ +void +ioapic_irq_mask(uint8_t irq) +{ + uint8_t gsi = irq_to_gsi(irq); + ioapic_gsi_mask(gsi); +} + +/* + * Unmasks I/O APIC pin via IRQ number + * + * @irq: Interrupt Request number + */ +void +ioapic_irq_unmask(uint8_t irq) +{ + uint8_t gsi = irq_to_gsi(irq); + ioapic_gsi_unmask(gsi); +} + +/* + * Assign an interrupt vector to a redirection + * entry. + * + * @irq: IRQ number to assign vector to. + * @vector: Vector assign. + */ +void +ioapic_set_vec(uint8_t irq, uint8_t vector) +{ + union ioapic_redentry redentry; + uint8_t gsi = irq_to_gsi(irq); + + ioapic_read_redentry(&redentry, gsi); + redentry.vector = vector; + ioapic_write_redentry(&redentry, gsi); +} + +void +ioapic_init(void *base) +{ + size_t tmp; + uint8_t redir_ent_cnt, ver; + + ioapic_base = base; + tmp = ioapic_readl(IOAPICVER); + ver = tmp & 0xFF; + redir_ent_cnt = ((tmp >> 16) & 0xFF) + 1; + + pr_trace("version=%d, address=0x%x\n", ver, base); + pr_trace("Masking %d GSIs...\n", redir_ent_cnt); + + for (uint8_t i = 0; i < redir_ent_cnt; ++i) { + ioapic_gsi_mask(i); + } +} diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c new file mode 100644 index 0000000..896c2bd --- /dev/null +++ b/sys/arch/amd64/amd64/lapic.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2023-2024 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/param.h> +#include <sys/types.h> +#include <sys/panic.h> +#include <sys/mmio.h> +#include <sys/syslog.h> +#include <sys/spinlock.h> +#include <dev/timer.h> +#include <machine/intr.h> +#include <machine/isa/i8254.h> +#include <machine/lapicvar.h> +#include <machine/lapic.h> +#include <machine/cpuid.h> +#include <machine/cpu.h> +#include <machine/msr.h> +#include <machine/idt.h> +#include <machine/tss.h> + +#define pr_trace(fmt, ...) kprintf("lapic: " fmt, ##__VA_ARGS__) + +/* + * Only calls pr_trace if we are the BSP. + */ +#define bsp_trace(...) do { \ + uint64_t msr_val; \ + \ + msr_val = rdmsr(IA32_APIC_BASE_MSR); \ + if (ISSET(msr_val, BIT(8))) { \ + pr_trace(__VA_ARGS__); \ + } \ + } while (0); + +static struct timer lapic_timer; +static uint8_t lapic_timer_vec = 0; +uintptr_t g_lapic_base = 0; + +void lapic_tmr_isr(void); + +/* + * Returns true if LAPIC is supported. + * + * LAPIC is supported if CPUID.(EAX=1H):EDX[9] == 1 + */ +static inline bool +lapic_supported(void) +{ + uint32_t eax, edx, tmp; + + CPUID(0x00000001, eax, tmp, tmp, edx); + return ISSET(edx, BIT(9)); +} + +/* + * Checks if the processor supports x2APIC + * mode. Returns true if so. + */ +static inline bool +lapic_has_x2apic(void) +{ + uint32_t ecx, tmp; + + CPUID(0x00000001, tmp, tmp, ecx, tmp); + return ISSET(ecx, BIT(21)); +} + +/* + * Reads a 32 bit value from Local APIC + * register space. + * + * @reg: Register to read from. + */ +static inline uint64_t +lapic_readl(uint32_t reg) +{ + void *addr; + const struct cpu_info *ci = this_cpu(); + + if (!ci->has_x2apic) { + addr = (void *)(g_lapic_base + reg); + return mmio_read32(addr); + } else { + reg >>= 4; + return rdmsr(x2APIC_MSR_BASE + reg); + } +} + +/* + * Writes a 32 bit value to Local APIC + * register space. + * + * @reg: Register to write to. + */ +static inline void +lapic_writel(uint32_t reg, uint64_t val) +{ + void *addr; + const struct cpu_info *ci = this_cpu(); + + if (!ci->has_x2apic) { + addr = (void *)(g_lapic_base + reg); + mmio_write32(addr, val); + } else { + reg >>= 4; + wrmsr(x2APIC_MSR_BASE + reg, val); + } +} + +/* + * Starts the Local APIC countdown timer... + * + * @mask: True to mask timer. + * @mode: Timer mode. + * @count: Count to start at. + */ +static inline void +lapic_timer_start(bool mask, uint8_t mode, uint32_t count) +{ + uint32_t tmp; + + tmp = (mode << 17) | (mask << 16) | lapic_timer_vec; + lapic_writel(LAPIC_LVT_TMR, tmp); + lapic_writel(LAPIC_DCR, 0); + lapic_writel(LAPIC_INIT_CNT, count); +} + +/* + * Start Local APIC timer oneshot with number + * of ticks to count down from. + * + * @mask: If `true', timer will be masked, `count' should be 0. + * @count: Number of ticks. + */ +static void +lapic_timer_oneshot(bool mask, uint32_t count) +{ + lapic_timer_start(mask, LVT_TMR_ONESHOT, count); +} + +/* + * Start Local APIC timer oneshot in microseconds. + * + * @us: Microseconds. + */ +static void +lapic_timer_oneshot_us(size_t usec) +{ + uint64_t ticks; + struct cpu_info *ci = this_cpu(); + + ticks = usec * (ci->lapic_tmr_freq / 1000000); + lapic_timer_oneshot(false, ticks); +} + +/* + * Stops the Local APIC timer + */ +static void +lapic_timer_stop(void) +{ + lapic_writel(LAPIC_LVT_TMR, LAPIC_LVT_MASK); + lapic_writel(LAPIC_INIT_CNT, 0); +} + +/* + * Set bits within a LAPIC register + * without overwriting the whole thing. + * + * @reg: Reg with bits to be set. + * @value: Value in reg will be ORd with this. + */ +static inline void +lapic_reg_set(uint32_t reg, uint32_t value) +{ + uint32_t old; + + old = lapic_readl(reg); + lapic_writel(reg, old | value); +} + +/* + * Clear bits within a LAPIC register + * without overwriting the whole thing. + * + * @reg: Reg with bits to be cleared. + * @value: Value in reg will be cleared by this value. + */ +static inline void +lapic_reg_clear(uint32_t reg, uint32_t value) +{ + uint32_t old; + + old = lapic_readl(reg); + lapic_writel(reg, old & ~(value)); +} + +/* + * Hardware and software enable the Local APIC + * through IA32_APIC_BASE_MSR + */ +static inline void +lapic_enable(const struct cpu_info *ci) +{ + uint64_t tmp; + + /* Hardware enable the Local APIC */ + tmp = rdmsr(IA32_APIC_BASE_MSR); + tmp |= ci->has_x2apic << x2APIC_ENABLE_SHIFT; + wrmsr(IA32_APIC_BASE_MSR, tmp | LAPIC_HW_ENABLE); + + /* Software enable the Local APIC */ + lapic_reg_set(LAPIC_SVR, LAPIC_SW_ENABLE); +} + +/* + * Reads the Local APIC ID of the current + * processor. + */ +static inline uint32_t +lapic_read_id(const struct cpu_info *ci) +{ + if (!ci->has_x2apic) { + return (lapic_readl(LAPIC_ID) >> 24) & 0xF; + } else { + return lapic_readl(LAPIC_ID); + } +} + +/* + * Init the Local APIC timer and return + * the frequency. + */ +static size_t +lapic_timer_init(void) +{ + uint16_t ticks_start, ticks_end; + size_t ticks_total, freq; + const uint16_t MAX_SAMPLES = 0xFFFF; + static struct spinlock lock = {0}; + + spinlock_acquire(&lock); + + lapic_timer_stop(); + i8254_set_reload(MAX_SAMPLES); + ticks_start = i8254_get_count(); + + lapic_writel(LAPIC_INIT_CNT, MAX_SAMPLES); + while (lapic_readl(LAPIC_CUR_CNT) != 0); + + ticks_end = i8254_get_count(); + ticks_total = ticks_start - ticks_end; + + freq = (MAX_SAMPLES / ticks_total) * I8254_DIVIDEND; + lapic_timer_stop(); + + spinlock_release(&lock); + return freq; +} + +/* + * Indicates that the current interrupt is finished + * being serviced. + */ +void +lapic_eoi(void) +{ + lapic_writel(LAPIC_EOI, 0); +} + +void +lapic_init(void) +{ + struct cpu_info *ci = this_cpu(); + union tss_stack tmr_stack; + + /* + * Hyra currently depends on the existance + * of a Local APIC. + */ + if (!lapic_supported()) { + panic("This machine does not support LAPIC!\n"); + } + + /* Try to allocate LAPIC timer interrupt stack */ + if (tss_alloc_stack(&tmr_stack, DEFAULT_PAGESIZE) != 0) { + panic("Failed to allocate LAPIC TMR stack!\n"); + } + + tss_update_ist(ci, tmr_stack, IST_SCHED); + + /* Allocate a vector if needed */ + if (lapic_timer_vec == 0) { + lapic_timer_vec = intr_alloc_vector("lapictmr", IPL_CLOCK); + idt_set_desc(lapic_timer_vec, IDT_INT_GATE, ISR(lapic_tmr_isr), + IST_SCHED); + } + + /* Ensure the LAPIC base is valid */ + if (g_lapic_base == 0) { + panic("Invalid LAPIC base\n"); + } + + ci->has_x2apic = lapic_has_x2apic(); + lapic_enable(ci); + + ci->apicid = lapic_read_id(ci); + ci->lapic_tmr_freq = lapic_timer_init(); + bsp_trace("BSP LAPIC enabled in %s mode (id=%d)\n", + ci->has_x2apic ? "x2APIC" : "xAPIC", ci->apicid); + + /* Try to register the timer */ + lapic_timer.name = "LAPIC_INTEGRATED_TIMER"; + lapic_timer.stop = lapic_timer_stop; + lapic_timer.oneshot_us = lapic_timer_oneshot_us; + register_timer(TIMER_SCHED, &lapic_timer); +} diff --git a/sys/arch/amd64/amd64/lapic_intr.S b/sys/arch/amd64/amd64/lapic_intr.S new file mode 100644 index 0000000..a3fa7e4 --- /dev/null +++ b/sys/arch/amd64/amd64/lapic_intr.S @@ -0,0 +1,11 @@ +#include <machine/frameasm.h> + + .text + .globl lapic_tmr_isr +lapic_tmr_isr: + push_trapframe $0 + mov %rsp, %rdi + call sched_switch + call lapic_eoi + pop_trapframe + iretq diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c new file mode 100644 index 0000000..82fa97b --- /dev/null +++ b/sys/arch/amd64/amd64/machdep.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023-2024 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 <machine/cpu.h> +#include <machine/gdt.h> +#include <machine/tss.h> +#include <machine/idt.h> +#include <machine/trap.h> +#include <machine/asm.h> +#include <machine/cpuid.h> +#include <machine/lapic.h> + +#if defined(__SPECTRE_IBRS) +#define SPECTRE_IBRS __SPECTRE_IBRS +#else +#define SPECTRE_IBRS 0 +#endif + +int ibrs_enable(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 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); + idt_set_desc(0x4, IDT_TRAP_GATE, ISR(overflow), 0); + idt_set_desc(0x5, IDT_TRAP_GATE, ISR(bound_range), 0); + idt_set_desc(0x6, IDT_TRAP_GATE, ISR(invl_op), 0); + idt_set_desc(0x8, IDT_TRAP_GATE, ISR(double_fault), 0); + idt_set_desc(0xA, IDT_TRAP_GATE, ISR(invl_tss), 0); + idt_set_desc(0xB, IDT_TRAP_GATE, ISR(segnp), 0); + idt_set_desc(0xC, IDT_TRAP_GATE, ISR(ss_fault), 0); + idt_set_desc(0xD, IDT_TRAP_GATE, ISR(general_prot), 0); + idt_set_desc(0xE, IDT_TRAP_GATE, ISR(page_fault), 0); +} + +static inline void +init_tss(struct cpu_info *ci) +{ + struct tss_desc *desc; + + desc = (struct tss_desc *)&g_gdt_data[GDT_TSS]; + write_tss(ci, desc); + tss_load(); +} + +static void +try_mitigate_spectre(void) +{ + if (!SPECTRE_IBRS) { + return; + } + + ibrs_enable(); +} + +/* + * Get the descriptor for the currently + * running processor. + */ +struct cpu_info * +this_cpu(void) +{ + return (void *)amd64_read_gs_base(); +} + +void +cpu_startup(struct cpu_info *ci) +{ + gdt_load(&bsp_gdtr); + idt_load(); + + setup_vectors(); + amd64_write_gs_base((uintptr_t)ci); + init_tss(ci); + + try_mitigate_spectre(); + __ASMV("sti"); /* Unmask interrupts */ + lapic_init(); +} diff --git a/sys/arch/amd64/amd64/mp.c b/sys/arch/amd64/amd64/mp.c new file mode 100644 index 0000000..9512aa6 --- /dev/null +++ b/sys/arch/amd64/amd64/mp.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023-2024 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/syslog.h> +#include <sys/spinlock.h> +#include <sys/sched.h> +#include <machine/cpu.h> +#include <vm/dynalloc.h> +#include <assert.h> +#include <string.h> + +#define pr_trace(fmt, ...) kprintf("cpu_mp: " fmt, ##__VA_ARGS__) + +static volatile struct limine_smp_request g_smp_req = { + .id = LIMINE_SMP_REQUEST, + .revision = 0 +}; + +static void +ap_trampoline(struct limine_smp_info *si) +{ + struct spinlock lock = {0}; + struct cpu_info *ci; + + spinlock_acquire(&lock); + ci = dynalloc(sizeof(*ci)); + __assert(ci != NULL); + + memset(ci, 0, sizeof(*ci)); + cpu_startup(ci); + + spinlock_release(&lock); + sched_enter(); + + while (1); +} + +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; + + /* Should not happen */ + __assert(resp != NULL); + + cpus = resp->cpus; + cpu_init_counter = resp->cpu_count - 1; + + if (resp->cpu_count == 1) { + pr_trace("CPU has 1 core, no APs to bootstrap...\n"); + return; + } + + pr_trace("Bootstrapping %d cores...\n", cpu_init_counter); + for (size_t i = 0; i < resp->cpu_count; ++i) { + if (ci->apicid == cpus[i]->lapic_id) { + pr_trace("Skip %d (BSP)... continue\n", ci->apicid); + continue; + } + + cpus[i]->goto_address = ap_trampoline; + } +} diff --git a/sys/arch/amd64/amd64/pio.c b/sys/arch/amd64/amd64/pio.c new file mode 100644 index 0000000..d4ce688 --- /dev/null +++ b/sys/arch/amd64/amd64/pio.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023-2024 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/pio.h> +#include <sys/cdefs.h> +#include <sys/types.h> + +uint8_t +inb(uint16_t port) +{ + uint8_t result; + __ASMV("in %%dx, %%al" : "=a" (result) : "d" (port)); + return result; +} + +uint16_t +inw(uint16_t port) +{ + uint16_t val; + __ASMV("inw %w1, %w0" : "=a" (val) : "Nd" (port)); + return val; +} + +uint32_t +inl(uint16_t port) +{ + uint32_t val; + __ASMV("inl %w1, %0" : "=a" (val) : "Nd" (port)); + return val; +} + +void +outb(uint16_t port, uint8_t val) +{ + __ASMV("out %%al, %%dx" : :"a" (val), "d" (port)); +} + +void +outw(uint16_t port, uint16_t val) +{ + __ASMV("outw %w0, %w1" : : "a" (val), "Nd" (port)); +} + +void +outl(uint16_t port, uint32_t val) +{ + __ASMV("outl %0, %w1" : : "a" (val), "Nd" (port)); +} diff --git a/sys/arch/amd64/amd64/pmap.c b/sys/arch/amd64/amd64/pmap.c new file mode 100644 index 0000000..108cd91 --- /dev/null +++ b/sys/arch/amd64/amd64/pmap.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2023-2024 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/cdefs.h> +#include <sys/errno.h> +#include <machine/tlb.h> +#include <machine/vas.h> +#include <vm/pmap.h> +#include <vm/physmem.h> +#include <vm/vm.h> +#include <assert.h> +#include <string.h> + +/* + * Page-Table Entry (PTE) flags + * + * See Intel SDM Vol 3A, Section 4.5, Table 4-19 + */ +#define PTE_ADDR_MASK 0x000FFFFFFFFFF000 +#define PTE_P BIT(0) /* Present */ +#define PTE_RW BIT(1) /* Writable */ +#define PTE_US BIT(2) /* User r/w allowed */ +#define PTE_PWT BIT(3) /* Page-level write-through */ +#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_GLOBAL BIT(8) +#define PTE_NX BIT(63) /* Execute-disable */ + +/* + * Convert pmap protection flags to PTE flags. + */ +static uint64_t +pmap_prot_to_pte(vm_prot_t prot) +{ + uint64_t pte_flags = PTE_P | PTE_NX; + + if (ISSET(prot, PROT_WRITE)) + pte_flags |= PTE_RW; + if (ISSET(prot, PROT_EXEC)) + pte_flags &= ~(PTE_NX); + if (ISSET(prot, PROT_USER)) + pte_flags |= PTE_US; + + return pte_flags; +} + +/* + * Returns index for a specific pagemap level. + * + * @level: Requested level. + * @va: Virtual address. + */ +static size_t +pmap_get_level_index(uint8_t level, vaddr_t va) +{ + __assert(level <= 4 && level != 0); + + switch (level) { + case 4: + return (va >> 39) & 0x1FF; + case 3: + return (va >> 30) & 0x1FF; + case 2: + return (va >> 21) & 0x1FF; + case 1: + return (va >> 12) & 0x1FF; + default: /* Should not be reachable */ + return 0; + } +} + +/* + * Extract a pagemap level. + */ +static uintptr_t * +pmap_extract(uint8_t level, vaddr_t va, vaddr_t *pmap, bool alloc) +{ + uintptr_t next, level_alloc; + size_t idx = pmap_get_level_index(level, va); + + if (pmap == NULL) { + return NULL; + } + + if (ISSET(pmap[idx], PTE_P)) { + next = (pmap[idx] & PTE_ADDR_MASK); + return PHYS_TO_VIRT(next); + } + + if (!alloc) { + return NULL; + } + + /* Allocate the next level */ + level_alloc = vm_alloc_frame(1); + if (level_alloc == 0) { + return NULL; + } + + memset(PHYS_TO_VIRT(level_alloc), 0, DEFAULT_PAGESIZE); + pmap[idx] = level_alloc | (PTE_P | PTE_RW | PTE_US); + return PHYS_TO_VIRT(level_alloc); +} + +/* + * Modify a page table by writing `val' to it. + * + * @vas: Virtual address space. + * @va: Virtual address. + * @alloc: True to alloc new entries. + * @res: Result + */ +static int +pmap_get_tbl(struct vas vas, vaddr_t va, bool alloc, uintptr_t **res) +{ + uintptr_t *pml4 = PHYS_TO_VIRT(vas.top_level); + uintptr_t *pdpt, *pd, *tbl; + int status = 0; + + pdpt = pmap_extract(4, va, pml4, alloc); + if (pdpt == NULL) { + status = 1; + goto done; + } + + pd = pmap_extract(3, va, pdpt, alloc); + if (pd == NULL) { + status = 1; + goto done; + } + + tbl = pmap_extract(2, va, pd, alloc); + if (tbl == NULL) { + status = 1; + goto done; + } + + *res = tbl; +done: + return status; +} + +/* + * Update the value in a page table. + * + * @vas: Virtual address space. + * @va: Target virtual address. + * @val: Value to write. + */ +static int +pmap_update_tbl(struct vas vas, vaddr_t va, uint64_t val) +{ + uintptr_t *tbl; + int status; + + if ((status = pmap_get_tbl(vas, va, true, &tbl)) != 0) { + return status; + } + + tbl[pmap_get_level_index(1, va)] = val; + tlb_flush(va); + return 0; +} + +int +pmap_new_vas(struct vas *res) +{ + const struct vas *kvas = &g_kvas; + struct vas new_vas; + uint64_t *src, *dest; + + new_vas.cr3_flags = kvas->cr3_flags; + new_vas.top_level = vm_alloc_frame(1); + if (new_vas.top_level == 0) + return -ENOMEM; + + src = PHYS_TO_VIRT(kvas->top_level); + dest = PHYS_TO_VIRT(new_vas.top_level); + + /* + * Keep the higher half but zero out the lower + * half for user programs. + */ + for (int i = 0; i < 512; ++i) { + if (i < 256) { + dest[i] = 0; + continue; + } + + dest[i] = src[i]; + } + + *res = new_vas; + return 0; +} + +struct vas +pmap_read_vas(void) +{ + struct vas vas; + uint64_t cr3_raw; + + __ASMV("mov %%cr3, %0" + : "=r" (cr3_raw) + : + : "memory" + ); + + vas.cr3_flags = cr3_raw & ~PTE_ADDR_MASK; + vas.top_level = cr3_raw & PTE_ADDR_MASK; + vas.use_l5_paging = false; /* TODO */ + vas.lock.lock = 0; + return vas; +} + +void +pmap_switch_vas(struct vas vas) +{ + uintptr_t cr3_val = vas.cr3_flags | vas.top_level; + + __ASMV("mov %0, %%cr3" + : + : "r" (cr3_val) + : "memory" + ); +} + +int +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)); +} + +int +pmap_unmap(struct vas vas, vaddr_t va) +{ + return pmap_update_tbl(vas, va, 0); +} diff --git a/sys/arch/amd64/amd64/proc_machdep.c b/sys/arch/amd64/amd64/proc_machdep.c new file mode 100644 index 0000000..eb3866a --- /dev/null +++ b/sys/arch/amd64/amd64/proc_machdep.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023-2024 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/proc.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <machine/frame.h> +#include <machine/gdt.h> +#include <vm/physmem.h> +#include <vm/vm.h> +#include <vm/map.h> +#include <string.h> + +/* + * MD thread init code + * + * @p: New process. + * @parent: Parent of new process. + * @ip: Instruction pointer. + */ +int +md_td_init(struct proc *p, struct proc *parent, uintptr_t ip) +{ + uintptr_t stack_base; + struct trapframe *tfp, *parent_tfp; + struct pcb *pcbp; + uint8_t rpl; + int error; + + tfp = &p->tf; + + /* Create a new VAS for this thread */ + pcbp = &p->pcb; + if ((error = pmap_new_vas(&pcbp->addrsp)) != 0) + return error; + + /* + * If parent is NULL, assume kernel space and zero the + * trapframe. Otherwise we can just set it up normally. + */ + if (parent == NULL) { + rpl = 0; + memset(tfp, 0, sizeof(*tfp)); + } else { + parent_tfp = &parent->tf; + rpl = parent_tfp->cs & 3; + memcpy(tfp, &parent->tf, sizeof(*tfp)); + } + + /* + * RPL being 3 indicates that the parent thread is in + * userland. If this is the case, we'd want this new thread + * to also be in userland. + */ + tfp->rip = ip; + tfp->cs = (rpl == 3) ? (USER_CS | 3) : KERNEL_CS; + tfp->ss = (rpl == 3) ? (USER_DS | 3) : KERNEL_DS; + tfp->rflags = 0x202; + + /* Try to allocate a new stack */ + stack_base = vm_alloc_frame(PROC_STACK_PAGES); + if (stack_base == 0) + return -ENOMEM; + + /* + * If RPL is 0 (kernel), adjust the stack base to the + * higher half. If we have an RPL of 3 (user) then we'll + * need to identity map the stack. + */ + if (rpl == 0) { + stack_base += VM_HIGHER_HALF; + } else { + vm_map(pcbp->addrsp, stack_base, stack_base, + PROT_READ | PROT_WRITE | PROT_USER, PROC_STACK_PAGES); + } + + p->stack_base = stack_base; + tfp->rsp = ALIGN_DOWN((stack_base + PROC_STACK_SIZE) - 1, 16); + return 0; +} diff --git a/sys/arch/amd64/amd64/reboot.c b/sys/arch/amd64/amd64/reboot.c new file mode 100644 index 0000000..da1b46a --- /dev/null +++ b/sys/arch/amd64/amd64/reboot.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2024 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/reboot.h> +#include <sys/param.h> +#include <sys/cdefs.h> +#include <machine/pio.h> + +__always_inline static inline void +cpu_halt(void) +{ + __ASMV("cli; hlt"); +} + +void +cpu_reboot(int method) +{ + if (ISSET(method, REBOOT_HALT)) { + cpu_halt(); + } + + /* Pulse the reset line until the machine goes down */ + for (;;) { + outb(0x64, 0xFE); + } +} diff --git a/sys/arch/amd64/amd64/spectre.S b/sys/arch/amd64/amd64/spectre.S new file mode 100644 index 0000000..6781cbd --- /dev/null +++ b/sys/arch/amd64/amd64/spectre.S @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023-2024 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/msr.h> + + .text + .globl ibrs_enable + .type ibrs_enable, @function +ibrs_enable: + /* See if it is supported */ + mov $7, %eax + xor %ecx, %ecx + cpuid + bt $26, %edx + jnc fail + + /* Now we enable it */ + mov $IA32_SPEC_CTL, %ecx + rdmsr + or $1, %eax + wrmsr + xor %rax, %rax + jmp 1f +fail: + mov $1, %rax +1: + retq diff --git a/sys/arch/amd64/amd64/trap.S b/sys/arch/amd64/amd64/trap.S new file mode 100644 index 0000000..fb3ca77 --- /dev/null +++ b/sys/arch/amd64/amd64/trap.S @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2023-2024 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> +#include <machine/trap.h> + +.macro handle_trap + mov %rsp, %rdi + call trap_handler +.endm + + .text + .globl breakpoint_handler +breakpoint_handler: + push_trapframe_ec $TRAP_BREAKPOINT + + handle_trap + + cli + hlt + + .globl arith_err +arith_err: + push_trapframe_ec $TRAP_ARITH_ERR + + handle_trap + + cli + hlt + + .globl overflow +overflow: + push_trapframe_ec $TRAP_OVERFLOW + + handle_trap + + cli + hlt + + .globl bound_range +bound_range: + push_trapframe_ec $TRAP_BOUND_RANGE + + handle_trap + + cli + hlt + + .globl invl_op +invl_op: + push_trapframe_ec $TRAP_INVLOP + + handle_trap + + cli + hlt + + .globl double_fault +double_fault: + push_trapframe_ec $TRAP_DOUBLE_FAULT + + handle_trap + + cli + hlt + + .globl invl_tss +invl_tss: + push_trapframe_ec $TRAP_INVLTSS + + handle_trap + + cli + hlt + + .globl segnp +segnp: + push_trapframe_ec $TRAP_SEGNP + + handle_trap + + cli + hlt + + .globl general_prot +general_prot: + push_trapframe_ec $TRAP_PROTFLT + + handle_trap + + cli + hlt + + .globl page_fault +page_fault: + push_trapframe_ec $TRAP_PAGEFLT + + handle_trap + cli + hlt + + .globl nmi +nmi: + push_trapframe_ec $TRAP_NMI + + handle_trap + + cli + hlt + + .globl ss_fault +ss_fault: + push_trapframe_ec $TRAP_SS + + handle_trap + cli + hlt diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c new file mode 100644 index 0000000..2d479ee --- /dev/null +++ b/sys/arch/amd64/amd64/trap.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023-2024 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/param.h> +#include <sys/cdefs.h> +#include <sys/reboot.h> +#include <sys/panic.h> +#include <sys/syslog.h> +#include <machine/trap.h> +#include <machine/frame.h> + +#define pr_error(fmt, ...) kprintf("trap: " fmt, ##__VA_ARGS__) + +static const char *trap_type[] = { + [TRAP_BREAKPOINT] = "breakpoint", + [TRAP_ARITH_ERR] = "arithmetic error", + [TRAP_OVERFLOW] = "overflow", + [TRAP_BOUND_RANGE] = "bound range exceeded", + [TRAP_INVLOP] = "invalid opcode", + [TRAP_DOUBLE_FAULT] = "double fault", + [TRAP_INVLTSS] = "invalid TSS", + [TRAP_SEGNP] = "segment not present", + [TRAP_PROTFLT] = "general protection", + [TRAP_PAGEFLT] = "page fault", + [TRAP_NMI] = "non-maskable interrupt", + [TRAP_SS] = "stack-segment fault" +}; + +static inline uintptr_t +pf_faultaddr(void) +{ + uintptr_t cr2; + __ASMV("mov %%cr2, %0\n" : "=r" (cr2) :: "memory"); + return cr2; +} + +static void +regdump(struct trapframe *tf) +{ + uintptr_t cr3, cr2 = pf_faultaddr(); + + __ASMV("mov %%cr3, %0\n" + : "=r" (cr3) + : + : "memory" + ); + + kprintf(OMIT_TIMESTAMP + "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", + tf->rax, tf->rcx, tf->rdx, + tf->rbx, tf->rsi, tf->rdi, + tf->rflags, cr2, cr3, + tf->rbp, tf->rsp, tf->rip); +} + +void +trap_handler(struct trapframe *tf) +{ + if (tf->trapno >= NELEM(trap_type)) { + panic("Got unknown trap %d\n", tf->trapno); + } + + pr_error("Got %s\n", trap_type[tf->trapno]); + regdump(tf); + panic("Fatal trap - halting\n"); +} diff --git a/sys/arch/amd64/amd64/tss.c b/sys/arch/amd64/amd64/tss.c new file mode 100644 index 0000000..5aab74a --- /dev/null +++ b/sys/arch/amd64/amd64/tss.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2023-2024 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/param.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/panic.h> +#include <vm/dynalloc.h> +#include <vm/physmem.h> +#include <machine/tss.h> +#include <machine/cpu.h> +#include <assert.h> +#include <string.h> + +/* + * Allocates memory for TSS and kernel + * stack. + * + * XXX: Kernel stack is allocated from + * vm_alloc_frame() + */ +static void +alloc_resources(struct cpu_info *ci) +{ + const size_t STACK_SIZE = 0x1000; + struct tss_entry *tss; + static uintptr_t rsp0_base, rsp0; + + if (ci->tss == NULL) { + tss = dynalloc(sizeof(*tss)); + + if (tss == NULL) { + panic("Failed to alloc TSS\n"); + } + + memset(tss, 0, sizeof(*tss)); + rsp0_base = vm_alloc_frame(1); + + if (rsp0_base == 0) { + panic("Could not allocate RSP0 base\n"); + } + + rsp0 = rsp0_base + STACK_SIZE; + tss->rsp0_lo = rsp0 & 0xFFFFFFFF; + tss->rsp0_hi = (rsp0 >> 32) & 0xFFFFFFFF; + ci->tss = tss; + } +} + +/* + * Update interrupt stack table entry `istno' with `stack' + * + * @stack: Interrupt stack. + * @istno: IST number, must be 1-based. + * + * Returns 0 on success. + */ +int +tss_update_ist(struct cpu_info *ci, union tss_stack stack, uint8_t istno) +{ + volatile struct tss_entry *tss = ci->tss; + + __assert(tss != NULL); + + switch (istno) { + case 1: + tss->ist1_lo = stack.top_lo; + tss->ist1_hi = stack.top_hi; + break; + case 2: + tss->ist2_lo = stack.top_lo; + tss->ist2_hi = stack.top_hi; + break; + case 3: + tss->ist3_lo = stack.top_lo; + tss->ist3_hi = stack.top_hi; + break; + case 4: + tss->ist4_lo = stack.top_lo; + tss->ist4_hi = stack.top_hi; + break; + case 5: + tss->ist5_lo = stack.top_lo; + tss->ist5_hi = stack.top_hi; + break; + case 6: + tss->ist6_lo = stack.top_lo; + tss->ist6_hi = stack.top_hi; + break; + case 7: + tss->ist7_lo = stack.top_lo; + tss->ist7_hi = stack.top_hi; + break; + default: + return -EINVAL; + }; + + return 0; +} + +/* + * Allocates TSS stack. + * + * @entry_out: Pointer to location where allocated entry + * will be sent. + * + * Returns 0 on success. + */ +int +tss_alloc_stack(union tss_stack *entry_out, size_t size) +{ + uintptr_t base = (uintptr_t)dynalloc(size); + + if (base == 0) { + return -ENOMEM; + } + + entry_out->top = base + size; + return 0; +} + +void +write_tss(struct cpu_info *ci, struct tss_desc *desc) +{ + volatile struct tss_entry *tss; + uintptr_t tss_base; + + alloc_resources(ci); + tss_base = (uintptr_t)ci->tss; + + /* + * XXX: The AVL (Available for use by system software) + * bit is ignored by hardware and it is up to us + * to decide how to use it... As of now, it is useless + * to us and shall remain 0. + */ + desc->seglimit = sizeof(struct tss_entry); + desc->p = 1; /* Must be present to be valid! */ + desc->g = 0; /* Granularity -> 0 */ + desc->avl = 0; /* Not used */ + desc->dpl = 0; /* Descriptor Privilege Level -> 0 */ + desc->type = 0x9; /* For TSS -> 0x9 (0b1001) */ + + desc->base_lo16 = tss_base & 0xFFFF; + desc->base_mid8 = (tss_base >> 16) & 0xFF; + desc->base_hi_mid8 = (tss_base >> 24) & 0xFF; + desc->base_hi32 = (tss_base >> 32) & 0xFFFFFFFF; + + tss = ci->tss; + tss->io_base = 0xFF; /* Disallow ring 3 port I/O */ +} diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC new file mode 100644 index 0000000..a7bbc81 --- /dev/null +++ b/sys/arch/amd64/conf/GENERIC @@ -0,0 +1,5 @@ +// Kernel options +option SPECTRE_IBRS no + +// Kernel constants +setval SCHED_NQUEUE 4 diff --git a/sys/arch/amd64/conf/link.ld b/sys/arch/amd64/conf/link.ld new file mode 100644 index 0000000..9c47a81 --- /dev/null +++ b/sys/arch/amd64/conf/link.ld @@ -0,0 +1,53 @@ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) +ENTRY(main) + +PHDRS +{ + text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ + rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ + data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ +} + +SECTIONS +{ + . = 0xFFFFFFFF80000000; + + .text : { + *(.text .text.*) + } :text + + . += CONSTANT(MAXPAGESIZE); + + .rodata : { + *(.rodata .rodata.*) + } :rodata + + .drivers : { + __drivers_init_start = .; + *(.drivers .drivers) + __drivers_init_end = .; + } :rodata + + . += CONSTANT(MAXPAGESIZE); + + .data : { + *(.data) + } :data + + .bss : { + *(COMMON) + *(.bss .bss.*) + } :data + + /* -- Cache line alignment -- */ + . = ALIGN(64); + .data.cacheline_aligned : { + *(.data.cacheline_aligned) + } + + /DISCARD/ : { + *(.eh_frame) + *(.note .note.*) + } +} diff --git a/sys/arch/amd64/isa/i8254.c b/sys/arch/amd64/isa/i8254.c new file mode 100644 index 0000000..b5ceb9c --- /dev/null +++ b/sys/arch/amd64/isa/i8254.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023-2024 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 <machine/isa/i8254.h> +#include <machine/pio.h> + +/* + * Fetches the current count. + */ +uint16_t +i8254_get_count(void) +{ + uint8_t lo, hi; + + outb(I8254_COMMAND, 0x00); + lo = inb(0x40); + hi = inb(0x40); + return COMBINE8(hi, lo); +} + +/* + * Set the reload value + * + * The reload value is where the i8254's counter + * starts... + */ +void +i8254_set_reload(uint16_t val) +{ + outb(I8254_COMMAND, 0x34); + outb(0x40, (val & 0xFF)); + outb(0x40, (val >> 8) & 0xFF); +} + +void +i8254_set_frequency(uint64_t freq_hz) +{ + uint64_t divisor = I8254_DIVIDEND / freq_hz; + + if ((I8254_DIVIDEND % freq_hz) > (freq_hz / 2)) { + ++divisor; + } + + i8254_set_reload(freq_hz); +} |