summaryrefslogtreecommitdiff
path: root/usr/src/psm/stand/boot/amd64/cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/psm/stand/boot/amd64/cpu.c')
-rw-r--r--usr/src/psm/stand/boot/amd64/cpu.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/usr/src/psm/stand/boot/amd64/cpu.c b/usr/src/psm/stand/boot/amd64/cpu.c
new file mode 100644
index 0000000000..61cbc19942
--- /dev/null
+++ b/usr/src/psm/stand/boot/amd64/cpu.c
@@ -0,0 +1,307 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/psw.h>
+
+#include <amd64/print.h>
+#include <amd64/debug.h>
+#include <amd64/cpu.h>
+#include <amd64/amd64.h>
+#include <amd64/msr.h>
+#include "../i386/common/biosint.h"
+
+#ifdef DEBUG
+static void
+amd64_dump_cpuid(uint32_t eaxmin, uint32_t eaxmax)
+{
+ uint32_t eax;
+ struct amd64_cpuid_regs __vcr, *vcr = &__vcr;
+
+ printf("\t%8s | %8s %8s %8s %8s\n",
+ "eax in", "eax", "ebx", "ecx", "edx");
+ for (eax = eaxmin; eax <= eaxmax; eax++) {
+ amd64_cpuid_insn(eax, vcr);
+ printf("\t%8x | %8x %8x %8x %8x\n", eax,
+ vcr->r_eax, vcr->r_ebx, vcr->r_ecx, vcr->r_edx);
+ }
+}
+
+#define cmprintf printf
+
+#else /* !DEBUG */
+
+#ifdef lint
+#define cmprintf printf
+#else
+#define cmprintf
+#endif /* lint */
+
+#endif /* DEBUG */
+
+static int detect_target_operating_mode();
+
+int is_amd64;
+
+/*ARGSUSED*/
+int
+amd64_config_cpu(void)
+{
+ struct amd64_cpuid_regs __vcr, *vcr = &__vcr;
+ uint32_t maxeax;
+ uint32_t max_maxeax = 0x100;
+ char vendor[13];
+ int isamd64 = 0;
+ uint32_t stdfeatures = 0, xtdfeatures = 0;
+ uint64_t efer;
+
+ /*
+ * This check may seem silly, but if the C preprocesor symbol __amd64
+ * is #defined during compilation, something that may outwardly seem
+ * like a good idea, uts/common/sys/isa_defs.h will #define _LP64,
+ * which will cause uts/common/sys/int_types.h to typedef uint64_t as
+ * an unsigned long - which is only 4 bytes in size when using a 32-bit
+ * compiler.
+ *
+ * If that happens, all the page table translation routines will fail
+ * horribly, so check the size of uint64_t just to insure some degree
+ * of sanity in future operations.
+ */
+ /*LINTED [sizeof result is invarient]*/
+ if (sizeof (uint64_t) != 8)
+ prom_panic("multiboot compiled improperly, unable to boot "
+ "64-bit AMD64 executables");
+
+ /*
+ * If the CPU doesn't support the CPUID instruction, it's definitely
+ * not an AMD64.
+ */
+ if (amd64_cpuid_supported() == 0)
+ return (0);
+
+ amd64_cpuid_insn(0, vcr);
+
+ maxeax = vcr->r_eax;
+ {
+ /*LINTED [vendor string from cpuid data]*/
+ uint32_t *iptr = (uint32_t *)vendor;
+
+ *iptr++ = vcr->r_ebx;
+ *iptr++ = vcr->r_edx;
+ *iptr++ = vcr->r_ecx;
+
+ vendor[12] = '\0';
+ }
+
+ if (maxeax > max_maxeax) {
+ cmprintf("cpu: warning, maxeax was 0x%x -> 0x%x\n",
+ maxeax, max_maxeax);
+ maxeax = max_maxeax;
+ }
+
+ if (maxeax < 1)
+ return (0); /* no additional functions, not an AMD64 */
+ else {
+ uint_t family, model, step;
+
+ amd64_cpuid_insn(1, vcr);
+
+ /*
+ * All AMD64/IA32e processors technically SHOULD report
+ * themselves as being in family 0xf, but for some reason
+ * Simics doesn't, and this may change in the future, so
+ * don't error out if it's not true.
+ */
+ if ((family = BITX(vcr->r_eax, 11, 8)) == 0xf)
+ family += BITX(vcr->r_eax, 27, 20);
+
+ if ((model = BITX(vcr->r_eax, 7, 4)) == 0xf)
+ model += BITX(vcr->r_eax, 19, 16) << 4;
+ step = BITX(vcr->r_eax, 3, 0);
+
+ cmprintf("cpu: '%s' family %d model %d step %d\n",
+ vendor, family, model, step);
+ stdfeatures = vcr->r_edx;
+ }
+
+#ifdef DEBUG
+ if (amd64_debug) {
+ cmprintf("cpu: standard cpuid data:\n");
+ amd64_dump_cpuid(0, maxeax);
+ }
+#endif /* DEBUG */
+
+ amd64_cpuid_insn(0x80000000, vcr);
+
+ if (vcr->r_eax & 0x80000000) {
+ uint32_t xmaxeax = vcr->r_eax;
+ const uint32_t max_xmaxeax = 0x80000100;
+
+ if (xmaxeax > max_xmaxeax) {
+ cmprintf("amd64: warning, xmaxeax was 0x%x -> 0x%x\n",
+ xmaxeax, max_xmaxeax);
+ xmaxeax = max_xmaxeax;
+ }
+
+#ifdef DEBUG
+ if (amd64_debug) {
+ cmprintf("amd64: extended cpuid data:\n");
+ amd64_dump_cpuid(0x80000000, xmaxeax);
+ }
+#endif /* DEBUG */
+
+ if (xmaxeax >= 0x80000001) {
+ amd64_cpuid_insn(0x80000001, vcr);
+ xtdfeatures = vcr->r_edx;
+ }
+ }
+
+ if (BITX(xtdfeatures, 29, 29)) /* long mode */
+ isamd64++;
+ else
+ cmprintf("amd64: CPU does NOT support long mode\n");
+
+ if (!BITX(stdfeatures, 0, 0)) {
+ cmprintf("amd64: CPU does NOT support FPU\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 3, 3)) {
+ cmprintf("amd64: CPU does NOT support PSE\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 4, 4)) {
+ cmprintf("amd64: CPU does NOT support TSC\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 5, 5)) {
+ cmprintf("amd64: CPU does NOT support MSRs\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 6, 6)) {
+ cmprintf("amd64: CPU does NOT support PAE\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 8, 8)) {
+ cmprintf("amd64: CPU does NOT support CX8\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 13, 13)) {
+ cmprintf("amd64: CPU does NOT support PGE\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 17, 17)) {
+ cmprintf("amd64: CPU does NOT support PSE\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 19, 19)) {
+ cmprintf("amd64: CPU does NOT support CLFSH\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 23, 23)) {
+ cmprintf("amd64: CPU does NOT support MMX\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 24, 24)) {
+ cmprintf("amd64: CPU does NOT support FXSR\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 25, 25)) {
+ cmprintf("amd64: CPU does NOT support SSE\n");
+ isamd64--;
+ }
+
+ if (!BITX(stdfeatures, 26, 26)) {
+ cmprintf("amd64: CPU does NOT support SSE2\n");
+ isamd64--;
+ }
+
+ if (isamd64 < 1) {
+ cmprintf("amd64: CPU does not support amd64 executables.\n");
+ return (0);
+ }
+
+ amd64_rdmsr(MSR_AMD_EFER, &efer);
+ if (efer & AMD_EFER_SCE)
+ cmprintf("amd64: EFER_SCE (syscall/sysret) already enabled\n");
+ if (efer & AMD_EFER_NXE)
+ cmprintf("amd64: EFER_NXE (no-exec prot) already enabled\n");
+ if (efer & AMD_EFER_LME)
+ cmprintf("amd64: EFER_LME (long mode) already enabled\n");
+
+ return (detect_target_operating_mode());
+}
+
+/*
+ * Issue 'Detect Target Operating Mode' callback to the BIOS
+ */
+static int
+detect_target_operating_mode()
+{
+ struct int_pb ic = {0};
+ int ret, ah;
+
+ ic.ax = 0xec00; /* Detect Target Operating Mode */
+ ic.bx = 0x03; /* mixed mode target */
+
+ ret = bios_doint(0x15, &ic);
+
+ ah = ic.ax >> 8;
+ if (ah == 0x86 && (ret & PS_C) != 0) {
+ dprintf("[BIOS 'Detect Target Operating Mode' "
+ "callback unsupported on this platform]\n");
+ return (1); /* unsupported, ignore */
+ }
+
+ if (ah == 0x0 && (ret & PS_C) == 0) {
+ dprintf("[BIOS accepted mixed-mode target setting!]\n");
+ return (1); /* told the bios what we're up to */
+ }
+
+ if (ah == 0 && ret & PS_C) {
+ printf("fatal: BIOS reports this machine CANNOT run in mixed "
+ "32/64-bit mode!\n");
+ return (0);
+ }
+
+ dprintf("warning: BIOS Detect Target Operating Mode callback "
+ "confused.\n %%ax = 0x%x, carry = %d\n", ic.ax,
+ ret & PS_C ? 1 : 0);
+
+ return (1);
+}