summaryrefslogtreecommitdiff
path: root/410kern/smp/mptable.c
diff options
context:
space:
mode:
Diffstat (limited to '410kern/smp/mptable.c')
-rw-r--r--410kern/smp/mptable.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/410kern/smp/mptable.c b/410kern/smp/mptable.c
new file mode 100644
index 0000000..cb0a5c9
--- /dev/null
+++ b/410kern/smp/mptable.c
@@ -0,0 +1,246 @@
+/** @file mptable.c
+ *
+ * @brief Code to parse the MP table and determine information
+ * about the APIC.
+ *
+ * @author Ryan Pearl (rpearl)
+ *
+ * This code is directly based on FreeBSD's MP table parsing code. License and
+ * copyright information repeated below.
+ */
+
+/*-
+ * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
+ * Copyright (c) 1996, by Steve Passe
+ * 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. The name of the developer may NOT be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stddef.h>
+
+
+#include <multiboot.h>
+
+#include <smp/smp.h>
+#include <smp/mptable.h>
+#include <smp/apic.h>
+
+#include <compiler.h>
+
+static int num_cpus = 1; /**< The number of CPUs */
+static int bsp_id; /**< The cpu number for the BSP */
+static int bsp_apic; /**< The APIC id for the BSP */
+static void *lapic_base = NULL; /**< Location in physical memory of the Local
+ APIC base */
+static void *ioapic_addr = NULL; /**< Location in physical memory of the I/O
+ APIC base */
+
+extern int cpu2apic[MAX_CPUS];
+extern int apic2cpu[MAX_CPUS];
+
+/** @brief Reset state to uniprocessor
+ *
+ * @return unit
+ */
+static void mp_set_uniprocessor() {
+ num_cpus = 1;
+ ioapic_addr = NULL;
+ lapic_base = NULL;
+}
+
+/** @brief Fills in the book-keeping information for a CPU
+ *
+ * @param apic_id The local APIC ID for this CPU
+ * @param bsp Wether this CPU is the BSP
+ *
+ * @return unit
+ */
+static void mp_cpu_create(int apic_id, int bsp) {
+
+ if (num_cpus >= MAX_CPUS || apic_id >= MAX_CPUS)
+ return; // out of range; ignore it.
+
+ cpu2apic[num_cpus] = apic_id;
+ apic2cpu[apic_id] = num_cpus;
+ if (bsp) {
+ bsp_apic = apic_id;
+ bsp_id = num_cpus;
+ }
+ num_cpus++;
+}
+
+/** @brief Perform a checksum on an MP structure.
+ *
+ * @param addr The base of the structure
+ * @param len The length of the structure
+ * @return nonzero if sucessful, 0 otherwise.
+ */
+static int checksum(char *addr, int len) {
+ int i;
+ uint8_t sum = 0;
+ for (i = 0; i < len; i++) {
+ sum += addr[i];
+ }
+ return (sum == 0);
+}
+
+/**
+ * @brief Search for the MP floating pointer signature.
+ *
+ * @param addr The address to start searching at
+ * @param len The number of bytes to search.
+ *
+ * @return A pointer to the structure
+ */
+static struct mpfps *mp_scan_sig(uintptr_t addr, int len) {
+
+ char *ptr;
+ char *end = (char *)addr + len;
+
+ for (ptr = (char *)addr; ptr < end; ptr += sizeof(struct mpfps)) {
+ uint32_t sig = *(uint32_t *)ptr;
+ if (sig == MP_FPS_SIG && checksum(ptr, sizeof(struct mpfps)))
+ return (struct mpfps *)ptr;
+ }
+ return NULL;
+}
+
+/** @brief Walk the MP configuration table and gather data.
+ *
+ * @param cfg The configuration table to read.
+ * @return unit
+ */
+static void mptable_walk(struct mptable *cfg) {
+ num_cpus = 0; // mp_cpu_create() will count the BSP
+ STATIC_ASSERT(sizeof(struct mp_cpu) == 20);
+ STATIC_ASSERT(sizeof(struct mp_ioapic) == 8);
+ uint8_t *ptr = (uint8_t *)(cfg + 1);
+ int smp = 1;
+ uint16_t i;
+ for (i = 0; i < cfg->entries; i++) {
+ switch (*ptr) {
+ case MP_PROC: {
+ struct mp_cpu *cpu = (struct mp_cpu *)ptr;
+ if (cpu->flags & MP_PROC_EN) {
+ mp_cpu_create(cpu->apic_id, cpu->flags & MP_PROC_BSP);
+ }
+ ptr += sizeof(struct mp_cpu);
+ break;
+ }
+ case MP_IOAPIC: {
+ if (ioapic_addr != NULL) {
+ /* More complete code is needed to handle multiple I/O
+ * APICS. But, we don't even use the one we find. */
+ smp = 0;
+ } else {
+ struct mp_ioapic *ioapic = (struct mp_ioapic *)ptr;
+ ioapic_addr = ioapic->phys_addr;
+ }
+ ptr += sizeof(struct mp_ioapic);
+ break;
+ }
+ case MP_BUS:
+ case MP_IOINTR:
+ case MP_LINTR:
+ /* On a more complete system with actual devices, we need to
+ * care about these things.
+ */
+ ptr += 8;
+ break;
+ default:
+ panic("Unknown MP configuration entry: %x", *ptr);
+ }
+ }
+ if (!smp) {
+ mp_set_uniprocessor();
+ }
+}
+
+/** @brief Find the EBDA from the BIOS.
+ *
+ * @return The EBDA base.
+ */
+static inline uint32_t get_ebda(void) {
+ uint32_t ebda_seg = (uint32_t)*(uint16_t *)MP_BIOS_EBDA;
+ return ebda_seg << 4;
+}
+
+/** @brief Reads MP Table Configuration and determines whether the system has
+ * multiple processors.
+ *
+ * @param info The multiboot info provided by the boatloader
+ * @return zero if multiprocessor, negative error code if not.
+ */
+int smp_init(mbinfo_t *info) {
+ struct mpfps *fp;
+ struct mptable *cfg;
+ uint32_t ebda = get_ebda();
+ if (ebda) {
+ if ((fp = mp_scan_sig(ebda, 1024)) != NULL)
+ goto found;
+ }
+
+ if ((fp = mp_scan_sig(info->mem_lower * 1024, 1024)) != NULL)
+ goto found;
+
+ if ((fp = mp_scan_sig(MP_BIOS_START, MP_BIOS_LEN)) != NULL)
+ goto found;
+
+ return -1;
+
+ found:
+ if (fp->type != 0) {
+ // These are default configurations.
+ if (fp->type != 5 && fp->type != 6)
+ return -1; // Unsupported configuration (old hardware)
+
+ lapic_base = (void *)LAPIC_DEFAULT_BASE;
+ ioapic_addr = (void *)IOAPIC_DEFAULT_BASE;
+ mp_cpu_create(0, 1);
+ mp_cpu_create(1, 0);
+ } else {
+ cfg = (struct mptable *)fp->phys_addr;
+
+ if (!checksum((char *)cfg, cfg->length) || cfg->sig != MP_CFG_SIG)
+ return -1;
+
+ lapic_base = (void *)cfg->lapic_addr;
+ mptable_walk(cfg);
+ }
+
+ return 0;
+}
+
+/** @brief Returns the number of CPUs in the system.
+ */
+int smp_num_cpus(void) {
+ return num_cpus;
+}
+
+/** @brief Return the base of the local APIC in physical memory.
+ */
+void *smp_lapic_base(void) {
+ return lapic_base;
+}