diff options
author | Robert Mustacchi <rm@joyent.com> | 2020-01-06 14:29:27 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@fingolfin.org> | 2020-03-24 22:27:39 +0000 |
commit | eb00b1c8a31c2253a353644606388dff5b0e0275 (patch) | |
tree | a038ab23cae69c3eb5d85a1c47653c025f97b677 /usr/src/lib/fm | |
parent | d1d0a29b5275e074d59d170cca23b45a6e5834d8 (diff) | |
download | illumos-joyent-eb00b1c8a31c2253a353644606388dff5b0e0275.tar.gz |
11609 Want modern Intel IMC driver
11612 x86 PCI enumeration should not rely on bios max bus
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Rob Johnston <rob.johnston@joyent.com>
Approved by: Gordon Ross <gordon.w.ross@gmail.com>
Diffstat (limited to 'usr/src/lib/fm')
-rw-r--r-- | usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c | 335 |
1 files changed, 333 insertions, 2 deletions
diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c index a57866c403..4c5da1e8cb 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c +++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <unistd.h> @@ -52,6 +53,27 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif +/* + * These are names that we use for properties. In the v0 scheme we try to pull + * these directly from the memory controller (which also causes a lot more + * variation). In the v1 scheme we translate to the name that topo wants to use, + * regardless of what the parent is called. + */ +#define MC_PROP_ECC "memory-ecc" +#define MC_PROP_POLICY "memory-policy" +#define CHAN_PROP_MODE "channel-mode" +#define DIMM_SIZE "size" +#define DIMM_STRING_SIZE "dimm-size" +#define DIMM_COL "ncolumn" +#define DIMM_ROW "nrow" +#define DIMM_DENSITY "density" +#define DIMM_WIDTH "width" +#define DIMM_RANKS "ranks" +#define DIMM_BANKS "nbanks" +#define DIMM_HDRL "hdrl-enabled" +#define DIMM_HDRL_PARITY "hdrl-parity" +#define DIMM_3DRANK "3d-subranks" + static const topo_pgroup_info_t dimm_channel_pgroup = { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; static const topo_pgroup_info_t dimm_pgroup = @@ -432,6 +454,310 @@ mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, return (0); } +static int +mc_dimm_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, + nvlist_t *dimm_nvl, uint_t id) +{ + int err, ret; + tnode_t *dimm; + nvlist_t *fmri; + boolean_t present; + uint64_t size, density; + uint32_t cols, rows, width, ranks, banks; + + /* + * First, figure out if this DIMM is present. If not, we don't bother + * creating anything. + */ + if (nvlist_lookup_boolean_value(dimm_nvl, + MCINTEL_NVLIST_V1_DIMM_PRESENT, &present) != 0) { + return (-1); + } else if (!present) { + return (0); + } + + fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, DIMM, id, + NULL, auth, NULL, NULL, NULL); + if (fmri == NULL) { + whinge(mod, NULL, "mc_dimm_create_v1: topo_mod_hcfmri " + "failed\n"); + return (-1); + } + + if ((dimm = topo_node_bind(mod, pnode, DIMM, id, fmri)) == NULL) { + whinge(mod, NULL, "mc_dimm_create_v1: node bind failed for " + "DIMM\n"); + nvlist_free(fmri); + return (-1); + } + + if (topo_node_fru_set(dimm, NULL, 0, &err) != 0) { + whinge(mod, NULL, "mc_dimm_create_v1: fru set failed: " + "%d\n", err); + nvlist_free(fmri); + return (topo_mod_seterrno(mod, err)); + } + nvlist_free(fmri); + + if (topo_pgroup_create(dimm, &dimm_pgroup, &err) != 0) { + whinge(mod, NULL, "mc_dimm_create_v1: failed to create " + "property group: %d\n", err); + return (topo_mod_seterrno(mod, err)); + } + + ret = 0; + if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_SIZE, + &size) == 0) { + char buf[64]; + const char *suffix; + ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_SIZE, + TOPO_PROP_IMMUTABLE, size, &err); + + /* + * We must manually cons up a dimm-size property which is the + * size of the dimm as a round number with a prefix such as M or + * G. This is used by Intel CPU eversholt rules. Older memory + * controller drivers did this in the driver, but we instead opt + * to do so in user land. + */ + if (size >= (1ULL << 40)) { + size /= (1ULL << 40); + suffix = "T"; + } else if (size >= (1ULL << 30)) { + size /= (1ULL << 30); + suffix = "G"; + } else if (size >= (1ULL << 20)) { + size /= (1ULL << 20); + suffix = "M"; + } else { + suffix = NULL; + } + + if (suffix != NULL) { + if (snprintf(buf, sizeof (buf), "%"PRIu64"%s", size, + suffix) >= sizeof (buf)) { + whinge(mod, NULL, "failed to construct DIMM " + "size due to buffer overflow"); + return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); + } + ret |= topo_prop_set_string(dimm, PGNAME(DIMM), + DIMM_STRING_SIZE, TOPO_PROP_IMMUTABLE, buf, &err); + } + } + + if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS, + &cols) == 0) { + ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_COL, + TOPO_PROP_IMMUTABLE, cols, &err); + } + + if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NROWS, + &rows) == 0) { + ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_ROW, + TOPO_PROP_IMMUTABLE, rows, &err); + } + + if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY, + &density) == 0) { + ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_DENSITY, + TOPO_PROP_IMMUTABLE, density, &err); + } + + if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH, + &width) == 0) { + ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_WIDTH, + TOPO_PROP_IMMUTABLE, width, &err); + } + + if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_BANKS, + &banks) == 0) { + ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_BANKS, + TOPO_PROP_IMMUTABLE, banks, &err); + } + + if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RANKS, + &ranks) == 0) { + ret |= topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_RANKS, + TOPO_PROP_IMMUTABLE, ranks, &err); + } + + return (ret != 0 ? -1 : 0); +} + +static int +mc_channel_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, + nvlist_t *chan_nvl, uint_t id, const char *cmode) +{ + int err; + tnode_t *chnode; + nvlist_t *fmri; + nvlist_t **dimms; + uint_t ndimms, i; + + if (mkrsrc(mod, pnode, DRAMCHANNEL, id, auth, &fmri) != 0) { + whinge(mod, NULL, "mc_channel_create_v1: mkrsrc failed\n"); + return (-1); + } + + if ((chnode = topo_node_bind(mod, pnode, DRAMCHANNEL, id, fmri)) == + NULL) { + whinge(mod, NULL, "mc_channel_create_v1: node bind failed" + " for dram-channel\n"); + nvlist_free(fmri); + return (-1); + } + + nvlist_free(fmri); + if (topo_node_fru_set(chnode, NULL, 0, &err) != 0) { + whinge(mod, NULL, "mc_channel_create_v1: fru set failed: " + "%d\n", err); + return (topo_mod_seterrno(mod, err)); + } + + if (topo_pgroup_create(chnode, &dimm_channel_pgroup, &err) != 0) { + whinge(mod, NULL, "mc_channel_create_v1: failed to create " + "property group: %d\n", err); + return (topo_mod_seterrno(mod, err)); + } + + if (topo_prop_set_string(chnode, PGNAME(CHAN), CHAN_PROP_MODE, + TOPO_PROP_IMMUTABLE, cmode, &err) != 0) { + return (topo_mod_seterrno(mod, err)); + } + + if (nvlist_lookup_nvlist_array(chan_nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS, + &dimms, &ndimms) != 0) { + whinge(mod, NULL, "mc_channel_create_v1: No DIMMS provided"); + return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); + } + + if (topo_node_range_create(mod, chnode, DIMM, 0, ndimms - 1) < 0) { + whinge(mod, NULL, "mc_channel_create_v1: dimm node range " + "create failed\n"); + return (-1); + } + + for (i = 0; i < ndimms; i++) { + if (mc_dimm_create_v1(mod, chnode, auth, dimms[i], i) != 0) + return (-1); + } + + return (0); +} + +static int +mc_imc_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name, + nvlist_t *auth, nvlist_t *mc_nvl, uint_t id) +{ + int err, ret; + tnode_t *mcnode; + nvlist_t *fmri, **channels; + boolean_t ecc; + char *page, *cmode; + uint_t nchans, i; + + if (mkrsrc(mod, pnode, name, id, auth, &fmri) != 0) { + whinge(mod, NULL, "mc_nb_create_v1: mkrsrc failed\n"); + return (-1); + } + + if ((mcnode = topo_node_bind(mod, pnode, name, id, fmri)) == NULL) { + whinge(mod, NULL, "mc_nb_create_v1: node bind failed" + " for memory-controller\n"); + nvlist_free(fmri); + return (-1); + } + + nvlist_free(fmri); + if (topo_node_fru_set(mcnode, NULL, 0, &err) != 0) { + whinge(mod, NULL, "mc_nb_create_v1: fru set failed: " + "%d\n", err); + return (topo_mod_seterrno(mod, err)); + } + + if (topo_pgroup_create(mcnode, &mc_pgroup, &err) != 0) { + whinge(mod, NULL, "mc_nb_create_v1: failed to create " + "property group: %d\n", err); + return (topo_mod_seterrno(mod, err)); + } + + /* + * Add properties to the controller. Our contract allows for these + * properties to be missing. + */ + ret = 0; + if (nvlist_lookup_boolean_value(mc_nvl, MCINTEL_NVLIST_V1_MC_ECC, + &ecc) == 0) { + const char *pval = ecc ? "enabled" : "disabled"; + ret |= topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_ECC, + TOPO_PROP_IMMUTABLE, pval, &err); + } + + if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_POLICY, + &page) == 0) { + ret |= topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_POLICY, + TOPO_PROP_IMMUTABLE, page, &err); + } + + if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE, + &cmode) != 0) { + cmode = NULL; + } + + if (ret != 0) + return (-1); + + if (nvlist_lookup_nvlist_array(mc_nvl, MCINTEL_NVLIST_V1_MC_CHANNELS, + &channels, &nchans) != 0) { + whinge(mod, NULL, "mc_imc_create_v1: missing channels entry"); + return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); + } + + if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, 0, + nchans - 1) < 0) { + whinge(mod, NULL, "mc_imc_create_v1: channel node range create " + "failed\n"); + return (-1); + } + + for (i = 0; i < nchans; i++) { + if (mc_channel_create_v1(mod, mcnode, auth, channels[i], i, + cmode) != 0) { + return (-1); + } + } + + return (0); +} + +static int +mc_nb_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name, + nvlist_t *auth, nvlist_t *nvl) +{ + nvlist_t **mc_nvl; + uint_t nmc, i; + + if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS, + &mc_nvl, &nmc) != 0) { + whinge(mod, NULL, "mc_nb_create_v1: failed to find memory " + "controller information\n"); + return (-1); + } + + if (topo_node_range_create(mod, pnode, name, 0, nmc - 1) < 0) { + whinge(mod, NULL, + "mc_nb_create: node range create failed\n"); + return (-1); + } + + for (i = 0; i < nmc; i++) { + if (mc_imc_create_v1(mod, pnode, name, auth, mc_nvl[i], i) != 0) + return (-1); + } + + return (0); +} + int mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, const char *name, nvlist_t *auth) @@ -461,13 +787,18 @@ mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, whinge(mod, NULL, "mc nvlist is not versioned\n"); nvlist_free(nvl); return (0); - } else if (ver != MCINTEL_NVLIST_VERS0) { + } else if (ver != MCINTEL_NVLIST_VERS0 && + ver != MCINTEL_NVLIST_VERS1) { whinge(mod, NULL, "mc nvlist version mismatch\n"); nvlist_free(nvl); return (0); } - rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl); + if (ver == MCINTEL_NVLIST_VERS1) { + rc = mc_nb_create_v1(mod, pnode, name, auth, nvl); + } else { + rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl); + } nvlist_free(nvl); return (rc); |