summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/amd64/amd64/acpi_machdep.c79
-rw-r--r--sys/arch/amd64/amd64/gdt.c78
-rw-r--r--sys/arch/amd64/amd64/hpet.c186
-rw-r--r--sys/arch/amd64/amd64/idt.c70
-rw-r--r--sys/arch/amd64/amd64/intr.c98
-rw-r--r--sys/arch/amd64/amd64/ioapic.c230
-rw-r--r--sys/arch/amd64/amd64/lapic.c343
-rw-r--r--sys/arch/amd64/amd64/lapic_intr.S11
-rw-r--r--sys/arch/amd64/amd64/machdep.c114
-rw-r--r--sys/arch/amd64/amd64/mp.c93
-rw-r--r--sys/arch/amd64/amd64/pio.c74
-rw-r--r--sys/arch/amd64/amd64/pmap.c270
-rw-r--r--sys/arch/amd64/amd64/proc_machdep.c107
-rw-r--r--sys/arch/amd64/amd64/reboot.c52
-rw-r--r--sys/arch/amd64/amd64/spectre.S53
-rw-r--r--sys/arch/amd64/amd64/trap.S143
-rw-r--r--sys/arch/amd64/amd64/trap.c95
-rw-r--r--sys/arch/amd64/amd64/tss.c177
-rw-r--r--sys/arch/amd64/conf/GENERIC5
-rw-r--r--sys/arch/amd64/conf/link.ld53
-rw-r--r--sys/arch/amd64/isa/i8254.c73
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);
+}