diff options
author | Robert Mustacchi <rm@joyent.com> | 2019-02-04 16:55:24 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2019-07-12 21:39:06 +0000 |
commit | 2cfe9601bab4521ae793cdb6e4e3496ba4ed55f2 (patch) | |
tree | 274e0935c500ca106799233491424a8d30ec3400 | |
parent | b28b9a96d2d0a92b8948678113495203d0607781 (diff) | |
download | illumos-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>
35 files changed, 18222 insertions, 10 deletions
@@ -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", ®s, &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", ®s, &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" |