aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/amd64/amd64/acpi_machdep.c2
-rw-r--r--sys/arch/amd64/amd64/lapic.c214
-rw-r--r--sys/arch/amd64/amd64/machdep.c13
-rw-r--r--sys/include/arch/amd64/cpu.h3
-rw-r--r--sys/include/arch/amd64/lapic.h39
-rw-r--r--sys/include/arch/amd64/lapicvar.h85
-rw-r--r--sys/include/arch/amd64/msr.h1
7 files changed, 356 insertions, 1 deletions
diff --git a/sys/arch/amd64/amd64/acpi_machdep.c b/sys/arch/amd64/amd64/acpi_machdep.c
index d1686df..3bcdc9d 100644
--- a/sys/arch/amd64/amd64/acpi_machdep.c
+++ b/sys/arch/amd64/amd64/acpi_machdep.c
@@ -33,6 +33,7 @@
#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__)
@@ -50,6 +51,7 @@ acpi_init_madt(void)
cur = (uint8_t *)(madt + 1);
end = (uint8_t *)madt + madt->hdr.length;
+ g_lapic_base = madt->lapic_addr;
while (cur < end) {
apichdr = (void *)cur;
diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c
new file mode 100644
index 0000000..f61715d
--- /dev/null
+++ b/sys/arch/amd64/amd64/lapic.c
@@ -0,0 +1,214 @@
+/*
+ * 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 <machine/lapicvar.h>
+#include <machine/lapic.h>
+#include <machine/cpuid.h>
+#include <machine/cpu.h>
+#include <machine/msr.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);
+
+uintptr_t g_lapic_base = 0;
+
+/*
+ * 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);
+ }
+}
+
+/*
+ * 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);
+ }
+}
+
+void
+lapic_init(void)
+{
+ struct cpu_info *ci = this_cpu();
+
+ /*
+ * Hyra currently depends on the existance
+ * of a Local APIC.
+ */
+ if (!lapic_supported()) {
+ panic("This machine does not support LAPIC!\n");
+ }
+
+ /* 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);
+ bsp_trace("BSP LAPIC enabled in %s mode (id=%d)\n",
+ ci->has_x2apic ? "x2APIC" : "xAPIC", ci->apicid);
+}
diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c
index 275c23e..983a480 100644
--- a/sys/arch/amd64/amd64/machdep.c
+++ b/sys/arch/amd64/amd64/machdep.c
@@ -34,6 +34,7 @@
#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
@@ -76,6 +77,16 @@ try_mitigate_spectre(void)
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(void)
{
@@ -84,5 +95,7 @@ cpu_startup(void)
setup_vectors();
amd64_write_gs_base((uintptr_t)&g_bsp_ci);
+
try_mitigate_spectre();
+ lapic_init();
}
diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h
index efa60ad..4e5baf1 100644
--- a/sys/include/arch/amd64/cpu.h
+++ b/sys/include/arch/amd64/cpu.h
@@ -35,9 +35,10 @@
struct cpu_info {
uint32_t apicid;
+ uint8_t has_x2apic : 1;
};
void cpu_startup(void);
-
+struct cpu_info *this_cpu(void);
#endif /* !_MACHINE_CPU_H_ */
diff --git a/sys/include/arch/amd64/lapic.h b/sys/include/arch/amd64/lapic.h
new file mode 100644
index 0000000..8a6bb14
--- /dev/null
+++ b/sys/include/arch/amd64/lapic.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef _MACHINE_LAPIC_H_
+#define _MACHINE_LAPIC_H_
+
+#include <sys/types.h>
+
+void lapic_init(void);
+
+extern uintptr_t g_lapic_base;
+
+#endif /* !_MACHINE_LAPIC_H_ */
diff --git a/sys/include/arch/amd64/lapicvar.h b/sys/include/arch/amd64/lapicvar.h
new file mode 100644
index 0000000..e224e43
--- /dev/null
+++ b/sys/include/arch/amd64/lapicvar.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef _MACHINE_LAPICVAR_H_
+#define _MACHINE_LAPICVAR_H_
+
+#include <sys/param.h>
+
+/* LAPIC register offsets */
+#define LAPIC_ID 0x0020U /* ID Register */
+#define LAPIC_VERSION 0x0030U /* Version Register */
+#define LAPIC_TPR 0x0080U /* Task Priority Register */
+#define LAPIC_APR 0x0090U /* Arbitration Priority Register */
+#define LAPIC_PPR 0x00A0U /* Processor Priority Register */
+#define LAPIC_EOI 0x00B0U /* End Of Interrupt Register */
+#define LAPIC_RRD 0x00C0U /* Remote Read Register */
+#define LAPIC_LDR 0x00D0U /* Logical Destination Register */
+#define LAPIC_DFR 0x00E0U /* Destination Format Register */
+#define LAPIC_SVR 0x00F0U /* Spurious Vector Register */
+#define LAPIC_ISR 0x0100U /* In service register (max=0x0220) */
+#define LAPIC_TMR 0x0180U /* Trigger Mode Register (max=0x0220) */
+#define LAPIC_IRR 0x0200U /* Interrupt Request Register (max=0x0270) */
+#define LAPIC_ERR 0x0280U /* Error Status Register */
+#define LAPIC_ICRLO 0x0300U /* Interrupt Command Low Register */
+#define LAPIC_ICRHI 0x0310U /* Interrupt Command High Register */
+#define LAPIC_LVT_TMR 0x0320U /* LVT Timer Register */
+#define LAPIC_DCR 0x03E0U /* Divide Configuration Register (for timer) */
+#define LAPIC_INIT_CNT 0x0380U /* Initial Count Register (for timer) */
+#define LAPIC_CUR_CNT 0x0390U /* Current Count Register (for timer) */
+
+/*
+ * The x2APIC register space is accessed via
+ * RDMSR/WRMSR instructions. The below defines
+ * the base MSR address for the register space.
+ */
+#define x2APIC_MSR_BASE 0x00000800
+
+/*
+ * To hardware enable, OR the value of the IA32_APIC_BASE
+ * MSR with LAPIC_HW_ENABLE and rewrite it.
+ *
+ * To software enable, OR the value of the SVR with
+ * LAPIC_SW_ENABLE and rewrite it.
+ *
+ * LAPIC_SW_ENABLE has the low 8 bits set as some hardware
+ * requires the spurious vector to be hardwired to 1s so
+ * we'll go with that to be safe.
+ */
+#define LAPIC_HW_ENABLE BIT(11)
+#define LAPIC_SW_ENABLE (BIT(8) | 0xFF)
+#define x2APIC_ENABLE_SHIFT 10
+
+/* LVT bits */
+#define LAPIC_LVT_MASK BIT(16)
+#define LVT_TMR_ONESHOT 0x00
+#define LVT_TMR_PERIODIC 0x01
+#define LVT_TMR_TSC_DEADLINE 0x02
+
+#endif /* !_MACHINE_LAPICVAR_H_ */
diff --git a/sys/include/arch/amd64/msr.h b/sys/include/arch/amd64/msr.h
index 6ad95f1..d3d0c9a 100644
--- a/sys/include/arch/amd64/msr.h
+++ b/sys/include/arch/amd64/msr.h
@@ -32,6 +32,7 @@
#define IA32_SPEC_CTL 0x00000048
#define IA32_KERNEL_GS_BASE 0xC0000102
+#define IA32_APIC_BASE_MSR 0x0000001B
#if !defined(__ASSEMBLER__)
static inline uint64_t