From dbfcd0e88e471564392549652d0bde2a967c34f4 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Wed, 5 Jun 2024 20:58:51 -0400 Subject: kernel/amd64: Add initial LAPIC code Signed-off-by: Ian Moffett --- sys/arch/amd64/amd64/acpi_machdep.c | 2 + sys/arch/amd64/amd64/lapic.c | 214 ++++++++++++++++++++++++++++++++++++ sys/arch/amd64/amd64/machdep.c | 13 +++ sys/include/arch/amd64/cpu.h | 3 +- sys/include/arch/amd64/lapic.h | 39 +++++++ sys/include/arch/amd64/lapicvar.h | 85 ++++++++++++++ sys/include/arch/amd64/msr.h | 1 + 7 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 sys/arch/amd64/amd64/lapic.c create mode 100644 sys/include/arch/amd64/lapic.h create mode 100644 sys/include/arch/amd64/lapicvar.h 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 #include #include +#include #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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 #include #include +#include #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 + +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 + +/* 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 -- cgit v1.2.3