summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/sys
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@fingolfin.org>2022-02-28 09:46:53 +0000
committerRobert Mustacchi <rm@fingolfin.org>2022-07-20 15:48:24 +0000
commit71815ce76261aa773c97600750fdce92334d1990 (patch)
tree3585ce8ffb6f3db3198f1d218191a3b36ee1918c /usr/src/uts/intel/sys
parent1bcd6a1a4eeaf2fd7a90ce8b8cebd4f34baf049f (diff)
downloadillumos-joyent-71815ce76261aa773c97600750fdce92334d1990.tar.gz
14727 Want AMD Unified Memory Controller Driver
Reviewed by: Keith M Wesolowski <wesolows@oxide.computer> Reviewed by: Richard Lowe <richlowe@richlowe.net> Reviewed by: C Fraire <cfraire@me.com> Approved by: Garrett D'Amore <garrett@damore.org>
Diffstat (limited to 'usr/src/uts/intel/sys')
-rw-r--r--usr/src/uts/intel/sys/amdzen/df.h896
-rw-r--r--usr/src/uts/intel/sys/amdzen/umc.h390
-rw-r--r--usr/src/uts/intel/sys/mc.h48
-rw-r--r--usr/src/uts/intel/sys/x86_archext.h11
4 files changed, 1341 insertions, 4 deletions
diff --git a/usr/src/uts/intel/sys/amdzen/df.h b/usr/src/uts/intel/sys/amdzen/df.h
new file mode 100644
index 0000000000..6c1e5b5c79
--- /dev/null
+++ b/usr/src/uts/intel/sys/amdzen/df.h
@@ -0,0 +1,896 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2022 Oxide Computer Company
+ */
+
+#ifndef _SYS_AMDZEN_DF_H
+#define _SYS_AMDZEN_DF_H
+
+/*
+ * This file contains definitions for the registers that appears in the AMD Zen
+ * Data Fabric. The data fabric is the main component which routes transactions
+ * between entities (e.g. CPUS, DRAM, PCIe, etc.) in the system. The data fabric
+ * itself is made up of up to 8 PCI functions. There can be multiple instances
+ * of the data fabric. There is one instance per die. In most AMD processors
+ * after Zen 1, there is only a single die per socket, for more background see
+ * the uts/i86pc/os/cpuid.c big theory statement. All data fabric instances
+ * appear on PCI bus 0. The first instance shows up on device 0x18. Subsequent
+ * instances simply increment that number by one.
+ *
+ * There are currently four major revisions of the data fabric that are
+ * supported here, which are v2 (Zen 1), v3 (Zen 2/3), v3.5 (Zen 2/3 with DDR5),
+ * and v4 (Zen 4). In many cases, while the same logical thing exists in
+ * different generations, they often have different shapes and sometimes things
+ * with the same shape show up in different locations.
+ *
+ * To make things a little easier for clients, each register definition encodes
+ * enough information to also include which hardware generations it supports,
+ * the actual PCI function it appears upon, and the register offset. This is to
+ * make sure that consumers don't have to guess some of this information in the
+ * latter cases and we can try to guarantee we're not accessing an incorrect
+ * register for our platform (unfortunately at runtime).
+ *
+ * Register definitions have the following form:
+ *
+ * DF_<reg name>_<vers>
+ *
+ * Here <reg name> is something that describes the register. This may not be the
+ * exact same as the PPR (processor programming reference); however, the PPR
+ * name for the register will be included above it in a comment (though these
+ * have sometimes changed from time to time). For example, DF_DRAM_HOLE. If a
+ * given register is the same in all currently supported versions, then there is
+ * no version suffix appended. Otherwise, the first version it is supported in
+ * is appended. For example, DF_DRAM_BASE_V2, DF_DRAM_BASE_V3, DF_DRAM_BASE_V4,
+ * etc. or DF_FIDMASK0_V3P5, etc. If the register offset is the same in multiple
+ * versions, then there they share the earliest version.
+ *
+ * For fields there are currently macros to extract these or chain them together
+ * leveraging bitx32() and bitset32(). Fields have the forms:
+ *
+ * DF_<reg name>_<vers>_GET_<field>
+ * DF_<reg name>_<vers>_SET_<field>
+ *
+ * Like in the above, if there are cases where a single field is the same across
+ * all versions, then the <vers> portion will be elided. There are many cases
+ * where the register definition does not change, but the fields themselves do
+ * change with each version because each hardware rev opts to be slightly
+ * different.
+ *
+ * When adding support for a new chip, please look carefully through the
+ * requisite documentation to ensure that they match what we see here. There are
+ * often cases where there may be a subtle thing or you hit a case like V3P5
+ * that until you dig deeper just seem to be weird.
+ */
+
+#include <sys/bitext.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum df_rev {
+ DF_REV_UNKNOWN = 0,
+ DF_REV_2 = 1 << 0,
+ DF_REV_3 = 1 << 1,
+ DF_REV_3P5 = 1 << 2,
+ DF_REV_4 = 1 << 3
+} df_rev_t;
+
+#define DF_REV_ALL_23 (DF_REV_2 | DF_REV_3 | DF_REV_3P5)
+#define DF_REV_ALL (DF_REV_2 | DF_REV_3 | DF_REV_3P5 | DF_REV_4)
+
+typedef struct df_reg_def {
+ df_rev_t drd_gens;
+ uint8_t drd_func;
+ uint16_t drd_reg;
+} df_reg_def_t;
+
+/*
+ * This set of registers provides us access to the count of instances in the
+ * data fabric and then a number of different pieces of information about them
+ * like their type. Note, these registers require indirect access because the
+ * information cannot be broadcast.
+ */
+
+/*
+ * DF::FabricBlockInstanceCount -- Describes the number of instances in the data
+ * fabric. With v4, also includes versioning information.
+ */
+/*CSTYLED*/
+#define DF_FBICNT (df_reg_def_t){ .drd_gens = DF_REV_ALL, \
+ .drd_func = 0, .drd_reg = 0x40 }
+#define DF_FBICNT_V4_GET_MAJOR(r) bitx32(r, 27, 24)
+#define DF_FBICNT_V4_GET_MINOR(r) bitx32(r, 23, 16)
+#define DF_FBICNT_GET_COUNT(r) bitx32(r, 7, 0)
+
+/*
+ * DF::FabricBlockInstanceInformation0 -- get basic information about a fabric
+ * instance.
+ */
+/*CSTYLED*/
+#define DF_FBIINFO0 (df_reg_def_t){ .drd_gens = DF_REV_ALL, \
+ .drd_func = 0, .drd_reg = 0x44 }
+#define DF_FBIINFO0_GET_SUBTYPE(r) bitx32(r, 26, 24)
+#define DF_SUBTYPE_NONE 0
+typedef enum {
+ DF_CAKE_SUBTYPE_GMI = 1,
+ DF_CAKE_SUBTYPE_xGMI = 2
+} df_cake_subtype_t;
+
+typedef enum {
+ DF_IOM_SUBTYPE_IOHUB = 1,
+} df_iom_subtype_t;
+
+typedef enum {
+ DF_CS_SUBTYPE_UMC = 1,
+ /*
+ * The subtype changed beginning in DFv4. Prior to DFv4, the secondary
+ * type was CCIX. Starting with DFv4, this is now CMP. It is unclear if
+ * these are the same thing or not.
+ */
+ DF_CS_SUBTYPE_CCIX = 2,
+ DF_CS_SUBTYPE_CMP = 2
+} df_cs_subtype_t;
+
+/*
+ * Note, this only exists in Genoa (maybe more generally Zen 4), otherwise it's
+ * always zero.
+ */
+typedef enum {
+ DF_CCM_SUBTYPE_CPU = 1,
+ DF_CCM_SUBTYPE_ACM = 2
+} df_ccm_subtype_v4_t;
+#define DF_FBIINFO0_GET_HAS_MCA(r) bitx32(r, 23, 23)
+#define DF_FBIINFO0_GET_FTI_DCNT(r) bitx32(r, 21, 20)
+#define DF_FBIINFO0_GET_FTI_PCNT(r) bitx32(r, 18, 16)
+#define DF_FBIINFO0_GET_SDP_RESPCNT(r) bitx32(r, 14, 14)
+#define DF_FBIINFO0_GET_SDP_PCNT(r) bitx32(r, 13, 12)
+#define DF_FBIINFO0_GET_FTI_WIDTH(r) bitx32(r, 9, 8)
+typedef enum {
+ DF_FTI_W_64 = 0,
+ DF_FTI_W_128,
+ DF_FTI_W_256,
+ DF_FTI_W_512
+} df_fti_width_t;
+#define DF_FBIINFO0_V3_GET_ENABLED(r) bitx32(r, 6, 6)
+#define DF_FBIINFO0_GET_SDP_WIDTH(r) bitx32(r, 5, 4)
+typedef enum {
+ DF_SDP_W_64 = 0,
+ DF_SDP_W_128,
+ DF_SDP_W_256,
+ DF_SDP_W_512
+} df_sdp_width_t;
+#define DF_FBIINFO0_GET_TYPE(r) bitx32(r, 3, 0)
+typedef enum {
+ DF_TYPE_CCM = 0,
+ DF_TYPE_GCM,
+ DF_TYPE_NCM,
+ DF_TYPE_IOMS,
+ DF_TYPE_CS,
+ DF_TYPE_NCS,
+ DF_TYPE_TCDX,
+ DF_TYPE_PIE,
+ DF_TYPE_SPF,
+ DF_TYPE_LLC,
+ DF_TYPE_CAKE,
+ DF_TYPE_CNLI = 0xd,
+} df_type_t;
+
+/*
+ * DF::FabricBlockInstanceInformation1 -- get basic information about a fabric
+ * instance.
+ */
+/*CSTYLED*/
+#define DF_FBIINFO1 (df_reg_def_t){ .drd_gens = DF_REV_ALL, \
+ .drd_func = 0, .drd_reg = 0x48 }
+#define DF_FBINFO1_GET_FTI3_NINSTID(r) bitx32(r, 31, 24)
+#define DF_FBINFO1_GET_FTI2_NINSTID(r) bitx32(r, 23, 16)
+#define DF_FBINFO1_GET_FTI1_NINSTID(r) bitx32(r, 15, 8)
+#define DF_FBINFO1_GET_FTI0_NINSTID(r) bitx32(r, 7, 0)
+
+/*
+ * DF::FabricBlockInstanceInformation2 -- get basic information about a fabric
+ * instance.
+ */
+/*CSTYLED*/
+#define DF_FBIINFO2 (df_reg_def_t){ .drd_gens = DF_REV_ALL, \
+ .drd_func = 0, .drd_reg = 0x4c }
+#define DF_FBINFO2_GET_FTI5_NINSTID(r) bitx32(r, 15, 8)
+#define DF_FBINFO2_GET_FTI4_NINSTID(r) bitx32(r, 7, 0)
+
+/*
+ * DF::FabricBlockInstanceInformation3 -- obtain the basic IDs for a given
+ * instance.
+ */
+/*CSTYLED*/
+#define DF_FBIINFO3 (df_reg_def_t){ .drd_gens = DF_REV_ALL, \
+ .drd_func = 0, .drd_reg = 0x50 }
+#define DF_FBIINFO3_V2_GET_BLOCKID(r) bitx32(r, 15, 8)
+#define DF_FBIINFO3_V3_GET_BLOCKID(r) bitx32(r, 13, 8)
+#define DF_FBIINFO3_V3P5_GET_BLOCKID(r) bitx32(r, 11, 8)
+#define DF_FBIINFO3_V4_GET_BLOCKID(r) bitx32(r, 19, 8)
+#define DF_FBIINFO3_GET_INSTID(r) bitx32(r, 7, 0)
+
+/*
+ * DF::Skt0CsTargetRemap0, DF::Skt0CsTargetRemap1, DF::Skt1CsTargetRemap0,
+ * DF::Skt1CsTargetRemap1 -- The next set of registers provide access to
+ * chip-select remapping. Caution, while these have a documented DF generation
+ * that they are specific to, it seems they still aren't always implemented and
+ * are specific to Milan (v3) and Genoa (v4). The actual remap extraction is the
+ * same between both.
+ */
+#define DF_CS_REMAP_GET_CSX(r, x) bitx32(r, (3 + (4 * (x))), (4 * ((x))))
+/*CSTYLED*/
+#define DF_SKT0_CS_REMAP0_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 0, .drd_reg = 0x60 }
+/*CSTYLED*/
+#define DF_SKT1_CS_REMAP0_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 0, .drd_reg = 0x68 }
+/*CSTYLED*/
+#define DF_SKT0_CS_REMAP1_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 0, .drd_reg = 0x64 }
+/*CSTYLED*/
+#define DF_SKT1_CS_REMAP1_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 0, .drd_reg = 0x6c }
+/*
+ * DF::CsTargetRemap0A, DF::CsTargetRemap0B, etc. -- These registers contain the
+ * remap engines in DFv4. Note, that while v3 used 0/1 as REMAP[01], as
+ * referring to the same logical set of things, here [0-3] is used for different
+ * things and A/B distinguish the different actual CS values.
+ */
+/*CSTYLED*/
+#define DF_CS_REMAP0A_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x180 }
+/*CSTYLED*/
+#define DF_CS_REMAP0B_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x184 }
+/*CSTYLED*/
+#define DF_CS_REMAP1A_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x188 }
+/*CSTYLED*/
+#define DF_CS_REMAP1B_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x18c }
+/*CSTYLED*/
+#define DF_CS_REMAP2A_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x190 }
+/*CSTYLED*/
+#define DF_CS_REMAP2B_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x194 }
+/*CSTYLED*/
+#define DF_CS_REMAP3A_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x198 }
+/*CSTYLED*/
+#define DF_CS_REMAP3B_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, .drd_reg = 0x19c }
+/*
+ * DF::CfgAddressCntl -- This register contains the information about the
+ * configuration of PCIe buses. We care about finding which one has our BUS A,
+ * which is required to map it to the in-package northbridge instance.
+ */
+/*CSTYLED*/
+#define DF_CFG_ADDR_CTL_V2 (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x84 }
+/*CSTYLED*/
+#define DF_CFG_ADDR_CTL_V4 (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0xc04 }
+#define DF_CFG_ADDR_CTL_GET_BUS_NUM(r) bitx32(r, 7, 0)
+
+/*
+ * DF::CfgAddressMap -- This next set of registers covers PCI Bus configuration
+ * address maps. The layout here changes at v4. This routes a given PCI bus to a
+ * device.
+ */
+/*CSTYLED*/
+#define DF_CFGMAP_V2(x) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0xa0 + ((x) * 4) }
+#define DF_MAX_CFGMAP 8
+#define DF_CFGMAP_V2_GET_BUS_LIMIT(r) bitx32(r, 31, 24)
+#define DF_CFGMAP_V2_GET_BUS_BASE(r) bitx32(r, 23, 16)
+#define DF_CFGMAP_V2_GET_DEST_ID(r) bitx32(r, 11, 4)
+#define DF_CFGMAP_V3_GET_DEST_ID(r) bitx32(r, 13, 4)
+#define DF_CFGMAP_V3P5_GET_DEST_ID(r) bitx32(r, 7, 4)
+#define DF_CFGMAP_V2_GET_WE(r) bitx32(r, 1, 1)
+#define DF_CFGMAP_V2_GET_RE(r) bitx32(r, 0, 0)
+
+/*
+ * DF::CfgBaseAddress, DF::CfgLimitAddress -- DFv4 variants of the above now in
+ * two registers and more possible entries!
+ */
+/*CSTYLED*/
+#define DF_CFGMAP_BASE_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xc80 + ((x) * 8) }
+/*CSTYLED*/
+#define DF_CFGMAP_LIMIT_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xc84 + ((x) * 8) }
+#define DF_CFGMAP_BASE_V4_GET_BASE(r) bitx32(r, 23, 16)
+#define DF_CFGMAP_BASE_V4_GET_SEG(r) bitx32(r, 15, 8)
+#define DF_CFGMAP_BASE_V4_GET_WE(r) bitx32(r, 1, 1)
+#define DF_CFGMAP_BASE_V4_GET_RE(r) bitx32(r, 0, 0)
+#define DF_CFGMAP_LIMIT_V4_GET_LIMIT(r) bitx32(r, 23, 16)
+#define DF_CFGMAP_LIMIT_V4_GET_DEST_ID(r) bitx32(r, 11, 0)
+
+/*
+ * DF::X86IOBaseAddress, DF::X86IOLimitAddress -- Base and limit registers for
+ * routing I/O space. These are fairly similar prior to DFv4.
+ */
+/*CSTYLED*/
+#define DF_IO_BASE_V2(x) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0xc0 + ((x) * 8) }
+/*CSTYLED*/
+#define DF_IO_BASE_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xd00 + ((x) * 8) }
+#define DF_MAX_IO_RULES 8
+#define DF_IO_BASE_SHIFT 12
+#define DF_IO_BASE_V2_GET_BASE(r) bitx32(r, 24, 12)
+#define DF_IO_BASE_V2_GET_IE(r) bitx32(r, 5, 5)
+#define DF_IO_BASE_V2_GET_WE(r) bitx32(r, 1, 1)
+#define DF_IO_BASE_V2_GET_RE(r) bitx32(r, 0, 0)
+#define DF_IO_BASE_V2_SET_BASE(r, v) bitset32(r, 24, 12, v)
+#define DF_IO_BASE_V2_SET_IE(r, v) bitset32(r, 5, 5, v)
+#define DF_IO_BASE_V2_SET_WE(r, v) bitset32(r, 1, 1, v)
+#define DF_IO_BASE_V2_SET_RE(r, v) bitset32(r, 0, 0, v)
+
+#define DF_IO_BASE_V4_GET_BASE(r) bitx32(r, 28, 16)
+#define DF_IO_BASE_V4_GET_IE(r) bitx32(r, 5, 5)
+#define DF_IO_BASE_V4_GET_WE(r) bitx32(r, 1, 1)
+#define DF_IO_BASE_V4_GET_RE(r) bitx32(r, 0, 0)
+#define DF_IO_BASE_V4_SET_BASE(r, v) bitset32(r, 28, 16, v)
+#define DF_IO_BASE_V4_SET_IE(r, v) bitset32(r, 5, 5, v)
+#define DF_IO_BASE_V4_SET_WE(r, v) bitset32(r, 1, 1, v)
+#define DF_IO_BASE_V4_SET_RE(r, v) bitset32(r, 0, 0, v)
+
+/*CSTYLED*/
+#define DF_IO_LIMIT_V2(x) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0xc4 + ((x) * 8) }
+/*CSTYLED*/
+#define DF_IO_LIMIT_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xd04 + ((x) * 8) }
+#define DF_MAX_IO_LIMIT ((1 << 24) - 1)
+#define DF_IO_LIMIT_SHIFT 12
+#define DF_IO_LIMIT_EXCL (1 << DF_IO_LIMIT_SHIFT)
+#define DF_IO_LIMIT_V2_GET_LIMIT(r) bitx32(r, 24, 12)
+#define DF_IO_LIMIT_V2_GET_DEST_ID(r) bitx32(r, 7, 0)
+#define DF_IO_LIMIT_V3_GET_DEST_ID(r) bitx32(r, 9, 0)
+#define DF_IO_LIMIT_V3P5_GET_DEST_ID(r) bitx32(r, 3, 0)
+#define DF_IO_LIMIT_V2_SET_LIMIT(r, v) bitset32(r, 24, 12, v)
+#define DF_IO_LIMIT_V2_SET_DEST_ID(r, v) bitset32(r, 7, 0, v)
+#define DF_IO_LIMIT_V3_SET_DEST_ID(r, v) bitset32(r, 9, 0, v)
+#define DF_IO_LIMIT_V3P5_SET_DEST_ID(r, v) bitset32(r, 3, 0, v)
+
+#define DF_IO_LIMIT_V4_GET_LIMIT(r) bitx32(r, 28, 16)
+#define DF_IO_LIMIT_V4_GET_DEST_ID(r) bitx32(r, 11, 0)
+#define DF_IO_LIMIT_V4_SET_LIMIT(r, v) bitset32(r, 28, 16, v)
+#define DF_IO_LIMIT_V4_SET_DEST_ID(r, v) bitset32(r, 11, 0, v)
+
+/*
+ * DF::DramHoleControl -- This controls MMIO below 4 GiB. Note, both this and
+ * the Top of Memory (TOM) need to be set consistently.
+ */
+/*CSTYLED*/
+#define DF_DRAM_HOLE_V2 (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x104 }
+/*CSTYLED*/
+#define DF_DRAM_HOLE_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, \
+ .drd_reg = 0x104 }
+#define DF_DRAM_HOLE_GET_BASE(r) bitx32(r, 31, 24)
+#define DF_DRAM_HOLE_BASE_SHIFT 24
+#define DF_DRAM_HOLE_GET_VALID(r) bitx32(r, 0, 0)
+
+/*
+ * DF::DramBaseAddress, DF::DramLimitAddress -- DRAM rules, these are split into
+ * a base and limit. While DFv2, 3, and 3.5 all have the same addresses, they
+ * have different bit patterns entirely. DFv4 is in a different location and
+ * further splits this into four registers. We do all of the pre-DFv4 stuff and
+ * follow with DFv4. In DFv2-3.5 the actual values of the bits (e.g. the meaning
+ * of the channel interleave value) are the same, even though where those bits
+ * are in the register changes.
+ *
+ * In DF v2, v3, and v3.5 the set of constants for interleave values are the
+ * same, so we define them once at the v2 version.
+ */
+/*CSTYLED*/
+#define DF_DRAM_BASE_V2(r) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x110 + ((r) * 8) }
+#define DF_DRAM_BASE_V2_GET_BASE(r) bitx32(r, 31, 12)
+#define DF_DRAM_BASE_V2_BASE_SHIFT 28
+#define DF_DRAM_BASE_V2_GET_ILV_ADDR(r) bitx32(r, 10, 8)
+#define DF_DRAM_BASE_V2_GET_ILV_CHAN(r) bitx32(r, 7, 4)
+#define DF_DRAM_BASE_V2_ILV_CHAN_1 0x0
+#define DF_DRAM_BASE_V2_ILV_CHAN_2 0x1
+#define DF_DRAM_BASE_V2_ILV_CHAN_4 0x3
+#define DF_DRAM_BASE_V2_ILV_CHAN_8 0x5
+#define DF_DRAM_BASE_V2_ILV_CHAN_6 0x6
+#define DF_DRAM_BASE_V2_ILV_CHAN_COD4_2 0xc
+#define DF_DRAM_BASE_V2_ILV_CHAN_COD2_4 0xd
+#define DF_DRAM_BASE_V2_ILV_CHAN_COD1_8 0xe
+#define DF_DRAM_BASE_V2_GET_HOLE_EN(r) bitx32(r, 1, 1)
+#define DF_DRAM_BASE_V2_GET_VALID(r) bitx32(r, 0, 0)
+
+#define DF_DRAM_BASE_V3_GET_ILV_ADDR(r) bitx32(r, 11, 9)
+#define DF_DRAM_BASE_V3_GET_ILV_SOCK(r) bitx32(r, 8, 8)
+#define DF_DRAM_BASE_V3_GET_ILV_DIE(r) bitx32(r, 7, 6)
+#define DF_DRAM_BASE_V3_GET_ILV_CHAN(r) bitx32(r, 5, 2)
+
+#define DF_DRAM_BASE_V3P5_GET_ILV_ADDR(r) bitx32(r, 11, 9)
+#define DF_DRAM_BASE_V3P5_GET_ILV_SOCK(r) bitx32(r, 8, 8)
+#define DF_DRAM_BASE_V3P5_GET_ILV_DIE(r) bitx32(r, 7, 7)
+#define DF_DRAM_BASE_V3P5_GET_ILV_CHAN(r) bitx32(r, 6, 2)
+
+/*
+ * Shared definitions for the DF DRAM interleaving address start bits. While the
+ * bitfield / register definition is different between DFv2/3/3.5 and DFv4, the
+ * actual contents of the base address register and the base are shared.
+ */
+#define DF_DRAM_ILV_ADDR_8 0
+#define DF_DRAM_ILV_ADDR_9 1
+#define DF_DRAM_ILV_ADDR_10 2
+#define DF_DRAM_ILV_ADDR_11 3
+#define DF_DRAM_ILV_ADDR_12 4
+#define DF_DRAM_ILV_ADDR_BASE 8
+
+/*CSTYLED*/
+#define DF_DRAM_LIMIT_V2(r) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x114 + ((r) * 8) }
+#define DF_DRAM_LIMIT_V2_GET_LIMIT(r) bitx32(r, 31, 12)
+#define DF_DRAM_LIMIT_V2_LIMIT_SHIFT 28
+#define DF_DRAM_LIMIT_V2_LIMIT_EXCL (1 << 28)
+/* These are in the base register for v3, v3.5 */
+#define DF_DRAM_LIMIT_V2_GET_ILV_DIE(r) bitx32(r, 11, 10)
+#define DF_DRAM_LIMIT_V2_GET_ILV_SOCK(r) bitx32(r, 8, 8)
+#define DF_DRAM_LIMIT_V2_GET_DEST_ID(r) bitx32(r, 7, 0)
+
+#define DF_DRAM_LIMIT_V3_GET_BUS_BREAK(r) bitx32(r, 10, 10)
+#define DF_DRAM_LIMIT_V3_GET_DEST_ID(r) bitx32(r, 9, 0)
+
+#define DF_DRAM_LIMIT_V3P5_GET_DEST_ID(r) bitx32(r, 3, 0)
+
+/*
+ * DF::DramBaseAddress, DF::DramLimitAddress, DF::DramAddressCtl,
+ * DF::DramAddressIntlv -- DFv4 edition. Here all the controls around the
+ * target, interleaving, hashing, and more is split out from the base and limit
+ * registers and put into dedicated control and interleave registers.
+ */
+/*CSTYLED*/
+#define DF_DRAM_BASE_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, \
+ .drd_reg = 0xe00 + ((x) * 0x10) }
+#define DF_DRAM_BASE_V4_GET_ADDR(r) bitx32(r, 27, 0)
+#define DF_DRAM_BASE_V4_BASE_SHIFT 28
+/*CSTYLED*/
+#define DF_DRAM_LIMIT_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, \
+ .drd_reg = 0xe04 + ((x) * 0x10) }
+#define DF_DRAM_LIMIT_V4_GET_ADDR(r) bitx32(r, 27, 0)
+#define DF_DRAM_LIMIT_V4_LIMIT_SHIFT 28
+#define DF_DRAM_LIMIT_V4_LIMIT_EXCL (1 << 28)
+
+/*CSTYLED*/
+#define DF_DRAM_CTL_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, \
+ .drd_reg = 0xe08 + ((x) * 0x10) }
+#define DF_DRAM_CTL_V4_GET_DEST_ID(r) bitx32(r, 27, 16)
+#define DF_DRAM_CTL_V4_GET_HASH_1G(r) bitx32(r, 10, 10)
+#define DF_DRAM_CTL_V4_GET_HASH_2M(r) bitx32(r, 9, 9)
+#define DF_DRAM_CTL_V4_GET_HASH_64K(r) bitx32(r, 8, 8)
+#define DF_DRAM_CTL_V4_GET_REMAP_SEL(r) bitx32(r, 7, 5)
+#define DF_DRAM_CTL_V4_GET_REMAP_EN(r) bitx32(r, 4, 4)
+#define DF_DRAM_CTL_V4_GET_SCM(r) bitx32(r, 2, 2)
+#define DF_DRAM_CTL_V4_GET_HOLE_EN(r) bitx32(r, 1, 1)
+#define DF_DRAM_CTL_V4_GET_VALID(r) bitx32(r, 0, 0)
+
+/*CSTYLED*/
+#define DF_DRAM_ILV_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, \
+ .drd_reg = 0xe0c + ((x) * 0x10) }
+#define DF_DRAM_ILV_V4_GET_SOCK(r) bitx32(r, 18, 18)
+#define DF_DRAM_ILV_V4_GET_DIE(r) bitx32(r, 13, 12)
+#define DF_DRAM_ILV_V4_GET_CHAN(r) bitx32(r, 8, 4)
+#define DF_DRAM_ILV_V4_CHAN_1 0x0
+#define DF_DRAM_ILV_V4_CHAN_2 0x1
+#define DF_DRAM_ILV_V4_CHAN_4 0x3
+#define DF_DRAM_ILV_V4_CHAN_8 0x5
+#define DF_DRAM_ILV_V4_CHAN_16 0x7
+#define DF_DRAM_ILV_V4_CHAN_32 0x8
+#define DF_DRAM_ILV_V4_CHAN_NPS4_2CH 0x10
+#define DF_DRAM_ILV_V4_CHAN_NPS2_4CH 0x11
+#define DF_DRAM_ILV_V4_CHAN_NPS1_8CH 0x12
+#define DF_DRAM_ILV_V4_CHAN_NPS4_3CH 0x13
+#define DF_DRAM_ILV_V4_CHAN_NPS2_6CH 0x14
+#define DF_DRAM_ILV_V4_CHAN_NPS1_12CH 0x15
+#define DF_DRAM_ILV_V4_CHAN_NPS2_5CH 0x16
+#define DF_DRAM_ILV_V4_CHAN_NPS1_10CH 0x17
+#define DF_DRAM_ILV_V4_GET_ADDR(r) bitx32(r, 2, 0)
+
+/*
+ * DF::DramOffset -- These exist only for CS entries, e.g. a UMC. There is
+ * generally only one of these in Zen 1-3. This register changes in Zen 4 and
+ * there are up to 3 instances there. This register corresponds to each DRAM
+ * rule that the UMC has starting at the second one. This is because the first
+ * DRAM rule in a channel always is defined to start at offset 0, so there is no
+ * entry here.
+ */
+/*CSTYLED*/
+#define DF_DRAM_OFFSET_V2 (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x1b4 }
+/*CSTYLED*/
+#define DF_DRAM_OFFSET_V4(r) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 7, \
+ .drd_reg = 0x404 + ((r) * 4) }
+#define DF_DRAM_OFFSET_V2_GET_OFFSET(r) bitx32(r, 31, 20)
+#define DF_DRAM_OFFSET_V3_GET_OFFSET(r) bitx32(r, 31, 12)
+#define DF_DRAM_OFFSET_V4_GET_OFFSET(r) bitx32(r, 24, 1)
+#define DF_DRAM_OFFSET_SHIFT 28
+#define DF_DRAM_OFFSET_GET_EN(r) bitx32(r, 0, 0)
+
+/*
+ * DF::MmioBaseAddress, DF::MmioLimitAddress, DF::MmioAddressControl -- These
+ * control the various MMIO rules for a given system.
+ */
+/*CSTYLED*/
+#define DF_MMIO_BASE_V2(x) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x200 + ((x) * 0x10) }
+/*CSTYLED*/
+#define DF_MMIO_LIMIT_V2(x) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x204 + ((x) * 0x10) }
+/*CSTYLED*/
+#define DF_MMIO_BASE_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xd80 + ((x) * 0x10) }
+/*CSTYLED*/
+#define DF_MMIO_LIMIT_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xd84 + ((x) * 0x10) }
+#define DF_MMIO_SHIFT 16
+#define DF_MMIO_LIMIT_EXCL (1 << DF_MMIO_SHIFT)
+#define DF_MAX_MMIO_RULES 16
+/*CSTYLED*/
+#define DF_MMIO_CTL_V2(x) (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 0, \
+ .drd_reg = 0x208 + ((x) * 0x10) }
+/*CSTYLED*/
+#define DF_MMIO_CTL_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xd88 + ((x) * 0x10) }
+#define DF_MMIO_CTL_V2_GET_NP(r) bitx32(r, 12, 12)
+#define DF_MMIO_CTL_V2_GET_DEST_ID(r) bitx32(r, 11, 4)
+#define DF_MMIO_CTL_V2_SET_NP(r, v) bitset32(r, 12, 12, v)
+#define DF_MMIO_CTL_V2_SET_DEST_ID(r, v) bitset32(r, 11, 4, v)
+
+#define DF_MMIO_CTL_V3_GET_NP(r) bitx32(r, 16, 16)
+#define DF_MMIO_CTL_V3_GET_DEST_ID(r) bitx32(r, 13, 4)
+#define DF_MMIO_CTL_V3P5_GET_DEST_ID(r) bitx32(r, 7, 4)
+#define DF_MMIO_CTL_V3_SET_NP(r, v) bitset32(r, 16, 16, v)
+#define DF_MMIO_CTL_V3_SET_DEST_ID(r, v) bitset32(r, 13, 4, v)
+#define DF_MMIO_CTL_V3P5_SET_DEST_ID(r, v) bitset32(r, 7, 4, v)
+
+#define DF_MMIO_CTL_V4_GET_DEST_ID(r) bitx32(r, 27, 16)
+#define DF_MMIO_CTL_V4_GET_NP(r) bitx32(r, 3, 3)
+#define DF_MMIO_CTL_V4_SET_DEST_ID(r, v) bitset32(r, 27, 16, v)
+#define DF_MMIO_CTL_V4_SET_NP(r, v) bitset32(r, 3, 3, v)
+
+#define DF_MMIO_CTL_GET_CPU_DIS(r) bitx32(r, 2, 2)
+#define DF_MMIO_CTL_GET_WE(r) bitx32(r, 1, 1)
+#define DF_MMIO_CTL_GET_RE(r) bitx32(r, 0, 0)
+#define DF_MMIO_CTL_SET_CPU_DIS(r, v) bitset32(r, 2, 2, v)
+#define DF_MMIO_CTL_SET_WE(r, v) bitset32(r, 1, 1, v)
+#define DF_MMIO_CTL_SET_RE(r, v) bitset32(r, 0, 0, v)
+
+/*
+ * DF::MmioExtAddress -- New in DFv4, this allows extending the number of bits
+ * used for MMIO.
+ */
+/*CSTYLED*/
+#define DF_MMIO_EXT_V4(x) (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 0, \
+ .drd_reg = 0xd8c + ((x) * 0x10) }
+#define DF_MMIO_EXT_V4_GET_LIMIT(r) bitx32(r, 23, 16)
+#define DF_MMIO_EXT_V4_GET_BASE(r) bitx32(r, 7, 0)
+#define DF_MMIO_EXT_V4_SET_LIMIT(r) bitset32(r, 23, 16)
+#define DF_MMIO_EXT_V4_SET_BASE(r) bitset32(r, 7, 0)
+
+/*
+ * DF::DfGlobalCtrl -- This register we generally only care about in the
+ * DFv3/3.5 timeframe when it has the actual hash controls, hence its current
+ * definition. It technically exists in DFv2/v4, but is not relevant.
+ */
+/*CSTYLED*/
+#define DF_GLOB_CTL_V3 (df_reg_def_t){ .drd_gens = DF_REV_3 | \
+ DF_REV_3P5, \
+ .drd_func = 0, \
+ .drd_reg = 0x3F8 }
+#define DF_GLOB_CTL_V3_GET_HASH_1G(r) bitx32(r, 22, 22)
+#define DF_GLOB_CTL_V3_GET_HASH_2M(r) bitx32(r, 21, 21)
+#define DF_GLOB_CTL_V3_GET_HASH_64K(r) bitx32(r, 20, 20)
+
+/*
+ * DF::SystemCfg -- This register describes the basic information about the data
+ * fabric that we're talking to. Don't worry, this is different in every
+ * generation, even when the address is the same. Somehow despite all these
+ * differences the actual things like defined types are somehow the same.
+ */
+typedef enum {
+ DF_DIE_TYPE_CPU = 0,
+ DF_DIE_TYPE_APU,
+ DF_DIE_TYPE_dGPU
+} df_die_type_t;
+
+/*CSTYLED*/
+#define DF_SYSCFG_V2 (df_reg_def_t){ .drd_gens = DF_REV_2, \
+ .drd_func = 1, \
+ .drd_reg = 0x200 }
+#define DF_SYSCFG_V2_GET_SOCK_ID(r) bitx32(r, 27, 27)
+#define DF_SYSCFG_V2_GET_DIE_ID(r) bitx32(r, 25, 24)
+#define DF_SYSCFG_V2_GET_MY_TYPE(r) bitx32(r, 22, 21)
+#define DF_SYSCFG_V2_GET_LOCAL_IS_ME(r) bitx32(r, 19, 16)
+#define DF_SYSCFG_V2_GET_LOCAL_TYPE3(r) bitx32(r, 13, 12)
+#define DF_SYSCFG_V2_GET_LOCAL_TYPE2(r) bitx32(r, 11, 10)
+#define DF_SYSCFG_V2_GET_LOCAL_TYPE1(r) bitx32(r, 9, 8)
+#define DF_SYSCFG_V2_GET_LOCAL_TYPE0(r) bitx32(r, 7, 6)
+#define DF_SYSCFG_V2_GET_OTHER_SOCK(r) bitx32(r, 5, 5)
+#define DF_SYSCFG_V2_GET_DIE_PRESENT(r) bitx32(r, 4, 0)
+#define DF_SYSCFG_V2_DIE_PRESENT(x) bitx32(r, 3, 0)
+
+/*CSTYLED*/
+#define DF_SYSCFG_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 1, \
+ .drd_reg = 0x200 }
+#define DF_SYSCFG_V3_GET_NODE_ID(r) bitx32(r, 30, 28)
+#define DF_SYSCFG_V3_GET_OTHER_SOCK(r) bitx32(r, 27, 27)
+#define DF_SYSCFG_V3_GET_OTHER_TYPE(r) bitx32(r, 26, 25)
+#define DF_SYSCFG_V3_GET_MY_TYPE(r) bitx32(r, 24, 23)
+#define DF_SYSCFG_V3_GET_DIE_TYPE(r) bitx32(r, 18, 11)
+#define DF_SYSCFG_V3_GET_DIE_PRESENT(r) bitx32(r, 7, 0)
+
+/*CSTYLED*/
+#define DF_SYSCFG_V3P5 (df_reg_def_t){ .drd_gens = DF_REV_3P5, \
+ .drd_func = 1, \
+ .drd_reg = 0x140 }
+#define DF_SYSCFG_V3P5_GET_NODE_ID(r) bitx32(r, 19, 16)
+#define DF_SYSCFG_V3P5_GET_OTHER_SOCK(r) bitx32(r, 8, 8)
+#define DF_SYSCFG_V3P5_GET_NODE_MAP(r) bitx32(r, 4, 4)
+#define DF_SYSCFG_V3P5_GET_OTHER_TYPE(r) bitx32(r, 3, 2)
+#define DF_SYSCFG_V3P5_GET_MY_TYPE(r) bitx32(r, 1, 0)
+
+/*CSTYLED*/
+#define DF_SYSCFG_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 4, \
+ .drd_reg = 0x180 }
+#define DF_SYSCFG_V4_GET_NODE_ID(r) bitx32(r, 27, 16)
+#define DF_SYSCFG_V4_GET_OTHER_SOCK(r) bitx32(r, 8, 8)
+#define DF_SYSCFG_V4_GET_NODE_MAP(r) bitx32(r, 4, 4)
+#define DF_SYSCFG_V4_GET_OTHER_TYPE(r) bitx32(r, 3, 2)
+#define DF_SYSCFG_V4_GET_MY_TYPE(r) bitx32(r, 1, 0)
+
+/*
+ * DF::SystemComponentCnt -- Has a count of how many things are here. However,
+ * this does not seem defined for DFv3.5
+ */
+/*CSTYLED*/
+#define DF_COMPCNT_V2 (df_reg_def_t){ .drd_gens = DF_REV_2 | \
+ DF_REV_3, \
+ .drd_func = 1, \
+ .drd_reg = 0x204 }
+#define DF_COMPCNT_V2_GET_IOMS(r) bitx32(r, 23, 16)
+#define DF_COMPCNT_V2_GET_GCM(r) bitx32(r, 15, 8)
+#define DF_COMPCNT_V2_GET_PIE(r) bitx32(r, 7, 0)
+
+/*CSTYLED*/
+#define DF_COMPCNT_V4 (df_reg_def_t){ .drd_gens = DF_REV_4 \
+ .drd_func = 4, \
+ .drd_reg = 0x184 }
+#define DF_COMPCNT_V4_GET_IOS(r) bitx32(r, 31, 26)
+#define DF_COMPCNT_V4_GET_GCM(r) bitx32(r, 25, 16)
+#define DF_COMPCNT_V4_GET_IOM(r) bitx32(r, 15, 8)
+#define DF_COMPCNT_V4_GET_PIE(r) bitx32(r, 7, 0)
+
+/*
+ * This next section contains a bunch of register definitions for how to take
+ * apart ID masks. The register names and sets have changed across every DF
+ * revision. This will be done in chunks that define all DFv2, then v3, etc.
+ */
+
+/*
+ * DF::SystemFabricIdMask -- DFv2 style breakdowns of IDs. Note, unlike others
+ * the socket and die shifts are not relative to a node mask, but are global.
+ */
+/*CSTYLED*/
+#define DF_FIDMASK_V2 (df_reg_def_t){ .drd_gens = DF_REV_2, \
+ .drd_func = 1, \
+ .drd_reg = 0x208 }
+#define DF_FIDMASK_V2_GET_SOCK_SHIFT(r) bitx32(r, 31, 28)
+#define DF_FIDMASK_V2_GET_DIE_SHIFT(r) bitx32(r, 27, 24)
+#define DF_FIDMASK_V2_GET_SOCK_MASK(r) bitx32(r, 23, 16)
+#define DF_FIDMASK_V2_GET_DIE_MASK(r) bitx32(r, 15, 8)
+
+/*
+ * DF::SystemFabricIdMask0, DF::SystemFabricIdMask1 -- The DFv3 variant of
+ * breaking down an ID into bits and shifts. Unlike in DFv2, the socket and die
+ * are relative to a node ID. For more, see amdzen_determine_fabric_decomp() in
+ * uts/intel/io/amdzen/amdzen.c.
+ */
+/*CSTYLED*/
+#define DF_FIDMASK0_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 1, \
+ .drd_reg = 0x208 }
+#define DF_FIDMASK0_V3_GET_NODE_MASK(r) bitx32(r, 25, 16)
+#define DF_FIDMASK0_V3_GET_COMP_MASK(r) bitx32(r, 9, 0)
+/*CSTYLED*/
+#define DF_FIDMASK1_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 1, \
+ .drd_reg = 0x20c }
+#define DF_FIDMASK1_V3_GET_SOCK_MASK(r) bitx32(r, 26, 24)
+#define DF_FIDMASK1_V3_GET_DIE_MASK(r) bitx32(r, 18, 16)
+#define DF_FIDMASK1_V3_GET_SOCK_SHIFT(r) bitx32(r, 9, 8)
+#define DF_FIDMASK1_V3_GET_NODE_SHIFT(r) bitx32(r, 3, 0)
+
+/*
+ * DF::SystemFabricIdMask0, DF::SystemFabricIdMask1, DF::SystemFabricIdMask2 --
+ * DFv3.5 and DFv4 have the same format here, but in different registers.
+ */
+/*CSTYLED*/
+#define DF_FIDMASK0_V3P5 (df_reg_def_t){ .drd_gens = DF_REV_3P5, \
+ .drd_func = 1, \
+ .drd_reg = 0x150 }
+/*CSTYLED*/
+#define DF_FIDMASK0_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 4, \
+ .drd_reg = 0x1b0 }
+#define DF_FIDMASK0_V3P5_GET_NODE_MASK(r) bitx32(r, 31, 16)
+#define DF_FIDMASK0_V3P5_GET_COMP_MASK(r) bitx32(r, 15, 0)
+/*CSTYLED*/
+#define DF_FIDMASK1_V3P5 (df_reg_def_t){ .drd_gens = DF_REV_3P5, \
+ .drd_func = 1, \
+ .drd_reg = 0x154 }
+/*CSTYLED*/
+#define DF_FIDMASK1_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 4, \
+ .drd_reg = 0x1b4 }
+#define DF_FIDMASK1_V3P5_GET_SOCK_SHIFT(r) bitx32(r, 11, 8)
+#define DF_FIDMASK1_V3P5_GET_NODE_SHIFT(r) bitx32(r, 3, 0)
+/*CSTYLED*/
+#define DF_FIDMASK2_V3P5 (df_reg_def_t){ .drd_gens = DF_REV_3P5, \
+ .drd_func = 1, \
+ .drd_reg = 0x158 }
+/*CSTYLED*/
+#define DF_FIDMASK2_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 4, \
+ .drd_reg = 0x1b8 }
+#define DF_FIDMASK2_V3P5_GET_SOCK_MASK(r) bitx32(r, 31, 16)
+#define DF_FIDMASK2_V3P5_GET_DIE_MASK(r) bitx32(r, 15, 0)
+
+/*
+ * DF::DieFabricIdMask -- This is a Zeppelin, DFv2 special. There are a couple
+ * instances of this for different types of devices; however, this is where the
+ * component mask is actually stored. This is replicated for a CPU, APU, and
+ * dGPU, each with slightly different values. We need to look at DF_SYSCFG_V2 to
+ * determine which type of die we have and use the appropriate one when looking
+ * at this. This makes the Zen 1 CPUs and APUs have explicitly different set up
+ * here. Look, it got better in DFv3.
+ */
+/*CSTYLED*/
+#define DF_DIEMASK_CPU_V2 (df_reg_def_t){ .drd_gens = DF_REV_2, \
+ .drd_func = 1, \
+ .drd_reg = 0x22c }
+/*CSTYLED*/
+#define DF_DIEMASK_APU_V2 (df_reg_def_t){ .drd_gens = DF_REV_2, \
+ .drd_func = 1, \
+ .drd_reg = 0x24c }
+#define DF_DIEMASK_V2_GET_SOCK_SHIFT(r) bitx32(r, 31, 28)
+#define DF_DIEMASK_V2_GET_DIE_SHIFT(r) bitx32(r, 27, 24)
+#define DF_DIEMASK_V2_GET_SOCK_MASK(r) bitx32(r, 23, 16)
+#define DF_DIEMASK_V2_GET_DIE_MASK(r) bitx32(r, 15, 8)
+#define DF_DIEMASK_V2_GET_COMP_MASK(r) bitx32(r, 7, 0)
+
+
+/*
+ * DF::PhysicalCoreEnable0, etc. -- These registers can be used to tell us which
+ * cores are actually enabled. We know these exist in DFv3 and v4. It is less
+ * clear in DFv3.5 and DFv2.
+ */
+/*CSTYLED*/
+#define DF_PHYS_CORE_EN0_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 1, \
+ .drd_reg = 0x300 }
+/*CSTYLED*/
+#define DF_PHYS_CORE_EN1_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 1, \
+ .drd_reg = 0x304 }
+/*CSTYLED*/
+#define DF_PHYS_CORE_EN0_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 1, \
+ .drd_reg = 0x140 }
+/*CSTYLED*/
+#define DF_PHYS_CORE_EN1_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 1, \
+ .drd_reg = 0x144 }
+/*CSTYLED*/
+#define DF_PHYS_CORE_EN2_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 1, \
+ .drd_reg = 0x148 }
+
+/*
+ * DF::Np2ChannelConfig -- This is used in Milan to contain information about
+ * how non-power of 2 based channel configuration works. Note, we only know that
+ * this exists in Milan (and its ThreadRipper equivalent). We don't believe it
+ * is in other DFv3 products like Rome, Matisse, Vermeer, or the APUs.
+ */
+/*CSTYLED*/
+#define DF_NP2_CONFIG_V3 (df_reg_def_t){ .drd_gens = DF_REV_3, \
+ .drd_func = 2, \
+ .drd_reg = 0x90 }
+#define DF_NP2_CONFIG_V3_GET_SPACE1(r) bitx32(r, 13, 8)
+#define DF_NP2_CONFIG_V3_GET_SPACE0(r) bitx32(r, 5, 0)
+
+
+/*
+ * DF::FabricIndirectConfigAccessAddress, DF::FabricIndirectConfigAccessDataLo,
+ * DF::FabricIndirectConfigAccessDataHi -- These registers are used to define
+ * Indirect Access, commonly known as FICAA and FICAD for the system. While
+ * there are multiple copies of the indirect access registers in device 4, we're
+ * only allowed access to one set of those (which are the ones present here).
+ * Specifically the OS is given access to set 3.
+ */
+/*CSTYLED*/
+#define DF_FICAA_V2 (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 4, \
+ .drd_reg = 0x5c }
+/*CSTYLED*/
+#define DF_FICAA_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 4, \
+ .drd_reg = 0x8c }
+#define DF_FICAA_V2_SET_INST(r, v) bitset32(r, 23, 16, v)
+#define DF_FICAA_V2_SET_64B(r, v) bitset32(r, 14, 14, v)
+#define DF_FICAA_V2_SET_FUNC(r, v) bitset32(r, 13, 11, v)
+#define DF_FICAA_V2_SET_REG(r, v) bitset32(r, 10, 2, v)
+#define DF_FICAA_V2_SET_TARG_INST(r, v) bitset32(r, 0, 0, v)
+
+#define DF_FICAA_V4_SET_REG(r, v) bitset32(r, 10, 1, v)
+
+/*CSTYLED*/
+#define DF_FICAD_LO_V2 (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 4, \
+ .drd_reg = 0x98}
+/*CSTYLED*/
+#define DF_FICAD_HI_V2 (df_reg_def_t){ .drd_gens = DF_REV_ALL_23, \
+ .drd_func = 4, \
+ .drd_reg = 0x9c}
+/*CSTYLED*/
+#define DF_FICAD_LO_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 4, \
+ .drd_reg = 0xb8}
+/*CSTYLED*/
+#define DF_FICAD_HI_V4 (df_reg_def_t){ .drd_gens = DF_REV_4, \
+ .drd_func = 4, \
+ .drd_reg = 0xbc}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_AMDZEN_DF_H */
diff --git a/usr/src/uts/intel/sys/amdzen/umc.h b/usr/src/uts/intel/sys/amdzen/umc.h
new file mode 100644
index 0000000000..78644442d4
--- /dev/null
+++ b/usr/src/uts/intel/sys/amdzen/umc.h
@@ -0,0 +1,390 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2022 Oxide Computer Company
+ */
+
+#ifndef _SYS_UMC_H
+#define _SYS_UMC_H
+
+#include <sys/bitext.h>
+
+/*
+ * Various register definitions for accessing the AMD Unified Memory Controller
+ * (UMC) over SMN (the system management network). Note, that the SMN exists
+ * independently in each die and must be accessed through the appropriate
+ * IOHC.
+ *
+ * There are effectively four different revisions of the UMC that we know about
+ * and support querying:
+ *
+ * o DDR4 capable APUs
+ * o DDR4 capable CPUs
+ * o DDR5 capable APUs
+ * o DDR5 capable CPUs
+ *
+ * In general for a given revision and generation of a controller (DDR4 vs.
+ * DDR5), all of the address layouts are the same whether it is for an APU or a
+ * CPU. The main difference is generally in the number of features. For example,
+ * most APUs may not support the same rank multiplication bits and related in a
+ * device. However, unlike the DF where everything changes, the main difference
+ * within a generation is just which bits are implemented. This makes it much
+ * easier to define UMC information.
+ *
+ * Between DDR4 and DDR5 based devices, the register locations have shifted;
+ * however, generally speaking, the registers themselves are actually the same.
+ * Registers here, similar to the DF, have a common form:
+ *
+ * UMC_<reg name>_<vers>
+ *
+ * Here, <reg name> would be something like 'BASE', for the UMC
+ * UMC::CH::BaseAddr register. <vers> is one of DDR4 or DDR5. When the same
+ * register is supported at the same address between versions, then <vers> is
+ * elided.
+ *
+ * For fields inside of these registers, everything follows the same pattern in
+ * <sys/amdzen/df.h> which is:
+ *
+ * UMC_<reg name>_<vers>_GET_<field>
+ *
+ * Note, <vers> will be elided if the register is the same between the DDR4 and
+ * DDR5 versions.
+ *
+ * Finally, a cautionary note. While the DF provided a way for us to determine
+ * what version something is, we have not determined a way to programmatically
+ * determine what something supports outside of making notes based on the
+ * family, model, and stepping CPUID information. Unfortunately, you must look
+ * towards the documentation and find what you need in the PPR (processor
+ * programming reference).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * UMC Channel registers. These are in SMN Space. DDR4 and DDR5 based UMCs share
+ * the same base address, somewhat surprisingly. This constructs the appropriate
+ * offset and ensures that a caller doesn't exceed the number of known instances
+ * of the register.
+ */
+static inline uint32_t
+amdzen_umc_smn_addr(uint8_t umcno, uint32_t base_reg, uint32_t nents,
+ uint32_t reginst)
+{
+ ASSERT3U(umcno, <, 12);
+ ASSERT3U(nents, >, reginst);
+
+ uint32_t base = 0x50000;
+ uint32_t reg = base_reg + reginst * 4;
+ return ((umcno << 20) + base + reg);
+}
+
+/*
+ * UMC::CH::BaseAddr, UMC::CH::BaseAddrSec -- determines the base address used
+ * to match a chip select. Instances 0/1 always refer to DIMM 0, while
+ * instances 2/3 always refer to DIMM 1.
+ */
+#define UMC_BASE(u, i) amdzen_umc_smn_addr(u, 0x00, 4, i)
+#define UMC_BASE_SEC(u, i) amdzen_umc_smn_addr(u, 0x10, 4, i)
+#define UMC_BASE_GET_ADDR(r) bitx32(r, 31, 1)
+#define UMC_BASE_ADDR_SHIFT 9
+#define UMC_BASE_GET_EN(r) bitx32(r, 0, 0)
+
+/*
+ * UMC::BaseAddrExt, UMC::BaseAddrSecExt -- The first of several extensions to
+ * registers that allow more address bits. Note, only present in some DDR5
+ * capable SoCs.
+ */
+#define UMC_BASE_EXT_DDR5(u, i) amdzen_umc_smn_addr(u, 0xb00, 4, i)
+#define UMC_BASE_EXT_SEC_DDR5(u, i) amdzen_umc_smn_addr(u, 0xb10, 4, i)
+#define UMC_BASE_EXT_GET_ADDR(r) bitx32(r, 7, 0)
+#define UMC_BASE_EXT_ADDR_SHIFT 40
+
+
+/*
+ * UMC::CH::AddrMask, UMC::CH::AddrMaskSec -- This register is used to compare
+ * the incoming address to see it matches the base. Tweaking what is used for
+ * match is often part of the interleaving strategy.
+ */
+#define UMC_MASK_DDR4(u, i) amdzen_umc_smn_addr(u, 0x20, 2, i)
+#define UMC_MASK_SEC_DDR4(u, i) amdzen_umc_smn_addr(u, 0x28, 2, i)
+#define UMC_MASK_DDR5(u, i) amdzen_umc_smn_addr(u, 0x20, 4, i)
+#define UMC_MASK_SEC_DDR5(u, i) amdzen_umc_smn_addr(u, 0x30, 4, i)
+#define UMC_MASK_GET_ADDR(r) bitx32(r, 31, 1)
+#define UMC_MASK_ADDR_SHIFT 9
+
+/*
+ * UMC::AddrMaskExt, UMC::AddrMaskSecExt -- Extended mask addresses.
+ */
+#define UMC_MASK_EXT_DDR5(u, i) amdzen_umc_smn_addr(u, 0xb20, 4, i)
+#define UMC_MASK_EXT_SEC_DDR5(u, i) amdzen_umc_smn_addr(u, 0xb30, 4, i)
+#define UMC_MASK_EXT_GET_ADDR(r) bitx32(r, 7, 0)
+#define UMC_MASK_EXT_ADDR_SHIFT 40
+
+/*
+ * UMC::CH::AddrCfg -- This register contains a number of bits that describe how
+ * the address is actually used, one per DIMM. Note, not all members are valid
+ * for all classes of DIMMs. It's worth calling out that the total number of
+ * banks value here describes the total number of banks on the entire chip, e.g.
+ * it is bank groups * banks/groups. Therefore to determine the number of
+ * banks/group you must subtract the number of bank group bits from the total
+ * number of bank bits.
+ */
+#define UMC_ADDRCFG_DDR4(u, i) amdzen_umc_smn_addr(u, 0x30, 2, i)
+#define UMC_ADDRCFG_DDR5(u, i) amdzen_umc_smn_addr(u, 0x40, 4, i)
+#define UMC_ADDRCFG_GET_NBANK_BITS(r) bitx32(r, 21, 20)
+#define UMC_ADDRCFG_NBANK_BITS_BASE 3
+#define UMC_ADDRCFG_GET_NCOL_BITS(r) bitx32(r, 19, 16)
+#define UMC_ADDRCFG_NCOL_BITS_BASE 5
+#define UMC_ADDRCFG_GET_NROW_BITS_LO(r) bitx32(r, 11, 8)
+#define UMC_ADDRCFG_NROW_BITS_LO_BASE 10
+#define UMC_ADDRCFG_GET_NBANKGRP_BITS(r) bitx32(r, 3, 2)
+
+#define UMC_ADDRCFG_DDR4_GET_NROW_BITS_HI(r) bitx32(r, 15, 12)
+#define UMC_ADDRCFG_DDR4_GET_NRM_BITS(r) bitx32(r, 5, 4)
+#define UMC_ADDRCFG_DDR5_GET_CSXOR(r) bitx32(r, 31, 30)
+#define UMC_ADDRCFG_DDR5_GET_NRM_BITS(r) bitx32(r, 6, 4)
+
+/*
+ * UMC::CH::AddrSel -- This register is used to program how the actual bits in
+ * the normalized address map to the row and bank. While the bank can select
+ * which bits in the normalized address are used to construct the bank number,
+ * row bits are contiguous from the starting number.
+ */
+#define UMC_ADDRSEL_DDR4(u, i) amdzen_umc_smn_addr(u, 0x40, 2, i)
+#define UMC_ADDRSEL_DDR5(u, i) amdzen_umc_smn_addr(u, 0x50, 4, i)
+#define UMC_ADDRSEL_GET_ROW_LO(r) bitx32(r, 27, 24)
+#define UMC_ADDRSEL_ROW_LO_BASE 12
+#define UMC_ADDRSEL_GET_BANK4(r) bitx32(r, 19, 16)
+#define UMC_ADDRSEL_GET_BANK3(r) bitx32(r, 15, 12)
+#define UMC_ADDRSEL_GET_BANK2(r) bitx32(r, 11, 8)
+#define UMC_ADDRSEL_GET_BANK1(r) bitx32(r, 7, 4)
+#define UMC_ADDRSEL_GET_BANK0(r) bitx32(r, 3, 0)
+#define UMC_ADDRSEL_BANK_BASE 5
+
+#define UMC_ADDRSEL_DDR4_GET_ROW_HI(r) bitx32(r, 31, 28)
+#define UMC_ADDRSEL_DDR4_ROW_HI_BASE 24
+
+/*
+ * UMC::CH::ColSelLo, UMC::CH::ColSelHi -- This register selects which address
+ * bits map to the various column select bits. These registers interleave so in
+ * the case of DDR4, it's 0x50, 0x54 for DIMM 0 lo, hi. Then 0x58, 0x5c for
+ * DIMM1. DDR5 based entries do something similar; however, instead of being
+ * per-DIMM, there is one of these for each CS.
+ *
+ * This leads to a somewhat odder construction for the maximum number of
+ * instances. Because amdzen_umc_smn_addr() assumes each register instance is 4
+ * bytes apart, we instead take the actual register instance and multiply it by
+ * 2. This means that in the DDR4 case we will always access what
+ * amdzen_umc_smn_addr() considers instance 0 and 2. In the DDR5 case this is 0,
+ * 2, 4, and 6. This means our maximum instance for both cases has to be one
+ * higher than this, 3 and 7 respectively. While technically you could use 4 and
+ * 8, this is a tighter bind.
+ */
+#define UMC_COLSEL_LO_DDR4(u, i) amdzen_umc_smn_addr(u, 0x50, 3, i * 2)
+#define UMC_COLSEL_HI_DDR4(u, i) amdzen_umc_smn_addr(u, 0x54, 3, i * 2)
+#define UMC_COLSEL_LO_DDR5(u, i) amdzen_umc_smn_addr(u, 0x60, 7, i * 2)
+#define UMC_COLSEL_HI_DDR5(u, i) amdzen_umc_smn_addr(u, 0x64, 7, i * 2)
+
+#define UMC_COLSEL_REMAP_GET_COL(r, x) bitx32(r, (3 + (4 * (x))), (4 * ((x))))
+#define UMC_COLSEL_LO_BASE 2
+#define UMC_COLSEL_HI_BASE 8
+
+/*
+ * UMC::CH::RmSel -- This register contains the bits that determine how the rank
+ * is determined. Which fields of this are valid vary a lot in the different
+ * parts. The DDR4 and DDR5 versions are different enough that we use totally
+ * disjoint definitions. It's also worth noting that DDR5 doesn't have a
+ * secondary version of this as it is included in the main register.
+ *
+ * In general, APUs have some of the MSBS (most significant bit swap) related
+ * fields; however, they do not have rank multiplication bits.
+ */
+#define UMC_RMSEL_DDR4(u, i) amdzen_umc_smn_addr(u, 0x70, 2, i)
+#define UMC_RMSEL_SEC_DDR4(u, i) amdzen_umc_smn_addr(u, 0x78, 2, i)
+#define UMC_RMSEL_DDR4_GET_INV_MSBO(r) bitx32(r, 19, 18)
+#define UMC_RMSEL_DDR4_GET_INV_MSBE(r) bitx32(r, 17, 16)
+#define UMC_RMSEL_DDR4_GET_RM2(r) bitx32(r, 11, 8)
+#define UMC_RMSEL_DDR4_GET_RM1(r) bitx32(r, 7, 4)
+#define UMC_RMSEL_DDR4_GET_RM0(r) bitx32(r, 3, 0)
+#define UMC_RMSEL_BASE 12
+
+#define UMC_RMSEL_DDR5(u, i) amdzen_umc_smn_addr(u, 0x80, 4, i)
+#define UMC_RMSEL_DDR5_GET_INV_MSBS_SEC(r) bitx32(r, 31, 30)
+#define UMC_RMSEL_DDR5_GET_INV_MSBS(r) bitx32(r, 29, 28)
+#define UMC_RMSEL_DDR5_GET_SUBCHAN(r) bitx32(r, 19, 16)
+#define UMC_RMSEL_DDR5_SUBCHAN_BASE 5
+#define UMC_RMSEL_DDR5_GET_RM3(r) bitx32(r, 15, 12)
+#define UMC_RMSEL_DDR5_GET_RM2(r) bitx32(r, 11, 8)
+#define UMC_RMSEL_DDR5_GET_RM1(r) bitx32(r, 7, 4)
+#define UMC_RMSEL_DDR5_GET_RM0(r) bitx32(r, 3, 0)
+
+
+/*
+ * UMC::CH::DimmCfg -- This describes several properties of the DIMM that is
+ * installed, such as its overall width or type.
+ */
+#define UMC_DIMMCFG_DDR4(u, i) amdzen_umc_smn_addr(u, 0x80, 2, i)
+#define UMC_DIMMCFG_DDR5(u, i) amdzen_umc_smn_addr(u, 0x90, 2, i)
+#define UMC_DIMMCFG_GET_PKG_RALIGN(r) bitx32(r, 10, 10)
+#define UMC_DIMMCFG_GET_REFRESH_DIS(r) bitx32(r, 9, 9)
+#define UMC_DIMMCFG_GET_DQ_SWAP_DIS(r) bitx32(r, 8, 8)
+#define UMC_DIMMCFG_GET_X16(r) bitx32(r, 7, 7)
+#define UMC_DIMMCFG_GET_X4(r) bitx32(r, 6, 6)
+#define UMC_DIMMCFG_GET_LRDIMM(r) bitx32(r, 5, 5)
+#define UMC_DIMMCFG_GET_RDIMM(r) bitx32(r, 4, 4)
+#define UMC_DIMMCFG_GET_CISCS(r) bitx32(r, 3, 3)
+#define UMC_DIMMCFG_GET_3DS(r) bitx32(r, 2, 2)
+
+#define UMC_DIMMCFG_DDR4_GET_NVDIMMP(r) bitx32(r, 12, 12)
+#define UMC_DIMMCFG_DDR4_GET_DDR4e(r) bitx32(r, 11, 11)
+#define UMC_DIMMCFG_DDR5_GET_RALIGN(r) bitx32(r, 13, 12)
+#define UMC_DIMMCFG_DDR5_GET_ASYM(r) bitx32(r, 11, 11)
+
+#define UMC_DIMMCFG_DDR4_GET_OUTPUT_INV(r) bitx32(r, 1, 1)
+#define UMC_DIMMCFG_DDR4_GET_MRS_MIRROR(r) bitx32(r, 0, 0)
+
+/*
+ * UMC::CH::AddrHashBank -- These registers contain various instructions about
+ * how to hash an address across a bank to influence which bank is used.
+ */
+#define UMC_BANK_HASH_DDR4(u, i) amdzen_umc_smn_addr(u, 0xc8, 5, i)
+#define UMC_BANK_HASH_DDR5(u, i) amdzen_umc_smn_addr(u, 0x98, 5, i)
+#define UMC_BANK_HASH_GET_ROW(r) bitx32(r, 31, 14)
+#define UMC_BANK_HASH_GET_COL(r) bitx32(r, 13, 1)
+#define UMC_BANK_HASH_GET_EN(r) bitx32(r, 0, 0)
+
+/*
+ * UMC::CH::AddrHashRM -- This hash register describes how to transform a UMC
+ * address when trying to do rank hashing. Note, instance 3 is is reserved in
+ * DDR5 modes.
+ */
+#define UMC_RANK_HASH_DDR4(u, i) amdzen_umc_smn_addr(u, 0xdc, 3, i)
+#define UMC_RANK_HASH_DDR5(u, i) amdzen_umc_smn_addr(u, 0xb0, 4, i)
+#define UMC_RANK_HASH_GET_ADDR(r) bitx32(r, 31, 1)
+#define UMC_RANK_HASH_SHIFT 9
+#define UMC_RANK_HASH_GET_EN(r) bitx32(r, 0, 0)
+
+/*
+ * UMC::AddrHashRMExt -- Extended rank hash addresses.
+ */
+#define UMC_RANK_HASH_EXT_DDR5(u, i) amdzen_umc_smn_addr(u, 0xbb0, 4, i)
+#define UMC_RANK_HASH_EXT_GET_ADDR(r) bitx32(r, 7, 0)
+#define UMC_RANK_HASH_EXT_ADDR_SHIFT 40
+
+/*
+ * UMC::CH::AddrHashPC, UMC::CH::AddrHashPC2 -- These registers describe a hash
+ * to use for the DDR5 sub-channel. Note, in the DDR4 case this is actually the
+ * upper two rank hash registers defined above because on the systems where this
+ * occurs for DDR4, they only have up to one rank hash.
+ */
+#define UMC_PC_HASH_DDR4(u) UMC_RANK_HASH_DDR4(u, 1)
+#define UMC_PC_HASH2_DDR4(u) UMC_RANK_HASH_DDR4(u, 2)
+#define UMC_PC_HASH_DDR5(u) amdzen_umc_smn_addr(u, 0xc0, 1, 0)
+#define UMC_PC_HASH2_DDR5(u) amdzen_umc_smn_addr(u, 0xc4, 1, 0)
+#define UMC_PC_HASH_GET_ROW(r) bitx32(r, 31, 14)
+#define UMC_PC_HASH_GET_COL(r) bitx32(r, 13, 1)
+#define UMC_PC_HASH_GET_EN(r) bitx32(r, 0, 0)
+#define UMC_PC_HASH2_GET_BANK(r) bitx32(r, 4, 0)
+
+/*
+ * UMC::CH::AddrHashCS -- Hashing: chip-select edition. Note, these can
+ * ultimately cause you to change which DIMM is being actually accessed.
+ */
+#define UMC_CS_HASH_DDR4(u, i) amdzen_umc_smn_addr(u, 0xe8, 2, i)
+#define UMC_CS_HASH_DDR5(u, i) amdzen_umc_smn_addr(u, 0xc8, 2, i)
+#define UMC_CS_HASH_GET_ADDR(r) bitx32(r, 31, 1)
+#define UMC_CS_HASH_SHIFT 9
+#define UMC_CS_HASH_GET_EN(r) bitx32(r, 0, 0)
+
+/*
+ * UMC::AddrHashExtCS -- Extended chip-select hash addresses.
+ */
+#define UMC_CS_HASH_EXT_DDR5(u, i) amdzen_umc_smn_addr(u, 0xbc8, 2, i)
+#define UMC_CS_HASH_EXT_GET_ADDR(r) bitx32(r, 7, 0)
+#define UMC_CS_HASH_EXT_ADDR_SHIFT 40
+
+/*
+ * UMC::CH::UmcConfig -- This register controls various features of the device.
+ * For our purposes we mostly care about seeing if ECC is enabled and a DIMM
+ * type.
+ */
+#define UMC_UMCCFG(u) amdzen_umc_smn_addr(u, 0x100, 1, 0)
+#define UMC_UMCCFG_GET_READY(r) bitx32(r, 31, 31)
+#define UMC_UMCCFG_GET_ECC_EN(r) bitx32(r, 12, 12)
+#define UMC_UMCCFG_GET_BURST_CTL(r) bitx32(r, 11, 10)
+#define UMC_UMCCFG_GET_BURST_LEN(r) bitx32(r, 9, 8)
+#define UMC_UMCCFG_GET_DDR_TYPE(r) bitx32(r, 2, 0)
+#define UMC_UMCCFG_DDR4_T_DDR4 0
+#define UMC_UMCCFG_DDR4_T_LPDDR4 5
+
+#define UMC_UMCCFG_DDR5_T_DDR4 0
+#define UMC_UMCCFG_DDR5_T_DDR5 1
+#define UMC_UMCCFG_DDR5_T_LPDDR4 5
+#define UMC_UMCCFG_DDR5_T_LPDDR5 6
+
+/*
+ * UMC::CH::DataCtrl -- Various settings around whether data encryption or
+ * scrambling is enabled. Note, this register really changes a bunch from family
+ * to family.
+ */
+#define UMC_DATACTL(u) amdzen_umc_smn_addr(u, 0x144, 1, 0)
+#define UMC_DATACTL_GET_ENCR_EN(r) bitx32(r, 8, 8)
+#define UMC_DATACTL_GET_SCRAM_EN(r) bitx32(r, 0, 0)
+
+#define UMC_DATACTL_DDR4_GET_TWEAK(r) bitx32(r, 19, 16)
+#define UMC_DATACTL_DDR4_GET_VMG2M(r) bitx32(r, 12, 12)
+#define UMC_DATACTL_DDR4_GET_FORCE_ENCR(r) bitx32(r, 11, 11)
+
+#define UMC_DATACTL_DDR5_GET_TWEAK(r) bitx32(r, 16, 16)
+#define UMC_DATACTL_DDR5_GET_XTS(r) bitx32(r, 14, 14)
+#define UMC_DATACTL_DDR5_GET_AES256(r) bitx32(r, 13, 13)
+
+/*
+ * UMC::CH:EccCtrl -- Various settings around how ECC operates.
+ */
+#define UMC_ECCCTL(u) amdzen_umc_smn_addr(u, 0x14c, 1, 0)
+#define UMC_ECCCTL_GET_RD_EN(r) bitx32(x, 10, 10)
+#define UMC_ECCCTL_GET_X16(r) bitx32(x, 9, 9)
+#define UMC_ECCCTL_GET_UC_FATAL(r) bitx32(x, 8, 8)
+#define UMC_ECCCTL_GET_SYM_SIZE(r) bitx32(x, 7, 7)
+#define UMC_ECCCTL_GET_BIT_IL(r) bitx32(x, 6, 6)
+#define UMC_ECCCTL_GET_HIST_EN(r) bitx32(x, 5, 5)
+#define UMC_ECCCTL_GET_SW_SYM_EN(r) bitx32(x, 4, 4)
+#define UMC_ECCCTL_GET_WR_EN(r) bitx32(x, 0, 0)
+
+/*
+ * Note, while this group appears generic and is the same in both DDR4/DDR5
+ * systems, this is not always present on every SoC and seems to depend on
+ * something else inside the chip.
+ */
+#define UMC_ECCCTL_DDR_GET_PI(r) bitx32(r, 13, 13)
+#define UMC_ECCCTL_DDR_GET_PF_DIS(r) bitx32(r, 12, 12)
+#define UMC_ECCCTL_DDR_GET_SDP_OVR(r) bitx32(x, 11, 11)
+#define UMC_ECCCTL_DDR_GET_REPLAY_EN(r) bitx32(x, 1, 1)
+
+#define UMC_ECCCTL_DDR5_GET_PIN_RED(r) bitx32(r, 14, 14)
+
+/*
+ * UMC::Ch::UmcCap, UMC::CH::UmcCapHi -- Various capability registers and
+ * feature disables. We mostly just record these for future us for debugging
+ * purposes. They aren't used as part of memory decoding.
+ */
+#define UMC_UMCCAP(u) amdzen_umc_smn_addr(u, 0xdf0, 1, 0)
+#define UMC_UMCCAP_HI(u) amdzen_umc_smn_addr(u, 0xdf4, 1, 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_UMC_H */
diff --git a/usr/src/uts/intel/sys/mc.h b/usr/src/uts/intel/sys/mc.h
index d4815b515f..6bba18ad1c 100644
--- a/usr/src/uts/intel/sys/mc.h
+++ b/usr/src/uts/intel/sys/mc.h
@@ -23,6 +23,7 @@
*/
/*
* Copyright 2019 Joyent, Inc.
+ * Copyright 2022 Oxide Computer Company
*/
#ifndef _SYS_MC_H
@@ -87,21 +88,62 @@ typedef struct mc_snapshot_info {
/*
* Data used to simulate encoding or decoding of a physical / DIMM address.
+ * These are used in different ways between AMD and Intel, so this is a bit of a
+ * smorgasbord. Details about each field are listed below.
*/
typedef struct mc_encode_ioc {
+ /*
+ * The first three values here are different addresses. We have a
+ * physical / system address. A DRAM-channel relative address, and
+ * finally a rank-relative address. Where a platform does not support
+ * one of these, UINT64_MAX is used.
+ */
uint64_t mcei_pa;
+ uint64_t mcei_chan_addr;
+ uint64_t mcei_rank_addr;
+ /*
+ * These next two provide a way for the memory controller software
+ * driver to provide additional information. The mcei_err generally
+ * corresponds to an enum that the driver has and the errdata is
+ * error-specific data that can be useful.
+ */
uint64_t mcei_errdata;
uint32_t mcei_err;
+ /*
+ * This next set is used to identify information about where to find a
+ * DIMM in question. The board and chip are used to uniquely identify a
+ * socket. Generally on x86, there is only one board, so it would be
+ * zero. The chip should correspond to the socket ID. The die refers to
+ * a particular internal die if on a chiplet or MCP. The memory
+ * controller and channel refer to a unique instance of both within a
+ * given die. On platforms where the memory controller and channel are
+ * 1:1 (that is each memory controller has only a single channel or
+ * doesn't have a specific distinction between the two), set chan to 0
+ * and set the mc to the logical channel value. The DIMM is a relative
+ * DIMM in the channel, meaning it's usually going to be 0, 1, or 2.
+ */
uint32_t mcei_board;
uint32_t mcei_chip;
+ uint32_t mcei_die;
uint32_t mcei_mc;
uint32_t mcei_chan;
uint32_t mcei_dimm;
- uint64_t mcei_rank_addr;
- uint32_t mcei_rank;
+ /*
+ * These values all refer to information on the DIMM itself and identify
+ * how to find the address. mcei_rank is meant to be a logical rank;
+ * however, some systems phrase things that way while others phrase
+ * things in terms of a chip select and rank multiplication. For unknown
+ * entries use UINT8_MAX.
+ */
uint32_t mcei_row;
uint32_t mcei_column;
- uint32_t mcei_pad;
+ uint8_t mcei_rank;
+ uint8_t mcei_cs;
+ uint8_t mcei_rm;
+ uint8_t mcei_bank;
+ uint8_t mcei_bank_group;
+ uint8_t mcei_subchan;
+ uint8_t mcei_pad[6];
} mc_encode_ioc_t;
#ifdef __cplusplus
diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h
index f1241a9183..ab62bd6deb 100644
--- a/usr/src/uts/intel/sys/x86_archext.h
+++ b/usr/src/uts/intel/sys/x86_archext.h
@@ -32,7 +32,7 @@
* Copyright 2012 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
* Copyright 2014 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
* Copyright 2018 Nexenta Systems, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
#ifndef _SYS_X86_ARCHEXT_H
@@ -617,6 +617,15 @@ extern "C" {
#define IA32_PKG_THERM_INTERRUPT_TR2_IE 0x00800000
#define IA32_PKG_THERM_INTERRUPT_PL_NE 0x01000000
+/*
+ * AMD TOM and TOM2 MSRs. These control the split between DRAM and MMIO below
+ * and above 4 GiB respectively. These have existed since family 0xf.
+ */
+#define MSR_AMD_TOM 0xc001001a
+#define MSR_AMD_TOM_MASK(x) ((x) & 0xffffff800000)
+#define MSR_AMD_TOM2 0xc001001d
+#define MSR_AMD_TOM2_MASK(x) ((x) & 0xffffff800000)
+
#define MCI_CTL_VALUE 0xffffffff