summaryrefslogtreecommitdiffstats
path: root/usr.sbin/sysinstall/biosmptable.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/sysinstall/biosmptable.c')
-rw-r--r--usr.sbin/sysinstall/biosmptable.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/usr.sbin/sysinstall/biosmptable.c b/usr.sbin/sysinstall/biosmptable.c
new file mode 100644
index 0000000..d6ee944
--- /dev/null
+++ b/usr.sbin/sysinstall/biosmptable.c
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 2005 Sandvine Incorporated. All righs 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.
+ *
+ * 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.
+ *
+ * Author: Ed Maste <emaste@phaedrus.sandvine.ca>
+ */
+
+/*
+ * This module detects Intel Multiprocessor spec info (mptable) and returns
+ * the number of cpu's identified.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <machine/mptable.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sysinstall.h"
+
+#define MPFPS_SIG "_MP_"
+#define MPCTH_SIG "PCMP"
+
+#define PTOV(pa) ((off_t)(pa))
+
+static mpfps_t biosmptable_find_mpfps(void);
+static mpfps_t biosmptable_search_mpfps(off_t base, int length);
+static mpcth_t biosmptable_check_mpcth(off_t addr);
+
+static int memopen(void);
+static void memclose(void);
+
+int
+biosmptable_detect(void)
+{
+ mpfps_t mpfps;
+ mpcth_t mpcth;
+ char *entry_type_p;
+ proc_entry_ptr proc;
+ int ncpu, i;
+
+ if (!memopen())
+ return -1; /* XXX 0? */
+ /* locate and validate the mpfps */
+ mpfps = biosmptable_find_mpfps();
+ mpcth = NULL;
+ if (mpfps == NULL) {
+ ncpu = 0;
+ } else if (mpfps->config_type != 0) {
+ /*
+ * If thie config_type is nonzero then this is a default configuration
+ * from Chapter 5 in the MP spec. Report 2 cpus and 1 I/O APIC.
+ */
+ ncpu = 2;
+ } else {
+ ncpu = 0;
+ mpcth = biosmptable_check_mpcth(PTOV(mpfps->pap));
+ if (mpcth != NULL) {
+ entry_type_p = (char *)(mpcth + 1);
+ for (i = 0; i < mpcth->entry_count; i++) {
+ switch (*entry_type_p) {
+ case 0:
+ entry_type_p += sizeof(struct PROCENTRY);
+ proc = (proc_entry_ptr) entry_type_p;
+ msgDebug("MPTable: Found CPU APIC ID %d %s\n",
+ proc->apic_id,
+ proc->cpu_flags & PROCENTRY_FLAG_EN ?
+ "enabled" : "disabled");
+ if (proc->cpu_flags & PROCENTRY_FLAG_EN)
+ ncpu++;
+ break;
+ case 1:
+ entry_type_p += sizeof(struct BUSENTRY);
+ break;
+ case 2:
+ entry_type_p += sizeof(struct IOAPICENTRY);
+ break;
+ case 3:
+ case 4:
+ entry_type_p += sizeof(struct INTENTRY);
+ break;
+ default:
+ msgDebug("%s: unknown mptable entry type (%d)\n",
+ __func__, *entry_type_p);
+ goto done; /* XXX error return? */
+ }
+ }
+ done:
+ ;
+ }
+ }
+ memclose();
+ if (mpcth != NULL)
+ free(mpcth);
+ if (mpfps != NULL)
+ free(mpfps);
+
+ return ncpu;
+}
+
+static int pfd = -1;
+
+static int
+memopen(void)
+{
+ if (pfd < 0) {
+ pfd = open(_PATH_MEM, O_RDONLY);
+ if (pfd < 0)
+ warn("%s: cannot open", _PATH_MEM);
+ }
+ return pfd >= 0;
+}
+
+static void
+memclose(void)
+{
+ if (pfd >= 0) {
+ close(pfd);
+ pfd = -1;
+ }
+}
+
+static int
+memread(off_t addr, void* entry, size_t size)
+{
+ if ((size_t)pread(pfd, entry, size, addr) != size) {
+ warn("pread (%lu @ 0x%lx)", size, addr);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * Find the MP Floating Pointer Structure. See the MP spec section 4.1.
+ */
+static mpfps_t
+biosmptable_find_mpfps(void)
+{
+ mpfps_t mpfps;
+ uint16_t addr;
+
+ /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */
+ if (!memread(PTOV(0x40E), &addr, sizeof(addr)))
+ return (NULL);
+ mpfps = biosmptable_search_mpfps(PTOV(addr << 4), 0x400);
+ if (mpfps != NULL)
+ return (mpfps);
+
+ /* Check the BIOS. */
+ mpfps = biosmptable_search_mpfps(PTOV(0xf0000), 0x10000);
+ if (mpfps != NULL)
+ return (mpfps);
+
+ return (NULL);
+}
+
+static mpfps_t
+biosmptable_search_mpfps(off_t base, int length)
+{
+ mpfps_t mpfps;
+ u_int8_t *cp, sum;
+ int ofs, idx;
+
+ mpfps = malloc(sizeof(*mpfps));
+ if (mpfps == NULL) {
+ msgDebug("%s: unable to malloc space for "
+ "MP Floating Pointer Structure\n", __func__);
+ return (NULL);
+ }
+ /* search on 16-byte boundaries */
+ for (ofs = 0; ofs < length; ofs += 16) {
+ if (!memread(base + ofs, mpfps, sizeof(*mpfps)))
+ break;
+
+ /* compare signature, validate checksum */
+ if (!strncmp(mpfps->signature, MPFPS_SIG, strlen(MPFPS_SIG))) {
+ cp = (u_int8_t *)mpfps;
+ sum = 0;
+ /* mpfps is 16 bytes, or one "paragraph" */
+ if (mpfps->length != 1) {
+ msgDebug("%s: bad mpfps length (%d)\n",
+ __func__, mpfps->length);
+ continue;
+ }
+ for (idx = 0; idx < mpfps->length * 16; idx++)
+ sum += *(cp + idx);
+ if (sum != 0) {
+ msgDebug("%s: bad mpfps checksum (%d)\n", __func__, sum);
+ continue;
+ }
+ return (mpfps);
+ }
+ }
+ free(mpfps);
+ return (NULL);
+}
+
+static mpcth_t
+biosmptable_check_mpcth(off_t addr)
+{
+ mpcth_t mpcth;
+ u_int8_t *cp, sum;
+ int idx, table_length;
+
+ /* mpcth must be in the first 1MB */
+ if ((u_int32_t)addr >= 1024 * 1024) {
+ msgDebug("%s: bad mpcth address (0x%lx)\n", __func__, addr);
+ return (NULL);
+ }
+
+ mpcth = malloc(sizeof(*mpcth));
+ if (mpcth == NULL) {
+ msgDebug("%s: unable to malloc space for "
+ "MP Configuration Table Header\n", __func__);
+ return (NULL);
+ }
+ if (!memread(addr, mpcth, sizeof(*mpcth)))
+ goto bad;
+ /* Compare signature and validate checksum. */
+ if (strncmp(mpcth->signature, MPCTH_SIG, strlen(MPCTH_SIG)) != 0) {
+ msgDebug("%s: bad mpcth signature\n", __func__);
+ goto bad;
+ }
+ table_length = mpcth->base_table_length;
+ mpcth = realloc(mpcth, table_length);
+ if (mpcth == NULL) {
+ msgDebug("%s: unable to realloc space for mpcth (len %u)\n",
+ __func__, table_length);
+ return (NULL);
+ }
+ if (!memread(addr, mpcth, table_length))
+ goto bad;
+ cp = (u_int8_t *)mpcth;
+ sum = 0;
+ for (idx = 0; idx < mpcth->base_table_length; idx++)
+ sum += *(cp + idx);
+ if (sum != 0) {
+ msgDebug("%s: bad mpcth checksum (%d)\n", __func__, sum);
+ goto bad;
+ }
+
+ return mpcth;
+bad:
+ free(mpcth);
+ return (NULL);
+}
OpenPOWER on IntegriCloud