diff options
author | Ian Moffett <ian@osmora.org> | 2025-09-15 11:03:10 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-09-15 11:03:10 -0400 |
commit | cff93b1fb12540f6945754ba75da3d4d1a891dc7 (patch) | |
tree | 3f5abadb90c652653fee1fc1a0af2df3528d4dbe /src/sys/arch/amd64 | |
parent | 5e3270af3c21a49fe75770d39e9dce483e759e9c (diff) |
kern/amd64: Add I/O APIC initialization code
This commit introduces the initial I/O APIC driver code that does basic
initialization i.e., grabbing base address from MADT, masking each pin,
etc.
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'src/sys/arch/amd64')
-rw-r--r-- | src/sys/arch/amd64/boot/boot_chip.c | 3 | ||||
-rw-r--r-- | src/sys/arch/amd64/mainbus/ioapic.c | 215 |
2 files changed, 218 insertions, 0 deletions
diff --git a/src/sys/arch/amd64/boot/boot_chip.c b/src/sys/arch/amd64/boot/boot_chip.c index 737309f..01d090c 100644 --- a/src/sys/arch/amd64/boot/boot_chip.c +++ b/src/sys/arch/amd64/boot/boot_chip.c @@ -32,11 +32,14 @@ #include <machine/gdt.h> #include <machine/boot.h> #include <machine/i8259.h> +#include <machine/ioapic.h> void platform_boot(void) { gdt_load(); i8259_disable(); + + ioapic_init(); uart_init(); } diff --git a/src/sys/arch/amd64/mainbus/ioapic.c b/src/sys/arch/amd64/mainbus/ioapic.c new file mode 100644 index 0000000..bcd7025 --- /dev/null +++ b/src/sys/arch/amd64/mainbus/ioapic.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2025 Ian Marco Moffett and L5 engineers + * 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 the project 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/panic.h> +#include <sys/syslog.h> +#include <acpi/tables.h> +#include <acpi/acpi.h> +#include <os/mmio.h> +#include <machine/ioapic.h> +#include <machine/ioapicvar.h> + +static struct ioapic *ioapic = NULL; +static struct acpi_madt *madt = NULL; + +/* + * Write a single 32-bit value to a specific + * I/O APIC register via MMIO. + * + * @ioapic: Target I/O APIC unit + * @reg: Register to write to + * @val: Value to write to register + */ +static void +ioapic_writel(uint32_t reg, uint32_t val) +{ + uint32_t *ioregsel; + uint32_t *iowin; + + if (ioapic == NULL) { + printf("ioapic_writel: error: got NULL `ioapic'\n"); + return; + } + + ioregsel = PTR_OFFSET(ioapic->ioapic_addr, IOREGSEL); + iowin = PTR_OFFSET(ioapic->ioapic_addr, IOWIN); + + /* Latch register and write value */ + mmio_write32(ioregsel, reg); + mmio_write32(iowin, val); +} + +/* + * Read a single 32-bit value from a specific I/O + * APIC register via MMIO + * + * @ioapic: I/O APIC unit to target + * @reg: Register to read + * + * Returns the register value + */ +static uint32_t +ioapic_readl(uint32_t reg) +{ + uint32_t *ioregsel; + uint32_t *iowin; + + if (ioapic == NULL) { + printf("ioapic_readl: error: got NULL `ioapic'\n"); + return 0; + } + + ioregsel = PTR_OFFSET(ioapic->ioapic_addr, IOREGSEL); + iowin = PTR_OFFSET(ioapic->ioapic_addr, IOWIN); + + /* Latch register and read value */ + mmio_write32(ioregsel, reg); + return mmio_read32(iowin); + return 0; +} + +/* + * 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)); +} + +/* + * Read a MADT entry + * + * @type: Type that we should scan for + */ +static struct apic_header * +ioapic_read_madt(uint32_t type) +{ + uint8_t *cur, *end; + struct apic_header *apichdr; + + /* Try to read the MADT table */ + madt = acpi_query("APIC"); + if (madt == NULL) { + panic("ioapic_read_madt: failed to get MADT\n"); + } + + cur = (uint8_t *)(madt + 1); + end = (uint8_t *)madt + madt->hdr.length; + while (cur < end) { + apichdr = (void *)cur; + if (apichdr->type == type) { + return apichdr; + } + + cur += apichdr->length; + } + + return NULL; +} + +/* + * Set the I/O APIC MMIO base address + */ +static void +ioapic_set_base(void) +{ + struct apic_header *hdr; + + hdr = ioapic_read_madt(APIC_TYPE_IO_APIC); + if (hdr == NULL) { + panic("ioapic_set_base: could not read APIC_TYPE_IO_APIC\n"); + } + + ioapic = (struct ioapic *)hdr; +} + +/* + * Mask or unmask a GSI + */ +void +ioapic_gsi_mask(uint8_t gsi, uint8_t mask) +{ + union ioapic_redentry redent; + + ioapic_read_redentry(&redent, gsi); + redent.interrupt_mask = gsi & 1; + ioapic_write_redentry(&redent, gsi); +} + +/* + * Initialize the I/O APIC + */ +void +ioapic_init(void) +{ + union ioapic_redentry redent; + uint32_t ioapicver; + uint8_t ver, nredir; + + if (ioapic == NULL) { + ioapic_set_base(); + } + + /* Read the IOAPIC version register */ + ioapicver = ioapic_readl(IOAPICVER); + ver = ioapicver & 0xFF; + nredir = (ioapicver >> 16) & 0xFF; + + printf("ioapic: ioapic @ mainbus:ver=%x,nredir=%d\n" + "ioapic: masking all %d pins...\n", + ver, nredir, nredir + ); + + /* Mask each and every entry */ + for (int i = 0; i < nredir; ++i) { + ioapic_gsi_mask(i, IOAPIC_PIN_MASK); + } +} |