summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2019-02-04 16:55:24 +0000
committerRobert Mustacchi <rm@joyent.com>2019-07-12 21:39:06 +0000
commit2cfe9601bab4521ae793cdb6e4e3496ba4ed55f2 (patch)
tree274e0935c500ca106799233491424a8d30ec3400
parentb28b9a96d2d0a92b8948678113495203d0607781 (diff)
downloadillumos-joyent-2cfe9601bab4521ae793cdb6e4e3496ba4ed55f2.tar.gz
OS-6500 Want modern Intel IMC driver
OS-6629 x86 PCI enumeration should not rely on bios max bus Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Rob Johnston <rob.johnston@joyent.com> Approved by: Jerry Jelinek <jerry.jelinek@joyent.com>
-rw-r--r--manifest6
-rw-r--r--usr/src/cmd/fm/Makefile6
-rw-r--r--usr/src/cmd/fm/mcdecode/Makefile59
-rw-r--r--usr/src/cmd/fm/mcdecode/mcdecode.c284
-rw-r--r--usr/src/common/mc/imc/imc_decode.c770
-rw-r--r--usr/src/common/mc/imc/imc_dump.c569
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c335
-rw-r--r--usr/src/man/man7d/Makefile2
-rw-r--r--usr/src/man/man7d/imc.7d103
-rw-r--r--usr/src/man/man7d/imcstub.7d46
-rw-r--r--usr/src/test/os-tests/runfiles/default.run3
-rw-r--r--usr/src/test/os-tests/tests/Makefile2
-rw-r--r--usr/src/test/os-tests/tests/imc/Makefile72
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test.c353
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test.h68
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test_badaddr.c230
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test_basic.c132
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test_fail.c1494
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test_rir.c856
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test_sad.c3212
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test_skx_loop.c175
-rw-r--r--usr/src/test/os-tests/tests/imc/imc_test_tad.c5228
-rw-r--r--usr/src/uts/i86pc/Makefile.files7
-rw-r--r--usr/src/uts/i86pc/Makefile.i86pc1
-rw-r--r--usr/src/uts/i86pc/Makefile.rules10
-rw-r--r--usr/src/uts/i86pc/imc/Makefile51
-rw-r--r--usr/src/uts/i86pc/imcstub/Makefile49
-rw-r--r--usr/src/uts/i86pc/io/imc/imc.c2972
-rw-r--r--usr/src/uts/i86pc/io/imc/imc.conf16
-rw-r--r--usr/src/uts/i86pc/io/imc/imc.h940
-rw-r--r--usr/src/uts/i86pc/io/imc/imcstub.c81
-rw-r--r--usr/src/uts/i86pc/os/pci_cfgspace.c10
-rw-r--r--usr/src/uts/intel/sys/cpu_module.h5
-rw-r--r--usr/src/uts/intel/sys/mc.h27
-rw-r--r--usr/src/uts/intel/sys/mc_intel.h58
35 files changed, 18222 insertions, 10 deletions
diff --git a/manifest b/manifest
index 7d3a01696b..cf5ce66068 100644
--- a/manifest
+++ b/manifest
@@ -1810,6 +1810,8 @@ f platform/i86pc/kernel/drv/amd64/cpudrv 0755 root sys
f platform/i86pc/kernel/drv/amd64/dr 0755 root sys
f platform/i86pc/kernel/drv/amd64/fipe 0755 root sys
f platform/i86pc/kernel/drv/amd64/ioat 0755 root sys
+f platform/i86pc/kernel/drv/amd64/imc 0755 root sys
+f platform/i86pc/kernel/drv/amd64/imcstub 0755 root sys
f platform/i86pc/kernel/drv/amd64/isa 0755 root sys
f platform/i86pc/kernel/drv/amd64/npe 0755 root sys
f platform/i86pc/kernel/drv/amd64/pci 0755 root sys
@@ -1823,6 +1825,7 @@ f platform/i86pc/kernel/drv/cpc.conf 0644 root sys
f platform/i86pc/kernel/drv/dr.conf 0644 root sys
f platform/i86pc/kernel/drv/fipe.conf 0644 root sys
f platform/i86pc/kernel/drv/ioat.conf 0644 root sys
+f platform/i86pc/kernel/drv/imc.conf 0644 root sys
f platform/i86pc/kernel/drv/pci-ide.conf 0644 root sys
f platform/i86pc/kernel/drv/pit_beep.conf 0644 root sys
f platform/i86pc/kernel/drv/ppm.conf 0644 root sys
@@ -5588,6 +5591,7 @@ f usr/lib/fm/fmd/fmd 0555 root bin
f usr/lib/fm/fmd/fminject 0555 root bin
f usr/lib/fm/fmd/fmsim 0555 root bin
f usr/lib/fm/fmd/fmtopo 0555 root bin
+f usr/lib/fm/fmd/mcdecode 0555 root bin
f usr/lib/fm/fmd/ipmitopo 0555 root bin
d usr/lib/fm/fmd/plugins 0755 root bin
f usr/lib/fm/fmd/plugins/cpumem-retire.conf 0644 root bin
@@ -19113,6 +19117,8 @@ f usr/share/man/man7d/hxge.7d 0444 root bin
f usr/share/man/man7d/i40e.7d 0444 root bin
f usr/share/man/man7d/ieee1394.7d 0444 root bin
f usr/share/man/man7d/igb.7d 0444 root bin
+f usr/share/man/man7d/imc.7d 0444 root bin
+f usr/share/man/man7d/imcstub.7d 0444 root bin
f usr/share/man/man7d/ipmi.7d 0444 root bin
f usr/share/man/man7d/ipnet.7d 0444 root bin
f usr/share/man/man7d/iprb.7d 0444 root bin
diff --git a/usr/src/cmd/fm/Makefile b/usr/src/cmd/fm/Makefile
index 4243767346..7b5d28d307 100644
--- a/usr/src/cmd/fm/Makefile
+++ b/usr/src/cmd/fm/Makefile
@@ -21,8 +21,11 @@
#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
+i386_SUBDIRS = mcdecode
+
SUBDIRS = \
fmd \
fmadm \
@@ -36,7 +39,8 @@ SUBDIRS = \
modules \
dicts \
eversholt \
- notify
+ notify \
+ $($(MACH)_SUBDIRS)
include ./Makefile.subdirs
diff --git a/usr/src/cmd/fm/mcdecode/Makefile b/usr/src/cmd/fm/mcdecode/Makefile
new file mode 100644
index 0000000000..6cfbde5556
--- /dev/null
+++ b/usr/src/cmd/fm/mcdecode/Makefile
@@ -0,0 +1,59 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include ../../Makefile.cmd
+include ../../Makefile.ctf
+
+SRCS += mcdecode.c imc_decode.o imc_dump.o
+OBJS = $(SRCS:%.c=%.o)
+
+PROG = mcdecode
+
+ROOTLIBFM = $(ROOT)/usr/lib/fm
+ROOTLIBFMD = $(ROOT)/usr/lib/fm/fmd
+ROOTPROG = $(ROOTLIBFMD)/$(PROG)
+
+$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
+CPPFLAGS += -I$(SRC)/uts/i86pc/io/imc
+LDLIBS += -lnvpair
+
+CSTD = $(CSTD_GNU99)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+%.o: $(SRC)/common/mc/imc/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+clobber: clean
+ $(RM) $(PROG)
+
+$(ROOTLIBFMD)/%: %
+ $(INS.file)
+
+install_h:
+
+install: all $(ROOTPROG)
diff --git a/usr/src/cmd/fm/mcdecode/mcdecode.c b/usr/src/cmd/fm/mcdecode/mcdecode.c
new file mode 100644
index 0000000000..e6b8b62ce7
--- /dev/null
+++ b/usr/src/cmd/fm/mcdecode/mcdecode.c
@@ -0,0 +1,284 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * Command utility to drive synthetic memory decoding.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <libnvpair.h>
+
+#include <sys/mc.h>
+#include "imc.h"
+
+#define MCDECODE_USAGE 2
+
+/*
+ * Write in 32k chunks.
+ */
+#define MCDECODE_WRITE (1024 * 32)
+
+static void
+mcdecode_usage(void)
+{
+ (void) fprintf(stderr,
+ "Usage: mcdecode [-f infile] [-d address | -w outfile] device\n"
+ "\n"
+ "\t-d decode physical address to the correspond dimm\n"
+ "\t-f use decoder image from infile\n"
+ "\t-w write decoder snapshot state to the specified file\n");
+ exit(MCDECODE_USAGE);
+}
+
+static void
+mcdecode_from_file(const char *file, uint64_t pa)
+{
+ int fd, ret;
+ struct stat st;
+ void *addr;
+ nvlist_t *nvl;
+ imc_t imc;
+ imc_decode_state_t dec;
+ char *driver;
+
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", file);
+ }
+
+ if (fstat(fd, &st) != 0) {
+ err(EXIT_FAILURE, "failed to get file information for %s",
+ file);
+ }
+
+ addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ fd, 0);
+ if (addr == MAP_FAILED) {
+ err(EXIT_FAILURE, "failed to map %s", file);
+ }
+ ret = nvlist_unpack(addr, st.st_size, &nvl, 0);
+ if (ret != 0) {
+ errx(EXIT_FAILURE, "failed to unpack %s: %s",
+ strerror(ret));
+ }
+ if (munmap(addr, st.st_size) != 0) {
+ err(EXIT_FAILURE, "failed to unmap %s", file);
+ }
+ if (close(fd) != 0) {
+ err(EXIT_FAILURE, "failed to close fd for %s", file);
+ }
+
+ if (nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0) {
+ errx(EXIT_FAILURE, "missing driver indication in dump %s",
+ file);
+ }
+
+ if (strcmp(driver, "imc") != 0) {
+ errx(EXIT_FAILURE, "unknown driver dump source %s\n", driver);
+ }
+
+ if (!imc_restore_decoder(nvl, &imc)) {
+ errx(EXIT_FAILURE, "failed to restore memory controller "
+ "snapshot in %s", file);
+ }
+
+ bzero(&dec, sizeof (dec));
+
+ if (!imc_decode_pa(&imc, pa, &dec)) {
+ errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64, pa);
+ }
+
+ (void) printf("Decoded physical address 0x%" PRIx64 "\n"
+ "\tchip:\t\t\t%u\n"
+ "\tmemory controller:\t%u\n"
+ "\tchannel:\t\t%u\n"
+ "\tdimm:\t\t\t%u\n"
+ "\trank:\t\t\t%u\n",
+ pa, dec.ids_nodeid, dec.ids_tadid, dec.ids_channelid,
+ dec.ids_dimmid, dec.ids_rankid);
+
+ nvlist_free(nvl);
+}
+
+static void
+mcdecode_pa(const char *device, uint64_t pa)
+{
+ int fd;
+ mc_encode_ioc_t ioc;
+
+ bzero(&ioc, sizeof (ioc));
+ ioc.mcei_pa = pa;
+
+ if ((fd = open(device, O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", device);
+ }
+
+ if (ioctl(fd, MC_IOC_DECODE_PA, &ioc) != 0) {
+ err(EXIT_FAILURE, "failed to issue decode ioctl");
+ }
+
+ if (ioc.mcei_err != 0) {
+ (void) fprintf(stderr, "decoding of address 0x%" PRIx64
+ " failed with error 0x%x\n", pa, ioc.mcei_err);
+ exit(EXIT_FAILURE);
+ }
+
+ (void) printf("Decoded physical address 0x%" PRIx64 "\n"
+ "\tchip:\t\t\t%u\n"
+ "\tmemory controller:\t%u\n"
+ "\tchannel:\t\t%u\n"
+ "\tdimm:\t\t\t%u\n"
+ "\trank:\t\t\t%u\n",
+ pa, ioc.mcei_chip, ioc.mcei_mc, ioc.mcei_chan, ioc.mcei_dimm,
+ ioc.mcei_rank);
+
+ (void) close(fd);
+}
+
+static void
+mcdecode_dump(const char *device, const char *outfile)
+{
+ int fd;
+ mc_snapshot_info_t mcs;
+ char *buf;
+
+ if ((fd = open(device, O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", device);
+ }
+
+ bzero(&mcs, sizeof (mcs));
+ if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT_INFO, &mcs) != 0) {
+ err(EXIT_FAILURE, "failed to get decode snapshot information");
+ }
+
+ if ((buf = malloc(mcs.mcs_size)) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate %u bytes for the "
+ "dump snapshot", mcs.mcs_size);
+ }
+
+ if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT, buf) != 0) {
+ err(EXIT_FAILURE, "failed to retrieve decode snapshot");
+ }
+ (void) close(fd);
+
+ if ((fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
+ err(EXIT_FAILURE, "failed to create output file %s", outfile);
+ }
+
+ while (mcs.mcs_size > 0) {
+ ssize_t ret;
+ size_t out = mcs.mcs_size > MCDECODE_WRITE ? MCDECODE_WRITE :
+ mcs.mcs_size;
+
+ ret = write(fd, buf, out);
+ if (ret < 0) {
+ warn("failed to write to output file %s", outfile);
+ (void) unlink(outfile);
+ exit(EXIT_FAILURE);
+ }
+
+ buf += ret;
+ mcs.mcs_size -= ret;
+ }
+
+ if (fsync(fd) != 0) {
+ warn("failed to sync output file %s", outfile);
+ (void) unlink(outfile);
+ exit(EXIT_FAILURE);
+ }
+
+ (void) close(fd);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ uint64_t pa = UINT64_MAX;
+ const char *outfile = NULL;
+ const char *infile = NULL;
+
+ while ((c = getopt(argc, argv, "d:f:w:")) != -1) {
+ char *eptr;
+ unsigned long long tmp;
+
+ switch (c) {
+ case 'd':
+ errno = 0;
+ tmp = strtoull(optarg, &eptr, 0);
+ if (errno != 0 || *eptr != '\0') {
+ errx(EXIT_FAILURE, "failed to parse address "
+ "'%s'", eptr);
+ }
+ pa = (uint64_t)tmp;
+ break;
+ case 'f':
+ infile = optarg;
+ break;
+ case 'w':
+ outfile = optarg;
+ break;
+ case ':':
+ warnx("Option -%c requires an operand", optopt);
+ mcdecode_usage();
+ break;
+ case '?':
+ warnx("Unknown option: -%c", optopt);
+ mcdecode_usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (outfile != NULL && infile != NULL) {
+ errx(EXIT_FAILURE, "-f and -w cannot be used together");
+ }
+
+ if (pa != UINT64_MAX && outfile != NULL) {
+ errx(EXIT_FAILURE, "-w and -d cannot be used together");
+ }
+
+ if (pa == UINT64_MAX && outfile == NULL) {
+ warnx("missing either -d or -w\n");
+ mcdecode_usage();
+
+ }
+
+ if (argc != 1 && infile == NULL) {
+ errx(EXIT_FAILURE, "missing device argument");
+ }
+
+
+ if (pa != UINT64_MAX) {
+ if (infile != NULL) {
+ mcdecode_from_file(infile, pa);
+ } else {
+ mcdecode_pa(argv[0], pa);
+ }
+ } else {
+ mcdecode_dump(argv[0], outfile);
+ }
+ return (0);
+}
diff --git a/usr/src/common/mc/imc/imc_decode.c b/usr/src/common/mc/imc/imc_decode.c
new file mode 100644
index 0000000000..7e52e9795e
--- /dev/null
+++ b/usr/src/common/mc/imc/imc_decode.c
@@ -0,0 +1,770 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * Memory decoding logic.
+ *
+ * This file is part of the 'imc' driver on x86. It supports taking a physical
+ * address and determining what the corresponding DIMM is. This is shared
+ * between the kernel and userland for easier testing.
+ *
+ * For more information about the different parts of the decoding process,
+ * please see the file 'uts/i86pc/io/imc/imc.c'.
+ */
+
+#include <sys/sysmacros.h>
+
+#ifndef _KERNEL
+#include <stdint.h>
+#include <strings.h>
+#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
+#endif /* !_KERNEL */
+
+#include "imc.h"
+
+/*
+ * Address ranges for decoding system addresses. There are three ranges that
+ * exist on x86, traditional DOS memory (hi 640 KiB), low memory, and high
+ * memory. Low memory always starts at 1 MiB and high memory always starts at 4
+ * GiB. The upper bounds of these ranges is based on registers on the system.
+ */
+#define IMC_DECODE_CONV_BASE 0UL
+#define IMC_DECODE_CONV_MAX 0x00009ffffULL /* 640 KiB - 1 */
+#define IMC_DECODE_LOW_BASE 0x000100000ULL /* 1 M */
+#define IMC_DECODE_HIGH_BASE 0x100000000ULL /* 4 GiB */
+
+typedef struct imc_legacy_range {
+ uint64_t ilr_base;
+ size_t ilr_len;
+ const char *ilr_desc;
+} imc_legacy_range_t;
+
+/*
+ * These represent regions of memory that are reserved for use and will not be
+ * decoded by DRAM.
+ */
+static imc_legacy_range_t imc_legacy_ranges[] = {
+ { 0x00000A0000ULL, 128 * 1024, "VGA" },
+ { 0x00000C0000ULL, 256 * 1024, "PAM" },
+ { 0x0000F00000ULL, 1024 * 1024, "Reserved" },
+ { 0x00FE000000ULL, 32 * 1024 * 1024, "Unknown" },
+ { 0x00FF000000ULL, 16 * 1024 * 1024, "Firmware" },
+ { 0x00FED20000ULL, 384 * 1024, "TXT" },
+ { 0x00FED00000ULL, 1024 * 1024, "PCH" },
+ { 0x00FEC00000ULL, 1024 * 1024, "IOAPIC" },
+ { 0x00FEB80000ULL, 512 * 1024, "Reserved" },
+ { 0x00FEB00000ULL, 64 * 1024, "Reserved" }
+};
+
+/*
+ * Determine whether or not this address is in one of the reserved regions or if
+ * it falls outside of the explicit DRAM ranges.
+ */
+static boolean_t
+imc_decode_addr_resvd(const imc_t *imc, imc_decode_state_t *dec)
+{
+ uint_t i;
+ const imc_sad_t *sad;
+
+ for (i = 0; i < ARRAY_SIZE(imc_legacy_ranges); i++) {
+ uint64_t end = imc_legacy_ranges[i].ilr_base +
+ imc_legacy_ranges[i].ilr_len;
+
+ if (dec->ids_pa >= imc_legacy_ranges[i].ilr_base &&
+ dec->ids_pa < end) {
+ dec->ids_fail = IMC_DECODE_F_LEGACY_RANGE;
+ dec->ids_fail_data = i;
+ return (B_TRUE);
+ }
+ }
+
+ /*
+ * For checking and determining whether or not we fit in DRAM, we need
+ * to check against the top of low memory and the top of high memory.
+ * While we technically have this information on a per-socket basis, we
+ * have to rely on the fact that both processors have the same
+ * information. A requirement which if not true, would lead to chaos
+ * depending on what socket we're running on.
+ */
+ sad = &imc->imc_sockets[0].isock_sad;
+ if (sad->isad_valid != IMC_SAD_V_VALID) {
+ dec->ids_fail = IMC_DECODE_F_BAD_SAD;
+ return (B_TRUE);
+ }
+
+ /*
+ * An address may fall into three ranges. It may fall into conventional
+ * memory. It may fall into low memory. It may fall into high memory.
+ * The conventional memory range is inclusive at the top. The others
+ * have been translated such that they are uniformly exclusive at the
+ * top. Because the bottom of conventional memory is at zero, the
+ * compiler will be angry if we compare against IMC_DECODE_CONV_BASE as
+ * it is always true.
+ */
+ if (dec->ids_pa <= IMC_DECODE_CONV_MAX) {
+ return (B_FALSE);
+ }
+
+ if (dec->ids_pa >= IMC_DECODE_LOW_BASE &&
+ dec->ids_pa < sad->isad_tolm) {
+ return (B_FALSE);
+ }
+
+ if (dec->ids_pa >= IMC_DECODE_HIGH_BASE &&
+ dec->ids_pa < sad->isad_tohm) {
+ return (B_FALSE);
+ }
+
+ /*
+ * Memory fell outside of the valid range. It's not for us.
+ */
+ dec->ids_fail = IMC_DECODE_F_OUTSIDE_DRAM;
+ return (B_TRUE);
+}
+
+static uint_t
+imc_decode_sad_interleave(const imc_sad_rule_t *rule, uint64_t pa)
+{
+ uint_t itgt = 0;
+
+ switch (rule->isr_imode) {
+ case IMC_SAD_IMODE_8t6:
+ if (rule->isr_a7mode) {
+ itgt = BITX(pa, 9, 9);
+ itgt |= (BITX(pa, 8, 7) << 1);
+ } else {
+ itgt = BITX(pa, 8, 6);
+ }
+ break;
+ case IMC_SAD_IMODE_8t6XOR:
+ if (rule->isr_a7mode) {
+ itgt = BITX(pa, 9, 9);
+ itgt |= (BITX(pa, 8, 7) << 1);
+ } else {
+ itgt = BITX(pa, 8, 6);
+ }
+ itgt ^= BITX(pa, 18, 16);
+ break;
+ case IMC_SAD_IMODE_10t8:
+ itgt = BITX(pa, 10, 8);
+ break;
+ case IMC_SAD_IMODE_14t12:
+ itgt = BITX(pa, 14, 12);
+ break;
+ case IMC_SAD_IMODE_32t30:
+ itgt = BITX(pa, 32, 30);
+ break;
+ }
+
+ return (itgt);
+}
+
+/*
+ * Use the system address decoder to try and find a valid SAD entry for this
+ * address. We always use socket zero's SAD as the SAD rules should be the same
+ * between the different sockets.
+ */
+static boolean_t
+imc_decode_sad(const imc_t *imc, imc_decode_state_t *dec)
+{
+ uint_t i, ileaveidx;
+ uint8_t ileavetgt;
+ uint32_t nodeid, tadid, channelid;
+ uint64_t base;
+ const imc_socket_t *socket = &imc->imc_sockets[0];
+ const imc_sad_t *sad = &socket->isock_sad;
+ const imc_sad_rule_t *rule;
+ boolean_t loop = B_FALSE;
+
+ /*
+ * Note, all SAD rules have been adjusted so that they are uniformly
+ * exclusive.
+ */
+start:
+ for (rule = NULL, i = 0, base = 0; i < sad->isad_nrules; i++) {
+ rule = &sad->isad_rules[i];
+
+ if (rule->isr_enable && dec->ids_pa >= base &&
+ dec->ids_pa < rule->isr_limit) {
+ break;
+ }
+
+ base = rule->isr_limit;
+ }
+
+ if (rule == NULL || i == sad->isad_nrules) {
+ dec->ids_fail = IMC_DECODE_F_NO_SAD_RULE;
+ return (B_FALSE);
+ }
+
+ /*
+ * Store the SAD rule in the decode information for debugging's sake.
+ */
+ dec->ids_sad = sad;
+ dec->ids_sad_rule = rule;
+
+ /*
+ * We have found a SAD rule. We now need to transform that into the
+ * corresponding target based on its mode, etc. The way we do this
+ * varies based on the generation.
+ *
+ * The first thing we need to do is to figure out the target in the
+ * interleave list.
+ */
+ ileaveidx = imc_decode_sad_interleave(rule, dec->ids_pa);
+ if (ileaveidx >= rule->isr_ntargets) {
+ dec->ids_fail = IMC_DECODE_F_BAD_SAD_INTERLEAVE;
+ dec->ids_fail_data = ileaveidx;
+ return (B_FALSE);
+ }
+ ileavetgt = rule->isr_targets[ileaveidx];
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE &&
+ IMC_SAD_ILEAVE_SKX_LOCAL(ileavetgt) == 0) {
+ /*
+ * If we're in this case, the interleave rule said we had a
+ * remote target. That means we need to find the correct SAD
+ * based on the Node ID and then do all of this over again.
+ */
+ nodeid = IMC_SAD_ILEAVE_SKX_TARGET(ileavetgt);
+
+ if (loop) {
+ dec->ids_fail = IMC_DECODE_F_SAD_SEARCH_LOOP;
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ if (imc->imc_sockets[i].isock_valid ==
+ IMC_SOCKET_V_VALID &&
+ imc->imc_sockets[i].isock_nodeid == nodeid) {
+ socket = &imc->imc_sockets[i];
+ sad = &imc->imc_sockets[i].isock_sad;
+ loop = B_TRUE;
+ goto start;
+ }
+ }
+
+ dec->ids_fail = IMC_DECODE_F_BAD_REMOTE_MC_ROUTE;
+ dec->ids_fail_data = nodeid;
+ return (B_FALSE);
+ }
+
+ /*
+ * On some platforms we need to derive the target channel based on the
+ * physical address and additional rules in the SAD. If we do, do that
+ * here. The idea is that this may overrule the memory channel route
+ * table target that was determined from the SAD rule.
+ */
+ if (rule->isr_need_mod3) {
+ uint64_t addr;
+ uint8_t channel;
+
+ switch (rule->isr_mod_mode) {
+ case IMC_SAD_MOD_MODE_45t6:
+ addr = dec->ids_pa >> 6;
+ break;
+ case IMC_SAD_MOD_MODE_45t8:
+ addr = dec->ids_pa >> 8;
+ break;
+ case IMC_SAD_MOD_MODE_45t12:
+ addr = dec->ids_pa >> 12;
+ break;
+ default:
+ dec->ids_fail = IMC_DECODE_F_SAD_BAD_MOD;
+ return (B_FALSE);
+ }
+
+ switch (rule->isr_mod_type) {
+ case IMC_SAD_MOD_TYPE_MOD3:
+ channel = (addr % 3) << 1;
+ channel |= ileavetgt & 1;
+ break;
+ case IMC_SAD_MOD_TYPE_MOD2_01:
+ channel = (addr % 2) << 1;
+ channel |= ileavetgt & 1;
+ break;
+ case IMC_SAD_MOD_TYPE_MOD2_12:
+ channel = (addr % 2) << 2;
+ channel |= (~addr % 2) << 1;
+ channel |= ileavetgt & 1;
+ break;
+ case IMC_SAD_MOD_TYPE_MOD2_02:
+ channel = (addr % 2) << 2;
+ channel |= ileavetgt & 1;
+ break;
+ default:
+ dec->ids_fail = IMC_DECODE_F_SAD_BAD_MOD;
+ return (B_FALSE);
+ }
+
+ ileavetgt = channel;
+ }
+
+ switch (imc->imc_gen) {
+ case IMC_GEN_SANDY:
+ /*
+ * Sandy Bridge systems only have a single home agent, so the
+ * interleave target is always the node id.
+ */
+ nodeid = ileavetgt;
+ tadid = 0;
+ channelid = UINT32_MAX;
+ break;
+ case IMC_GEN_IVY:
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ /*
+ * On these generations, the interleave NodeID in the SAD
+ * encodes both the nodeid and the home agent ID that we care
+ * about.
+ */
+ nodeid = IMC_NODEID_IVY_BRD_UPPER(ileavetgt) |
+ IMC_NODEID_IVY_BRD_LOWER(ileavetgt);
+ tadid = IMC_NODEID_IVY_BRD_HA(ileavetgt);
+ channelid = UINT32_MAX;
+ break;
+ case IMC_GEN_SKYLAKE:
+ /*
+ * On Skylake generation systems we take the interleave target
+ * and use that to look up both the memory controller and the
+ * physical channel in the route table. The nodeid is already
+ * known because its SAD rules redirect us.
+ */
+ nodeid = socket->isock_nodeid;
+ if (ileavetgt > IMC_SAD_ILEAVE_SKX_MAX) {
+ dec->ids_fail = IMC_DECODE_F_BAD_SAD_INTERLEAVE;
+ dec->ids_fail_data = ileavetgt;
+ return (B_FALSE);
+ }
+ ileavetgt = IMC_SAD_ILEAVE_SKX_TARGET(ileavetgt);
+ if (ileavetgt > sad->isad_mcroute.ismc_nroutes) {
+ dec->ids_fail = IMC_DECODE_F_BAD_SAD_INTERLEAVE;
+ dec->ids_fail_data = ileavetgt;
+ return (B_FALSE);
+ }
+ tadid = sad->isad_mcroute.ismc_mcroutes[ileavetgt].ismce_imc;
+ channelid =
+ sad->isad_mcroute.ismc_mcroutes[ileavetgt].ismce_pchannel;
+ break;
+ default:
+ nodeid = tadid = channelid = UINT32_MAX;
+ break;
+ }
+
+ /*
+ * Map to the correct socket based on the nodeid. Make sure that we have
+ * a valid TAD.
+ */
+ dec->ids_socket = NULL;
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ if (imc->imc_sockets[i].isock_nodeid == nodeid) {
+ dec->ids_socket = &imc->imc_sockets[i];
+ break;
+ }
+ }
+ if (dec->ids_socket == NULL) {
+ dec->ids_fail = IMC_DECODE_F_SAD_BAD_SOCKET;
+ dec->ids_fail_data = nodeid;
+ return (B_FALSE);
+ }
+
+ if (tadid >= dec->ids_socket->isock_ntad) {
+ dec->ids_fail = IMC_DECODE_F_SAD_BAD_TAD;
+ dec->ids_fail_data = tadid;
+ return (B_FALSE);
+ }
+
+ dec->ids_nodeid = nodeid;
+ dec->ids_tadid = tadid;
+ dec->ids_channelid = channelid;
+ dec->ids_tad = &dec->ids_socket->isock_tad[tadid];
+ dec->ids_mc = &dec->ids_socket->isock_imcs[tadid];
+
+ return (B_TRUE);
+}
+
+/*
+ * For Sandy Bridge through Broadwell we need to decode the memory channel that
+ * we're targeting. This is determined based on the number of ways that the
+ * socket and channel are supposed to be interleaved. The TAD has a target
+ * channel list sitting with the TAD rule. To figure out the appropriate index,
+ * the algorithm is roughly:
+ *
+ * idx = [(dec->ids_pa >> 6) / socket-ways] % channel-ways
+ *
+ * The shift by six, comes from taking the number of bits that are in theory in
+ * the cache line size. Of course, if things were this simple, that'd be great.
+ * The first complication is a7mode / MCChanShiftUpEnable. When this is enabled,
+ * more cache lines are used for this. The next complication comes when the
+ * feature MCChanHashEn is enabled. This means that we have to hash the
+ * resulting address before we do the modulus based on the number of channel
+ * ways.
+ *
+ * The last, and most complicated problem is when the number of channel ways is
+ * set to three. When this is the case, the base address of the range may not
+ * actually start at index zero. The nominal solution is to use the offset
+ * that's programmed on a per-channel basis to offset the system address.
+ * However, to get that information we would have to know what channel we're on,
+ * which is what we're trying to figure out. Regretfully, proclaim that we can't
+ * in this case.
+ */
+static boolean_t
+imc_decode_tad_channel(const imc_t *imc, imc_decode_state_t *dec)
+{
+ uint64_t index;
+ const imc_tad_rule_t *rule = dec->ids_tad_rule;
+
+ index = dec->ids_pa >> 6;
+ if ((dec->ids_tad->itad_flags & IMC_TAD_FLAG_CHANSHIFT) != 0) {
+ index = index >> 1;
+ }
+
+ /*
+ * When performing a socket way equals three comparison, this would not
+ * work.
+ */
+ index = index / rule->itr_sock_way;
+
+ if ((dec->ids_tad->itad_flags & IMC_TAD_FLAG_CHANHASH) != 0) {
+ uint_t i;
+ for (i = 12; i < 28; i += 2) {
+ uint64_t shift = (dec->ids_pa >> i) & 0x3;
+ index ^= shift;
+ }
+ }
+
+ index %= rule->itr_chan_way;
+ if (index >= rule->itr_ntargets) {
+ dec->ids_fail = IMC_DECODE_F_TAD_BAD_TARGET_INDEX;
+ dec->ids_fail_data = index;
+ return (B_FALSE);
+ }
+
+ dec->ids_channelid = rule->itr_targets[index];
+ return (B_TRUE);
+}
+
+static uint_t
+imc_tad_gran_to_shift(const imc_tad_t *tad, imc_tad_gran_t gran)
+{
+ uint_t shift = 0;
+
+ switch (gran) {
+ case IMC_TAD_GRAN_64B:
+ shift = 6;
+ if ((tad->itad_flags & IMC_TAD_FLAG_CHANSHIFT) != 0) {
+ shift++;
+ }
+ break;
+ case IMC_TAD_GRAN_256B:
+ shift = 8;
+ break;
+ case IMC_TAD_GRAN_4KB:
+ shift = 12;
+ break;
+ case IMC_TAD_GRAN_1GB:
+ shift = 30;
+ break;
+ }
+
+ return (shift);
+}
+
+static boolean_t
+imc_decode_tad(const imc_t *imc, imc_decode_state_t *dec)
+{
+ uint_t i, tadruleno;
+ uint_t sockshift, chanshift, sockmask, chanmask;
+ uint64_t off, chanaddr;
+ const imc_tad_t *tad = dec->ids_tad;
+ const imc_mc_t *mc = dec->ids_mc;
+ const imc_tad_rule_t *rule = NULL;
+ const imc_channel_t *chan;
+
+ /*
+ * The first step in all of this is to determine which TAD rule applies
+ * for this address.
+ */
+ for (i = 0; i < tad->itad_nrules; i++) {
+ rule = &tad->itad_rules[i];
+
+ if (dec->ids_pa >= rule->itr_base &&
+ dec->ids_pa < rule->itr_limit) {
+ break;
+ }
+ }
+
+ if (rule == NULL || i == tad->itad_nrules) {
+ dec->ids_fail = IMC_DECODE_F_NO_TAD_RULE;
+ return (B_FALSE);
+ }
+ tadruleno = i;
+ dec->ids_tad_rule = rule;
+
+ /*
+ * Check if our TAD rule requires 3-way interleaving on the channel. We
+ * basically can't do that right now. For more information, see the
+ * comment above imc_decode_tad_channel().
+ */
+ if (rule->itr_chan_way == 3) {
+ dec->ids_fail = IMC_DECODE_F_TAD_3_ILEAVE;
+ return (B_FALSE);
+ }
+
+ /*
+ * On some platforms, we need to now calculate the channel index from
+ * this. The way that we calculate this is nominally straightforward,
+ * but complicated by a number of different issues.
+ */
+ switch (imc->imc_gen) {
+ case IMC_GEN_SANDY:
+ case IMC_GEN_IVY:
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ if (!imc_decode_tad_channel(imc, dec)) {
+ return (B_FALSE);
+ }
+ break;
+ default:
+ /*
+ * On Skylake and newer platforms we should have already decoded
+ * the target channel based on using the memory controller route
+ * table above.
+ */
+ break;
+ }
+
+ /*
+ * We initialize ids_channelid to UINT32_MAX, so this should make sure
+ * that we catch an incorrect channel as well.
+ */
+ if (dec->ids_channelid >= mc->icn_nchannels) {
+ dec->ids_fail = IMC_DECODE_F_BAD_CHANNEL_ID;
+ dec->ids_fail_data = dec->ids_channelid;
+ return (B_FALSE);
+ }
+ chan = &mc->icn_channels[dec->ids_channelid];
+ dec->ids_chan = chan;
+
+ if (tadruleno >= chan->ich_ntad_offsets) {
+ dec->ids_fail = IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET;
+ dec->ids_fail_data = tadruleno;
+ return (B_FALSE);
+ }
+
+ /*
+ * Now we can go ahead and calculate the channel address, which is
+ * roughly equal to:
+ *
+ * chan_addr = (sys_addr - off) / (chan way * sock way).
+ *
+ * The catch is that we want to preserve the low bits where possible.
+ * The number of bits is based on the interleaving granularities, the
+ * way that's calculated is based on information in the TAD rule.
+ * However, if a7mode is enabled on Ivy Bridge through Broadwell, then
+ * we need to add one to that. So we will save the smallest number of
+ * bits that are left after interleaving.
+ *
+ * Because the interleaving occurs at different granularities, we need
+ * to break this into two discrete steps, one where we apply the socket
+ * interleaving and one where we apply the channel interleaving,
+ * shifting and dividing at each step.
+ */
+ off = chan->ich_tad_offsets[tadruleno];
+ if (off > dec->ids_pa) {
+ dec->ids_fail = IMC_DECODE_F_CHANOFF_UNDERFLOW;
+ return (B_FALSE);
+ }
+ chanshift = imc_tad_gran_to_shift(tad, rule->itr_chan_gran);
+ sockshift = imc_tad_gran_to_shift(tad, rule->itr_sock_gran);
+ chanmask = (1 << chanshift) - 1;
+ sockmask = (1 << sockshift) - 1;
+
+ chanaddr = dec->ids_pa - off;
+ chanaddr >>= sockshift;
+ chanaddr /= rule->itr_sock_way;
+ chanaddr <<= sockshift;
+ chanaddr |= dec->ids_pa & sockmask;
+ chanaddr >>= chanshift;
+ chanaddr /= rule->itr_chan_way;
+ chanaddr <<= chanshift;
+ chanaddr |= dec->ids_pa & chanmask;
+
+ dec->ids_chanaddr = chanaddr;
+
+ return (B_TRUE);
+}
+
+static boolean_t
+imc_decode_rir(const imc_t *imc, imc_decode_state_t *dec)
+{
+ const imc_mc_t *mc = dec->ids_mc;
+ const imc_channel_t *chan = dec->ids_chan;
+ const imc_rank_ileave_t *rir = NULL;
+ const imc_rank_ileave_entry_t *rirtarg;
+ const imc_dimm_t *dimm;
+ uint32_t shift, index;
+ uint_t i, dimmid, rankid;
+ uint64_t mask, base, rankaddr;
+
+ if (mc->icn_closed) {
+ shift = IMC_PAGE_BITS_CLOSED;
+ } else {
+ shift = IMC_PAGE_BITS_OPEN;
+ }
+ mask = (1UL << shift) - 1;
+
+ for (i = 0, base = 0; i < chan->ich_nrankileaves; i++) {
+ rir = &chan->ich_rankileaves[i];
+ if (rir->irle_enabled && dec->ids_chanaddr >= base &&
+ dec->ids_chanaddr < rir->irle_limit) {
+ break;
+ }
+
+ base = rir->irle_limit;
+ }
+
+ if (rir == NULL || i == chan->ich_nrankileaves) {
+ dec->ids_fail = IMC_DECODE_F_NO_RIR_RULE;
+ return (B_FALSE);
+ }
+ dec->ids_rir = rir;
+
+ /*
+ * Determine the index of the rule that we care about. This is done by
+ * shifting the address based on the open and closed page bits and then
+ * just modding it by the number of ways in question.
+ */
+ index = (dec->ids_chanaddr >> shift) % rir->irle_nways;
+ if (index >= rir->irle_nentries) {
+ dec->ids_fail = IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET;
+ dec->ids_fail_data = index;
+ return (B_FALSE);
+ }
+ rirtarg = &rir->irle_entries[index];
+
+ /*
+ * The rank interleaving register has information about a physical rank
+ * target. This is within the notion of the physical chip selects that
+ * exist. While the memory controller only has eight actual chip
+ * selects, the physical values that are programmed depend a bit on the
+ * underlying hardware. Effectively, in this ID space, each DIMM has
+ * four ranks associated with it. Even when we only have two ranks with
+ * each physical channel, they'll be programmed so we can simply do the
+ * following match:
+ *
+ * DIMM = rank id / 4
+ * RANK = rank id % 4
+ */
+ dec->ids_physrankid = rirtarg->irle_target;
+ dimmid = dec->ids_physrankid / 4;
+ rankid = dec->ids_physrankid % 4;
+
+ if (dimmid >= chan->ich_ndimms) {
+ dec->ids_fail = IMC_DECODE_F_BAD_DIMM_INDEX;
+ dec->ids_fail_data = dimmid;
+ return (B_FALSE);
+ }
+
+ dimm = &chan->ich_dimms[dimmid];
+ if (!dimm->idimm_present) {
+ dec->ids_fail = IMC_DECODE_F_DIMM_NOT_PRESENT;
+ return (B_FALSE);
+ }
+ dec->ids_dimmid = dimmid;
+ dec->ids_dimm = dimm;
+
+ if (rankid >= dimm->idimm_nranks) {
+ dec->ids_fail = IMC_DECODE_F_BAD_DIMM_RANK;
+ dec->ids_fail_data = rankid;
+ return (B_FALSE);
+ }
+ dec->ids_rankid = rankid;
+
+ /*
+ * Calculate the rank address. We need to divide the address by the
+ * number of rank ways and then or in the lower bits.
+ */
+ rankaddr = dec->ids_chanaddr;
+ rankaddr >>= shift;
+ rankaddr /= rir->irle_nways;
+ rankaddr <<= shift;
+ rankaddr |= dec->ids_chanaddr & mask;
+
+ if (rirtarg->irle_offset > rankaddr) {
+ dec->ids_fail = IMC_DECODE_F_RANKOFF_UNDERFLOW;
+ return (B_FALSE);
+ }
+ rankaddr -= rirtarg->irle_offset;
+ dec->ids_rankaddr = rankaddr;
+
+ return (B_TRUE);
+}
+
+boolean_t
+imc_decode_pa(const imc_t *imc, uint64_t pa, imc_decode_state_t *dec)
+{
+ bzero(dec, sizeof (*dec));
+ dec->ids_pa = pa;
+ dec->ids_nodeid = dec->ids_tadid = dec->ids_channelid = UINT32_MAX;
+
+ /*
+ * We need to rely on socket zero's information. Make sure that it both
+ * exists and is considered valid.
+ */
+ if (imc->imc_nsockets < 1 ||
+ imc->imc_sockets[0].isock_valid != IMC_SOCKET_V_VALID) {
+ dec->ids_fail = IMC_DECODE_F_BAD_SOCKET;
+ dec->ids_fail_data = 0;
+ return (B_FALSE);
+ }
+
+ /*
+ * First, we need to make sure that the PA we've been given actually is
+ * meant to target a DRAM address. This address may fall to MMIO, MMCFG,
+ * be an address that's outside of DRAM, or belong to a legacy address
+ * range that is interposed.
+ */
+ if (imc_decode_addr_resvd(imc, dec)) {
+ return (B_FALSE);
+ }
+
+ /*
+ * Now that we have this data, we want to go through and look at the
+ * SAD. The SAD will point us to a specific socket and an IMC / home
+ * agent on that socket which will tell us which TAD we need to use.
+ */
+ if (!imc_decode_sad(imc, dec)) {
+ return (B_FALSE);
+ }
+
+ /*
+ * The decoded SAD information has pointed us a TAD. We need to use this
+ * to point us to the corresponding memory channel and the corresponding
+ * address on the channel.
+ */
+ if (!imc_decode_tad(imc, dec)) {
+ return (B_FALSE);
+ }
+
+ /*
+ * Use the rank interleaving data to determine which DIMM this is, the
+ * relevant rank, and the rank address.
+ */
+ if (!imc_decode_rir(imc, dec)) {
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
diff --git a/usr/src/common/mc/imc/imc_dump.c b/usr/src/common/mc/imc/imc_dump.c
new file mode 100644
index 0000000000..05a2f72308
--- /dev/null
+++ b/usr/src/common/mc/imc/imc_dump.c
@@ -0,0 +1,569 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * This implements logic to allow us to dump IMC data for decoding purposes,
+ * such that we can later encode it elsewhere. In general, dumping is done by
+ * the kernel and reconstituting this data is done by user land.
+ */
+
+#include "imc.h"
+
+#ifndef _KERNEL
+#include <stdint.h>
+#include <strings.h>
+#endif /* !_KERNEL */
+
+
+static nvlist_t *
+imc_dump_sad(imc_sad_t *sad)
+{
+ uint_t i;
+ nvlist_t *nvl;
+ nvlist_t *rules[IMC_MAX_SAD_RULES];
+ nvlist_t *routes[IMC_MAX_SAD_MCROUTES];
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_uint32(nvl, "isad_flags", sad->isad_flags);
+ fnvlist_add_uint32(nvl, "isad_valid", sad->isad_valid);
+ fnvlist_add_uint64(nvl, "isad_tolm", sad->isad_tolm);
+ fnvlist_add_uint64(nvl, "isad_tohm", sad->isad_tohm);
+
+ for (i = 0; i < sad->isad_nrules; i++) {
+ nvlist_t *n = fnvlist_alloc();
+ imc_sad_rule_t *r = &sad->isad_rules[i];
+
+ fnvlist_add_boolean_value(n, "isr_enable", r->isr_enable);
+ fnvlist_add_boolean_value(n, "isr_a7mode", r->isr_a7mode);
+ fnvlist_add_boolean_value(n, "isr_need_mod3", r->isr_need_mod3);
+ fnvlist_add_uint64(n, "isr_limit", r->isr_limit);
+ fnvlist_add_uint32(n, "isr_type", r->isr_type);
+ fnvlist_add_uint32(n, "isr_imode", r->isr_imode);
+ fnvlist_add_uint32(n, "isr_mod_mode", r->isr_mod_mode);
+ fnvlist_add_uint32(n, "isr_mod_type", r->isr_mod_type);
+ fnvlist_add_uint8_array(n, "isr_targets", r->isr_targets,
+ r->isr_ntargets);
+
+ rules[i] = n;
+ }
+ fnvlist_add_nvlist_array(nvl, "isad_rules", rules, sad->isad_nrules);
+ for (i = 0; i < sad->isad_nrules; i++) {
+ nvlist_free(rules[i]);
+ }
+
+ if (sad->isad_mcroute.ismc_nroutes == 0) {
+ return (nvl);
+ }
+
+ for (i = 0; i < sad->isad_mcroute.ismc_nroutes; i++) {
+ nvlist_t *r = fnvlist_alloc();
+ imc_sad_mcroute_entry_t *e =
+ &sad->isad_mcroute.ismc_mcroutes[i];
+
+ fnvlist_add_uint8(r, "ismce_imc", e->ismce_imc);
+ fnvlist_add_uint8(r, "ismce_pchannel", e->ismce_pchannel);
+ routes[i] = r;
+ }
+ fnvlist_add_nvlist_array(nvl, "isad_mcroute", routes, i);
+ for (i = 0; i < sad->isad_mcroute.ismc_nroutes; i++) {
+ nvlist_free(routes[i]);
+ }
+
+ return (nvl);
+}
+
+static nvlist_t *
+imc_dump_tad(imc_tad_t *tad)
+{
+ uint_t i;
+ nvlist_t *nvl;
+ nvlist_t *rules[IMC_MAX_TAD_RULES];
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_uint32(nvl, "itad_valid", tad->itad_valid);
+ fnvlist_add_uint32(nvl, "itad_flags", tad->itad_flags);
+ for (i = 0; i < tad->itad_nrules; i++) {
+ nvlist_t *t = fnvlist_alloc();
+ imc_tad_rule_t *r = &tad->itad_rules[i];
+
+ fnvlist_add_uint64(t, "itr_base", r->itr_base);
+ fnvlist_add_uint64(t, "itr_limit", r->itr_limit);
+ fnvlist_add_uint8(t, "itr_sock_way", r->itr_sock_way);
+ fnvlist_add_uint8(t, "itr_chan_way", r->itr_chan_way);
+ fnvlist_add_uint32(t, "itr_sock_gran", r->itr_sock_gran);
+ fnvlist_add_uint32(t, "itr_chan_gran", r->itr_chan_gran);
+ fnvlist_add_uint8_array(t, "itr_targets", r->itr_targets,
+ r->itr_ntargets);
+
+ rules[i] = t;
+ }
+ fnvlist_add_nvlist_array(nvl, "itad_rules", rules, tad->itad_nrules);
+ for (i = 0; i < tad->itad_nrules; i++) {
+ nvlist_free(rules[i]);
+ }
+
+ return (nvl);
+}
+
+static nvlist_t *
+imc_dump_channel(imc_channel_t *chan)
+{
+ uint_t i;
+ nvlist_t *nvl;
+ nvlist_t *dimms[IMC_MAX_DIMMPERCHAN];
+ nvlist_t *ranks[IMC_MAX_RANK_WAYS];
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_uint32(nvl, "ich_valid", chan->ich_valid);
+ for (i = 0; i < chan->ich_ndimms; i++) {
+ nvlist_t *d = fnvlist_alloc();
+ imc_dimm_t *dimm = &chan->ich_dimms[i];
+
+ fnvlist_add_uint32(d, "idimm_valid", dimm->idimm_valid);
+ fnvlist_add_boolean_value(d, "idimm_present",
+ dimm->idimm_present);
+ if (!dimm->idimm_present)
+ goto add;
+
+ fnvlist_add_uint8(d, "idimm_nbanks", dimm->idimm_nbanks);
+ fnvlist_add_uint8(d, "idimm_nranks", dimm->idimm_nranks);
+ fnvlist_add_uint8(d, "idimm_width", dimm->idimm_width);
+ fnvlist_add_uint8(d, "idimm_density", dimm->idimm_density);
+ fnvlist_add_uint8(d, "idimm_nrows", dimm->idimm_nrows);
+ fnvlist_add_uint8(d, "idimm_ncolumns", dimm->idimm_ncolumns);
+ fnvlist_add_uint64(d, "idimm_size", dimm->idimm_size);
+add:
+ dimms[i] = d;
+ }
+ fnvlist_add_nvlist_array(nvl, "ich_dimms", dimms, i);
+ for (i = 0; i < chan->ich_ndimms; i++) {
+ nvlist_free(dimms[i]);
+ }
+
+ fnvlist_add_uint64_array(nvl, "ich_tad_offsets", chan->ich_tad_offsets,
+ chan->ich_ntad_offsets);
+
+ for (i = 0; i < chan->ich_nrankileaves; i++) {
+ uint_t j;
+ nvlist_t *r = fnvlist_alloc();
+ nvlist_t *ileaves[IMC_MAX_RANK_INTERLEAVES];
+ imc_rank_ileave_t *rank = &chan->ich_rankileaves[i];
+
+ fnvlist_add_boolean_value(r, "irle_enabled",
+ rank->irle_enabled);
+ fnvlist_add_uint8(r, "irle_nways", rank->irle_nways);
+ fnvlist_add_uint8(r, "irle_nwaysbits", rank->irle_nwaysbits);
+ fnvlist_add_uint64(r, "irle_limit", rank->irle_limit);
+
+ for (j = 0; j < rank->irle_nentries; j++) {
+ nvlist_t *e = fnvlist_alloc();
+
+ fnvlist_add_uint8(e, "irle_target",
+ rank->irle_entries[j].irle_target);
+ fnvlist_add_uint64(e, "irle_offset",
+ rank->irle_entries[j].irle_offset);
+ ileaves[j] = e;
+ }
+ fnvlist_add_nvlist_array(r, "irle_entries", ileaves, j);
+ for (j = 0; j < rank->irle_nentries; j++) {
+ nvlist_free(ileaves[j]);
+ }
+
+ ranks[i] = r;
+ }
+ fnvlist_add_nvlist_array(nvl, "ich_rankileaves", ranks, i);
+ for (i = 0; i < chan->ich_nrankileaves; i++) {
+ nvlist_free(ranks[i]);
+ }
+
+ return (nvl);
+}
+
+static nvlist_t *
+imc_dump_mc(imc_mc_t *mc)
+{
+ uint_t i;
+ nvlist_t *nvl;
+ nvlist_t *channels[IMC_MAX_CHANPERMC];
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_boolean_value(nvl, "icn_ecc", mc->icn_ecc);
+ fnvlist_add_boolean_value(nvl, "icn_lockstep", mc->icn_lockstep);
+ fnvlist_add_boolean_value(nvl, "icn_closed", mc->icn_closed);
+ fnvlist_add_uint32(nvl, "icn_dimm_type", mc->icn_dimm_type);
+
+ for (i = 0; i < mc->icn_nchannels; i++) {
+ channels[i] = imc_dump_channel(&mc->icn_channels[i]);
+ }
+ fnvlist_add_nvlist_array(nvl, "icn_channels", channels, i);
+ for (i = 0; i < mc->icn_nchannels; i++) {
+ nvlist_free(channels[i]);
+ }
+
+ return (nvl);
+}
+
+static nvlist_t *
+imc_dump_socket(imc_socket_t *sock)
+{
+ uint_t i;
+ nvlist_t *nvl, *sad;
+ nvlist_t *tad[IMC_MAX_TAD];
+ nvlist_t *mc[IMC_MAX_IMCPERSOCK];
+
+ nvl = fnvlist_alloc();
+
+ sad = imc_dump_sad(&sock->isock_sad);
+ fnvlist_add_nvlist(nvl, "isock_sad", sad);
+ nvlist_free(sad);
+
+ for (i = 0; i < sock->isock_ntad; i++) {
+ tad[i] = imc_dump_tad(&sock->isock_tad[i]);
+ }
+ fnvlist_add_nvlist_array(nvl, "isock_tad", tad, i);
+ for (i = 0; i < sock->isock_ntad; i++) {
+ fnvlist_free(tad[i]);
+ }
+
+ fnvlist_add_uint32(nvl, "isock_nodeid", sock->isock_nodeid);
+
+ for (i = 0; i < sock->isock_nimc; i++) {
+ mc[i] = imc_dump_mc(&sock->isock_imcs[i]);
+ }
+ fnvlist_add_nvlist_array(nvl, "isock_imcs", mc, i);
+ for (i = 0; i < sock->isock_nimc; i++) {
+ fnvlist_free(mc[i]);
+ }
+ return (nvl);
+}
+
+nvlist_t *
+imc_dump_decoder(imc_t *imc)
+{
+ uint_t i;
+ nvlist_t *nvl, *invl;
+ nvlist_t *sockets[IMC_MAX_SOCKETS];
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_uint32(nvl, "mc_dump_version", 0);
+ fnvlist_add_string(nvl, "mc_dump_driver", "imc");
+
+ invl = fnvlist_alloc();
+ fnvlist_add_uint32(invl, "imc_gen", imc->imc_gen);
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ sockets[i] = imc_dump_socket(&imc->imc_sockets[i]);
+ }
+ fnvlist_add_nvlist_array(invl, "imc_sockets", sockets, i);
+ fnvlist_add_nvlist(nvl, "imc", invl);
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ nvlist_free(sockets[i]);
+ }
+ nvlist_free(invl);
+
+ return (nvl);
+}
+
+static boolean_t
+imc_restore_sad(nvlist_t *nvl, imc_sad_t *sad)
+{
+ nvlist_t **rules, **routes;
+ uint_t i, nroutes;
+
+ if (nvlist_lookup_uint32(nvl, "isad_flags", &sad->isad_flags) != 0 ||
+ nvlist_lookup_uint32(nvl, "isad_valid", &sad->isad_valid) != 0 ||
+ nvlist_lookup_uint64(nvl, "isad_tolm", &sad->isad_tolm) != 0 ||
+ nvlist_lookup_uint64(nvl, "isad_tohm", &sad->isad_tohm) != 0 ||
+ nvlist_lookup_nvlist_array(nvl, "isad_rules",
+ &rules, &sad->isad_nrules) != 0) {
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < sad->isad_nrules; i++) {
+ imc_sad_rule_t *r = &sad->isad_rules[i];
+ uint8_t *targs;
+
+ if (nvlist_lookup_boolean_value(rules[i], "isr_enable",
+ &r->isr_enable) != 0 ||
+ nvlist_lookup_boolean_value(rules[i], "isr_a7mode",
+ &r->isr_a7mode) != 0 ||
+ nvlist_lookup_boolean_value(rules[i], "isr_need_mod3",
+ &r->isr_need_mod3) != 0 ||
+ nvlist_lookup_uint64(rules[i], "isr_limit",
+ &r->isr_limit) != 0 ||
+ nvlist_lookup_uint32(rules[i], "isr_type",
+ &r->isr_type) != 0 ||
+ nvlist_lookup_uint32(rules[i], "isr_imode",
+ &r->isr_imode) != 0 ||
+ nvlist_lookup_uint32(rules[i], "isr_mod_mode",
+ &r->isr_mod_mode) != 0 ||
+ nvlist_lookup_uint32(rules[i], "isr_mod_type",
+ &r->isr_mod_type) != 0 ||
+ nvlist_lookup_uint8_array(rules[i], "isr_targets", &targs,
+ &r->isr_ntargets) != 0 ||
+ r->isr_ntargets > IMC_MAX_SAD_RULES) {
+ return (B_FALSE);
+ }
+
+ bcopy(targs, r->isr_targets, r->isr_ntargets *
+ sizeof (uint8_t));
+ }
+
+ /*
+ * The mcroutes entry right now is only included conditionally.
+ */
+ if (nvlist_lookup_nvlist_array(nvl, "isad_mcroute", &routes,
+ &nroutes) == 0) {
+ if (nroutes > IMC_MAX_SAD_MCROUTES)
+ return (B_FALSE);
+ sad->isad_mcroute.ismc_nroutes = nroutes;
+ for (i = 0; i < nroutes; i++) {
+ imc_sad_mcroute_entry_t *r =
+ &sad->isad_mcroute.ismc_mcroutes[i];
+ if (nvlist_lookup_uint8(routes[i], "ismce_imc",
+ &r->ismce_imc) != 0 ||
+ nvlist_lookup_uint8(routes[i], "ismce_pchannel",
+ &r->ismce_pchannel) != 0) {
+ return (B_FALSE);
+ }
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+imc_restore_tad(nvlist_t *nvl, imc_tad_t *tad)
+{
+ nvlist_t **rules;
+
+ if (nvlist_lookup_uint32(nvl, "itad_valid", &tad->itad_valid) != 0 ||
+ nvlist_lookup_uint32(nvl, "itad_flags", &tad->itad_flags) != 0 ||
+ nvlist_lookup_nvlist_array(nvl, "itad_rules", &rules,
+ &tad->itad_nrules) != 0 || tad->itad_nrules > IMC_MAX_TAD_RULES) {
+ return (B_FALSE);
+ }
+
+ for (uint_t i = 0; i < tad->itad_nrules; i++) {
+ imc_tad_rule_t *r = &tad->itad_rules[i];
+ uint8_t *targs;
+
+ if (nvlist_lookup_uint64(rules[i], "itr_base",
+ &r->itr_base) != 0 ||
+ nvlist_lookup_uint64(rules[i], "itr_limit",
+ &r->itr_limit) != 0 ||
+ nvlist_lookup_uint8(rules[i], "itr_sock_way",
+ &r->itr_sock_way) != 0 ||
+ nvlist_lookup_uint8(rules[i], "itr_chan_way",
+ &r->itr_chan_way) != 0 ||
+ nvlist_lookup_uint32(rules[i], "itr_sock_gran",
+ &r->itr_sock_gran) != 0 ||
+ nvlist_lookup_uint32(rules[i], "itr_chan_gran",
+ &r->itr_chan_gran) != 0 ||
+ nvlist_lookup_uint8_array(rules[i], "itr_targets",
+ &targs, &r->itr_ntargets) != 0 ||
+ r->itr_ntargets > IMC_MAX_TAD_TARGETS) {
+ return (B_FALSE);
+ }
+
+ bcopy(targs, r->itr_targets, r->itr_ntargets *
+ sizeof (uint8_t));
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+imc_restore_channel(nvlist_t *nvl, imc_channel_t *chan)
+{
+ nvlist_t **dimms, **rir;
+ uint64_t *tadoff;
+
+ if (nvlist_lookup_uint32(nvl, "ich_valid", &chan->ich_valid) != 0 ||
+ nvlist_lookup_nvlist_array(nvl, "ich_dimms", &dimms,
+ &chan->ich_ndimms) != 0 ||
+ chan->ich_ndimms > IMC_MAX_DIMMPERCHAN ||
+ nvlist_lookup_uint64_array(nvl, "ich_tad_offsets", &tadoff,
+ &chan->ich_ntad_offsets) != 0 ||
+ chan->ich_ntad_offsets > IMC_MAX_TAD_RULES ||
+ nvlist_lookup_nvlist_array(nvl, "ich_rankileaves", &rir,
+ &chan->ich_nrankileaves) != 0 ||
+ chan->ich_nrankileaves > IMC_MAX_RANK_WAYS) {
+ return (B_FALSE);
+ }
+
+ for (uint_t i = 0; i < chan->ich_ndimms; i++) {
+ imc_dimm_t *d = &chan->ich_dimms[i];
+
+ if (nvlist_lookup_uint32(dimms[i], "idimm_valid",
+ &d->idimm_valid) != 0 ||
+ nvlist_lookup_boolean_value(dimms[i], "idimm_present",
+ &d->idimm_present) != 0) {
+ return (B_FALSE);
+ }
+
+ if (!d->idimm_present)
+ continue;
+
+ if (nvlist_lookup_uint8(dimms[i], "idimm_nbanks",
+ &d->idimm_nbanks) != 0 ||
+ nvlist_lookup_uint8(dimms[i], "idimm_nranks",
+ &d->idimm_nranks) != 0 ||
+ nvlist_lookup_uint8(dimms[i], "idimm_width",
+ &d->idimm_width) != 0 ||
+ nvlist_lookup_uint8(dimms[i], "idimm_density",
+ &d->idimm_density) != 0 ||
+ nvlist_lookup_uint8(dimms[i], "idimm_nrows",
+ &d->idimm_nrows) != 0 ||
+ nvlist_lookup_uint8(dimms[i], "idimm_ncolumns",
+ &d->idimm_ncolumns) != 0 ||
+ nvlist_lookup_uint64(dimms[i], "idimm_size",
+ &d->idimm_size) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ bcopy(tadoff, chan->ich_tad_offsets, chan->ich_ntad_offsets *
+ sizeof (uint64_t));
+
+ for (uint_t i = 0; i < chan->ich_nrankileaves; i++) {
+ nvlist_t **ileaves;
+ imc_rank_ileave_t *r = &chan->ich_rankileaves[i];
+
+ if (nvlist_lookup_boolean_value(rir[i], "irle_enabled",
+ &r->irle_enabled) != 0 ||
+ nvlist_lookup_uint8(rir[i], "irle_nways",
+ &r->irle_nways) != 0 ||
+ nvlist_lookup_uint8(rir[i], "irle_nwaysbits",
+ &r->irle_nwaysbits) != 0 ||
+ nvlist_lookup_uint64(rir[i], "irle_limit",
+ &r->irle_limit) != 0 ||
+ nvlist_lookup_nvlist_array(rir[i], "irle_entries",
+ &ileaves, &r->irle_nentries) != 0 ||
+ r->irle_nentries > IMC_MAX_RANK_INTERLEAVES) {
+ return (B_FALSE);
+ }
+
+ for (uint_t j = 0; j < r->irle_nentries; j++) {
+ imc_rank_ileave_entry_t *ril = &r->irle_entries[j];
+
+ if (nvlist_lookup_uint8(ileaves[j], "irle_target",
+ &ril->irle_target) != 0 ||
+ nvlist_lookup_uint64(ileaves[j], "irle_offset",
+ &ril->irle_offset) != 0) {
+ return (B_FALSE);
+ }
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+imc_restore_mc(nvlist_t *nvl, imc_mc_t *mc)
+{
+ nvlist_t **channels;
+
+ if (nvlist_lookup_boolean_value(nvl, "icn_ecc", &mc->icn_ecc) != 0 ||
+ nvlist_lookup_boolean_value(nvl, "icn_lockstep",
+ &mc->icn_lockstep) != 0 ||
+ nvlist_lookup_boolean_value(nvl, "icn_closed",
+ &mc->icn_closed) != 0 ||
+ nvlist_lookup_uint32(nvl, "icn_dimm_type",
+ &mc->icn_dimm_type) != 0 ||
+ nvlist_lookup_nvlist_array(nvl, "icn_channels", &channels,
+ &mc->icn_nchannels) != 0 || mc->icn_nchannels > IMC_MAX_CHANPERMC) {
+ return (B_FALSE);
+ }
+
+ for (uint_t i = 0; i < mc->icn_nchannels; i++) {
+ if (!imc_restore_channel(channels[i], &mc->icn_channels[i])) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+imc_restore_socket(nvlist_t *nvl, imc_socket_t *sock)
+{
+ uint_t i;
+ nvlist_t *sad, **tads, **imcs;
+
+ if (nvlist_lookup_nvlist(nvl, "isock_sad", &sad) != 0 ||
+ nvlist_lookup_nvlist_array(nvl, "isock_tad", &tads,
+ &sock->isock_ntad) != 0 ||
+ nvlist_lookup_uint32(nvl, "isock_nodeid",
+ &sock->isock_nodeid) != 0 ||
+ nvlist_lookup_nvlist_array(nvl, "isock_imcs", &imcs,
+ &sock->isock_nimc) != 0 ||
+ sock->isock_ntad > IMC_MAX_TAD ||
+ sock->isock_nimc > IMC_MAX_IMCPERSOCK) {
+ return (B_FALSE);
+ }
+
+ if (!imc_restore_sad(sad, &sock->isock_sad)) {
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < sock->isock_ntad; i++) {
+ if (!imc_restore_tad(tads[i], &sock->isock_tad[i])) {
+ return (B_FALSE);
+ }
+ }
+
+ for (i = 0; i < sock->isock_nimc; i++) {
+ if (!imc_restore_mc(imcs[i], &sock->isock_imcs[i])) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+boolean_t
+imc_restore_decoder(nvlist_t *nvl, imc_t *imc)
+{
+ uint_t i;
+ uint32_t vers;
+ nvlist_t *invl, **socks;
+ char *driver;
+
+ bzero(imc, sizeof (imc_t));
+
+ if (nvlist_lookup_uint32(nvl, "mc_dump_version", &vers) != 0 ||
+ vers != 0 ||
+ nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0 ||
+ strcmp(driver, "imc") != 0 ||
+ nvlist_lookup_nvlist(nvl, "imc", &invl) != 0) {
+ return (B_FALSE);
+ }
+
+ if (nvlist_lookup_uint32(invl, "imc_gen", &imc->imc_gen) != 0 ||
+ nvlist_lookup_nvlist_array(invl, "imc_sockets", &socks,
+ &imc->imc_nsockets) != 0 ||
+ imc->imc_nsockets > IMC_MAX_SOCKETS) {
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ if (!imc_restore_socket(socks[i], &imc->imc_sockets[i]))
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c
index a57866c403..4c5da1e8cb 100644
--- a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c
+++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <unistd.h>
@@ -52,6 +53,27 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
+/*
+ * These are names that we use for properties. In the v0 scheme we try to pull
+ * these directly from the memory controller (which also causes a lot more
+ * variation). In the v1 scheme we translate to the name that topo wants to use,
+ * regardless of what the parent is called.
+ */
+#define MC_PROP_ECC "memory-ecc"
+#define MC_PROP_POLICY "memory-policy"
+#define CHAN_PROP_MODE "channel-mode"
+#define DIMM_SIZE "size"
+#define DIMM_STRING_SIZE "dimm-size"
+#define DIMM_COL "ncolumn"
+#define DIMM_ROW "nrow"
+#define DIMM_DENSITY "density"
+#define DIMM_WIDTH "width"
+#define DIMM_RANKS "ranks"
+#define DIMM_BANKS "nbanks"
+#define DIMM_HDRL "hdrl-enabled"
+#define DIMM_HDRL_PARITY "hdrl-parity"
+#define DIMM_3DRANK "3d-subranks"
+
static const topo_pgroup_info_t dimm_channel_pgroup =
{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
static const topo_pgroup_info_t dimm_pgroup =
@@ -432,6 +454,310 @@ mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
return (0);
}
+static int
+mc_dimm_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
+ nvlist_t *dimm_nvl, uint_t id)
+{
+ int err, ret;
+ tnode_t *dimm;
+ nvlist_t *fmri;
+ boolean_t present;
+ uint64_t size, density;
+ uint32_t cols, rows, width, ranks, banks;
+
+ /*
+ * First, figure out if this DIMM is present. If not, we don't bother
+ * creating anything.
+ */
+ if (nvlist_lookup_boolean_value(dimm_nvl,
+ MCINTEL_NVLIST_V1_DIMM_PRESENT, &present) != 0) {
+ return (-1);
+ } else if (!present) {
+ return (0);
+ }
+
+ fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, DIMM, id,
+ NULL, auth, NULL, NULL, NULL);
+ if (fmri == NULL) {
+ whinge(mod, NULL, "mc_dimm_create_v1: topo_mod_hcfmri "
+ "failed\n");
+ return (-1);
+ }
+
+ if ((dimm = topo_node_bind(mod, pnode, DIMM, id, fmri)) == NULL) {
+ whinge(mod, NULL, "mc_dimm_create_v1: node bind failed for "
+ "DIMM\n");
+ nvlist_free(fmri);
+ return (-1);
+ }
+
+ if (topo_node_fru_set(dimm, NULL, 0, &err) != 0) {
+ whinge(mod, NULL, "mc_dimm_create_v1: fru set failed: "
+ "%d\n", err);
+ nvlist_free(fmri);
+ return (topo_mod_seterrno(mod, err));
+ }
+ nvlist_free(fmri);
+
+ if (topo_pgroup_create(dimm, &dimm_pgroup, &err) != 0) {
+ whinge(mod, NULL, "mc_dimm_create_v1: failed to create "
+ "property group: %d\n", err);
+ return (topo_mod_seterrno(mod, err));
+ }
+
+ ret = 0;
+ if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_SIZE,
+ &size) == 0) {
+ char buf[64];
+ const char *suffix;
+ ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_SIZE,
+ TOPO_PROP_IMMUTABLE, size, &err);
+
+ /*
+ * We must manually cons up a dimm-size property which is the
+ * size of the dimm as a round number with a prefix such as M or
+ * G. This is used by Intel CPU eversholt rules. Older memory
+ * controller drivers did this in the driver, but we instead opt
+ * to do so in user land.
+ */
+ if (size >= (1ULL << 40)) {
+ size /= (1ULL << 40);
+ suffix = "T";
+ } else if (size >= (1ULL << 30)) {
+ size /= (1ULL << 30);
+ suffix = "G";
+ } else if (size >= (1ULL << 20)) {
+ size /= (1ULL << 20);
+ suffix = "M";
+ } else {
+ suffix = NULL;
+ }
+
+ if (suffix != NULL) {
+ if (snprintf(buf, sizeof (buf), "%"PRIu64"%s", size,
+ suffix) >= sizeof (buf)) {
+ whinge(mod, NULL, "failed to construct DIMM "
+ "size due to buffer overflow");
+ return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
+ }
+ ret |= topo_prop_set_string(dimm, PGNAME(DIMM),
+ DIMM_STRING_SIZE, TOPO_PROP_IMMUTABLE, buf, &err);
+ }
+ }
+
+ if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS,
+ &cols) == 0) {
+ ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_COL,
+ TOPO_PROP_IMMUTABLE, cols, &err);
+ }
+
+ if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NROWS,
+ &rows) == 0) {
+ ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_ROW,
+ TOPO_PROP_IMMUTABLE, rows, &err);
+ }
+
+ if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY,
+ &density) == 0) {
+ ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_DENSITY,
+ TOPO_PROP_IMMUTABLE, density, &err);
+ }
+
+ if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH,
+ &width) == 0) {
+ ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_WIDTH,
+ TOPO_PROP_IMMUTABLE, width, &err);
+ }
+
+ if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_BANKS,
+ &banks) == 0) {
+ ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_BANKS,
+ TOPO_PROP_IMMUTABLE, banks, &err);
+ }
+
+ if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RANKS,
+ &ranks) == 0) {
+ ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_RANKS,
+ TOPO_PROP_IMMUTABLE, ranks, &err);
+ }
+
+ return (ret != 0 ? -1 : 0);
+}
+
+static int
+mc_channel_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
+ nvlist_t *chan_nvl, uint_t id, const char *cmode)
+{
+ int err;
+ tnode_t *chnode;
+ nvlist_t *fmri;
+ nvlist_t **dimms;
+ uint_t ndimms, i;
+
+ if (mkrsrc(mod, pnode, DRAMCHANNEL, id, auth, &fmri) != 0) {
+ whinge(mod, NULL, "mc_channel_create_v1: mkrsrc failed\n");
+ return (-1);
+ }
+
+ if ((chnode = topo_node_bind(mod, pnode, DRAMCHANNEL, id, fmri)) ==
+ NULL) {
+ whinge(mod, NULL, "mc_channel_create_v1: node bind failed"
+ " for dram-channel\n");
+ nvlist_free(fmri);
+ return (-1);
+ }
+
+ nvlist_free(fmri);
+ if (topo_node_fru_set(chnode, NULL, 0, &err) != 0) {
+ whinge(mod, NULL, "mc_channel_create_v1: fru set failed: "
+ "%d\n", err);
+ return (topo_mod_seterrno(mod, err));
+ }
+
+ if (topo_pgroup_create(chnode, &dimm_channel_pgroup, &err) != 0) {
+ whinge(mod, NULL, "mc_channel_create_v1: failed to create "
+ "property group: %d\n", err);
+ return (topo_mod_seterrno(mod, err));
+ }
+
+ if (topo_prop_set_string(chnode, PGNAME(CHAN), CHAN_PROP_MODE,
+ TOPO_PROP_IMMUTABLE, cmode, &err) != 0) {
+ return (topo_mod_seterrno(mod, err));
+ }
+
+ if (nvlist_lookup_nvlist_array(chan_nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS,
+ &dimms, &ndimms) != 0) {
+ whinge(mod, NULL, "mc_channel_create_v1: No DIMMS provided");
+ return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
+ }
+
+ if (topo_node_range_create(mod, chnode, DIMM, 0, ndimms - 1) < 0) {
+ whinge(mod, NULL, "mc_channel_create_v1: dimm node range "
+ "create failed\n");
+ return (-1);
+ }
+
+ for (i = 0; i < ndimms; i++) {
+ if (mc_dimm_create_v1(mod, chnode, auth, dimms[i], i) != 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+mc_imc_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name,
+ nvlist_t *auth, nvlist_t *mc_nvl, uint_t id)
+{
+ int err, ret;
+ tnode_t *mcnode;
+ nvlist_t *fmri, **channels;
+ boolean_t ecc;
+ char *page, *cmode;
+ uint_t nchans, i;
+
+ if (mkrsrc(mod, pnode, name, id, auth, &fmri) != 0) {
+ whinge(mod, NULL, "mc_nb_create_v1: mkrsrc failed\n");
+ return (-1);
+ }
+
+ if ((mcnode = topo_node_bind(mod, pnode, name, id, fmri)) == NULL) {
+ whinge(mod, NULL, "mc_nb_create_v1: node bind failed"
+ " for memory-controller\n");
+ nvlist_free(fmri);
+ return (-1);
+ }
+
+ nvlist_free(fmri);
+ if (topo_node_fru_set(mcnode, NULL, 0, &err) != 0) {
+ whinge(mod, NULL, "mc_nb_create_v1: fru set failed: "
+ "%d\n", err);
+ return (topo_mod_seterrno(mod, err));
+ }
+
+ if (topo_pgroup_create(mcnode, &mc_pgroup, &err) != 0) {
+ whinge(mod, NULL, "mc_nb_create_v1: failed to create "
+ "property group: %d\n", err);
+ return (topo_mod_seterrno(mod, err));
+ }
+
+ /*
+ * Add properties to the controller. Our contract allows for these
+ * properties to be missing.
+ */
+ ret = 0;
+ if (nvlist_lookup_boolean_value(mc_nvl, MCINTEL_NVLIST_V1_MC_ECC,
+ &ecc) == 0) {
+ const char *pval = ecc ? "enabled" : "disabled";
+ ret |= topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_ECC,
+ TOPO_PROP_IMMUTABLE, pval, &err);
+ }
+
+ if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_POLICY,
+ &page) == 0) {
+ ret |= topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_POLICY,
+ TOPO_PROP_IMMUTABLE, page, &err);
+ }
+
+ if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE,
+ &cmode) != 0) {
+ cmode = NULL;
+ }
+
+ if (ret != 0)
+ return (-1);
+
+ if (nvlist_lookup_nvlist_array(mc_nvl, MCINTEL_NVLIST_V1_MC_CHANNELS,
+ &channels, &nchans) != 0) {
+ whinge(mod, NULL, "mc_imc_create_v1: missing channels entry");
+ return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
+ }
+
+ if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, 0,
+ nchans - 1) < 0) {
+ whinge(mod, NULL, "mc_imc_create_v1: channel node range create "
+ "failed\n");
+ return (-1);
+ }
+
+ for (i = 0; i < nchans; i++) {
+ if (mc_channel_create_v1(mod, mcnode, auth, channels[i], i,
+ cmode) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+mc_nb_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name,
+ nvlist_t *auth, nvlist_t *nvl)
+{
+ nvlist_t **mc_nvl;
+ uint_t nmc, i;
+
+ if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS,
+ &mc_nvl, &nmc) != 0) {
+ whinge(mod, NULL, "mc_nb_create_v1: failed to find memory "
+ "controller information\n");
+ return (-1);
+ }
+
+ if (topo_node_range_create(mod, pnode, name, 0, nmc - 1) < 0) {
+ whinge(mod, NULL,
+ "mc_nb_create: node range create failed\n");
+ return (-1);
+ }
+
+ for (i = 0; i < nmc; i++) {
+ if (mc_imc_create_v1(mod, pnode, name, auth, mc_nvl[i], i) != 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
int
mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
const char *name, nvlist_t *auth)
@@ -461,13 +787,18 @@ mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
whinge(mod, NULL, "mc nvlist is not versioned\n");
nvlist_free(nvl);
return (0);
- } else if (ver != MCINTEL_NVLIST_VERS0) {
+ } else if (ver != MCINTEL_NVLIST_VERS0 &&
+ ver != MCINTEL_NVLIST_VERS1) {
whinge(mod, NULL, "mc nvlist version mismatch\n");
nvlist_free(nvl);
return (0);
}
- rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl);
+ if (ver == MCINTEL_NVLIST_VERS1) {
+ rc = mc_nb_create_v1(mod, pnode, name, auth, nvl);
+ } else {
+ rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl);
+ }
nvlist_free(nvl);
return (rc);
diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile
index af6103f821..e7d933f9d3 100644
--- a/usr/src/man/man7d/Makefile
+++ b/usr/src/man/man7d/Makefile
@@ -209,6 +209,8 @@ i386_MANFILES= ahci.7d \
ecpp.7d \
elxl.7d \
i40e.7d \
+ imc.7d \
+ imcstub.7d \
ipmi.7d \
iprb.7d \
ipw.7d \
diff --git a/usr/src/man/man7d/imc.7d b/usr/src/man/man7d/imc.7d
new file mode 100644
index 0000000000..9389afd0f1
--- /dev/null
+++ b/usr/src/man/man7d/imc.7d
@@ -0,0 +1,103 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2019 Joyent, Inc.
+.\"
+.Dd June 25, 2019
+.Dt IMC 7D
+.Os
+.Sh NAME
+.Nm imc
+.Nd Intel memory controller driver
+.Sh SYNOPSIS
+.Pa /dev/mc/mc*
+.Sh DESCRIPTION
+The
+.Nm
+driver interfaces with the memory controller found on certain
+generations of Intel CPUs and provides a means for decoding physical
+addresses to the corresponding memory device.
+The
+.Nm
+driver plugs into the operating systems fault management framework
+providing additional details to the system about the memory topology and
+the ability to decode physical addresses into the corresponding portion
+of the memory hierarchy.
+.Pp
+The
+.Nm
+driver is supported on the following Intel processors:
+.Bl -bullet -offset indent -width Sy
+.It
+Sandy Bridge E5 and E7 Xeon Processors
+.It
+Ivy Bridge E5 and E7 Xeon Processors
+.It
+Haswell E5 and E7 Xeon Processors
+.It
+Broadwell E5 and E7 Xeon Processors
+.It
+Skylake Xeon Scalable Processors
+.It
+Cascade Lake Xeon Scalable Processors
+.It
+Broadwell and Skylake Xeon-D processors
+.El
+.Pp
+Other lines involving the above microarchitectures, such as Xeon E3
+branded processors, are not supported as they do not provide the
+necessary hardware support.
+.Pp
+The
+.Nm
+driver is a pseudo-device driver that amalgamates all of the different
+.Xr imcstub 7D
+instances into a coherent view.
+The
+.Xr imcstub 7D
+driver attaches to all of the different PCI devices that the processor
+exposes.
+.Pp
+One challenge with the
+.Nm
+driver is the Intel Enhanced Machine Check Architecture v2
+.Pq EMCAv2 .
+Many vendors use EMCAv2 to hide memory errors from the operating system.
+Such systems limit the effectiveness of the
+.Nm
+driver and the fault management architecture by hiding correctable and
+uncorrectable DIMM errors from the operating system.
+.Pp
+The
+.Nm
+driver has a few limitations.
+Currently it does not always properly handle lockstep and mirroring
+mode, particularly in variants that are common on Skylake and newer
+systems.
+It also does not properly handle cases where Intel Optane NVDIMMs are in
+use on the memory bus.
+.Sh ARCHITECTURE
+The
+.Nm
+driver is only supported on specific Intel
+.Sy x86
+systems.
+.Sh FILES
+.Bl -tag -width Pa
+.It Pa /platform/i86pc/kernel/drv/amd64/imc
+64-bit device driver (x86).
+.It Pa /platform/i86pc/kernel/drv/imc.conf
+Driver configuration file.
+.El
+.Sh SEE ALSO
+.Xr fmadm 1M ,
+.Xr fmdump 1M ,
+.Xr imcstub 7D
diff --git a/usr/src/man/man7d/imcstub.7d b/usr/src/man/man7d/imcstub.7d
new file mode 100644
index 0000000000..d4f69c3b7a
--- /dev/null
+++ b/usr/src/man/man7d/imcstub.7d
@@ -0,0 +1,46 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2019 Joyent, Inc.
+.\"
+.Dd June 25, 2019
+.Dt IMCSTUB 7D
+.Os
+.Sh NAME
+.Nm imcstub
+.Nd Intel memory controller stub driver
+.Sh DESCRIPTION
+The
+.Nm
+driver is a counterpart to the
+.Xr imc 7D
+driver.
+The
+.Nm
+driver is responsible for attaching to all of the logical PCI devices
+that are exposed by Intel CPUs and providing them for use by the
+.Xr imc 7D
+driver.
+For more information on how this is used by the system, please see
+.Xr imc 7D .
+.Sh ARCHITECTURE
+The
+.Nm
+driver is only supported on certain Intel
+.Sy x86
+systems.
+.Sh FILES
+.Bl -tag -width Pa
+.It Pa /platform/i86pc/kernel/drv/amd64/imcstub
+64-bit device driver (x86).
+.El
+.Sh SEE ALSO
+.Xr imc 7D
diff --git a/usr/src/test/os-tests/runfiles/default.run b/usr/src/test/os-tests/runfiles/default.run
index 974829b1f6..232688ccce 100644
--- a/usr/src/test/os-tests/runfiles/default.run
+++ b/usr/src/test/os-tests/runfiles/default.run
@@ -78,3 +78,6 @@ tests = ['acquire-compare', 'acquire-spray']
user = root
arch = i86pc
tests = ['ldt', 'badseg']
+
+[/opt/os-tests/tests/imc_test]
+arch = i86pc
diff --git a/usr/src/test/os-tests/tests/Makefile b/usr/src/test/os-tests/tests/Makefile
index ac5066e43c..e3e756c946 100644
--- a/usr/src/test/os-tests/tests/Makefile
+++ b/usr/src/test/os-tests/tests/Makefile
@@ -14,7 +14,7 @@
# Copyright 2019 Joyent, Inc.
#
-SUBDIRS_i386 = i386
+SUBDIRS_i386 = i386 imc
SUBDIRS = \
ddi_ufm \
diff --git a/usr/src/test/os-tests/tests/imc/Makefile b/usr/src/test/os-tests/tests/imc/Makefile
new file mode 100644
index 0000000000..d62c8048f1
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/Makefile
@@ -0,0 +1,72 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+ROOTOPTPKG = $(ROOT)/opt/os-tests
+TESTDIR = $(ROOTOPTPKG)/tests
+
+#
+# Test objects
+#
+OBJS = imc_test.o \
+ imc_test_basic.o \
+ imc_test_badaddr.o \
+ imc_test_fail.o \
+ imc_test_rir.o \
+ imc_test_sad.o \
+ imc_test_skx_loop.o \
+ imc_test_tad.o
+
+#
+# Common objects that we need.
+#
+OBJS += imc_decode.o
+
+PROG = imc_test
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+include $(SRC)/cmd/Makefile.ctf
+
+CPPFLAGS += -I$(SRC)/uts/i86pc/io/imc
+
+CMDS = $(PROG:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+all: $(PROG)
+
+install: all $(CMDS)
+
+clobber: clean
+ -$(RM) $(PROG)
+
+clean:
+ -$(RM) *.o
+
+$(CMDS): $(TESTDIR) $(PROG)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+%.o: $(SRC)/common/mc/imc/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/test/os-tests/tests/imc/imc_test.c b/usr/src/test/os-tests/tests/imc/imc_test.c
new file mode 100644
index 0000000000..7a916fc90f
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test.c
@@ -0,0 +1,353 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <err.h>
+
+#include "imc_test.h"
+
+/*
+ * Test runner for the IMC driver and its decoder. This operates by creating
+ * fake topologies and then building a copy of the decoder into this.
+ */
+
+static void
+imc_print(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+static const char *
+imc_test_strerror(imc_decode_failure_t fail)
+{
+ switch (fail) {
+ case IMC_DECODE_F_NONE:
+ return ("Actually succeeded");
+ case IMC_DECODE_F_LEGACY_RANGE:
+ return ("Asked to decode legacy address");
+ case IMC_DECODE_F_BAD_SOCKET:
+ return ("BAD socket data");
+ case IMC_DECODE_F_BAD_SAD:
+ return ("BAD SAD data");
+ case IMC_DECODE_F_OUTSIDE_DRAM:
+ return ("Address not DRAM");
+ case IMC_DECODE_F_NO_SAD_RULE:
+ return ("No valid SAD rule");
+ case IMC_DECODE_F_BAD_SAD_INTERLEAVE:
+ return ("SAD bad interleave target");
+ case IMC_DECODE_F_BAD_REMOTE_MC_ROUTE:
+ return ("SAD MC_ROUTE refers to non-existent socket");
+ case IMC_DECODE_F_SAD_SEARCH_LOOP:
+ return ("SAD search looped");
+ case IMC_DECODE_F_SAD_BAD_MOD:
+ return ("SAD has a bad mod rule");
+ case IMC_DECODE_F_SAD_BAD_SOCKET:
+ return ("SAD has a bad Socket target");
+ case IMC_DECODE_F_SAD_BAD_TAD:
+ return ("SAD has a bad TAD target");
+ case IMC_DECODE_F_NO_TAD_RULE:
+ return ("No valid TAD rule");
+ case IMC_DECODE_F_TAD_3_ILEAVE:
+ return ("Unsupported 3-way channel interleave");
+ case IMC_DECODE_F_TAD_BAD_TARGET_INDEX:
+ return ("Bad TAD target index");
+ case IMC_DECODE_F_BAD_CHANNEL_ID:
+ return ("Bad channel ID");
+ case IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET:
+ return ("Bad channel tad offset");
+ case IMC_DECODE_F_NO_RIR_RULE:
+ return ("No valid rank interleave rule");
+ case IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET:
+ return ("Bad rank interleave target");
+ case IMC_DECODE_F_BAD_DIMM_INDEX:
+ return ("Bad DIMM target index");
+ case IMC_DECODE_F_DIMM_NOT_PRESENT:
+ return ("DIMM not present");
+ case IMC_DECODE_F_BAD_DIMM_RANK:
+ return ("Bad DIMM rank");
+ case IMC_DECODE_F_CHANOFF_UNDERFLOW:
+ return ("Channel address offset calculation underflow");
+ case IMC_DECODE_F_RANKOFF_UNDERFLOW:
+ return ("Rank address offset calculation underflow");
+ default:
+ return ("<unknown>");
+ }
+}
+
+static const char *
+imc_test_strenum(imc_decode_failure_t fail)
+{
+ switch (fail) {
+ case IMC_DECODE_F_NONE:
+ return ("IMC_DECODE_F_NONE");
+ case IMC_DECODE_F_LEGACY_RANGE:
+ return ("IMC_DECODE_F_LEGACY_RANGE");
+ case IMC_DECODE_F_BAD_SOCKET:
+ return ("IMC_DECODE_F_BAD_SOCKET");
+ case IMC_DECODE_F_BAD_SAD:
+ return ("IMC_DECODE_F_BAD_SAD");
+ case IMC_DECODE_F_OUTSIDE_DRAM:
+ return ("IMC_DECODE_F_OUTSIDE_DRAM");
+ case IMC_DECODE_F_NO_SAD_RULE:
+ return ("IMC_DECODE_F_NO_SAD_RULE");
+ case IMC_DECODE_F_BAD_SAD_INTERLEAVE:
+ return ("IMC_DECODE_F_BAD_SAD_INTERLEAVE");
+ case IMC_DECODE_F_BAD_REMOTE_MC_ROUTE:
+ return ("IMC_DECODE_F_BAD_REMOTE_MC_ROUTE");
+ case IMC_DECODE_F_SAD_SEARCH_LOOP:
+ return ("IMC_DECODE_F_SAD_SEARCH_LOOP");
+ case IMC_DECODE_F_SAD_BAD_MOD:
+ return ("IMC_DECODE_F_SAD_BAD_MOD");
+ case IMC_DECODE_F_SAD_BAD_SOCKET:
+ return ("IMC_DECODE_F_SAD_BAD_SOCKET");
+ case IMC_DECODE_F_SAD_BAD_TAD:
+ return ("IMC_DECODE_F_SAD_BAD_TAD");
+ case IMC_DECODE_F_NO_TAD_RULE:
+ return ("IMC_DECODE_F_NO_TAD_RULE");
+ case IMC_DECODE_F_TAD_3_ILEAVE:
+ return ("IMC_DECODE_F_TAD_3_ILEAVE");
+ case IMC_DECODE_F_TAD_BAD_TARGET_INDEX:
+ return ("IMC_DECODE_F_TAD_BAD_TARGET_INDEX");
+ case IMC_DECODE_F_BAD_CHANNEL_ID:
+ return ("IMC_DECODE_F_BAD_CHANNEL_ID");
+ case IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET:
+ return ("IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET");
+ case IMC_DECODE_F_NO_RIR_RULE:
+ return ("IMC_DECODE_F_NO_RIR_RULE");
+ case IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET:
+ return ("IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET");
+ case IMC_DECODE_F_BAD_DIMM_INDEX:
+ return ("IMC_DECODE_F_BAD_DIMM_INDEX");
+ case IMC_DECODE_F_DIMM_NOT_PRESENT:
+ return ("IMC_DECODE_F_DIMM_NOT_PRESENT");
+ case IMC_DECODE_F_BAD_DIMM_RANK:
+ return ("IMC_DECODE_F_BAD_DIMM_RANK");
+ case IMC_DECODE_F_CHANOFF_UNDERFLOW:
+ return ("IMC_DECODE_F_CHANOFF_UNDERFLOW");
+ case IMC_DECODE_F_RANKOFF_UNDERFLOW:
+ return ("IMC_DECODE_F_RANKOFF_UNDERFLOW");
+ default:
+ return ("<unknown>");
+ }
+}
+
+static uint_t
+imc_test_run_one(const imc_test_case_t *test)
+{
+ imc_decode_state_t dec;
+ boolean_t pass;
+
+ imc_print("Running test: %s\n", test->itc_desc);
+ imc_print("\tDecoding address: 0x%" PRIx64 "\n", test->itc_pa);
+
+ (void) memset(&dec, '\0', sizeof (dec));
+ pass = imc_decode_pa(test->itc_imc, test->itc_pa, &dec);
+ if (pass && !test->itc_pass) {
+ imc_print("\tdecode unexpectedly succeeded\n");
+ imc_print("\texpected error '%s' (%s/0x%x)\n",
+ imc_test_strerror(test->itc_fail),
+ imc_test_strenum(test->itc_fail),
+ test->itc_fail);
+ imc_print("\t\tdecoded socket: %u\n", dec.ids_nodeid);
+ imc_print("\t\tdecoded tad: %u\n", dec.ids_tadid);
+ imc_print("\t\tdecoded channel: %u\n",
+ dec.ids_channelid);
+ imc_print("\t\tdecoded channel address: 0x%" PRIx64 "\n",
+ dec.ids_chanaddr);
+ imc_print("\t\tdecoded rank: %u\n", dec.ids_rankid);
+ imc_print("\t\tdecoded rank address: 0x%" PRIx64 "\n",
+ dec.ids_rankaddr);
+ imc_print("\ttest failed\n");
+
+ return (1);
+ } else if (pass) {
+ uint_t err = 0;
+
+ if (test->itc_nodeid != UINT32_MAX &&
+ test->itc_nodeid != dec.ids_nodeid) {
+ imc_print("\tsocket mismatch\n"
+ "\t\texpected %u\n\t\tfound %u\n",
+ test->itc_nodeid, dec.ids_nodeid);
+ err |= 1;
+ }
+
+ if (test->itc_tadid != UINT32_MAX &&
+ test->itc_tadid != dec.ids_tadid) {
+ imc_print("\tTAD mismatch\n"
+ "\t\texpected %u\n\t\tfound %u\n",
+ test->itc_tadid, dec.ids_tadid);
+ err |= 1;
+ }
+
+ if (test->itc_channelid != UINT32_MAX &&
+ test->itc_channelid != dec.ids_channelid) {
+ imc_print("\tchannel mismatch\n"
+ "\t\texpected %u\n\t\tfound %u\n",
+ test->itc_channelid, dec.ids_channelid);
+ err |= 1;
+ }
+
+ if (test->itc_chanaddr != UINT64_MAX &&
+ test->itc_chanaddr != dec.ids_chanaddr) {
+ imc_print("\tchannel address mismatch\n"
+ "\t\texpected 0x%" PRIx64 "\n\t\t"
+ "found 0x%" PRIx64 "\n",
+ test->itc_chanaddr, dec.ids_chanaddr);
+ err |= 1;
+ }
+
+ if (test->itc_dimmid != UINT32_MAX &&
+ test->itc_dimmid != dec.ids_dimmid) {
+ imc_print("\tDIMM mismatch\n"
+ "\t\texpected %u\n\t\tfound %u\n",
+ test->itc_dimmid, dec.ids_dimmid);
+ err |= 1;
+ }
+
+ if (test->itc_rankid != UINT32_MAX &&
+ test->itc_rankid != dec.ids_rankid) {
+ imc_print("\trank mismatch\n"
+ "\t\texpected %u\n\t\tfound %u\n",
+ test->itc_rankid, dec.ids_rankid);
+ err |= 1;
+ }
+
+ if (test->itc_rankaddr != UINT64_MAX &&
+ test->itc_rankaddr != dec.ids_rankaddr) {
+ imc_print("\trank address mismatch\n"
+ "\t\texpected 0x%" PRIx64 "\n\t\t"
+ "found 0x%" PRIx64 "\n",
+ test->itc_rankaddr, dec.ids_rankaddr);
+ err |= 1;
+ }
+
+ if (err) {
+ imc_print("\tDecoding failed\n");
+ } else {
+ imc_print("\tDecoded successfully\n");
+ }
+
+ return (err);
+ } else if (!pass && !test->itc_pass) {
+ if (dec.ids_fail != test->itc_fail) {
+ imc_print("\terror mismatch\n"
+ "\t\texpected '%s' (%s/0x%x)\n\t\tfound '%s' "
+ "(%s/0x%x)\n", imc_test_strerror(test->itc_fail),
+ imc_test_strenum(test->itc_fail), test->itc_fail,
+ imc_test_strerror(dec.ids_fail),
+ imc_test_strenum(dec.ids_fail), dec.ids_fail);
+ return (1);
+ }
+
+ imc_print("\tCorrect decoding error generated\n");
+ return (0);
+ } else {
+ imc_print("\tdecode failed with '%s' (%s/0x%x)\n",
+ imc_test_strerror(dec.ids_fail),
+ imc_test_strenum(dec.ids_fail),
+ dec.ids_fail);
+ if (test->itc_nodeid != UINT32_MAX) {
+ imc_print("\t\texpected socket: %u\n",
+ test->itc_nodeid);
+ }
+
+ if (test->itc_tadid != UINT32_MAX) {
+ imc_print("\t\texpected tad: %u\n", test->itc_tadid);
+ }
+
+ if (test->itc_channelid != UINT32_MAX) {
+ imc_print("\t\texpected channel: %u\n",
+ test->itc_channelid);
+ }
+
+ if (test->itc_chanaddr != UINT64_MAX) {
+ imc_print("\t\texpected channel address: 0x%" PRIx64
+ "\n", test->itc_chanaddr);
+ }
+
+ if (test->itc_rankid != UINT32_MAX) {
+ imc_print("\t\texpected rank: %u\n",
+ test->itc_rankid);
+ }
+
+ if (test->itc_rankaddr != UINT64_MAX) {
+ imc_print("\t\texpected rank address: 0x%" PRIx64 "\n",
+ test->itc_rankaddr);
+ }
+
+ imc_print("\tdecode failed, expected pass\n");
+
+ return (1);
+ }
+}
+
+static void
+imc_test_run(const imc_test_case_t *tests, uint_t *ntests, uint_t *nfail)
+{
+ while (tests[0].itc_desc != NULL) {
+ *nfail += imc_test_run_one(tests);
+ *ntests += 1;
+ tests++;
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ uint_t ntests = 0, nfail = 0;
+ int i;
+
+ if (argc > 1) {
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "basic") == 0) {
+ imc_test_run(imc_test_basics, &ntests, &nfail);
+ } else if (strcmp(argv[i], "badaddr") == 0) {
+ imc_test_run(imc_test_badaddr, &ntests, &nfail);
+ } else if (strcmp(argv[i], "sad") == 0) {
+ imc_test_run(imc_test_sad, &ntests, &nfail);
+ } else if (strcmp(argv[i], "skx_loop") == 0) {
+ imc_test_run(imc_test_skx_loop, &ntests,
+ &nfail);
+ } else if (strcmp(argv[i], "tad") == 0) {
+ imc_test_run(imc_test_tad, &ntests, &nfail);
+ } else if (strcmp(argv[i], "rir") == 0) {
+ imc_test_run(imc_test_rir, &ntests, &nfail);
+ } else if (strcmp(argv[i], "fail") == 0) {
+ imc_test_run(imc_test_fail, &ntests, &nfail);
+ } else {
+ errx(EXIT_FAILURE, "Unknown test argument %s",
+ argv[i]);
+ }
+ }
+ } else {
+ imc_test_run(imc_test_basics, &ntests, &nfail);
+ imc_test_run(imc_test_badaddr, &ntests, &nfail);
+ imc_test_run(imc_test_skx_loop, &ntests, &nfail);
+ imc_test_run(imc_test_rir, &ntests, &nfail);
+ imc_test_run(imc_test_tad, &ntests, &nfail);
+ imc_test_run(imc_test_sad, &ntests, &nfail);
+ imc_test_run(imc_test_fail, &ntests, &nfail);
+ }
+
+ imc_print("%u/%u tests passed\n", ntests - nfail, ntests);
+ return (nfail > 0);
+}
diff --git a/usr/src/test/os-tests/tests/imc/imc_test.h b/usr/src/test/os-tests/tests/imc/imc_test.h
new file mode 100644
index 0000000000..d1785dd837
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test.h
@@ -0,0 +1,68 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#ifndef _IMC_TEST_H
+#define _IMC_TEST_H
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "imc.h"
+
+/*
+ * Standard interfaces for the IMC test files.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct imc_test_case {
+ const char *itc_desc;
+ uint64_t itc_pa;
+ const imc_t *itc_imc;
+ boolean_t itc_pass;
+ imc_decode_failure_t itc_fail;
+ /*
+ * These will all be checked on the success case unless set to the
+ * respective UINTXX_MAX value.
+ */
+ uint32_t itc_nodeid;
+ uint32_t itc_tadid;
+ uint32_t itc_channelid;
+ uint64_t itc_chanaddr;
+ uint32_t itc_dimmid;
+ uint32_t itc_rankid;
+ uint64_t itc_rankaddr;
+} imc_test_case_t;
+
+/*
+ * Arrays of tests cases that exist. They are terminated with a NULL itc_desc
+ * member.
+ */
+extern const imc_test_case_t imc_test_basics[];
+extern const imc_test_case_t imc_test_badaddr[];
+extern const imc_test_case_t imc_test_fail[];
+extern const imc_test_case_t imc_test_rir[];
+extern const imc_test_case_t imc_test_sad[];
+extern const imc_test_case_t imc_test_skx_loop[];
+extern const imc_test_case_t imc_test_tad[];
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IMC_TEST_H */
diff --git a/usr/src/test/os-tests/tests/imc/imc_test_badaddr.c b/usr/src/test/os-tests/tests/imc/imc_test_badaddr.c
new file mode 100644
index 0000000000..b45b3b4773
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test_badaddr.c
@@ -0,0 +1,230 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include "imc_test.h"
+
+/*
+ * This file does a bunch of tests to make sure that we correctly handle cases
+ * where we're asked to decode the following types of addresses:
+ *
+ * - Legacy Reserved Addresses
+ * - Between TOLM, TOHM
+ * - Above TOHM
+ */
+
+/*
+ * This IMC represents a basic case where we have a single 8 GiB dual rank DIMM.
+ * We have system memory in the lower 2 GiB and then the remaining 6 GiB starts
+ * at the bottom of high memory (4 GiB).
+ */
+static const imc_t imc_badaddr = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+const imc_test_case_t imc_test_badaddr[] = { {
+ .itc_desc = "Bad Address, legacy VGA (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xa0000,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, legacy VGA (2)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xbffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, legacy VGA (3)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xafc89,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, legacy PAM (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xc0000,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, legacy PAM (2)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xfffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, Reserved (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xffffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, Reserved (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xffffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, System (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x00fe000000,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, System (2)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x00fe123446,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, System (3)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x00ff000000,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, System (4)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x00ffffffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Bad Address, System (5)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x00ff5abc32,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_LEGACY_RANGE
+}, {
+ .itc_desc = "Outside TOLM (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x80000000,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_OUTSIDE_DRAM
+}, {
+ .itc_desc = "Outside TOLM (2)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xF0000000,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_OUTSIDE_DRAM
+}, {
+ .itc_desc = "Outside TOLM (3)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0xfdffffffULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_OUTSIDE_DRAM
+}, {
+ .itc_desc = "Outside TOHM (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x280000000ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_OUTSIDE_DRAM
+}, {
+ .itc_desc = "Outside TOHM (2)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = UINT64_MAX,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_OUTSIDE_DRAM
+}, {
+ .itc_desc = "Outside TOHM (1)",
+ .itc_imc = &imc_badaddr,
+ .itc_pa = 0x1280000000ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_OUTSIDE_DRAM
+}, {
+ .itc_desc = NULL
+} };
diff --git a/usr/src/test/os-tests/tests/imc/imc_test_basic.c b/usr/src/test/os-tests/tests/imc/imc_test_basic.c
new file mode 100644
index 0000000000..b65506ead1
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test_basic.c
@@ -0,0 +1,132 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include "imc_test.h"
+
+/*
+ * This represents a basic configuration with a single socket, channel, and
+ * DIMM that is 2 GiB in size. This entirely punts on the fact that the legacy
+ * ranges overlap here.
+ */
+static const imc_t imc_basic_snb = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_TRUE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x80000000,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+const imc_test_case_t imc_test_basics[] = { {
+ .itc_desc = "decode basic single socket/channel/DIMM, dual rank (1)",
+ .itc_imc = &imc_basic_snb,
+ .itc_pa = 0x0,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "decode basic single socket/channel/DIMM, dual rank (2)",
+ .itc_imc = &imc_basic_snb,
+ .itc_pa = 0x1000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+}, {
+ .itc_desc = "decode basic single socket/channel/DIMM, dual rank (3)",
+ .itc_imc = &imc_basic_snb,
+ .itc_pa = 0x7fffffff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x7fffffff,
+ .itc_dimmid = 0,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0x3fffffff,
+}, {
+ .itc_desc = NULL
+} };
diff --git a/usr/src/test/os-tests/tests/imc/imc_test_fail.c b/usr/src/test/os-tests/tests/imc/imc_test_fail.c
new file mode 100644
index 0000000000..ef0cb003a2
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test_fail.c
@@ -0,0 +1,1494 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include "imc_test.h"
+
+/*
+ * This file tests several different miscellaneous failure modes by using
+ * incomplete imc_t and imc_t with bad data.
+ */
+
+/*
+ * This IMC is a nominally valid IMC; however, it has flags indicate that the
+ * socket has bad data.
+ */
+static const imc_t imc_badsock = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_BAD_NODEID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_invalid_sad = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = 0,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_BAD_DRAM_ATTR,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ }
+ }
+};
+
+static const imc_t imc_invalid_sad_rule = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = 0,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x34,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x42,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ }
+ }
+};
+
+static const imc_t imc_invalid_sad_interleave = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = 0,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = 0
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = 0
+ }
+ }
+ }
+};
+
+static const imc_t imc_invalid_sad_target = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = 0,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 9, 9, 9, 9, 9, 9, 9, 9 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_tad_rule = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x2,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x277777777ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_tad_3way = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 3,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 3,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_tad_target = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 0,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 0,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_tad_channelid = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 17, 23, 42, 167 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 17, 23, 42, 167 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_channel_offset = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 0,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_rir_rule = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x1,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_rir_ileave = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 0
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_dimm_index = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x23, 0x0 },
+ .irle_entries[1] = { 0x42, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_missing_dimm = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_FALSE
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_bad_rank_index = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x2, 0x0 },
+ .irle_entries[1] = { 0x3, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_chanoff_underflow = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL,
+ 0x1000000000ULL
+ },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_riroff_underflow = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000, /* 2 GiB */
+ .isad_tohm = 0x280000000ULL, /* 10 GiB */
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x80000000,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x80000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x200000000ULL,
+ .irle_nentries = 5,
+ .irle_entries[0] = { 0x0,
+ 0x100000000000ULL },
+ .irle_entries[1] = { 0x1,
+ 0x100000000000ULL }
+ }
+ }
+ }
+ }
+};
+
+const imc_test_case_t imc_test_fail[] = { {
+ .itc_desc = "Bad Socket data (1)",
+ .itc_imc = &imc_badsock,
+ .itc_pa = 0x34,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SOCKET
+}, {
+ .itc_desc = "Bad Socket data (2)",
+ .itc_imc = &imc_badsock,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SOCKET
+}, {
+ .itc_desc = "Bad Socket data (3)",
+ .itc_imc = &imc_badsock,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SOCKET
+}, {
+ .itc_desc = "Bad SAD data (1)",
+ .itc_imc = &imc_invalid_sad,
+ .itc_pa = 0x34,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SAD
+}, {
+ .itc_desc = "Bad SAD data (2)",
+ .itc_imc = &imc_invalid_sad,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SAD
+}, {
+ .itc_desc = "Bad SAD data (3)",
+ .itc_imc = &imc_invalid_sad,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SAD
+}, {
+ .itc_desc = "Bad SAD rule (1)",
+ .itc_imc = &imc_invalid_sad_rule,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_SAD_RULE
+}, {
+ .itc_desc = "Bad SAD rule (2)",
+ .itc_imc = &imc_invalid_sad_rule,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_SAD_RULE
+}, {
+ .itc_desc = "Bad SAD rule (3)",
+ .itc_imc = &imc_invalid_sad_rule,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_SAD_RULE
+}, {
+ .itc_desc = "Bad SAD interleave (1)",
+ .itc_imc = &imc_invalid_sad_interleave,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SAD_INTERLEAVE
+}, {
+ .itc_desc = "Bad SAD interleave (2)",
+ .itc_imc = &imc_invalid_sad_interleave,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SAD_INTERLEAVE
+}, {
+ .itc_desc = "Bad SAD interleave (3)",
+ .itc_imc = &imc_invalid_sad_interleave,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_SAD_INTERLEAVE
+}, {
+ .itc_desc = "Bad SAD TAD target (1)",
+ .itc_imc = &imc_invalid_sad_target,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_SAD_BAD_SOCKET
+}, {
+ .itc_desc = "Bad SAD TAD target (2)",
+ .itc_imc = &imc_invalid_sad_target,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_SAD_BAD_SOCKET
+}, {
+ .itc_desc = "Bad SAD TAD target (3)",
+ .itc_imc = &imc_invalid_sad_target,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_SAD_BAD_TAD
+}, {
+ .itc_desc = "Bad TAD rule (1)",
+ .itc_imc = &imc_bad_tad_rule,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_TAD_RULE
+}, {
+ .itc_desc = "Bad TAD rule (2)",
+ .itc_imc = &imc_bad_tad_rule,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_TAD_RULE
+}, {
+ .itc_desc = "Bad TAD rule (3)",
+ .itc_imc = &imc_bad_tad_rule,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_TAD_RULE
+}, {
+ .itc_desc = "Unsupported 3 way interleave (1)",
+ .itc_imc = &imc_bad_tad_3way,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_TAD_3_ILEAVE
+}, {
+ .itc_desc = "Unsupported 3 way interleave (2)",
+ .itc_imc = &imc_bad_tad_3way,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_TAD_3_ILEAVE
+}, {
+ .itc_desc = "Unsupported 3 way interleave (3)",
+ .itc_imc = &imc_bad_tad_3way,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_TAD_3_ILEAVE
+}, {
+ .itc_desc = "Bad TAD target index (1)",
+ .itc_imc = &imc_bad_tad_target,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_TAD_BAD_TARGET_INDEX
+}, {
+ .itc_desc = "Bad TAD target index (2)",
+ .itc_imc = &imc_bad_tad_target,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_TAD_BAD_TARGET_INDEX
+}, {
+ .itc_desc = "Bad TAD target index (3)",
+ .itc_imc = &imc_bad_tad_target,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_TAD_BAD_TARGET_INDEX
+}, {
+ .itc_desc = "Bad TAD target channel (1)",
+ .itc_imc = &imc_bad_tad_channelid,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_CHANNEL_ID
+}, {
+ .itc_desc = "Bad TAD target channel (2)",
+ .itc_imc = &imc_bad_tad_channelid,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_CHANNEL_ID
+}, {
+ .itc_desc = "Bad TAD target channel (3)",
+ .itc_imc = &imc_bad_tad_channelid,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_CHANNEL_ID
+}, {
+ .itc_desc = "Bad channel offset target (1)",
+ .itc_imc = &imc_bad_channel_offset,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET
+}, {
+ .itc_desc = "Bad channel offset target (2)",
+ .itc_imc = &imc_bad_channel_offset,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET
+}, {
+ .itc_desc = "Bad channel offset target (3)",
+ .itc_imc = &imc_bad_channel_offset,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET
+}, {
+ .itc_desc = "Bad RIR rule (1)",
+ .itc_imc = &imc_bad_rir_rule,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_RIR_RULE
+}, {
+ .itc_desc = "Bad RIR rule (2)",
+ .itc_imc = &imc_bad_rir_rule,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_NO_RIR_RULE
+}, {
+ .itc_desc = "Bad RIR rule (3)",
+ .itc_pa = 0x123456789ULL,
+ .itc_imc = &imc_bad_rir_rule,
+ .itc_fail = IMC_DECODE_F_NO_RIR_RULE
+}, {
+ .itc_desc = "Bad RIR interleave target (1)",
+ .itc_imc = &imc_bad_rir_ileave,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET
+}, {
+ .itc_desc = "Bad RIR interleave target (2)",
+ .itc_imc = &imc_bad_rir_ileave,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET
+}, {
+ .itc_desc = "Bad RIR interleave target (3)",
+ .itc_imc = &imc_bad_rir_ileave,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET
+}, {
+ .itc_desc = "Bad RIR DIMM target (1)",
+ .itc_imc = &imc_bad_dimm_index,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_DIMM_INDEX
+}, {
+ .itc_desc = "Bad RIR DIMM target (2)",
+ .itc_imc = &imc_bad_dimm_index,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_DIMM_INDEX
+}, {
+ .itc_desc = "Bad RIR DIMM target (3)",
+ .itc_imc = &imc_bad_dimm_index,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_DIMM_INDEX
+}, {
+ .itc_desc = "Bad RIR DIMM target (1)",
+ .itc_imc = &imc_missing_dimm,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_DIMM_NOT_PRESENT
+}, {
+ .itc_desc = "Bad RIR DIMM target (2)",
+ .itc_imc = &imc_missing_dimm,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_DIMM_NOT_PRESENT
+}, {
+ .itc_desc = "Bad RIR DIMM target (3)",
+ .itc_imc = &imc_missing_dimm,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_DIMM_NOT_PRESENT
+}, {
+ .itc_desc = "Bad RIR rank target (1)",
+ .itc_imc = &imc_bad_rank_index,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_DIMM_RANK
+}, {
+ .itc_desc = "Bad RIR rank target (2)",
+ .itc_imc = &imc_bad_rank_index,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_DIMM_RANK
+}, {
+ .itc_desc = "Bad RIR rank target (3)",
+ .itc_imc = &imc_bad_rank_index,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_DIMM_RANK
+}, {
+ .itc_desc = "Bad channel offset underflow (1)",
+ .itc_imc = &imc_chanoff_underflow,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_CHANOFF_UNDERFLOW
+}, {
+ .itc_desc = "Bad channel offset underflow (2)",
+ .itc_imc = &imc_chanoff_underflow,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_CHANOFF_UNDERFLOW
+}, {
+ .itc_desc = "Bad channel offset underflow (3)",
+ .itc_imc = &imc_chanoff_underflow,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_CHANOFF_UNDERFLOW
+}, {
+ .itc_desc = "Bad rank offset underflow (1)",
+ .itc_imc = &imc_riroff_underflow,
+ .itc_pa = 0x45,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_RANKOFF_UNDERFLOW
+}, {
+ .itc_desc = "Bad rank offset underflow (2)",
+ .itc_imc = &imc_riroff_underflow,
+ .itc_pa = 0x7fffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_RANKOFF_UNDERFLOW
+}, {
+ .itc_desc = "Bad rank offset underflow (3)",
+ .itc_imc = &imc_riroff_underflow,
+ .itc_pa = 0x123456789ULL,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_RANKOFF_UNDERFLOW
+}, {
+ .itc_desc = NULL
+} };
diff --git a/usr/src/test/os-tests/tests/imc/imc_test_rir.c b/usr/src/test/os-tests/tests/imc/imc_test_rir.c
new file mode 100644
index 0000000000..8feedc690d
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test_rir.c
@@ -0,0 +1,856 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include "imc_test.h"
+
+/*
+ * Test various aspects of RIR decoding and rank interleaving.
+ *
+ * The first test series uses imc_rir_8w_4r_closed which basically tests our
+ * rank interleaving across a single DIMM/channel in a closed page
+ * configuration. Technically such a configuration has aliasing, so it
+ * shouldn't be used in the wild. This is to validate that we're doing
+ * interleaving with a single rule across closed pages.
+ *
+ * The second test set, imc_rir_4w_4r_open is similar; however, it uses open
+ * pages instead.
+ *
+ * The third test set, imc_rir_8w_4r_2dpc, is used to make sure that we can
+ * properly perform interleaving across two DIMMs in a single channel
+ * configuration.
+ *
+ * The fourth test set, imc_rir_2w_1r_3dpc, is used to make sure that we can use
+ * multiple rank interleaving rules to point us to different parts of a DIMM on
+ * a single channel.
+ */
+
+static const imc_t imc_rir_8w_4r_closed = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_TRUE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 4,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 8,
+ .irle_nwaysbits = 3,
+ .irle_limit = 0x80000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x3, 0x0 },
+ .irle_entries[1] = { 0x2, 0x0 },
+ .irle_entries[2] = { 0x1, 0x0 },
+ .irle_entries[3] = { 0x0, 0x0 },
+ .irle_entries[4] = { 0x2, 0x0 },
+ .irle_entries[5] = { 0x3, 0x0 },
+ .irle_entries[6] = { 0x0, 0x0 },
+ .irle_entries[7] = { 0x1, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_rir_4w_4r_open = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 8,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 4,
+ .irle_nwaysbits = 2,
+ .irle_limit = 0x80000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x2, 0x0 },
+ .irle_entries[1] = { 0x0, 0x0 },
+ .irle_entries[2] = { 0x3, 0x0 },
+ .irle_entries[3] = { 0x1, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_rir_8w_4r_2dpc = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_TRUE,
+ .icn_channels[0] = {
+ .ich_ndimms = 2,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 4,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_dimms[1] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 4,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 8,
+ .irle_nwaysbits = 3,
+ .irle_limit = 0x100000000ULL,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ .irle_entries[1] = { 0x4, 0x0 },
+ .irle_entries[2] = { 0x1, 0x0 },
+ .irle_entries[3] = { 0x5, 0x0 },
+ .irle_entries[4] = { 0x2, 0x0 },
+ .irle_entries[5] = { 0x6, 0x0 },
+ .irle_entries[6] = { 0x3, 0x0 },
+ .irle_entries[7] = { 0x7, 0x0 }
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_rir_2w_1r_3dpc = {
+ .imc_gen = IMC_GEN_HASWELL,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x180000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x180000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x180000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_TRUE,
+ .icn_channels[0] = {
+ .ich_ndimms = 3,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 1,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_dimms[1] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 1,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_dimms[2] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 1,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x80000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x80000000,
+ .irle_nentries = 2,
+ .irle_entries[0] = { 0x4, 0x0 },
+ .irle_entries[1] = { 0x0, 0x0 },
+ },
+ .ich_rankileaves[1] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x100000000ULL,
+ .irle_nentries = 2,
+ .irle_entries[0] = { 0x8, 0x40000000 },
+ .irle_entries[1] = { 0x4, 0x0 },
+ },
+ .ich_rankileaves[2] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 2,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x180000000ULL,
+ .irle_nentries = 2,
+ .irle_entries[0] = { 0x8, 0x40000000 },
+ .irle_entries[1] = { 0x0, 0x40000000 },
+ }
+ }
+ }
+ }
+};
+
+
+const imc_test_case_t imc_test_rir[] = { {
+ .itc_desc = "RIR target 0, 8-way/4-rank, closed (1)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x0,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "RIR target 1, 8-way/4-rank, closed",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x40,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x40,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "RIR target 2, 8-way/4-rank, closed",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x80,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x80,
+ .itc_dimmid = 0,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "RIR target 3, 8-way/4-rank, closed",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0xc0,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xc0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "RIR target 4, 8-way/4-rank, closed",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x100,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x100,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "RIR target 5, 8-way/4-rank, closed",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x140,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "RIR target 6, 8-way/4-rank, closed",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x180,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x180,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "RIR target 7, 8-way/4-rank, closed",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x1c0,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1c0,
+ .itc_dimmid = 0,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (1)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x4000012f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x4000012f,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0x800002f
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (2)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x76543210,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x76543210,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0xeca8650
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (3)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x12345678,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x12345678,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0x2468af8
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (4)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x232023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x232023,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (5)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x232063,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x232063,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (6)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x2320a3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2320a3,
+ .itc_dimmid = 0,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (7)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x2320e3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2320e3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (8)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x232123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x232123,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (9)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x232163,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x232163,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (10)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x2321a3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2321a3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "8-way/4-rank misc, closed (11)",
+ .itc_imc = &imc_rir_8w_4r_closed,
+ .itc_pa = 0x2321e3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2321e3,
+ .itc_dimmid = 0,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0x46423,
+}, {
+ .itc_desc = "4-way/4-rank, open (1)",
+ .itc_imc = &imc_rir_4w_4r_open,
+ .itc_pa = 0x0,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0x0,
+}, {
+ .itc_desc = "4-way/4-rank, open (2)",
+ .itc_imc = &imc_rir_4w_4r_open,
+ .itc_pa = 0x2000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "4-way/4-rank, open (3)",
+ .itc_imc = &imc_rir_4w_4r_open,
+ .itc_pa = 0x4000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x4000,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "4-way/4-rank, open (4)",
+ .itc_imc = &imc_rir_4w_4r_open,
+ .itc_pa = 0x6000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x6000,
+ .itc_dimmid = 0,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "4-way/4-rank, open (5)",
+ .itc_imc = &imc_rir_4w_4r_open,
+ .itc_pa = 0x1234567,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1234567,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0x48c567
+}, {
+ .itc_desc = "4-way/4-rank, open (6)",
+ .itc_imc = &imc_rir_4w_4r_open,
+ .itc_pa = 0x76543210,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x76543210,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1d951210
+}, {
+ .itc_desc = "2DPC (1)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabcfe,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabcfe,
+ .itc_dimmid = 1,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0x1d9b57be
+}, {
+ .itc_desc = "2DPC (2)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabd3e,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabd3e,
+ .itc_dimmid = 0,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0x1d9b57be,
+}, {
+ .itc_desc = "2DPC (3)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabd7e,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabd7e,
+ .itc_dimmid = 1,
+ .itc_rankid = 2,
+ .itc_rankaddr = 0x1d9b57be
+}, {
+ .itc_desc = "2DPC (4)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabdbe,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabdbe,
+ .itc_dimmid = 0,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0x1d9b57be
+}, {
+ .itc_desc = "2DPC (5)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabdfe,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabdfe,
+ .itc_dimmid = 1,
+ .itc_rankid = 3,
+ .itc_rankaddr = 0x1d9b57be
+}, {
+ .itc_desc = "2DPC (6)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabe3e,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabe3e,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1d9b57fe
+}, {
+ .itc_desc = "2DPC (7)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabe7e,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabe7e,
+ .itc_dimmid = 1,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1d9b57fe
+}, {
+ .itc_desc = "2DPC (8)",
+ .itc_imc = &imc_rir_8w_4r_2dpc,
+ .itc_pa = 0xecdabebe,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xecdabebe,
+ .itc_dimmid = 0,
+ .itc_rankid = 1,
+ .itc_rankaddr = 0x1d9b57fe
+}, {
+ .itc_desc = "Multi-RIR 1R 3DPC (1)",
+ .itc_imc = &imc_rir_2w_1r_3dpc,
+ .itc_pa = 0x0,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 1,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "Multi-RIR 1R 3DPC (2)",
+ .itc_imc = &imc_rir_2w_1r_3dpc,
+ .itc_pa = 0x80000000ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x80000000ULL,
+ .itc_dimmid = 2,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "Multi-RIR 1R 3DPC (3)",
+ .itc_imc = &imc_rir_2w_1r_3dpc,
+ .itc_pa = 0x100000000ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x100000000ULL,
+ .itc_dimmid = 2,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x40000000
+}, {
+ .itc_desc = "Multi-RIR 1R 3DPC (4)",
+ .itc_imc = &imc_rir_2w_1r_3dpc,
+ .itc_pa = 0x654321f5,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x654321f5,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x32a190f5
+}, {
+ .itc_desc = "Multi-RIR 1R 3DPC (5)",
+ .itc_imc = &imc_rir_2w_1r_3dpc,
+ .itc_pa = 0xdaddadf5,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0xdaddadf5,
+ .itc_dimmid = 1,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x6d6ed6f5
+}, {
+ .itc_desc = "Multi-RIR 1R 3DPC (6)",
+ .itc_imc = &imc_rir_2w_1r_3dpc,
+ .itc_pa = 0x170ff6099ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x170ff6099ULL,
+ .itc_dimmid = 2,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x787fb059
+}, {
+ .itc_desc = NULL
+} };
diff --git a/usr/src/test/os-tests/tests/imc/imc_test_sad.c b/usr/src/test/os-tests/tests/imc/imc_test_sad.c
new file mode 100644
index 0000000000..ea10963585
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test_sad.c
@@ -0,0 +1,3212 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include "imc_test.h"
+
+/*
+ * This tests various aspects of the source address decoder. We need to test
+ * several of the following:
+ *
+ * o SAD rules with different interleave options
+ * - XOR (SNB->BRD)
+ * - 10t8, 14t12, 32t30 (SKX)
+ * o SAD rules with a7mode (IVB->BRD)
+ * - And XOR
+ * o Different SAD rules for different regions
+ */
+
+/*
+ * This tests basics SAD interleaving with a 2 socket system that has a single
+ * channel and DIMM. The other aspects are simplified to try and make life
+ * easier.
+ */
+
+static const imc_t imc_sad_2s_basic = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This is a 4 socket variants of the previous one. Each DIMM now has a much
+ * smaller amount of memory in it.
+ */
+static const imc_t imc_sad_4s_basic = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 4,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[2] = {
+ .isock_nodeid = 2,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[3] = {
+ .isock_nodeid = 3,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This is similar to imc_sad_2s_basic; however, it enables the XOR mode.
+ */
+static const imc_t imc_sad_2s_xor = {
+ .imc_gen = IMC_GEN_IVY,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6XOR,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6XOR,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_sad_2s_a7 = {
+ .imc_gen = IMC_GEN_IVY,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This is a 4 socket variants of the previous one. Each DIMM now has a much
+ * smaller amount of memory in it.
+ */
+static const imc_t imc_sad_4s_a7 = {
+ .imc_gen = IMC_GEN_HASWELL,
+ .imc_nsockets = 4,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[2] = {
+ .isock_nodeid = 2,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[3] = {
+ .isock_nodeid = 3,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 2, 3, 0, 1, 2, 3 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 4,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x20000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x20000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This is similar to imc_sad_2s_basic; however, it enables the XOR mode.
+ */
+static const imc_t imc_sad_2s_a7_xor = {
+ .imc_gen = IMC_GEN_BROADWELL,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6XOR,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6XOR,
+ .isr_a7mode = B_TRUE,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This constructs an IMC that has multiple SAD rules that change how we
+ * interleave across different regions of memory.
+ */
+static const imc_t imc_sad_2s_multirule = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x20000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x40000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 1, 1, 1, 1, 1, 1, 1, 1 }
+ },
+ .isad_rules[2] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x60000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[3] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 1, 0, 1, 0, 1, 0, 1, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x20000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x20000000,
+ .itr_limit = 0x60000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[2] = {
+ .itr_base = 0x60000000,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x30000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x20000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 1, 0, 1, 0, 1, 0, 1 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x40000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 1, 1, 1, 1, 1, 1, 1, 1 }
+ },
+ .isad_rules[2] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x60000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[3] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 1, 0, 1, 0, 1, 0, 1, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x20000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x20000000,
+ .itr_limit = 0x60000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[2] = {
+ .itr_base = 0x60000000,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x10000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+static const imc_t imc_sad_2s_skx_10t8 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_10t8,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 1, 8, 1, 8, 1, 8, 1 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_256B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_10t8,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 8, 0, 8, 0, 8, 0, 8 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_256B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0x100, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This performs 2 way interleaving across memory controllers, rather than
+ * across sockets.
+ */
+static const imc_t imc_sad_1s_skx_14t12 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_14t12,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 8, 9, 8, 9, 8, 9 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_4KB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 2,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_4KB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+};
+
+static const imc_t imc_sad_4s_8w_skx_32t30 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 4,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0x280000000ULL,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 8, 9, 8, 9, 8, 9 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 3, 3, 0, 0, 1, 1, 2, 2 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0x280000000ULL,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 3, 3, 0, 0, 8, 9, 2, 2 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[2] = {
+ .isock_nodeid = 2,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0x280000000ULL,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 3, 3, 0, 0, 1, 1, 8, 9 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[3] = {
+ .isock_nodeid = 3,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0x280000000ULL,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_rules[1] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x280000000ULL,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0, 0, 1, 1, 2, 2 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x100000000ULL,
+ .itr_limit = 0x280000000ULL,
+ .itr_sock_way = 8,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x200000000ULL, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 1,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x240000000ULL, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+};
+
+const imc_test_case_t imc_test_sad[] = {
+/*
+ * This first set of tests just makes sure that we properly handle SAD
+ * interleaving rules and get routed to the right socket.
+ */
+{
+ .itc_desc = "2 Socket SAD 8-6 Interleave (1)",
+ .itc_imc = &imc_sad_2s_basic,
+ .itc_pa = 0x0,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "2 Socket SAD 8-6 Interleave (2)",
+ .itc_imc = &imc_sad_2s_basic,
+ .itc_pa = 0x12345678,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91a2b38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91a2b38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 Interleave (3)",
+ .itc_imc = &imc_sad_2s_basic,
+ .itc_pa = 0x12345638,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91a2b38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91a2b38
+},
+/*
+ * This is the same as above, but uses a 4-socket configuration instead.
+ */
+{
+ .itc_desc = "4 Socket SAD 8-6 Interleave (1)",
+ .itc_imc = &imc_sad_4s_basic,
+ .itc_pa = 0x12345638,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x48d15b8,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x48d15b8
+}, {
+ .itc_desc = "4 Socket SAD 8-6 Interleave (2)",
+ .itc_imc = &imc_sad_4s_basic,
+ .itc_pa = 0x12345678,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x48d15b8,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x48d15b8
+}, {
+ .itc_desc = "4 Socket SAD 8-6 Interleave (3)",
+ .itc_imc = &imc_sad_4s_basic,
+ .itc_pa = 0x123456b8,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 2,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x48d15b8,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x48d15b8
+}, {
+ .itc_desc = "4 Socket SAD 8-6 Interleave (4)",
+ .itc_imc = &imc_sad_4s_basic,
+ .itc_pa = 0x123456f8,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 3,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x48d15b8,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x48d15b8
+},
+/*
+ * This is a variant on the basic 2s tests. XOR mode is enabled, so we use that
+ * to see that we actually have differences versus the basic 2s tests.
+ */
+{
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (1)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12345638,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91a2b38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91a2b38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (2)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12345678,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91a2b38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91a2b38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (3)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12355638,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91aab38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91aab38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (4)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12355678,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91aab38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91aab38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (5)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12365638,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91b2b38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91b2b38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (6)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12365678,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91b2b38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91b2b38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (7)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12375638,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91bab38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91bab38
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR Interleave (8)",
+ .itc_imc = &imc_sad_2s_xor,
+ .itc_pa = 0x12375678,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x91bab38,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x91bab38
+},
+/*
+ * Next, we're going to repeat the same initial set of tests that we had, but
+ * we're also going to turn on a7 mode. First up is the 2 socket case.
+ */
+{
+ .itc_desc = "2 Socket SAD 8-6 A7 Interleave (1)",
+ .itc_imc = &imc_sad_2s_a7,
+ .itc_pa = 0x2342000f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x11a1000f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x11a1000f
+}, {
+ .itc_desc = "2 Socket SAD 8-6 A7 Interleave (2)",
+ .itc_imc = &imc_sad_2s_a7,
+ .itc_pa = 0x2342004f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x11a1004f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x11a1004f
+}, {
+ .itc_desc = "2 Socket SAD 8-6 A7 Interleave (3)",
+ .itc_imc = &imc_sad_2s_a7,
+ .itc_pa = 0x2342020f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x11a1010f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x11a1010f
+}, {
+ .itc_desc = "2 Socket SAD 8-6 A7 Interleave (4)",
+ .itc_imc = &imc_sad_2s_a7,
+ .itc_pa = 0x2342024f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x11a1014f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x11a1014f
+},
+/*
+ * Next, we're going to repeat the same initial set of tests that we had, but
+ * we're also going to turn on a7 mode. First up is the 4 socket case.
+ */
+{
+ .itc_desc = "4 Socket SAD 8-6 A7 (1)",
+ .itc_imc = &imc_sad_4s_a7,
+ .itc_pa = 0x2342000f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08d0800f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08d0800f
+}, {
+ .itc_desc = "4 Socket SAD 8-6 A7 (2)",
+ .itc_imc = &imc_sad_4s_a7,
+ .itc_pa = 0x2342008f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 2,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08d0800f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08d0800f
+}, {
+ .itc_desc = "4 Socket SAD 8-6 A7 (3)",
+ .itc_imc = &imc_sad_4s_a7,
+ .itc_pa = 0x2342020f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08d0808f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08d0808f
+}, {
+ .itc_desc = "4 Socket SAD 8-6 A7 (4)",
+ .itc_imc = &imc_sad_4s_a7,
+ .itc_pa = 0x2342028f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 3,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08d0808f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08d0808f
+}, {
+ .itc_desc = "4 Socket SAD 8-6 A7 (5)",
+ .itc_imc = &imc_sad_4s_a7,
+ .itc_pa = 0x23420f8f,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 3,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08d0838f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08d0838f
+},
+/*
+ * 2 Socket 8-6 XOR mode, with a7 set. Here, we'll end up working through all of
+ * the XOR permutations to make sure that we're in good shape.
+ */
+{
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (1)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4200000b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2100000b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2100000b
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (2)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4200020b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2100010b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2100010b
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (3)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4201000b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2100800b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2100800b
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (4)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4201020b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2100810b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2100810b
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (5)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4202000b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2101000b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2101000b
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (6)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4202020b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2101010b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2101010b
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (7)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4203000b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2101800b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2101800b
+}, {
+ .itc_desc = "2 Socket SAD 8-6 XOR A7 (8)",
+ .itc_imc = &imc_sad_2s_a7_xor,
+ .itc_pa = 0x4203020b,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2101810b,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2101810b
+},
+/*
+ * This is a multi-rule SAD that alternates how we target socket interleaving
+ * depending on which address range we're at.
+ */
+{
+ .itc_desc = "SAD Multi-rule (1)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x0ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x07fb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x07fb0003
+}, {
+ .itc_desc = "SAD Multi-rule (2)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x0ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x07fb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x07fb0003
+}, {
+ .itc_desc = "SAD Multi-rule (3)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x1ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0ffb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0ffb0003
+}, {
+ .itc_desc = "SAD Multi-rule (4)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x1ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0ffb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0ffb0003
+}, {
+ .itc_desc = "SAD Multi-rule (5)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x2ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1ff60003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1ff60003
+},
+{
+ .itc_desc = "SAD Multi-rule (6)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x2ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1ff60043,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1ff60043
+}, {
+ .itc_desc = "SAD Multi-rule (7)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x3ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2ff60003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2ff60003
+}, {
+ .itc_desc = "SAD Multi-rule (8)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x3ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2ff60043,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2ff60043
+}, {
+ .itc_desc = "SAD Multi-rule (9)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x4ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1ff60003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1ff60003
+}, {
+ .itc_desc = "SAD Multi-rule (10)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x4ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1ff60043,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1ff60043
+}, {
+ .itc_desc = "SAD Multi-rule (11)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x5ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2ff60003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2ff60003
+}, {
+ .itc_desc = "SAD Multi-rule (12)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x5ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x2ff60043,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2ff60043
+}, {
+ .itc_desc = "SAD Multi-rule (13)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x6ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x37fb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x37fb0003
+}, {
+ .itc_desc = "SAD Multi-rule (14)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x6ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x37fb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x37fb0003
+}, {
+ .itc_desc = "SAD Multi-rule (15)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x7ff60003,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3ffb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3ffb0003
+}, {
+ .itc_desc = "SAD Multi-rule (16)",
+ .itc_imc = &imc_sad_2s_multirule,
+ .itc_pa = 0x7ff60043,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3ffb0003,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3ffb0003
+},
+/*
+ * Verify that SAD interleaving at 10-8 works.
+ */
+{
+ .itc_desc = "SAD 2s SKX 10-8 (1)",
+ .itc_imc = &imc_sad_2s_skx_10t8,
+ .itc_pa = 0x11220000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08910000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08910000
+}, {
+ .itc_desc = "SAD 2s SKX 10-8 (2)",
+ .itc_imc = &imc_sad_2s_skx_10t8,
+ .itc_pa = 0x11220100,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08910000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08910000
+}, {
+ .itc_desc = "SAD 2s SKX 10-8 (3)",
+ .itc_imc = &imc_sad_2s_skx_10t8,
+ .itc_pa = 0x112200ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x089100ff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x089100ff
+}, {
+ .itc_desc = "SAD 2s SKX 10-8 (4)",
+ .itc_imc = &imc_sad_2s_skx_10t8,
+ .itc_pa = 0x112201ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x089100ff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x089100ff
+}, {
+ .itc_desc = "SAD 2s SKX 10-8 (5)",
+ .itc_imc = &imc_sad_2s_skx_10t8,
+ .itc_pa = 0x7ffffeff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fffffff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fffffff
+}, {
+ .itc_desc = "SAD 2s SKX 10-8 (6)",
+ .itc_imc = &imc_sad_2s_skx_10t8,
+ .itc_pa = 0x7fffffff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fffffff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fffffff
+},
+/*
+ * Again with SKX; however, now with 15-12.
+ */
+{
+ .itc_desc = "SAD 2s SKX 14-12 (1)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x11220000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08910000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08910000
+}, {
+ .itc_desc = "SAD 2s SKX 14-12 (2)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x11220100,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08910100,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08910100
+}, {
+ .itc_desc = "SAD 2s SKX 14-12 (3)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x112200ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x089100ff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x089100ff
+}, {
+ .itc_desc = "SAD 2s SKX 14-12 (4)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x112201ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x089101ff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x089101ff
+}, {
+ .itc_desc = "SAD 2s SKX 14-12 (5)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x11221000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08910000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08910000
+}, {
+ .itc_desc = "SAD 2s SKX 14-12 (6)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x11221100,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x08910100,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x08910100
+}, {
+ .itc_desc = "SAD 2s SKX 14-12 (7)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x112210ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x089100ff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x089100ff
+}, {
+ .itc_desc = "SAD 2s SKX 14-12 (8)",
+ .itc_imc = &imc_sad_1s_skx_14t12,
+ .itc_pa = 0x112211ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x089101ff,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x089101ff
+},
+/*
+ * This set covers using an 8-way socket granularity on Skylake. This means that
+ * we have two IMCs per socket as well. We're also using 1 GiB granularity here.
+ * So we want to verify that is working as well.
+ */
+{
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (1)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x0badcafe,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (2)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x4badcafe,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (3)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x10badcafeULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (4)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x14badcafeULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (5)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x18badcafeULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 2,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (6)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x1cbadcafeULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 2,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (7)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x20badcafeULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 3,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (8)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x24badcafeULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 3,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badcafe,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badcafe
+},
+
+{
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (9)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x0badca77,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (10)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x4badca77,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (11)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x10badca77ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (12)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x14badca77ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (13)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x18badca77ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 2,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (14)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x1cbadca77ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 2,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (15)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x20badca77ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 3,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = "SAD 4s 8-way SKX 32-30 (16)",
+ .itc_imc = &imc_sad_4s_8w_skx_32t30,
+ .itc_pa = 0x24badca77ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 3,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0badca77,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0badca77
+}, {
+ .itc_desc = NULL
+} };
diff --git a/usr/src/test/os-tests/tests/imc/imc_test_skx_loop.c b/usr/src/test/os-tests/tests/imc/imc_test_skx_loop.c
new file mode 100644
index 0000000000..5efa0998f1
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test_skx_loop.c
@@ -0,0 +1,175 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include "imc_test.h"
+
+/*
+ * Verify that we properly detect loops on Skylake based multi-socket systems.
+ * This represents an erroneous condition.
+ */
+
+/*
+ * This is a multi-socket bare bones Skylake structure (we don't bother with
+ * anything past the SAD as we should never need it. This checks to make sure
+ * that we detect such a loop.
+ */
+static const imc_t imc_skx_loop_2s = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 1, 1, 1, 1, 1, 1, 1, 1 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ }
+ }
+ }
+};
+
+/*
+ * This has a target that causes us to loop back to ourselves.
+ */
+static const imc_t imc_skx_loop_self = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ }
+ }
+ }
+};
+
+/*
+ * This referes to a non-existant socket in the search loop.
+ */
+static const imc_t imc_skx_loop_badsock = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 5, 5, 5, 5, 5, 5, 5, 5 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ }
+ }
+ }
+};
+
+const imc_test_case_t imc_test_skx_loop[] = { {
+ .itc_desc = "Skylake loop detection, 2s (1)",
+ .itc_imc = &imc_skx_loop_2s,
+ .itc_pa = 0x0,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_SAD_SEARCH_LOOP
+}, {
+ .itc_desc = "Skylake loop detection, 2s (2)",
+ .itc_imc = &imc_skx_loop_2s,
+ .itc_pa = 0x7fffffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_SAD_SEARCH_LOOP
+}, {
+ .itc_desc = "Skylake loop detection, self (1)",
+ .itc_imc = &imc_skx_loop_self,
+ .itc_pa = 0x0,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_SAD_SEARCH_LOOP
+}, {
+ .itc_desc = "Skylake loop detection, self (2)",
+ .itc_imc = &imc_skx_loop_self,
+ .itc_pa = 0x7fffffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_SAD_SEARCH_LOOP
+}, {
+ .itc_desc = "Skylake loop detection, bad sock (1)",
+ .itc_imc = &imc_skx_loop_badsock,
+ .itc_pa = 0x0,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_REMOTE_MC_ROUTE
+}, {
+ .itc_desc = "Skylake loop detection, bad sock (2)",
+ .itc_imc = &imc_skx_loop_badsock,
+ .itc_pa = 0x7fffffff,
+ .itc_pass = B_FALSE,
+ .itc_fail = IMC_DECODE_F_BAD_REMOTE_MC_ROUTE
+}, {
+ .itc_desc = NULL
+} };
diff --git a/usr/src/test/os-tests/tests/imc/imc_test_tad.c b/usr/src/test/os-tests/tests/imc/imc_test_tad.c
new file mode 100644
index 0000000000..b16edbd7b2
--- /dev/null
+++ b/usr/src/test/os-tests/tests/imc/imc_test_tad.c
@@ -0,0 +1,5228 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include "imc_test.h"
+
+/*
+ * This tests various aspects of the target address decoder.
+ *
+ * o TAD rules with different channel interleaving
+ * o TAD rules with channel shifting (IVB->BRD)
+ * o TAD rules with channel hashing (IVB->BRD)
+ * o TAD rules with different granularities (SKX)
+ * o Channel rules with mod2/3 variants (SKX)
+ *
+ * We use the most basic of SAD rules and RIR rules when constructing these.
+ * Those are more generally exercised elsewhere. Basic socket granularity rules
+ * are tested in imc_test_sad.c.
+ *
+ * There are currently no tests for mirroring or lockstep mode as that's not
+ * more generally supported.
+ */
+
+static const imc_t imc_tad_1s_2cw = {
+ .imc_gen = IMC_GEN_SANDY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 1, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This IMC has the a7mode/McChanShiftUp set. This means that instead of using
+ * bits 0-6 for an address, it should use bits 0-7.
+ */
+static const imc_t imc_tad_1s_2cw_shiftup = {
+ .imc_gen = IMC_GEN_IVY,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_a7mode = B_TRUE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANSHIFT,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 1, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This IMC has the channel hashing mode set on all of the channels in question.
+ * This means that the TAD will hash the upper address bits into the channel
+ * determination.
+ */
+static const imc_t imc_tad_1s_2cw_chanhash = {
+ .imc_gen = IMC_GEN_HASWELL,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = IMC_TAD_FLAG_CHANHASH,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 1, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This IMC has different TAD rules that cover different ranges, which change
+ * how we interleave. The main goal is to make sure that we're always going to
+ * the right place. This also requires us to set TAD offsets on a
+ * per-channel/TAD rule basis. These are required to correctly make sure that we
+ * map things. The following is how the address space should in theory look. We
+ * have 2 GiB (0x80000000) of address space. We break that into 4 512 MiB
+ * chunks. The first and last are 2-way interleaved. The middle two are 1-way
+ * interleaved to a specific channel.
+ */
+static const imc_t imc_tad_1s_multirule = {
+ .imc_gen = IMC_GEN_BROADWELL,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 0, 0, 0, 0, 0, 0, 0 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x20000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 1, 0, 0 }
+ },
+ .itad_rules[1] = {
+ .itr_base = 0x20000000,
+ .itr_limit = 0x40000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 1, 1, 0, 0 }
+ },
+ .itad_rules[2] = {
+ .itr_base = 0x40000000,
+ .itr_limit = 0x60000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 1,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ },
+ .itad_rules[3] = {
+ .itr_base = 0x60000000,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 1, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0x30000000, 0, 0,
+ 0, 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0x10000000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * The purpse of this IMC is to use a combination of both socket and channel
+ * interleaving. It employs a system with two sockets, each which have 2 IMCs.
+ * Each IMC has two channels. We have a 4-way socket interleave followed by a
+ * 2-way channel interleave. We use a simplified memory layout (TOLM = 4 GiB) to
+ * simplify other rules.
+ */
+static const imc_t imc_tad_2s_2cw_4sw = {
+ .imc_gen = IMC_GEN_IVY,
+ .imc_nsockets = 2,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0x200000000ULL,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x200000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 4, 1, 5, 0, 4, 1, 5 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x200000000ULL,
+ .itr_sock_way = 4,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 1, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x200000000ULL,
+ .itr_sock_way = 4,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 1, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ },
+ .imc_sockets[1] = {
+ .isock_nodeid = 1,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 10,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 0, 4, 1, 5, 0, 4, 1, 5 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x200000000ULL,
+ .itr_sock_way = 4,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 1, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 12,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x200000000ULL,
+ .itr_sock_way = 4,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 1, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR3,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 8,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This IMC has a single socket with two IMCs and two channels. It uses the
+ * default granularities and sizes. This just serves as a basis for the
+ * subsequent tests.
+ */
+static const imc_t imc_skx_64b_gran = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 8, 9, 8, 9, 8, 9 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This tests a different channel granularity. Note the channel and socket
+ * granulariites match at this point in time to simplify the test.
+ */
+static const imc_t imc_skx_256b_gran = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_10t8,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 8, 9, 8, 9, 8, 9 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_256B,
+ .itr_chan_gran = IMC_TAD_GRAN_256B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This time, use a 4k granularity.
+ */
+static const imc_t imc_skx_4k_gran = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_14t12,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 8, 9, 8, 9, 8, 9 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_4KB,
+ .itr_chan_gran = IMC_TAD_GRAN_4KB,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * Once more, with 1 GiB granularity.
+ */
+static const imc_t imc_skx_1g_gran = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x80000000,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x80000000,
+ .isr_imode = IMC_SAD_IMODE_32t30,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 8, 9, 8, 9, 8, 9 }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 }
+ }
+ },
+ .isock_ntad = 1,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x80000000,
+ .itr_sock_way = 1,
+ .itr_chan_way = 2,
+ .itr_sock_gran = IMC_TAD_GRAN_1GB,
+ .itr_chan_gran = IMC_TAD_GRAN_1GB,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 1,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * This tests a 1 socket, 4 channel-way configuration.
+ */
+static const imc_t imc_tad_1s_4cw = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0xa, 0xb, 8, 9, 0xa,
+ 0xb }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 },
+ .ismc_mcroutes[2] = { 1, 1 },
+ .ismc_mcroutes[3] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 2,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * A variant on 1imc_tad_1s_4cw that uses Skylake mod3 rules to change how the
+ * target channel is determined. While we have six channels here, technically
+ * this configuration has wasted memory. This is on purpose to simplify the
+ * rules below.
+ */
+static const imc_t imc_tad_skx_mod3_45t6 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_need_mod3 = B_TRUE,
+ .isr_mod_mode = IMC_SAD_MOD_MODE_45t6,
+ .isr_mod_type = IMC_SAD_MOD_TYPE_MOD3,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0xa, 0xb, 8, 9, 0xa,
+ 0xb }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 },
+ .ismc_mcroutes[2] = { 0, 2 },
+ .ismc_mcroutes[3] = { 1, 2 },
+ .ismc_mcroutes[4] = { 1, 1 },
+ .ismc_mcroutes[5] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * A variant on imc_tad_skx_mod3_45t6, but here we test the 45t8 mod variant.
+ */
+static const imc_t imc_tad_skx_mod3_45t8 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_need_mod3 = B_TRUE,
+ .isr_mod_mode = IMC_SAD_MOD_MODE_45t8,
+ .isr_mod_type = IMC_SAD_MOD_TYPE_MOD3,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0xa, 0xb, 8, 9, 0xa,
+ 0xb }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 },
+ .ismc_mcroutes[2] = { 0, 2 },
+ .ismc_mcroutes[3] = { 1, 2 },
+ .ismc_mcroutes[4] = { 1, 1 },
+ .ismc_mcroutes[5] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * A variant on imc_tad_skx_mod3_45t6, but here we test the 45t12 mod variant.
+ */
+static const imc_t imc_tad_skx_mod3_45t12 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_need_mod3 = B_TRUE,
+ .isr_mod_mode = IMC_SAD_MOD_MODE_45t12,
+ .isr_mod_type = IMC_SAD_MOD_TYPE_MOD3,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0xa, 0xb, 8, 9, 0xa,
+ 0xb }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 },
+ .ismc_mcroutes[2] = { 0, 2 },
+ .ismc_mcroutes[3] = { 1, 2 },
+ .ismc_mcroutes[4] = { 1, 1 },
+ .ismc_mcroutes[5] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * A variant on imc_tad_skx_mod3_45t12, but instead of using mod3, we use the
+ * mod2 variant that favors 0/1. This menas we can only output route entries, 0,
+ * 1, 2, and 3.
+ */
+static const imc_t imc_tad_skx_mod2_01_45t12 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_need_mod3 = B_TRUE,
+ .isr_mod_mode = IMC_SAD_MOD_MODE_45t12,
+ .isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_01,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0xa, 0xb, 8, 9, 0xa,
+ 0xb }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 },
+ .ismc_mcroutes[2] = { 0, 2 },
+ .ismc_mcroutes[3] = { 1, 2 },
+ .ismc_mcroutes[4] = { 1, 1 },
+ .ismc_mcroutes[5] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * A variant on imc_tad_skx_mod3_45t12, but instead of using mod3, we use the
+ * mod2 variant that favors 1/2. This menas we can only output route entries, 2,
+ * 3, 4, and 5.
+ */
+static const imc_t imc_tad_skx_mod2_12_45t12 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_need_mod3 = B_TRUE,
+ .isr_mod_mode = IMC_SAD_MOD_MODE_45t12,
+ .isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_12,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0xa, 0xb, 8, 9, 0xa,
+ 0xb }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 },
+ .ismc_mcroutes[2] = { 0, 2 },
+ .ismc_mcroutes[3] = { 1, 2 },
+ .ismc_mcroutes[4] = { 1, 1 },
+ .ismc_mcroutes[5] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+/*
+ * A variant on imc_tad_skx_mod3_45t12, but instead of using mod3, we use the
+ * mod2 variant that favors 0/2. This means we can only output route entries, 0,
+ * 1, 4, and 5.
+ */
+static const imc_t imc_tad_skx_mod2_02_45t12 = {
+ .imc_gen = IMC_GEN_SKYLAKE,
+ .imc_nsockets = 1,
+ .imc_sockets[0] = {
+ .isock_nodeid = 0,
+ .isock_valid = IMC_SOCKET_V_VALID,
+ .isock_sad = {
+ .isad_flags = 0,
+ .isad_valid = IMC_SAD_V_VALID,
+ .isad_tolm = 0x100000000ULL,
+ .isad_tohm = 0,
+ .isad_nrules = 24,
+ .isad_rules[0] = {
+ .isr_enable = B_TRUE,
+ .isr_limit = 0x100000000ULL,
+ .isr_imode = IMC_SAD_IMODE_8t6,
+ .isr_need_mod3 = B_TRUE,
+ .isr_mod_mode = IMC_SAD_MOD_MODE_45t12,
+ .isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_02,
+ .isr_ntargets = IMC_MAX_SAD_INTERLEAVE,
+ .isr_targets = { 8, 9, 0xa, 0xb, 8, 9, 0xa,
+ 0xb }
+ },
+ .isad_mcroute = {
+ .ismc_nroutes = 6,
+ .ismc_mcroutes[0] = { 0, 0 },
+ .ismc_mcroutes[1] = { 0, 1 },
+ .ismc_mcroutes[2] = { 0, 2 },
+ .ismc_mcroutes[3] = { 1, 2 },
+ .ismc_mcroutes[4] = { 1, 1 },
+ .ismc_mcroutes[5] = { 1, 0 }
+ }
+ },
+ .isock_ntad = 2,
+ .isock_tad[0] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_tad[1] = {
+ .itad_flags = 0,
+ .itad_nrules = 8,
+ .itad_rules[0] = {
+ .itr_base = 0x0,
+ .itr_limit = 0x100000000ULL,
+ .itr_sock_way = 1,
+ .itr_chan_way = 4,
+ .itr_sock_gran = IMC_TAD_GRAN_64B,
+ .itr_chan_gran = IMC_TAD_GRAN_64B,
+ .itr_ntargets = 4,
+ .itr_targets = { 0, 0, 0, 0 }
+ }
+ },
+ .isock_nimc = 2,
+ .isock_imcs[0] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ },
+ .isock_imcs[1] = {
+ .icn_nchannels = 3,
+ .icn_dimm_type = IMC_DIMM_DDR4,
+ .icn_ecc = B_TRUE,
+ .icn_lockstep = B_FALSE,
+ .icn_closed = B_FALSE,
+ .icn_channels[0] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[1] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ },
+ .icn_channels[2] = {
+ .ich_ndimms = 1,
+ .ich_dimms[0] = {
+ .idimm_present = B_TRUE,
+ .idimm_nbanks = 3,
+ .idimm_width = 8,
+ .idimm_density = 2,
+ .idimm_nranks = 2,
+ .idimm_nrows = 14,
+ .idimm_ncolumns = 10,
+ .idimm_size = 0x40000000
+ },
+ .ich_ntad_offsets = 12,
+ .ich_tad_offsets = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ .ich_nrankileaves = 4,
+ .ich_rankileaves[0] = {
+ .irle_enabled = B_TRUE,
+ .irle_nways = 1,
+ .irle_nwaysbits = 1,
+ .irle_limit = 0x40000000,
+ .irle_nentries = 8,
+ .irle_entries[0] = { 0x0, 0x0 },
+ }
+ }
+ }
+ }
+};
+
+const imc_test_case_t imc_test_tad[] = {
+/*
+ * These tests come in pairs. The first two verify that we can get the same
+ * address on the channel and interleave. The second set verifies that we end up
+ * in the same channel when we're within interleaving. The third set shows that
+ * we interleave again and will be used as a point of comparison in the next
+ * group of tests. The fourth set varies this and makes sure that we can end up
+ * on the right channel at different address ranges.
+ */
+{
+ .itc_desc = "1 Socket, 2 Channel way (1)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x33333333,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x199999b3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999b3
+}, {
+ .itc_desc = "1 Socket, 2 Channel way (2)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x33333373,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x199999b3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999b3
+}, {
+ .itc_desc = "1 Socket, 2 Channel way (3)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x3333331a,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1999999a,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1999999a
+}, {
+ .itc_desc = "1 Socket, 2 Channel way (4)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x33333342,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x19999982,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x19999982
+}, {
+ .itc_desc = "1 Socket, 2 Channel way (5)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x333333b3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x199999f3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999f3
+}, {
+ .itc_desc = "1 Socket, 2 Channel way (6)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x333333f3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x199999f3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999f3
+}, {
+ .itc_desc = "1 Socket, 2 Channel way (7)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x22222222,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x11111122,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x11111122
+}, {
+ .itc_desc = "1 Socket, 2 Channel way (8)",
+ .itc_imc = &imc_tad_1s_2cw,
+ .itc_pa = 0x77777777,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3bbbbbb7,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3bbbbbb7
+},
+/*
+ * This next set of tests is similar to the previous one, except we have the
+ * a7mode / McChanShiftUp enabled, which means that we use 7-bits to index into
+ * the channel by default rather than 6. We have tests that compare this
+ * behavior that would have varied in the previous case, but does not now. We do
+ * this mostly by using the same initial set of addresses (tests 1-6 of the
+ * previous set).
+ */
+{
+ .itc_desc = "1 Socket, 2 Channel way, Shift Up (1)",
+ .itc_imc = &imc_tad_1s_2cw_shiftup,
+ .itc_pa = 0x33333333,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x199999b3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999b3
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Shift Up (2)",
+ .itc_imc = &imc_tad_1s_2cw_shiftup,
+ .itc_pa = 0x33333373,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x199999f3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999f3
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Shift Up (3)",
+ .itc_imc = &imc_tad_1s_2cw_shiftup,
+ .itc_pa = 0x3333331a,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x1999999a,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1999999a
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Shift Up (4)",
+ .itc_imc = &imc_tad_1s_2cw_shiftup,
+ .itc_pa = 0x33333342,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x199999c2,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999c2
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Shift Up (5)",
+ .itc_imc = &imc_tad_1s_2cw_shiftup,
+ .itc_pa = 0x333333b3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x199999b3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999b3
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Shift Up (6)",
+ .itc_imc = &imc_tad_1s_2cw_shiftup,
+ .itc_pa = 0x333333f3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x199999f3,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x199999f3
+},
+/*
+ * This next set of tests focuses on channel hashing. This is when we take the
+ * upper bits of the system addrses and use that to influence which channel
+ * something should be directed to. To see this, we take addresses that have the
+ * same base address (using bits 0-11) and see that they channels based on the
+ * different upper bits, where as without channel hashing, we shouldn't expect
+ * that.
+ */
+{
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (1)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00000bad,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x000005ed,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x000005ed
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (2)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00001bad,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x00000ded,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x00000ded
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (3)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00011bad,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x00008ded,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x00008ded
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (4)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00111bad,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x00088ded,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x00088ded
+}, {
+ /* The channel shouldn't change as it's not a bit we index on */
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (5)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00311bad,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x00188ded,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x00188ded
+}, {
+ /* This one shouldn't change as the 1 is > bit 28 */
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (6)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x20111bad,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x10088ded,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x10088ded
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (7)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00000bed,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x000005ed,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x000005ed
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (8)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00100bed,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x000805ed,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x000805ed
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (9)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00300bed,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x001805ed,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x001805ed
+}, {
+ .itc_desc = "1 Socket, 2 Channel way, Hashing (10)",
+ .itc_imc = &imc_tad_1s_2cw_chanhash,
+ .itc_pa = 0x00500bed,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x002805ed,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x002805ed
+},
+/*
+ * This range of tests basically checks how we interleave in the multi-rule
+ * system that we've put together. We have regions that should be direct mapped
+ * an others that should be interleaved.
+ */
+{
+ .itc_desc = "1s Multi-rule (1)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x07654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03b2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03b2a1a1
+}, {
+ .itc_desc = "1s Multi-rule (2)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x07654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03b2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03b2a1a1
+}, {
+ .itc_desc = "1s Multi-rule (3)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x17654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0bb2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0bb2a1a1
+}, {
+ .itc_desc = "1s Multi-rule (4)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x17654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0bb2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0bb2a1a1
+}, {
+ .itc_desc = "1s Multi-rule (5)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x27654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x17654321,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x17654321
+}, {
+ .itc_desc = "1s Multi-rule (6)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x27654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x17654361,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x17654361
+}, {
+ .itc_desc = "1s Multi-rule (7)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x37654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x27654321,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x27654321
+}, {
+ .itc_desc = "1s Multi-rule (8)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x37654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x27654361,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x27654361
+}, {
+ .itc_desc = "1s Multi-rule (9)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x47654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x17654321,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x17654321
+}, {
+ .itc_desc = "1s Multi-rule (10)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x47654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x17654361,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x17654361
+}, {
+ .itc_desc = "1s Multi-rule (11)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x57654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x27654321,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x27654321
+}, {
+ .itc_desc = "1s Multi-rule (12)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x57654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x27654361,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x27654361
+}, {
+ .itc_desc = "1s Multi-rule (13)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x67654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x33b2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x33b2a1a1
+}, {
+ .itc_desc = "1s Multi-rule (14)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x67654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x33b2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x33b2a1a1
+}, {
+ .itc_desc = "1s Multi-rule (15)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x77654321,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3bb2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3bb2a1a1
+}, {
+ .itc_desc = "1s Multi-rule (16)",
+ .itc_imc = &imc_tad_1s_multirule,
+ .itc_pa = 0x77654361,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3bb2a1a1,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3bb2a1a1
+},
+/*
+ * This set of tests looks at using a combination of channel interleaving and
+ * socket interleaving and makes sure that we handle that correctly when across
+ * multiple IMCs and sockets. We have four tests per dimm. Two that show that we
+ * are consistent within the cache line. Two that show that we are consistent
+ * when we go to a different line.
+ */
+{
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (1)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60007,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (2)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (3)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150007ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (4)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150023ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (5)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60047,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (6)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60063,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (7)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150047ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (8)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150063ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (9)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60087,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (10)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff600a3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (11)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150087ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (12)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff1500a3ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (13)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff600c7,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (14)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff600f3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec033,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec033
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (15)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff1500c7ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (16)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff1500f3ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a033,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a033
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (17)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60107,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (18)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (19)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150107ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (20)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150123ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (21)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60147,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (22)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60163,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (23)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150147ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (24)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150163ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (25)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff60187,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (26)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff601a3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fec023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (27)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff150187ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (28)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff1501a3ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x3fe2a023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a023
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (29)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff601c7,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (30)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff601f3,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fec033,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fec033
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (31)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff1501c7ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a007,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a007
+}, {
+ .itc_desc = "2 socket, 4-sock way, 2-channel way (32)",
+ .itc_imc = &imc_tad_2s_2cw_4sw,
+ .itc_pa = 0x1ff1501f3ULL,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 1,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3fe2a033,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3fe2a033
+},
+/*
+ * This begins a series of tests related to Skylake channel granularities.
+ */
+{
+ .itc_desc = "SKX 2ch 64b chan gran (1)",
+ .itc_imc = &imc_skx_64b_gran,
+ .itc_pa = 0x0c120000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090000
+}, {
+ .itc_desc = "SKX 2ch 64b chan gran (2)",
+ .itc_imc = &imc_skx_64b_gran,
+ .itc_pa = 0x0c120040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090000
+}, {
+ .itc_desc = "SKX 2ch 64b chan gran (3)",
+ .itc_imc = &imc_skx_64b_gran,
+ .itc_pa = 0x0c120023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090023
+}, {
+ .itc_desc = "SKX 2ch 64b chan gran (4)",
+ .itc_imc = &imc_skx_64b_gran,
+ .itc_pa = 0x0c120068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090028,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090028
+},
+/*
+ * Move onto a 256 byte granularity and repeat.
+ */
+{
+ .itc_desc = "SKX 2ch 256b chan gran (1)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090000
+}, {
+ .itc_desc = "SKX 2ch 256b chan gran (2)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090040
+}, {
+ .itc_desc = "SKX 2ch 256b chan gran (3)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090023
+}, {
+ .itc_desc = "SKX 2ch 256b chan gran (4)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090068,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090068
+}, {
+ .itc_desc = "SKX 2ch 256b chan gran (5)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120300,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090100,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090100
+}, {
+ .itc_desc = "SKX 2ch 256b chan gran (6)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090040
+}, {
+ .itc_desc = "SKX 2ch 256b chan gran (7)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090023
+}, {
+ .itc_desc = "SKX 2ch 256b chan gran (8)",
+ .itc_imc = &imc_skx_256b_gran,
+ .itc_pa = 0x0c120368,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090168,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090168
+},
+/*
+ * Now, use 4k granularities.
+ */
+{
+ .itc_desc = "SKX 2ch 4k chan gran (1)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090000
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (2)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090040
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (3)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090023
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (4)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090068,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090068
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (5)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120300,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090300,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090300
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (6)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090140,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090140
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (7)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090123,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090123
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (8)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c120368,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x06090368,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090368
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (9)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c121000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090000
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (10)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c123040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06091040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06091040
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (11)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c121023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090023
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (12)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c121068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090068,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090068
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (13)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c121300,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090300,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090300
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (14)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c121140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090140,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090140
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (15)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c123123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06091123,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06091123
+}, {
+ .itc_desc = "SKX 2ch 4k chan gran (16)",
+ .itc_imc = &imc_skx_4k_gran,
+ .itc_pa = 0x0c121368,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x06090368,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x06090368
+},
+/*
+ * Use a 1 GiB Interleaving next.
+ */
+{
+ .itc_desc = "SKX 2ch 1g chan gran (1)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120000
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (2)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120040
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (3)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120023
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (4)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120068,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120068
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (5)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120300,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120300,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120300
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (6)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120140,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120140
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (7)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120123,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120123
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (8)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c120368,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c120368,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120368
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (9)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c121000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c121000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c121000
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (10)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c123040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c123040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c123040
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (11)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c121023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c121023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c121023
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (12)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c121068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c121068,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c121068
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (13)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c121300,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c121300,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c121300
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (14)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c121140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c121140,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c121140
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (15)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c123123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c123123,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c123123
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (16)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x0c121368,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0c121368,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c121368
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (1)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x4c120000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0c120000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120000
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (2)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x4c120040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0c120040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120040
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (3)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x5c120023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x1c120023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1c120023
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (4)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x6c120068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x2c120068,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2c120068
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (5)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x7c120300,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3c120300,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3c120300
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (6)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x4c120140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0c120140,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c120140
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (7)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x5c120123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x1c120123,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1c120123
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (8)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x6c120368,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x2c120368,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2c120368
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (9)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x7c121000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3c121000,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3c121000
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (10)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x4c123040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0c123040,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0c123040
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (11)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x5c121023,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x1c121023,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1c121023
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (12)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x6c121068,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x2c121068,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2c121068
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (13)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x7c121300,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3c121300,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3c121300
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (14)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x7c121140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3c121140,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3c121140
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (15)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x6c123123,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x2c123123,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x2c123123
+}, {
+ .itc_desc = "SKX 2ch 1g chan gran (16)",
+ .itc_imc = &imc_skx_1g_gran,
+ .itc_pa = 0x5c121368,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x1c121368,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x1c121368
+},
+/*
+ * This round of tests puts together a 1 socekt configuration with 4 channel way
+ * interleaving. This means that we're interleaving across two IMCs in the same
+ * socket.
+ */
+{
+ .itc_desc = "1 socket, 4-channel way (1)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff13006,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (2)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff13046,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (3)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff13086,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (4)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff130c6,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (5)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff13026,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c26,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c26
+}, {
+ .itc_desc = "1 socket, 4-channel way (6)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff13077,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fc4c37,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c37
+}, {
+ .itc_desc = "1 socket, 4-channel way (7)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff13099,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x03fc4c19,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c19
+}, {
+ .itc_desc = "1 socket, 4-channel way (8)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x0ff130ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c3f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c3f
+}, {
+ .itc_desc = "1 socket, 4-channel way (9)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x8ff13006,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x23fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x23fc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (10)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x3ff13046,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0ffc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0ffc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (11)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x4ff13086,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x13fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x13fc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (12)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x9ff130c6,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x27fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x27fc4c06
+}, {
+ .itc_desc = "1 socket, 4-channel way (13)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0xdff13026,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x37fc4c26,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x37fc4c26
+}, {
+ .itc_desc = "1 socket, 4-channel way (14)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0xeff13077,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x3bfc4c37,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3bfc4c37
+}, {
+ .itc_desc = "1 socket, 4-channel way (15)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x4ff13099,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x13fc4c19,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x13fc4c19
+}, {
+ .itc_desc = "1 socket, 4-channel way (16)",
+ .itc_imc = &imc_tad_1s_4cw,
+ .itc_pa = 0x8ff130ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x23fc4c3f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x23fc4c3f
+},
+/*
+ * Test the first variation of mod3 rules. We basically try to find addresses
+ * that map to all 6 channels and then do different variations thereof. We
+ * mostly use the addresses from the previous test run to get a good random
+ * smattering of addresses.
+ */
+{
+ .itc_desc = "1s mod 3 45t6 (1)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff13006,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (2)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff13046,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (3)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff13086,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (4)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff130c6,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x03fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (5)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff13026,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x03fc4c26,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c26
+}, {
+ .itc_desc = "1s mod 3 45t6 (6)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff13077,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c37,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c37
+}, {
+ .itc_desc = "1s mod 3 45t6 (7)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff13099,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x03fc4c19,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c19
+}, {
+ .itc_desc = "1s mod 3 45t6 (8)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x0ff130ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x03fc4c3f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x03fc4c3f
+}, {
+ .itc_desc = "1s mod 3 45t6 (9)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x8ff13006,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x23fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x23fc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (10)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x3ff13046,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0ffc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0ffc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (11)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x4ff13086,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x13fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x13fc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (12)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x9ff130c6,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x27fc4c06,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x27fc4c06
+}, {
+ .itc_desc = "1s mod 3 45t6 (13)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0xdff13026,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x37fc4c26,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x37fc4c26
+}, {
+ .itc_desc = "1s mod 3 45t6 (14)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0xeff13077,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x3bfc4c37,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x3bfc4c37
+}, {
+ .itc_desc = "1s mod 3 45t6 (15)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x4ff13099,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x13fc4c19,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x13fc4c19
+}, {
+ .itc_desc = "1s mod 3 45t6 (16)",
+ .itc_imc = &imc_tad_skx_mod3_45t6,
+ .itc_pa = 0x8ff130ff,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x23fc4c3f,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x23fc4c3f
+},
+/*
+ * Now use PA bits 45:8 to determine the basic mod3 rule. We make sure that we
+ * can construct addresses that hit every routing table entry.
+ */
+{
+ .itc_desc = "1s mod 3 45t8 (1)",
+ .itc_imc = &imc_tad_skx_mod3_45t8,
+ .itc_pa = 0x00000000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod 3 45t8 (2)",
+ .itc_imc = &imc_tad_skx_mod3_45t8,
+ .itc_pa = 0x00000040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod 3 45t8 (3)",
+ .itc_imc = &imc_tad_skx_mod3_45t8,
+ .itc_pa = 0x00000100,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x40,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x40
+}, {
+ .itc_desc = "1s mod 3 45t8 (4)",
+ .itc_imc = &imc_tad_skx_mod3_45t8,
+ .itc_pa = 0x00000140,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x40,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x40
+}, {
+ .itc_desc = "1s mod 3 45t8 (5)",
+ .itc_imc = &imc_tad_skx_mod3_45t8,
+ .itc_pa = 0x00000280,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x80,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x80
+}, {
+ .itc_desc = "1s mod 3 45t8 (6)",
+ .itc_imc = &imc_tad_skx_mod3_45t8,
+ .itc_pa = 0x00000240,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x80,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x80
+},
+/*
+ * Hit every valid routing table entry with a 45:12 rule.
+ */
+{
+ .itc_desc = "1s mod 3 45t12 (1)",
+ .itc_imc = &imc_tad_skx_mod3_45t12,
+ .itc_pa = 0x00000000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod 3 45t12 (2)",
+ .itc_imc = &imc_tad_skx_mod3_45t12,
+ .itc_pa = 0x00000040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod 3 45t12 (3)",
+ .itc_imc = &imc_tad_skx_mod3_45t12,
+ .itc_pa = 0x00001000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod 3 45t12 (4)",
+ .itc_imc = &imc_tad_skx_mod3_45t12,
+ .itc_pa = 0x00001040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod 3 45t12 (5)",
+ .itc_imc = &imc_tad_skx_mod3_45t12,
+ .itc_pa = 0x00002080,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+}, {
+ .itc_desc = "1s mod 3 45t12 (6)",
+ .itc_imc = &imc_tad_skx_mod3_45t12,
+ .itc_pa = 0x00002040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+},
+/*
+ * Test to make sure we can trigger all variants of mod2 favoring 0/1.
+ */
+{
+ .itc_desc = "1s mod2_01 45t12 (1)",
+ .itc_imc = &imc_tad_skx_mod2_01_45t12,
+ .itc_pa = 0x00000000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod2_01 45t12 (2)",
+ .itc_imc = &imc_tad_skx_mod2_01_45t12,
+ .itc_pa = 0x00000040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod2_01 45t12 (3)",
+ .itc_imc = &imc_tad_skx_mod2_01_45t12,
+ .itc_pa = 0x00001000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod2_01 45t12 (4)",
+ .itc_imc = &imc_tad_skx_mod2_01_45t12,
+ .itc_pa = 0x00001040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod2_01 45t12 (5)",
+ .itc_imc = &imc_tad_skx_mod2_01_45t12,
+ .itc_pa = 0x00002080,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+}, {
+ .itc_desc = "1s mod2_01 45t12 (6)",
+ .itc_imc = &imc_tad_skx_mod2_01_45t12,
+ .itc_pa = 0x00002040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+},
+/*
+ * Test to make sure we can trigger all variants of mod2 favoring 1/2.
+ */
+{
+ .itc_desc = "1s mod2_12 45t12 (1)",
+ .itc_imc = &imc_tad_skx_mod2_12_45t12,
+ .itc_pa = 0x00000000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod2_12 45t12 (2)",
+ .itc_imc = &imc_tad_skx_mod2_12_45t12,
+ .itc_pa = 0x00000040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod2_12 45t12 (3)",
+ .itc_imc = &imc_tad_skx_mod2_12_45t12,
+ .itc_pa = 0x00001000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod2_12 45t12 (4)",
+ .itc_imc = &imc_tad_skx_mod2_12_45t12,
+ .itc_pa = 0x00001040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod2_12 45t12 (5)",
+ .itc_imc = &imc_tad_skx_mod2_12_45t12,
+ .itc_pa = 0x00002080,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+}, {
+ .itc_desc = "1s mod2_12 45t12 (6)",
+ .itc_imc = &imc_tad_skx_mod2_12_45t12,
+ .itc_pa = 0x00002040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 2,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+},
+/*
+ * Test to make sure we can trigger all variants of mod2 favoring 0/2.
+ */
+{
+ .itc_desc = "1s mod2_02 45t12 (1)",
+ .itc_imc = &imc_tad_skx_mod2_02_45t12,
+ .itc_pa = 0x00000000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod2_02 45t12 (2)",
+ .itc_imc = &imc_tad_skx_mod2_02_45t12,
+ .itc_pa = 0x00000040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x0,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x0
+}, {
+ .itc_desc = "1s mod2_02 45t12 (3)",
+ .itc_imc = &imc_tad_skx_mod2_02_45t12,
+ .itc_pa = 0x00001000,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod2_02 45t12 (4)",
+ .itc_imc = &imc_tad_skx_mod2_02_45t12,
+ .itc_pa = 0x00001040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 1,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x400,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x400
+}, {
+ .itc_desc = "1s mod2_02 45t12 (5)",
+ .itc_imc = &imc_tad_skx_mod2_02_45t12,
+ .itc_pa = 0x00002080,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 0,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+}, {
+ .itc_desc = "1s mod2_02 45t12 (6)",
+ .itc_imc = &imc_tad_skx_mod2_02_45t12,
+ .itc_pa = 0x00002040,
+ .itc_pass = B_TRUE,
+ .itc_nodeid = 0,
+ .itc_tadid = 0,
+ .itc_channelid = 1,
+ .itc_chanaddr = 0x800,
+ .itc_dimmid = 0,
+ .itc_rankid = 0,
+ .itc_rankaddr = 0x800
+}, {
+ .itc_desc = NULL
+} };
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files
index e33bb30c87..d541e92bf3 100644
--- a/usr/src/uts/i86pc/Makefile.files
+++ b/usr/src/uts/i86pc/Makefile.files
@@ -324,3 +324,10 @@ ASSYM_DEPS += \
CPR_IMPL_OBJS = cpr_impl.o cpr_wakecode.o
$(KDI_ASSYM_DEPS:%=$(OBJS_DIR)/%): $(DSF_DIR)/$(OBJS_DIR)/kdi_assym.h
+
+#
+# Intel Integrated Memory Controller
+# (Sandy Bridge - Cascade Lake)
+#
+IMC_OBJS = imc.o imc_decode.o imc_dump.o
+IMCSTUB_OBJS = imcstub.o
diff --git a/usr/src/uts/i86pc/Makefile.i86pc b/usr/src/uts/i86pc/Makefile.i86pc
index 66570f2f41..47ca5bf8e9 100644
--- a/usr/src/uts/i86pc/Makefile.i86pc
+++ b/usr/src/uts/i86pc/Makefile.i86pc
@@ -260,6 +260,7 @@ DRV_KMODS += fipe
DRV_KMODS += vmm
DRV_KMODS += viona
DRV_KMODS += ppt
+DRV_KMODS += imc imcstub
DRV_KMODS += cpudrv
diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules
index 95a84aa8ab..1863226fd0 100644
--- a/usr/src/uts/i86pc/Makefile.rules
+++ b/usr/src/uts/i86pc/Makefile.rules
@@ -22,7 +22,7 @@
#
# Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
-# Copyright 2017 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
#
@@ -121,6 +121,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/ioat/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/imc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(SRC)/common/mc/imc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pci/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
diff --git a/usr/src/uts/i86pc/imc/Makefile b/usr/src/uts/i86pc/imc/Makefile
new file mode 100644
index 0000000000..f649faa4d4
--- /dev/null
+++ b/usr/src/uts/i86pc/imc/Makefile
@@ -0,0 +1,51 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE = imc
+OBJECTS = $(IMC_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(IMC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/i86pc/io/imc
+
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+ALL_TARGET = $(BINARY) $(CONFMOD)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(CONF_SRCDIR)
+LDFLAGS += -dy
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/i86pc/Makefile.targ
diff --git a/usr/src/uts/i86pc/imcstub/Makefile b/usr/src/uts/i86pc/imcstub/Makefile
new file mode 100644
index 0000000000..afec0a8127
--- /dev/null
+++ b/usr/src/uts/i86pc/imcstub/Makefile
@@ -0,0 +1,49 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE = imcstub
+OBJECTS = $(IMCSTUB_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(IMCSTUB_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+LDFLAGS += -dy -Ndrv/imc
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/i86pc/Makefile.targ
diff --git a/usr/src/uts/i86pc/io/imc/imc.c b/usr/src/uts/i86pc/io/imc/imc.c
new file mode 100644
index 0000000000..25ba86061b
--- /dev/null
+++ b/usr/src/uts/i86pc/io/imc/imc.c
@@ -0,0 +1,2972 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * Generic Intel Integrated Memory Controller (IMC) Driver
+ *
+ * This driver talks to the CPU's IMC to understand the detailed topology of the
+ * processor and to determine how to map between physical addresses to the
+ * corresponding DIMM. This driver supports the following generations of Intel
+ * chips:
+ *
+ * - Sandy Bridge
+ * - Ivy Bridge
+ * - Haswell
+ * - Broadwell
+ * - Skylake / Cascade Lake
+ *
+ * Memory Decoding
+ * ---------------
+ *
+ * For more detailed summaries of the memory decoding process, please refer to
+ * the Intel External Design Specifications for the corresponding processor.
+ * What follows is a rough overview of how the memory decoding system works.
+ *
+ * First, we'd like to define the following concepts:
+ *
+ * SYSTEM ADDRESS
+ *
+ * This is a physical address that the operating system normally uses. This
+ * address may refer to DRAM, it may refer to memory mapped PCI
+ * configuration space or device registers, or it may refer to other parts
+ * of the system's memory map, such as the extended advanced programmable
+ * interrupt controller (xAPIC), etc.
+ *
+ * DIMM
+ *
+ * Dual-inline memory module. This refers to a physical stick of volatile
+ * memory that is inserted into a slot on the motherboard.
+ *
+ * RANK
+ *
+ * A potential sub-division of a DIMM. A DIMM's memory capacity is divided
+ * into a number of equal sized ranks. For example, an 8 GiB DIMM, may have
+ * 1 8 GiB rank, 2 4 GiB ranks, or 4 2 GiB ranks.
+ *
+ * RANK ADDRESS
+ *
+ * An address that exists in the context of a given rank on a DIMM. All
+ * ranks have overlapping addresses, so the address 0x400 exists on all
+ * ranks on a given DIMM.
+ *
+ * CHANNEL
+ *
+ * Multiple DIMMs may be combined into a single channel. The channel
+ * represents the combined memory of all the DIMMs. A given channel only
+ * ever exists on a socket and is bound to a single memory controller.
+ *
+ * CHANNEL ADDRESS
+ *
+ * This is an address that exists logically on a channel. Each address on a
+ * channel maps to a corresponding DIMM that exists on that channel. The
+ * address space on one channel is independent from that on another. This
+ * means that address 0x1000 can exist on each memory channel in the
+ * system.
+ *
+ * INTERLEAVE
+ *
+ * There are several different cases where interleaving occurs on the
+ * system. For example, addresses may be interleaved across sockets,
+ * memory channels, or DIMM ranks. When addresses are interleaved, then
+ * some number of bits in an address are used to select which target to go
+ * to (usually through a look up table). The effect of interleaving is that
+ * addresses that are next to one another may not all go to the same
+ * device. The following image shows a non-interleaving case.
+ *
+ * 0x0fff +-----+ +-----+ 0x7ff
+ * | |\___________/| |
+ * | | __________ | (b) |
+ * | | / \| |
+ * 0x0800 |=====|= +-----+ 0x000 +-----+ 0x7ff
+ * | | \______________________________/| |
+ * | | _______________________________ | (a) |
+ * | |/ \| |
+ * 0x0000 +-----+ +-----+ 0x000
+ *
+ * In this example of non-interleaving, addresses 0x0000 to 0x07ff go to
+ * device (a). While, addresses 0x08000 to 0xfff, go to device (b).
+ * However, each range is divided into the same number of components.
+ *
+ * If instead, we were to look at that with interleaving, what we might say
+ * is that rather than splitting the range in half, we might say that if
+ * the address has bit 8 set (0x100), then it goes to (b), otherwise it
+ * goes to (a). This means that addresses 0x000 to 0x0ff, would go to (a).
+ * 0x100 to 0x1ff would go to (b). 0x200 to 0x2ff would go back to (a)
+ * again, and then 0x300 to 0x2ff would go back to (b). This would continue
+ * for a while. This would instead look something more like:
+ *
+ *
+ * 0x0fff +-----+ A: 0x7ff +---------+ B: 0x7ff +---------+
+ * | (b) | | e00-eff | | f00-fff |
+ * 0x0f00 |-----| 0x700 +---------+ 0x700 +---------+
+ * | (a) | | c00-cff | | d00-dff |
+ * 0x0e00 ~~~~~~~ 0x600 +---------+ 0x600 +---------+
+ * *** | a00-aff | | b00-bff |
+ * 0x0400 ~~~~~~~ 0x500 +---------+ 0x500 +---------+
+ * | (b) | | 800-8ff | | 900-9ff |
+ * 0x0300 |-----| 0x400 +---------+ 0x400 +---------+
+ * | (a) | | 600-6ff | | 700-7ff |
+ * 0x0200 |-----| 0x300 +---------+ 0x300 +---------+
+ * | (b) | | 400-4ff | | 500-5ff |
+ * 0x0100 |-----| 0x200 +---------+ 0x200 +---------+
+ * | (a) | | 200-2ff | | 300-3ff |
+ * 0x0000 +-----+ 0x100 +---------+ 0x100 +---------+
+ * | 000-0ff | | 100-1ff |
+ * 0x000 +---------+ 0x000 +---------+
+ *
+ * In this example we've performed two-way interleaving. The number of ways
+ * that something can interleave varies based on what we're interleaving
+ * between.
+ *
+ * MEMORY CONTROLLER
+ *
+ * A given processor die (see uts/i86pc/os/cpuid.c) contains a number of
+ * memory controllers. Usually 1 or two. Each memory controller supports a
+ * given number of DIMMs, which are divided across multiple channels.
+ *
+ * TARGET ADDRESS DECODER
+ *
+ * The target address decoder (TAD) is responsible for taking a system
+ * address and transforming it into a channel address based on the rules
+ * that are present. Each memory controller has a corresponding TAD. The
+ * TAD is often contained in a device called a 'Home Agent'.
+ *
+ * SYSTEM ADDRESS DECODER
+ *
+ * The system address decoder (SAD) is responsible for taking a system
+ * address and directing it to the right place, whether this be memory or
+ * otherwise. There is a single memory controller per socket (see
+ * uts/i86pc/os/cpuid.c) that is shared between all the cores currently.
+ *
+ * NODE IDENTIFIER
+ *
+ * The node identifier is used to uniquely identify an element in the
+ * various routing topologies on the die (see uts/i86pc/os/cpuid.c for the
+ * definition of 'die'). One can roughly think about this as a unique
+ * identifier for the socket itself. In general, the primary node ID for a
+ * socket should map to the socket APIC ID.
+ *
+ * Finding Devices
+ * ---------------
+ *
+ * There is a bit of a chicken and egg problem on Intel systems and in the
+ * device driver interface. The information that we need in the system is spread
+ * out amongst a large number of different PCI devices that the processor
+ * exposes. The number of such devices can vary based on the processor
+ * generation and the specific SKU in the processor. To deal with this, we break
+ * the driver into two different components: a stub driver and the full driver.
+ *
+ * The stub driver has aliases for all known PCI devices that we might attach to
+ * in a given generation on the system. This driver is called 'imcstub'. When a
+ * stub attaches, it just registers itself with the main driver, upon which it
+ * has a module dependency.
+ *
+ * The main driver, 'imc', is a pseudo-device driver. When it first attaches, it
+ * kicks off a scan of the device tree which takes place in a task queue. Once
+ * there, it determines the number of devices that it expects to exist by
+ * walking the tree and comparing it against the generation-specific table.
+ *
+ * If all devices are found, we'll go ahead and read through all the devices and
+ * build a map of all the information we need to understand the topology of the
+ * system and to be able to decode addresses. We do this here, because we can be
+ * asked to perform decoding in dangerous contexts (after taking an MCE, panic,
+ * etc) where we don't want to have to rely on the broader kernel functioning at
+ * this point in time.
+ *
+ * Once our topology is built, we'll create minor nodes which are used by the
+ * fault management architecture to query for information and register our
+ * decoding functionality with the kernel.
+ *
+ * PCI Numbering
+ * -------------
+ *
+ * For each device that we care about, Intel defines the device and function
+ * that we can expect to find the information and PCI configuration space
+ * registers that we care about at. However, the PCI bus is not well defined.
+ * Devices that are on the same socket use the same set of bus numbers; however,
+ * some sockets have multiple device numbers that they'll use to represent
+ * different classes. These bus numbers are programmed by systems firmware as
+ * part of powering on the system. This means, that we need the ability to
+ * map together these disparate ranges ourselves.
+ *
+ * There is a device called a utility box (UBOX), which exists per-socket and
+ * maps the different sockets together. We use this to determine which devices
+ * correspond to which sockets.
+ *
+ * Mapping Sockets
+ * ---------------
+ *
+ * Another wrinkle is that the way that the OS sees the numbering of the CPUs is
+ * generally based on the APIC ID (see uts/i86pc/os/cpuid.c for more
+ * information). However, to map to the corresponding socket, we need to look at
+ * the socket's node ID. The order of PCI buses in the system is not required to
+ * have any relation to the socket ID. Therefore, we have to have yet another
+ * indirection table in the imc_t.
+ *
+ * Exposing Data
+ * -------------
+ *
+ * We expose topology data to FMA using the OS-private memory controller
+ * interfaces. By creating minor nodes of the type, 'ddi_mem_ctrl', there are a
+ * number of specific interfaces that we can then implement. The ioctl API asks
+ * us for a snapshot of data, which basically has us go through and send an
+ * nvlist_t to userland. This nvlist_t is constructed as part of the scan
+ * process. This nvlist uses the version 1 format, which more explicitly encodes
+ * the topology in a series of nested nvlists.
+ *
+ * In addition, the tool /usr/lib/fm/fmd/mcdecode can be used to query the
+ * decoder and ask it to perform decoding.
+ *
+ * Decoding Addresses
+ * ------------------
+ *
+ * The decoding logic can be found in common/imc/imc_decode.c. This file is
+ * shared between the kernel and userland to allow for easier testing and
+ * additional flexibility in operation. The decoding process happens in a few
+ * different phases.
+ *
+ * The first phase, is to determine which memory controller on which socket is
+ * responsible for this data. To determine this, we use the system address
+ * decoder and walk the rules, looking for the correct target. There are various
+ * manipulations to the address that exist which are used to determine which
+ * index we use. The way that we interpret the output of the rule varies
+ * somewhat based on the generation. Sandy Bridge just has a node ID which
+ * points us to the socket with its single IMC. On Ivy Bridge through Broadwell,
+ * the memory controller to use is also encoded in part of the node ID. Finally,
+ * on Skylake, the SAD tells us which socket to look at. The socket in question
+ * then has a routing table which tells us which channel on which memory
+ * controller that is local to that socket.
+ *
+ * Once we have the target memory controller, we walk the list of target address
+ * decoder rules. These rules can help tell us which channel we care about
+ * (which is required on Sandy Bridge through Broadwell) and then describe some
+ * amount of the interleaving rules which are used to turn the system address
+ * into a channel address.
+ *
+ * Once we know the channel and the channel address, we walk the rank interleave
+ * rules which help us determine which DIMM and the corresponding rank on it
+ * that the corresponding channel address is on. It also has logic that we need
+ * to use to determine how to transform a channel address into an address on
+ * that specific rank. Once we have that, then the initial decoding is done.
+ *
+ * The logic in imc_decode.c is abstracted away from the broader kernel CMI
+ * logic. This is on purpose and allows us not only an easier time unit testing
+ * the logic, but also allows us to express more high fidelity errors that are
+ * translated into a much smaller subset. This logic is exercised in the
+ * 'imc_test' program which is built in 'test/os-tests/tests/imc'.
+ *
+ * Limitations
+ * -----------
+ *
+ * Currently, this driver has the following limitations:
+ *
+ * o It doesn't decode the row and column addresses.
+ * o It doesn't encode from a DIMM address to a system address.
+ * o It doesn't properly support lockstep and mirroring modes on Sandy Bridge -
+ * Broadwell platforms.
+ * o It doesn't support virtual lockstep and adaptive mirroring on Purley
+ * platforms.
+ * o It doesn't properly handle Intel Optane (3D-X Point) NVDIMMs.
+ * o It doesn't know how to decode three way channel interleaving.
+ *
+ * None of these are intrinsic problems to the driver, it's mostly a matter of
+ * having proper documentation and testing.
+ */
+
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/pci.h>
+#include <sys/sysmacros.h>
+#include <sys/avl.h>
+#include <sys/stat.h>
+#include <sys/policy.h>
+
+#include <sys/cpu_module.h>
+#include <sys/mc.h>
+#include <sys/mc_intel.h>
+
+#include "imc.h"
+
+/*
+ * These tables contain generational data that varies between processor
+ * generation such as the maximum number of sockets, memory controllers, and the
+ * offsets of the various registers.
+ */
+
+static const imc_gen_data_t imc_gen_data_snb = {
+ .igd_max_sockets = 4,
+ .igd_max_imcs = 2,
+ .igd_max_channels = 4,
+ .igd_max_dimms = 3,
+ .igd_max_ranks = IMC_MTR_DDR_RANKS_MAX,
+ .igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1,
+ IMC_REG_MC_MTR2 },
+ .igd_mcmtr_offset = 0x7c,
+ .igd_tolm_offset = 0x80,
+ .igd_tohm_low_offset = 0x84,
+ .igd_sad_dram_offset = 0x80,
+ .igd_sad_ndram_rules = 10,
+ .igd_sad_nodeid_offset = 0x40,
+ .igd_tad_nrules = 12,
+ .igd_tad_rule_offset = 0x40,
+ .igd_tad_chan_offset = 0x90,
+ .igd_tad_sysdef = 0x80,
+ .igd_tad_sysdef2 = 0x84,
+ .igd_mc_mirror = 0xac,
+ .igd_rir_nways = 5,
+ .igd_rir_way_offset = 0x108,
+ .igd_rir_nileaves = 8,
+ .igd_rir_ileave_offset = 0x120,
+ .igd_ubox_cpubusno_offset = 0xd0,
+};
+
+static const imc_gen_data_t imc_gen_data_ivb = {
+ .igd_max_sockets = 4,
+ .igd_max_imcs = 2,
+ .igd_max_channels = 4,
+ .igd_max_dimms = 3,
+ .igd_max_ranks = IMC_MTR_DDR_RANKS_MAX,
+ .igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1,
+ IMC_REG_MC_MTR2 },
+ .igd_mcmtr_offset = 0x7c,
+ .igd_tolm_offset = 0x80,
+ .igd_tohm_low_offset = 0x84,
+ .igd_sad_dram_offset = 0x60,
+ .igd_sad_ndram_rules = 20,
+ .igd_sad_nodeid_offset = 0x40,
+ .igd_tad_nrules = 12,
+ .igd_tad_rule_offset = 0x40,
+ .igd_tad_chan_offset = 0x90,
+ .igd_tad_sysdef = 0x80,
+ .igd_tad_sysdef2 = 0x84,
+ .igd_mc_mirror = 0xac,
+ .igd_rir_nways = 5,
+ .igd_rir_way_offset = 0x108,
+ .igd_rir_nileaves = 8,
+ .igd_rir_ileave_offset = 0x120,
+ .igd_ubox_cpubusno_offset = 0xd0,
+};
+
+static const imc_gen_data_t imc_gen_data_has_brd = {
+ .igd_max_sockets = 4,
+ .igd_max_imcs = 2,
+ .igd_max_channels = 4,
+ .igd_max_dimms = 3,
+ .igd_max_ranks = IMC_MTR_DDR_RANKS_MAX_HAS_SKX,
+ .igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1,
+ IMC_REG_MC_MTR2 },
+ .igd_mcmtr_offset = 0x7c,
+ .igd_tolm_offset = 0xd0,
+ .igd_tohm_low_offset = 0xd4,
+ .igd_tohm_hi_offset = 0xd8,
+ .igd_sad_dram_offset = 0x60,
+ .igd_sad_ndram_rules = 20,
+ .igd_sad_nodeid_offset = 0x40,
+ .igd_tad_nrules = 12,
+ .igd_tad_rule_offset = 0x40,
+ .igd_tad_chan_offset = 0x90,
+ .igd_tad_sysdef = 0x80,
+ .igd_tad_sysdef2 = 0x84,
+ .igd_mc_mirror = 0xac,
+ .igd_rir_nways = 5,
+ .igd_rir_way_offset = 0x108,
+ .igd_rir_nileaves = 8,
+ .igd_rir_ileave_offset = 0x120,
+ .igd_ubox_cpubusno_offset = 0xd0,
+};
+
+static const imc_gen_data_t imc_gen_data_skx = {
+ .igd_max_sockets = 8,
+ .igd_max_imcs = 2,
+ .igd_max_channels = 3,
+ .igd_max_dimms = 2,
+ .igd_max_ranks = IMC_MTR_DDR_RANKS_MAX,
+ .igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1 },
+ .igd_mcmtr_offset = 0x87c,
+ .igd_topo_offset = 0x88,
+ .igd_tolm_offset = 0xd0,
+ .igd_tohm_low_offset = 0xd4,
+ .igd_tohm_hi_offset = 0xd8,
+ .igd_sad_dram_offset = 0x60,
+ .igd_sad_ndram_rules = 24,
+ .igd_sad_nodeid_offset = 0xc0,
+ .igd_tad_nrules = 8,
+ .igd_tad_rule_offset = 0x850,
+ .igd_tad_chan_offset = 0x90,
+ .igd_rir_nways = 4,
+ .igd_rir_way_offset = 0x108,
+ .igd_rir_nileaves = 4,
+ .igd_rir_ileave_offset = 0x120,
+ .igd_ubox_cpubusno_offset = 0xcc,
+};
+
+/*
+ * This table contains all of the devices that we're looking for from a stub
+ * perspective. These are organized by generation. Different generations behave
+ * in slightly different ways. For example, Sandy Bridge through Broadwell use
+ * unique PCI IDs for each PCI device/function combination that appears. Whereas
+ * Skylake based systems use the same PCI ID; however, different device/function
+ * values indicate that the IDs are used for different purposes.
+ */
+/* BEGIN CSTYLED */
+static const imc_stub_table_t imc_stub_table[] = {
+ /* Sandy Bridge */
+ { IMC_GEN_SANDY, IMC_TYPE_MC0_MAIN0, 0x3ca8, 15, 0, "IMC 0 Main 0" },
+ { IMC_GEN_SANDY, IMC_TYPE_MC0_MAIN1, 0x3c71, 15, 1, "IMC 0 Main 0" },
+ { IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL0, 0x3caa, 15, 2, "IMC 0 Channel 0 Info" },
+ { IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL1, 0x3cab, 15, 3, "IMC 0 Channel 1 Info" },
+ { IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL2, 0x3cac, 15, 4, "IMC 0 Channel 2 Info" },
+ { IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL3, 0x3cad, 15, 5, "IMC 0 Channel 3 Info" },
+ { IMC_GEN_SANDY, IMC_TYPE_SAD_DRAM, 0x3cf4, 12, 6, "SAD DRAM Rules" },
+ { IMC_GEN_SANDY, IMC_TYPE_SAD_MMIO, 0x3cf5, 13, 6, "SAD MMIO Rules" },
+ { IMC_GEN_SANDY, IMC_TYPE_SAD_MISC, 0x3cf6, 12, 7, "SAD Memory Map" },
+ { IMC_GEN_SANDY, IMC_TYPE_UBOX, 0x3ce0, 11, 0, "UBox" },
+ { IMC_GEN_SANDY, IMC_TYPE_UBOX_CPUBUSNO, 0x3ce3, 11, 3, "UBox Scratch" },
+ { IMC_GEN_SANDY, IMC_TYPE_HA0, 0x3ca0, 14, 0, "Home Agent" },
+ /* Ivy Bridge */
+ { IMC_GEN_IVY, IMC_TYPE_MC0_MAIN0, 0x0ea8, 15, 0, "IMC 0 Main 0" },
+ { IMC_GEN_IVY, IMC_TYPE_MC0_MAIN1, 0x0e71, 15, 1, "IMC 0 Main 1" },
+ { IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL0, 0x0eaa, 15, 2, "IMC 0 Channel 0 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL1, 0x0eab, 15, 3, "IMC 0 Channel 1 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL2, 0x0eac, 15, 4, "IMC 0 Channel 2 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL3, 0x0ead, 15, 5, "IMC 0 Channel 3 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_MC1_MAIN0, 0x0e68, 29, 0, "IMC 1 Main 0" },
+ { IMC_GEN_IVY, IMC_TYPE_MC1_MAIN1, 0x0e79, 29, 1, "IMC 1 Main 1" },
+ { IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL0, 0x0e6a, 15, 2, "IMC 1 Channel 0 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL1, 0x0e6b, 15, 3, "IMC 1 Channel 1 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL2, 0x0e6c, 15, 4, "IMC 1 Channel 2 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL3, 0x0e6d, 15, 5, "IMC 1 Channel 3 Info" },
+ { IMC_GEN_IVY, IMC_TYPE_SAD_DRAM, 0x0ec8, 22, 0, "SAD DRAM Rules" },
+ { IMC_GEN_IVY, IMC_TYPE_SAD_MMIO, 0x0ec9, 22, 1, "SAD MMIO Rules" },
+ { IMC_GEN_IVY, IMC_TYPE_SAD_MISC, 0x0eca, 22, 2, "SAD Memory Map" },
+ { IMC_GEN_IVY, IMC_TYPE_UBOX, 0x0e1e, 11, 0, "UBox" },
+ { IMC_GEN_IVY, IMC_TYPE_UBOX_CPUBUSNO, 0x0e1f, 11, 3, "UBox Scratch" },
+ { IMC_GEN_IVY, IMC_TYPE_HA0, 0x0ea0, 14, 0, "Home Agent 0" },
+ { IMC_GEN_IVY, IMC_TYPE_HA1, 0x0e60, 28, 0, "Home Agent 1" },
+ /* Haswell */
+ { IMC_GEN_HASWELL, IMC_TYPE_MC0_MAIN0, 0x2fa8, 19, 0, "IMC 0 Main 0" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC0_MAIN1, 0x2f71, 19, 1, "IMC 0 Main 1" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL0, 0x2faa, 19, 2, "IMC 0 Channel 0 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL1, 0x2fab, 19, 3, "IMC 0 Channel 1 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL2, 0x2fac, 19, 4, "IMC 0 Channel 2 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL3, 0x2fad, 19, 5, "IMC 0 Channel 3 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC1_MAIN0, 0x2f68, 22, 0, "IMC 1 Main 0" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC1_MAIN1, 0x2f79, 22, 1, "IMC 1 Main 1" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL0, 0x2f6a, 22, 2, "IMC 1 Channel 0 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL1, 0x2f6b, 22, 3, "IMC 1 Channel 1 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL2, 0x2f6c, 22, 4, "IMC 1 Channel 2 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL3, 0x2f6d, 22, 5, "IMC 1 Channel 3 Info" },
+ { IMC_GEN_HASWELL, IMC_TYPE_SAD_DRAM, 0x2ffc, 15, 4, "SAD DRAM Rules" },
+ { IMC_GEN_HASWELL, IMC_TYPE_SAD_MMIO, 0x2ffd, 15, 5, "SAD MMIO Rules" },
+ { IMC_GEN_HASWELL, IMC_TYPE_VTD_MISC, 0x2f28, 5, 0, "Misc. Vritualization" },
+ { IMC_GEN_HASWELL, IMC_TYPE_UBOX, 0x2f1e, 16, 5, "UBox" },
+ { IMC_GEN_HASWELL, IMC_TYPE_UBOX_CPUBUSNO, 0x2f1f, 16, 7, "UBox Scratch" },
+ { IMC_GEN_HASWELL, IMC_TYPE_HA0, 0x2fa0, 18, 0, "Home Agent 0" },
+ { IMC_GEN_HASWELL, IMC_TYPE_HA1, 0x2f60, 18, 4, "Home Agent 1" },
+ /* Broadwell Devices */
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC0_MAIN0, 0x6fa8, 19, 0, "IMC 0 Main 0" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC0_MAIN1, 0x6f71, 19, 1, "IMC 0 Main 1" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL0, 0x6faa, 19, 2, "IMC 0 Channel 0 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL1, 0x6fab, 19, 3, "IMC 0 Channel 1 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL2, 0x6fac, 19, 4, "IMC 0 Channel 2 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL3, 0x6fad, 19, 5, "IMC 0 Channel 3 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC1_MAIN0, 0x6f68, 22, 0, "IMC 1 Main 0" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC1_MAIN1, 0x6f79, 22, 1, "IMC 1 Main 1" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL0, 0x6f6a, 22, 2, "IMC 1 Channel 0 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL1, 0x6f6b, 22, 3, "IMC 1 Channel 1 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL2, 0x6f6c, 22, 4, "IMC 1 Channel 2 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL3, 0x6f6d, 22, 5, "IMC 1 Channel 3 Info" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_SAD_DRAM, 0x6ffc, 15, 4, "SAD DRAM Rules" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_SAD_MMIO, 0x6ffd, 15, 5, "SAD MMIO Rules" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_VTD_MISC, 0x6f28, 5, 0, "Misc. Vritualization" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_UBOX, 0x6f1e, 16, 5, "UBox" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_UBOX_CPUBUSNO, 0x6f1f, 16, 7, "UBox Scratch" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_HA0, 0x6fa0, 18, 0, "Home Agent 0" },
+ { IMC_GEN_BROADWELL, IMC_TYPE_HA1, 0x6f60, 18, 4, "Home Agent 1" },
+ /* Skylake and Cascade Lake Devices */
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC0_M2M, 0x2066, 8, 0, "IMC 0 M2M" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC1_M2M, 0x2066, 9, 0, "IMC 0 M2M" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC0_MAIN0, 0x2040, 10, 0, "IMC 0 Main / Channel 0" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC1_MAIN0, 0x2040, 12, 0, "IMC 0 Main / Channel 0" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC0_CHANNEL1, 0x2044, 10, 4, "IMC 0 Channel 1" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC0_CHANNEL2, 0x2048, 11, 0, "IMC 0 Channel 2" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC1_CHANNEL1, 0x2044, 12, 4, "IMC 1 Channel 1" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_MC1_CHANNEL2, 0x2048, 13, 0, "IMC 1 Channel 2" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_DRAM, 0x2054, 29, 0, "SAD DRAM Rules" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MMIO, 0x2055, 29, 1, "SAD MMIO Rules" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_VTD_MISC, 0x2024, 5, 0, "Misc. Virtualization" },
+
+ /*
+ * There is one SAD MC Route type device per core! Because of this a
+ * wide array of device and functions are allocated. For now, we list
+ * all 28 of them out.
+ */
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 0, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 1, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 2, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 3, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 4, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 5, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 6, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 7, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 0, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 1, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 2, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 3, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 4, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 5, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 6, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 7, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 0, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 1, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 2, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 3, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 4, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 5, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 6, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 7, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 0, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 1, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 2, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 3, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 4, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 5, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 6, "Per-Core SAD" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 7, "Per-Core SAD" },
+
+ { IMC_GEN_SKYLAKE, IMC_TYPE_UBOX, 0x2014, 8, 0, "UBox" },
+ { IMC_GEN_SKYLAKE, IMC_TYPE_UBOX_CPUBUSNO, 0x2016, 8, 2, "DECS" },
+};
+/* END CSTYLED */
+
+#define IMC_PCI_VENDOR_INTC 0x8086
+
+/*
+ * Our IMC data is global and statically set up during a combination of
+ * _init(9E) and attach(9E). While we have a module dependency between the PCI
+ * stub driver, imcstub, and this pseudo-driver, imc, the dependencies don't
+ * guarantee that the imc driver has finished attaching. As such we make sure
+ * that it can operate without it being attached in any way.
+ */
+static imc_t *imc_data = NULL;
+
+/*
+ * By default we should not allow the stubs to detach as we don't have a good
+ * way of forcing them to attach again. This is provided in case someone does
+ * want to allow the driver to unload.
+ */
+int imc_allow_detach = 0;
+
+static void
+imc_set_gen_data(imc_t *imc)
+{
+ switch (imc->imc_gen) {
+ case IMC_GEN_SANDY:
+ imc->imc_gen_data = &imc_gen_data_snb;
+ break;
+ case IMC_GEN_IVY:
+ imc->imc_gen_data = &imc_gen_data_ivb;
+ break;
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ imc->imc_gen_data = &imc_gen_data_has_brd;
+ break;
+ case IMC_GEN_SKYLAKE:
+ imc->imc_gen_data = &imc_gen_data_skx;
+ break;
+ default:
+ dev_err(imc->imc_dip, CE_PANIC, "imc driver programmer error: "
+ "set to unknown generation: %u", imc->imc_gen);
+ }
+}
+
+/*
+ * If our device (dev_info_t) does not have a non-zero unit address, then
+ * devfsadmd will not pay attention to us at all. Therefore we need to set the
+ * unit address below, before we create minor nodes.
+ *
+ * The rest of the system expects us to have one minor node per socket. The
+ * minor node ID should be the ID of the socket.
+ */
+static boolean_t
+imc_create_minors(imc_t *imc)
+{
+ uint_t i;
+
+ ddi_set_name_addr(imc->imc_dip, "1");
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ char buf[MAXNAMELEN];
+
+ if (snprintf(buf, sizeof (buf), "mc-imc-%u", i) >=
+ sizeof (buf)) {
+ goto fail;
+ }
+
+ if (ddi_create_minor_node(imc->imc_dip, buf, S_IFCHR, i,
+ "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
+ dev_err(imc->imc_dip, CE_WARN, "failed to create "
+ "minor node %u: %s", i, buf);
+ goto fail;
+ }
+ }
+ return (B_TRUE);
+
+fail:
+ ddi_remove_minor_node(imc->imc_dip, NULL);
+ return (B_FALSE);
+}
+
+/*
+ * Check the current MC route value for this SAD. On Skylake systems there is
+ * one per core. Every core should agree. If not, we will not trust the SAD
+ * MCROUTE values and this will cause system address decoding to fail on
+ * skylake.
+ */
+static void
+imc_mcroute_check(imc_t *imc, imc_sad_t *sad, imc_stub_t *stub)
+{
+ uint32_t val;
+
+ val = pci_config_get32(stub->istub_cfgspace,
+ IMC_REG_SKX_SAD_MC_ROUTE_TABLE);
+ if (val == PCI_EINVAL32) {
+ sad->isad_valid |= IMC_SAD_V_BAD_PCI_READ;
+ return;
+ }
+
+ if ((sad->isad_flags & IMC_SAD_MCROUTE_VALID) == 0 && val != 0) {
+ sad->isad_flags |= IMC_SAD_MCROUTE_VALID;
+ sad->isad_mcroute.ismc_raw_mcroute = val;
+ return;
+ }
+
+ /*
+ * Occasionally we see MC ROUTE table entries with a value of zero.
+ * We should ignore those for now.
+ */
+ if (val != sad->isad_mcroute.ismc_raw_mcroute && val != 0) {
+ dev_err(imc->imc_dip, CE_WARN, "SAD MC_ROUTE_TABLE mismatch "
+ "with socket. SAD has val 0x%x, system has %x\n",
+ val, sad->isad_mcroute.ismc_raw_mcroute);
+ sad->isad_valid |= IMC_SAD_V_BAD_MCROUTE;
+ }
+}
+
+/*
+ * On Skylake, many of the devices that we care about are on separate PCI Buses.
+ * These can be mapped together by the DECS register. However, we need to know
+ * how to map different buses together so that we can more usefully associate
+ * information. The set of buses is all present in the DECS register. We'll
+ * effectively assign sockets to buses. This is also still something that comes
+ * up on pre-Skylake systems as well.
+ */
+static boolean_t
+imc_map_buses(imc_t *imc)
+{
+ imc_stub_t *stub;
+ uint_t nsock;
+
+ /*
+ * Find the UBOX_DECS registers so we can establish socket mappings. On
+ * Skylake, there are three different sets of buses that we need to
+ * cover all of our devices, while there are only two before that.
+ */
+ for (nsock = 0, stub = avl_first(&imc->imc_stubs); stub != NULL;
+ stub = AVL_NEXT(&imc->imc_stubs, stub)) {
+ uint32_t busno;
+
+ if (stub->istub_table->imcs_type != IMC_TYPE_UBOX_CPUBUSNO) {
+ continue;
+ }
+
+ busno = pci_config_get32(stub->istub_cfgspace,
+ imc->imc_gen_data->igd_ubox_cpubusno_offset);
+ if (busno == PCI_EINVAL32) {
+ dev_err(imc->imc_dip, CE_WARN, "failed to read "
+ "UBOX_DECS CPUBUSNO0: invalid PCI read");
+ return (B_FALSE);
+ }
+
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ imc->imc_sockets[nsock].isock_nbus = 3;
+ imc->imc_sockets[nsock].isock_bus[0] =
+ IMC_UBOX_CPUBUSNO_0(busno);
+ imc->imc_sockets[nsock].isock_bus[1] =
+ IMC_UBOX_CPUBUSNO_1(busno);
+ imc->imc_sockets[nsock].isock_bus[2] =
+ IMC_UBOX_CPUBUSNO_2(busno);
+ } else {
+ imc->imc_sockets[nsock].isock_bus[0] =
+ IMC_UBOX_CPUBUSNO_0(busno);
+ imc->imc_sockets[nsock].isock_bus[1] =
+ IMC_UBOX_CPUBUSNO_1(busno);
+ imc->imc_sockets[nsock].isock_nbus = 2;
+ }
+ nsock++;
+ }
+ imc->imc_nsockets = nsock;
+
+ return (B_TRUE);
+}
+
+/*
+ * For a given stub that we've found, map it to its corresponding socket based
+ * on the PCI bus that it has.
+ */
+static imc_socket_t *
+imc_map_find_socket(imc_t *imc, imc_stub_t *stub)
+{
+ uint_t i;
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ uint_t bus;
+
+ for (bus = 0; bus < imc->imc_sockets[i].isock_nbus; bus++) {
+ if (imc->imc_sockets[i].isock_bus[bus] ==
+ stub->istub_bus) {
+ return (&imc->imc_sockets[i]);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+static boolean_t
+imc_map_stubs(imc_t *imc)
+{
+ imc_stub_t *stub;
+
+ if (!imc_map_buses(imc)) {
+ return (B_FALSE);
+ }
+
+ stub = avl_first(&imc->imc_stubs);
+ for (stub = avl_first(&imc->imc_stubs); stub != NULL;
+ stub = AVL_NEXT(&imc->imc_stubs, stub)) {
+ imc_socket_t *sock = imc_map_find_socket(imc, stub);
+
+ if (sock == NULL) {
+ dev_err(imc->imc_dip, CE_WARN, "found stub type %u "
+ "PCI%x,%x with bdf %u/%u/%u that does not match a "
+ "known PCI bus for any of %u sockets",
+ stub->istub_table->imcs_type, stub->istub_vid,
+ stub->istub_did, stub->istub_bus, stub->istub_dev,
+ stub->istub_func, imc->imc_nsockets);
+ continue;
+ }
+
+ /*
+ * We don't have to worry about duplicates here. We check to
+ * make sure that we have unique bdfs here.
+ */
+ switch (stub->istub_table->imcs_type) {
+ case IMC_TYPE_MC0_M2M:
+ sock->isock_imcs[0].icn_m2m = stub;
+ break;
+ case IMC_TYPE_MC1_M2M:
+ sock->isock_imcs[1].icn_m2m = stub;
+ break;
+ case IMC_TYPE_MC0_MAIN0:
+ sock->isock_nimc++;
+ sock->isock_imcs[0].icn_main0 = stub;
+
+ /*
+ * On Skylake, the MAIN0 does double duty as channel
+ * zero and as the TAD.
+ */
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ sock->isock_imcs[0].icn_nchannels++;
+ sock->isock_imcs[0].icn_channels[0].ich_desc =
+ stub;
+ sock->isock_tad[0].itad_stub = stub;
+ sock->isock_ntad++;
+ }
+ break;
+ case IMC_TYPE_MC0_MAIN1:
+ sock->isock_imcs[0].icn_main1 = stub;
+ break;
+ case IMC_TYPE_MC1_MAIN0:
+ sock->isock_nimc++;
+ sock->isock_imcs[1].icn_main0 = stub;
+
+ /*
+ * On Skylake, the MAIN0 does double duty as channel
+ * zero and as the TAD.
+ */
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ sock->isock_imcs[1].icn_nchannels++;
+ sock->isock_imcs[1].icn_channels[0].ich_desc =
+ stub;
+ sock->isock_tad[1].itad_stub = stub;
+ sock->isock_ntad++;
+ }
+ break;
+ case IMC_TYPE_MC1_MAIN1:
+ sock->isock_imcs[1].icn_main1 = stub;
+ break;
+ case IMC_TYPE_MC0_CHANNEL0:
+ sock->isock_imcs[0].icn_nchannels++;
+ sock->isock_imcs[0].icn_channels[0].ich_desc = stub;
+ break;
+ case IMC_TYPE_MC0_CHANNEL1:
+ sock->isock_imcs[0].icn_nchannels++;
+ sock->isock_imcs[0].icn_channels[1].ich_desc = stub;
+ break;
+ case IMC_TYPE_MC0_CHANNEL2:
+ sock->isock_imcs[0].icn_nchannels++;
+ sock->isock_imcs[0].icn_channels[2].ich_desc = stub;
+ break;
+ case IMC_TYPE_MC0_CHANNEL3:
+ sock->isock_imcs[0].icn_nchannels++;
+ sock->isock_imcs[0].icn_channels[3].ich_desc = stub;
+ break;
+ case IMC_TYPE_MC1_CHANNEL0:
+ sock->isock_imcs[1].icn_nchannels++;
+ sock->isock_imcs[1].icn_channels[0].ich_desc = stub;
+ break;
+ case IMC_TYPE_MC1_CHANNEL1:
+ sock->isock_imcs[1].icn_nchannels++;
+ sock->isock_imcs[1].icn_channels[1].ich_desc = stub;
+ break;
+ case IMC_TYPE_MC1_CHANNEL2:
+ sock->isock_imcs[1].icn_nchannels++;
+ sock->isock_imcs[1].icn_channels[2].ich_desc = stub;
+ break;
+ case IMC_TYPE_MC1_CHANNEL3:
+ sock->isock_imcs[1].icn_nchannels++;
+ sock->isock_imcs[1].icn_channels[3].ich_desc = stub;
+ break;
+ case IMC_TYPE_SAD_DRAM:
+ sock->isock_sad.isad_dram = stub;
+ break;
+ case IMC_TYPE_SAD_MMIO:
+ sock->isock_sad.isad_mmio = stub;
+ break;
+ case IMC_TYPE_SAD_MISC:
+ sock->isock_sad.isad_tolh = stub;
+ break;
+ case IMC_TYPE_VTD_MISC:
+ /*
+ * Some systems have multiple VT-D Misc. entry points
+ * in the system. In this case, only use the first one
+ * we find.
+ */
+ if (imc->imc_gvtd_misc == NULL) {
+ imc->imc_gvtd_misc = stub;
+ }
+ break;
+ case IMC_TYPE_SAD_MCROUTE:
+ ASSERT3U(imc->imc_gen, >=, IMC_GEN_SKYLAKE);
+ imc_mcroute_check(imc, &sock->isock_sad, stub);
+ break;
+ case IMC_TYPE_UBOX:
+ sock->isock_ubox = stub;
+ break;
+ case IMC_TYPE_HA0:
+ sock->isock_ntad++;
+ sock->isock_tad[0].itad_stub = stub;
+ break;
+ case IMC_TYPE_HA1:
+ sock->isock_ntad++;
+ sock->isock_tad[1].itad_stub = stub;
+ break;
+ case IMC_TYPE_UBOX_CPUBUSNO:
+ sock->isock_cpubusno = stub;
+ break;
+ default:
+ /*
+ * Attempt to still attach if we can.
+ */
+ dev_err(imc->imc_dip, CE_WARN, "Encountered unknown "
+ "IMC type (%u) on PCI %x,%x",
+ stub->istub_table->imcs_type,
+ stub->istub_vid, stub->istub_did);
+ break;
+ }
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Go through and fix up various aspects of the stubs mappings on systems. The
+ * following are a list of what we need to fix up:
+ *
+ * 1. On Haswell and newer systems, there is only one global VT-d device. We
+ * need to go back and map that to all of the per-socket imc_sad_t entries.
+ */
+static void
+imc_fixup_stubs(imc_t *imc)
+{
+ if (imc->imc_gen >= IMC_GEN_HASWELL) {
+ uint_t i;
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ ASSERT3P(imc->imc_sockets[i].isock_sad.isad_tolh,
+ ==, NULL);
+ imc->imc_sockets[i].isock_sad.isad_tolh =
+ imc->imc_gvtd_misc;
+ }
+ }
+}
+
+/*
+ * Attempt to map all of the discovered sockets to the corresponding APIC based
+ * socket. We do these mappings by getting the node id of the socket and
+ * adjusting it to make sure that no home agent is present in it. We use the
+ * UBOX to avoid any home agent related bits that are present in other
+ * registers.
+ */
+static void
+imc_map_sockets(imc_t *imc)
+{
+ uint_t i;
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ uint32_t nodeid;
+ ddi_acc_handle_t h;
+
+ h = imc->imc_sockets[i].isock_ubox->istub_cfgspace;
+ nodeid = pci_config_get32(h,
+ imc->imc_gen_data->igd_sad_nodeid_offset);
+ if (nodeid == PCI_EINVAL32) {
+ imc->imc_sockets[i].isock_valid |=
+ IMC_SOCKET_V_BAD_NODEID;
+ continue;
+ }
+
+ imc->imc_sockets[i].isock_nodeid = IMC_NODEID_UBOX_MASK(nodeid);
+ imc->imc_spointers[nodeid] = &imc->imc_sockets[i];
+ }
+}
+
+/*
+ * Decode the MTR, accounting for variances between processor generations.
+ */
+static void
+imc_decode_mtr(imc_t *imc, imc_mc_t *icn, imc_dimm_t *dimm, uint32_t mtr)
+{
+ uint8_t disable;
+
+ /*
+ * Check present first, before worrying about anything else.
+ */
+ if (imc->imc_gen < IMC_GEN_SKYLAKE &&
+ IMC_MTR_PRESENT_SNB_BRD(mtr) == 0) {
+ dimm->idimm_present = B_FALSE;
+ return;
+ } else if (imc->imc_gen >= IMC_GEN_SKYLAKE &&
+ IMC_MTR_PRESENT_SKYLAKE(mtr) == 0) {
+ dimm->idimm_present = B_FALSE;
+ return;
+ }
+
+ dimm->idimm_present = B_TRUE;
+ dimm->idimm_ncolumns = IMC_MTR_CA_WIDTH(mtr) + IMC_MTR_CA_BASE;
+ if (dimm->idimm_ncolumns < IMC_MTR_CA_MIN ||
+ dimm->idimm_ncolumns > IMC_MTR_CA_MAX) {
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_COLUMNS;
+ }
+
+ dimm->idimm_nrows = IMC_MTR_RA_WIDTH(mtr) + IMC_MTR_RA_BASE;
+ if (dimm->idimm_nrows < IMC_MTR_RA_MIN ||
+ dimm->idimm_nrows > IMC_MTR_RA_MAX) {
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_ROWS;
+ }
+
+ /*
+ * Determine Density, this information is not present on Sandy Bridge.
+ */
+ switch (imc->imc_gen) {
+ case IMC_GEN_IVY:
+ dimm->idimm_density = 1U << IMC_MTR_DENSITY_IVY_BRD(mtr);
+ break;
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ switch (IMC_MTR_DENSITY_IVY_BRD(mtr)) {
+ case 0:
+ default:
+ dimm->idimm_density = 0;
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_DENSITY;
+ break;
+ case 1:
+ dimm->idimm_density = 2;
+ break;
+ case 2:
+ dimm->idimm_density = 4;
+ break;
+ case 3:
+ dimm->idimm_density = 8;
+ break;
+ }
+ break;
+ case IMC_GEN_SKYLAKE:
+ switch (IMC_MTR_DENSITY_SKX(mtr)) {
+ case 0:
+ default:
+ dimm->idimm_density = 0;
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_DENSITY;
+ break;
+ case 1:
+ dimm->idimm_density = 2;
+ break;
+ case 2:
+ dimm->idimm_density = 4;
+ break;
+ case 3:
+ dimm->idimm_density = 8;
+ break;
+ case 4:
+ dimm->idimm_density = 16;
+ break;
+ case 5:
+ dimm->idimm_density = 12;
+ break;
+ }
+ break;
+ case IMC_GEN_UNKNOWN:
+ case IMC_GEN_SANDY:
+ dimm->idimm_density = 0;
+ break;
+ }
+
+ /*
+ * The values of width are the same on IVY->SKX, but the bits are
+ * different. This doesn't exist on SNB.
+ */
+ if (imc->imc_gen > IMC_GEN_SANDY) {
+ uint8_t width;
+
+ if (imc->imc_gen >= IMC_GEN_BROADWELL) {
+ width = IMC_MTR_WIDTH_BRD_SKX(mtr);
+ } else {
+ width = IMC_MTR_WIDTH_IVB_HAS(mtr);
+ }
+ switch (width) {
+ case 0:
+ dimm->idimm_width = 4;
+ break;
+ case 1:
+ dimm->idimm_width = 8;
+ break;
+ case 2:
+ dimm->idimm_width = 16;
+ break;
+ default:
+ dimm->idimm_width = 0;
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_WIDTH;
+ break;
+ }
+ } else {
+ dimm->idimm_width = 0;
+ }
+
+ dimm->idimm_nranks = 1 << IMC_MTR_DDR_RANKS(mtr);
+ switch (imc->imc_gen) {
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ case IMC_GEN_SKYLAKE:
+ if (dimm->idimm_nranks > IMC_MTR_DDR_RANKS_MAX_HAS_SKX) {
+ dimm->idimm_nranks = 0;
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_RANKS;
+ }
+ break;
+ default:
+ if (dimm->idimm_nranks > IMC_MTR_DDR_RANKS_MAX) {
+ dimm->idimm_nranks = 0;
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_RANKS;
+ }
+ }
+
+ disable = IMC_MTR_RANK_DISABLE(mtr);
+ dimm->idimm_ranks_disabled[0] = (disable & 0x1) != 0;
+ dimm->idimm_ranks_disabled[1] = (disable & 0x2) != 0;
+ dimm->idimm_ranks_disabled[2] = (disable & 0x4) != 0;
+ dimm->idimm_ranks_disabled[3] = (disable & 0x8) != 0;
+
+ /*
+ * Only Haswell and later have this information.
+ */
+ if (imc->imc_gen >= IMC_GEN_HASWELL) {
+ dimm->idimm_hdrl = IMC_MTR_HDRL_HAS_SKX(mtr) != 0;
+ dimm->idimm_hdrl_parity = IMC_MTR_HDRL_PARITY_HAS_SKX(mtr) != 0;
+ dimm->idimm_3dsranks = IMC_MTR_3DSRANKS_HAS_SKX(mtr);
+ if (dimm->idimm_3dsranks != 0) {
+ dimm->idimm_3dsranks = 1 << dimm->idimm_3dsranks;
+ }
+ }
+
+
+ if (icn->icn_dimm_type == IMC_DIMM_DDR4) {
+ dimm->idimm_nbanks = 16;
+ } else {
+ dimm->idimm_nbanks = 8;
+ }
+
+ /*
+ * To calculate the DIMM size we need first take the number of rows and
+ * columns. This gives us the number of slots per chip. In a given rank
+ * there are nbanks of these. There are nrank entries of those. Each of
+ * these slots can fit a byte.
+ */
+ dimm->idimm_size = dimm->idimm_nbanks * dimm->idimm_nranks * 8 *
+ (1ULL << (dimm->idimm_ncolumns + dimm->idimm_nrows));
+}
+
+static void
+imc_fill_dimms(imc_t *imc, imc_mc_t *icn, imc_channel_t *chan)
+{
+ uint_t i;
+
+ /*
+ * There's one register for each DIMM that might be present, we always
+ * read that information to determine information about the DIMMs.
+ */
+ chan->ich_ndimms = imc->imc_gen_data->igd_max_dimms;
+ for (i = 0; i < imc->imc_gen_data->igd_max_dimms; i++) {
+ uint32_t mtr;
+ imc_dimm_t *dimm = &chan->ich_dimms[i];
+
+ bzero(dimm, sizeof (imc_dimm_t));
+ mtr = pci_config_get32(chan->ich_desc->istub_cfgspace,
+ imc->imc_gen_data->igd_mtr_offsets[i]);
+ dimm->idimm_mtr = mtr;
+ /*
+ * We don't really expect to get a bad PCIe read. However, if we
+ * do, treat that for the moment as though the DIMM is bad.
+ */
+ if (mtr == PCI_EINVAL32) {
+ dimm->idimm_valid |= IMC_DIMM_V_BAD_PCI_READ;
+ continue;
+ }
+
+ imc_decode_mtr(imc, icn, dimm, mtr);
+ }
+}
+
+static boolean_t
+imc_fill_controller(imc_t *imc, imc_mc_t *icn)
+{
+ uint32_t mcmtr;
+
+ mcmtr = pci_config_get32(icn->icn_main0->istub_cfgspace,
+ imc->imc_gen_data->igd_mcmtr_offset);
+ if (mcmtr == PCI_EINVAL32) {
+ icn->icn_invalid = B_TRUE;
+ return (B_FALSE);
+ }
+
+ icn->icn_closed = IMC_MCMTR_CLOSED_PAGE(mcmtr) != 0;
+ if (imc->imc_gen < IMC_GEN_SKYLAKE) {
+ icn->icn_lockstep = IMC_MCMTR_LOCKSTEP(mcmtr) != 0;
+ } else {
+ icn->icn_lockstep = B_FALSE;
+ }
+
+ icn->icn_ecc = IMC_MCMTR_ECC_ENABLED(mcmtr) != 0;
+
+ /*
+ * SNB and IVB only support DDR3. Haswell and Broadwell may support
+ * DDR4, depends on the SKU. Skylake only supports DDR4.
+ */
+ switch (imc->imc_gen) {
+ case IMC_GEN_SANDY:
+ case IMC_GEN_IVY:
+ icn->icn_dimm_type = IMC_DIMM_DDR3;
+ break;
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ if (IMC_MCMTR_DDR4_HAS_BRD(mcmtr)) {
+ icn->icn_dimm_type = IMC_DIMM_DDR4;
+ } else {
+ icn->icn_dimm_type = IMC_DIMM_DDR3;
+ }
+ break;
+ default:
+ /*
+ * Skylake and on are all DDR4.
+ */
+ icn->icn_dimm_type = IMC_DIMM_DDR4;
+ break;
+ }
+
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE && icn->icn_m2m != NULL) {
+ icn->icn_topo = pci_config_get32(icn->icn_m2m->istub_cfgspace,
+ imc->imc_gen_data->igd_topo_offset);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Walk the IMC data and fill in the information on DIMMs and the memory
+ * controller configurations.
+ */
+static void
+imc_fill_data(imc_t *imc)
+{
+ uint_t csock, cmc, cchan;
+
+ for (csock = 0; csock < imc->imc_nsockets; csock++) {
+ imc_socket_t *sock = &imc->imc_sockets[csock];
+
+ for (cmc = 0; cmc < sock->isock_nimc; cmc++) {
+ imc_mc_t *icn = &sock->isock_imcs[cmc];
+
+ if (!imc_fill_controller(imc, icn))
+ continue;
+
+ for (cchan = 0; cchan < icn->icn_nchannels; cchan++) {
+ imc_fill_dimms(imc, icn,
+ &icn->icn_channels[cchan]);
+ }
+ }
+ }
+}
+
+static nvlist_t *
+imc_nvl_create_dimm(imc_t *imc, imc_dimm_t *dimm)
+{
+ nvlist_t *nvl;
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_DIMM_PRESENT,
+ dimm->idimm_present);
+ if (!dimm->idimm_present) {
+ return (nvl);
+ }
+
+ fnvlist_add_uint64(nvl, MCINTEL_NVLIST_V1_DIMM_SIZE, dimm->idimm_size);
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS,
+ dimm->idimm_ncolumns);
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_NROWS,
+ dimm->idimm_nrows);
+
+ if (imc->imc_gen > IMC_GEN_SANDY) {
+ fnvlist_add_uint64(nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY,
+ dimm->idimm_density * (1ULL << 30));
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH,
+ dimm->idimm_width);
+ }
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_RANKS,
+ dimm->idimm_nranks);
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_BANKS,
+ dimm->idimm_nbanks);
+ fnvlist_add_boolean_array(nvl, MCINTEL_NVLIST_V1_DIMM_RDIS,
+ dimm->idimm_ranks_disabled, IMC_MAX_RANK_DISABLE);
+
+ if (imc->imc_gen >= IMC_GEN_HASWELL) {
+ fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_DIMM_HDRL,
+ dimm->idimm_hdrl);
+ fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_DIMM_HDRLP,
+ dimm->idimm_hdrl_parity);
+ if (dimm->idimm_3dsranks > 0) {
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_3DRANK,
+ dimm->idimm_3dsranks);
+ }
+ }
+
+ return (nvl);
+}
+
+static nvlist_t *
+imc_nvl_create_channel(imc_t *imc, imc_channel_t *chan)
+{
+ nvlist_t *nvl;
+ nvlist_t *dimms[IMC_MAX_DIMMPERCHAN];
+ uint_t i;
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_CHAN_NDPC,
+ imc->imc_gen_data->igd_max_dimms);
+ for (i = 0; i < imc->imc_gen_data->igd_max_dimms; i++) {
+ dimms[i] = imc_nvl_create_dimm(imc, &chan->ich_dimms[i]);
+ }
+
+ fnvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS,
+ dimms, i);
+
+ for (; i > 0; i--) {
+ nvlist_free(dimms[i-1]);
+ }
+
+ return (nvl);
+}
+
+static nvlist_t *
+imc_nvl_create_mc(imc_t *imc, imc_mc_t *icn)
+{
+ nvlist_t *nvl;
+ nvlist_t *channels[IMC_MAX_CHANPERMC];
+ uint_t i;
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_MC_NCHAN, icn->icn_nchannels);
+ fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_MC_ECC,
+ icn->icn_ecc);
+ if (icn->icn_lockstep) {
+ fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE,
+ MCINTEL_NVLIST_V1_MC_CHAN_MODE_LOCK);
+ } else {
+ fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE,
+ MCINTEL_NVLIST_V1_MC_CHAN_MODE_INDEP);
+
+ }
+
+ if (icn->icn_closed) {
+ fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_POLICY,
+ MCINTEL_NVLIST_V1_MC_POLICY_CLOSED);
+ } else {
+ fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_POLICY,
+ MCINTEL_NVLIST_V1_MC_POLICY_OPEN);
+ }
+
+ for (i = 0; i < icn->icn_nchannels; i++) {
+ channels[i] = imc_nvl_create_channel(imc,
+ &icn->icn_channels[i]);
+ }
+ fnvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_V1_MC_CHANNELS,
+ channels, icn->icn_nchannels);
+ for (i = 0; i < icn->icn_nchannels; i++) {
+ nvlist_free(channels[i]);
+ }
+
+ return (nvl);
+}
+
+static void
+imc_nvl_pack(imc_socket_t *sock, boolean_t sleep)
+{
+ char *buf = NULL;
+ size_t len = 0;
+ int kmflag;
+
+ if (sock->isock_nvl == NULL)
+ return;
+
+ if (sock->isock_buf != NULL)
+ return;
+
+ if (sleep) {
+ kmflag = KM_SLEEP;
+ } else {
+ kmflag = KM_NOSLEEP | KM_NORMALPRI;
+ }
+
+ if (nvlist_pack(sock->isock_nvl, &buf, &len, NV_ENCODE_XDR,
+ kmflag) != 0) {
+ return;
+ }
+
+ sock->isock_buf = buf;
+ sock->isock_buflen = len;
+ sock->isock_gen++;
+}
+
+static void
+imc_decoder_pack(imc_t *imc)
+{
+ char *buf = NULL;
+ size_t len = 0;
+
+ if (imc->imc_decoder_buf != NULL)
+ return;
+
+ if (imc->imc_decoder_dump == NULL) {
+ imc->imc_decoder_dump = imc_dump_decoder(imc);
+ }
+
+ if (nvlist_pack(imc->imc_decoder_dump, &buf, &len, NV_ENCODE_XDR,
+ KM_NOSLEEP | KM_NORMALPRI) != 0) {
+ return;
+ }
+
+ imc->imc_decoder_buf = buf;
+ imc->imc_decoder_len = len;
+}
+
+static void
+imc_nvl_create(imc_t *imc)
+{
+ uint_t csock;
+ for (csock = 0; csock < imc->imc_nsockets; csock++) {
+ uint_t i;
+ nvlist_t *nvl;
+ nvlist_t *mcs[IMC_MAX_IMCPERSOCK];
+ imc_socket_t *sock = &imc->imc_sockets[csock];
+
+ nvl = fnvlist_alloc();
+ fnvlist_add_uint8(nvl, MCINTEL_NVLIST_VERSTR,
+ MCINTEL_NVLIST_VERS1);
+ fnvlist_add_uint8(nvl, MCINTEL_NVLIST_V1_NMC,
+ sock->isock_nimc);
+
+ for (i = 0; i < sock->isock_nimc; i++) {
+ mcs[i] = imc_nvl_create_mc(imc, &sock->isock_imcs[i]);
+ }
+
+ fnvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS,
+ mcs, sock->isock_nimc);
+
+ for (i = 0; i < sock->isock_nimc; i++) {
+ nvlist_free(mcs[i]);
+ }
+
+ sock->isock_nvl = nvl;
+ imc_nvl_pack(sock, B_TRUE);
+ }
+}
+
+/*
+ * Determine the top of low and high memory. These determine whether transaction
+ * addresses target main memory or not. Unfortunately, the way that these are
+ * stored and fetched changes with different generations.
+ */
+static void
+imc_sad_read_tohm(imc_t *imc, imc_sad_t *sad)
+{
+ uint32_t tolm, tohm_low, tohm_hi;
+
+ tolm = pci_config_get32(sad->isad_tolh->istub_cfgspace,
+ imc->imc_gen_data->igd_tolm_offset);
+ tohm_low = pci_config_get32(sad->isad_tolh->istub_cfgspace,
+ imc->imc_gen_data->igd_tohm_low_offset);
+ if (imc->imc_gen_data->igd_tohm_hi_offset != 0) {
+ tohm_hi = pci_config_get32(sad->isad_tolh->istub_cfgspace,
+ imc->imc_gen_data->igd_tohm_hi_offset);
+ } else {
+ tohm_hi = 0;
+ }
+
+ if (tolm == PCI_EINVAL32 || tohm_low == PCI_EINVAL32 ||
+ tohm_hi == PCI_EINVAL32) {
+ sad->isad_valid |= IMC_SAD_V_BAD_PCI_READ;
+ return;
+ }
+
+ switch (imc->imc_gen) {
+ case IMC_GEN_SANDY:
+ case IMC_GEN_IVY:
+ sad->isad_tolm = ((uint64_t)tolm & IMC_TOLM_SNB_IVY_MASK) <<
+ IMC_TOLM_SNB_IVY_SHIFT;
+ sad->isad_tohm = ((uint64_t)tohm_low & IMC_TOHM_SNB_IVY_MASK) <<
+ IMC_TOLM_SNB_IVY_SHIFT;
+ break;
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ case IMC_GEN_SKYLAKE:
+ sad->isad_tolm = (uint64_t)tolm & IMC_TOLM_HAS_SKX_MASK;
+ sad->isad_tohm = ((uint64_t)tohm_low &
+ IMC_TOHM_LOW_HAS_SKX_MASK) | ((uint64_t)tohm_hi << 32);
+
+ /*
+ * Adjust the values to turn them into an exclusive range.
+ */
+ sad->isad_tolm += IMC_TOLM_HAS_SKY_EXCL;
+ sad->isad_tohm += IMC_TOHM_HAS_SKY_EXCL;
+ break;
+ default:
+ dev_err(imc->imc_dip, CE_PANIC, "imc driver programmer error: "
+ "set to unknown generation: %u", imc->imc_gen);
+ return;
+ }
+}
+
+static void
+imc_sad_fill_rule(imc_t *imc, imc_sad_t *sad, imc_sad_rule_t *rule,
+ uint32_t raw)
+{
+ uint_t attr;
+ uint64_t limit;
+ bzero(rule, sizeof (imc_sad_rule_t));
+
+ rule->isr_raw_dram = raw;
+ rule->isr_enable = IMC_SAD_DRAM_RULE_ENABLE(raw) != 0;
+ if (imc->imc_gen < IMC_GEN_SKYLAKE) {
+ switch (IMC_SAD_DRAM_INTERLEAVE_SNB_BRD(raw)) {
+ case IMC_SAD_DRAM_INTERLEAVE_SNB_BRD_8t6:
+ rule->isr_imode = IMC_SAD_IMODE_8t6;
+ break;
+ case IMC_SAD_DRAM_INTERLEAVE_SNB_BRD_8t6XOR:
+ rule->isr_imode = IMC_SAD_IMODE_8t6XOR;
+ break;
+ }
+ } else {
+ switch (IMC_SAD_DRAM_INTERLEAVE_SKX(raw)) {
+ case IMC_SAD_DRAM_INTERLEAVE_SKX_8t6:
+ rule->isr_imode = IMC_SAD_IMODE_8t6;
+ break;
+ case IMC_SAD_DRAM_INTERLEAVE_SKX_10t8:
+ rule->isr_imode = IMC_SAD_IMODE_10t8;
+ break;
+ case IMC_SAD_DRAM_INTERLEAVE_SKX_14t12:
+ rule->isr_imode = IMC_SAD_IMODE_14t12;
+ break;
+ case IMC_SAD_DRAM_INTERLEAVE_SKX_32t30:
+ rule->isr_imode = IMC_SAD_IMODE_32t30;
+ break;
+ }
+ }
+
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ attr = IMC_SAD_DRAM_ATTR_SKX(raw);
+ } else {
+ attr = IMC_SAD_DRAM_ATTR_SNB_BRD(raw);
+ }
+
+ switch (attr) {
+ case IMC_SAD_DRAM_ATTR_DRAM:
+ rule->isr_type = IMC_SAD_TYPE_DRAM;
+ break;
+ case IMC_SAD_DRAM_ATTR_MMCFG:
+ rule->isr_type = IMC_SAD_TYPE_MMCFG;
+ break;
+ case IMC_SAD_DRAM_ATTR_NXM:
+ if (imc->imc_gen < IMC_GEN_SKYLAKE) {
+ sad->isad_valid |= IMC_SAD_V_BAD_DRAM_ATTR;
+ }
+ rule->isr_type = IMC_SAD_TYPE_NXM;
+ break;
+ default:
+ sad->isad_valid |= IMC_SAD_V_BAD_DRAM_ATTR;
+ break;
+ }
+
+ /*
+ * Fetch the limit which represents bits 45:26 and then adjust this so
+ * that it is exclusive.
+ */
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ limit = IMC_SAD_DRAM_LIMIT_SKX(raw);
+ } else {
+ limit = IMC_SAD_DRAM_LIMIT_SNB_BRD(raw);
+ }
+ rule->isr_limit = (limit << IMC_SAD_DRAM_LIMIT_SHIFT) +
+ IMC_SAD_DRAM_LIMIT_EXCLUSIVE;
+
+ /*
+ * The rest of this does not apply to Sandy Bridge.
+ */
+ if (imc->imc_gen == IMC_GEN_SANDY)
+ return;
+
+ if (imc->imc_gen >= IMC_GEN_IVY && imc->imc_gen < IMC_GEN_SKYLAKE) {
+ rule->isr_a7mode = IMC_SAD_DRAM_A7_IVB_BRD(raw) != 0;
+ return;
+ }
+
+ switch (IMC_SAD_DRAM_MOD23_SKX(raw)) {
+ case IMC_SAD_DRAM_MOD23_MOD3:
+ rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD3;
+ break;
+ case IMC_SAD_DRAM_MOD23_MOD2_C01:
+ rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_01;
+ break;
+ case IMC_SAD_DRAM_MOD23_MOD2_C12:
+ rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_12;
+ break;
+ case IMC_SAD_DRAM_MOD23_MOD2_C02:
+ rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_02;
+ break;
+ }
+
+ rule->isr_need_mod3 = IMC_SAD_DRAM_MOD3_SKX(raw) != 0;
+ switch (IMC_SAD_DRAM_MOD3_SKX(raw)) {
+ case IMC_SAD_DRAM_MOD3_MODE_45t6:
+ rule->isr_mod_mode = IMC_SAD_MOD_MODE_45t6;
+ break;
+ case IMC_SAD_DRAM_MOD3_MODE_45t8:
+ rule->isr_mod_mode = IMC_SAD_MOD_MODE_45t8;
+ break;
+ case IMC_SAD_DRAM_MOD3_MODE_45t12:
+ rule->isr_mod_mode = IMC_SAD_MOD_MODE_45t12;
+ break;
+ default:
+ sad->isad_valid |= IMC_SAD_V_BAD_MOD3;
+ break;
+ }
+}
+
+static void
+imc_sad_fill_rule_interleave(imc_t *imc, imc_sad_rule_t *rule, uint32_t raw)
+{
+ uint_t i;
+ uint32_t mlen, mbase, skipbits, skipafter;
+
+ rule->isr_raw_interleave = raw;
+
+ /*
+ * Right now all architectures always have the maximum number of SAD
+ * interleave targets.
+ */
+ rule->isr_ntargets = IMC_MAX_SAD_INTERLEAVE;
+
+ /*
+ * Sandy Bridge has a gap in the interleave list due to the fact that it
+ * uses a smaller length.
+ */
+ if (imc->imc_gen > IMC_GEN_SANDY) {
+ mlen = IMC_SAD_ILEAVE_IVB_SKX_LEN;
+ mbase = IMC_SAD_ILEAVE_IVB_SKX_MASK;
+ skipbits = skipafter = 0;
+ } else {
+ mlen = IMC_SAD_ILEAVE_SNB_LEN;
+ mbase = IMC_SAD_ILEAVE_SNB_MASK;
+ skipbits = 2;
+ skipafter = 4;
+ }
+
+ for (i = 0; i < rule->isr_ntargets; i++) {
+ uint32_t mask, shift;
+
+ shift = i * mlen;
+ if (i >= skipafter)
+ shift += skipbits;
+ mask = mbase << shift;
+ rule->isr_targets[i] = (raw & mask) >> shift;
+ }
+}
+
+static void
+imc_sad_read_dram_rules(imc_t *imc, imc_sad_t *sad)
+{
+ uint_t i;
+ off_t off;
+
+ sad->isad_nrules = imc->imc_gen_data->igd_sad_ndram_rules;
+ for (i = 0, off = imc->imc_gen_data->igd_sad_dram_offset;
+ i < sad->isad_nrules; i++, off += sizeof (uint64_t)) {
+ uint32_t dram, interleave;
+ imc_sad_rule_t *rule = &sad->isad_rules[i];
+
+ dram = pci_config_get32(sad->isad_dram->istub_cfgspace, off);
+ interleave = pci_config_get32(sad->isad_dram->istub_cfgspace,
+ off + 4);
+
+ if (dram == PCI_EINVAL32 || interleave == PCI_EINVAL32) {
+ sad->isad_valid |= IMC_SAD_V_BAD_PCI_READ;
+ return;
+ }
+
+ imc_sad_fill_rule(imc, sad, rule, dram);
+ imc_sad_fill_rule_interleave(imc, rule, interleave);
+ }
+}
+
+static void
+imc_sad_decode_mcroute(imc_t *imc, imc_sad_t *sad)
+{
+ uint_t i;
+ imc_sad_mcroute_table_t *mc = &sad->isad_mcroute;
+
+ if (imc->imc_gen < IMC_GEN_SKYLAKE)
+ return;
+ if (sad->isad_valid != 0)
+ return;
+
+ mc->ismc_nroutes = IMC_MAX_SAD_MCROUTES;
+ for (i = 0; i < IMC_MAX_SAD_MCROUTES; i++) {
+ uint_t chanoff, ringoff;
+
+ ringoff = i * IMC_MC_ROUTE_RING_BITS;
+ chanoff = i * IMC_MC_ROUTE_CHAN_BITS + IMC_MC_ROUTE_CHAN_OFFSET;
+
+ mc->ismc_mcroutes[i].ismce_imc = (mc->ismc_raw_mcroute >>
+ ringoff) & IMC_MC_ROUTE_RING_MASK;
+ mc->ismc_mcroutes[i].ismce_pchannel = (mc->ismc_raw_mcroute >>
+ chanoff) & IMC_MC_ROUTE_CHAN_MASK;
+ }
+}
+
+/*
+ * Initialize the SAD. To do this we have to do a few different things:
+ *
+ * 1. Determine where the top of low and high memory is.
+ * 2. Read and decode all of the rules for the SAD
+ * 3. On systems with a route table, decode the raw routes
+ *
+ * At this point in time, we treat TOLM and TOHM as a per-socket construct, even
+ * though it really should be global, this just makes life a bit simpler.
+ */
+static void
+imc_decoder_init_sad(imc_t *imc)
+{
+ uint_t i;
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ imc_sad_read_tohm(imc, &imc->imc_sockets[i].isock_sad);
+ imc_sad_read_dram_rules(imc, &imc->imc_sockets[i].isock_sad);
+ imc_sad_decode_mcroute(imc, &imc->imc_sockets[i].isock_sad);
+ }
+}
+
+static void
+imc_tad_fill_rule(imc_t *imc, imc_tad_t *tad, imc_tad_rule_t *prev,
+ imc_tad_rule_t *rule, uint32_t val)
+{
+ uint64_t limit;
+
+ limit = IMC_TAD_LIMIT(val);
+ rule->itr_limit = (limit << IMC_TAD_LIMIT_SHIFT) +
+ IMC_TAD_LIMIT_EXCLUSIVE;
+ rule->itr_raw = val;
+
+ switch (IMC_TAD_SOCK_WAY(val)) {
+ case IMC_TAD_SOCK_WAY_1:
+ rule->itr_sock_way = 1;
+ break;
+ case IMC_TAD_SOCK_WAY_2:
+ rule->itr_sock_way = 2;
+ break;
+ case IMC_TAD_SOCK_WAY_4:
+ rule->itr_sock_way = 4;
+ break;
+ case IMC_TAD_SOCK_WAY_8:
+ rule->itr_sock_way = 8;
+ break;
+ }
+
+ rule->itr_chan_way = IMC_TAD_CHAN_WAY(val) + 1;
+ rule->itr_sock_gran = IMC_TAD_GRAN_64B;
+ rule->itr_chan_gran = IMC_TAD_GRAN_64B;
+
+ /*
+ * Starting with Skylake the targets that are used are no longer part of
+ * the TAD. Those come from the IMC route table.
+ */
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ rule->itr_ntargets = 0;
+ return;
+ }
+
+ rule->itr_ntargets = IMC_TAD_SNB_BRD_NTARGETS;
+ rule->itr_targets[0] = IMC_TAD_TARG0(val);
+ rule->itr_targets[1] = IMC_TAD_TARG1(val);
+ rule->itr_targets[2] = IMC_TAD_TARG2(val);
+ rule->itr_targets[3] = IMC_TAD_TARG3(val);
+
+ if (prev == NULL) {
+ rule->itr_base = 0;
+ } else {
+ rule->itr_base = prev->itr_limit + 1;
+ }
+}
+
+static void
+imc_tad_fill_skx(imc_t *imc, imc_tad_t *tad, imc_tad_rule_t *rule,
+ uint32_t val)
+{
+ uint64_t base;
+
+ rule->itr_raw_gran = val;
+ base = IMC_TAD_BASE_BASE(val);
+ rule->itr_base = base << IMC_TAD_BASE_SHIFT;
+
+ switch (IMC_TAD_BASE_CHAN_GRAN(val)) {
+ case IMC_TAD_BASE_CHAN_GRAN_64B:
+ rule->itr_sock_gran = IMC_TAD_GRAN_64B;
+ break;
+ case IMC_TAD_BASE_CHAN_GRAN_256B:
+ rule->itr_sock_gran = IMC_TAD_GRAN_256B;
+ break;
+ case IMC_TAD_BASE_CHAN_GRAN_4KB:
+ rule->itr_sock_gran = IMC_TAD_GRAN_4KB;
+ break;
+ default:
+ tad->itad_valid |= IMC_TAD_V_BAD_CHAN_GRAN;
+ return;
+ }
+
+ switch (IMC_TAD_BASE_SOCK_GRAN(val)) {
+ case IMC_TAD_BASE_SOCK_GRAN_64B:
+ rule->itr_sock_gran = IMC_TAD_GRAN_64B;
+ break;
+ case IMC_TAD_BASE_SOCK_GRAN_256B:
+ rule->itr_sock_gran = IMC_TAD_GRAN_256B;
+ break;
+ case IMC_TAD_BASE_SOCK_GRAN_4KB:
+ rule->itr_sock_gran = IMC_TAD_GRAN_4KB;
+ break;
+ case IMC_TAD_BASE_SOCK_GRAN_1GB:
+ rule->itr_sock_gran = IMC_TAD_GRAN_1GB;
+ break;
+ }
+}
+
+/*
+ * When mirroring is enabled, at least in Sandy Bridge to Broadwell, it's
+ * suggested that the channel wayness will take this into account and therefore
+ * should be accurately reflected.
+ */
+static void
+imc_tad_read_rules(imc_t *imc, imc_tad_t *tad)
+{
+ uint_t i;
+ off_t baseoff;
+ imc_tad_rule_t *prev;
+
+ tad->itad_nrules = imc->imc_gen_data->igd_tad_nrules;
+ for (i = 0, baseoff = imc->imc_gen_data->igd_tad_rule_offset,
+ prev = NULL; i < tad->itad_nrules;
+ i++, baseoff += sizeof (uint32_t)) {
+ uint32_t val;
+ off_t off;
+ imc_tad_rule_t *rule = &tad->itad_rules[i];
+
+ /*
+ * On Skylake, the TAD rules are split among two registers. The
+ * latter set mimics what exists on pre-Skylake.
+ */
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ off = baseoff + IMC_SKX_WAYNESS_OFFSET;
+ } else {
+ off = baseoff;
+ }
+
+ val = pci_config_get32(tad->itad_stub->istub_cfgspace, off);
+ if (val == PCI_EINVAL32) {
+ tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
+ return;
+ }
+
+ imc_tad_fill_rule(imc, tad, prev, rule, val);
+ prev = rule;
+ if (imc->imc_gen < IMC_GEN_SKYLAKE)
+ continue;
+
+ val = pci_config_get32(tad->itad_stub->istub_cfgspace, baseoff);
+ if (val == PCI_EINVAL32) {
+ tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
+ return;
+ }
+
+ imc_tad_fill_skx(imc, tad, rule, val);
+ }
+}
+
+/*
+ * Check for features which change how decoding works.
+ */
+static void
+imc_tad_read_features(imc_t *imc, imc_tad_t *tad, imc_mc_t *mc)
+{
+ uint32_t val;
+
+ /*
+ * Determine whether or not lockstep mode or mirroring are enabled.
+ * These change the behavior of how we're supposed to interpret channel
+ * wayness. Lockstep is available in the TAD's features. Mirroring is
+ * available on the IMC's features. This isn't present in Skylake+. On
+ * Skylake Mirorring is a property of the SAD rule and there is no
+ * lockstep.
+ */
+ switch (imc->imc_gen) {
+ case IMC_GEN_SANDY:
+ case IMC_GEN_IVY:
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ val = pci_config_get32(tad->itad_stub->istub_cfgspace,
+ imc->imc_gen_data->igd_tad_sysdef);
+ if (val == PCI_EINVAL32) {
+ tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
+ return;
+ }
+ if (IMC_TAD_SYSDEF_LOCKSTEP(val)) {
+ tad->itad_flags |= IMC_TAD_FLAG_LOCKSTEP;
+ }
+
+ val = pci_config_get32(mc->icn_main1->istub_cfgspace,
+ imc->imc_gen_data->igd_mc_mirror);
+ if (val == PCI_EINVAL32) {
+ tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
+ return;
+ }
+ if (IMC_MC_MIRROR_SNB_BRD(val)) {
+ tad->itad_flags |= IMC_TAD_FLAG_MIRROR;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Now, go through and look at values that'll change how we do the
+ * channel index and adddress calculation. These are only present
+ * between Ivy Bridge and Broadwell. They don't exist on Sandy Bridge
+ * and they don't exist on Skylake+.
+ */
+ switch (imc->imc_gen) {
+ case IMC_GEN_IVY:
+ case IMC_GEN_HASWELL:
+ case IMC_GEN_BROADWELL:
+ val = pci_config_get32(tad->itad_stub->istub_cfgspace,
+ imc->imc_gen_data->igd_tad_sysdef2);
+ if (val == PCI_EINVAL32) {
+ tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
+ return;
+ }
+ if (IMC_TAD_SYSDEF2_SHIFTUP(val)) {
+ tad->itad_flags |= IMC_TAD_FLAG_CHANSHIFT;
+ }
+ if (IMC_TAD_SYSDEF2_SHIFTUP(val)) {
+ tad->itad_flags |= IMC_TAD_FLAG_CHANHASH;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Read the IMC channel interleave records
+ */
+static void
+imc_tad_read_interleave(imc_t *imc, imc_channel_t *chan)
+{
+ uint_t i;
+ off_t off;
+
+ chan->ich_ntad_offsets = imc->imc_gen_data->igd_tad_nrules;
+ for (i = 0, off = imc->imc_gen_data->igd_tad_chan_offset;
+ i < chan->ich_ntad_offsets; i++, off += sizeof (uint32_t)) {
+ uint32_t val;
+ uint64_t offset;
+
+ val = pci_config_get32(chan->ich_desc->istub_cfgspace,
+ off);
+ if (val == PCI_EINVAL32) {
+ chan->ich_valid |= IMC_CHANNEL_V_BAD_PCI_READ;
+ return;
+ }
+
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ offset = IMC_TADCHAN_OFFSET_SKX(val);
+ } else {
+ offset = IMC_TADCHAN_OFFSET_SNB_BRD(val);
+ }
+
+ chan->ich_tad_offsets[i] = offset << IMC_TADCHAN_OFFSET_SHIFT;
+ chan->ich_tad_offsets_raw[i] = val;
+ }
+}
+
+static void
+imc_decoder_init_tad(imc_t *imc)
+{
+ uint_t i;
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ uint_t j;
+
+ for (j = 0; j < imc->imc_sockets[i].isock_ntad; j++) {
+ imc_tad_read_features(imc,
+ &imc->imc_sockets[i].isock_tad[j],
+ &imc->imc_sockets[i].isock_imcs[j]);
+ imc_tad_read_rules(imc,
+ &imc->imc_sockets[i].isock_tad[j]);
+ }
+ }
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ uint_t j;
+ imc_socket_t *sock = &imc->imc_sockets[i];
+
+ for (j = 0; j < imc->imc_sockets[i].isock_nimc; j++) {
+ uint_t k;
+ imc_mc_t *mc = &sock->isock_imcs[j];
+
+ for (k = 0; k < mc->icn_nchannels; k++) {
+ imc_channel_t *chan = &mc->icn_channels[k];
+ imc_tad_read_interleave(imc, chan);
+ }
+ }
+ }
+}
+
+static void
+imc_rir_read_ileave_offsets(imc_t *imc, imc_channel_t *chan,
+ imc_rank_ileave_t *rank, uint_t rirno, boolean_t contig)
+{
+ uint_t i;
+ off_t off, incr;
+
+ /*
+ * Rank interleave offset registers come in two forms. Either they are
+ * contiguous for a given wayness, meaning that all of the entries for
+ * wayness zero are contiguous, or they are sparse, meaning that there
+ * is a bank for entry zero for all wayness, then entry one for all
+ * wayness, etc.
+ */
+ if (contig) {
+ off = imc->imc_gen_data->igd_rir_ileave_offset +
+ (rirno * imc->imc_gen_data->igd_rir_nileaves *
+ sizeof (uint32_t));
+ incr = sizeof (uint32_t);
+ } else {
+ off = imc->imc_gen_data->igd_rir_ileave_offset +
+ (rirno * sizeof (uint32_t));
+ incr = imc->imc_gen_data->igd_rir_nileaves * sizeof (uint32_t);
+ }
+ for (i = 0; i < rank->irle_nentries; i++, off += incr) {
+ uint32_t val;
+ uint64_t offset;
+ imc_rank_ileave_entry_t *ent = &rank->irle_entries[i];
+
+ val = pci_config_get32(chan->ich_desc->istub_cfgspace, off);
+ if (val == PCI_EINVAL32) {
+ chan->ich_valid |= IMC_CHANNEL_V_BAD_PCI_READ;
+ return;
+ }
+
+ switch (imc->imc_gen) {
+ case IMC_GEN_BROADWELL:
+ ent->irle_target = IMC_RIR_OFFSET_TARGET_BRD(val);
+ break;
+ default:
+ ent->irle_target = IMC_RIR_OFFSET_TARGET(val);
+ break;
+ }
+ if (imc->imc_gen >= IMC_GEN_HASWELL) {
+ offset = IMC_RIR_OFFSET_OFFSET_HAS_SKX(val);
+ } else {
+ offset = IMC_RIR_OFFSET_OFFSET_SNB_IVB(val);
+ }
+ ent->irle_offset = offset << IMC_RIR_OFFSET_SHIFT;
+ }
+}
+
+static void
+imc_rir_read_wayness(imc_t *imc, imc_channel_t *chan)
+{
+ uint_t i;
+ off_t off;
+
+ chan->ich_nrankileaves = imc->imc_gen_data->igd_rir_nways;
+ for (i = 0, off = imc->imc_gen_data->igd_rir_way_offset;
+ i < chan->ich_nrankileaves; i++, off += sizeof (uint32_t)) {
+ uint32_t val;
+ uint64_t lim;
+ imc_rank_ileave_t *ent = &chan->ich_rankileaves[i];
+
+ val = pci_config_get32(chan->ich_desc->istub_cfgspace, off);
+ if (val == PCI_EINVAL32) {
+ chan->ich_valid |= IMC_CHANNEL_V_BAD_PCI_READ;
+ return;
+ }
+
+ ent->irle_raw = val;
+ ent->irle_enabled = IMC_RIR_WAYNESS_ENABLED(val) != 0;
+ ent->irle_nways = 1 << IMC_RIR_WAYNESS_WAY(val);
+ ent->irle_nwaysbits = IMC_RIR_WAYNESS_WAY(val);
+ if (imc->imc_gen >= IMC_GEN_HASWELL) {
+ lim = IMC_RIR_LIMIT_HAS_SKX(val);
+ } else {
+ lim = IMC_RIR_LIMIT_SNB_IVB(val);
+ }
+
+ ent->irle_limit = (lim << IMC_RIR_LIMIT_SHIFT) +
+ IMC_RIR_LIMIT_EXCLUSIVE;
+
+ ent->irle_nentries = imc->imc_gen_data->igd_rir_nileaves;
+ if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
+ imc_rir_read_ileave_offsets(imc, chan, ent, i, B_FALSE);
+ } else {
+ imc_rir_read_ileave_offsets(imc, chan, ent, i, B_TRUE);
+ }
+ }
+}
+
+static void
+imc_decoder_init_rir(imc_t *imc)
+{
+ uint_t i;
+
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ uint_t j;
+ imc_socket_t *sock = &imc->imc_sockets[i];
+
+ for (j = 0; j < imc->imc_sockets[i].isock_nimc; j++) {
+ uint_t k;
+ imc_mc_t *mc = &sock->isock_imcs[j];
+
+ for (k = 0; k < mc->icn_nchannels; k++) {
+ imc_channel_t *chan = &mc->icn_channels[k];
+ imc_rir_read_wayness(imc, chan);
+ }
+ }
+ }
+}
+
+static cmi_errno_t
+imc_mc_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
+ uint32_t synd, int syndtype, mc_unum_t *unump)
+{
+ imc_t *imc = arg;
+ uint_t i;
+ imc_decode_state_t dec;
+
+ bzero(&dec, sizeof (dec));
+ if (!imc_decode_pa(imc, pa, &dec)) {
+ switch (dec.ids_fail) {
+ case IMC_DECODE_F_LEGACY_RANGE:
+ case IMC_DECODE_F_OUTSIDE_DRAM:
+ return (CMIERR_MC_NOTDIMMADDR);
+ default:
+ return (CMIERR_MC_BADSTATE);
+ }
+ }
+
+ unump->unum_board = 0;
+ /*
+ * The chip id needs to be in the order that the OS expects it, which
+ * may not be our order.
+ */
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ if (imc->imc_spointers[i] == dec.ids_socket)
+ break;
+ }
+ if (i == imc->imc_nsockets) {
+ return (CMIERR_MC_BADSTATE);
+ }
+ unump->unum_chip = i;
+ unump->unum_mc = dec.ids_tadid;
+ unump->unum_chan = dec.ids_channelid;
+ unump->unum_cs = dec.ids_dimmid;
+ unump->unum_rank = dec.ids_rankid;
+ unump->unum_offset = dec.ids_rankaddr;
+ for (i = 0; i < MC_UNUM_NDIMM; i++) {
+ unump->unum_dimms[i] = MC_INVALNUM;
+ }
+
+ return (CMI_SUCCESS);
+}
+
+static cmi_errno_t
+imc_mc_unumtopa(void *arg, mc_unum_t *unum, nvlist_t *nvl, uint64_t *pa)
+{
+ return (CMIERR_UNKNOWN);
+}
+
+static const cmi_mc_ops_t imc_mc_ops = {
+ .cmi_mc_patounum = imc_mc_patounum,
+ .cmi_mc_unumtopa = imc_mc_unumtopa
+};
+
+/*
+ * This is where we really finish attaching and become open for business. This
+ * occurs once we have all of the expected stubs attached. Here's where all of
+ * the real fun begins.
+ */
+static void
+imc_attach_complete(void *arg)
+{
+ imc_t *imc = arg;
+ cmi_errno_t err;
+
+ imc_set_gen_data(imc);
+
+ /*
+ * On SKX and newer, we can fail to map PCI buses at this point due to
+ * bad PCIe reads.
+ */
+ if (!imc_map_stubs(imc)) {
+ goto done;
+ }
+
+ imc_fixup_stubs(imc);
+ imc_map_sockets(imc);
+
+ if (!imc_create_minors(imc)) {
+ goto done;
+ }
+
+ imc_fill_data(imc);
+ imc_nvl_create(imc);
+
+ /*
+ * Gather additional information that we need so that we can properly
+ * initialize the memory decoder and encoder.
+ */
+ imc_decoder_init_sad(imc);
+ imc_decoder_init_tad(imc);
+ imc_decoder_init_rir(imc);
+
+ /*
+ * Register decoder functions. This may fail. If so, try and complain
+ * loudly, but stay active to allow other data to be useful. Register a
+ * global handle.
+ */
+ if ((err = cmi_mc_register_global(&imc_mc_ops, imc)) != CMI_SUCCESS) {
+ imc->imc_flags |= IMC_F_MCREG_FAILED;
+ dev_err(imc->imc_dip, CE_WARN, "failed to register memory "
+ "decoding operations: 0x%x", err);
+ }
+
+done:
+ mutex_enter(&imc->imc_lock);
+ imc->imc_flags &= IMC_F_ATTACH_DISPATCHED;
+ imc->imc_flags |= IMC_F_ATTACH_COMPLETE;
+ mutex_exit(&imc->imc_lock);
+}
+
+static int
+imc_stub_comparator(const void *l, const void *r)
+{
+ const imc_stub_t *sl = l, *sr = r;
+ if (sl->istub_bus > sr->istub_bus)
+ return (1);
+ if (sl->istub_bus < sr->istub_bus)
+ return (-1);
+ if (sl->istub_dev > sr->istub_dev)
+ return (1);
+ if (sl->istub_dev < sr->istub_dev)
+ return (-1);
+ if (sl->istub_func > sr->istub_func)
+ return (1);
+ if (sl->istub_func < sr->istub_func)
+ return (-1);
+ return (0);
+}
+
+static int
+imc_stub_scan_cb(dev_info_t *dip, void *arg)
+{
+ int vid, did;
+ const imc_stub_table_t *table;
+ imc_t *imc = arg;
+ int *regs;
+ uint_t i, nregs;
+
+ if (dip == ddi_root_node()) {
+ return (DDI_WALK_CONTINUE);
+ }
+
+ /*
+ * Get the dev info name. PCI devices will always be children of PCI
+ * devices today on x86. If we reach something that has a device name
+ * that's not PCI, then we can prune it's children.
+ */
+ if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
+ return (DDI_WALK_PRUNECHILD);
+ }
+
+ /*
+ * Get the device and vendor ID and see if this is something the imc
+ * knows about or cares about.
+ */
+ vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "vendor-id", PCI_EINVAL16);
+ did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "device-id", PCI_EINVAL16);
+ if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
+ return (DDI_WALK_CONTINUE);
+ }
+
+ if (vid != IMC_PCI_VENDOR_INTC) {
+ return (DDI_WALK_PRUNECHILD);
+ }
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
+ return (DDI_WALK_CONTINUE);
+ }
+
+ if (nregs == 0) {
+ ddi_prop_free(regs);
+ return (DDI_WALK_CONTINUE);
+ }
+
+
+ table = NULL;
+ for (i = 0; i < ARRAY_SIZE(imc_stub_table); i++) {
+ if (imc_stub_table[i].imcs_devid == did &&
+ imc_stub_table[i].imcs_pcidev == PCI_REG_DEV_G(regs[0]) &&
+ imc_stub_table[i].imcs_pcifunc == PCI_REG_FUNC_G(regs[0])) {
+ table = &imc_stub_table[i];
+ break;
+ }
+ }
+ ddi_prop_free(regs);
+
+ /*
+ * Not a match, not interesting.
+ */
+ if (table == NULL) {
+ return (DDI_WALK_CONTINUE);
+ }
+
+ mutex_enter(&imc->imc_lock);
+ imc->imc_nscanned++;
+ mutex_exit(&imc->imc_lock);
+
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * From here, go through and see how many of the devices that we know about.
+ */
+static void
+imc_stub_scan(void *arg)
+{
+ imc_t *imc = arg;
+ boolean_t dispatch = B_FALSE;
+
+ /*
+ * Zero out the scan results in case we've been detached and reattached.
+ */
+ mutex_enter(&imc->imc_lock);
+ imc->imc_nscanned = 0;
+ mutex_exit(&imc->imc_lock);
+
+ ddi_walk_devs(ddi_root_node(), imc_stub_scan_cb, imc);
+
+ mutex_enter(&imc->imc_lock);
+ imc->imc_flags |= IMC_F_SCAN_COMPLETE;
+ imc->imc_flags &= ~IMC_F_SCAN_DISPATCHED;
+
+ /*
+ * If the scan found no nodes, then that means that we're on a hardware
+ * platform that we don't support. Therefore, there's no reason to do
+ * anything here.
+ */
+ if (imc->imc_nscanned == 0) {
+ imc->imc_flags |= IMC_F_UNSUP_PLATFORM;
+ mutex_exit(&imc->imc_lock);
+ return;
+ }
+
+ if (avl_numnodes(&imc->imc_stubs) == imc->imc_nscanned) {
+ imc->imc_flags |= IMC_F_ATTACH_DISPATCHED;
+ dispatch = B_TRUE;
+ }
+
+ mutex_exit(&imc->imc_lock);
+
+ if (dispatch) {
+ (void) ddi_taskq_dispatch(imc->imc_taskq, imc_attach_complete,
+ imc, DDI_SLEEP);
+ }
+}
+
+/*
+ * By default, refuse to allow stubs to detach.
+ */
+int
+imc_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ imc_stub_t *stub;
+ imc_t *imc = imc_data;
+
+ mutex_enter(&imc->imc_lock);
+
+ /*
+ * By default, we do not allow stubs to detach. However, if the driver
+ * has attached to devices on a platform it doesn't recognize or
+ * support or if the override flag has been set, then allow detach to
+ * proceed.
+ */
+ if ((imc->imc_flags & IMC_F_UNSUP_PLATFORM) == 0 &&
+ imc_allow_detach == 0) {
+ mutex_exit(&imc->imc_lock);
+ return (DDI_FAILURE);
+ }
+
+ for (stub = avl_first(&imc->imc_stubs); stub != NULL;
+ stub = AVL_NEXT(&imc->imc_stubs, stub)) {
+ if (stub->istub_dip == dip) {
+ break;
+ }
+ }
+
+ /*
+ * A device was attached to us that we somehow don't know about. Allow
+ * this to proceed.
+ */
+ if (stub == NULL) {
+ mutex_exit(&imc->imc_lock);
+ return (DDI_SUCCESS);
+ }
+
+ pci_config_teardown(&stub->istub_cfgspace);
+ avl_remove(&imc->imc_stubs, stub);
+ kmem_free(stub, sizeof (imc_stub_t));
+ mutex_exit(&imc->imc_lock);
+
+ return (DDI_SUCCESS);
+}
+
+int
+imc_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ imc_stub_t *stub, *lookup;
+ int did, vid, *regs;
+ uint_t i, nregs;
+ const imc_stub_table_t *table;
+ avl_index_t idx;
+ boolean_t dispatch = B_FALSE;
+ imc_t *imc = imc_data;
+
+ if (cmd != DDI_ATTACH) {
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * We've been asked to attach a stub. First, determine if this is even a
+ * PCI device that we should care about. Then, append it to our global
+ * list and kick off the configuration task. Note that we do this
+ * configuration task in a taskq so that we don't interfere with the
+ * normal attach / detach path processing.
+ */
+ if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Get the device and vendor ID and see if this is something the imc
+ * knows about or cares about.
+ */
+ vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "vendor-id", PCI_EINVAL16);
+ did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "device-id", PCI_EINVAL16);
+ if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Only accept INTC parts on the imc driver.
+ */
+ if (vid != IMC_PCI_VENDOR_INTC) {
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ if (nregs == 0) {
+ ddi_prop_free(regs);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Determine if this matches a known device.
+ */
+ table = NULL;
+ for (i = 0; i < ARRAY_SIZE(imc_stub_table); i++) {
+ if (imc_stub_table[i].imcs_devid == did &&
+ imc_stub_table[i].imcs_pcidev == PCI_REG_DEV_G(regs[0]) &&
+ imc_stub_table[i].imcs_pcifunc == PCI_REG_FUNC_G(regs[0])) {
+ table = &imc_stub_table[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(imc_stub_table)) {
+ ddi_prop_free(regs);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * We've found something. Make sure the generation matches our current
+ * one. If it does, construct the entry and append it to the list.
+ */
+ mutex_enter(&imc->imc_lock);
+ if (imc->imc_gen != IMC_GEN_UNKNOWN && imc->imc_gen !=
+ table->imcs_gen) {
+ mutex_exit(&imc->imc_lock);
+ ddi_prop_free(regs);
+ dev_err(dip, CE_WARN, "Encountered IMC stub device (%u/%u) "
+ "that has different hardware generation (%u) from current "
+ "generation (%u)", vid, did, table->imcs_gen, imc->imc_gen);
+ return (DDI_FAILURE);
+ } else {
+ imc->imc_gen = table->imcs_gen;
+ }
+ mutex_exit(&imc->imc_lock);
+
+ stub = kmem_zalloc(sizeof (imc_stub_t), KM_SLEEP);
+ stub->istub_dip = dip;
+ stub->istub_vid = vid;
+ stub->istub_did = did;
+ stub->istub_bus = PCI_REG_BUS_G(regs[0]);
+ stub->istub_dev = PCI_REG_DEV_G(regs[0]);
+ stub->istub_func = PCI_REG_FUNC_G(regs[0]);
+ ddi_prop_free(regs);
+ stub->istub_table = table;
+
+ if (pci_config_setup(dip, &stub->istub_cfgspace) != DDI_SUCCESS) {
+ kmem_free(stub, sizeof (stub));
+ dev_err(dip, CE_WARN, "Failed to set up PCI config space "
+ "for IMC stub device %s (%u/%u)", ddi_node_name(dip),
+ vid, did);
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&imc->imc_lock);
+ if ((lookup = avl_find(&imc->imc_stubs, stub, &idx)) != NULL) {
+ dev_err(dip, CE_WARN, "IMC stub %s (%u/%u) has duplicate "
+ "bdf %u/%u/%u with %s (%u/%u), not attaching",
+ ddi_node_name(imc->imc_dip), vid, did,
+ stub->istub_bus, stub->istub_dev, stub->istub_func,
+ ddi_node_name(lookup->istub_dip), lookup->istub_vid,
+ lookup->istub_did);
+ mutex_exit(&imc->imc_lock);
+ pci_config_teardown(&stub->istub_cfgspace);
+ kmem_free(stub, sizeof (stub));
+
+ return (DDI_FAILURE);
+ }
+ avl_insert(&imc->imc_stubs, stub, idx);
+
+ if ((imc->imc_flags & IMC_F_ALL_FLAGS) == IMC_F_SCAN_COMPLETE &&
+ avl_numnodes(&imc->imc_stubs) == imc->imc_nscanned) {
+ imc->imc_flags |= IMC_F_ATTACH_DISPATCHED;
+ dispatch = B_TRUE;
+ }
+ mutex_exit(&imc->imc_lock);
+
+ if (dispatch) {
+ (void) ddi_taskq_dispatch(imc->imc_taskq, imc_attach_complete,
+ imc, DDI_SLEEP);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static int
+imc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ imc_t *imc = imc_data;
+
+ if ((flag & (FEXCL | FNDELAY)) != 0)
+ return (EINVAL);
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ mutex_enter(&imc->imc_lock);
+
+ if ((imc->imc_flags & IMC_F_UNSUP_PLATFORM) != 0) {
+ mutex_exit(&imc->imc_lock);
+ return (ENOTSUP);
+ }
+
+ /*
+ * It's possible that someone has come in during the window between when
+ * we've created the minor node and when we've finished doing work.
+ */
+ if ((imc->imc_flags & IMC_F_ATTACH_COMPLETE) == 0) {
+ mutex_exit(&imc->imc_lock);
+ return (EAGAIN);
+ }
+
+ /*
+ * It's not clear how someone would get a minor that we didn't create.
+ * But be paranoid and make sure.
+ */
+ if (getminor(*devp) >= imc->imc_nsockets) {
+ mutex_exit(&imc->imc_lock);
+ return (EINVAL);
+ }
+
+ /*
+ * Make sure this socket entry has been filled in.
+ */
+ if (imc->imc_spointers[getminor(*devp)] == NULL) {
+ mutex_exit(&imc->imc_lock);
+ return (EINVAL);
+ }
+
+ mutex_exit(&imc->imc_lock);
+
+ return (0);
+}
+
+static void
+imc_ioctl_decode(imc_t *imc, mc_encode_ioc_t *encode)
+{
+ imc_decode_state_t dec;
+ uint_t i;
+
+ bzero(&dec, sizeof (dec));
+ if (!imc_decode_pa(imc, encode->mcei_pa, &dec)) {
+ encode->mcei_err = (uint32_t)dec.ids_fail;
+ encode->mcei_errdata = dec.ids_fail_data;
+ return;
+ }
+
+ encode->mcei_errdata = 0;
+ encode->mcei_err = 0;
+ encode->mcei_board = 0;
+ for (i = 0; i < imc->imc_nsockets; i++) {
+ if (imc->imc_spointers[i] == dec.ids_socket)
+ break;
+ }
+ encode->mcei_chip = i;
+ encode->mcei_mc = dec.ids_tadid;
+ encode->mcei_chan = dec.ids_channelid;
+ encode->mcei_dimm = dec.ids_dimmid;
+ encode->mcei_rank_addr = dec.ids_rankaddr;
+ encode->mcei_rank = dec.ids_rankid;
+ encode->mcei_row = UINT32_MAX;
+ encode->mcei_column = UINT32_MAX;
+ encode->mcei_pad = 0;
+}
+
+static int
+imc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ int ret;
+ minor_t m;
+ mc_snapshot_info_t info;
+ mc_encode_ioc_t encode;
+ imc_t *imc = imc_data;
+ imc_socket_t *sock;
+
+ mutex_enter(&imc->imc_lock);
+ m = getminor(dev);
+ if (m >= imc->imc_nsockets) {
+ ret = EINVAL;
+ goto done;
+ }
+ sock = imc->imc_spointers[m];
+ if (sock == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Note, other memory controller drivers don't check mode for reading
+ * data nor do they care who can read it from a credential perspective.
+ * As such we don't either at this time.
+ */
+ switch (cmd) {
+ case MC_IOC_SNAPSHOT_INFO:
+ imc_nvl_pack(sock, B_FALSE);
+ if (sock->isock_buf == NULL) {
+ ret = EIO;
+ break;
+ }
+
+ info.mcs_size = sock->isock_buflen;
+ info.mcs_gen = sock->isock_gen;
+
+ if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0) {
+ ret = EFAULT;
+ break;
+ }
+
+ ret = 0;
+ break;
+ case MC_IOC_SNAPSHOT:
+ imc_nvl_pack(sock, B_FALSE);
+ if (sock->isock_buf == NULL) {
+ ret = EIO;
+ break;
+ }
+
+ if (ddi_copyout(sock->isock_buf, (void *)arg,
+ sock->isock_buflen, mode) != 0) {
+ ret = EFAULT;
+ break;
+ }
+
+ ret = 0;
+ break;
+ case MC_IOC_DECODE_SNAPSHOT_INFO:
+ imc_decoder_pack(imc);
+ if (imc->imc_decoder_buf == NULL) {
+ ret = EIO;
+ break;
+ }
+
+ info.mcs_size = imc->imc_decoder_len;
+ info.mcs_gen = imc->imc_spointers[0]->isock_gen;
+
+ if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0) {
+ ret = EFAULT;
+ break;
+ }
+
+ ret = 0;
+ break;
+ case MC_IOC_DECODE_SNAPSHOT:
+ imc_decoder_pack(imc);
+ if (imc->imc_decoder_buf == NULL) {
+ ret = EIO;
+ break;
+ }
+
+ if (ddi_copyout(imc->imc_decoder_buf, (void *)arg,
+ imc->imc_decoder_len, mode) != 0) {
+ ret = EFAULT;
+ break;
+ }
+
+ ret = 0;
+ break;
+ case MC_IOC_DECODE_PA:
+ if (crgetzoneid(credp) != GLOBAL_ZONEID ||
+ drv_priv(credp) != 0) {
+ ret = EPERM;
+ break;
+ }
+
+ if (ddi_copyin((void *)arg, &encode, sizeof (encode),
+ mode & FKIOCTL) != 0) {
+ ret = EPERM;
+ break;
+ }
+
+ imc_ioctl_decode(imc, &encode);
+ ret = 0;
+
+ if (ddi_copyout(&encode, (void *)arg, sizeof (encode),
+ mode & FKIOCTL) != 0) {
+ ret = EPERM;
+ break;
+ }
+ break;
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+
+done:
+ mutex_exit(&imc->imc_lock);
+ return (ret);
+}
+
+static int
+imc_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+static int
+imc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ if (cmd != DDI_ATTACH) {
+ return (DDI_FAILURE);
+ }
+
+ if (imc_data == NULL || imc_data->imc_dip != NULL) {
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&imc_data->imc_lock);
+ if ((imc_data->imc_taskq = ddi_taskq_create(dip, "imc", 1,
+ TASKQ_DEFAULTPRI, 0)) == NULL) {
+ mutex_exit(&imc_data->imc_lock);
+ return (DDI_FAILURE);
+ }
+
+ imc_data->imc_dip = dip;
+ imc_data->imc_flags |= IMC_F_SCAN_DISPATCHED;
+ mutex_exit(&imc_data->imc_lock);
+
+ (void) ddi_taskq_dispatch(imc_data->imc_taskq, imc_stub_scan, imc_data,
+ DDI_SLEEP);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * We only export a single instance.
+ */
+static int
+imc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
+{
+ /*
+ * getinfo(9E) shouldn't be called if we're not attached. But be
+ * paranoid.
+ */
+ if (imc_data == NULL || imc_data->imc_dip == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *resultp = imc_data->imc_dip;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = (void *)0;
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static int
+imc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH) {
+ return (DDI_FAILURE);
+ }
+
+ if (imc_data == NULL || imc_data->imc_dip) {
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&imc_data->imc_lock);
+
+ /*
+ * While a scan or attach is outstanding, don't allow us to detach.
+ */
+ if ((imc_data->imc_flags &
+ (IMC_F_SCAN_DISPATCHED | IMC_F_ATTACH_DISPATCHED)) != 0) {
+ mutex_exit(&imc_data->imc_lock);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Because the stub driver depends on the imc driver, we shouldn't be
+ * able to have any entries in this list when we detach. However, we
+ * check just to make sure.
+ */
+ if (!avl_is_empty(&imc_data->imc_stubs)) {
+ mutex_exit(&imc_data->imc_lock);
+ return (DDI_FAILURE);
+ }
+
+ nvlist_free(imc_data->imc_decoder_dump);
+ imc_data->imc_decoder_dump = NULL;
+ if (imc_data->imc_decoder_buf != NULL) {
+ kmem_free(imc_data->imc_decoder_buf, imc_data->imc_decoder_len);
+ imc_data->imc_decoder_buf = NULL;
+ imc_data->imc_decoder_len = 0;
+ }
+
+ ddi_remove_minor_node(imc_data->imc_dip, NULL);
+ imc_data->imc_dip = NULL;
+ mutex_exit(&imc_data->imc_lock);
+
+ ddi_taskq_wait(imc_data->imc_taskq);
+ ddi_taskq_destroy(imc_data->imc_taskq);
+ imc_data->imc_taskq = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+static void
+imc_free(void)
+{
+ if (imc_data == NULL) {
+ return;
+ }
+
+ VERIFY(avl_is_empty(&imc_data->imc_stubs));
+ avl_destroy(&imc_data->imc_stubs);
+ mutex_destroy(&imc_data->imc_lock);
+ kmem_free(imc_data, sizeof (imc_t));
+ imc_data = NULL;
+}
+
+static void
+imc_alloc(void)
+{
+ imc_data = kmem_zalloc(sizeof (imc_t), KM_SLEEP);
+
+ mutex_init(&imc_data->imc_lock, NULL, MUTEX_DRIVER, NULL);
+ avl_create(&imc_data->imc_stubs, imc_stub_comparator,
+ sizeof (imc_stub_t), offsetof(imc_stub_t, istub_link));
+}
+
+static struct cb_ops imc_cb_ops = {
+ .cb_open = imc_open,
+ .cb_close = imc_close,
+ .cb_strategy = nodev,
+ .cb_print = nodev,
+ .cb_dump = nodev,
+ .cb_read = nodev,
+ .cb_write = nodev,
+ .cb_ioctl = imc_ioctl,
+ .cb_devmap = nodev,
+ .cb_mmap = nodev,
+ .cb_segmap = nodev,
+ .cb_chpoll = nochpoll,
+ .cb_prop_op = ddi_prop_op,
+ .cb_flag = D_MP,
+ .cb_rev = CB_REV,
+ .cb_aread = nodev,
+ .cb_awrite = nodev
+};
+
+static struct dev_ops imc_dev_ops = {
+ .devo_rev = DEVO_REV,
+ .devo_refcnt = 0,
+ .devo_getinfo = imc_getinfo,
+ .devo_identify = nulldev,
+ .devo_probe = nulldev,
+ .devo_attach = imc_attach,
+ .devo_detach = imc_detach,
+ .devo_reset = nodev,
+ .devo_cb_ops = &imc_cb_ops,
+ .devo_quiesce = ddi_quiesce_not_needed
+};
+
+static struct modldrv imc_modldrv = {
+ .drv_modops = &mod_driverops,
+ .drv_linkinfo = "Intel Integrated Memory Controller Driver",
+ .drv_dev_ops = &imc_dev_ops
+};
+
+static struct modlinkage imc_modlinkage = {
+ .ml_rev = MODREV_1,
+ .ml_linkage = { &imc_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ int ret;
+
+ if ((ret = mod_install(&imc_modlinkage)) == 0) {
+ imc_alloc();
+ }
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&imc_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ if ((ret = mod_remove(&imc_modlinkage)) == 0) {
+ imc_free();
+ }
+ return (ret);
+}
diff --git a/usr/src/uts/i86pc/io/imc/imc.conf b/usr/src/uts/i86pc/io/imc/imc.conf
new file mode 100644
index 0000000000..7f55dc2cae
--- /dev/null
+++ b/usr/src/uts/i86pc/io/imc/imc.conf
@@ -0,0 +1,16 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+name="imc" parent="pseudo" instance=0;
diff --git a/usr/src/uts/i86pc/io/imc/imc.h b/usr/src/uts/i86pc/io/imc/imc.h
new file mode 100644
index 0000000000..7d07be20af
--- /dev/null
+++ b/usr/src/uts/i86pc/io/imc/imc.h
@@ -0,0 +1,940 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#ifndef _INTEL_IMC_H
+#define _INTEL_IMC_H
+
+#include <sys/types.h>
+#include <sys/bitmap.h>
+#include <sys/list.h>
+#include <sys/sunddi.h>
+
+/*
+ * This header file contains the definitions used for the various generations of
+ * the Intel IMC driver.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The maximum number of sockets that the IMC driver supports. This is currently
+ * determined by the Purley platforms (Skylake) which support up to 8 CPUs.
+ */
+#define IMC_MAX_SOCKETS 8
+
+/*
+ * The maximum number of memory controllers that exist per socket. Currently all
+ * supported platforms (Sandy Bridge -> Skylake) support at most two.
+ */
+#define IMC_MAX_IMCPERSOCK 2
+
+/*
+ * The maximum number of channels that exist per IMC. Currently Skylake supports
+ * 3 per IMC. On certain configurations of Haswell/Broadwell, there is only a
+ * single IMC which supports all 4 channels.
+ */
+#define IMC_MAX_CHANPERMC 4
+
+/*
+ * The maximum number of DIMMs that exist per channel. On Skylake this is two
+ * DIMMs. However, Sandy Bridge through Broadwell support three.
+ */
+#define IMC_MAX_DIMMPERCHAN 3
+
+/*
+ * The maximum number of rank disable bits per DIMM. This is currently
+ * consistent across all generations that have these bits.
+ */
+#define IMC_MAX_RANK_DISABLE 4
+
+/*
+ * The number of different PCI buses that we need to record for a given
+ * platform. Pre-Skylake there are only two that are required, one for the IIO
+ * and one for the non-IIO. On Skylake, more PCI buses are used.
+ */
+#define IMC_MAX_PCIBUSES 3
+
+/*
+ * Macros to take apart the node id for a given processor. These assume that
+ * we're reading the nodeid from the UBox and not from the SAD control.
+ */
+#define IMC_NODEID_UBOX_MASK(x) ((x) & 0x7)
+
+/*
+ * On Ivy Bridge through Broadwell, the node id that is found in the SAD targets
+ * has the HA indicator as NodeID[2]. This means that the actual target node of
+ * the socket is NodeID[3] | NodeID[1:0].
+ */
+#define IMC_NODEID_IVY_BRD_UPPER(x) BITX(x, 3, 3)
+#define IMC_NODEID_IVY_BRD_LOWER(x) BITX(x, 1, 0)
+#define IMC_NODEID_IVY_BRD_HA(x) BITX(x, 2, 2)
+
+/*
+ * Macros to take apart the MCMTR register bits that we care about.
+ */
+#define IMC_MCMTR_CLOSED_PAGE(x) BITX(x, 0, 0)
+#define IMC_MCMTR_LOCKSTEP(x) BITX(x, 1, 1)
+#define IMC_MCMTR_ECC_ENABLED(x) BITX(x, 2, 2)
+
+#define IMC_MCMTR_DDR4_HAS_BRD(x) BITX(x, 14, 14)
+
+/*
+ * Macros to take apart the dimmmtr_* registers in different generations. While
+ * there are similarities, these often end up different between generations and
+ * chips. These macros use a range of CPUs that they're valid for in the name.
+ * Macros with no suffix are valid for all currently supported CPUs.
+ */
+
+#define IMC_REG_MC_MTR0 0x80
+#define IMC_REG_MC_MTR1 0x84
+#define IMC_REG_MC_MTR2 0x88
+
+#define IMC_MTR_CA_WIDTH(x) BITX(x, 1, 0)
+#define IMC_MTR_CA_BASE 10
+#define IMC_MTR_CA_MIN 10
+#define IMC_MTR_CA_MAX 12
+
+#define IMC_MTR_RA_WIDTH(x) BITX(x, 4, 2)
+#define IMC_MTR_RA_BASE 12
+#define IMC_MTR_RA_MIN 13
+#define IMC_MTR_RA_MAX 18
+
+#define IMC_MTR_DENSITY_IVY_BRD(x) BITX(x, 6, 5)
+#define IMC_MTR_DENSITY_SKX(x) BITX(x, 7, 5)
+
+#define IMC_MTR_WIDTH_IVB_HAS(x) BITX(x, 8, 7)
+#define IMC_MTR_WIDTH_BRD_SKX(x) BITX(x, 9, 8)
+
+#define IMC_MTR_DDR_RANKS(x) BITX(x, 13, 12)
+#define IMC_MTR_DDR_RANKS_MAX 4
+#define IMC_MTR_DDR_RANKS_MAX_HAS_SKX 8
+
+#define IMC_MTR_PRESENT_SNB_BRD(x) BITX(x, 14, 14)
+#define IMC_MTR_PRESENT_SKYLAKE(x) BITX(x, 15, 15)
+
+#define IMC_MTR_RANK_DISABLE(x) BITX(x, 19, 16)
+
+#define IMC_MTR_DDR4_ENABLE_HAS_BRD(x) BITX(x, 20, 20)
+#define IMC_MTR_HDRL_HAS_SKX(x) BITX(x, 21, 21)
+#define IMC_MTR_HDRL_PARITY_HAS_SKX(x) BITX(x, 22, 22)
+#define IMC_MTR_3DSRANKS_HAS_SKX(x) BITX(x, 24, 23)
+
+/*
+ * Data for the RASENABLES register.
+ */
+#define IMC_MC_MIRROR_SNB_BRD(x) BITX(x, 0, 0)
+
+/*
+ * The maximum number of SAD rules that exist on all supported platforms.
+ */
+#define IMC_MAX_SAD_RULES 24
+
+/*
+ * The maximum number of targets that can be interleaved in a sad rule.
+ */
+#define IMC_MAX_SAD_INTERLEAVE 8
+
+/*
+ * The maximum number of route entries that exist in SAD. This is only used on
+ * SKX.
+ */
+#define IMC_MAX_SAD_MCROUTES 6
+
+/*
+ * Definitions used to decode the MC Route table. Note that at this time this is
+ * very Skylake specific (as it's the only platform it's supported on).
+ */
+#define IMC_REG_SKX_SAD_MC_ROUTE_TABLE 0xb4
+#define IMC_MC_ROUTE_RING_BITS 3
+#define IMC_MC_ROUTE_RING_MASK 0x7
+#define IMC_MC_ROUTE_CHAN_BITS 2
+#define IMC_MC_ROUTE_CHAN_MASK 0x3
+#define IMC_MC_ROUTE_CHAN_OFFSET 18
+
+/*
+ * Definitions to help decode TOLM (top of low memory) and TOHM (top of high
+ * memory). The way this is done varies based on generation. These regions are
+ * currently always 64-MByte aligned
+ *
+ * On Sandy Bridge and Ivy Bridge the low four bits of TOLM are bits 31:28. TOHM
+ * is a single register. Bits 20:0 map to bits 45:25. Both registers represent
+ * the upper limit (as in one higher than the max DRAM value).
+ *
+ * On Haswell through Skylake, TOLM is represented as a 32-bit quantity. No
+ * shifting is required. However, only bits 31:26 are present. TOHM is spread
+ * out among two registers. The lower 32-bits is masked in a similar fashion. In
+ * both cases, these registers represent an inclusive range where we don't care
+ * about other bits. To deal with this we'll increment the lowest bit we care
+ * about to make it an exclusive range.
+ *
+ * Based on the above, we have opted to make both ranges in the IMC driver
+ * normalized to an _exclusive_ value.
+ *
+ * Ivy Bridge has the values in both the CBo SAD and a VT-d section; however, we
+ * use the CBo SAD which is why it looks like Sandy Bridge and not Haswell.
+ */
+
+#define IMC_TOLM_SNB_IVY_MASK 0xf
+#define IMC_TOLM_SNB_IVY_SHIFT 28
+#define IMC_TOHM_SNB_IVY_MASK 0x1fffff
+#define IMC_TOHM_SNB_IVY_SHIFT 25
+
+#define IMC_TOLM_HAS_SKX_MASK 0xfc000000
+#define IMC_TOLM_HAS_SKY_EXCL (1 << 26)
+#define IMC_TOHM_LOW_HAS_SKX_MASK 0xfc000000
+#define IMC_TOHM_HAS_SKY_EXCL (1 << 26)
+
+/*
+ * Definitions to decode SAD values. These are sometimes subtlety different
+ * across generations.
+ */
+#define IMC_SAD_DRAM_RULE_ENABLE(x) BITX(x, 0, 0)
+
+#define IMC_SAD_DRAM_INTERLEAVE_SNB_BRD(x) BITX(x, 1, 1)
+#define IMC_SAD_DRAM_INTERLEAVE_SNB_BRD_8t6XOR 0
+#define IMC_SAD_DRAM_INTERLEAVE_SNB_BRD_8t6 1
+
+#define IMC_SAD_DRAM_INTERLEAVE_SKX(x) BITX(x, 2, 1)
+#define IMC_SAD_DRAM_INTERLEAVE_SKX_8t6 0
+#define IMC_SAD_DRAM_INTERLEAVE_SKX_10t8 1
+#define IMC_SAD_DRAM_INTERLEAVE_SKX_14t12 2
+#define IMC_SAD_DRAM_INTERLEAVE_SKX_32t30 3
+
+#define IMC_SAD_DRAM_ATTR_SNB_BRD(x) BITX(x, 3, 2)
+#define IMC_SAD_DRAM_ATTR_SKX(x) BITX(x, 4, 3)
+#define IMC_SAD_DRAM_ATTR_DRAM 0
+#define IMC_SAD_DRAM_ATTR_MMCFG 1
+#define IMC_SAD_DRAM_ATTR_NXM 2
+
+#define IMC_SAD_DRAM_MOD23_SKX(x) BITX(x, 6, 5)
+#define IMC_SAD_DRAM_MOD23_MOD3 0
+#define IMC_SAD_DRAM_MOD23_MOD2_C01 1
+#define IMC_SAD_DRAM_MOD23_MOD2_C12 2
+#define IMC_SAD_DRAM_MOD23_MOD2_C02 3
+
+#define IMC_SAD_DRAM_LIMIT_SNB_BRD(x) BITX(x, 25, 6)
+#define IMC_SAD_DRAM_LIMIT_SKX(x) BITX(x, 26, 7)
+#define IMC_SAD_DRAM_LIMIT_SHIFT 26
+#define IMC_SAD_DRAM_LIMIT_EXCLUSIVE (1 << IMC_SAD_DRAM_LIMIT_SHIFT)
+
+#define IMC_SAD_DRAM_A7_IVB_BRD(x) BITX(x, 26, 26)
+#define IMC_SAD_DRAM_MOD3_SKX(x) BITX(x, 27, 27)
+#define IMC_SAD_DRAM_MOD3_MODE_SKX(x) BITX(x, 31, 30)
+#define IMC_SAD_DRAM_MOD3_MODE_45t6 0
+#define IMC_SAD_DRAM_MOD3_MODE_45t8 1
+#define IMC_SAD_DRAM_MOD3_MODE_45t12 2
+
+#define IMC_SAD_ILEAVE_SNB_MASK 0x7
+#define IMC_SAD_ILEAVE_SNB_LEN 3
+#define IMC_SAD_ILEAVE_IVB_SKX_MASK 0xf
+#define IMC_SAD_ILEAVE_IVB_SKX_LEN 4
+
+/*
+ * The interleave targets on Skylake use the upper bit to indicate whether it is
+ * referring to a local memory controller or if it actually refers to another
+ * node that is far away. The maximum value includes the upper bit which is used
+ * to indicate whether it is remote or far.
+ */
+#define IMC_SAD_ILEAVE_SKX_LOCAL(x) BITX(x, 3, 3)
+#define IMC_SAD_ILEAVE_SKX_TARGET(x) BITX(x, 2, 0)
+#define IMC_SAD_ILEAVE_SKX_MAX 0xf
+
+/*
+ * Maximum number of TAD tables that we need to consider. On Sandy Bridge
+ * through Broadwell this is based on the number of home agents that are present
+ * in the system. On Sandy Bridge there is one, on others, there are up to two.
+ * On Skylake, there is one TAD per IMC.
+ */
+#define IMC_MAX_TAD 2
+
+/*
+ * Maximum number of TAD rules on any of the supported processors.
+ */
+#define IMC_MAX_TAD_RULES 12
+
+/*
+ * Maximum number of interleave targets. Note, this only applies to Sandy Bridge
+ * through Broadwell. Skylake gets this information in another form.
+ */
+#define IMC_MAX_TAD_TARGETS 4
+
+/*
+ * Offset between the base TAD rule and the corresponding wayness rule on
+ * Skylake.
+ */
+#define IMC_SKX_WAYNESS_OFFSET 0x30
+
+/*
+ * Various macros to decode the TAD rules.
+ */
+#define IMC_TAD_LIMIT(x) BITX(x, 31, 12)
+#define IMC_TAD_LIMIT_SHIFT 26
+#define IMC_TAD_LIMIT_EXCLUSIVE (1 << IMC_TAD_LIMIT_SHIFT)
+
+#define IMC_TAD_SOCK_WAY(x) BITX(x, 11, 10)
+#define IMC_TAD_SOCK_WAY_1 0
+#define IMC_TAD_SOCK_WAY_2 1
+#define IMC_TAD_SOCK_WAY_4 2
+#define IMC_TAD_SOCK_WAY_8 3
+#define IMC_TAD_CHAN_WAY(x) BITX(x, 9, 8)
+#define IMC_TAD_TARG3(x) BITX(x, 7, 6)
+#define IMC_TAD_TARG2(x) BITX(x, 5, 4)
+#define IMC_TAD_TARG1(x) BITX(x, 3, 2)
+#define IMC_TAD_TARG0(x) BITX(x, 1, 0)
+
+#define IMC_TAD_SNB_BRD_NTARGETS 4
+
+/*
+ * These are registers specific to the Skylake and newer TAD BASE registers.
+ */
+#define IMC_TAD_BASE_BASE(x) BITX(x, 31, 12)
+#define IMC_TAD_BASE_SHIFT 26
+
+#define IMC_TAD_BASE_CHAN_GRAN(x) BITX(x, 7, 6)
+#define IMC_TAD_BASE_CHAN_GRAN_64B 0
+#define IMC_TAD_BASE_CHAN_GRAN_256B 1
+#define IMC_TAD_BASE_CHAN_GRAN_4KB 2
+
+#define IMC_TAD_BASE_SOCK_GRAN(x) BITX(x, 5, 4)
+#define IMC_TAD_BASE_SOCK_GRAN_64B 0
+#define IMC_TAD_BASE_SOCK_GRAN_256B 1
+#define IMC_TAD_BASE_SOCK_GRAN_4KB 2
+#define IMC_TAD_BASE_SOCK_GRAN_1GB 3
+
+#define IMC_TADCHAN_OFFSET_SNB_BRD(x) BITX(x, 25, 6)
+#define IMC_TADCHAN_OFFSET_SKX(x) BITX(x, 23, 4)
+#define IMC_TADCHAN_OFFSET_SHIFT 26
+
+/*
+ * Macros to get at various TAD features.
+ */
+#define IMC_TAD_SYSDEF_LOCKSTEP(x) BITX(x, 7, 7)
+#define IMC_TAD_SYSDEF2_SHIFTUP(x) BITX(x, 22, 22)
+#define IMC_TAD_SYSDEF2_CHANHASH(x) BITX(x, 21, 21)
+
+/*
+ * Maximum number of different wayness entries that exist across the various IMC
+ * generations. Each wayness then has a maximum number of target entries.
+ */
+#define IMC_MAX_RANK_WAYS 5
+#define IMC_MAX_RANK_INTERLEAVES 8
+
+/*
+ * Macros to take apart the rank interleave wayness and offset registers.
+ */
+#define IMC_RIR_WAYNESS_ENABLED(x) BITX(x, 31, 31)
+#define IMC_RIR_WAYNESS_WAY(x) BITX(x, 29, 28)
+#define IMC_RIR_LIMIT_HAS_SKX(x) BITX(x, 11, 1)
+#define IMC_RIR_LIMIT_SNB_IVB(x) BITX(x, 10, 1)
+#define IMC_RIR_LIMIT_SHIFT 29
+#define IMC_RIR_LIMIT_EXCLUSIVE (1 << IMC_RIR_LIMIT_SHIFT)
+
+/*
+ * Currently, everything other than Broadwell has the same value for the target
+ * offset.
+ */
+#define IMC_RIR_OFFSET_TARGET_BRD(x) BITX(x, 23, 20)
+#define IMC_RIR_OFFSET_TARGET(x) BITX(x, 19, 16)
+#define IMC_RIR_OFFSET_OFFSET_HAS_SKX(x) BITX(x, 15, 2)
+#define IMC_RIR_OFFSET_OFFSET_SNB_IVB(x) BITX(x, 14, 2)
+#define IMC_RIR_OFFSET_SHIFT 29
+
+/*
+ * Definitions to cover manipulations of open and closed pages.
+ */
+#define IMC_PAGE_BITS_CLOSED 6
+#define IMC_PAGE_BITS_OPEN 13
+
+/*
+ * Macros to decode and understand the CPUBUSNO registers in the UBOX_DECS.
+ */
+#define IMC_UBOX_CPUBUSNO_0(x) BITX(x, 7, 0)
+#define IMC_UBOX_CPUBUSNO_1(x) BITX(x, 15, 8)
+#define IMC_UBOX_CPUBUSNO_2(x) BITX(x, 23, 16)
+
+/*
+ * Hardware generations supported by the IMC driver.
+ */
+typedef enum {
+ IMC_GEN_UNKNOWN = 0,
+ IMC_GEN_SANDY,
+ IMC_GEN_IVY,
+ IMC_GEN_HASWELL,
+ IMC_GEN_BROADWELL,
+ /*
+ * IMC_GEN_SKYLAKE also covers Cascade Lake. The two are similar to the
+ * point of even having the same PCI IDs for all of the devices. The
+ * only difference in the cpuid signature between them is the stepping,
+ * hence we do not have a separate Cascade Lake target here, as it's
+ * really the same as Skylake.
+ */
+ IMC_GEN_SKYLAKE
+} imc_gen_t;
+
+/*
+ * Generation specific limits.
+ */
+typedef struct imc_gen_data {
+ uint_t igd_max_sockets;
+ uint_t igd_max_imcs;
+ uint_t igd_max_channels;
+ uint_t igd_max_dimms;
+ uint_t igd_max_ranks;
+ uint_t igd_mtr_offsets[IMC_MAX_DIMMPERCHAN];
+ uint_t igd_mcmtr_offset;
+ uint_t igd_topo_offset;
+ uint_t igd_num_mcroutes;
+ uint_t igd_tolm_offset;
+ uint_t igd_tohm_low_offset;
+ uint_t igd_tohm_hi_offset;
+ uint_t igd_sad_dram_offset;
+ uint_t igd_sad_ndram_rules;
+ uint_t igd_sad_nodeid_offset;
+ uint_t igd_tad_nrules;
+ uint_t igd_tad_rule_offset;
+ uint_t igd_tad_chan_offset;
+ uint_t igd_tad_sysdef;
+ uint_t igd_tad_sysdef2;
+ uint_t igd_mc_mirror;
+ uint_t igd_rir_nways;
+ uint_t igd_rir_way_offset;
+ uint_t igd_rir_nileaves;
+ uint_t igd_rir_ileave_offset;
+ uint_t igd_ubox_cpubusno_offset;
+} imc_gen_data_t;
+
+/*
+ * Different types of PCI devices that show up on the core that we may need to
+ * attach to.
+ */
+typedef enum {
+ IMC_TYPE_UNKNOWN = 0,
+ IMC_TYPE_MC0_M2M, /* SKX Only */
+ IMC_TYPE_MC1_M2M, /* SKX Only */
+ IMC_TYPE_MC0_MAIN0,
+ IMC_TYPE_MC0_MAIN1,
+ IMC_TYPE_MC1_MAIN0,
+ IMC_TYPE_MC1_MAIN1,
+ IMC_TYPE_MC0_CHANNEL0,
+ IMC_TYPE_MC0_CHANNEL1,
+ IMC_TYPE_MC0_CHANNEL2,
+ IMC_TYPE_MC0_CHANNEL3,
+ IMC_TYPE_MC1_CHANNEL0,
+ IMC_TYPE_MC1_CHANNEL1,
+ IMC_TYPE_MC1_CHANNEL2,
+ IMC_TYPE_MC1_CHANNEL3,
+ IMC_TYPE_SAD_DRAM,
+ IMC_TYPE_SAD_MMIO,
+ /*
+ * We want to note which device has the TOLM and TOHM registers.
+ * Unfortunately this is a rather complicated affair. On Sandy Bridge
+ * they are a part of the IMC_TYPE_SAD_MMIO. On Ivy Bridge, it's on its
+ * own dedicated device on the CBo.
+ *
+ * On Haswell onward, these move to the VT-D misc. registers. On Haswell
+ * and Broadwell, only one of these exist in the system. However, on
+ * Skylake these exist per socket.
+ */
+ IMC_TYPE_SAD_MISC,
+ IMC_TYPE_VTD_MISC,
+ /*
+ * On SKX this exists on a per-core basis. It contains the memory
+ * controller routing table.
+ */
+ IMC_TYPE_SAD_MCROUTE,
+ IMC_TYPE_UBOX,
+ IMC_TYPE_UBOX_CPUBUSNO,
+ IMC_TYPE_HA0,
+ IMC_TYPE_HA1,
+} imc_type_t;
+
+/*
+ * Each entry in the stub table represents a device that we might attach to in a
+ * given generation. This is only defined in the kernel to make it easier to
+ * build the imc decoder in userland for testing.
+ */
+#ifdef _KERNEL
+typedef struct imc_stub_table {
+ imc_gen_t imcs_gen;
+ imc_type_t imcs_type;
+ uint16_t imcs_devid;
+ uint16_t imcs_pcidev;
+ uint16_t imcs_pcifunc;
+ const char *imcs_desc;
+} imc_stub_table_t;
+
+typedef struct imc_stub {
+ avl_node_t istub_link;
+ dev_info_t *istub_dip;
+ uint16_t istub_vid;
+ uint16_t istub_did;
+ uint16_t istub_bus;
+ uint16_t istub_dev;
+ uint16_t istub_func;
+ ddi_acc_handle_t istub_cfgspace;
+ const imc_stub_table_t *istub_table;
+} imc_stub_t;
+#else
+typedef struct imc_stub {
+ void *istub_unused;
+} imc_stub_t;
+#endif /* _KERNEL */
+
+typedef enum {
+ IMC_F_UNSUP_PLATFORM = (1 << 0),
+ IMC_F_SCAN_DISPATCHED = (1 << 1),
+ IMC_F_SCAN_COMPLETE = (1 << 2),
+ IMC_F_ATTACH_DISPATCHED = (1 << 3),
+ IMC_F_ATTACH_COMPLETE = (1 << 4),
+ IMC_F_MCREG_FAILED = (1 << 5)
+} imc_flags_t;
+
+#define IMC_F_ALL_FLAGS (IMC_F_UNSUP_PLATFORM | IMC_F_SCAN_DISPATCHED | \
+ IMC_F_SCAN_COMPLETE | IMC_F_ATTACH_DISPATCHED | IMC_F_ATTACH_COMPLETE | \
+ IMC_F_MCREG_FAILED)
+
+typedef enum imc_dimm_type {
+ IMC_DIMM_UNKNOWN,
+ IMC_DIMM_DDR3,
+ IMC_DIMM_DDR4,
+ IMC_DIMM_NVDIMM
+} imc_dimm_type_t;
+
+typedef enum imc_dimm_valid {
+ IMC_DIMM_V_VALID = 0,
+ IMC_DIMM_V_BAD_PCI_READ = (1 << 0),
+ IMC_DIMM_V_BAD_ROWS = (1 << 1),
+ IMC_DIMM_V_BAD_COLUMNS = (1 << 2),
+ IMC_DIMM_V_BAD_DENSITY = (1 << 3),
+ IMC_DIMM_V_BAD_WIDTH = (1 << 4),
+ IMC_DIMM_V_BAD_RANKS = (1 << 5)
+} imc_dimm_valid_t;
+
+typedef struct imc_dimm {
+ imc_dimm_valid_t idimm_valid;
+ boolean_t idimm_present;
+ uint8_t idimm_3dsranks;
+ boolean_t idimm_hdrl_parity;
+ boolean_t idimm_hdrl;
+ boolean_t idimm_ranks_disabled[IMC_MAX_RANK_DISABLE];
+ uint8_t idimm_nbanks;
+ uint8_t idimm_nranks;
+ uint8_t idimm_width;
+ uint8_t idimm_density; /* In GiB */
+ uint8_t idimm_nrows;
+ uint8_t idimm_ncolumns;
+ /* Synthesized */
+ uint64_t idimm_size;
+ /* Raw data */
+ uint32_t idimm_mtr;
+} imc_dimm_t;
+
+typedef struct imc_rank_ileave_entry {
+ uint8_t irle_target;
+ uint64_t irle_offset;
+} imc_rank_ileave_entry_t;
+
+typedef struct imc_rank_ileave {
+ boolean_t irle_enabled;
+ uint32_t irle_raw;
+ uint8_t irle_nways;
+ uint8_t irle_nwaysbits;
+ uint64_t irle_limit;
+ uint_t irle_nentries;
+ imc_rank_ileave_entry_t irle_entries[IMC_MAX_RANK_INTERLEAVES];
+} imc_rank_ileave_t;
+
+typedef enum imc_channel_valid {
+ IMC_CHANNEL_V_VALID = 0,
+ IMC_CHANNEL_V_BAD_PCI_READ = 1 << 0,
+} imc_channel_valid_t;
+
+typedef struct imc_channel {
+ imc_channel_valid_t ich_valid;
+ imc_stub_t *ich_desc;
+ uint_t ich_ndimms;
+ imc_dimm_t ich_dimms[IMC_MAX_DIMMPERCHAN];
+ uint_t ich_ntad_offsets;
+ uint32_t ich_tad_offsets_raw[IMC_MAX_TAD_RULES];
+ uint64_t ich_tad_offsets[IMC_MAX_TAD_RULES];
+ uint_t ich_nrankileaves;
+ imc_rank_ileave_t ich_rankileaves[IMC_MAX_RANK_WAYS];
+} imc_channel_t;
+
+typedef struct imc_controller {
+ imc_stub_t *icn_main0;
+ imc_stub_t *icn_main1;
+ imc_stub_t *icn_m2m;
+ boolean_t icn_invalid;
+ imc_dimm_type_t icn_dimm_type;
+ boolean_t icn_ecc;
+ boolean_t icn_lockstep;
+ boolean_t icn_closed;
+ uint32_t icn_topo;
+ uint_t icn_nchannels;
+ imc_channel_t icn_channels[IMC_MAX_CHANPERMC];
+} imc_mc_t;
+
+typedef enum imc_sad_rule_type {
+ IMC_SAD_TYPE_DRAM,
+ IMC_SAD_TYPE_MMCFG,
+ IMC_SAD_TYPE_NXM
+} imc_sad_rule_type_t;
+
+typedef enum imc_sad_rule_imode {
+ IMC_SAD_IMODE_8t6,
+ IMC_SAD_IMODE_8t6XOR,
+ IMC_SAD_IMODE_10t8,
+ IMC_SAD_IMODE_14t12,
+ IMC_SAD_IMODE_32t30
+} imc_sad_rule_imode_t;
+
+typedef enum imc_sad_rule_mod_mode {
+ IMC_SAD_MOD_MODE_NONE,
+ IMC_SAD_MOD_MODE_45t6,
+ IMC_SAD_MOD_MODE_45t8,
+ IMC_SAD_MOD_MODE_45t12
+} imc_sad_rule_mod_mode_t;
+
+typedef enum imc_sad_rule_mod_type {
+ IMC_SAD_MOD_TYPE_NONE,
+ IMC_SAD_MOD_TYPE_MOD3,
+ IMC_SAD_MOD_TYPE_MOD2_01,
+ IMC_SAD_MOD_TYPE_MOD2_12,
+ IMC_SAD_MOD_TYPE_MOD2_02
+} imc_sad_rule_mod_type_t;
+
+typedef struct imc_sad_mcroute_entry {
+ uint8_t ismce_imc; /* ID of the target IMC */
+ uint8_t ismce_pchannel; /* ID of the target physical channel */
+} imc_sad_mcroute_entry_t;
+
+typedef struct imc_sad_mcroute_table {
+ uint32_t ismc_raw_mcroute;
+ uint_t ismc_nroutes;
+ imc_sad_mcroute_entry_t ismc_mcroutes[IMC_MAX_SAD_MCROUTES];
+} imc_sad_mcroute_table_t;
+
+/*
+ * This rule represents a single SAD entry.
+ */
+typedef struct imc_sad_rule {
+ uint32_t isr_raw_dram;
+ uint32_t isr_raw_interleave;
+ boolean_t isr_enable;
+ boolean_t isr_a7mode;
+ boolean_t isr_need_mod3;
+ uint64_t isr_limit;
+ imc_sad_rule_type_t isr_type;
+ imc_sad_rule_imode_t isr_imode;
+ imc_sad_rule_mod_mode_t isr_mod_mode;
+ imc_sad_rule_mod_type_t isr_mod_type;
+ uint_t isr_ntargets;
+ uint8_t isr_targets[IMC_MAX_SAD_INTERLEAVE];
+} imc_sad_rule_t;
+
+typedef enum imc_sad_flags {
+ IMC_SAD_MCROUTE_VALID = 1 << 0,
+} imc_sad_flags_t;
+
+typedef enum imc_sad_valid {
+ IMC_SAD_V_VALID = 0,
+ IMC_SAD_V_BAD_PCI_READ = 1 << 0,
+ IMC_SAD_V_BAD_MCROUTE = 1 << 1,
+ IMC_SAD_V_BAD_DRAM_ATTR = 1 << 2,
+ IMC_SAD_V_BAD_MOD3 = 1 << 3,
+} imc_sad_valid_t;
+
+typedef struct imc_sad {
+ imc_sad_flags_t isad_flags;
+ imc_sad_valid_t isad_valid;
+ imc_stub_t *isad_dram;
+ imc_stub_t *isad_mmio;
+ imc_stub_t *isad_tolh;
+ uint64_t isad_tolm;
+ uint64_t isad_tohm;
+ uint_t isad_nrules;
+ imc_sad_rule_t isad_rules[IMC_MAX_SAD_RULES];
+ imc_sad_mcroute_table_t isad_mcroute;
+} imc_sad_t;
+
+typedef enum imc_tad_gran {
+ IMC_TAD_GRAN_64B = 0,
+ IMC_TAD_GRAN_256B,
+ IMC_TAD_GRAN_4KB,
+ IMC_TAD_GRAN_1GB
+} imc_tad_gran_t;
+
+typedef struct imc_tad_rule {
+ uint64_t itr_base;
+ uint64_t itr_limit;
+ uint32_t itr_raw;
+ uint32_t itr_raw_gran;
+ uint8_t itr_sock_way;
+ uint8_t itr_chan_way;
+ imc_tad_gran_t itr_sock_gran;
+ imc_tad_gran_t itr_chan_gran;
+ uint_t itr_ntargets;
+ uint8_t itr_targets[IMC_MAX_TAD_TARGETS];
+} imc_tad_rule_t;
+
+typedef enum imc_tad_valid {
+ IMC_TAD_V_VALID = 1 << 0,
+ IMC_TAD_V_BAD_PCI_READ = 1 << 1,
+ IMC_TAD_V_BAD_CHAN_GRAN = 1 << 2
+} imc_tad_valid_t;
+
+typedef enum imc_tad_flags {
+ IMC_TAD_FLAG_CHANSHIFT = 1 << 0,
+ IMC_TAD_FLAG_CHANHASH = 1 << 1,
+ IMC_TAD_FLAG_MIRROR = 1 << 2,
+ IMC_TAD_FLAG_LOCKSTEP = 1 << 3
+} imc_tad_flags_t;
+
+typedef struct imc_tad {
+ imc_tad_valid_t itad_valid;
+ imc_stub_t *itad_stub;
+ imc_tad_flags_t itad_flags;
+ uint_t itad_nrules;
+ imc_tad_rule_t itad_rules[IMC_MAX_TAD_RULES];
+} imc_tad_t;
+
+typedef enum imc_socket_valid {
+ IMC_SOCKET_V_VALID = 0,
+ IMC_SOCKET_V_BAD_NODEID = 1 << 0
+} imc_socket_valid_t;
+
+typedef struct imc_socket {
+ imc_socket_valid_t isock_valid;
+ uint_t isock_bus[IMC_MAX_PCIBUSES];
+ uint_t isock_nbus;
+ uint_t isock_gen;
+ nvlist_t *isock_nvl;
+ char *isock_buf;
+ size_t isock_buflen;
+ imc_sad_t isock_sad;
+ uint_t isock_ntad;
+ imc_tad_t isock_tad[IMC_MAX_TAD];
+ imc_stub_t *isock_ubox;
+ imc_stub_t *isock_cpubusno;
+ uint32_t isock_nodeid;
+ uint_t isock_nimc;
+ imc_mc_t isock_imcs[IMC_MAX_IMCPERSOCK];
+} imc_socket_t;
+
+typedef struct imc {
+ /*
+ * The initial members here are only used in the kernel. This is done to
+ * make it easier for us to be able to define a version of this to use
+ * in testing.
+ */
+#ifdef _KERNEL
+ dev_info_t *imc_dip;
+ kmutex_t imc_lock;
+ imc_flags_t imc_flags;
+ const imc_gen_data_t *imc_gen_data;
+ ddi_taskq_t *imc_taskq;
+ uint_t imc_nscanned;
+ avl_tree_t imc_stubs;
+ nvlist_t *imc_decoder_dump;
+ char *imc_decoder_buf;
+ size_t imc_decoder_len;
+#endif /* _KERNEL */
+ imc_gen_t imc_gen;
+
+ /*
+ * Data about the memory in the system
+ */
+ uint_t imc_nsockets;
+ imc_socket_t imc_sockets[IMC_MAX_SOCKETS];
+
+#ifdef _KERNEL
+ /*
+ * The imc_sockets[] array is organized based on increasing PCI Bus ID.
+ * This array maps the socket id that user land thinks of back to the
+ * actual underlying socket in case hardware does not put them in order.
+ */
+ imc_socket_t *imc_spointers[IMC_MAX_SOCKETS];
+
+ /*
+ * Store the IIO global VT-D misc. device. While there are sometimes
+ * multiple on the system, we only keep a single one around.
+ */
+ imc_stub_t *imc_gvtd_misc;
+#endif
+} imc_t;
+
+
+/*
+ * Decoder failure reasons
+ */
+typedef enum imc_decode_failure {
+ IMC_DECODE_F_NONE = 0,
+ /*
+ * Indicates that the memory address fell into a reserved legacy range.
+ * The legacy range index is stored in the failure data.
+ */
+ IMC_DECODE_F_LEGACY_RANGE,
+ /*
+ * Indicates that we had bad socket data. The socket in question is
+ * noted in the failure data.
+ */
+ IMC_DECODE_F_BAD_SOCKET,
+ /*
+ * Indicates that we had bad SAD data. The socket the SAD is associated
+ * with is noted in the failure data.
+ */
+ IMC_DECODE_F_BAD_SAD,
+ /*
+ * Indicates that the address was not contained in conventional, low,
+ * or high memory.
+ */
+ IMC_DECODE_F_OUTSIDE_DRAM,
+ /*
+ * Indicates that no valid SAD rule was found for the address.
+ */
+ IMC_DECODE_F_NO_SAD_RULE,
+ /*
+ * Indicates that the SAD interleave target was beyond the valid index.
+ */
+ IMC_DECODE_F_BAD_SAD_INTERLEAVE,
+ /*
+ * Indicates that the route suggested a remote processor we can't find.
+ */
+ IMC_DECODE_F_BAD_REMOTE_MC_ROUTE,
+ /*
+ * Indicates that we ended up in a loop trying to find the right socket
+ * to use.
+ */
+ IMC_DECODE_F_SAD_SEARCH_LOOP,
+ /*
+ * Indicates that we encountered a SAD rule that asked for inconsistent
+ * mod rules.
+ */
+ IMC_DECODE_F_SAD_BAD_MOD,
+ /*
+ * Indicates that the socket or tad rule we found doesn't actually point
+ * to something that we know about.
+ */
+ IMC_DECODE_F_SAD_BAD_SOCKET,
+ IMC_DECODE_F_SAD_BAD_TAD,
+ /*
+ * Indicates that we could not find a matching tad rule.
+ */
+ IMC_DECODE_F_NO_TAD_RULE,
+ /*
+ * Indicates that we encountered the TAD channel 3-way interleave that
+ * we don't support.
+ */
+ IMC_DECODE_F_TAD_3_ILEAVE,
+ /*
+ * Indicates that we had a bad target index.
+ */
+ IMC_DECODE_F_TAD_BAD_TARGET_INDEX,
+ /*
+ * Indicates that we have a bad channel ID.
+ */
+ IMC_DECODE_F_BAD_CHANNEL_ID,
+ /*
+ * Indicates that the TAD rule offset in the channel interleave was
+ * incorrect.
+ */
+ IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET,
+ /*
+ * We couldn't find a valid rank interleave rule.
+ */
+ IMC_DECODE_F_NO_RIR_RULE,
+ /*
+ * Indicates that the index of the rank interleaving target was bad.
+ */
+ IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET,
+ /*
+ * Indicates that the calculated DIMM represents an invalid DIMM that is
+ * beyond the number of supported DIMMS per channel on the platform.
+ */
+ IMC_DECODE_F_BAD_DIMM_INDEX,
+ /*
+ * Indicates that the specified DIMM is not preset; however, it is a
+ * valid DIMM number.
+ */
+ IMC_DECODE_F_DIMM_NOT_PRESENT,
+ /*
+ * Indicates that the specified rank on the DIMM is more than the number
+ * of ranks that the DIMM has.
+ */
+ IMC_DECODE_F_BAD_DIMM_RANK,
+ /*
+ * Indicates that the channel offset is larger than the system address,
+ * meaning that we would end up with an underflow if we continued. The
+ * equivalent is true for the rank address.
+ */
+ IMC_DECODE_F_CHANOFF_UNDERFLOW,
+ IMC_DECODE_F_RANKOFF_UNDERFLOW,
+} imc_decode_failure_t;
+
+/*
+ * Decoder state tracking
+ */
+typedef struct imc_decode_state {
+ imc_decode_failure_t ids_fail;
+ uint64_t ids_fail_data;
+ uint64_t ids_pa;
+ uint64_t ids_chanaddr;
+ uint64_t ids_rankaddr;
+ uint32_t ids_nodeid;
+ uint32_t ids_tadid;
+ uint32_t ids_channelid;
+ uint32_t ids_physrankid;
+ uint32_t ids_dimmid;
+ uint32_t ids_rankid;
+ const imc_socket_t *ids_socket;
+ const imc_sad_t *ids_sad;
+ const imc_sad_rule_t *ids_sad_rule;
+ const imc_tad_t *ids_tad;
+ const imc_tad_rule_t *ids_tad_rule;
+ const imc_mc_t *ids_mc;
+ const imc_channel_t *ids_chan;
+ const imc_rank_ileave_t *ids_rir;
+ const imc_dimm_t *ids_dimm;
+} imc_decode_state_t;
+
+#ifdef _KERNEL
+
+/*
+ * Functions needed for the stub drivers.
+ */
+extern int imc_attach_stub(dev_info_t *, ddi_attach_cmd_t);
+extern int imc_detach_stub(dev_info_t *, ddi_detach_cmd_t);
+
+/*
+ * Decoder related functions
+ */
+extern void imc_decoder_init(imc_t *);
+
+extern nvlist_t *imc_dump_decoder(imc_t *);
+#else /* !_KERNEL */
+extern boolean_t imc_restore_decoder(nvlist_t *, imc_t *);
+#endif /* _KERNEL */
+
+extern boolean_t imc_decode_pa(const imc_t *, uint64_t, imc_decode_state_t *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INTEL_IMC_H */
diff --git a/usr/src/uts/i86pc/io/imc/imcstub.c b/usr/src/uts/i86pc/io/imc/imcstub.c
new file mode 100644
index 0000000000..ee020dd5c4
--- /dev/null
+++ b/usr/src/uts/i86pc/io/imc/imcstub.c
@@ -0,0 +1,81 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * This is a stub driver that is used by the main imcstub driver to attach
+ * component PCI devices so that it can access their dev_info_t.
+ */
+
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include "imc.h"
+
+
+static int
+imcstub_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ return (imc_attach_stub(dip, cmd));
+}
+
+static int
+imcstub_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ return (imc_detach_stub(dip, cmd));
+}
+
+static struct dev_ops imcstub_dev_ops = {
+ .devo_rev = DEVO_REV,
+ .devo_refcnt = 0,
+ .devo_getinfo = nodev,
+ .devo_identify = nodev,
+ .devo_probe = nulldev,
+ .devo_attach = imcstub_attach,
+ .devo_detach = imcstub_detach,
+ .devo_reset = nodev,
+ .devo_quiesce = ddi_quiesce_not_needed
+};
+
+static struct modldrv imcstub_modldrv = {
+ .drv_modops = &mod_driverops,
+ .drv_linkinfo = "IMC Stub driver",
+ .drv_dev_ops = &imcstub_dev_ops
+};
+
+static struct modlinkage imcstub_modlinkage = {
+ .ml_rev = MODREV_1,
+ .ml_linkage = { &imcstub_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ return (mod_install(&imcstub_modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&imcstub_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&imcstub_modlinkage));
+}
diff --git a/usr/src/uts/i86pc/os/pci_cfgspace.c b/usr/src/uts/i86pc/os/pci_cfgspace.c
index 6865c26f39..c6d6c02ced 100644
--- a/usr/src/uts/i86pc/os/pci_cfgspace.c
+++ b/usr/src/uts/i86pc/os/pci_cfgspace.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -289,7 +290,14 @@ pci_check_bios(void)
pci_bios_mech = (ax & 0x3);
pci_bios_vers = regs.ebx.word.bx;
- pci_bios_maxbus = (regs.ecx.word.cx & 0xff);
+
+ /*
+ * Several BIOS implementations have known problems where they don't end
+ * up correctly telling us to scan all PCI buses in the system. In
+ * particular, many on-die CPU PCI devices are on a last bus that is
+ * sometimes not enumerated. As such, do not trust the BIOS.
+ */
+ pci_bios_maxbus = pci_max_nbus;
switch (pci_bios_mech) {
default: /* ?!? */
diff --git a/usr/src/uts/intel/sys/cpu_module.h b/usr/src/uts/intel/sys/cpu_module.h
index c227acacd2..ad5308d28a 100644
--- a/usr/src/uts/intel/sys/cpu_module.h
+++ b/usr/src/uts/intel/sys/cpu_module.h
@@ -22,7 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _SYS_CPU_MODULE_H
@@ -67,7 +67,8 @@ typedef enum cmi_errno {
CMIERR_MC_RSRCNOTPRESENT, /* Resource not present in system */
CMIERR_MC_ADDRBITS, /* Too few valid addr bits */
CMIERR_MC_INVALUNUM, /* Invalid input unum */
- CMIERR_MC_PARTIALUNUMTOPA /* unum to pa reflected physaddr */
+ CMIERR_MC_PARTIALUNUMTOPA, /* unum to pa reflected physaddr */
+ CMIERR_MC_NOTDIMMADDR /* Address not backed by DRAM */
} cmi_errno_t;
/*
diff --git a/usr/src/uts/intel/sys/mc.h b/usr/src/uts/intel/sys/mc.h
index 27ef52684d..d4815b515f 100644
--- a/usr/src/uts/intel/sys/mc.h
+++ b/usr/src/uts/intel/sys/mc.h
@@ -21,12 +21,13 @@
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
#ifndef _SYS_MC_H
#define _SYS_MC_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Public interfaces exposed by the memory controller driver
*/
@@ -63,6 +64,9 @@ typedef struct mc_unum {
#define MC_IOC_SNAPSHOT_INFO (MC_IOC | 1)
#define MC_IOC_SNAPSHOT (MC_IOC | 2)
#define MC_IOC_ONLINESPARE_EN (MC_IOC | 4)
+#define MC_IOC_DECODE_PA (MC_IOC | 5)
+#define MC_IOC_DECODE_SNAPSHOT_INFO (MC_IOC | 6)
+#define MC_IOC_DECODE_SNAPSHOT (MC_IOC | 7)
/*
* Prior to requesting a copy of the snapshot, consumers are advised to request
@@ -81,6 +85,25 @@ typedef struct mc_snapshot_info {
uint_t mcs_gen; /* snapshot generation number */
} mc_snapshot_info_t;
+/*
+ * Data used to simulate encoding or decoding of a physical / DIMM address.
+ */
+typedef struct mc_encode_ioc {
+ uint64_t mcei_pa;
+ uint64_t mcei_errdata;
+ uint32_t mcei_err;
+ uint32_t mcei_board;
+ uint32_t mcei_chip;
+ uint32_t mcei_mc;
+ uint32_t mcei_chan;
+ uint32_t mcei_dimm;
+ uint64_t mcei_rank_addr;
+ uint32_t mcei_rank;
+ uint32_t mcei_row;
+ uint32_t mcei_column;
+ uint32_t mcei_pad;
+} mc_encode_ioc_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/intel/sys/mc_intel.h b/usr/src/uts/intel/sys/mc_intel.h
index f0c83b1d8b..4c43d3d695 100644
--- a/usr/src/uts/intel/sys/mc_intel.h
+++ b/usr/src/uts/intel/sys/mc_intel.h
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _MC_INTEL_H
@@ -35,6 +36,7 @@ extern "C" {
#define MCINTEL_NVLIST_VERSTR "mcintel-nvlist-version"
#define MCINTEL_NVLIST_VERS0 0
+#define MCINTEL_NVLIST_VERS1 1
#define MCINTEL_NVLIST_VERS MCINTEL_NVLIST_VERS0
@@ -60,6 +62,62 @@ extern "C" {
#define MCINTEL_NVLIST_PARTNO "dimm-part-number"
#define MCINTEL_NVLIST_REV "dimm-part-rev"
+/*
+ * Version 1 payload. Whereas the version 0 payload uses a flat name space, we
+ * instead opt to use a hierarchical name space. This means that we can know how
+ * many devices there are at any level, as each level has this. Effectively,
+ * this means that we have an nvlist structure, for a socket that looks like:
+ *
+ * socket
+ * string version
+ * uint8_t num-memory-controllers
+ * nvlist array memory-controller[]
+ * uint8_t num-channels
+ * boolean ecc
+ * string page policy
+ * string lockstep || independent
+ * nvlist array channel[]
+ * uint8_t dpc
+ * nvlist array dimm[]
+ * boolean_t present;
+ * uint32_t ncolumns
+ * uint32_t nrows
+ * uint64_t density (in bytes)
+ * uint32_t width
+ * uint32_t ranks
+ * uint32_t banks
+ * boolean_t array ranks_disabled
+ * boolean_t hdrl-enabled
+ * boolean_t hdrl-parity
+ * uint32_t 3dnumranks
+ */
+
+#define MCINTEL_NVLIST_V1_NMC "num-memory-controllers"
+#define MCINTEL_NVLIST_V1_MCS "memory-controllers"
+#define MCINTEL_NVLIST_V1_MC_NCHAN "num-memory-channels"
+#define MCINTEL_NVLIST_V1_MC_CHANNELS "memory-controller-channels"
+#define MCINTEL_NVLIST_V1_MC_ECC "memory-controller-ecc"
+#define MCINTEL_NVLIST_V1_MC_POLICY "memory-controller-page-policy"
+#define MCINTEL_NVLIST_V1_MC_POLICY_OPEN "open-page"
+#define MCINTEL_NVLIST_V1_MC_POLICY_CLOSED "closed-page"
+#define MCINTEL_NVLIST_V1_MC_CHAN_MODE "memory-controller-channel-mode"
+#define MCINTEL_NVLIST_V1_MC_CHAN_MODE_LOCK "lockstep"
+#define MCINTEL_NVLIST_V1_MC_CHAN_MODE_INDEP "independent"
+#define MCINTEL_NVLIST_V1_CHAN_NDPC "memory-channel-dimms-per-channel"
+#define MCINTEL_NVLIST_V1_CHAN_DIMMS "memory-channel-dimms"
+#define MCINTEL_NVLIST_V1_DIMM_PRESENT "dimm-present"
+#define MCINTEL_NVLIST_V1_DIMM_SIZE "dimm-size"
+#define MCINTEL_NVLIST_V1_DIMM_NCOLS "dimm-num-columns"
+#define MCINTEL_NVLIST_V1_DIMM_NROWS "dimm-num-rows"
+#define MCINTEL_NVLIST_V1_DIMM_DENSITY "dimm-density"
+#define MCINTEL_NVLIST_V1_DIMM_WIDTH "dimm-width"
+#define MCINTEL_NVLIST_V1_DIMM_RANKS "dimm-ranks"
+#define MCINTEL_NVLIST_V1_DIMM_BANKS "dimm-banks"
+#define MCINTEL_NVLIST_V1_DIMM_RDIS "dimm-ranks-disabled"
+#define MCINTEL_NVLIST_V1_DIMM_HDRL "dimm-hdrl-enabled"
+#define MCINTEL_NVLIST_V1_DIMM_HDRLP "dimm-hdrl-parity-enabled"
+#define MCINTEL_NVLIST_V1_DIMM_3DRANK "dimm-3dranks"
+
#define FM_EREPORT_PAYLOAD_NAME_FERR_GLOBAL "ferr_global"
#define FM_EREPORT_PAYLOAD_NAME_NERR_GLOBAL "nerr_global"
#define FM_EREPORT_PAYLOAD_NAME_FSB "fsb"