xref: /illumos-gate/usr/src/uts/intel/io/imc/imc.c (revision 71815ce76261aa773c97600750fdce92334d1990)
174079a53SRobert Mustacchi /*
274079a53SRobert Mustacchi  * This file and its contents are supplied under the terms of the
374079a53SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
474079a53SRobert Mustacchi  * You may only use this file in accordance with the terms of version
574079a53SRobert Mustacchi  * 1.0 of the CDDL.
674079a53SRobert Mustacchi  *
774079a53SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
874079a53SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
974079a53SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
1074079a53SRobert Mustacchi  */
1174079a53SRobert Mustacchi 
1274079a53SRobert Mustacchi /*
1374079a53SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
14*71815ce7SRobert Mustacchi  * Copyright 2022 Oxide Computer Company
1574079a53SRobert Mustacchi  */
1674079a53SRobert Mustacchi 
1774079a53SRobert Mustacchi /*
1874079a53SRobert Mustacchi  * Generic Intel Integrated Memory Controller (IMC) Driver
1974079a53SRobert Mustacchi  *
2074079a53SRobert Mustacchi  * This driver talks to the CPU's IMC to understand the detailed topology of the
2174079a53SRobert Mustacchi  * processor and to determine how to map between physical addresses to the
2274079a53SRobert Mustacchi  * corresponding DIMM. This driver supports the following generations of Intel
2374079a53SRobert Mustacchi  * chips:
2474079a53SRobert Mustacchi  *
2574079a53SRobert Mustacchi  *  - Sandy Bridge
2674079a53SRobert Mustacchi  *  - Ivy Bridge
2774079a53SRobert Mustacchi  *  - Haswell
2874079a53SRobert Mustacchi  *  - Broadwell
2974079a53SRobert Mustacchi  *  - Skylake / Cascade Lake
3074079a53SRobert Mustacchi  *
3174079a53SRobert Mustacchi  * Memory Decoding
3274079a53SRobert Mustacchi  * ---------------
3374079a53SRobert Mustacchi  *
3474079a53SRobert Mustacchi  * For more detailed summaries of the memory decoding process, please refer to
3574079a53SRobert Mustacchi  * the Intel External Design Specifications for the corresponding processor.
3674079a53SRobert Mustacchi  * What follows is a rough overview of how the memory decoding system works.
3774079a53SRobert Mustacchi  *
3874079a53SRobert Mustacchi  * First, we'd like to define the following concepts:
3974079a53SRobert Mustacchi  *
4074079a53SRobert Mustacchi  * SYSTEM ADDRESS
4174079a53SRobert Mustacchi  *
4274079a53SRobert Mustacchi  *	This is a physical address that the operating system normally uses. This
4374079a53SRobert Mustacchi  *	address may refer to DRAM, it may refer to memory mapped PCI
4474079a53SRobert Mustacchi  *	configuration space or device registers, or it may refer to other parts
4574079a53SRobert Mustacchi  *	of the system's memory map, such as the extended advanced programmable
4674079a53SRobert Mustacchi  *	interrupt controller (xAPIC), etc.
4774079a53SRobert Mustacchi  *
4874079a53SRobert Mustacchi  * DIMM
4974079a53SRobert Mustacchi  *
5074079a53SRobert Mustacchi  *	Dual-inline memory module. This refers to a physical stick of volatile
5174079a53SRobert Mustacchi  *	memory that is inserted into a slot on the motherboard.
5274079a53SRobert Mustacchi  *
5374079a53SRobert Mustacchi  * RANK
5474079a53SRobert Mustacchi  *
5574079a53SRobert Mustacchi  *	A potential sub-division of a DIMM. A DIMM's memory capacity is divided
5674079a53SRobert Mustacchi  *	into a number of equal sized ranks. For example, an 8 GiB DIMM, may have
5774079a53SRobert Mustacchi  *	1 8 GiB rank, 2 4 GiB ranks, or 4 2 GiB ranks.
5874079a53SRobert Mustacchi  *
5974079a53SRobert Mustacchi  * RANK ADDRESS
6074079a53SRobert Mustacchi  *
6174079a53SRobert Mustacchi  *	An address that exists in the context of a given rank on a DIMM. All
6274079a53SRobert Mustacchi  *	ranks have overlapping addresses, so the address 0x400 exists on all
6374079a53SRobert Mustacchi  *	ranks on a given DIMM.
6474079a53SRobert Mustacchi  *
6574079a53SRobert Mustacchi  * CHANNEL
6674079a53SRobert Mustacchi  *
6774079a53SRobert Mustacchi  *	Multiple DIMMs may be combined into a single channel. The channel
6874079a53SRobert Mustacchi  *	represents the combined memory of all the DIMMs. A given channel only
6974079a53SRobert Mustacchi  *	ever exists on a socket and is bound to a single memory controller.
7074079a53SRobert Mustacchi  *
7174079a53SRobert Mustacchi  * CHANNEL ADDRESS
7274079a53SRobert Mustacchi  *
7374079a53SRobert Mustacchi  *	This is an address that exists logically on a channel. Each address on a
7474079a53SRobert Mustacchi  *	channel maps to a corresponding DIMM that exists on that channel. The
7574079a53SRobert Mustacchi  *	address space on one channel is independent from that on another. This
7674079a53SRobert Mustacchi  *	means that address 0x1000 can exist on each memory channel in the
7774079a53SRobert Mustacchi  *	system.
7874079a53SRobert Mustacchi  *
7974079a53SRobert Mustacchi  * INTERLEAVE
8074079a53SRobert Mustacchi  *
8174079a53SRobert Mustacchi  *	There are several different cases where interleaving occurs on the
8274079a53SRobert Mustacchi  *	system. For example, addresses may be interleaved across sockets,
8374079a53SRobert Mustacchi  *	memory channels, or DIMM ranks. When addresses are interleaved, then
8474079a53SRobert Mustacchi  *	some number of bits in an address are used to select which target to go
8574079a53SRobert Mustacchi  *	to (usually through a look up table). The effect of interleaving is that
8674079a53SRobert Mustacchi  *	addresses that are next to one another may not all go to the same
8774079a53SRobert Mustacchi  *	device. The following image shows a non-interleaving case.
8874079a53SRobert Mustacchi  *
8974079a53SRobert Mustacchi  *	0x0fff +-----+             +-----+ 0x7ff
9074079a53SRobert Mustacchi  *	       |     |\___________/|     |
9174079a53SRobert Mustacchi  *	       |     |  __________ | (b) |
9274079a53SRobert Mustacchi  *	       |     | /          \|     |
9374079a53SRobert Mustacchi  *	0x0800 |=====|=            +-----+ 0x000       +-----+ 0x7ff
9474079a53SRobert Mustacchi  *	       |     | \______________________________/|     |
9574079a53SRobert Mustacchi  *	       |     | _______________________________ | (a) |
9674079a53SRobert Mustacchi  *	       |     |/                               \|     |
9774079a53SRobert Mustacchi  *	0x0000 +-----+                                 +-----+ 0x000
9874079a53SRobert Mustacchi  *
9974079a53SRobert Mustacchi  *	In this example of non-interleaving, addresses 0x0000 to 0x07ff go to
10074079a53SRobert Mustacchi  *	device (a). While, addresses 0x08000 to 0xfff, go to device (b).
10174079a53SRobert Mustacchi  *	However, each range is divided into the same number of components.
10274079a53SRobert Mustacchi  *
10374079a53SRobert Mustacchi  *	If instead, we were to look at that with interleaving, what we might say
10474079a53SRobert Mustacchi  *	is that rather than splitting the range in half, we might say that if
10574079a53SRobert Mustacchi  *	the address has bit 8 set (0x100), then it goes to (b), otherwise it
10674079a53SRobert Mustacchi  *	goes to (a). This means that addresses 0x000 to 0x0ff, would go to (a).
10774079a53SRobert Mustacchi  *	0x100 to 0x1ff would go to (b). 0x200 to 0x2ff would go back to (a)
10874079a53SRobert Mustacchi  *	again, and then 0x300 to 0x2ff would go back to (b). This would continue
10974079a53SRobert Mustacchi  *	for a while. This would instead look something more like:
11074079a53SRobert Mustacchi  *
11174079a53SRobert Mustacchi  *
11274079a53SRobert Mustacchi  *      0x0fff +-----+       A: 0x7ff +---------+   B: 0x7ff +---------+
11374079a53SRobert Mustacchi  *             | (b) |                | e00-eff |            | f00-fff |
11474079a53SRobert Mustacchi  *      0x0f00 |-----|          0x700 +---------+      0x700 +---------+
11574079a53SRobert Mustacchi  *             | (a) |                | c00-cff |            | d00-dff |
11674079a53SRobert Mustacchi  *      0x0e00 ~~~~~~~          0x600 +---------+      0x600 +---------+
11774079a53SRobert Mustacchi  *               ***                  | a00-aff |            | b00-bff |
11874079a53SRobert Mustacchi  *      0x0400 ~~~~~~~          0x500 +---------+      0x500 +---------+
11974079a53SRobert Mustacchi  *             | (b) |                | 800-8ff |            | 900-9ff |
12074079a53SRobert Mustacchi  *      0x0300 |-----|          0x400 +---------+      0x400 +---------+
12174079a53SRobert Mustacchi  *             | (a) |                | 600-6ff |            | 700-7ff |
12274079a53SRobert Mustacchi  *      0x0200 |-----|          0x300 +---------+      0x300 +---------+
12374079a53SRobert Mustacchi  *             | (b) |                | 400-4ff |            | 500-5ff |
12474079a53SRobert Mustacchi  *      0x0100 |-----|          0x200 +---------+      0x200 +---------+
12574079a53SRobert Mustacchi  *             | (a) |                | 200-2ff |            | 300-3ff |
12674079a53SRobert Mustacchi  *      0x0000 +-----+          0x100 +---------+      0x100 +---------+
12774079a53SRobert Mustacchi  *                                    | 000-0ff |            | 100-1ff |
12874079a53SRobert Mustacchi  *                              0x000 +---------+      0x000 +---------+
12974079a53SRobert Mustacchi  *
13074079a53SRobert Mustacchi  *	In this example we've performed two-way interleaving. The number of ways
13174079a53SRobert Mustacchi  *	that something can interleave varies based on what we're interleaving
13274079a53SRobert Mustacchi  *	between.
13374079a53SRobert Mustacchi  *
13474079a53SRobert Mustacchi  * MEMORY CONTROLLER
13574079a53SRobert Mustacchi  *
13674079a53SRobert Mustacchi  *	A given processor die (see uts/i86pc/os/cpuid.c) contains a number of
13774079a53SRobert Mustacchi  *	memory controllers. Usually 1 or two. Each memory controller supports a
13874079a53SRobert Mustacchi  *	given number of DIMMs, which are divided across multiple channels.
13974079a53SRobert Mustacchi  *
14074079a53SRobert Mustacchi  * TARGET ADDRESS DECODER
14174079a53SRobert Mustacchi  *
14274079a53SRobert Mustacchi  *	The target address decoder (TAD) is responsible for taking a system
14374079a53SRobert Mustacchi  *	address and transforming it into a channel address based on the rules
14474079a53SRobert Mustacchi  *	that are present. Each memory controller has a corresponding TAD. The
14574079a53SRobert Mustacchi  *	TAD is often contained in a device called a 'Home Agent'.
14674079a53SRobert Mustacchi  *
14774079a53SRobert Mustacchi  * SYSTEM ADDRESS DECODER
14874079a53SRobert Mustacchi  *
14974079a53SRobert Mustacchi  *	The system address decoder (SAD) is responsible for taking a system
15074079a53SRobert Mustacchi  *	address and directing it to the right place, whether this be memory or
15174079a53SRobert Mustacchi  *	otherwise. There is a single memory controller per socket (see
15274079a53SRobert Mustacchi  *	uts/i86pc/os/cpuid.c) that is shared between all the cores currently.
15374079a53SRobert Mustacchi  *
15474079a53SRobert Mustacchi  * NODE IDENTIFIER
15574079a53SRobert Mustacchi  *
15674079a53SRobert Mustacchi  *	The node identifier is used to uniquely identify an element in the
15774079a53SRobert Mustacchi  *	various routing topologies on the die (see uts/i86pc/os/cpuid.c for the
15874079a53SRobert Mustacchi  *	definition of 'die'). One can roughly think about this as a unique
15974079a53SRobert Mustacchi  *	identifier for the socket itself. In general, the primary node ID for a
16074079a53SRobert Mustacchi  *	socket should map to the socket APIC ID.
16174079a53SRobert Mustacchi  *
16274079a53SRobert Mustacchi  * Finding Devices
16374079a53SRobert Mustacchi  * ---------------
16474079a53SRobert Mustacchi  *
16574079a53SRobert Mustacchi  * There is a bit of a chicken and egg problem on Intel systems and in the
16674079a53SRobert Mustacchi  * device driver interface. The information that we need in the system is spread
16774079a53SRobert Mustacchi  * out amongst a large number of different PCI devices that the processor
16874079a53SRobert Mustacchi  * exposes. The number of such devices can vary based on the processor
16974079a53SRobert Mustacchi  * generation and the specific SKU in the processor. To deal with this, we break
17074079a53SRobert Mustacchi  * the driver into two different components: a stub driver and the full driver.
17174079a53SRobert Mustacchi  *
17274079a53SRobert Mustacchi  * The stub driver has aliases for all known PCI devices that we might attach to
17374079a53SRobert Mustacchi  * in a given generation on the system. This driver is called 'imcstub'. When a
17474079a53SRobert Mustacchi  * stub attaches, it just registers itself with the main driver, upon which it
17574079a53SRobert Mustacchi  * has a module dependency.
17674079a53SRobert Mustacchi  *
17774079a53SRobert Mustacchi  * The main driver, 'imc', is a pseudo-device driver. When it first attaches, it
17874079a53SRobert Mustacchi  * kicks off a scan of the device tree which takes place in a task queue. Once
17974079a53SRobert Mustacchi  * there, it determines the number of devices that it expects to exist by
18074079a53SRobert Mustacchi  * walking the tree and comparing it against the generation-specific table.
18174079a53SRobert Mustacchi  *
18274079a53SRobert Mustacchi  * If all devices are found, we'll go ahead and read through all the devices and
18374079a53SRobert Mustacchi  * build a map of all the information we need to understand the topology of the
18474079a53SRobert Mustacchi  * system and to be able to decode addresses. We do this here, because we can be
18574079a53SRobert Mustacchi  * asked to perform decoding in dangerous contexts (after taking an MCE, panic,
18674079a53SRobert Mustacchi  * etc) where we don't want to have to rely on the broader kernel functioning at
18774079a53SRobert Mustacchi  * this point in time.
18874079a53SRobert Mustacchi  *
18974079a53SRobert Mustacchi  * Once our topology is built, we'll create minor nodes which are used by the
19074079a53SRobert Mustacchi  * fault management architecture to query for information and register our
19174079a53SRobert Mustacchi  * decoding functionality with the kernel.
19274079a53SRobert Mustacchi  *
19374079a53SRobert Mustacchi  * PCI Numbering
19474079a53SRobert Mustacchi  * -------------
19574079a53SRobert Mustacchi  *
19674079a53SRobert Mustacchi  * For each device that we care about, Intel defines the device and function
19774079a53SRobert Mustacchi  * that we can expect to find the information and PCI configuration space
19874079a53SRobert Mustacchi  * registers that we care about at. However, the PCI bus is not well defined.
19974079a53SRobert Mustacchi  * Devices that are on the same socket use the same set of bus numbers; however,
20074079a53SRobert Mustacchi  * some sockets have multiple device numbers that they'll use to represent
20174079a53SRobert Mustacchi  * different classes. These bus numbers are programmed by systems firmware as
20274079a53SRobert Mustacchi  * part of powering on the system. This means, that we need the ability to
20374079a53SRobert Mustacchi  * map together these disparate ranges ourselves.
20474079a53SRobert Mustacchi  *
20574079a53SRobert Mustacchi  * There is a device called a utility box (UBOX), which exists per-socket and
20674079a53SRobert Mustacchi  * maps the different sockets together. We use this to determine which devices
20774079a53SRobert Mustacchi  * correspond to which sockets.
20874079a53SRobert Mustacchi  *
20974079a53SRobert Mustacchi  * Mapping Sockets
21074079a53SRobert Mustacchi  * ---------------
21174079a53SRobert Mustacchi  *
21274079a53SRobert Mustacchi  * Another wrinkle is that the way that the OS sees the numbering of the CPUs is
21374079a53SRobert Mustacchi  * generally based on the APIC ID (see uts/i86pc/os/cpuid.c for more
21474079a53SRobert Mustacchi  * information). However, to map to the corresponding socket, we need to look at
21574079a53SRobert Mustacchi  * the socket's node ID. The order of PCI buses in the system is not required to
21674079a53SRobert Mustacchi  * have any relation to the socket ID. Therefore, we have to have yet another
21774079a53SRobert Mustacchi  * indirection table in the imc_t.
21874079a53SRobert Mustacchi  *
21974079a53SRobert Mustacchi  * Exposing Data
22074079a53SRobert Mustacchi  * -------------
22174079a53SRobert Mustacchi  *
22274079a53SRobert Mustacchi  * We expose topology data to FMA using the OS-private memory controller
22374079a53SRobert Mustacchi  * interfaces. By creating minor nodes of the type, 'ddi_mem_ctrl', there are a
22474079a53SRobert Mustacchi  * number of specific interfaces that we can then implement. The ioctl API asks
22574079a53SRobert Mustacchi  * us for a snapshot of data, which basically has us go through and send an
22674079a53SRobert Mustacchi  * nvlist_t to userland. This nvlist_t is constructed as part of the scan
22774079a53SRobert Mustacchi  * process. This nvlist uses the version 1 format, which more explicitly encodes
22874079a53SRobert Mustacchi  * the topology in a series of nested nvlists.
22974079a53SRobert Mustacchi  *
23074079a53SRobert Mustacchi  * In addition, the tool /usr/lib/fm/fmd/mcdecode can be used to query the
23174079a53SRobert Mustacchi  * decoder and ask it to perform decoding.
23274079a53SRobert Mustacchi  *
23374079a53SRobert Mustacchi  * Decoding Addresses
23474079a53SRobert Mustacchi  * ------------------
23574079a53SRobert Mustacchi  *
23674079a53SRobert Mustacchi  * The decoding logic can be found in common/imc/imc_decode.c. This file is
23774079a53SRobert Mustacchi  * shared between the kernel and userland to allow for easier testing and
23874079a53SRobert Mustacchi  * additional flexibility in operation. The decoding process happens in a few
23974079a53SRobert Mustacchi  * different phases.
24074079a53SRobert Mustacchi  *
24174079a53SRobert Mustacchi  * The first phase, is to determine which memory controller on which socket is
24274079a53SRobert Mustacchi  * responsible for this data. To determine this, we use the system address
24374079a53SRobert Mustacchi  * decoder and walk the rules, looking for the correct target. There are various
24474079a53SRobert Mustacchi  * manipulations to the address that exist which are used to determine which
24574079a53SRobert Mustacchi  * index we use. The way that we interpret the output of the rule varies
24674079a53SRobert Mustacchi  * somewhat based on the generation. Sandy Bridge just has a node ID which
24774079a53SRobert Mustacchi  * points us to the socket with its single IMC. On Ivy Bridge through Broadwell,
24874079a53SRobert Mustacchi  * the memory controller to use is also encoded in part of the node ID. Finally,
24974079a53SRobert Mustacchi  * on Skylake, the SAD tells us which socket to look at. The socket in question
25074079a53SRobert Mustacchi  * then has a routing table which tells us which channel on which memory
25174079a53SRobert Mustacchi  * controller that is local to that socket.
25274079a53SRobert Mustacchi  *
25374079a53SRobert Mustacchi  * Once we have the target memory controller, we walk the list of target address
25474079a53SRobert Mustacchi  * decoder rules. These rules can help tell us which channel we care about
25574079a53SRobert Mustacchi  * (which is required on Sandy Bridge through Broadwell) and then describe some
25674079a53SRobert Mustacchi  * amount of the interleaving rules which are used to turn the system address
25774079a53SRobert Mustacchi  * into a channel address.
25874079a53SRobert Mustacchi  *
25974079a53SRobert Mustacchi  * Once we know the channel and the channel address, we walk the rank interleave
26074079a53SRobert Mustacchi  * rules which help us determine which DIMM and the corresponding rank on it
26174079a53SRobert Mustacchi  * that the corresponding channel address is on. It also has logic that we need
26274079a53SRobert Mustacchi  * to use to determine how to transform a channel address into an address on
26374079a53SRobert Mustacchi  * that specific rank. Once we have that, then the initial decoding is done.
26474079a53SRobert Mustacchi  *
26574079a53SRobert Mustacchi  * The logic in imc_decode.c is abstracted away from the broader kernel CMI
26674079a53SRobert Mustacchi  * logic.  This is on purpose and allows us not only an easier time unit testing
26774079a53SRobert Mustacchi  * the logic, but also allows us to express more high fidelity errors that are
26874079a53SRobert Mustacchi  * translated into a much smaller subset. This logic is exercised in the
26974079a53SRobert Mustacchi  * 'imc_test' program which is built in 'test/os-tests/tests/imc'.
27074079a53SRobert Mustacchi  *
27174079a53SRobert Mustacchi  * Limitations
27274079a53SRobert Mustacchi  * -----------
27374079a53SRobert Mustacchi  *
27474079a53SRobert Mustacchi  * Currently, this driver has the following limitations:
27574079a53SRobert Mustacchi  *
27674079a53SRobert Mustacchi  *  o It doesn't decode the row and column addresses.
27774079a53SRobert Mustacchi  *  o It doesn't encode from a DIMM address to a system address.
27874079a53SRobert Mustacchi  *  o It doesn't properly support lockstep and mirroring modes on Sandy Bridge -
27974079a53SRobert Mustacchi  *    Broadwell platforms.
28074079a53SRobert Mustacchi  *  o It doesn't support virtual lockstep and adaptive mirroring on Purley
28174079a53SRobert Mustacchi  *    platforms.
28274079a53SRobert Mustacchi  *  o It doesn't properly handle Intel Optane (3D-X Point) NVDIMMs.
28374079a53SRobert Mustacchi  *  o It doesn't know how to decode three way channel interleaving.
28474079a53SRobert Mustacchi  *
28574079a53SRobert Mustacchi  * None of these are intrinsic problems to the driver, it's mostly a matter of
28674079a53SRobert Mustacchi  * having proper documentation and testing.
28774079a53SRobert Mustacchi  */
28874079a53SRobert Mustacchi 
28974079a53SRobert Mustacchi #include <sys/modctl.h>
29074079a53SRobert Mustacchi #include <sys/conf.h>
29174079a53SRobert Mustacchi #include <sys/devops.h>
29274079a53SRobert Mustacchi #include <sys/ddi.h>
29374079a53SRobert Mustacchi #include <sys/sunddi.h>
29474079a53SRobert Mustacchi #include <sys/types.h>
29574079a53SRobert Mustacchi #include <sys/file.h>
29674079a53SRobert Mustacchi #include <sys/errno.h>
29774079a53SRobert Mustacchi #include <sys/open.h>
29874079a53SRobert Mustacchi #include <sys/cred.h>
29974079a53SRobert Mustacchi #include <sys/pci.h>
30074079a53SRobert Mustacchi #include <sys/sysmacros.h>
30174079a53SRobert Mustacchi #include <sys/avl.h>
30274079a53SRobert Mustacchi #include <sys/stat.h>
30374079a53SRobert Mustacchi #include <sys/policy.h>
30474079a53SRobert Mustacchi 
30574079a53SRobert Mustacchi #include <sys/cpu_module.h>
30674079a53SRobert Mustacchi #include <sys/mc.h>
30774079a53SRobert Mustacchi #include <sys/mc_intel.h>
30874079a53SRobert Mustacchi 
30974079a53SRobert Mustacchi #include "imc.h"
31074079a53SRobert Mustacchi 
31174079a53SRobert Mustacchi /*
31274079a53SRobert Mustacchi  * These tables contain generational data that varies between processor
31374079a53SRobert Mustacchi  * generation such as the maximum number of sockets, memory controllers, and the
31474079a53SRobert Mustacchi  * offsets of the various registers.
31574079a53SRobert Mustacchi  */
31674079a53SRobert Mustacchi 
31774079a53SRobert Mustacchi static const imc_gen_data_t imc_gen_data_snb = {
31874079a53SRobert Mustacchi 	.igd_max_sockets = 4,
31974079a53SRobert Mustacchi 	.igd_max_imcs = 2,
32074079a53SRobert Mustacchi 	.igd_max_channels = 4,
32174079a53SRobert Mustacchi 	.igd_max_dimms = 3,
32274079a53SRobert Mustacchi 	.igd_max_ranks = IMC_MTR_DDR_RANKS_MAX,
32374079a53SRobert Mustacchi 	.igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1,
32474079a53SRobert Mustacchi 	    IMC_REG_MC_MTR2 },
32574079a53SRobert Mustacchi 	.igd_mcmtr_offset = 0x7c,
32674079a53SRobert Mustacchi 	.igd_tolm_offset = 0x80,
32774079a53SRobert Mustacchi 	.igd_tohm_low_offset = 0x84,
32874079a53SRobert Mustacchi 	.igd_sad_dram_offset = 0x80,
32974079a53SRobert Mustacchi 	.igd_sad_ndram_rules = 10,
33074079a53SRobert Mustacchi 	.igd_sad_nodeid_offset = 0x40,
33174079a53SRobert Mustacchi 	.igd_tad_nrules = 12,
33274079a53SRobert Mustacchi 	.igd_tad_rule_offset = 0x40,
33374079a53SRobert Mustacchi 	.igd_tad_chan_offset = 0x90,
33474079a53SRobert Mustacchi 	.igd_tad_sysdef = 0x80,
33574079a53SRobert Mustacchi 	.igd_tad_sysdef2 = 0x84,
33674079a53SRobert Mustacchi 	.igd_mc_mirror = 0xac,
33774079a53SRobert Mustacchi 	.igd_rir_nways = 5,
33874079a53SRobert Mustacchi 	.igd_rir_way_offset = 0x108,
33974079a53SRobert Mustacchi 	.igd_rir_nileaves = 8,
34074079a53SRobert Mustacchi 	.igd_rir_ileave_offset = 0x120,
34174079a53SRobert Mustacchi 	.igd_ubox_cpubusno_offset = 0xd0,
34274079a53SRobert Mustacchi };
34374079a53SRobert Mustacchi 
34474079a53SRobert Mustacchi static const imc_gen_data_t imc_gen_data_ivb = {
34574079a53SRobert Mustacchi 	.igd_max_sockets = 4,
34674079a53SRobert Mustacchi 	.igd_max_imcs = 2,
34774079a53SRobert Mustacchi 	.igd_max_channels = 4,
34874079a53SRobert Mustacchi 	.igd_max_dimms = 3,
34974079a53SRobert Mustacchi 	.igd_max_ranks = IMC_MTR_DDR_RANKS_MAX,
35074079a53SRobert Mustacchi 	.igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1,
35174079a53SRobert Mustacchi 	    IMC_REG_MC_MTR2 },
35274079a53SRobert Mustacchi 	.igd_mcmtr_offset = 0x7c,
35374079a53SRobert Mustacchi 	.igd_tolm_offset = 0x80,
35474079a53SRobert Mustacchi 	.igd_tohm_low_offset = 0x84,
35574079a53SRobert Mustacchi 	.igd_sad_dram_offset = 0x60,
35674079a53SRobert Mustacchi 	.igd_sad_ndram_rules = 20,
35774079a53SRobert Mustacchi 	.igd_sad_nodeid_offset = 0x40,
35874079a53SRobert Mustacchi 	.igd_tad_nrules = 12,
35974079a53SRobert Mustacchi 	.igd_tad_rule_offset = 0x40,
36074079a53SRobert Mustacchi 	.igd_tad_chan_offset = 0x90,
36174079a53SRobert Mustacchi 	.igd_tad_sysdef = 0x80,
36274079a53SRobert Mustacchi 	.igd_tad_sysdef2 = 0x84,
36374079a53SRobert Mustacchi 	.igd_mc_mirror = 0xac,
36474079a53SRobert Mustacchi 	.igd_rir_nways = 5,
36574079a53SRobert Mustacchi 	.igd_rir_way_offset = 0x108,
36674079a53SRobert Mustacchi 	.igd_rir_nileaves = 8,
36774079a53SRobert Mustacchi 	.igd_rir_ileave_offset = 0x120,
36874079a53SRobert Mustacchi 	.igd_ubox_cpubusno_offset = 0xd0,
36974079a53SRobert Mustacchi };
37074079a53SRobert Mustacchi 
37174079a53SRobert Mustacchi static const imc_gen_data_t imc_gen_data_has_brd = {
37274079a53SRobert Mustacchi 	.igd_max_sockets = 4,
37374079a53SRobert Mustacchi 	.igd_max_imcs = 2,
37474079a53SRobert Mustacchi 	.igd_max_channels = 4,
37574079a53SRobert Mustacchi 	.igd_max_dimms = 3,
37674079a53SRobert Mustacchi 	.igd_max_ranks = IMC_MTR_DDR_RANKS_MAX_HAS_SKX,
37774079a53SRobert Mustacchi 	.igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1,
37874079a53SRobert Mustacchi 	    IMC_REG_MC_MTR2 },
37974079a53SRobert Mustacchi 	.igd_mcmtr_offset = 0x7c,
38074079a53SRobert Mustacchi 	.igd_tolm_offset = 0xd0,
38174079a53SRobert Mustacchi 	.igd_tohm_low_offset = 0xd4,
38274079a53SRobert Mustacchi 	.igd_tohm_hi_offset = 0xd8,
38374079a53SRobert Mustacchi 	.igd_sad_dram_offset = 0x60,
38474079a53SRobert Mustacchi 	.igd_sad_ndram_rules = 20,
38574079a53SRobert Mustacchi 	.igd_sad_nodeid_offset = 0x40,
38674079a53SRobert Mustacchi 	.igd_tad_nrules = 12,
38774079a53SRobert Mustacchi 	.igd_tad_rule_offset = 0x40,
38874079a53SRobert Mustacchi 	.igd_tad_chan_offset = 0x90,
38974079a53SRobert Mustacchi 	.igd_tad_sysdef = 0x80,
39074079a53SRobert Mustacchi 	.igd_tad_sysdef2 = 0x84,
39174079a53SRobert Mustacchi 	.igd_mc_mirror = 0xac,
39274079a53SRobert Mustacchi 	.igd_rir_nways = 5,
39374079a53SRobert Mustacchi 	.igd_rir_way_offset = 0x108,
39474079a53SRobert Mustacchi 	.igd_rir_nileaves = 8,
39574079a53SRobert Mustacchi 	.igd_rir_ileave_offset = 0x120,
39674079a53SRobert Mustacchi 	.igd_ubox_cpubusno_offset = 0xd0,
39774079a53SRobert Mustacchi };
39874079a53SRobert Mustacchi 
39974079a53SRobert Mustacchi static const imc_gen_data_t imc_gen_data_skx = {
40074079a53SRobert Mustacchi 	.igd_max_sockets = 8,
40174079a53SRobert Mustacchi 	.igd_max_imcs = 2,
40274079a53SRobert Mustacchi 	.igd_max_channels = 3,
40374079a53SRobert Mustacchi 	.igd_max_dimms = 2,
40474079a53SRobert Mustacchi 	.igd_max_ranks = IMC_MTR_DDR_RANKS_MAX,
40574079a53SRobert Mustacchi 	.igd_mtr_offsets = { IMC_REG_MC_MTR0, IMC_REG_MC_MTR1 },
40674079a53SRobert Mustacchi 	.igd_mcmtr_offset = 0x87c,
40774079a53SRobert Mustacchi 	.igd_topo_offset = 0x88,
40874079a53SRobert Mustacchi 	.igd_tolm_offset = 0xd0,
40974079a53SRobert Mustacchi 	.igd_tohm_low_offset = 0xd4,
41074079a53SRobert Mustacchi 	.igd_tohm_hi_offset = 0xd8,
41174079a53SRobert Mustacchi 	.igd_sad_dram_offset = 0x60,
41274079a53SRobert Mustacchi 	.igd_sad_ndram_rules = 24,
41374079a53SRobert Mustacchi 	.igd_sad_nodeid_offset = 0xc0,
41474079a53SRobert Mustacchi 	.igd_tad_nrules = 8,
41574079a53SRobert Mustacchi 	.igd_tad_rule_offset = 0x850,
41674079a53SRobert Mustacchi 	.igd_tad_chan_offset = 0x90,
41774079a53SRobert Mustacchi 	.igd_rir_nways = 4,
41874079a53SRobert Mustacchi 	.igd_rir_way_offset = 0x108,
41974079a53SRobert Mustacchi 	.igd_rir_nileaves = 4,
42074079a53SRobert Mustacchi 	.igd_rir_ileave_offset = 0x120,
42174079a53SRobert Mustacchi 	.igd_ubox_cpubusno_offset = 0xcc,
42274079a53SRobert Mustacchi };
42374079a53SRobert Mustacchi 
42474079a53SRobert Mustacchi /*
42574079a53SRobert Mustacchi  * This table contains all of the devices that we're looking for from a stub
42674079a53SRobert Mustacchi  * perspective. These are organized by generation. Different generations behave
42774079a53SRobert Mustacchi  * in slightly different ways. For example, Sandy Bridge through Broadwell use
42874079a53SRobert Mustacchi  * unique PCI IDs for each PCI device/function combination that appears. Whereas
42974079a53SRobert Mustacchi  * Skylake based systems use the same PCI ID; however, different device/function
43074079a53SRobert Mustacchi  * values indicate that the IDs are used for different purposes.
43174079a53SRobert Mustacchi  */
43274079a53SRobert Mustacchi /* BEGIN CSTYLED */
43374079a53SRobert Mustacchi static const imc_stub_table_t imc_stub_table[] = {
43474079a53SRobert Mustacchi 	/* Sandy Bridge */
43574079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_MC0_MAIN0, 0x3ca8, 15, 0, "IMC 0 Main 0" },
43674079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_MC0_MAIN1, 0x3c71, 15, 1, "IMC 0 Main 0" },
43774079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL0, 0x3caa, 15, 2, "IMC 0 Channel 0 Info" },
43874079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL1, 0x3cab, 15, 3, "IMC 0 Channel 1 Info" },
43974079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL2, 0x3cac, 15, 4, "IMC 0 Channel 2 Info" },
44074079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_MC0_CHANNEL3, 0x3cad, 15, 5, "IMC 0 Channel 3 Info" },
44174079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_SAD_DRAM, 0x3cf4, 12, 6, "SAD DRAM Rules" },
44274079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_SAD_MMIO, 0x3cf5, 13, 6, "SAD MMIO Rules" },
44374079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_SAD_MISC, 0x3cf6, 12, 7, "SAD Memory Map" },
44474079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_UBOX, 0x3ce0, 11, 0, "UBox" },
44574079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_UBOX_CPUBUSNO, 0x3ce3, 11, 3, "UBox Scratch" },
44674079a53SRobert Mustacchi 	{ IMC_GEN_SANDY, IMC_TYPE_HA0, 0x3ca0, 14, 0, "Home Agent" },
44774079a53SRobert Mustacchi 	/* Ivy Bridge */
44874079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC0_MAIN0, 0x0ea8, 15, 0, "IMC 0 Main 0" },
44974079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC0_MAIN1, 0x0e71, 15, 1, "IMC 0 Main 1" },
45074079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL0, 0x0eaa, 15, 2, "IMC 0 Channel 0 Info" },
45174079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL1, 0x0eab, 15, 3, "IMC 0 Channel 1 Info" },
45274079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL2, 0x0eac, 15, 4, "IMC 0 Channel 2 Info" },
45374079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC0_CHANNEL3, 0x0ead, 15, 5, "IMC 0 Channel 3 Info" },
45474079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC1_MAIN0, 0x0e68, 29, 0, "IMC 1 Main 0" },
45574079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC1_MAIN1, 0x0e79, 29, 1, "IMC 1 Main 1" },
45674079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL0, 0x0e6a, 15, 2, "IMC 1 Channel 0 Info" },
45774079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL1, 0x0e6b, 15, 3, "IMC 1 Channel 1 Info" },
45874079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL2, 0x0e6c, 15, 4, "IMC 1 Channel 2 Info" },
45974079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_MC1_CHANNEL3, 0x0e6d, 15, 5, "IMC 1 Channel 3 Info" },
46074079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_SAD_DRAM, 0x0ec8, 22, 0, "SAD DRAM Rules" },
46174079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_SAD_MMIO, 0x0ec9, 22, 1, "SAD MMIO Rules" },
46274079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_SAD_MISC, 0x0eca, 22, 2, "SAD Memory Map" },
46374079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_UBOX, 0x0e1e, 11, 0, "UBox" },
46474079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_UBOX_CPUBUSNO, 0x0e1f, 11, 3, "UBox Scratch" },
46574079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_HA0, 0x0ea0, 14, 0, "Home Agent 0" },
46674079a53SRobert Mustacchi 	{ IMC_GEN_IVY, IMC_TYPE_HA1, 0x0e60, 28, 0, "Home Agent 1" },
46774079a53SRobert Mustacchi 	/* Haswell */
46874079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC0_MAIN0, 0x2fa8, 19, 0, "IMC 0 Main 0" },
46974079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC0_MAIN1, 0x2f71, 19, 1, "IMC 0 Main 1" },
47074079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL0, 0x2faa, 19, 2, "IMC 0 Channel 0 Info" },
47174079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL1, 0x2fab, 19, 3, "IMC 0 Channel 1 Info" },
47274079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL2, 0x2fac, 19, 4, "IMC 0 Channel 2 Info" },
47374079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC0_CHANNEL3, 0x2fad, 19, 5, "IMC 0 Channel 3 Info" },
47474079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC1_MAIN0, 0x2f68, 22, 0, "IMC 1 Main 0" },
47574079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC1_MAIN1, 0x2f79, 22, 1, "IMC 1 Main 1" },
47674079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL0, 0x2f6a, 22, 2, "IMC 1 Channel 0 Info" },
47774079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL1, 0x2f6b, 22, 3, "IMC 1 Channel 1 Info" },
47874079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL2, 0x2f6c, 22, 4, "IMC 1 Channel 2 Info" },
47974079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_MC1_CHANNEL3, 0x2f6d, 22, 5, "IMC 1 Channel 3 Info" },
48074079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_SAD_DRAM, 0x2ffc, 15, 4, "SAD DRAM Rules" },
48174079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_SAD_MMIO, 0x2ffd, 15, 5, "SAD MMIO Rules" },
48274079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_VTD_MISC, 0x2f28, 5, 0, "Misc. Vritualization" },
48374079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_UBOX, 0x2f1e, 16, 5, "UBox" },
48474079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_UBOX_CPUBUSNO, 0x2f1f, 16, 7, "UBox Scratch" },
48574079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_HA0, 0x2fa0, 18, 0, "Home Agent 0" },
48674079a53SRobert Mustacchi 	{ IMC_GEN_HASWELL, IMC_TYPE_HA1, 0x2f60, 18, 4, "Home Agent 1" },
48774079a53SRobert Mustacchi 	/* Broadwell Devices */
48874079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC0_MAIN0, 0x6fa8, 19, 0, "IMC 0 Main 0" },
48974079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC0_MAIN1, 0x6f71, 19, 1, "IMC 0 Main 1" },
49074079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL0, 0x6faa, 19, 2, "IMC 0 Channel 0 Info" },
49174079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL1, 0x6fab, 19, 3, "IMC 0 Channel 1 Info" },
49274079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL2, 0x6fac, 19, 4, "IMC 0 Channel 2 Info" },
49374079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC0_CHANNEL3, 0x6fad, 19, 5, "IMC 0 Channel 3 Info" },
49474079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC1_MAIN0, 0x6f68, 22, 0, "IMC 1 Main 0" },
49574079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC1_MAIN1, 0x6f79, 22, 1, "IMC 1 Main 1" },
49674079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL0, 0x6f6a, 22, 2, "IMC 1 Channel 0 Info" },
49774079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL1, 0x6f6b, 22, 3, "IMC 1 Channel 1 Info" },
49874079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL2, 0x6f6c, 22, 4, "IMC 1 Channel 2 Info" },
49974079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_MC1_CHANNEL3, 0x6f6d, 22, 5, "IMC 1 Channel 3 Info" },
50074079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_SAD_DRAM, 0x6ffc, 15, 4, "SAD DRAM Rules" },
50174079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_SAD_MMIO, 0x6ffd, 15, 5, "SAD MMIO Rules" },
50274079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_VTD_MISC, 0x6f28, 5, 0, "Misc. Vritualization" },
50374079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_UBOX, 0x6f1e, 16, 5, "UBox" },
50474079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_UBOX_CPUBUSNO, 0x6f1f, 16, 7, "UBox Scratch" },
50574079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_HA0, 0x6fa0, 18, 0, "Home Agent 0" },
50674079a53SRobert Mustacchi 	{ IMC_GEN_BROADWELL, IMC_TYPE_HA1, 0x6f60, 18, 4, "Home Agent 1" },
50774079a53SRobert Mustacchi 	/* Skylake and Cascade Lake Devices */
50874079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC0_M2M, 0x2066, 8, 0, "IMC 0 M2M" },
50974079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC1_M2M, 0x2066, 9, 0, "IMC 0 M2M" },
51074079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC0_MAIN0, 0x2040, 10, 0, "IMC 0 Main / Channel 0" },
51174079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC1_MAIN0, 0x2040, 12, 0, "IMC 0 Main / Channel 0" },
51274079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC0_CHANNEL1, 0x2044, 10, 4, "IMC 0 Channel 1" },
51374079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC0_CHANNEL2, 0x2048, 11, 0, "IMC 0 Channel 2" },
51474079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC1_CHANNEL1, 0x2044, 12, 4, "IMC 1 Channel 1" },
51574079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_MC1_CHANNEL2, 0x2048, 13, 0, "IMC 1 Channel 2" },
51674079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_DRAM, 0x2054, 29, 0, "SAD DRAM Rules" },
51774079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MMIO, 0x2055, 29, 1, "SAD MMIO Rules" },
51874079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_VTD_MISC, 0x2024, 5, 0, "Misc. Virtualization" },
51974079a53SRobert Mustacchi 
52074079a53SRobert Mustacchi 	/*
52174079a53SRobert Mustacchi 	 * There is one SAD MC Route type device per core! Because of this a
52274079a53SRobert Mustacchi 	 * wide array of device and functions are allocated. For now, we list
52374079a53SRobert Mustacchi 	 * all 28 of them out.
52474079a53SRobert Mustacchi 	 */
52574079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 0, "Per-Core SAD" },
52674079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 1, "Per-Core SAD" },
52774079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 2, "Per-Core SAD" },
52874079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 3, "Per-Core SAD" },
52974079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 4, "Per-Core SAD" },
53074079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 5, "Per-Core SAD" },
53174079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 6, "Per-Core SAD" },
53274079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 14, 7, "Per-Core SAD" },
53374079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 0, "Per-Core SAD" },
53474079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 1, "Per-Core SAD" },
53574079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 2, "Per-Core SAD" },
53674079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 3, "Per-Core SAD" },
53774079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 4, "Per-Core SAD" },
53874079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 5, "Per-Core SAD" },
53974079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 6, "Per-Core SAD" },
54074079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 15, 7, "Per-Core SAD" },
54174079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 0, "Per-Core SAD" },
54274079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 1, "Per-Core SAD" },
54374079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 2, "Per-Core SAD" },
54474079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 3, "Per-Core SAD" },
54574079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 4, "Per-Core SAD" },
54674079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 5, "Per-Core SAD" },
54774079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 6, "Per-Core SAD" },
54874079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 16, 7, "Per-Core SAD" },
54974079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 0, "Per-Core SAD" },
55074079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 1, "Per-Core SAD" },
55174079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 2, "Per-Core SAD" },
55274079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 3, "Per-Core SAD" },
55374079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 4, "Per-Core SAD" },
55474079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 5, "Per-Core SAD" },
55574079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 6, "Per-Core SAD" },
55674079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_SAD_MCROUTE, 0x208e, 17, 7, "Per-Core SAD" },
55774079a53SRobert Mustacchi 
55874079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_UBOX, 0x2014, 8, 0, "UBox" },
55974079a53SRobert Mustacchi 	{ IMC_GEN_SKYLAKE, IMC_TYPE_UBOX_CPUBUSNO, 0x2016, 8, 2, "DECS" },
56074079a53SRobert Mustacchi };
56174079a53SRobert Mustacchi /* END CSTYLED */
56274079a53SRobert Mustacchi 
56374079a53SRobert Mustacchi #define	IMC_PCI_VENDOR_INTC	0x8086
56474079a53SRobert Mustacchi 
56574079a53SRobert Mustacchi /*
56674079a53SRobert Mustacchi  * Our IMC data is global and statically set up during a combination of
56774079a53SRobert Mustacchi  * _init(9E) and attach(9E). While we have a module dependency between the PCI
56874079a53SRobert Mustacchi  * stub driver, imcstub, and this pseudo-driver, imc, the dependencies don't
56974079a53SRobert Mustacchi  * guarantee that the imc driver has finished attaching. As such we make sure
57074079a53SRobert Mustacchi  * that it can operate without it being attached in any way.
57174079a53SRobert Mustacchi  */
57274079a53SRobert Mustacchi static imc_t *imc_data = NULL;
57374079a53SRobert Mustacchi 
57474079a53SRobert Mustacchi /*
57574079a53SRobert Mustacchi  * By default we should not allow the stubs to detach as we don't have a good
57674079a53SRobert Mustacchi  * way of forcing them to attach again. This is provided in case someone does
57774079a53SRobert Mustacchi  * want to allow the driver to unload.
57874079a53SRobert Mustacchi  */
57974079a53SRobert Mustacchi int imc_allow_detach = 0;
58074079a53SRobert Mustacchi 
58174079a53SRobert Mustacchi static void
imc_set_gen_data(imc_t * imc)58274079a53SRobert Mustacchi imc_set_gen_data(imc_t *imc)
58374079a53SRobert Mustacchi {
58474079a53SRobert Mustacchi 	switch (imc->imc_gen) {
58574079a53SRobert Mustacchi 	case IMC_GEN_SANDY:
58674079a53SRobert Mustacchi 		imc->imc_gen_data = &imc_gen_data_snb;
58774079a53SRobert Mustacchi 		break;
58874079a53SRobert Mustacchi 	case IMC_GEN_IVY:
58974079a53SRobert Mustacchi 		imc->imc_gen_data = &imc_gen_data_ivb;
59074079a53SRobert Mustacchi 		break;
59174079a53SRobert Mustacchi 	case IMC_GEN_HASWELL:
59274079a53SRobert Mustacchi 	case IMC_GEN_BROADWELL:
59374079a53SRobert Mustacchi 		imc->imc_gen_data = &imc_gen_data_has_brd;
59474079a53SRobert Mustacchi 		break;
59574079a53SRobert Mustacchi 	case IMC_GEN_SKYLAKE:
59674079a53SRobert Mustacchi 		imc->imc_gen_data = &imc_gen_data_skx;
59774079a53SRobert Mustacchi 		break;
59874079a53SRobert Mustacchi 	default:
59974079a53SRobert Mustacchi 		dev_err(imc->imc_dip, CE_PANIC, "imc driver programmer error: "
60074079a53SRobert Mustacchi 		    "set to unknown generation: %u", imc->imc_gen);
60174079a53SRobert Mustacchi 	}
60274079a53SRobert Mustacchi }
60374079a53SRobert Mustacchi 
60474079a53SRobert Mustacchi /*
60574079a53SRobert Mustacchi  * If our device (dev_info_t) does not have a non-zero unit address, then
60674079a53SRobert Mustacchi  * devfsadmd will not pay attention to us at all. Therefore we need to set the
60774079a53SRobert Mustacchi  * unit address below, before we create minor nodes.
60874079a53SRobert Mustacchi  *
60974079a53SRobert Mustacchi  * The rest of the system expects us to have one minor node per socket. The
61074079a53SRobert Mustacchi  * minor node ID should be the ID of the socket.
61174079a53SRobert Mustacchi  */
61274079a53SRobert Mustacchi static boolean_t
imc_create_minors(imc_t * imc)61374079a53SRobert Mustacchi imc_create_minors(imc_t *imc)
61474079a53SRobert Mustacchi {
61574079a53SRobert Mustacchi 	uint_t i;
61674079a53SRobert Mustacchi 
61774079a53SRobert Mustacchi 	ddi_set_name_addr(imc->imc_dip, "1");
61874079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
61974079a53SRobert Mustacchi 		char buf[MAXNAMELEN];
62074079a53SRobert Mustacchi 
62174079a53SRobert Mustacchi 		if (snprintf(buf, sizeof (buf), "mc-imc-%u", i) >=
62274079a53SRobert Mustacchi 		    sizeof (buf)) {
62374079a53SRobert Mustacchi 			goto fail;
62474079a53SRobert Mustacchi 		}
62574079a53SRobert Mustacchi 
62674079a53SRobert Mustacchi 		if (ddi_create_minor_node(imc->imc_dip, buf, S_IFCHR, i,
62774079a53SRobert Mustacchi 		    "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
62874079a53SRobert Mustacchi 			dev_err(imc->imc_dip, CE_WARN, "failed to create "
62974079a53SRobert Mustacchi 			    "minor node %u: %s", i, buf);
63074079a53SRobert Mustacchi 			goto fail;
63174079a53SRobert Mustacchi 		}
63274079a53SRobert Mustacchi 	}
63374079a53SRobert Mustacchi 	return (B_TRUE);
63474079a53SRobert Mustacchi 
63574079a53SRobert Mustacchi fail:
63674079a53SRobert Mustacchi 	ddi_remove_minor_node(imc->imc_dip, NULL);
63774079a53SRobert Mustacchi 	return (B_FALSE);
63874079a53SRobert Mustacchi }
63974079a53SRobert Mustacchi 
64074079a53SRobert Mustacchi /*
64174079a53SRobert Mustacchi  * Check the current MC route value for this SAD. On Skylake systems there is
64274079a53SRobert Mustacchi  * one per core. Every core should agree. If not, we will not trust the SAD
64374079a53SRobert Mustacchi  * MCROUTE values and this will cause system address decoding to fail on
64474079a53SRobert Mustacchi  * skylake.
64574079a53SRobert Mustacchi  */
64674079a53SRobert Mustacchi static void
imc_mcroute_check(imc_t * imc,imc_sad_t * sad,imc_stub_t * stub)64774079a53SRobert Mustacchi imc_mcroute_check(imc_t *imc, imc_sad_t *sad, imc_stub_t *stub)
64874079a53SRobert Mustacchi {
64974079a53SRobert Mustacchi 	uint32_t val;
65074079a53SRobert Mustacchi 
65174079a53SRobert Mustacchi 	val = pci_config_get32(stub->istub_cfgspace,
65274079a53SRobert Mustacchi 	    IMC_REG_SKX_SAD_MC_ROUTE_TABLE);
65374079a53SRobert Mustacchi 	if (val == PCI_EINVAL32) {
65474079a53SRobert Mustacchi 		sad->isad_valid |= IMC_SAD_V_BAD_PCI_READ;
65574079a53SRobert Mustacchi 		return;
65674079a53SRobert Mustacchi 	}
65774079a53SRobert Mustacchi 
65874079a53SRobert Mustacchi 	if ((sad->isad_flags & IMC_SAD_MCROUTE_VALID) == 0 && val != 0) {
65974079a53SRobert Mustacchi 		sad->isad_flags |= IMC_SAD_MCROUTE_VALID;
66074079a53SRobert Mustacchi 		sad->isad_mcroute.ismc_raw_mcroute = val;
66174079a53SRobert Mustacchi 		return;
66274079a53SRobert Mustacchi 	}
66374079a53SRobert Mustacchi 
66474079a53SRobert Mustacchi 	/*
66574079a53SRobert Mustacchi 	 * Occasionally we see MC ROUTE table entries with a value of zero.
66674079a53SRobert Mustacchi 	 * We should ignore those for now.
66774079a53SRobert Mustacchi 	 */
66874079a53SRobert Mustacchi 	if (val != sad->isad_mcroute.ismc_raw_mcroute && val != 0) {
66974079a53SRobert Mustacchi 		dev_err(imc->imc_dip, CE_WARN, "SAD MC_ROUTE_TABLE mismatch "
67074079a53SRobert Mustacchi 		    "with socket. SAD has val 0x%x, system has %x\n",
67174079a53SRobert Mustacchi 		    val, sad->isad_mcroute.ismc_raw_mcroute);
67274079a53SRobert Mustacchi 		sad->isad_valid |= IMC_SAD_V_BAD_MCROUTE;
67374079a53SRobert Mustacchi 	}
67474079a53SRobert Mustacchi }
67574079a53SRobert Mustacchi 
67674079a53SRobert Mustacchi /*
67774079a53SRobert Mustacchi  * On Skylake, many of the devices that we care about are on separate PCI Buses.
67874079a53SRobert Mustacchi  * These can be mapped together by the DECS register. However, we need to know
67974079a53SRobert Mustacchi  * how to map different buses together so that we can more usefully associate
68074079a53SRobert Mustacchi  * information. The set of buses is all present in the DECS register. We'll
68174079a53SRobert Mustacchi  * effectively assign sockets to buses. This is also still something that comes
68274079a53SRobert Mustacchi  * up on pre-Skylake systems as well.
68374079a53SRobert Mustacchi  */
68474079a53SRobert Mustacchi static boolean_t
imc_map_buses(imc_t * imc)68574079a53SRobert Mustacchi imc_map_buses(imc_t *imc)
68674079a53SRobert Mustacchi {
68774079a53SRobert Mustacchi 	imc_stub_t *stub;
68874079a53SRobert Mustacchi 	uint_t nsock;
68974079a53SRobert Mustacchi 
69074079a53SRobert Mustacchi 	/*
69174079a53SRobert Mustacchi 	 * Find the UBOX_DECS registers so we can establish socket mappings. On
69274079a53SRobert Mustacchi 	 * Skylake, there are three different sets of buses that we need to
69374079a53SRobert Mustacchi 	 * cover all of our devices, while there are only two before that.
69474079a53SRobert Mustacchi 	 */
69574079a53SRobert Mustacchi 	for (nsock = 0, stub = avl_first(&imc->imc_stubs); stub != NULL;
69674079a53SRobert Mustacchi 	    stub = AVL_NEXT(&imc->imc_stubs, stub)) {
69774079a53SRobert Mustacchi 		uint32_t busno;
69874079a53SRobert Mustacchi 
69974079a53SRobert Mustacchi 		if (stub->istub_table->imcs_type != IMC_TYPE_UBOX_CPUBUSNO) {
70074079a53SRobert Mustacchi 			continue;
70174079a53SRobert Mustacchi 		}
70274079a53SRobert Mustacchi 
70374079a53SRobert Mustacchi 		busno = pci_config_get32(stub->istub_cfgspace,
70474079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_ubox_cpubusno_offset);
70574079a53SRobert Mustacchi 		if (busno == PCI_EINVAL32) {
70674079a53SRobert Mustacchi 			dev_err(imc->imc_dip, CE_WARN, "failed to read "
70774079a53SRobert Mustacchi 			    "UBOX_DECS CPUBUSNO0: invalid PCI read");
70874079a53SRobert Mustacchi 			return (B_FALSE);
70974079a53SRobert Mustacchi 		}
71074079a53SRobert Mustacchi 
71174079a53SRobert Mustacchi 		if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
71274079a53SRobert Mustacchi 			imc->imc_sockets[nsock].isock_nbus = 3;
71374079a53SRobert Mustacchi 			imc->imc_sockets[nsock].isock_bus[0] =
71474079a53SRobert Mustacchi 			    IMC_UBOX_CPUBUSNO_0(busno);
71574079a53SRobert Mustacchi 			imc->imc_sockets[nsock].isock_bus[1] =
71674079a53SRobert Mustacchi 			    IMC_UBOX_CPUBUSNO_1(busno);
71774079a53SRobert Mustacchi 			imc->imc_sockets[nsock].isock_bus[2] =
71874079a53SRobert Mustacchi 			    IMC_UBOX_CPUBUSNO_2(busno);
71974079a53SRobert Mustacchi 		} else {
72074079a53SRobert Mustacchi 			imc->imc_sockets[nsock].isock_bus[0] =
72174079a53SRobert Mustacchi 			    IMC_UBOX_CPUBUSNO_0(busno);
72274079a53SRobert Mustacchi 			imc->imc_sockets[nsock].isock_bus[1] =
72374079a53SRobert Mustacchi 			    IMC_UBOX_CPUBUSNO_1(busno);
72474079a53SRobert Mustacchi 			imc->imc_sockets[nsock].isock_nbus = 2;
72574079a53SRobert Mustacchi 		}
72674079a53SRobert Mustacchi 		nsock++;
72774079a53SRobert Mustacchi 	}
72874079a53SRobert Mustacchi 	imc->imc_nsockets = nsock;
72974079a53SRobert Mustacchi 
73074079a53SRobert Mustacchi 	return (B_TRUE);
73174079a53SRobert Mustacchi }
73274079a53SRobert Mustacchi 
73374079a53SRobert Mustacchi /*
73474079a53SRobert Mustacchi  * For a given stub that we've found, map it to its corresponding socket based
73574079a53SRobert Mustacchi  * on the PCI bus that it has.
73674079a53SRobert Mustacchi  */
73774079a53SRobert Mustacchi static imc_socket_t *
imc_map_find_socket(imc_t * imc,imc_stub_t * stub)73874079a53SRobert Mustacchi imc_map_find_socket(imc_t *imc, imc_stub_t *stub)
73974079a53SRobert Mustacchi {
74074079a53SRobert Mustacchi 	uint_t i;
74174079a53SRobert Mustacchi 
74274079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
74374079a53SRobert Mustacchi 		uint_t bus;
74474079a53SRobert Mustacchi 
74574079a53SRobert Mustacchi 		for (bus = 0; bus < imc->imc_sockets[i].isock_nbus; bus++) {
74674079a53SRobert Mustacchi 			if (imc->imc_sockets[i].isock_bus[bus] ==
74774079a53SRobert Mustacchi 			    stub->istub_bus) {
74874079a53SRobert Mustacchi 				return (&imc->imc_sockets[i]);
74974079a53SRobert Mustacchi 			}
75074079a53SRobert Mustacchi 		}
75174079a53SRobert Mustacchi 	}
75274079a53SRobert Mustacchi 
75374079a53SRobert Mustacchi 	return (NULL);
75474079a53SRobert Mustacchi }
75574079a53SRobert Mustacchi 
75674079a53SRobert Mustacchi static boolean_t
imc_map_stubs(imc_t * imc)75774079a53SRobert Mustacchi imc_map_stubs(imc_t *imc)
75874079a53SRobert Mustacchi {
75974079a53SRobert Mustacchi 	imc_stub_t *stub;
76074079a53SRobert Mustacchi 
76174079a53SRobert Mustacchi 	if (!imc_map_buses(imc)) {
76274079a53SRobert Mustacchi 		return (B_FALSE);
76374079a53SRobert Mustacchi 	}
76474079a53SRobert Mustacchi 
76574079a53SRobert Mustacchi 	stub = avl_first(&imc->imc_stubs);
76674079a53SRobert Mustacchi 	for (stub = avl_first(&imc->imc_stubs); stub != NULL;
76774079a53SRobert Mustacchi 	    stub = AVL_NEXT(&imc->imc_stubs, stub)) {
76874079a53SRobert Mustacchi 		imc_socket_t *sock = imc_map_find_socket(imc, stub);
76974079a53SRobert Mustacchi 
77074079a53SRobert Mustacchi 		if (sock == NULL) {
77174079a53SRobert Mustacchi 			dev_err(imc->imc_dip, CE_WARN, "found stub type %u "
77274079a53SRobert Mustacchi 			    "PCI%x,%x with bdf %u/%u/%u that does not match a "
77374079a53SRobert Mustacchi 			    "known PCI bus for any of %u sockets",
77474079a53SRobert Mustacchi 			    stub->istub_table->imcs_type, stub->istub_vid,
77574079a53SRobert Mustacchi 			    stub->istub_did, stub->istub_bus, stub->istub_dev,
77674079a53SRobert Mustacchi 			    stub->istub_func, imc->imc_nsockets);
77774079a53SRobert Mustacchi 			continue;
77874079a53SRobert Mustacchi 		}
77974079a53SRobert Mustacchi 
78074079a53SRobert Mustacchi 		/*
78174079a53SRobert Mustacchi 		 * We don't have to worry about duplicates here. We check to
78274079a53SRobert Mustacchi 		 * make sure that we have unique bdfs here.
78374079a53SRobert Mustacchi 		 */
78474079a53SRobert Mustacchi 		switch (stub->istub_table->imcs_type) {
78574079a53SRobert Mustacchi 		case IMC_TYPE_MC0_M2M:
78674079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_m2m = stub;
78774079a53SRobert Mustacchi 			break;
78874079a53SRobert Mustacchi 		case IMC_TYPE_MC1_M2M:
78974079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_m2m = stub;
79074079a53SRobert Mustacchi 			break;
79174079a53SRobert Mustacchi 		case IMC_TYPE_MC0_MAIN0:
79274079a53SRobert Mustacchi 			sock->isock_nimc++;
79374079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_main0 = stub;
79474079a53SRobert Mustacchi 
79574079a53SRobert Mustacchi 			/*
79674079a53SRobert Mustacchi 			 * On Skylake, the MAIN0 does double duty as channel
79774079a53SRobert Mustacchi 			 * zero and as the TAD.
79874079a53SRobert Mustacchi 			 */
79974079a53SRobert Mustacchi 			if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
80074079a53SRobert Mustacchi 				sock->isock_imcs[0].icn_nchannels++;
80174079a53SRobert Mustacchi 				sock->isock_imcs[0].icn_channels[0].ich_desc =
80274079a53SRobert Mustacchi 				    stub;
80374079a53SRobert Mustacchi 				sock->isock_tad[0].itad_stub = stub;
80474079a53SRobert Mustacchi 				sock->isock_ntad++;
80574079a53SRobert Mustacchi 			}
80674079a53SRobert Mustacchi 			break;
80774079a53SRobert Mustacchi 		case IMC_TYPE_MC0_MAIN1:
80874079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_main1 = stub;
80974079a53SRobert Mustacchi 			break;
81074079a53SRobert Mustacchi 		case IMC_TYPE_MC1_MAIN0:
81174079a53SRobert Mustacchi 			sock->isock_nimc++;
81274079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_main0 = stub;
81374079a53SRobert Mustacchi 
81474079a53SRobert Mustacchi 			/*
81574079a53SRobert Mustacchi 			 * On Skylake, the MAIN0 does double duty as channel
81674079a53SRobert Mustacchi 			 * zero and as the TAD.
81774079a53SRobert Mustacchi 			 */
81874079a53SRobert Mustacchi 			if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
81974079a53SRobert Mustacchi 				sock->isock_imcs[1].icn_nchannels++;
82074079a53SRobert Mustacchi 				sock->isock_imcs[1].icn_channels[0].ich_desc =
82174079a53SRobert Mustacchi 				    stub;
82274079a53SRobert Mustacchi 				sock->isock_tad[1].itad_stub = stub;
82374079a53SRobert Mustacchi 				sock->isock_ntad++;
82474079a53SRobert Mustacchi 			}
82574079a53SRobert Mustacchi 			break;
82674079a53SRobert Mustacchi 		case IMC_TYPE_MC1_MAIN1:
82774079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_main1 = stub;
82874079a53SRobert Mustacchi 			break;
82974079a53SRobert Mustacchi 		case IMC_TYPE_MC0_CHANNEL0:
83074079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_nchannels++;
83174079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_channels[0].ich_desc = stub;
83274079a53SRobert Mustacchi 			break;
83374079a53SRobert Mustacchi 		case IMC_TYPE_MC0_CHANNEL1:
83474079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_nchannels++;
83574079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_channels[1].ich_desc = stub;
83674079a53SRobert Mustacchi 			break;
83774079a53SRobert Mustacchi 		case IMC_TYPE_MC0_CHANNEL2:
83874079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_nchannels++;
83974079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_channels[2].ich_desc = stub;
84074079a53SRobert Mustacchi 			break;
84174079a53SRobert Mustacchi 		case IMC_TYPE_MC0_CHANNEL3:
84274079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_nchannels++;
84374079a53SRobert Mustacchi 			sock->isock_imcs[0].icn_channels[3].ich_desc = stub;
84474079a53SRobert Mustacchi 			break;
84574079a53SRobert Mustacchi 		case IMC_TYPE_MC1_CHANNEL0:
84674079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_nchannels++;
84774079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_channels[0].ich_desc = stub;
84874079a53SRobert Mustacchi 			break;
84974079a53SRobert Mustacchi 		case IMC_TYPE_MC1_CHANNEL1:
85074079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_nchannels++;
85174079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_channels[1].ich_desc = stub;
85274079a53SRobert Mustacchi 			break;
85374079a53SRobert Mustacchi 		case IMC_TYPE_MC1_CHANNEL2:
85474079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_nchannels++;
85574079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_channels[2].ich_desc = stub;
85674079a53SRobert Mustacchi 			break;
85774079a53SRobert Mustacchi 		case IMC_TYPE_MC1_CHANNEL3:
85874079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_nchannels++;
85974079a53SRobert Mustacchi 			sock->isock_imcs[1].icn_channels[3].ich_desc = stub;
86074079a53SRobert Mustacchi 			break;
86174079a53SRobert Mustacchi 		case IMC_TYPE_SAD_DRAM:
86274079a53SRobert Mustacchi 			sock->isock_sad.isad_dram = stub;
86374079a53SRobert Mustacchi 			break;
86474079a53SRobert Mustacchi 		case IMC_TYPE_SAD_MMIO:
86574079a53SRobert Mustacchi 			sock->isock_sad.isad_mmio = stub;
86674079a53SRobert Mustacchi 			break;
86774079a53SRobert Mustacchi 		case IMC_TYPE_SAD_MISC:
86874079a53SRobert Mustacchi 			sock->isock_sad.isad_tolh = stub;
86974079a53SRobert Mustacchi 			break;
87074079a53SRobert Mustacchi 		case IMC_TYPE_VTD_MISC:
87174079a53SRobert Mustacchi 			/*
87274079a53SRobert Mustacchi 			 * Some systems have multiple VT-D Misc. entry points
87374079a53SRobert Mustacchi 			 * in the system. In this case, only use the first one
87474079a53SRobert Mustacchi 			 * we find.
87574079a53SRobert Mustacchi 			 */
87674079a53SRobert Mustacchi 			if (imc->imc_gvtd_misc == NULL) {
87774079a53SRobert Mustacchi 				imc->imc_gvtd_misc = stub;
87874079a53SRobert Mustacchi 			}
87974079a53SRobert Mustacchi 			break;
88074079a53SRobert Mustacchi 		case IMC_TYPE_SAD_MCROUTE:
88174079a53SRobert Mustacchi 			ASSERT3U(imc->imc_gen, >=, IMC_GEN_SKYLAKE);
88274079a53SRobert Mustacchi 			imc_mcroute_check(imc, &sock->isock_sad, stub);
88374079a53SRobert Mustacchi 			break;
88474079a53SRobert Mustacchi 		case IMC_TYPE_UBOX:
88574079a53SRobert Mustacchi 			sock->isock_ubox = stub;
88674079a53SRobert Mustacchi 			break;
88774079a53SRobert Mustacchi 		case IMC_TYPE_HA0:
88874079a53SRobert Mustacchi 			sock->isock_ntad++;
88974079a53SRobert Mustacchi 			sock->isock_tad[0].itad_stub = stub;
89074079a53SRobert Mustacchi 			break;
89174079a53SRobert Mustacchi 		case IMC_TYPE_HA1:
89274079a53SRobert Mustacchi 			sock->isock_ntad++;
89374079a53SRobert Mustacchi 			sock->isock_tad[1].itad_stub = stub;
89474079a53SRobert Mustacchi 			break;
89574079a53SRobert Mustacchi 		case IMC_TYPE_UBOX_CPUBUSNO:
89674079a53SRobert Mustacchi 			sock->isock_cpubusno = stub;
89774079a53SRobert Mustacchi 			break;
89874079a53SRobert Mustacchi 		default:
89974079a53SRobert Mustacchi 			/*
90074079a53SRobert Mustacchi 			 * Attempt to still attach if we can.
90174079a53SRobert Mustacchi 			 */
90274079a53SRobert Mustacchi 			dev_err(imc->imc_dip, CE_WARN, "Encountered unknown "
90374079a53SRobert Mustacchi 			    "IMC type (%u) on PCI %x,%x",
90474079a53SRobert Mustacchi 			    stub->istub_table->imcs_type,
90574079a53SRobert Mustacchi 			    stub->istub_vid, stub->istub_did);
90674079a53SRobert Mustacchi 			break;
90774079a53SRobert Mustacchi 		}
90874079a53SRobert Mustacchi 	}
90974079a53SRobert Mustacchi 
91074079a53SRobert Mustacchi 	return (B_TRUE);
91174079a53SRobert Mustacchi }
91274079a53SRobert Mustacchi 
91374079a53SRobert Mustacchi /*
91474079a53SRobert Mustacchi  * Go through and fix up various aspects of the stubs mappings on systems. The
91574079a53SRobert Mustacchi  * following are a list of what we need to fix up:
91674079a53SRobert Mustacchi  *
91774079a53SRobert Mustacchi  *  1. On Haswell and newer systems, there is only one global VT-d device. We
91874079a53SRobert Mustacchi  *     need to go back and map that to all of the per-socket imc_sad_t entries.
91974079a53SRobert Mustacchi  */
92074079a53SRobert Mustacchi static void
imc_fixup_stubs(imc_t * imc)92174079a53SRobert Mustacchi imc_fixup_stubs(imc_t *imc)
92274079a53SRobert Mustacchi {
92374079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_HASWELL) {
92474079a53SRobert Mustacchi 		uint_t i;
92574079a53SRobert Mustacchi 
92674079a53SRobert Mustacchi 		for (i = 0; i < imc->imc_nsockets; i++) {
92774079a53SRobert Mustacchi 			ASSERT3P(imc->imc_sockets[i].isock_sad.isad_tolh,
92874079a53SRobert Mustacchi 			    ==, NULL);
92974079a53SRobert Mustacchi 			imc->imc_sockets[i].isock_sad.isad_tolh =
93074079a53SRobert Mustacchi 			    imc->imc_gvtd_misc;
93174079a53SRobert Mustacchi 		}
93274079a53SRobert Mustacchi 	}
93374079a53SRobert Mustacchi }
93474079a53SRobert Mustacchi 
93574079a53SRobert Mustacchi /*
93674079a53SRobert Mustacchi  * In the wild we've hit a few odd cases where not all devices are exposed that
93774079a53SRobert Mustacchi  * we might expect by firmware. In particular we've seen and validate the
93874079a53SRobert Mustacchi  * following cases:
93974079a53SRobert Mustacchi  *
94074079a53SRobert Mustacchi  *  o We don't find all of the channel devices that we expect, e.g. we have the
94174079a53SRobert Mustacchi  *    stubs for channels 1-3, but not 0. That has been seen on an Intel S2600CW
94274079a53SRobert Mustacchi  *    with an E5-2630v3.
94374079a53SRobert Mustacchi  */
94474079a53SRobert Mustacchi static boolean_t
imc_validate_stubs(imc_t * imc)94574079a53SRobert Mustacchi imc_validate_stubs(imc_t *imc)
94674079a53SRobert Mustacchi {
94774079a53SRobert Mustacchi 	for (uint_t sock = 0; sock < imc->imc_nsockets; sock++) {
94874079a53SRobert Mustacchi 		imc_socket_t *socket = &imc->imc_sockets[sock];
94974079a53SRobert Mustacchi 
95074079a53SRobert Mustacchi 		for (uint_t mc = 0; mc < socket->isock_nimc; mc++) {
95174079a53SRobert Mustacchi 			imc_mc_t *mcp = &socket->isock_imcs[mc];
95274079a53SRobert Mustacchi 
95374079a53SRobert Mustacchi 			for (uint_t chan = 0; chan < mcp->icn_nchannels;
95474079a53SRobert Mustacchi 			    chan++) {
95574079a53SRobert Mustacchi 				if (mcp->icn_channels[chan].ich_desc == NULL) {
95674079a53SRobert Mustacchi 					dev_err(imc->imc_dip, CE_WARN,
95774079a53SRobert Mustacchi 					    "!missing device for socket %u/"
95874079a53SRobert Mustacchi 					    "imc %u/channel %u", sock, mc,
95974079a53SRobert Mustacchi 					    chan);
96074079a53SRobert Mustacchi 					return (B_FALSE);
96174079a53SRobert Mustacchi 				}
96274079a53SRobert Mustacchi 			}
96374079a53SRobert Mustacchi 		}
96474079a53SRobert Mustacchi 	}
96574079a53SRobert Mustacchi 
96674079a53SRobert Mustacchi 	return (B_TRUE);
96774079a53SRobert Mustacchi }
96874079a53SRobert Mustacchi 
96974079a53SRobert Mustacchi /*
97074079a53SRobert Mustacchi  * Attempt to map all of the discovered sockets to the corresponding APIC based
97174079a53SRobert Mustacchi  * socket. We do these mappings by getting the node id of the socket and
97274079a53SRobert Mustacchi  * adjusting it to make sure that no home agent is present in it. We use the
97374079a53SRobert Mustacchi  * UBOX to avoid any home agent related bits that are present in other
97474079a53SRobert Mustacchi  * registers.
97574079a53SRobert Mustacchi  */
97674079a53SRobert Mustacchi static void
imc_map_sockets(imc_t * imc)97774079a53SRobert Mustacchi imc_map_sockets(imc_t *imc)
97874079a53SRobert Mustacchi {
97974079a53SRobert Mustacchi 	uint_t i;
98074079a53SRobert Mustacchi 
98174079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
98274079a53SRobert Mustacchi 		uint32_t nodeid;
98374079a53SRobert Mustacchi 		ddi_acc_handle_t h;
98474079a53SRobert Mustacchi 
98574079a53SRobert Mustacchi 		h = imc->imc_sockets[i].isock_ubox->istub_cfgspace;
98674079a53SRobert Mustacchi 		nodeid = pci_config_get32(h,
98774079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_sad_nodeid_offset);
98874079a53SRobert Mustacchi 		if (nodeid == PCI_EINVAL32) {
98974079a53SRobert Mustacchi 			imc->imc_sockets[i].isock_valid |=
99074079a53SRobert Mustacchi 			    IMC_SOCKET_V_BAD_NODEID;
99174079a53SRobert Mustacchi 			continue;
99274079a53SRobert Mustacchi 		}
99374079a53SRobert Mustacchi 
99474079a53SRobert Mustacchi 		imc->imc_sockets[i].isock_nodeid = IMC_NODEID_UBOX_MASK(nodeid);
99574079a53SRobert Mustacchi 		imc->imc_spointers[nodeid] = &imc->imc_sockets[i];
99674079a53SRobert Mustacchi 	}
99774079a53SRobert Mustacchi }
99874079a53SRobert Mustacchi 
99974079a53SRobert Mustacchi /*
100074079a53SRobert Mustacchi  * Decode the MTR, accounting for variances between processor generations.
100174079a53SRobert Mustacchi  */
100274079a53SRobert Mustacchi static void
imc_decode_mtr(imc_t * imc,imc_mc_t * icn,imc_dimm_t * dimm,uint32_t mtr)100374079a53SRobert Mustacchi imc_decode_mtr(imc_t *imc, imc_mc_t *icn, imc_dimm_t *dimm, uint32_t mtr)
100474079a53SRobert Mustacchi {
100574079a53SRobert Mustacchi 	uint8_t disable;
100674079a53SRobert Mustacchi 
100774079a53SRobert Mustacchi 	/*
100874079a53SRobert Mustacchi 	 * Check present first, before worrying about anything else.
100974079a53SRobert Mustacchi 	 */
101074079a53SRobert Mustacchi 	if (imc->imc_gen < IMC_GEN_SKYLAKE &&
101174079a53SRobert Mustacchi 	    IMC_MTR_PRESENT_SNB_BRD(mtr) == 0) {
101274079a53SRobert Mustacchi 		dimm->idimm_present = B_FALSE;
101374079a53SRobert Mustacchi 		return;
101474079a53SRobert Mustacchi 	} else if (imc->imc_gen >= IMC_GEN_SKYLAKE &&
101574079a53SRobert Mustacchi 	    IMC_MTR_PRESENT_SKYLAKE(mtr) == 0) {
101674079a53SRobert Mustacchi 		dimm->idimm_present = B_FALSE;
101774079a53SRobert Mustacchi 		return;
101874079a53SRobert Mustacchi 	}
101974079a53SRobert Mustacchi 
102074079a53SRobert Mustacchi 	dimm->idimm_present = B_TRUE;
102174079a53SRobert Mustacchi 	dimm->idimm_ncolumns = IMC_MTR_CA_WIDTH(mtr) + IMC_MTR_CA_BASE;
102274079a53SRobert Mustacchi 	if (dimm->idimm_ncolumns < IMC_MTR_CA_MIN ||
102374079a53SRobert Mustacchi 	    dimm->idimm_ncolumns > IMC_MTR_CA_MAX) {
102474079a53SRobert Mustacchi 		dimm->idimm_valid |= IMC_DIMM_V_BAD_COLUMNS;
102574079a53SRobert Mustacchi 	}
102674079a53SRobert Mustacchi 
102774079a53SRobert Mustacchi 	dimm->idimm_nrows = IMC_MTR_RA_WIDTH(mtr) + IMC_MTR_RA_BASE;
102874079a53SRobert Mustacchi 	if (dimm->idimm_nrows < IMC_MTR_RA_MIN ||
102974079a53SRobert Mustacchi 	    dimm->idimm_nrows > IMC_MTR_RA_MAX) {
103074079a53SRobert Mustacchi 		dimm->idimm_valid |= IMC_DIMM_V_BAD_ROWS;
103174079a53SRobert Mustacchi 	}
103274079a53SRobert Mustacchi 
103374079a53SRobert Mustacchi 	/*
103474079a53SRobert Mustacchi 	 * Determine Density, this information is not present on Sandy Bridge.
103574079a53SRobert Mustacchi 	 */
103674079a53SRobert Mustacchi 	switch (imc->imc_gen) {
103774079a53SRobert Mustacchi 	case IMC_GEN_IVY:
103874079a53SRobert Mustacchi 		dimm->idimm_density = 1U << IMC_MTR_DENSITY_IVY_BRD(mtr);
103974079a53SRobert Mustacchi 		break;
104074079a53SRobert Mustacchi 	case IMC_GEN_HASWELL:
104174079a53SRobert Mustacchi 	case IMC_GEN_BROADWELL:
104274079a53SRobert Mustacchi 		switch (IMC_MTR_DENSITY_IVY_BRD(mtr)) {
104374079a53SRobert Mustacchi 		case 0:
104474079a53SRobert Mustacchi 		default:
104574079a53SRobert Mustacchi 			dimm->idimm_density = 0;
104674079a53SRobert Mustacchi 			dimm->idimm_valid |= IMC_DIMM_V_BAD_DENSITY;
104774079a53SRobert Mustacchi 			break;
104874079a53SRobert Mustacchi 		case 1:
104974079a53SRobert Mustacchi 			dimm->idimm_density = 2;
105074079a53SRobert Mustacchi 			break;
105174079a53SRobert Mustacchi 		case 2:
105274079a53SRobert Mustacchi 			dimm->idimm_density = 4;
105374079a53SRobert Mustacchi 			break;
105474079a53SRobert Mustacchi 		case 3:
105574079a53SRobert Mustacchi 			dimm->idimm_density = 8;
105674079a53SRobert Mustacchi 			break;
105774079a53SRobert Mustacchi 		}
105874079a53SRobert Mustacchi 		break;
105974079a53SRobert Mustacchi 	case IMC_GEN_SKYLAKE:
106074079a53SRobert Mustacchi 		switch (IMC_MTR_DENSITY_SKX(mtr)) {
106174079a53SRobert Mustacchi 		case 0:
106274079a53SRobert Mustacchi 		default:
106374079a53SRobert Mustacchi 			dimm->idimm_density = 0;
106474079a53SRobert Mustacchi 			dimm->idimm_valid |= IMC_DIMM_V_BAD_DENSITY;
106574079a53SRobert Mustacchi 			break;
106674079a53SRobert Mustacchi 		case 1:
106774079a53SRobert Mustacchi 			dimm->idimm_density = 2;
106874079a53SRobert Mustacchi 			break;
106974079a53SRobert Mustacchi 		case 2:
107074079a53SRobert Mustacchi 			dimm->idimm_density = 4;
107174079a53SRobert Mustacchi 			break;
107274079a53SRobert Mustacchi 		case 3:
107374079a53SRobert Mustacchi 			dimm->idimm_density = 8;
107474079a53SRobert Mustacchi 			break;
107574079a53SRobert Mustacchi 		case 4:
107674079a53SRobert Mustacchi 			dimm->idimm_density = 16;
107774079a53SRobert Mustacchi 			break;
107874079a53SRobert Mustacchi 		case 5:
107974079a53SRobert Mustacchi 			dimm->idimm_density = 12;
108074079a53SRobert Mustacchi 			break;
108174079a53SRobert Mustacchi 		}
108274079a53SRobert Mustacchi 		break;
108374079a53SRobert Mustacchi 	case IMC_GEN_UNKNOWN:
108474079a53SRobert Mustacchi 	case IMC_GEN_SANDY:
108574079a53SRobert Mustacchi 		dimm->idimm_density = 0;
108674079a53SRobert Mustacchi 		break;
108774079a53SRobert Mustacchi 	}
108874079a53SRobert Mustacchi 
108974079a53SRobert Mustacchi 	/*
109074079a53SRobert Mustacchi 	 * The values of width are the same on IVY->SKX, but the bits are
109174079a53SRobert Mustacchi 	 * different. This doesn't exist on SNB.
109274079a53SRobert Mustacchi 	 */
109374079a53SRobert Mustacchi 	if (imc->imc_gen > IMC_GEN_SANDY) {
109474079a53SRobert Mustacchi 		uint8_t width;
109574079a53SRobert Mustacchi 
109674079a53SRobert Mustacchi 		if (imc->imc_gen >= IMC_GEN_BROADWELL) {
109774079a53SRobert Mustacchi 			width = IMC_MTR_WIDTH_BRD_SKX(mtr);
109874079a53SRobert Mustacchi 		} else {
109974079a53SRobert Mustacchi 			width = IMC_MTR_WIDTH_IVB_HAS(mtr);
110074079a53SRobert Mustacchi 		}
110174079a53SRobert Mustacchi 		switch (width) {
110274079a53SRobert Mustacchi 		case 0:
110374079a53SRobert Mustacchi 			dimm->idimm_width = 4;
110474079a53SRobert Mustacchi 			break;
110574079a53SRobert Mustacchi 		case 1:
110674079a53SRobert Mustacchi 			dimm->idimm_width = 8;
110774079a53SRobert Mustacchi 			break;
110874079a53SRobert Mustacchi 		case 2:
110974079a53SRobert Mustacchi 			dimm->idimm_width = 16;
111074079a53SRobert Mustacchi 			break;
111174079a53SRobert Mustacchi 		default:
111274079a53SRobert Mustacchi 			dimm->idimm_width = 0;
111374079a53SRobert Mustacchi 			dimm->idimm_valid |= IMC_DIMM_V_BAD_WIDTH;
111474079a53SRobert Mustacchi 			break;
111574079a53SRobert Mustacchi 		}
111674079a53SRobert Mustacchi 	} else {
111774079a53SRobert Mustacchi 		dimm->idimm_width = 0;
111874079a53SRobert Mustacchi 	}
111974079a53SRobert Mustacchi 
112074079a53SRobert Mustacchi 	dimm->idimm_nranks = 1 << IMC_MTR_DDR_RANKS(mtr);
112174079a53SRobert Mustacchi 	switch (imc->imc_gen) {
112274079a53SRobert Mustacchi 	case IMC_GEN_HASWELL:
112374079a53SRobert Mustacchi 	case IMC_GEN_BROADWELL:
112474079a53SRobert Mustacchi 	case IMC_GEN_SKYLAKE:
112574079a53SRobert Mustacchi 		if (dimm->idimm_nranks > IMC_MTR_DDR_RANKS_MAX_HAS_SKX) {
112674079a53SRobert Mustacchi 			dimm->idimm_nranks = 0;
112774079a53SRobert Mustacchi 			dimm->idimm_valid |= IMC_DIMM_V_BAD_RANKS;
112874079a53SRobert Mustacchi 		}
112974079a53SRobert Mustacchi 		break;
113074079a53SRobert Mustacchi 	default:
113174079a53SRobert Mustacchi 		if (dimm->idimm_nranks > IMC_MTR_DDR_RANKS_MAX) {
113274079a53SRobert Mustacchi 			dimm->idimm_nranks = 0;
113374079a53SRobert Mustacchi 			dimm->idimm_valid |= IMC_DIMM_V_BAD_RANKS;
113474079a53SRobert Mustacchi 		}
113574079a53SRobert Mustacchi 	}
113674079a53SRobert Mustacchi 
113774079a53SRobert Mustacchi 	disable = IMC_MTR_RANK_DISABLE(mtr);
113874079a53SRobert Mustacchi 	dimm->idimm_ranks_disabled[0] = (disable & 0x1) != 0;
113974079a53SRobert Mustacchi 	dimm->idimm_ranks_disabled[1] = (disable & 0x2) != 0;
114074079a53SRobert Mustacchi 	dimm->idimm_ranks_disabled[2] = (disable & 0x4) != 0;
114174079a53SRobert Mustacchi 	dimm->idimm_ranks_disabled[3] = (disable & 0x8) != 0;
114274079a53SRobert Mustacchi 
114374079a53SRobert Mustacchi 	/*
114474079a53SRobert Mustacchi 	 * Only Haswell and later have this information.
114574079a53SRobert Mustacchi 	 */
114674079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_HASWELL) {
114774079a53SRobert Mustacchi 		dimm->idimm_hdrl = IMC_MTR_HDRL_HAS_SKX(mtr) != 0;
114874079a53SRobert Mustacchi 		dimm->idimm_hdrl_parity = IMC_MTR_HDRL_PARITY_HAS_SKX(mtr) != 0;
114974079a53SRobert Mustacchi 		dimm->idimm_3dsranks = IMC_MTR_3DSRANKS_HAS_SKX(mtr);
115074079a53SRobert Mustacchi 		if (dimm->idimm_3dsranks != 0) {
115174079a53SRobert Mustacchi 			dimm->idimm_3dsranks = 1 << dimm->idimm_3dsranks;
115274079a53SRobert Mustacchi 		}
115374079a53SRobert Mustacchi 	}
115474079a53SRobert Mustacchi 
115574079a53SRobert Mustacchi 
115674079a53SRobert Mustacchi 	if (icn->icn_dimm_type == IMC_DIMM_DDR4) {
115774079a53SRobert Mustacchi 		dimm->idimm_nbanks = 16;
115874079a53SRobert Mustacchi 	} else {
115974079a53SRobert Mustacchi 		dimm->idimm_nbanks = 8;
116074079a53SRobert Mustacchi 	}
116174079a53SRobert Mustacchi 
116274079a53SRobert Mustacchi 	/*
116374079a53SRobert Mustacchi 	 * To calculate the DIMM size we need first take the number of rows and
116474079a53SRobert Mustacchi 	 * columns. This gives us the number of slots per chip. In a given rank
116574079a53SRobert Mustacchi 	 * there are nbanks of these. There are nrank entries of those. Each of
116674079a53SRobert Mustacchi 	 * these slots can fit a byte.
116774079a53SRobert Mustacchi 	 */
116874079a53SRobert Mustacchi 	dimm->idimm_size = dimm->idimm_nbanks * dimm->idimm_nranks * 8 *
116974079a53SRobert Mustacchi 	    (1ULL << (dimm->idimm_ncolumns + dimm->idimm_nrows));
117074079a53SRobert Mustacchi }
117174079a53SRobert Mustacchi 
117274079a53SRobert Mustacchi static void
imc_fill_dimms(imc_t * imc,imc_mc_t * icn,imc_channel_t * chan)117374079a53SRobert Mustacchi imc_fill_dimms(imc_t *imc, imc_mc_t *icn, imc_channel_t *chan)
117474079a53SRobert Mustacchi {
117574079a53SRobert Mustacchi 	uint_t i;
117674079a53SRobert Mustacchi 
117774079a53SRobert Mustacchi 	/*
117874079a53SRobert Mustacchi 	 * There's one register for each DIMM that might be present, we always
117974079a53SRobert Mustacchi 	 * read that information to determine information about the DIMMs.
118074079a53SRobert Mustacchi 	 */
118174079a53SRobert Mustacchi 	chan->ich_ndimms = imc->imc_gen_data->igd_max_dimms;
118274079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_gen_data->igd_max_dimms; i++) {
118374079a53SRobert Mustacchi 		uint32_t mtr;
118474079a53SRobert Mustacchi 		imc_dimm_t *dimm = &chan->ich_dimms[i];
118574079a53SRobert Mustacchi 
118674079a53SRobert Mustacchi 		bzero(dimm, sizeof (imc_dimm_t));
118774079a53SRobert Mustacchi 		mtr = pci_config_get32(chan->ich_desc->istub_cfgspace,
118874079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_mtr_offsets[i]);
118974079a53SRobert Mustacchi 		dimm->idimm_mtr = mtr;
119074079a53SRobert Mustacchi 		/*
119174079a53SRobert Mustacchi 		 * We don't really expect to get a bad PCIe read. However, if we
119274079a53SRobert Mustacchi 		 * do, treat that for the moment as though the DIMM is bad.
119374079a53SRobert Mustacchi 		 */
119474079a53SRobert Mustacchi 		if (mtr == PCI_EINVAL32) {
119574079a53SRobert Mustacchi 			dimm->idimm_valid |= IMC_DIMM_V_BAD_PCI_READ;
119674079a53SRobert Mustacchi 			continue;
119774079a53SRobert Mustacchi 		}
119874079a53SRobert Mustacchi 
119974079a53SRobert Mustacchi 		imc_decode_mtr(imc, icn, dimm, mtr);
120074079a53SRobert Mustacchi 	}
120174079a53SRobert Mustacchi }
120274079a53SRobert Mustacchi 
120374079a53SRobert Mustacchi static boolean_t
imc_fill_controller(imc_t * imc,imc_mc_t * icn)120474079a53SRobert Mustacchi imc_fill_controller(imc_t *imc, imc_mc_t *icn)
120574079a53SRobert Mustacchi {
120674079a53SRobert Mustacchi 	uint32_t mcmtr;
120774079a53SRobert Mustacchi 
120874079a53SRobert Mustacchi 	mcmtr = pci_config_get32(icn->icn_main0->istub_cfgspace,
120974079a53SRobert Mustacchi 	    imc->imc_gen_data->igd_mcmtr_offset);
121074079a53SRobert Mustacchi 	if (mcmtr == PCI_EINVAL32) {
121174079a53SRobert Mustacchi 		icn->icn_invalid = B_TRUE;
121274079a53SRobert Mustacchi 		return (B_FALSE);
121374079a53SRobert Mustacchi 	}
121474079a53SRobert Mustacchi 
121574079a53SRobert Mustacchi 	icn->icn_closed = IMC_MCMTR_CLOSED_PAGE(mcmtr) != 0;
121674079a53SRobert Mustacchi 	if (imc->imc_gen < IMC_GEN_SKYLAKE) {
121774079a53SRobert Mustacchi 		icn->icn_lockstep = IMC_MCMTR_LOCKSTEP(mcmtr) != 0;
121874079a53SRobert Mustacchi 	} else {
121974079a53SRobert Mustacchi 		icn->icn_lockstep = B_FALSE;
122074079a53SRobert Mustacchi 	}
122174079a53SRobert Mustacchi 
122274079a53SRobert Mustacchi 	icn->icn_ecc = IMC_MCMTR_ECC_ENABLED(mcmtr) != 0;
122374079a53SRobert Mustacchi 
122474079a53SRobert Mustacchi 	/*
122574079a53SRobert Mustacchi 	 * SNB and IVB only support DDR3. Haswell and Broadwell may support
122674079a53SRobert Mustacchi 	 * DDR4, depends on the SKU. Skylake only supports DDR4.
122774079a53SRobert Mustacchi 	 */
122874079a53SRobert Mustacchi 	switch (imc->imc_gen) {
122974079a53SRobert Mustacchi 	case IMC_GEN_SANDY:
123074079a53SRobert Mustacchi 	case IMC_GEN_IVY:
123174079a53SRobert Mustacchi 		icn->icn_dimm_type = IMC_DIMM_DDR3;
123274079a53SRobert Mustacchi 		break;
123374079a53SRobert Mustacchi 	case IMC_GEN_HASWELL:
123474079a53SRobert Mustacchi 	case IMC_GEN_BROADWELL:
123574079a53SRobert Mustacchi 		if (IMC_MCMTR_DDR4_HAS_BRD(mcmtr)) {
123674079a53SRobert Mustacchi 			icn->icn_dimm_type = IMC_DIMM_DDR4;
123774079a53SRobert Mustacchi 		} else {
123874079a53SRobert Mustacchi 			icn->icn_dimm_type = IMC_DIMM_DDR3;
123974079a53SRobert Mustacchi 		}
124074079a53SRobert Mustacchi 		break;
124174079a53SRobert Mustacchi 	default:
124274079a53SRobert Mustacchi 		/*
124374079a53SRobert Mustacchi 		 * Skylake and on are all DDR4.
124474079a53SRobert Mustacchi 		 */
124574079a53SRobert Mustacchi 		icn->icn_dimm_type = IMC_DIMM_DDR4;
124674079a53SRobert Mustacchi 		break;
124774079a53SRobert Mustacchi 	}
124874079a53SRobert Mustacchi 
124974079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_SKYLAKE && icn->icn_m2m != NULL) {
125074079a53SRobert Mustacchi 		icn->icn_topo = pci_config_get32(icn->icn_m2m->istub_cfgspace,
125174079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_topo_offset);
125274079a53SRobert Mustacchi 	}
125374079a53SRobert Mustacchi 
125474079a53SRobert Mustacchi 	return (B_TRUE);
125574079a53SRobert Mustacchi }
125674079a53SRobert Mustacchi 
125774079a53SRobert Mustacchi /*
125874079a53SRobert Mustacchi  * Walk the IMC data and fill in the information on DIMMs and the memory
125974079a53SRobert Mustacchi  * controller configurations.
126074079a53SRobert Mustacchi  */
126174079a53SRobert Mustacchi static void
imc_fill_data(imc_t * imc)126274079a53SRobert Mustacchi imc_fill_data(imc_t *imc)
126374079a53SRobert Mustacchi {
126474079a53SRobert Mustacchi 	uint_t csock, cmc, cchan;
126574079a53SRobert Mustacchi 
126674079a53SRobert Mustacchi 	for (csock = 0; csock < imc->imc_nsockets; csock++) {
126774079a53SRobert Mustacchi 		imc_socket_t *sock = &imc->imc_sockets[csock];
126874079a53SRobert Mustacchi 
126974079a53SRobert Mustacchi 		for (cmc = 0; cmc < sock->isock_nimc; cmc++) {
127074079a53SRobert Mustacchi 			imc_mc_t *icn = &sock->isock_imcs[cmc];
127174079a53SRobert Mustacchi 
127274079a53SRobert Mustacchi 			if (!imc_fill_controller(imc, icn))
127374079a53SRobert Mustacchi 				continue;
127474079a53SRobert Mustacchi 
127574079a53SRobert Mustacchi 			for (cchan = 0; cchan < icn->icn_nchannels; cchan++) {
127674079a53SRobert Mustacchi 				imc_fill_dimms(imc, icn,
127774079a53SRobert Mustacchi 				    &icn->icn_channels[cchan]);
127874079a53SRobert Mustacchi 			}
127974079a53SRobert Mustacchi 		}
128074079a53SRobert Mustacchi 	}
128174079a53SRobert Mustacchi }
128274079a53SRobert Mustacchi 
128374079a53SRobert Mustacchi static nvlist_t *
imc_nvl_create_dimm(imc_t * imc,imc_dimm_t * dimm)128474079a53SRobert Mustacchi imc_nvl_create_dimm(imc_t *imc, imc_dimm_t *dimm)
128574079a53SRobert Mustacchi {
128674079a53SRobert Mustacchi 	nvlist_t *nvl;
128774079a53SRobert Mustacchi 
128874079a53SRobert Mustacchi 	nvl = fnvlist_alloc();
128974079a53SRobert Mustacchi 	fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_DIMM_PRESENT,
129074079a53SRobert Mustacchi 	    dimm->idimm_present);
129174079a53SRobert Mustacchi 	if (!dimm->idimm_present) {
129274079a53SRobert Mustacchi 		return (nvl);
129374079a53SRobert Mustacchi 	}
129474079a53SRobert Mustacchi 
129574079a53SRobert Mustacchi 	fnvlist_add_uint64(nvl, MCINTEL_NVLIST_V1_DIMM_SIZE, dimm->idimm_size);
129674079a53SRobert Mustacchi 	fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS,
129774079a53SRobert Mustacchi 	    dimm->idimm_ncolumns);
129874079a53SRobert Mustacchi 	fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_NROWS,
129974079a53SRobert Mustacchi 	    dimm->idimm_nrows);
130074079a53SRobert Mustacchi 
130174079a53SRobert Mustacchi 	if (imc->imc_gen > IMC_GEN_SANDY) {
130274079a53SRobert Mustacchi 		fnvlist_add_uint64(nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY,
130374079a53SRobert Mustacchi 		    dimm->idimm_density * (1ULL << 30));
130474079a53SRobert Mustacchi 		fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH,
130574079a53SRobert Mustacchi 		    dimm->idimm_width);
130674079a53SRobert Mustacchi 	}
130774079a53SRobert Mustacchi 	fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_RANKS,
130874079a53SRobert Mustacchi 	    dimm->idimm_nranks);
130974079a53SRobert Mustacchi 	fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_BANKS,
131074079a53SRobert Mustacchi 	    dimm->idimm_nbanks);
131174079a53SRobert Mustacchi 	fnvlist_add_boolean_array(nvl, MCINTEL_NVLIST_V1_DIMM_RDIS,
131274079a53SRobert Mustacchi 	    dimm->idimm_ranks_disabled, IMC_MAX_RANK_DISABLE);
131374079a53SRobert Mustacchi 
131474079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_HASWELL) {
131574079a53SRobert Mustacchi 		fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_DIMM_HDRL,
131674079a53SRobert Mustacchi 		    dimm->idimm_hdrl);
131774079a53SRobert Mustacchi 		fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_DIMM_HDRLP,
131874079a53SRobert Mustacchi 		    dimm->idimm_hdrl_parity);
131974079a53SRobert Mustacchi 		if (dimm->idimm_3dsranks > 0) {
132074079a53SRobert Mustacchi 			fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_DIMM_3DRANK,
132174079a53SRobert Mustacchi 			    dimm->idimm_3dsranks);
132274079a53SRobert Mustacchi 		}
132374079a53SRobert Mustacchi 	}
132474079a53SRobert Mustacchi 
132574079a53SRobert Mustacchi 	return (nvl);
132674079a53SRobert Mustacchi }
132774079a53SRobert Mustacchi 
132874079a53SRobert Mustacchi static nvlist_t *
imc_nvl_create_channel(imc_t * imc,imc_channel_t * chan)132974079a53SRobert Mustacchi imc_nvl_create_channel(imc_t *imc, imc_channel_t *chan)
133074079a53SRobert Mustacchi {
133174079a53SRobert Mustacchi 	nvlist_t *nvl;
133274079a53SRobert Mustacchi 	nvlist_t *dimms[IMC_MAX_DIMMPERCHAN];
133374079a53SRobert Mustacchi 	uint_t i;
133474079a53SRobert Mustacchi 
133574079a53SRobert Mustacchi 	nvl = fnvlist_alloc();
133674079a53SRobert Mustacchi 	fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_CHAN_NDPC,
133774079a53SRobert Mustacchi 	    imc->imc_gen_data->igd_max_dimms);
133874079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_gen_data->igd_max_dimms; i++) {
133974079a53SRobert Mustacchi 		dimms[i] = imc_nvl_create_dimm(imc, &chan->ich_dimms[i]);
134074079a53SRobert Mustacchi 	}
134174079a53SRobert Mustacchi 
134274079a53SRobert Mustacchi 	fnvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS,
134374079a53SRobert Mustacchi 	    dimms, i);
134474079a53SRobert Mustacchi 
134574079a53SRobert Mustacchi 	for (; i > 0; i--) {
134674079a53SRobert Mustacchi 		nvlist_free(dimms[i-1]);
134774079a53SRobert Mustacchi 	}
134874079a53SRobert Mustacchi 
134974079a53SRobert Mustacchi 	return (nvl);
135074079a53SRobert Mustacchi }
135174079a53SRobert Mustacchi 
135274079a53SRobert Mustacchi static nvlist_t *
imc_nvl_create_mc(imc_t * imc,imc_mc_t * icn)135374079a53SRobert Mustacchi imc_nvl_create_mc(imc_t *imc, imc_mc_t *icn)
135474079a53SRobert Mustacchi {
135574079a53SRobert Mustacchi 	nvlist_t *nvl;
135674079a53SRobert Mustacchi 	nvlist_t *channels[IMC_MAX_CHANPERMC];
135774079a53SRobert Mustacchi 	uint_t i;
135874079a53SRobert Mustacchi 
135974079a53SRobert Mustacchi 	nvl = fnvlist_alloc();
136074079a53SRobert Mustacchi 	fnvlist_add_uint32(nvl, MCINTEL_NVLIST_V1_MC_NCHAN, icn->icn_nchannels);
136174079a53SRobert Mustacchi 	fnvlist_add_boolean_value(nvl, MCINTEL_NVLIST_V1_MC_ECC,
136274079a53SRobert Mustacchi 	    icn->icn_ecc);
136374079a53SRobert Mustacchi 	if (icn->icn_lockstep) {
136474079a53SRobert Mustacchi 		fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE,
136574079a53SRobert Mustacchi 		    MCINTEL_NVLIST_V1_MC_CHAN_MODE_LOCK);
136674079a53SRobert Mustacchi 	} else {
136774079a53SRobert Mustacchi 		fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE,
136874079a53SRobert Mustacchi 		    MCINTEL_NVLIST_V1_MC_CHAN_MODE_INDEP);
136974079a53SRobert Mustacchi 
137074079a53SRobert Mustacchi 	}
137174079a53SRobert Mustacchi 
137274079a53SRobert Mustacchi 	if (icn->icn_closed) {
137374079a53SRobert Mustacchi 		fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_POLICY,
137474079a53SRobert Mustacchi 		    MCINTEL_NVLIST_V1_MC_POLICY_CLOSED);
137574079a53SRobert Mustacchi 	} else {
137674079a53SRobert Mustacchi 		fnvlist_add_string(nvl, MCINTEL_NVLIST_V1_MC_POLICY,
137774079a53SRobert Mustacchi 		    MCINTEL_NVLIST_V1_MC_POLICY_OPEN);
137874079a53SRobert Mustacchi 	}
137974079a53SRobert Mustacchi 
138074079a53SRobert Mustacchi 	for (i = 0; i < icn->icn_nchannels; i++) {
138174079a53SRobert Mustacchi 		channels[i] = imc_nvl_create_channel(imc,
138274079a53SRobert Mustacchi 		    &icn->icn_channels[i]);
138374079a53SRobert Mustacchi 	}
138474079a53SRobert Mustacchi 	fnvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_V1_MC_CHANNELS,
138574079a53SRobert Mustacchi 	    channels, icn->icn_nchannels);
138674079a53SRobert Mustacchi 	for (i = 0; i < icn->icn_nchannels; i++) {
138774079a53SRobert Mustacchi 		nvlist_free(channels[i]);
138874079a53SRobert Mustacchi 	}
138974079a53SRobert Mustacchi 
139074079a53SRobert Mustacchi 	return (nvl);
139174079a53SRobert Mustacchi }
139274079a53SRobert Mustacchi 
139374079a53SRobert Mustacchi static void
imc_nvl_pack(imc_socket_t * sock,boolean_t sleep)139474079a53SRobert Mustacchi imc_nvl_pack(imc_socket_t *sock, boolean_t sleep)
139574079a53SRobert Mustacchi {
139674079a53SRobert Mustacchi 	char *buf = NULL;
139774079a53SRobert Mustacchi 	size_t len = 0;
139874079a53SRobert Mustacchi 	int kmflag;
139974079a53SRobert Mustacchi 
140074079a53SRobert Mustacchi 	if (sock->isock_nvl == NULL)
140174079a53SRobert Mustacchi 		return;
140274079a53SRobert Mustacchi 
140374079a53SRobert Mustacchi 	if (sock->isock_buf != NULL)
140474079a53SRobert Mustacchi 		return;
140574079a53SRobert Mustacchi 
140674079a53SRobert Mustacchi 	if (sleep) {
140774079a53SRobert Mustacchi 		kmflag = KM_SLEEP;
140874079a53SRobert Mustacchi 	} else {
1409ca783257SDan McDonald 		kmflag = KM_NOSLEEP_LAZY;
141074079a53SRobert Mustacchi 	}
141174079a53SRobert Mustacchi 
141274079a53SRobert Mustacchi 	if (nvlist_pack(sock->isock_nvl, &buf, &len, NV_ENCODE_XDR,
141374079a53SRobert Mustacchi 	    kmflag) != 0) {
141474079a53SRobert Mustacchi 		return;
141574079a53SRobert Mustacchi 	}
141674079a53SRobert Mustacchi 
141774079a53SRobert Mustacchi 	sock->isock_buf = buf;
141874079a53SRobert Mustacchi 	sock->isock_buflen = len;
141974079a53SRobert Mustacchi 	sock->isock_gen++;
142074079a53SRobert Mustacchi }
142174079a53SRobert Mustacchi 
142274079a53SRobert Mustacchi static void
imc_decoder_pack(imc_t * imc)142374079a53SRobert Mustacchi imc_decoder_pack(imc_t *imc)
142474079a53SRobert Mustacchi {
142574079a53SRobert Mustacchi 	char *buf = NULL;
142674079a53SRobert Mustacchi 	size_t len = 0;
142774079a53SRobert Mustacchi 
142874079a53SRobert Mustacchi 	if (imc->imc_decoder_buf != NULL)
142974079a53SRobert Mustacchi 		return;
143074079a53SRobert Mustacchi 
143174079a53SRobert Mustacchi 	if (imc->imc_decoder_dump == NULL) {
143274079a53SRobert Mustacchi 		imc->imc_decoder_dump = imc_dump_decoder(imc);
143374079a53SRobert Mustacchi 	}
143474079a53SRobert Mustacchi 
143574079a53SRobert Mustacchi 	if (nvlist_pack(imc->imc_decoder_dump, &buf, &len, NV_ENCODE_XDR,
1436ca783257SDan McDonald 	    KM_NOSLEEP_LAZY) != 0) {
143774079a53SRobert Mustacchi 		return;
143874079a53SRobert Mustacchi 	}
143974079a53SRobert Mustacchi 
144074079a53SRobert Mustacchi 	imc->imc_decoder_buf = buf;
144174079a53SRobert Mustacchi 	imc->imc_decoder_len = len;
144274079a53SRobert Mustacchi }
144374079a53SRobert Mustacchi 
144474079a53SRobert Mustacchi static void
imc_nvl_create(imc_t * imc)144574079a53SRobert Mustacchi imc_nvl_create(imc_t *imc)
144674079a53SRobert Mustacchi {
144774079a53SRobert Mustacchi 	uint_t csock;
144874079a53SRobert Mustacchi 	for (csock = 0; csock < imc->imc_nsockets; csock++) {
144974079a53SRobert Mustacchi 		uint_t i;
145074079a53SRobert Mustacchi 		nvlist_t *nvl;
145174079a53SRobert Mustacchi 		nvlist_t *mcs[IMC_MAX_IMCPERSOCK];
145274079a53SRobert Mustacchi 		imc_socket_t *sock = &imc->imc_sockets[csock];
145374079a53SRobert Mustacchi 
145474079a53SRobert Mustacchi 		nvl = fnvlist_alloc();
145574079a53SRobert Mustacchi 		fnvlist_add_uint8(nvl, MCINTEL_NVLIST_VERSTR,
145674079a53SRobert Mustacchi 		    MCINTEL_NVLIST_VERS1);
145774079a53SRobert Mustacchi 		fnvlist_add_uint8(nvl, MCINTEL_NVLIST_V1_NMC,
145874079a53SRobert Mustacchi 		    sock->isock_nimc);
145974079a53SRobert Mustacchi 
146074079a53SRobert Mustacchi 		for (i = 0; i < sock->isock_nimc; i++) {
146174079a53SRobert Mustacchi 			mcs[i] = imc_nvl_create_mc(imc, &sock->isock_imcs[i]);
146274079a53SRobert Mustacchi 		}
146374079a53SRobert Mustacchi 
146474079a53SRobert Mustacchi 		fnvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS,
146574079a53SRobert Mustacchi 		    mcs, sock->isock_nimc);
146674079a53SRobert Mustacchi 
146774079a53SRobert Mustacchi 		for (i = 0; i < sock->isock_nimc; i++) {
146874079a53SRobert Mustacchi 			nvlist_free(mcs[i]);
146974079a53SRobert Mustacchi 		}
147074079a53SRobert Mustacchi 
147174079a53SRobert Mustacchi 		sock->isock_nvl = nvl;
147274079a53SRobert Mustacchi 		imc_nvl_pack(sock, B_TRUE);
147374079a53SRobert Mustacchi 	}
147474079a53SRobert Mustacchi }
147574079a53SRobert Mustacchi 
147674079a53SRobert Mustacchi /*
147774079a53SRobert Mustacchi  * Determine the top of low and high memory. These determine whether transaction
147874079a53SRobert Mustacchi  * addresses target main memory or not. Unfortunately, the way that these are
147974079a53SRobert Mustacchi  * stored and fetched changes with different generations.
148074079a53SRobert Mustacchi  */
148174079a53SRobert Mustacchi static void
imc_sad_read_tohm(imc_t * imc,imc_sad_t * sad)148274079a53SRobert Mustacchi imc_sad_read_tohm(imc_t *imc, imc_sad_t *sad)
148374079a53SRobert Mustacchi {
148474079a53SRobert Mustacchi 	uint32_t tolm, tohm_low, tohm_hi;
148574079a53SRobert Mustacchi 
148674079a53SRobert Mustacchi 	tolm = pci_config_get32(sad->isad_tolh->istub_cfgspace,
148774079a53SRobert Mustacchi 	    imc->imc_gen_data->igd_tolm_offset);
148874079a53SRobert Mustacchi 	tohm_low = pci_config_get32(sad->isad_tolh->istub_cfgspace,
148974079a53SRobert Mustacchi 	    imc->imc_gen_data->igd_tohm_low_offset);
149074079a53SRobert Mustacchi 	if (imc->imc_gen_data->igd_tohm_hi_offset != 0) {
149174079a53SRobert Mustacchi 		tohm_hi = pci_config_get32(sad->isad_tolh->istub_cfgspace,
149274079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_tohm_hi_offset);
149374079a53SRobert Mustacchi 	} else {
149474079a53SRobert Mustacchi 		tohm_hi = 0;
149574079a53SRobert Mustacchi 	}
149674079a53SRobert Mustacchi 
149774079a53SRobert Mustacchi 	if (tolm == PCI_EINVAL32 || tohm_low == PCI_EINVAL32 ||
149874079a53SRobert Mustacchi 	    tohm_hi == PCI_EINVAL32) {
149974079a53SRobert Mustacchi 		sad->isad_valid |= IMC_SAD_V_BAD_PCI_READ;
150074079a53SRobert Mustacchi 		return;
150174079a53SRobert Mustacchi 	}
150274079a53SRobert Mustacchi 
150374079a53SRobert Mustacchi 	switch (imc->imc_gen) {
150474079a53SRobert Mustacchi 	case IMC_GEN_SANDY:
150574079a53SRobert Mustacchi 	case IMC_GEN_IVY:
150674079a53SRobert Mustacchi 		sad->isad_tolm = ((uint64_t)tolm & IMC_TOLM_SNB_IVY_MASK) <<
150774079a53SRobert Mustacchi 		    IMC_TOLM_SNB_IVY_SHIFT;
150874079a53SRobert Mustacchi 		sad->isad_tohm = ((uint64_t)tohm_low & IMC_TOHM_SNB_IVY_MASK) <<
150974079a53SRobert Mustacchi 		    IMC_TOLM_SNB_IVY_SHIFT;
151074079a53SRobert Mustacchi 		break;
151174079a53SRobert Mustacchi 	case IMC_GEN_HASWELL:
151274079a53SRobert Mustacchi 	case IMC_GEN_BROADWELL:
151374079a53SRobert Mustacchi 	case IMC_GEN_SKYLAKE:
151474079a53SRobert Mustacchi 		sad->isad_tolm = (uint64_t)tolm & IMC_TOLM_HAS_SKX_MASK;
151574079a53SRobert Mustacchi 		sad->isad_tohm = ((uint64_t)tohm_low &
151674079a53SRobert Mustacchi 		    IMC_TOHM_LOW_HAS_SKX_MASK) | ((uint64_t)tohm_hi << 32);
151774079a53SRobert Mustacchi 
151874079a53SRobert Mustacchi 		/*
151974079a53SRobert Mustacchi 		 * Adjust the values to turn them into an exclusive range.
152074079a53SRobert Mustacchi 		 */
152174079a53SRobert Mustacchi 		sad->isad_tolm += IMC_TOLM_HAS_SKY_EXCL;
152274079a53SRobert Mustacchi 		sad->isad_tohm += IMC_TOHM_HAS_SKY_EXCL;
152374079a53SRobert Mustacchi 		break;
152474079a53SRobert Mustacchi 	default:
152574079a53SRobert Mustacchi 		dev_err(imc->imc_dip, CE_PANIC, "imc driver programmer error: "
152674079a53SRobert Mustacchi 		    "set to unknown generation: %u", imc->imc_gen);
152774079a53SRobert Mustacchi 		return;
152874079a53SRobert Mustacchi 	}
152974079a53SRobert Mustacchi }
153074079a53SRobert Mustacchi 
153174079a53SRobert Mustacchi static void
imc_sad_fill_rule(imc_t * imc,imc_sad_t * sad,imc_sad_rule_t * rule,uint32_t raw)153274079a53SRobert Mustacchi imc_sad_fill_rule(imc_t *imc, imc_sad_t *sad, imc_sad_rule_t *rule,
153374079a53SRobert Mustacchi     uint32_t raw)
153474079a53SRobert Mustacchi {
153574079a53SRobert Mustacchi 	uint_t attr;
153674079a53SRobert Mustacchi 	uint64_t limit;
153774079a53SRobert Mustacchi 	bzero(rule, sizeof (imc_sad_rule_t));
153874079a53SRobert Mustacchi 
153974079a53SRobert Mustacchi 	rule->isr_raw_dram = raw;
154074079a53SRobert Mustacchi 	rule->isr_enable = IMC_SAD_DRAM_RULE_ENABLE(raw) != 0;
154174079a53SRobert Mustacchi 	if (imc->imc_gen < IMC_GEN_SKYLAKE) {
154274079a53SRobert Mustacchi 		switch (IMC_SAD_DRAM_INTERLEAVE_SNB_BRD(raw)) {
154374079a53SRobert Mustacchi 		case IMC_SAD_DRAM_INTERLEAVE_SNB_BRD_8t6:
154474079a53SRobert Mustacchi 			rule->isr_imode = IMC_SAD_IMODE_8t6;
154574079a53SRobert Mustacchi 			break;
154674079a53SRobert Mustacchi 		case IMC_SAD_DRAM_INTERLEAVE_SNB_BRD_8t6XOR:
154774079a53SRobert Mustacchi 			rule->isr_imode = IMC_SAD_IMODE_8t6XOR;
154874079a53SRobert Mustacchi 			break;
154974079a53SRobert Mustacchi 		}
155074079a53SRobert Mustacchi 	} else {
155174079a53SRobert Mustacchi 		switch (IMC_SAD_DRAM_INTERLEAVE_SKX(raw)) {
155274079a53SRobert Mustacchi 		case IMC_SAD_DRAM_INTERLEAVE_SKX_8t6:
155374079a53SRobert Mustacchi 			rule->isr_imode = IMC_SAD_IMODE_8t6;
155474079a53SRobert Mustacchi 			break;
155574079a53SRobert Mustacchi 		case IMC_SAD_DRAM_INTERLEAVE_SKX_10t8:
155674079a53SRobert Mustacchi 			rule->isr_imode = IMC_SAD_IMODE_10t8;
155774079a53SRobert Mustacchi 			break;
155874079a53SRobert Mustacchi 		case IMC_SAD_DRAM_INTERLEAVE_SKX_14t12:
155974079a53SRobert Mustacchi 			rule->isr_imode = IMC_SAD_IMODE_14t12;
156074079a53SRobert Mustacchi 			break;
156174079a53SRobert Mustacchi 		case IMC_SAD_DRAM_INTERLEAVE_SKX_32t30:
156274079a53SRobert Mustacchi 			rule->isr_imode = IMC_SAD_IMODE_32t30;
156374079a53SRobert Mustacchi 			break;
156474079a53SRobert Mustacchi 		}
156574079a53SRobert Mustacchi 	}
156674079a53SRobert Mustacchi 
156774079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
156874079a53SRobert Mustacchi 		attr = IMC_SAD_DRAM_ATTR_SKX(raw);
156974079a53SRobert Mustacchi 	} else {
157074079a53SRobert Mustacchi 		attr = IMC_SAD_DRAM_ATTR_SNB_BRD(raw);
157174079a53SRobert Mustacchi 	}
157274079a53SRobert Mustacchi 
157374079a53SRobert Mustacchi 	switch (attr) {
157474079a53SRobert Mustacchi 	case IMC_SAD_DRAM_ATTR_DRAM:
157574079a53SRobert Mustacchi 		rule->isr_type = IMC_SAD_TYPE_DRAM;
157674079a53SRobert Mustacchi 		break;
157774079a53SRobert Mustacchi 	case IMC_SAD_DRAM_ATTR_MMCFG:
157874079a53SRobert Mustacchi 		rule->isr_type = IMC_SAD_TYPE_MMCFG;
157974079a53SRobert Mustacchi 		break;
158074079a53SRobert Mustacchi 	case IMC_SAD_DRAM_ATTR_NXM:
158174079a53SRobert Mustacchi 		if (imc->imc_gen < IMC_GEN_SKYLAKE) {
158274079a53SRobert Mustacchi 			sad->isad_valid |= IMC_SAD_V_BAD_DRAM_ATTR;
158374079a53SRobert Mustacchi 		}
158474079a53SRobert Mustacchi 		rule->isr_type = IMC_SAD_TYPE_NXM;
158574079a53SRobert Mustacchi 		break;
158674079a53SRobert Mustacchi 	default:
158774079a53SRobert Mustacchi 		sad->isad_valid |= IMC_SAD_V_BAD_DRAM_ATTR;
158874079a53SRobert Mustacchi 		break;
158974079a53SRobert Mustacchi 	}
159074079a53SRobert Mustacchi 
159174079a53SRobert Mustacchi 	/*
159274079a53SRobert Mustacchi 	 * Fetch the limit which represents bits 45:26 and then adjust this so
159374079a53SRobert Mustacchi 	 * that it is exclusive.
159474079a53SRobert Mustacchi 	 */
159574079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
159674079a53SRobert Mustacchi 		limit = IMC_SAD_DRAM_LIMIT_SKX(raw);
159774079a53SRobert Mustacchi 	} else {
159874079a53SRobert Mustacchi 		limit = IMC_SAD_DRAM_LIMIT_SNB_BRD(raw);
159974079a53SRobert Mustacchi 	}
160074079a53SRobert Mustacchi 	rule->isr_limit = (limit << IMC_SAD_DRAM_LIMIT_SHIFT) +
160174079a53SRobert Mustacchi 	    IMC_SAD_DRAM_LIMIT_EXCLUSIVE;
160274079a53SRobert Mustacchi 
160374079a53SRobert Mustacchi 	/*
160474079a53SRobert Mustacchi 	 * The rest of this does not apply to Sandy Bridge.
160574079a53SRobert Mustacchi 	 */
160674079a53SRobert Mustacchi 	if (imc->imc_gen == IMC_GEN_SANDY)
160774079a53SRobert Mustacchi 		return;
160874079a53SRobert Mustacchi 
160974079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_IVY && imc->imc_gen < IMC_GEN_SKYLAKE) {
161074079a53SRobert Mustacchi 		rule->isr_a7mode = IMC_SAD_DRAM_A7_IVB_BRD(raw) != 0;
161174079a53SRobert Mustacchi 		return;
161274079a53SRobert Mustacchi 	}
161374079a53SRobert Mustacchi 
161474079a53SRobert Mustacchi 	switch (IMC_SAD_DRAM_MOD23_SKX(raw)) {
161574079a53SRobert Mustacchi 	case IMC_SAD_DRAM_MOD23_MOD3:
161674079a53SRobert Mustacchi 		rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD3;
161774079a53SRobert Mustacchi 		break;
161874079a53SRobert Mustacchi 	case IMC_SAD_DRAM_MOD23_MOD2_C01:
161974079a53SRobert Mustacchi 		rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_01;
162074079a53SRobert Mustacchi 		break;
162174079a53SRobert Mustacchi 	case IMC_SAD_DRAM_MOD23_MOD2_C12:
162274079a53SRobert Mustacchi 		rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_12;
162374079a53SRobert Mustacchi 		break;
162474079a53SRobert Mustacchi 	case IMC_SAD_DRAM_MOD23_MOD2_C02:
162574079a53SRobert Mustacchi 		rule->isr_mod_type = IMC_SAD_MOD_TYPE_MOD2_02;
162674079a53SRobert Mustacchi 		break;
162774079a53SRobert Mustacchi 	}
162874079a53SRobert Mustacchi 
162974079a53SRobert Mustacchi 	rule->isr_need_mod3 = IMC_SAD_DRAM_MOD3_SKX(raw) != 0;
163074079a53SRobert Mustacchi 	switch (IMC_SAD_DRAM_MOD3_SKX(raw)) {
163174079a53SRobert Mustacchi 	case IMC_SAD_DRAM_MOD3_MODE_45t6:
163274079a53SRobert Mustacchi 		rule->isr_mod_mode = IMC_SAD_MOD_MODE_45t6;
163374079a53SRobert Mustacchi 		break;
163474079a53SRobert Mustacchi 	case IMC_SAD_DRAM_MOD3_MODE_45t8:
163574079a53SRobert Mustacchi 		rule->isr_mod_mode = IMC_SAD_MOD_MODE_45t8;
163674079a53SRobert Mustacchi 		break;
163774079a53SRobert Mustacchi 	case IMC_SAD_DRAM_MOD3_MODE_45t12:
163874079a53SRobert Mustacchi 		rule->isr_mod_mode = IMC_SAD_MOD_MODE_45t12;
163974079a53SRobert Mustacchi 		break;
164074079a53SRobert Mustacchi 	default:
164174079a53SRobert Mustacchi 		sad->isad_valid |= IMC_SAD_V_BAD_MOD3;
164274079a53SRobert Mustacchi 		break;
164374079a53SRobert Mustacchi 	}
164474079a53SRobert Mustacchi }
164574079a53SRobert Mustacchi 
164674079a53SRobert Mustacchi static void
imc_sad_fill_rule_interleave(imc_t * imc,imc_sad_rule_t * rule,uint32_t raw)164774079a53SRobert Mustacchi imc_sad_fill_rule_interleave(imc_t *imc, imc_sad_rule_t *rule, uint32_t raw)
164874079a53SRobert Mustacchi {
164974079a53SRobert Mustacchi 	uint_t i;
165074079a53SRobert Mustacchi 	uint32_t mlen, mbase, skipbits, skipafter;
165174079a53SRobert Mustacchi 
165274079a53SRobert Mustacchi 	rule->isr_raw_interleave = raw;
165374079a53SRobert Mustacchi 
165474079a53SRobert Mustacchi 	/*
165574079a53SRobert Mustacchi 	 * Right now all architectures always have the maximum number of SAD
165674079a53SRobert Mustacchi 	 * interleave targets.
165774079a53SRobert Mustacchi 	 */
165874079a53SRobert Mustacchi 	rule->isr_ntargets = IMC_MAX_SAD_INTERLEAVE;
165974079a53SRobert Mustacchi 
166074079a53SRobert Mustacchi 	/*
166174079a53SRobert Mustacchi 	 * Sandy Bridge has a gap in the interleave list due to the fact that it
166274079a53SRobert Mustacchi 	 * uses a smaller length.
166374079a53SRobert Mustacchi 	 */
166474079a53SRobert Mustacchi 	if (imc->imc_gen > IMC_GEN_SANDY) {
166574079a53SRobert Mustacchi 		mlen = IMC_SAD_ILEAVE_IVB_SKX_LEN;
166674079a53SRobert Mustacchi 		mbase = IMC_SAD_ILEAVE_IVB_SKX_MASK;
166774079a53SRobert Mustacchi 		skipbits = skipafter = 0;
166874079a53SRobert Mustacchi 	} else {
166974079a53SRobert Mustacchi 		mlen = IMC_SAD_ILEAVE_SNB_LEN;
167074079a53SRobert Mustacchi 		mbase = IMC_SAD_ILEAVE_SNB_MASK;
167174079a53SRobert Mustacchi 		skipbits = 2;
167274079a53SRobert Mustacchi 		skipafter = 4;
167374079a53SRobert Mustacchi 	}
167474079a53SRobert Mustacchi 
167574079a53SRobert Mustacchi 	for (i = 0; i < rule->isr_ntargets; i++) {
167674079a53SRobert Mustacchi 		uint32_t mask, shift;
167774079a53SRobert Mustacchi 
167874079a53SRobert Mustacchi 		shift = i * mlen;
167974079a53SRobert Mustacchi 		if (i >= skipafter)
168074079a53SRobert Mustacchi 			shift += skipbits;
168174079a53SRobert Mustacchi 		mask = mbase << shift;
168274079a53SRobert Mustacchi 		rule->isr_targets[i] = (raw & mask) >> shift;
168374079a53SRobert Mustacchi 	}
168474079a53SRobert Mustacchi }
168574079a53SRobert Mustacchi 
168674079a53SRobert Mustacchi static void
imc_sad_read_dram_rules(imc_t * imc,imc_sad_t * sad)168774079a53SRobert Mustacchi imc_sad_read_dram_rules(imc_t *imc, imc_sad_t *sad)
168874079a53SRobert Mustacchi {
168974079a53SRobert Mustacchi 	uint_t i;
169074079a53SRobert Mustacchi 	off_t off;
169174079a53SRobert Mustacchi 
169274079a53SRobert Mustacchi 	sad->isad_nrules = imc->imc_gen_data->igd_sad_ndram_rules;
169374079a53SRobert Mustacchi 	for (i = 0, off = imc->imc_gen_data->igd_sad_dram_offset;
169474079a53SRobert Mustacchi 	    i < sad->isad_nrules; i++, off += sizeof (uint64_t)) {
169574079a53SRobert Mustacchi 		uint32_t dram, interleave;
169674079a53SRobert Mustacchi 		imc_sad_rule_t *rule = &sad->isad_rules[i];
169774079a53SRobert Mustacchi 
169874079a53SRobert Mustacchi 		dram = pci_config_get32(sad->isad_dram->istub_cfgspace, off);
169974079a53SRobert Mustacchi 		interleave = pci_config_get32(sad->isad_dram->istub_cfgspace,
170074079a53SRobert Mustacchi 		    off + 4);
170174079a53SRobert Mustacchi 
170274079a53SRobert Mustacchi 		if (dram == PCI_EINVAL32 || interleave == PCI_EINVAL32) {
170374079a53SRobert Mustacchi 			sad->isad_valid |= IMC_SAD_V_BAD_PCI_READ;
170474079a53SRobert Mustacchi 			return;
170574079a53SRobert Mustacchi 		}
170674079a53SRobert Mustacchi 
170774079a53SRobert Mustacchi 		imc_sad_fill_rule(imc, sad, rule, dram);
170874079a53SRobert Mustacchi 		imc_sad_fill_rule_interleave(imc, rule, interleave);
170974079a53SRobert Mustacchi 	}
171074079a53SRobert Mustacchi }
171174079a53SRobert Mustacchi 
171274079a53SRobert Mustacchi static void
imc_sad_decode_mcroute(imc_t * imc,imc_sad_t * sad)171374079a53SRobert Mustacchi imc_sad_decode_mcroute(imc_t *imc, imc_sad_t *sad)
171474079a53SRobert Mustacchi {
171574079a53SRobert Mustacchi 	uint_t i;
171674079a53SRobert Mustacchi 	imc_sad_mcroute_table_t *mc = &sad->isad_mcroute;
171774079a53SRobert Mustacchi 
171874079a53SRobert Mustacchi 	if (imc->imc_gen < IMC_GEN_SKYLAKE)
171974079a53SRobert Mustacchi 		return;
172074079a53SRobert Mustacchi 	if (sad->isad_valid != 0)
172174079a53SRobert Mustacchi 		return;
172274079a53SRobert Mustacchi 
172374079a53SRobert Mustacchi 	mc->ismc_nroutes = IMC_MAX_SAD_MCROUTES;
172474079a53SRobert Mustacchi 	for (i = 0; i < IMC_MAX_SAD_MCROUTES; i++) {
172574079a53SRobert Mustacchi 		uint_t chanoff, ringoff;
172674079a53SRobert Mustacchi 
172774079a53SRobert Mustacchi 		ringoff = i * IMC_MC_ROUTE_RING_BITS;
172874079a53SRobert Mustacchi 		chanoff = i * IMC_MC_ROUTE_CHAN_BITS + IMC_MC_ROUTE_CHAN_OFFSET;
172974079a53SRobert Mustacchi 
173074079a53SRobert Mustacchi 		mc->ismc_mcroutes[i].ismce_imc = (mc->ismc_raw_mcroute >>
173174079a53SRobert Mustacchi 		    ringoff) & IMC_MC_ROUTE_RING_MASK;
173274079a53SRobert Mustacchi 		mc->ismc_mcroutes[i].ismce_pchannel = (mc->ismc_raw_mcroute >>
173374079a53SRobert Mustacchi 		    chanoff) & IMC_MC_ROUTE_CHAN_MASK;
173474079a53SRobert Mustacchi 	}
173574079a53SRobert Mustacchi }
173674079a53SRobert Mustacchi 
173774079a53SRobert Mustacchi /*
173874079a53SRobert Mustacchi  * Initialize the SAD. To do this we have to do a few different things:
173974079a53SRobert Mustacchi  *
174074079a53SRobert Mustacchi  * 1. Determine where the top of low and high memory is.
174174079a53SRobert Mustacchi  * 2. Read and decode all of the rules for the SAD
174274079a53SRobert Mustacchi  * 3. On systems with a route table, decode the raw routes
174374079a53SRobert Mustacchi  *
174474079a53SRobert Mustacchi  * At this point in time, we treat TOLM and TOHM as a per-socket construct, even
174574079a53SRobert Mustacchi  * though it really should be global, this just makes life a bit simpler.
174674079a53SRobert Mustacchi  */
174774079a53SRobert Mustacchi static void
imc_decoder_init_sad(imc_t * imc)174874079a53SRobert Mustacchi imc_decoder_init_sad(imc_t *imc)
174974079a53SRobert Mustacchi {
175074079a53SRobert Mustacchi 	uint_t i;
175174079a53SRobert Mustacchi 
175274079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
175374079a53SRobert Mustacchi 		imc_sad_read_tohm(imc, &imc->imc_sockets[i].isock_sad);
175474079a53SRobert Mustacchi 		imc_sad_read_dram_rules(imc, &imc->imc_sockets[i].isock_sad);
175574079a53SRobert Mustacchi 		imc_sad_decode_mcroute(imc, &imc->imc_sockets[i].isock_sad);
175674079a53SRobert Mustacchi 	}
175774079a53SRobert Mustacchi }
175874079a53SRobert Mustacchi 
175974079a53SRobert Mustacchi 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)176074079a53SRobert Mustacchi imc_tad_fill_rule(imc_t *imc, imc_tad_t *tad, imc_tad_rule_t *prev,
176174079a53SRobert Mustacchi     imc_tad_rule_t *rule, uint32_t val)
176274079a53SRobert Mustacchi {
176374079a53SRobert Mustacchi 	uint64_t limit;
176474079a53SRobert Mustacchi 
176574079a53SRobert Mustacchi 	limit = IMC_TAD_LIMIT(val);
176674079a53SRobert Mustacchi 	rule->itr_limit = (limit << IMC_TAD_LIMIT_SHIFT) +
176774079a53SRobert Mustacchi 	    IMC_TAD_LIMIT_EXCLUSIVE;
176874079a53SRobert Mustacchi 	rule->itr_raw = val;
176974079a53SRobert Mustacchi 
177074079a53SRobert Mustacchi 	switch (IMC_TAD_SOCK_WAY(val)) {
177174079a53SRobert Mustacchi 	case IMC_TAD_SOCK_WAY_1:
177274079a53SRobert Mustacchi 		rule->itr_sock_way = 1;
177374079a53SRobert Mustacchi 		break;
177474079a53SRobert Mustacchi 	case IMC_TAD_SOCK_WAY_2:
177574079a53SRobert Mustacchi 		rule->itr_sock_way = 2;
177674079a53SRobert Mustacchi 		break;
177774079a53SRobert Mustacchi 	case IMC_TAD_SOCK_WAY_4:
177874079a53SRobert Mustacchi 		rule->itr_sock_way = 4;
177974079a53SRobert Mustacchi 		break;
178074079a53SRobert Mustacchi 	case IMC_TAD_SOCK_WAY_8:
178174079a53SRobert Mustacchi 		rule->itr_sock_way = 8;
178274079a53SRobert Mustacchi 		break;
178374079a53SRobert Mustacchi 	}
178474079a53SRobert Mustacchi 
178574079a53SRobert Mustacchi 	rule->itr_chan_way = IMC_TAD_CHAN_WAY(val) + 1;
178674079a53SRobert Mustacchi 	rule->itr_sock_gran = IMC_TAD_GRAN_64B;
178774079a53SRobert Mustacchi 	rule->itr_chan_gran = IMC_TAD_GRAN_64B;
178874079a53SRobert Mustacchi 
178974079a53SRobert Mustacchi 	/*
179074079a53SRobert Mustacchi 	 * Starting with Skylake the targets that are used are no longer part of
179174079a53SRobert Mustacchi 	 * the TAD. Those come from the IMC route table.
179274079a53SRobert Mustacchi 	 */
179374079a53SRobert Mustacchi 	if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
179474079a53SRobert Mustacchi 		rule->itr_ntargets = 0;
179574079a53SRobert Mustacchi 		return;
179674079a53SRobert Mustacchi 	}
179774079a53SRobert Mustacchi 
179874079a53SRobert Mustacchi 	rule->itr_ntargets = IMC_TAD_SNB_BRD_NTARGETS;
179974079a53SRobert Mustacchi 	rule->itr_targets[0] = IMC_TAD_TARG0(val);
180074079a53SRobert Mustacchi 	rule->itr_targets[1] = IMC_TAD_TARG1(val);
180174079a53SRobert Mustacchi 	rule->itr_targets[2] = IMC_TAD_TARG2(val);
180274079a53SRobert Mustacchi 	rule->itr_targets[3] = IMC_TAD_TARG3(val);
180374079a53SRobert Mustacchi 
180474079a53SRobert Mustacchi 	if (prev == NULL) {
180574079a53SRobert Mustacchi 		rule->itr_base = 0;
180674079a53SRobert Mustacchi 	} else {
180774079a53SRobert Mustacchi 		rule->itr_base = prev->itr_limit + 1;
180874079a53SRobert Mustacchi 	}
180974079a53SRobert Mustacchi }
181074079a53SRobert Mustacchi 
181174079a53SRobert Mustacchi static void
imc_tad_fill_skx(imc_t * imc,imc_tad_t * tad,imc_tad_rule_t * rule,uint32_t val)181274079a53SRobert Mustacchi imc_tad_fill_skx(imc_t *imc, imc_tad_t *tad, imc_tad_rule_t *rule,
181374079a53SRobert Mustacchi     uint32_t val)
181474079a53SRobert Mustacchi {
181574079a53SRobert Mustacchi 	uint64_t base;
181674079a53SRobert Mustacchi 
181774079a53SRobert Mustacchi 	rule->itr_raw_gran = val;
181874079a53SRobert Mustacchi 	base = IMC_TAD_BASE_BASE(val);
181974079a53SRobert Mustacchi 	rule->itr_base = base << IMC_TAD_BASE_SHIFT;
182074079a53SRobert Mustacchi 
182174079a53SRobert Mustacchi 	switch (IMC_TAD_BASE_CHAN_GRAN(val)) {
182274079a53SRobert Mustacchi 	case IMC_TAD_BASE_CHAN_GRAN_64B:
182374079a53SRobert Mustacchi 		rule->itr_sock_gran = IMC_TAD_GRAN_64B;
182474079a53SRobert Mustacchi 		break;
182574079a53SRobert Mustacchi 	case IMC_TAD_BASE_CHAN_GRAN_256B:
182674079a53SRobert Mustacchi 		rule->itr_sock_gran = IMC_TAD_GRAN_256B;
182774079a53SRobert Mustacchi 		break;
182874079a53SRobert Mustacchi 	case IMC_TAD_BASE_CHAN_GRAN_4KB:
182974079a53SRobert Mustacchi 		rule->itr_sock_gran = IMC_TAD_GRAN_4KB;
183074079a53SRobert Mustacchi 		break;
183174079a53SRobert Mustacchi 	default:
183274079a53SRobert Mustacchi 		tad->itad_valid |= IMC_TAD_V_BAD_CHAN_GRAN;
183374079a53SRobert Mustacchi 		return;
183474079a53SRobert Mustacchi 	}
183574079a53SRobert Mustacchi 
183674079a53SRobert Mustacchi 	switch (IMC_TAD_BASE_SOCK_GRAN(val)) {
183774079a53SRobert Mustacchi 	case IMC_TAD_BASE_SOCK_GRAN_64B:
183874079a53SRobert Mustacchi 		rule->itr_sock_gran = IMC_TAD_GRAN_64B;
183974079a53SRobert Mustacchi 		break;
184074079a53SRobert Mustacchi 	case IMC_TAD_BASE_SOCK_GRAN_256B:
184174079a53SRobert Mustacchi 		rule->itr_sock_gran = IMC_TAD_GRAN_256B;
184274079a53SRobert Mustacchi 		break;
184374079a53SRobert Mustacchi 	case IMC_TAD_BASE_SOCK_GRAN_4KB:
184474079a53SRobert Mustacchi 		rule->itr_sock_gran = IMC_TAD_GRAN_4KB;
184574079a53SRobert Mustacchi 		break;
184674079a53SRobert Mustacchi 	case IMC_TAD_BASE_SOCK_GRAN_1GB:
184774079a53SRobert Mustacchi 		rule->itr_sock_gran = IMC_TAD_GRAN_1GB;
184874079a53SRobert Mustacchi 		break;
184974079a53SRobert Mustacchi 	}
185074079a53SRobert Mustacchi }
185174079a53SRobert Mustacchi 
185274079a53SRobert Mustacchi /*
185374079a53SRobert Mustacchi  * When mirroring is enabled, at least in Sandy Bridge to Broadwell, it's
185474079a53SRobert Mustacchi  * suggested that the channel wayness will take this into account and therefore
185574079a53SRobert Mustacchi  * should be accurately reflected.
185674079a53SRobert Mustacchi  */
185774079a53SRobert Mustacchi static void
imc_tad_read_rules(imc_t * imc,imc_tad_t * tad)185874079a53SRobert Mustacchi imc_tad_read_rules(imc_t *imc, imc_tad_t *tad)
185974079a53SRobert Mustacchi {
186074079a53SRobert Mustacchi 	uint_t i;
186174079a53SRobert Mustacchi 	off_t baseoff;
186274079a53SRobert Mustacchi 	imc_tad_rule_t *prev;
186374079a53SRobert Mustacchi 
186474079a53SRobert Mustacchi 	tad->itad_nrules = imc->imc_gen_data->igd_tad_nrules;
186574079a53SRobert Mustacchi 	for (i = 0, baseoff = imc->imc_gen_data->igd_tad_rule_offset,
186674079a53SRobert Mustacchi 	    prev = NULL; i < tad->itad_nrules;
186774079a53SRobert Mustacchi 	    i++, baseoff += sizeof (uint32_t)) {
186874079a53SRobert Mustacchi 		uint32_t val;
186974079a53SRobert Mustacchi 		off_t off;
187074079a53SRobert Mustacchi 		imc_tad_rule_t *rule = &tad->itad_rules[i];
187174079a53SRobert Mustacchi 
187274079a53SRobert Mustacchi 		/*
187374079a53SRobert Mustacchi 		 * On Skylake, the TAD rules are split among two registers. The
187474079a53SRobert Mustacchi 		 * latter set mimics what exists on pre-Skylake.
187574079a53SRobert Mustacchi 		 */
187674079a53SRobert Mustacchi 		if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
187774079a53SRobert Mustacchi 			off = baseoff + IMC_SKX_WAYNESS_OFFSET;
187874079a53SRobert Mustacchi 		} else {
187974079a53SRobert Mustacchi 			off = baseoff;
188074079a53SRobert Mustacchi 		}
188174079a53SRobert Mustacchi 
188274079a53SRobert Mustacchi 		val = pci_config_get32(tad->itad_stub->istub_cfgspace, off);
188374079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
188474079a53SRobert Mustacchi 			tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
188574079a53SRobert Mustacchi 			return;
188674079a53SRobert Mustacchi 		}
188774079a53SRobert Mustacchi 
188874079a53SRobert Mustacchi 		imc_tad_fill_rule(imc, tad, prev, rule, val);
188974079a53SRobert Mustacchi 		prev = rule;
189074079a53SRobert Mustacchi 		if (imc->imc_gen < IMC_GEN_SKYLAKE)
189174079a53SRobert Mustacchi 			continue;
189274079a53SRobert Mustacchi 
189374079a53SRobert Mustacchi 		val = pci_config_get32(tad->itad_stub->istub_cfgspace, baseoff);
189474079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
189574079a53SRobert Mustacchi 			tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
189674079a53SRobert Mustacchi 			return;
189774079a53SRobert Mustacchi 		}
189874079a53SRobert Mustacchi 
189974079a53SRobert Mustacchi 		imc_tad_fill_skx(imc, tad, rule, val);
190074079a53SRobert Mustacchi 	}
190174079a53SRobert Mustacchi }
190274079a53SRobert Mustacchi 
190374079a53SRobert Mustacchi /*
190474079a53SRobert Mustacchi  * Check for features which change how decoding works.
190574079a53SRobert Mustacchi  */
190674079a53SRobert Mustacchi static void
imc_tad_read_features(imc_t * imc,imc_tad_t * tad,imc_mc_t * mc)190774079a53SRobert Mustacchi imc_tad_read_features(imc_t *imc, imc_tad_t *tad, imc_mc_t *mc)
190874079a53SRobert Mustacchi {
190974079a53SRobert Mustacchi 	uint32_t val;
191074079a53SRobert Mustacchi 
191174079a53SRobert Mustacchi 	/*
191274079a53SRobert Mustacchi 	 * Determine whether or not lockstep mode or mirroring are enabled.
191374079a53SRobert Mustacchi 	 * These change the behavior of how we're supposed to interpret channel
191474079a53SRobert Mustacchi 	 * wayness. Lockstep is available in the TAD's features. Mirroring is
191574079a53SRobert Mustacchi 	 * available on the IMC's features. This isn't present in Skylake+. On
191674079a53SRobert Mustacchi 	 * Skylake Mirorring is a property of the SAD rule and there is no
191774079a53SRobert Mustacchi 	 * lockstep.
191874079a53SRobert Mustacchi 	 */
191974079a53SRobert Mustacchi 	switch (imc->imc_gen) {
192074079a53SRobert Mustacchi 	case IMC_GEN_SANDY:
192174079a53SRobert Mustacchi 	case IMC_GEN_IVY:
192274079a53SRobert Mustacchi 	case IMC_GEN_HASWELL:
192374079a53SRobert Mustacchi 	case IMC_GEN_BROADWELL:
192474079a53SRobert Mustacchi 		val = pci_config_get32(tad->itad_stub->istub_cfgspace,
192574079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_tad_sysdef);
192674079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
192774079a53SRobert Mustacchi 			tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
192874079a53SRobert Mustacchi 			return;
192974079a53SRobert Mustacchi 		}
193074079a53SRobert Mustacchi 		if (IMC_TAD_SYSDEF_LOCKSTEP(val)) {
193174079a53SRobert Mustacchi 			tad->itad_flags |= IMC_TAD_FLAG_LOCKSTEP;
193274079a53SRobert Mustacchi 		}
193374079a53SRobert Mustacchi 
193474079a53SRobert Mustacchi 		val = pci_config_get32(mc->icn_main1->istub_cfgspace,
193574079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_mc_mirror);
193674079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
193774079a53SRobert Mustacchi 			tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
193874079a53SRobert Mustacchi 			return;
193974079a53SRobert Mustacchi 		}
194074079a53SRobert Mustacchi 		if (IMC_MC_MIRROR_SNB_BRD(val)) {
194174079a53SRobert Mustacchi 			tad->itad_flags |= IMC_TAD_FLAG_MIRROR;
194274079a53SRobert Mustacchi 		}
194374079a53SRobert Mustacchi 		break;
194474079a53SRobert Mustacchi 	default:
194574079a53SRobert Mustacchi 		break;
194674079a53SRobert Mustacchi 	}
194774079a53SRobert Mustacchi 
194874079a53SRobert Mustacchi 	/*
194974079a53SRobert Mustacchi 	 * Now, go through and look at values that'll change how we do the
195074079a53SRobert Mustacchi 	 * channel index and adddress calculation. These are only present
195174079a53SRobert Mustacchi 	 * between Ivy Bridge and Broadwell. They don't exist on Sandy Bridge
195274079a53SRobert Mustacchi 	 * and they don't exist on Skylake+.
195374079a53SRobert Mustacchi 	 */
195474079a53SRobert Mustacchi 	switch (imc->imc_gen) {
195574079a53SRobert Mustacchi 	case IMC_GEN_IVY:
195674079a53SRobert Mustacchi 	case IMC_GEN_HASWELL:
195774079a53SRobert Mustacchi 	case IMC_GEN_BROADWELL:
195874079a53SRobert Mustacchi 		val = pci_config_get32(tad->itad_stub->istub_cfgspace,
195974079a53SRobert Mustacchi 		    imc->imc_gen_data->igd_tad_sysdef2);
196074079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
196174079a53SRobert Mustacchi 			tad->itad_valid |= IMC_TAD_V_BAD_PCI_READ;
196274079a53SRobert Mustacchi 			return;
196374079a53SRobert Mustacchi 		}
196474079a53SRobert Mustacchi 		if (IMC_TAD_SYSDEF2_SHIFTUP(val)) {
196574079a53SRobert Mustacchi 			tad->itad_flags |= IMC_TAD_FLAG_CHANSHIFT;
196674079a53SRobert Mustacchi 		}
196774079a53SRobert Mustacchi 		if (IMC_TAD_SYSDEF2_SHIFTUP(val)) {
196874079a53SRobert Mustacchi 			tad->itad_flags |= IMC_TAD_FLAG_CHANHASH;
196974079a53SRobert Mustacchi 		}
197074079a53SRobert Mustacchi 		break;
197174079a53SRobert Mustacchi 	default:
197274079a53SRobert Mustacchi 		break;
197374079a53SRobert Mustacchi 	}
197474079a53SRobert Mustacchi }
197574079a53SRobert Mustacchi 
197674079a53SRobert Mustacchi /*
197774079a53SRobert Mustacchi  * Read the IMC channel interleave records
197874079a53SRobert Mustacchi  */
197974079a53SRobert Mustacchi static void
imc_tad_read_interleave(imc_t * imc,imc_channel_t * chan)198074079a53SRobert Mustacchi imc_tad_read_interleave(imc_t *imc, imc_channel_t *chan)
198174079a53SRobert Mustacchi {
198274079a53SRobert Mustacchi 	uint_t i;
198374079a53SRobert Mustacchi 	off_t off;
198474079a53SRobert Mustacchi 
198574079a53SRobert Mustacchi 	chan->ich_ntad_offsets = imc->imc_gen_data->igd_tad_nrules;
198674079a53SRobert Mustacchi 	for (i = 0, off = imc->imc_gen_data->igd_tad_chan_offset;
198774079a53SRobert Mustacchi 	    i < chan->ich_ntad_offsets; i++, off += sizeof (uint32_t)) {
198874079a53SRobert Mustacchi 		uint32_t val;
198974079a53SRobert Mustacchi 		uint64_t offset;
199074079a53SRobert Mustacchi 
199174079a53SRobert Mustacchi 		val = pci_config_get32(chan->ich_desc->istub_cfgspace,
199274079a53SRobert Mustacchi 		    off);
199374079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
199474079a53SRobert Mustacchi 			chan->ich_valid |= IMC_CHANNEL_V_BAD_PCI_READ;
199574079a53SRobert Mustacchi 			return;
199674079a53SRobert Mustacchi 		}
199774079a53SRobert Mustacchi 
199874079a53SRobert Mustacchi 		if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
199974079a53SRobert Mustacchi 			offset = IMC_TADCHAN_OFFSET_SKX(val);
200074079a53SRobert Mustacchi 		} else {
200174079a53SRobert Mustacchi 			offset = IMC_TADCHAN_OFFSET_SNB_BRD(val);
200274079a53SRobert Mustacchi 		}
200374079a53SRobert Mustacchi 
200474079a53SRobert Mustacchi 		chan->ich_tad_offsets[i] = offset << IMC_TADCHAN_OFFSET_SHIFT;
200574079a53SRobert Mustacchi 		chan->ich_tad_offsets_raw[i] = val;
200674079a53SRobert Mustacchi 	}
200774079a53SRobert Mustacchi }
200874079a53SRobert Mustacchi 
200974079a53SRobert Mustacchi static void
imc_decoder_init_tad(imc_t * imc)201074079a53SRobert Mustacchi imc_decoder_init_tad(imc_t *imc)
201174079a53SRobert Mustacchi {
201274079a53SRobert Mustacchi 	uint_t i;
201374079a53SRobert Mustacchi 
201474079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
201574079a53SRobert Mustacchi 		uint_t j;
201674079a53SRobert Mustacchi 
201774079a53SRobert Mustacchi 		for (j = 0; j < imc->imc_sockets[i].isock_ntad; j++) {
201874079a53SRobert Mustacchi 			imc_tad_read_features(imc,
201974079a53SRobert Mustacchi 			    &imc->imc_sockets[i].isock_tad[j],
202074079a53SRobert Mustacchi 			    &imc->imc_sockets[i].isock_imcs[j]);
202174079a53SRobert Mustacchi 			imc_tad_read_rules(imc,
202274079a53SRobert Mustacchi 			    &imc->imc_sockets[i].isock_tad[j]);
202374079a53SRobert Mustacchi 		}
202474079a53SRobert Mustacchi 	}
202574079a53SRobert Mustacchi 
202674079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
202774079a53SRobert Mustacchi 		uint_t j;
202874079a53SRobert Mustacchi 		imc_socket_t *sock = &imc->imc_sockets[i];
202974079a53SRobert Mustacchi 
203074079a53SRobert Mustacchi 		for (j = 0; j < imc->imc_sockets[i].isock_nimc; j++) {
203174079a53SRobert Mustacchi 			uint_t k;
203274079a53SRobert Mustacchi 			imc_mc_t *mc = &sock->isock_imcs[j];
203374079a53SRobert Mustacchi 
203474079a53SRobert Mustacchi 			for (k = 0; k < mc->icn_nchannels; k++) {
203574079a53SRobert Mustacchi 				imc_channel_t *chan = &mc->icn_channels[k];
203674079a53SRobert Mustacchi 				imc_tad_read_interleave(imc, chan);
203774079a53SRobert Mustacchi 			}
203874079a53SRobert Mustacchi 		}
203974079a53SRobert Mustacchi 	}
204074079a53SRobert Mustacchi }
204174079a53SRobert Mustacchi 
204274079a53SRobert Mustacchi 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)204374079a53SRobert Mustacchi imc_rir_read_ileave_offsets(imc_t *imc, imc_channel_t *chan,
204474079a53SRobert Mustacchi     imc_rank_ileave_t *rank, uint_t rirno, boolean_t contig)
204574079a53SRobert Mustacchi {
204674079a53SRobert Mustacchi 	uint_t i;
204774079a53SRobert Mustacchi 	off_t off, incr;
204874079a53SRobert Mustacchi 
204974079a53SRobert Mustacchi 	/*
205074079a53SRobert Mustacchi 	 * Rank interleave offset registers come in two forms. Either they are
205174079a53SRobert Mustacchi 	 * contiguous for a given wayness, meaning that all of the entries for
205274079a53SRobert Mustacchi 	 * wayness zero are contiguous, or they are sparse, meaning that there
205374079a53SRobert Mustacchi 	 * is a bank for entry zero for all wayness, then entry one for all
205474079a53SRobert Mustacchi 	 * wayness, etc.
205574079a53SRobert Mustacchi 	 */
205674079a53SRobert Mustacchi 	if (contig) {
205774079a53SRobert Mustacchi 		off = imc->imc_gen_data->igd_rir_ileave_offset +
205874079a53SRobert Mustacchi 		    (rirno * imc->imc_gen_data->igd_rir_nileaves *
205974079a53SRobert Mustacchi 		    sizeof (uint32_t));
206074079a53SRobert Mustacchi 		incr = sizeof (uint32_t);
206174079a53SRobert Mustacchi 	} else {
206274079a53SRobert Mustacchi 		off = imc->imc_gen_data->igd_rir_ileave_offset +
206374079a53SRobert Mustacchi 		    (rirno * sizeof (uint32_t));
206474079a53SRobert Mustacchi 		incr = imc->imc_gen_data->igd_rir_nileaves * sizeof (uint32_t);
206574079a53SRobert Mustacchi 	}
206674079a53SRobert Mustacchi 	for (i = 0; i < rank->irle_nentries; i++, off += incr) {
206774079a53SRobert Mustacchi 		uint32_t val;
206874079a53SRobert Mustacchi 		uint64_t offset;
206974079a53SRobert Mustacchi 		imc_rank_ileave_entry_t *ent = &rank->irle_entries[i];
207074079a53SRobert Mustacchi 
207174079a53SRobert Mustacchi 		val = pci_config_get32(chan->ich_desc->istub_cfgspace, off);
207274079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
207374079a53SRobert Mustacchi 			chan->ich_valid |= IMC_CHANNEL_V_BAD_PCI_READ;
207474079a53SRobert Mustacchi 			return;
207574079a53SRobert Mustacchi 		}
207674079a53SRobert Mustacchi 
207774079a53SRobert Mustacchi 		switch (imc->imc_gen) {
207874079a53SRobert Mustacchi 		case IMC_GEN_BROADWELL:
207974079a53SRobert Mustacchi 			ent->irle_target = IMC_RIR_OFFSET_TARGET_BRD(val);
208074079a53SRobert Mustacchi 			break;
208174079a53SRobert Mustacchi 		default:
208274079a53SRobert Mustacchi 			ent->irle_target = IMC_RIR_OFFSET_TARGET(val);
208374079a53SRobert Mustacchi 			break;
208474079a53SRobert Mustacchi 		}
208574079a53SRobert Mustacchi 		if (imc->imc_gen >= IMC_GEN_HASWELL) {
208674079a53SRobert Mustacchi 			offset = IMC_RIR_OFFSET_OFFSET_HAS_SKX(val);
208774079a53SRobert Mustacchi 		} else {
208874079a53SRobert Mustacchi 			offset = IMC_RIR_OFFSET_OFFSET_SNB_IVB(val);
208974079a53SRobert Mustacchi 		}
209074079a53SRobert Mustacchi 		ent->irle_offset = offset << IMC_RIR_OFFSET_SHIFT;
209174079a53SRobert Mustacchi 	}
209274079a53SRobert Mustacchi }
209374079a53SRobert Mustacchi 
209474079a53SRobert Mustacchi static void
imc_rir_read_wayness(imc_t * imc,imc_channel_t * chan)209574079a53SRobert Mustacchi imc_rir_read_wayness(imc_t *imc, imc_channel_t *chan)
209674079a53SRobert Mustacchi {
209774079a53SRobert Mustacchi 	uint_t i;
209874079a53SRobert Mustacchi 	off_t off;
209974079a53SRobert Mustacchi 
210074079a53SRobert Mustacchi 	chan->ich_nrankileaves = imc->imc_gen_data->igd_rir_nways;
210174079a53SRobert Mustacchi 	for (i = 0, off = imc->imc_gen_data->igd_rir_way_offset;
210274079a53SRobert Mustacchi 	    i < chan->ich_nrankileaves; i++, off += sizeof (uint32_t)) {
210374079a53SRobert Mustacchi 		uint32_t val;
210474079a53SRobert Mustacchi 		uint64_t lim;
210574079a53SRobert Mustacchi 		imc_rank_ileave_t *ent = &chan->ich_rankileaves[i];
210674079a53SRobert Mustacchi 
210774079a53SRobert Mustacchi 		val = pci_config_get32(chan->ich_desc->istub_cfgspace, off);
210874079a53SRobert Mustacchi 		if (val == PCI_EINVAL32) {
210974079a53SRobert Mustacchi 			chan->ich_valid |= IMC_CHANNEL_V_BAD_PCI_READ;
211074079a53SRobert Mustacchi 			return;
211174079a53SRobert Mustacchi 		}
211274079a53SRobert Mustacchi 
211374079a53SRobert Mustacchi 		ent->irle_raw = val;
211474079a53SRobert Mustacchi 		ent->irle_enabled = IMC_RIR_WAYNESS_ENABLED(val) != 0;
211574079a53SRobert Mustacchi 		ent->irle_nways = 1 << IMC_RIR_WAYNESS_WAY(val);
211674079a53SRobert Mustacchi 		ent->irle_nwaysbits = IMC_RIR_WAYNESS_WAY(val);
211774079a53SRobert Mustacchi 		if (imc->imc_gen >= IMC_GEN_HASWELL) {
211874079a53SRobert Mustacchi 			lim = IMC_RIR_LIMIT_HAS_SKX(val);
211974079a53SRobert Mustacchi 		} else {
212074079a53SRobert Mustacchi 			lim = IMC_RIR_LIMIT_SNB_IVB(val);
212174079a53SRobert Mustacchi 		}
212274079a53SRobert Mustacchi 
212374079a53SRobert Mustacchi 		ent->irle_limit = (lim << IMC_RIR_LIMIT_SHIFT) +
212474079a53SRobert Mustacchi 		    IMC_RIR_LIMIT_EXCLUSIVE;
212574079a53SRobert Mustacchi 
212674079a53SRobert Mustacchi 		ent->irle_nentries = imc->imc_gen_data->igd_rir_nileaves;
212774079a53SRobert Mustacchi 		if (imc->imc_gen >= IMC_GEN_SKYLAKE) {
212874079a53SRobert Mustacchi 			imc_rir_read_ileave_offsets(imc, chan, ent, i, B_FALSE);
212974079a53SRobert Mustacchi 		} else {
213074079a53SRobert Mustacchi 			imc_rir_read_ileave_offsets(imc, chan, ent, i, B_TRUE);
213174079a53SRobert Mustacchi 		}
213274079a53SRobert Mustacchi 	}
213374079a53SRobert Mustacchi }
213474079a53SRobert Mustacchi 
213574079a53SRobert Mustacchi static void
imc_decoder_init_rir(imc_t * imc)213674079a53SRobert Mustacchi imc_decoder_init_rir(imc_t *imc)
213774079a53SRobert Mustacchi {
213874079a53SRobert Mustacchi 	uint_t i;
213974079a53SRobert Mustacchi 
214074079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
214174079a53SRobert Mustacchi 		uint_t j;
214274079a53SRobert Mustacchi 		imc_socket_t *sock = &imc->imc_sockets[i];
214374079a53SRobert Mustacchi 
214474079a53SRobert Mustacchi 		for (j = 0; j < imc->imc_sockets[i].isock_nimc; j++) {
214574079a53SRobert Mustacchi 			uint_t k;
214674079a53SRobert Mustacchi 			imc_mc_t *mc = &sock->isock_imcs[j];
214774079a53SRobert Mustacchi 
214874079a53SRobert Mustacchi 			for (k = 0; k < mc->icn_nchannels; k++) {
214974079a53SRobert Mustacchi 				imc_channel_t *chan = &mc->icn_channels[k];
215074079a53SRobert Mustacchi 				imc_rir_read_wayness(imc, chan);
215174079a53SRobert Mustacchi 			}
215274079a53SRobert Mustacchi 		}
215374079a53SRobert Mustacchi 	}
215474079a53SRobert Mustacchi }
215574079a53SRobert Mustacchi 
215674079a53SRobert Mustacchi 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)215774079a53SRobert Mustacchi imc_mc_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
215874079a53SRobert Mustacchi     uint32_t synd, int syndtype, mc_unum_t *unump)
215974079a53SRobert Mustacchi {
216074079a53SRobert Mustacchi 	imc_t *imc = arg;
216174079a53SRobert Mustacchi 	uint_t i;
216274079a53SRobert Mustacchi 	imc_decode_state_t dec;
216374079a53SRobert Mustacchi 
216474079a53SRobert Mustacchi 	bzero(&dec, sizeof (dec));
216574079a53SRobert Mustacchi 	if (!imc_decode_pa(imc, pa, &dec)) {
216674079a53SRobert Mustacchi 		switch (dec.ids_fail) {
216774079a53SRobert Mustacchi 		case IMC_DECODE_F_LEGACY_RANGE:
216874079a53SRobert Mustacchi 		case IMC_DECODE_F_OUTSIDE_DRAM:
216974079a53SRobert Mustacchi 			return (CMIERR_MC_NOTDIMMADDR);
217074079a53SRobert Mustacchi 		default:
217174079a53SRobert Mustacchi 			return (CMIERR_MC_BADSTATE);
217274079a53SRobert Mustacchi 		}
217374079a53SRobert Mustacchi 	}
217474079a53SRobert Mustacchi 
217574079a53SRobert Mustacchi 	unump->unum_board = 0;
217674079a53SRobert Mustacchi 	/*
217774079a53SRobert Mustacchi 	 * The chip id needs to be in the order that the OS expects it, which
217874079a53SRobert Mustacchi 	 * may not be our order.
217974079a53SRobert Mustacchi 	 */
218074079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
218174079a53SRobert Mustacchi 		if (imc->imc_spointers[i] == dec.ids_socket)
218274079a53SRobert Mustacchi 			break;
218374079a53SRobert Mustacchi 	}
218474079a53SRobert Mustacchi 	if (i == imc->imc_nsockets) {
218574079a53SRobert Mustacchi 		return (CMIERR_MC_BADSTATE);
218674079a53SRobert Mustacchi 	}
218774079a53SRobert Mustacchi 	unump->unum_chip = i;
218874079a53SRobert Mustacchi 	unump->unum_mc = dec.ids_tadid;
218974079a53SRobert Mustacchi 	unump->unum_chan = dec.ids_channelid;
219074079a53SRobert Mustacchi 	unump->unum_cs = dec.ids_dimmid;
219174079a53SRobert Mustacchi 	unump->unum_rank = dec.ids_rankid;
219274079a53SRobert Mustacchi 	unump->unum_offset = dec.ids_rankaddr;
219374079a53SRobert Mustacchi 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
219474079a53SRobert Mustacchi 		unump->unum_dimms[i] = MC_INVALNUM;
219574079a53SRobert Mustacchi 	}
219674079a53SRobert Mustacchi 
219774079a53SRobert Mustacchi 	return (CMI_SUCCESS);
219874079a53SRobert Mustacchi }
219974079a53SRobert Mustacchi 
220074079a53SRobert Mustacchi static cmi_errno_t
imc_mc_unumtopa(void * arg,mc_unum_t * unum,nvlist_t * nvl,uint64_t * pa)220174079a53SRobert Mustacchi imc_mc_unumtopa(void *arg, mc_unum_t *unum, nvlist_t *nvl, uint64_t *pa)
220274079a53SRobert Mustacchi {
220374079a53SRobert Mustacchi 	return (CMIERR_UNKNOWN);
220474079a53SRobert Mustacchi }
220574079a53SRobert Mustacchi 
220674079a53SRobert Mustacchi static const cmi_mc_ops_t imc_mc_ops = {
220774079a53SRobert Mustacchi 	.cmi_mc_patounum = imc_mc_patounum,
220874079a53SRobert Mustacchi 	.cmi_mc_unumtopa = imc_mc_unumtopa
220974079a53SRobert Mustacchi };
221074079a53SRobert Mustacchi 
221174079a53SRobert Mustacchi /*
221274079a53SRobert Mustacchi  * This is where we really finish attaching and become open for business. This
221374079a53SRobert Mustacchi  * occurs once we have all of the expected stubs attached. Here's where all of
221474079a53SRobert Mustacchi  * the real fun begins.
221574079a53SRobert Mustacchi  */
221674079a53SRobert Mustacchi static void
imc_attach_complete(void * arg)221774079a53SRobert Mustacchi imc_attach_complete(void *arg)
221874079a53SRobert Mustacchi {
221974079a53SRobert Mustacchi 	imc_t *imc = arg;
222074079a53SRobert Mustacchi 	cmi_errno_t err;
222174079a53SRobert Mustacchi 
222274079a53SRobert Mustacchi 	imc_set_gen_data(imc);
222374079a53SRobert Mustacchi 
222474079a53SRobert Mustacchi 	/*
222574079a53SRobert Mustacchi 	 * On SKX and newer, we can fail to map PCI buses at this point due to
222674079a53SRobert Mustacchi 	 * bad PCIe reads.
222774079a53SRobert Mustacchi 	 */
222874079a53SRobert Mustacchi 	if (!imc_map_stubs(imc)) {
222974079a53SRobert Mustacchi 		goto done;
223074079a53SRobert Mustacchi 	}
223174079a53SRobert Mustacchi 
223274079a53SRobert Mustacchi 	if (!imc_validate_stubs(imc)) {
223374079a53SRobert Mustacchi 		imc->imc_flags |= IMC_F_VALIDATE_FAILED;
223474079a53SRobert Mustacchi 		goto done;
223574079a53SRobert Mustacchi 	}
223674079a53SRobert Mustacchi 
223774079a53SRobert Mustacchi 	imc_fixup_stubs(imc);
223874079a53SRobert Mustacchi 	imc_map_sockets(imc);
223974079a53SRobert Mustacchi 
224074079a53SRobert Mustacchi 	if (!imc_create_minors(imc)) {
224174079a53SRobert Mustacchi 		goto done;
224274079a53SRobert Mustacchi 	}
224374079a53SRobert Mustacchi 
224474079a53SRobert Mustacchi 	imc_fill_data(imc);
224574079a53SRobert Mustacchi 	imc_nvl_create(imc);
224674079a53SRobert Mustacchi 
224774079a53SRobert Mustacchi 	/*
224874079a53SRobert Mustacchi 	 * Gather additional information that we need so that we can properly
224974079a53SRobert Mustacchi 	 * initialize the memory decoder and encoder.
225074079a53SRobert Mustacchi 	 */
225174079a53SRobert Mustacchi 	imc_decoder_init_sad(imc);
225274079a53SRobert Mustacchi 	imc_decoder_init_tad(imc);
225374079a53SRobert Mustacchi 	imc_decoder_init_rir(imc);
225474079a53SRobert Mustacchi 
225574079a53SRobert Mustacchi 	/*
225674079a53SRobert Mustacchi 	 * Register decoder functions. This may fail. If so, try and complain
225774079a53SRobert Mustacchi 	 * loudly, but stay active to allow other data to be useful. Register a
225874079a53SRobert Mustacchi 	 * global handle.
225974079a53SRobert Mustacchi 	 */
226074079a53SRobert Mustacchi 	if ((err = cmi_mc_register_global(&imc_mc_ops, imc)) != CMI_SUCCESS) {
226174079a53SRobert Mustacchi 		imc->imc_flags |= IMC_F_MCREG_FAILED;
226274079a53SRobert Mustacchi 		dev_err(imc->imc_dip, CE_WARN, "failed to register memory "
226374079a53SRobert Mustacchi 		    "decoding operations: 0x%x", err);
226474079a53SRobert Mustacchi 	}
226574079a53SRobert Mustacchi 
226674079a53SRobert Mustacchi done:
226774079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
226874079a53SRobert Mustacchi 	imc->imc_flags &= IMC_F_ATTACH_DISPATCHED;
226974079a53SRobert Mustacchi 	imc->imc_flags |= IMC_F_ATTACH_COMPLETE;
227074079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
227174079a53SRobert Mustacchi }
227274079a53SRobert Mustacchi 
227374079a53SRobert Mustacchi static int
imc_stub_comparator(const void * l,const void * r)227474079a53SRobert Mustacchi imc_stub_comparator(const void *l, const void *r)
227574079a53SRobert Mustacchi {
227674079a53SRobert Mustacchi 	const imc_stub_t *sl = l, *sr = r;
227774079a53SRobert Mustacchi 	if (sl->istub_bus > sr->istub_bus)
227874079a53SRobert Mustacchi 		return (1);
227974079a53SRobert Mustacchi 	if (sl->istub_bus < sr->istub_bus)
228074079a53SRobert Mustacchi 		return (-1);
228174079a53SRobert Mustacchi 	if (sl->istub_dev > sr->istub_dev)
228274079a53SRobert Mustacchi 		return (1);
228374079a53SRobert Mustacchi 	if (sl->istub_dev < sr->istub_dev)
228474079a53SRobert Mustacchi 		return (-1);
228574079a53SRobert Mustacchi 	if (sl->istub_func > sr->istub_func)
228674079a53SRobert Mustacchi 		return (1);
228774079a53SRobert Mustacchi 	if (sl->istub_func < sr->istub_func)
228874079a53SRobert Mustacchi 		return (-1);
228974079a53SRobert Mustacchi 	return (0);
229074079a53SRobert Mustacchi }
229174079a53SRobert Mustacchi 
229274079a53SRobert Mustacchi static int
imc_stub_scan_cb(dev_info_t * dip,void * arg)229374079a53SRobert Mustacchi imc_stub_scan_cb(dev_info_t *dip, void *arg)
229474079a53SRobert Mustacchi {
229574079a53SRobert Mustacchi 	int vid, did;
229674079a53SRobert Mustacchi 	const imc_stub_table_t *table;
229774079a53SRobert Mustacchi 	imc_t *imc = arg;
229874079a53SRobert Mustacchi 	int *regs;
229974079a53SRobert Mustacchi 	uint_t i, nregs;
230074079a53SRobert Mustacchi 
230174079a53SRobert Mustacchi 	if (dip == ddi_root_node()) {
230274079a53SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
230374079a53SRobert Mustacchi 	}
230474079a53SRobert Mustacchi 
230574079a53SRobert Mustacchi 	/*
230674079a53SRobert Mustacchi 	 * Get the dev info name. PCI devices will always be children of PCI
230774079a53SRobert Mustacchi 	 * devices today on x86. If we reach something that has a device name
230874079a53SRobert Mustacchi 	 * that's not PCI, then we can prune it's children.
230974079a53SRobert Mustacchi 	 */
231074079a53SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
231174079a53SRobert Mustacchi 		return (DDI_WALK_PRUNECHILD);
231274079a53SRobert Mustacchi 	}
231374079a53SRobert Mustacchi 
231474079a53SRobert Mustacchi 	/*
231574079a53SRobert Mustacchi 	 * Get the device and vendor ID and see if this is something the imc
231674079a53SRobert Mustacchi 	 * knows about or cares about.
231774079a53SRobert Mustacchi 	 */
231874079a53SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
231974079a53SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
232074079a53SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
232174079a53SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
232274079a53SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
232374079a53SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
232474079a53SRobert Mustacchi 	}
232574079a53SRobert Mustacchi 
232674079a53SRobert Mustacchi 	if (vid != IMC_PCI_VENDOR_INTC) {
232774079a53SRobert Mustacchi 		return (DDI_WALK_PRUNECHILD);
232874079a53SRobert Mustacchi 	}
232974079a53SRobert Mustacchi 
233074079a53SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
233174079a53SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
233274079a53SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
233374079a53SRobert Mustacchi 	}
233474079a53SRobert Mustacchi 
233574079a53SRobert Mustacchi 	if (nregs == 0) {
233674079a53SRobert Mustacchi 		ddi_prop_free(regs);
233774079a53SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
233874079a53SRobert Mustacchi 	}
233974079a53SRobert Mustacchi 
234074079a53SRobert Mustacchi 
234174079a53SRobert Mustacchi 	table = NULL;
234274079a53SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(imc_stub_table); i++) {
234374079a53SRobert Mustacchi 		if (imc_stub_table[i].imcs_devid == did &&
234474079a53SRobert Mustacchi 		    imc_stub_table[i].imcs_pcidev == PCI_REG_DEV_G(regs[0]) &&
234574079a53SRobert Mustacchi 		    imc_stub_table[i].imcs_pcifunc == PCI_REG_FUNC_G(regs[0])) {
234674079a53SRobert Mustacchi 			table = &imc_stub_table[i];
234774079a53SRobert Mustacchi 			break;
234874079a53SRobert Mustacchi 		}
234974079a53SRobert Mustacchi 	}
235074079a53SRobert Mustacchi 	ddi_prop_free(regs);
235174079a53SRobert Mustacchi 
235274079a53SRobert Mustacchi 	/*
235374079a53SRobert Mustacchi 	 * Not a match, not interesting.
235474079a53SRobert Mustacchi 	 */
235574079a53SRobert Mustacchi 	if (table == NULL) {
235674079a53SRobert Mustacchi 		return (DDI_WALK_CONTINUE);
235774079a53SRobert Mustacchi 	}
235874079a53SRobert Mustacchi 
235974079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
236074079a53SRobert Mustacchi 	imc->imc_nscanned++;
236174079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
236274079a53SRobert Mustacchi 
236374079a53SRobert Mustacchi 	return (DDI_WALK_CONTINUE);
236474079a53SRobert Mustacchi }
236574079a53SRobert Mustacchi 
236674079a53SRobert Mustacchi /*
236774079a53SRobert Mustacchi  * From here, go through and see how many of the devices that we know about.
236874079a53SRobert Mustacchi  */
236974079a53SRobert Mustacchi static void
imc_stub_scan(void * arg)237074079a53SRobert Mustacchi imc_stub_scan(void *arg)
237174079a53SRobert Mustacchi {
237274079a53SRobert Mustacchi 	imc_t *imc = arg;
237374079a53SRobert Mustacchi 	boolean_t dispatch = B_FALSE;
237474079a53SRobert Mustacchi 
237574079a53SRobert Mustacchi 	/*
237674079a53SRobert Mustacchi 	 * Zero out the scan results in case we've been detached and reattached.
237774079a53SRobert Mustacchi 	 */
237874079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
237974079a53SRobert Mustacchi 	imc->imc_nscanned = 0;
238074079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
238174079a53SRobert Mustacchi 
238274079a53SRobert Mustacchi 	ddi_walk_devs(ddi_root_node(), imc_stub_scan_cb, imc);
238374079a53SRobert Mustacchi 
238474079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
238574079a53SRobert Mustacchi 	imc->imc_flags |= IMC_F_SCAN_COMPLETE;
238674079a53SRobert Mustacchi 	imc->imc_flags &= ~IMC_F_SCAN_DISPATCHED;
238774079a53SRobert Mustacchi 
238874079a53SRobert Mustacchi 	/*
238974079a53SRobert Mustacchi 	 * If the scan found no nodes, then that means that we're on a hardware
239074079a53SRobert Mustacchi 	 * platform that we don't support. Therefore, there's no reason to do
239174079a53SRobert Mustacchi 	 * anything here.
239274079a53SRobert Mustacchi 	 */
239374079a53SRobert Mustacchi 	if (imc->imc_nscanned == 0) {
239474079a53SRobert Mustacchi 		imc->imc_flags |= IMC_F_UNSUP_PLATFORM;
239574079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
239674079a53SRobert Mustacchi 		return;
239774079a53SRobert Mustacchi 	}
239874079a53SRobert Mustacchi 
239974079a53SRobert Mustacchi 	if (avl_numnodes(&imc->imc_stubs) == imc->imc_nscanned) {
240074079a53SRobert Mustacchi 		imc->imc_flags |= IMC_F_ATTACH_DISPATCHED;
240174079a53SRobert Mustacchi 		dispatch = B_TRUE;
240274079a53SRobert Mustacchi 	}
240374079a53SRobert Mustacchi 
240474079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
240574079a53SRobert Mustacchi 
240674079a53SRobert Mustacchi 	if (dispatch) {
240774079a53SRobert Mustacchi 		(void) ddi_taskq_dispatch(imc->imc_taskq, imc_attach_complete,
240874079a53SRobert Mustacchi 		    imc, DDI_SLEEP);
240974079a53SRobert Mustacchi 	}
241074079a53SRobert Mustacchi }
241174079a53SRobert Mustacchi 
241274079a53SRobert Mustacchi /*
241374079a53SRobert Mustacchi  * By default, refuse to allow stubs to detach.
241474079a53SRobert Mustacchi  */
241574079a53SRobert Mustacchi int
imc_detach_stub(dev_info_t * dip,ddi_detach_cmd_t cmd)241674079a53SRobert Mustacchi imc_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd)
241774079a53SRobert Mustacchi {
241874079a53SRobert Mustacchi 	imc_stub_t *stub;
241974079a53SRobert Mustacchi 	imc_t *imc = imc_data;
242074079a53SRobert Mustacchi 
242174079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
242274079a53SRobert Mustacchi 
242374079a53SRobert Mustacchi 	/*
242474079a53SRobert Mustacchi 	 * By default, we do not allow stubs to detach. However, if the driver
242574079a53SRobert Mustacchi 	 * has attached to devices on a platform it doesn't recognize or
242674079a53SRobert Mustacchi 	 * support or if the override flag has been set, then allow detach to
242774079a53SRobert Mustacchi 	 * proceed.
242874079a53SRobert Mustacchi 	 */
242974079a53SRobert Mustacchi 	if ((imc->imc_flags & IMC_F_UNSUP_PLATFORM) == 0 &&
243074079a53SRobert Mustacchi 	    imc_allow_detach == 0) {
243174079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
243274079a53SRobert Mustacchi 		return (DDI_FAILURE);
243374079a53SRobert Mustacchi 	}
243474079a53SRobert Mustacchi 
243574079a53SRobert Mustacchi 	for (stub = avl_first(&imc->imc_stubs); stub != NULL;
243674079a53SRobert Mustacchi 	    stub = AVL_NEXT(&imc->imc_stubs, stub)) {
243774079a53SRobert Mustacchi 		if (stub->istub_dip == dip) {
243874079a53SRobert Mustacchi 			break;
243974079a53SRobert Mustacchi 		}
244074079a53SRobert Mustacchi 	}
244174079a53SRobert Mustacchi 
244274079a53SRobert Mustacchi 	/*
244374079a53SRobert Mustacchi 	 * A device was attached to us that we somehow don't know about. Allow
244474079a53SRobert Mustacchi 	 * this to proceed.
244574079a53SRobert Mustacchi 	 */
244674079a53SRobert Mustacchi 	if (stub == NULL) {
244774079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
244874079a53SRobert Mustacchi 		return (DDI_SUCCESS);
244974079a53SRobert Mustacchi 	}
245074079a53SRobert Mustacchi 
245174079a53SRobert Mustacchi 	pci_config_teardown(&stub->istub_cfgspace);
245274079a53SRobert Mustacchi 	avl_remove(&imc->imc_stubs, stub);
245374079a53SRobert Mustacchi 	kmem_free(stub, sizeof (imc_stub_t));
245474079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
245574079a53SRobert Mustacchi 
245674079a53SRobert Mustacchi 	return (DDI_SUCCESS);
245774079a53SRobert Mustacchi }
245874079a53SRobert Mustacchi 
245974079a53SRobert Mustacchi int
imc_attach_stub(dev_info_t * dip,ddi_attach_cmd_t cmd)246074079a53SRobert Mustacchi imc_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd)
246174079a53SRobert Mustacchi {
246274079a53SRobert Mustacchi 	imc_stub_t *stub, *lookup;
246374079a53SRobert Mustacchi 	int did, vid, *regs;
246474079a53SRobert Mustacchi 	uint_t i, nregs;
246574079a53SRobert Mustacchi 	const imc_stub_table_t *table;
246674079a53SRobert Mustacchi 	avl_index_t idx;
246774079a53SRobert Mustacchi 	boolean_t dispatch = B_FALSE;
246874079a53SRobert Mustacchi 	imc_t *imc = imc_data;
246974079a53SRobert Mustacchi 
247074079a53SRobert Mustacchi 	if (cmd != DDI_ATTACH) {
247174079a53SRobert Mustacchi 		return (DDI_FAILURE);
247274079a53SRobert Mustacchi 	}
247374079a53SRobert Mustacchi 
247474079a53SRobert Mustacchi 	/*
247574079a53SRobert Mustacchi 	 * We've been asked to attach a stub. First, determine if this is even a
247674079a53SRobert Mustacchi 	 * PCI device that we should care about. Then, append it to our global
247774079a53SRobert Mustacchi 	 * list and kick off the configuration task. Note that we do this
247874079a53SRobert Mustacchi 	 * configuration task in a taskq so that we don't interfere with the
247974079a53SRobert Mustacchi 	 * normal attach / detach path processing.
248074079a53SRobert Mustacchi 	 */
248174079a53SRobert Mustacchi 	if (strncmp("pci", ddi_get_name(dip), 3) != 0) {
248274079a53SRobert Mustacchi 		return (DDI_FAILURE);
248374079a53SRobert Mustacchi 	}
248474079a53SRobert Mustacchi 
248574079a53SRobert Mustacchi 	/*
248674079a53SRobert Mustacchi 	 * Get the device and vendor ID and see if this is something the imc
248774079a53SRobert Mustacchi 	 * knows about or cares about.
248874079a53SRobert Mustacchi 	 */
248974079a53SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
249074079a53SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
249174079a53SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
249274079a53SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
249374079a53SRobert Mustacchi 	if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) {
249474079a53SRobert Mustacchi 		return (DDI_FAILURE);
249574079a53SRobert Mustacchi 	}
249674079a53SRobert Mustacchi 
249774079a53SRobert Mustacchi 	/*
249874079a53SRobert Mustacchi 	 * Only accept INTC parts on the imc driver.
249974079a53SRobert Mustacchi 	 */
250074079a53SRobert Mustacchi 	if (vid != IMC_PCI_VENDOR_INTC) {
250174079a53SRobert Mustacchi 		return (DDI_FAILURE);
250274079a53SRobert Mustacchi 	}
250374079a53SRobert Mustacchi 
250474079a53SRobert Mustacchi 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
250574079a53SRobert Mustacchi 	    "reg", &regs, &nregs) != DDI_PROP_SUCCESS) {
250674079a53SRobert Mustacchi 		return (DDI_FAILURE);
250774079a53SRobert Mustacchi 	}
250874079a53SRobert Mustacchi 
250974079a53SRobert Mustacchi 	if (nregs == 0) {
251074079a53SRobert Mustacchi 		ddi_prop_free(regs);
251174079a53SRobert Mustacchi 		return (DDI_FAILURE);
251274079a53SRobert Mustacchi 	}
251374079a53SRobert Mustacchi 
251474079a53SRobert Mustacchi 	/*
251574079a53SRobert Mustacchi 	 * Determine if this matches a known device.
251674079a53SRobert Mustacchi 	 */
251774079a53SRobert Mustacchi 	table = NULL;
251874079a53SRobert Mustacchi 	for (i = 0; i < ARRAY_SIZE(imc_stub_table); i++) {
251974079a53SRobert Mustacchi 		if (imc_stub_table[i].imcs_devid == did &&
252074079a53SRobert Mustacchi 		    imc_stub_table[i].imcs_pcidev == PCI_REG_DEV_G(regs[0]) &&
252174079a53SRobert Mustacchi 		    imc_stub_table[i].imcs_pcifunc == PCI_REG_FUNC_G(regs[0])) {
252274079a53SRobert Mustacchi 			table = &imc_stub_table[i];
252374079a53SRobert Mustacchi 			break;
252474079a53SRobert Mustacchi 		}
252574079a53SRobert Mustacchi 	}
252674079a53SRobert Mustacchi 
252774079a53SRobert Mustacchi 	if (i == ARRAY_SIZE(imc_stub_table)) {
252874079a53SRobert Mustacchi 		ddi_prop_free(regs);
252974079a53SRobert Mustacchi 		return (DDI_FAILURE);
253074079a53SRobert Mustacchi 	}
253174079a53SRobert Mustacchi 
253274079a53SRobert Mustacchi 	/*
253374079a53SRobert Mustacchi 	 * We've found something. Make sure the generation matches our current
253474079a53SRobert Mustacchi 	 * one. If it does, construct the entry and append it to the list.
253574079a53SRobert Mustacchi 	 */
253674079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
253774079a53SRobert Mustacchi 	if (imc->imc_gen != IMC_GEN_UNKNOWN && imc->imc_gen !=
253874079a53SRobert Mustacchi 	    table->imcs_gen) {
253974079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
254074079a53SRobert Mustacchi 		ddi_prop_free(regs);
254174079a53SRobert Mustacchi 		dev_err(dip, CE_WARN, "Encountered IMC stub device (%u/%u) "
254274079a53SRobert Mustacchi 		    "that has different hardware generation (%u) from current "
254374079a53SRobert Mustacchi 		    "generation (%u)", vid, did, table->imcs_gen, imc->imc_gen);
254474079a53SRobert Mustacchi 		return (DDI_FAILURE);
254574079a53SRobert Mustacchi 	} else {
254674079a53SRobert Mustacchi 		imc->imc_gen = table->imcs_gen;
254774079a53SRobert Mustacchi 	}
254874079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
254974079a53SRobert Mustacchi 
255074079a53SRobert Mustacchi 	stub = kmem_zalloc(sizeof (imc_stub_t), KM_SLEEP);
255174079a53SRobert Mustacchi 	stub->istub_dip = dip;
255274079a53SRobert Mustacchi 	stub->istub_vid = vid;
255374079a53SRobert Mustacchi 	stub->istub_did = did;
255474079a53SRobert Mustacchi 	stub->istub_bus = PCI_REG_BUS_G(regs[0]);
255574079a53SRobert Mustacchi 	stub->istub_dev = PCI_REG_DEV_G(regs[0]);
255674079a53SRobert Mustacchi 	stub->istub_func = PCI_REG_FUNC_G(regs[0]);
255774079a53SRobert Mustacchi 	ddi_prop_free(regs);
255874079a53SRobert Mustacchi 	stub->istub_table = table;
255974079a53SRobert Mustacchi 
256074079a53SRobert Mustacchi 	if (pci_config_setup(dip, &stub->istub_cfgspace) != DDI_SUCCESS) {
256174079a53SRobert Mustacchi 		kmem_free(stub, sizeof (stub));
256274079a53SRobert Mustacchi 		dev_err(dip, CE_WARN, "Failed to set up PCI config space "
256374079a53SRobert Mustacchi 		    "for IMC stub device %s (%u/%u)", ddi_node_name(dip),
256474079a53SRobert Mustacchi 		    vid, did);
256574079a53SRobert Mustacchi 		return (DDI_FAILURE);
256674079a53SRobert Mustacchi 	}
256774079a53SRobert Mustacchi 
256874079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
256974079a53SRobert Mustacchi 	if ((lookup = avl_find(&imc->imc_stubs, stub, &idx)) != NULL) {
257074079a53SRobert Mustacchi 		dev_err(dip, CE_WARN, "IMC stub %s (%u/%u) has duplicate "
257174079a53SRobert Mustacchi 		    "bdf %u/%u/%u with %s (%u/%u), not attaching",
257274079a53SRobert Mustacchi 		    ddi_node_name(imc->imc_dip), vid, did,
257374079a53SRobert Mustacchi 		    stub->istub_bus, stub->istub_dev, stub->istub_func,
257474079a53SRobert Mustacchi 		    ddi_node_name(lookup->istub_dip), lookup->istub_vid,
257574079a53SRobert Mustacchi 		    lookup->istub_did);
257674079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
257774079a53SRobert Mustacchi 		pci_config_teardown(&stub->istub_cfgspace);
257874079a53SRobert Mustacchi 		kmem_free(stub, sizeof (stub));
257974079a53SRobert Mustacchi 
258074079a53SRobert Mustacchi 		return (DDI_FAILURE);
258174079a53SRobert Mustacchi 	}
258274079a53SRobert Mustacchi 	avl_insert(&imc->imc_stubs, stub, idx);
258374079a53SRobert Mustacchi 
258474079a53SRobert Mustacchi 	if ((imc->imc_flags & IMC_F_ALL_FLAGS) == IMC_F_SCAN_COMPLETE &&
258574079a53SRobert Mustacchi 	    avl_numnodes(&imc->imc_stubs) == imc->imc_nscanned) {
258674079a53SRobert Mustacchi 		imc->imc_flags |= IMC_F_ATTACH_DISPATCHED;
258774079a53SRobert Mustacchi 		dispatch = B_TRUE;
258874079a53SRobert Mustacchi 	}
258974079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
259074079a53SRobert Mustacchi 
259174079a53SRobert Mustacchi 	if (dispatch) {
259274079a53SRobert Mustacchi 		(void) ddi_taskq_dispatch(imc->imc_taskq, imc_attach_complete,
259374079a53SRobert Mustacchi 		    imc, DDI_SLEEP);
259474079a53SRobert Mustacchi 	}
259574079a53SRobert Mustacchi 
259674079a53SRobert Mustacchi 	return (DDI_SUCCESS);
259774079a53SRobert Mustacchi }
259874079a53SRobert Mustacchi 
259974079a53SRobert Mustacchi static int
imc_open(dev_t * devp,int flag,int otyp,cred_t * credp)260074079a53SRobert Mustacchi imc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
260174079a53SRobert Mustacchi {
260274079a53SRobert Mustacchi 	imc_t *imc = imc_data;
260374079a53SRobert Mustacchi 
260474079a53SRobert Mustacchi 	if ((flag & (FEXCL | FNDELAY)) != 0)
260574079a53SRobert Mustacchi 		return (EINVAL);
260674079a53SRobert Mustacchi 
260774079a53SRobert Mustacchi 	if (otyp != OTYP_CHR)
260874079a53SRobert Mustacchi 		return (EINVAL);
260974079a53SRobert Mustacchi 
261074079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
261174079a53SRobert Mustacchi 
261274079a53SRobert Mustacchi 	if ((imc->imc_flags & IMC_F_UNSUP_PLATFORM) != 0) {
261374079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
261474079a53SRobert Mustacchi 		return (ENOTSUP);
261574079a53SRobert Mustacchi 	}
261674079a53SRobert Mustacchi 
261774079a53SRobert Mustacchi 	/*
261874079a53SRobert Mustacchi 	 * It's possible that someone has come in during the window between when
261974079a53SRobert Mustacchi 	 * we've created the minor node and when we've finished doing work.
262074079a53SRobert Mustacchi 	 */
262174079a53SRobert Mustacchi 	if ((imc->imc_flags & IMC_F_ATTACH_COMPLETE) == 0) {
262274079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
262374079a53SRobert Mustacchi 		return (EAGAIN);
262474079a53SRobert Mustacchi 	}
262574079a53SRobert Mustacchi 
262674079a53SRobert Mustacchi 	/*
262774079a53SRobert Mustacchi 	 * It's not clear how someone would get a minor that we didn't create.
262874079a53SRobert Mustacchi 	 * But be paranoid and make sure.
262974079a53SRobert Mustacchi 	 */
263074079a53SRobert Mustacchi 	if (getminor(*devp) >= imc->imc_nsockets) {
263174079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
263274079a53SRobert Mustacchi 		return (EINVAL);
263374079a53SRobert Mustacchi 	}
263474079a53SRobert Mustacchi 
263574079a53SRobert Mustacchi 	/*
263674079a53SRobert Mustacchi 	 * Make sure this socket entry has been filled in.
263774079a53SRobert Mustacchi 	 */
263874079a53SRobert Mustacchi 	if (imc->imc_spointers[getminor(*devp)] == NULL) {
263974079a53SRobert Mustacchi 		mutex_exit(&imc->imc_lock);
264074079a53SRobert Mustacchi 		return (EINVAL);
264174079a53SRobert Mustacchi 	}
264274079a53SRobert Mustacchi 
264374079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
264474079a53SRobert Mustacchi 
264574079a53SRobert Mustacchi 	return (0);
264674079a53SRobert Mustacchi }
264774079a53SRobert Mustacchi 
264874079a53SRobert Mustacchi static void
imc_ioctl_decode(imc_t * imc,mc_encode_ioc_t * encode)264974079a53SRobert Mustacchi imc_ioctl_decode(imc_t *imc, mc_encode_ioc_t *encode)
265074079a53SRobert Mustacchi {
265174079a53SRobert Mustacchi 	imc_decode_state_t dec;
265274079a53SRobert Mustacchi 	uint_t i;
265374079a53SRobert Mustacchi 
265474079a53SRobert Mustacchi 	bzero(&dec, sizeof (dec));
265574079a53SRobert Mustacchi 	if (!imc_decode_pa(imc, encode->mcei_pa, &dec)) {
265674079a53SRobert Mustacchi 		encode->mcei_err = (uint32_t)dec.ids_fail;
265774079a53SRobert Mustacchi 		encode->mcei_errdata = dec.ids_fail_data;
265874079a53SRobert Mustacchi 		return;
265974079a53SRobert Mustacchi 	}
266074079a53SRobert Mustacchi 
266174079a53SRobert Mustacchi 	encode->mcei_errdata = 0;
266274079a53SRobert Mustacchi 	encode->mcei_err = 0;
266374079a53SRobert Mustacchi 	encode->mcei_board = 0;
266474079a53SRobert Mustacchi 	for (i = 0; i < imc->imc_nsockets; i++) {
266574079a53SRobert Mustacchi 		if (imc->imc_spointers[i] == dec.ids_socket)
266674079a53SRobert Mustacchi 			break;
266774079a53SRobert Mustacchi 	}
266874079a53SRobert Mustacchi 	encode->mcei_chip = i;
2669*71815ce7SRobert Mustacchi 	/*
2670*71815ce7SRobert Mustacchi 	 * These Intel platforms are all monolithic dies, so set the die to
2671*71815ce7SRobert Mustacchi 	 * zero.
2672*71815ce7SRobert Mustacchi 	 */
2673*71815ce7SRobert Mustacchi 	encode->mcei_die = 0;
267474079a53SRobert Mustacchi 	encode->mcei_mc = dec.ids_tadid;
2675*71815ce7SRobert Mustacchi 	encode->mcei_chan_addr = dec.ids_chanaddr;
267674079a53SRobert Mustacchi 	encode->mcei_chan = dec.ids_channelid;
267774079a53SRobert Mustacchi 	encode->mcei_dimm = dec.ids_dimmid;
267874079a53SRobert Mustacchi 	encode->mcei_rank_addr = dec.ids_rankaddr;
267974079a53SRobert Mustacchi 	encode->mcei_rank = dec.ids_rankid;
268074079a53SRobert Mustacchi 	encode->mcei_row = UINT32_MAX;
268174079a53SRobert Mustacchi 	encode->mcei_column = UINT32_MAX;
2682*71815ce7SRobert Mustacchi 	encode->mcei_cs = encode->mcei_rm = UINT8_MAX;
2683*71815ce7SRobert Mustacchi 	encode->mcei_bank = encode->mcei_bank_group = UINT8_MAX;
2684*71815ce7SRobert Mustacchi 	encode->mcei_subchan = UINT8_MAX;
268574079a53SRobert Mustacchi }
268674079a53SRobert Mustacchi 
268774079a53SRobert Mustacchi static int
imc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)268874079a53SRobert Mustacchi imc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
268974079a53SRobert Mustacchi     int *rvalp)
269074079a53SRobert Mustacchi {
269174079a53SRobert Mustacchi 	int ret;
269274079a53SRobert Mustacchi 	minor_t m;
269374079a53SRobert Mustacchi 	mc_snapshot_info_t info;
269474079a53SRobert Mustacchi 	mc_encode_ioc_t encode;
269574079a53SRobert Mustacchi 	imc_t *imc = imc_data;
269674079a53SRobert Mustacchi 	imc_socket_t *sock;
269774079a53SRobert Mustacchi 
269874079a53SRobert Mustacchi 	mutex_enter(&imc->imc_lock);
269974079a53SRobert Mustacchi 	m = getminor(dev);
270074079a53SRobert Mustacchi 	if (m >= imc->imc_nsockets) {
270174079a53SRobert Mustacchi 		ret = EINVAL;
270274079a53SRobert Mustacchi 		goto done;
270374079a53SRobert Mustacchi 	}
270474079a53SRobert Mustacchi 	sock = imc->imc_spointers[m];
270574079a53SRobert Mustacchi 	if (sock == NULL) {
270674079a53SRobert Mustacchi 		ret = EINVAL;
270774079a53SRobert Mustacchi 		goto done;
270874079a53SRobert Mustacchi 	}
270974079a53SRobert Mustacchi 
271074079a53SRobert Mustacchi 	/*
271174079a53SRobert Mustacchi 	 * Note, other memory controller drivers don't check mode for reading
271274079a53SRobert Mustacchi 	 * data nor do they care who can read it from a credential perspective.
271374079a53SRobert Mustacchi 	 * As such we don't either at this time.
271474079a53SRobert Mustacchi 	 */
271574079a53SRobert Mustacchi 	switch (cmd) {
271674079a53SRobert Mustacchi 	case MC_IOC_SNAPSHOT_INFO:
271774079a53SRobert Mustacchi 		imc_nvl_pack(sock, B_FALSE);
271874079a53SRobert Mustacchi 		if (sock->isock_buf == NULL) {
271974079a53SRobert Mustacchi 			ret = EIO;
272074079a53SRobert Mustacchi 			break;
272174079a53SRobert Mustacchi 		}
272274079a53SRobert Mustacchi 
272374079a53SRobert Mustacchi 		info.mcs_size = sock->isock_buflen;
272474079a53SRobert Mustacchi 		info.mcs_gen = sock->isock_gen;
272574079a53SRobert Mustacchi 
272674079a53SRobert Mustacchi 		if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0) {
272774079a53SRobert Mustacchi 			ret = EFAULT;
272874079a53SRobert Mustacchi 			break;
272974079a53SRobert Mustacchi 		}
273074079a53SRobert Mustacchi 
273174079a53SRobert Mustacchi 		ret = 0;
273274079a53SRobert Mustacchi 		break;
273374079a53SRobert Mustacchi 	case MC_IOC_SNAPSHOT:
273474079a53SRobert Mustacchi 		imc_nvl_pack(sock, B_FALSE);
273574079a53SRobert Mustacchi 		if (sock->isock_buf == NULL) {
273674079a53SRobert Mustacchi 			ret = EIO;
273774079a53SRobert Mustacchi 			break;
273874079a53SRobert Mustacchi 		}
273974079a53SRobert Mustacchi 
274074079a53SRobert Mustacchi 		if (ddi_copyout(sock->isock_buf, (void *)arg,
274174079a53SRobert Mustacchi 		    sock->isock_buflen, mode) != 0) {
274274079a53SRobert Mustacchi 			ret = EFAULT;
274374079a53SRobert Mustacchi 			break;
274474079a53SRobert Mustacchi 		}
274574079a53SRobert Mustacchi 
274674079a53SRobert Mustacchi 		ret = 0;
274774079a53SRobert Mustacchi 		break;
274874079a53SRobert Mustacchi 	case MC_IOC_DECODE_SNAPSHOT_INFO:
274974079a53SRobert Mustacchi 		imc_decoder_pack(imc);
275074079a53SRobert Mustacchi 		if (imc->imc_decoder_buf == NULL) {
275174079a53SRobert Mustacchi 			ret = EIO;
275274079a53SRobert Mustacchi 			break;
275374079a53SRobert Mustacchi 		}
275474079a53SRobert Mustacchi 
275574079a53SRobert Mustacchi 		info.mcs_size = imc->imc_decoder_len;
275674079a53SRobert Mustacchi 		info.mcs_gen = imc->imc_spointers[0]->isock_gen;
275774079a53SRobert Mustacchi 
275874079a53SRobert Mustacchi 		if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0) {
275974079a53SRobert Mustacchi 			ret = EFAULT;
276074079a53SRobert Mustacchi 			break;
276174079a53SRobert Mustacchi 		}
276274079a53SRobert Mustacchi 
276374079a53SRobert Mustacchi 		ret = 0;
276474079a53SRobert Mustacchi 		break;
276574079a53SRobert Mustacchi 	case MC_IOC_DECODE_SNAPSHOT:
276674079a53SRobert Mustacchi 		imc_decoder_pack(imc);
276774079a53SRobert Mustacchi 		if (imc->imc_decoder_buf == NULL) {
276874079a53SRobert Mustacchi 			ret = EIO;
276974079a53SRobert Mustacchi 			break;
277074079a53SRobert Mustacchi 		}
277174079a53SRobert Mustacchi 
277274079a53SRobert Mustacchi 		if (ddi_copyout(imc->imc_decoder_buf, (void *)arg,
277374079a53SRobert Mustacchi 		    imc->imc_decoder_len, mode) != 0) {
277474079a53SRobert Mustacchi 			ret = EFAULT;
277574079a53SRobert Mustacchi 			break;
277674079a53SRobert Mustacchi 		}
277774079a53SRobert Mustacchi 
277874079a53SRobert Mustacchi 		ret = 0;
277974079a53SRobert Mustacchi 		break;
278074079a53SRobert Mustacchi 	case MC_IOC_DECODE_PA:
278174079a53SRobert Mustacchi 		if (crgetzoneid(credp) != GLOBAL_ZONEID ||
278274079a53SRobert Mustacchi 		    drv_priv(credp) != 0) {
278374079a53SRobert Mustacchi 			ret = EPERM;
278474079a53SRobert Mustacchi 			break;
278574079a53SRobert Mustacchi 		}
278674079a53SRobert Mustacchi 
278774079a53SRobert Mustacchi 		if (ddi_copyin((void *)arg, &encode, sizeof (encode),
278874079a53SRobert Mustacchi 		    mode & FKIOCTL) != 0) {
278974079a53SRobert Mustacchi 			ret = EPERM;
279074079a53SRobert Mustacchi 			break;
279174079a53SRobert Mustacchi 		}
279274079a53SRobert Mustacchi 
279374079a53SRobert Mustacchi 		imc_ioctl_decode(imc, &encode);
279474079a53SRobert Mustacchi 		ret = 0;
279574079a53SRobert Mustacchi 
279674079a53SRobert Mustacchi 		if (ddi_copyout(&encode, (void *)arg, sizeof (encode),
279774079a53SRobert Mustacchi 		    mode & FKIOCTL) != 0) {
279874079a53SRobert Mustacchi 			ret = EPERM;
279974079a53SRobert Mustacchi 			break;
280074079a53SRobert Mustacchi 		}
280174079a53SRobert Mustacchi 		break;
280274079a53SRobert Mustacchi 	default:
280374079a53SRobert Mustacchi 		ret = EINVAL;
280474079a53SRobert Mustacchi 		goto done;
280574079a53SRobert Mustacchi 	}
280674079a53SRobert Mustacchi 
280774079a53SRobert Mustacchi done:
280874079a53SRobert Mustacchi 	mutex_exit(&imc->imc_lock);
280974079a53SRobert Mustacchi 	return (ret);
281074079a53SRobert Mustacchi }
281174079a53SRobert Mustacchi 
281274079a53SRobert Mustacchi static int
imc_close(dev_t dev,int flag,int otyp,cred_t * credp)281374079a53SRobert Mustacchi imc_close(dev_t dev, int flag, int otyp, cred_t *credp)
281474079a53SRobert Mustacchi {
281574079a53SRobert Mustacchi 	return (0);
281674079a53SRobert Mustacchi }
281774079a53SRobert Mustacchi 
281874079a53SRobert Mustacchi static int
imc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)281974079a53SRobert Mustacchi imc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
282074079a53SRobert Mustacchi {
282174079a53SRobert Mustacchi 	if (cmd != DDI_ATTACH) {
282274079a53SRobert Mustacchi 		return (DDI_FAILURE);
282374079a53SRobert Mustacchi 	}
282474079a53SRobert Mustacchi 
282574079a53SRobert Mustacchi 	if (imc_data == NULL || imc_data->imc_dip != NULL) {
282674079a53SRobert Mustacchi 		return (DDI_FAILURE);
282774079a53SRobert Mustacchi 	}
282874079a53SRobert Mustacchi 
282974079a53SRobert Mustacchi 	mutex_enter(&imc_data->imc_lock);
283074079a53SRobert Mustacchi 	if ((imc_data->imc_taskq = ddi_taskq_create(dip, "imc", 1,
283174079a53SRobert Mustacchi 	    TASKQ_DEFAULTPRI, 0)) == NULL) {
283274079a53SRobert Mustacchi 		mutex_exit(&imc_data->imc_lock);
283374079a53SRobert Mustacchi 		return (DDI_FAILURE);
283474079a53SRobert Mustacchi 	}
283574079a53SRobert Mustacchi 
283674079a53SRobert Mustacchi 	imc_data->imc_dip = dip;
283774079a53SRobert Mustacchi 	imc_data->imc_flags |= IMC_F_SCAN_DISPATCHED;
283874079a53SRobert Mustacchi 	mutex_exit(&imc_data->imc_lock);
283974079a53SRobert Mustacchi 
284074079a53SRobert Mustacchi 	(void) ddi_taskq_dispatch(imc_data->imc_taskq, imc_stub_scan, imc_data,
284174079a53SRobert Mustacchi 	    DDI_SLEEP);
284274079a53SRobert Mustacchi 
284374079a53SRobert Mustacchi 	return (DDI_SUCCESS);
284474079a53SRobert Mustacchi }
284574079a53SRobert Mustacchi 
284674079a53SRobert Mustacchi /*
284774079a53SRobert Mustacchi  * We only export a single instance.
284874079a53SRobert Mustacchi  */
284974079a53SRobert Mustacchi static int
imc_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** resultp)285074079a53SRobert Mustacchi imc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
285174079a53SRobert Mustacchi {
285274079a53SRobert Mustacchi 	/*
285374079a53SRobert Mustacchi 	 * getinfo(9E) shouldn't be called if we're not attached. But be
285474079a53SRobert Mustacchi 	 * paranoid.
285574079a53SRobert Mustacchi 	 */
285674079a53SRobert Mustacchi 	if (imc_data == NULL || imc_data->imc_dip == NULL) {
285774079a53SRobert Mustacchi 		return (DDI_FAILURE);
285874079a53SRobert Mustacchi 	}
285974079a53SRobert Mustacchi 
286074079a53SRobert Mustacchi 	switch (infocmd) {
286174079a53SRobert Mustacchi 	case DDI_INFO_DEVT2DEVINFO:
286274079a53SRobert Mustacchi 		*resultp = imc_data->imc_dip;
286374079a53SRobert Mustacchi 		break;
286474079a53SRobert Mustacchi 	case DDI_INFO_DEVT2INSTANCE:
286574079a53SRobert Mustacchi 		*resultp = (void *)0;
286674079a53SRobert Mustacchi 		break;
286774079a53SRobert Mustacchi 	default:
286874079a53SRobert Mustacchi 		return (DDI_FAILURE);
286974079a53SRobert Mustacchi 	}
287074079a53SRobert Mustacchi 
287174079a53SRobert Mustacchi 	return (DDI_SUCCESS);
287274079a53SRobert Mustacchi }
287374079a53SRobert Mustacchi 
287474079a53SRobert Mustacchi static int
imc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)287574079a53SRobert Mustacchi imc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
287674079a53SRobert Mustacchi {
287774079a53SRobert Mustacchi 	if (cmd != DDI_DETACH) {
287874079a53SRobert Mustacchi 		return (DDI_FAILURE);
287974079a53SRobert Mustacchi 	}
288074079a53SRobert Mustacchi 
288174079a53SRobert Mustacchi 	if (imc_data == NULL || imc_data->imc_dip) {
288274079a53SRobert Mustacchi 		return (DDI_FAILURE);
288374079a53SRobert Mustacchi 	}
288474079a53SRobert Mustacchi 
288574079a53SRobert Mustacchi 	mutex_enter(&imc_data->imc_lock);
288674079a53SRobert Mustacchi 
288774079a53SRobert Mustacchi 	/*
288874079a53SRobert Mustacchi 	 * While a scan or attach is outstanding, don't allow us to detach.
288974079a53SRobert Mustacchi 	 */
289074079a53SRobert Mustacchi 	if ((imc_data->imc_flags &
289174079a53SRobert Mustacchi 	    (IMC_F_SCAN_DISPATCHED | IMC_F_ATTACH_DISPATCHED)) != 0) {
289274079a53SRobert Mustacchi 		mutex_exit(&imc_data->imc_lock);
289374079a53SRobert Mustacchi 		return (DDI_FAILURE);
289474079a53SRobert Mustacchi 	}
289574079a53SRobert Mustacchi 
289674079a53SRobert Mustacchi 	/*
289774079a53SRobert Mustacchi 	 * Because the stub driver depends on the imc driver, we shouldn't be
289874079a53SRobert Mustacchi 	 * able to have any entries in this list when we detach. However, we
289974079a53SRobert Mustacchi 	 * check just to make sure.
290074079a53SRobert Mustacchi 	 */
290174079a53SRobert Mustacchi 	if (!avl_is_empty(&imc_data->imc_stubs)) {
290274079a53SRobert Mustacchi 		mutex_exit(&imc_data->imc_lock);
290374079a53SRobert Mustacchi 		return (DDI_FAILURE);
290474079a53SRobert Mustacchi 	}
290574079a53SRobert Mustacchi 
290674079a53SRobert Mustacchi 	nvlist_free(imc_data->imc_decoder_dump);
290774079a53SRobert Mustacchi 	imc_data->imc_decoder_dump = NULL;
290874079a53SRobert Mustacchi 	if (imc_data->imc_decoder_buf != NULL) {
290974079a53SRobert Mustacchi 		kmem_free(imc_data->imc_decoder_buf, imc_data->imc_decoder_len);
291074079a53SRobert Mustacchi 		imc_data->imc_decoder_buf = NULL;
291174079a53SRobert Mustacchi 		imc_data->imc_decoder_len = 0;
291274079a53SRobert Mustacchi 	}
291374079a53SRobert Mustacchi 
291474079a53SRobert Mustacchi 	ddi_remove_minor_node(imc_data->imc_dip, NULL);
291574079a53SRobert Mustacchi 	imc_data->imc_dip = NULL;
291674079a53SRobert Mustacchi 	mutex_exit(&imc_data->imc_lock);
291774079a53SRobert Mustacchi 
291874079a53SRobert Mustacchi 	ddi_taskq_wait(imc_data->imc_taskq);
291974079a53SRobert Mustacchi 	ddi_taskq_destroy(imc_data->imc_taskq);
292074079a53SRobert Mustacchi 	imc_data->imc_taskq = NULL;
292174079a53SRobert Mustacchi 
292274079a53SRobert Mustacchi 	return (DDI_SUCCESS);
292374079a53SRobert Mustacchi }
292474079a53SRobert Mustacchi 
292574079a53SRobert Mustacchi static void
imc_free(void)292674079a53SRobert Mustacchi imc_free(void)
292774079a53SRobert Mustacchi {
292874079a53SRobert Mustacchi 	if (imc_data == NULL) {
292974079a53SRobert Mustacchi 		return;
293074079a53SRobert Mustacchi 	}
293174079a53SRobert Mustacchi 
293274079a53SRobert Mustacchi 	VERIFY(avl_is_empty(&imc_data->imc_stubs));
293374079a53SRobert Mustacchi 	avl_destroy(&imc_data->imc_stubs);
293474079a53SRobert Mustacchi 	mutex_destroy(&imc_data->imc_lock);
293574079a53SRobert Mustacchi 	kmem_free(imc_data, sizeof (imc_t));
293674079a53SRobert Mustacchi 	imc_data = NULL;
293774079a53SRobert Mustacchi }
293874079a53SRobert Mustacchi 
293974079a53SRobert Mustacchi static void
imc_alloc(void)294074079a53SRobert Mustacchi imc_alloc(void)
294174079a53SRobert Mustacchi {
294274079a53SRobert Mustacchi 	imc_data = kmem_zalloc(sizeof (imc_t), KM_SLEEP);
294374079a53SRobert Mustacchi 
294474079a53SRobert Mustacchi 	mutex_init(&imc_data->imc_lock, NULL, MUTEX_DRIVER, NULL);
294574079a53SRobert Mustacchi 	avl_create(&imc_data->imc_stubs, imc_stub_comparator,
294674079a53SRobert Mustacchi 	    sizeof (imc_stub_t), offsetof(imc_stub_t, istub_link));
294774079a53SRobert Mustacchi }
294874079a53SRobert Mustacchi 
294974079a53SRobert Mustacchi static struct cb_ops imc_cb_ops = {
295074079a53SRobert Mustacchi 	.cb_open = imc_open,
295174079a53SRobert Mustacchi 	.cb_close = imc_close,
295274079a53SRobert Mustacchi 	.cb_strategy = nodev,
295374079a53SRobert Mustacchi 	.cb_print = nodev,
295474079a53SRobert Mustacchi 	.cb_dump = nodev,
295574079a53SRobert Mustacchi 	.cb_read = nodev,
295674079a53SRobert Mustacchi 	.cb_write = nodev,
295774079a53SRobert Mustacchi 	.cb_ioctl = imc_ioctl,
295874079a53SRobert Mustacchi 	.cb_devmap = nodev,
295974079a53SRobert Mustacchi 	.cb_mmap = nodev,
296074079a53SRobert Mustacchi 	.cb_segmap = nodev,
296174079a53SRobert Mustacchi 	.cb_chpoll = nochpoll,
296274079a53SRobert Mustacchi 	.cb_prop_op = ddi_prop_op,
296374079a53SRobert Mustacchi 	.cb_flag = D_MP,
296474079a53SRobert Mustacchi 	.cb_rev = CB_REV,
296574079a53SRobert Mustacchi 	.cb_aread = nodev,
296674079a53SRobert Mustacchi 	.cb_awrite = nodev
296774079a53SRobert Mustacchi };
296874079a53SRobert Mustacchi 
296974079a53SRobert Mustacchi static struct dev_ops imc_dev_ops = {
297074079a53SRobert Mustacchi 	.devo_rev = DEVO_REV,
297174079a53SRobert Mustacchi 	.devo_refcnt = 0,
297274079a53SRobert Mustacchi 	.devo_getinfo = imc_getinfo,
297374079a53SRobert Mustacchi 	.devo_identify = nulldev,
297474079a53SRobert Mustacchi 	.devo_probe = nulldev,
297574079a53SRobert Mustacchi 	.devo_attach = imc_attach,
297674079a53SRobert Mustacchi 	.devo_detach = imc_detach,
297774079a53SRobert Mustacchi 	.devo_reset = nodev,
297874079a53SRobert Mustacchi 	.devo_cb_ops = &imc_cb_ops,
297974079a53SRobert Mustacchi 	.devo_quiesce = ddi_quiesce_not_needed
298074079a53SRobert Mustacchi };
298174079a53SRobert Mustacchi 
298274079a53SRobert Mustacchi static struct modldrv imc_modldrv = {
298374079a53SRobert Mustacchi 	.drv_modops = &mod_driverops,
298474079a53SRobert Mustacchi 	.drv_linkinfo = "Intel Integrated Memory Controller Driver",
298574079a53SRobert Mustacchi 	.drv_dev_ops = &imc_dev_ops
298674079a53SRobert Mustacchi };
298774079a53SRobert Mustacchi 
298874079a53SRobert Mustacchi static struct modlinkage imc_modlinkage = {
298974079a53SRobert Mustacchi 	.ml_rev = MODREV_1,
299074079a53SRobert Mustacchi 	.ml_linkage = { &imc_modldrv, NULL }
299174079a53SRobert Mustacchi };
299274079a53SRobert Mustacchi 
299374079a53SRobert Mustacchi int
_init(void)299474079a53SRobert Mustacchi _init(void)
299574079a53SRobert Mustacchi {
299674079a53SRobert Mustacchi 	int ret;
299774079a53SRobert Mustacchi 
299874079a53SRobert Mustacchi 	if ((ret = mod_install(&imc_modlinkage)) == 0) {
299974079a53SRobert Mustacchi 		imc_alloc();
300074079a53SRobert Mustacchi 	}
300174079a53SRobert Mustacchi 
300274079a53SRobert Mustacchi 	return (ret);
300374079a53SRobert Mustacchi }
300474079a53SRobert Mustacchi 
300574079a53SRobert Mustacchi int
_info(struct modinfo * modinfop)300674079a53SRobert Mustacchi _info(struct modinfo *modinfop)
300774079a53SRobert Mustacchi {
300874079a53SRobert Mustacchi 	return (mod_info(&imc_modlinkage, modinfop));
300974079a53SRobert Mustacchi }
301074079a53SRobert Mustacchi 
301174079a53SRobert Mustacchi int
_fini(void)301274079a53SRobert Mustacchi _fini(void)
301374079a53SRobert Mustacchi {
301474079a53SRobert Mustacchi 	int ret;
301574079a53SRobert Mustacchi 
301674079a53SRobert Mustacchi 	if ((ret = mod_remove(&imc_modlinkage)) == 0) {
301774079a53SRobert Mustacchi 		imc_free();
301874079a53SRobert Mustacchi 	}
301974079a53SRobert Mustacchi 	return (ret);
302074079a53SRobert Mustacchi }
3021