#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/obpdefs.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/open.h>
#include <sys/thread.h>
#include <sys/cpuvar.h>
#include <sys/x_call.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/ivintr.h>
#include <sys/intr.h>
#include <sys/intreg.h>
#include <sys/autoconf.h>
#include <sys/modctl.h>
#include <sys/spl.h>
#include <sys/async.h>
#include <sys/mc.h>
#include <sys/mc-us3.h>
#include <sys/cpu_module.h>
#include <sys/platform_module.h>
static int mc_open(dev_t *, int, int, cred_t *);
static int mc_close(dev_t, int, int, cred_t *);
static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
static struct cb_ops mc_cb_ops = {
mc_open,
mc_close,
nulldev,
nulldev,
nodev,
nulldev,
nulldev,
mc_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_MP | D_NEW | D_HOTPLUG,
CB_REV,
nodev,
nodev
};
static struct dev_ops mc_ops = {
DEVO_REV,
0,
ddi_getinfo_1to1,
nulldev,
nulldev,
mc_attach,
mc_detach,
nulldev,
&mc_cb_ops,
(struct bus_ops *)0,
nulldev,
ddi_quiesce_not_needed,
};
static void *mcp;
static int nmcs = 0;
static int seg_id = 0;
static int nsegments = 0;
static uint64_t memsize = 0;
static int maxbanks = 0;
static mc_dlist_t *seg_head, *seg_tail, *bank_head, *bank_tail;
static mc_dlist_t *mctrl_head, *mctrl_tail, *dgrp_head, *dgrp_tail;
static mc_dlist_t *device_head, *device_tail;
static kmutex_t mcmutex;
static kmutex_t mcdatamutex;
static krwlock_t mcdimmsids_rw;
static dimm_sid_cache_t *mc_dimm_sids;
static int max_entries;
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"Memory-controller",
&mc_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
static int mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf,
int buflen, int *lenp);
static int mc_get_mem_info(int synd_code, uint64_t paddr,
uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
int *segsp, int *banksp, int *mcidp);
static int mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp);
static int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
static int mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr);
static int mc_init_sid_cache(void);
static int mc_get_mcregs(struct mc_soft_state *);
static void mc_construct(int mc_id, void *dimminfop);
static int mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop);
static void mlayout_del(int mc_id, int delete);
static struct seg_info *seg_match_base(u_longlong_t base);
static void mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
static void mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
static mc_dlist_t *mc_node_get(int id, mc_dlist_t *head);
static void mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm);
static int mc_populate_sid_cache(void);
static int mc_get_sid_cache_index(int mcid);
static void mc_update_bank(struct bank_info *bank);
#pragma weak p2get_mem_unum
#pragma weak p2get_mem_info
#pragma weak p2get_mem_sid
#pragma weak p2get_mem_offset
#pragma weak p2get_mem_addr
#pragma weak p2init_sid_cache
#pragma weak plat_add_mem_unum_label
#pragma weak plat_alloc_sid_cache
#pragma weak plat_populate_sid_cache
#define QWORD_SIZE 144
#define QWORD_SIZE_BYTES (QWORD_SIZE / 8)
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&mcp,
sizeof (struct mc_soft_state), 1)) != 0)
return (error);
error = mod_install(&modlinkage);
if (error == 0) {
mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&mcdatamutex, NULL, MUTEX_DRIVER, NULL);
rw_init(&mcdimmsids_rw, NULL, RW_DRIVER, NULL);
}
return (error);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) != 0)
return (error);
ddi_soft_state_fini(&mcp);
mutex_destroy(&mcmutex);
mutex_destroy(&mcdatamutex);
rw_destroy(&mcdimmsids_rw);
if (mc_dimm_sids)
kmem_free(mc_dimm_sids, sizeof (dimm_sid_cache_t) *
max_entries);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
struct mc_soft_state *softsp;
struct dimm_info *dimminfop;
int instance, len, err;
instance = ddi_get_instance(devi);
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
softsp = ddi_get_soft_state(mcp, instance);
DPRINTF(MC_ATTACH_DEBUG, ("mc%d: DDI_RESUME: updating MADRs\n",
instance));
if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
MEM_CFG_PROP_NAME) == 1) {
(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
MEM_CFG_PROP_NAME);
}
mlayout_del(softsp->portid, 0);
if (mc_get_mcregs(softsp) == -1) {
cmn_err(CE_WARN, "mc_attach: mc%d DDI_RESUME failure\n",
instance);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(mcp, instance) != DDI_SUCCESS)
return (DDI_FAILURE);
softsp = ddi_get_soft_state(mcp, instance);
softsp->dip = devi;
if ((softsp->portid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, "portid", -1)) == -1) {
DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to get %s property",
instance, "portid"));
goto bad;
}
DPRINTF(MC_ATTACH_DEBUG, ("mc%d ATTACH: portid %d, cpuid %d\n",
instance, softsp->portid, CPU->cpu_id));
if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0)) {
DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to map registers",
instance));
goto bad;
}
err = ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip, DDI_PROP_DONTPASS,
"memory-layout", (caddr_t)&dimminfop, &len);
if (err == DDI_PROP_SUCCESS) {
softsp->memlayoutp = dimminfop;
softsp->size = len;
} else if (err == DDI_PROP_NOT_FOUND) {
softsp->memlayoutp = NULL;
softsp->size = 0;
} else {
DPRINTF(MC_ATTACH_DEBUG, ("mc%d is disabled: dimminfop %p\n",
instance, (void *)dimminfop));
goto bad2;
}
DPRINTF(MC_ATTACH_DEBUG, ("mc%d: dimminfop=0x%p data=0x%lx len=%d\n",
instance, (void *)dimminfop, *(uint64_t *)dimminfop, len));
if (mc_get_mcregs(softsp) == -1)
goto bad1;
mutex_enter(&mcmutex);
if (nmcs == 1) {
if (&p2get_mem_unum)
p2get_mem_unum = mc_get_mem_unum;
if (&p2get_mem_info)
p2get_mem_info = mc_get_mem_info;
if (&p2get_mem_sid)
p2get_mem_sid = mc_get_mem_sid;
if (&p2get_mem_offset)
p2get_mem_offset = mc_get_mem_offset;
if (&p2get_mem_addr)
p2get_mem_addr = mc_get_mem_addr;
if (&p2init_sid_cache)
p2init_sid_cache = mc_init_sid_cache;
}
mutex_exit(&mcmutex);
if (mc_dimm_sids) {
rw_enter(&mcdimmsids_rw, RW_WRITER);
(void) mc_populate_sid_cache();
rw_exit(&mcdimmsids_rw);
}
if (ddi_create_minor_node(devi, "mc-us3", S_IFCHR, instance,
"ddi_mem_ctrl", 0) != DDI_SUCCESS) {
DPRINTF(MC_ATTACH_DEBUG, ("mc_attach: create_minor_node"
" failed \n"));
goto bad1;
}
ddi_report_dev(devi);
return (DDI_SUCCESS);
bad1:
mlayout_del(softsp->portid, 0);
if (softsp->memlayoutp != NULL)
kmem_free(softsp->memlayoutp, softsp->size);
if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
MEM_CFG_PROP_NAME) == 1) {
(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
MEM_CFG_PROP_NAME);
}
bad2:
ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
bad:
ddi_soft_state_free(mcp, instance);
return (DDI_FAILURE);
}
static int
mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
int instance;
struct mc_soft_state *softsp;
instance = ddi_get_instance(devi);
softsp = ddi_get_soft_state(mcp, instance);
switch (cmd) {
case DDI_SUSPEND:
return (DDI_SUCCESS);
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
DPRINTF(MC_DETACH_DEBUG, ("mc%d DETACH: portid= %d, table 0x%p\n",
instance, softsp->portid, softsp->memlayoutp));
if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
MEM_CFG_PROP_NAME) == 1) {
(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
MEM_CFG_PROP_NAME);
}
mlayout_del(softsp->portid, 1);
if (softsp->memlayoutp != NULL)
kmem_free(softsp->memlayoutp, softsp->size);
ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
mutex_enter(&mcmutex);
if (nmcs == 0) {
if (&p2get_mem_unum)
p2get_mem_unum = NULL;
if (&p2get_mem_info)
p2get_mem_info = NULL;
if (&p2get_mem_sid)
p2get_mem_sid = NULL;
if (&p2get_mem_offset)
p2get_mem_offset = NULL;
if (&p2get_mem_addr)
p2get_mem_addr = NULL;
if (&p2init_sid_cache)
p2init_sid_cache = NULL;
}
mutex_exit(&mcmutex);
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(mcp, instance);
return (DDI_SUCCESS);
}
static int
mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
if (otyp != OTYP_CHR) {
return (EINVAL);
}
return (0);
}
static int
mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
{
return (0);
}
static int
mc_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
int *rval_p)
{
size_t size;
struct mc_memconf mcmconf;
struct mc_memory *mcmem, mcmem_in;
struct mc_segment *mcseg, mcseg_in;
struct mc_bank mcbank;
struct mc_devgrp mcdevgrp;
struct mc_ctrlconf *mcctrlconf, mcctrlconf_in;
struct mc_control *mccontrol, mccontrol_in;
struct seg_info *seg = NULL;
struct bank_info *bank = NULL;
struct dgrp_info *dgrp = NULL;
struct mctrl_info *mcport;
mc_dlist_t *mctrl;
int i, status = 0;
cpu_t *cpu;
switch (cmd) {
case MCIOC_MEMCONF:
mutex_enter(&mcdatamutex);
mcmconf.nmcs = nmcs;
mcmconf.nsegments = nsegments;
mcmconf.nbanks = maxbanks;
mcmconf.ndevgrps = NDGRPS;
mcmconf.ndevs = NDIMMS;
mcmconf.len_dev = MAX_DEVLEN;
mcmconf.xfer_size = TRANSFER_SIZE;
mutex_exit(&mcdatamutex);
if (copyout(&mcmconf, (void *)arg, sizeof (struct mc_memconf)))
return (EFAULT);
return (0);
case MCIOC_MEM:
if (copyin((void *)arg, &mcmem_in,
sizeof (struct mc_memory)) != 0)
return (EFAULT);
mutex_enter(&mcdatamutex);
if (mcmem_in.nsegments < nsegments) {
mcmem_in.nsegments = nsegments;
if (copyout(&mcmem_in, (void *)arg,
sizeof (struct mc_memory)))
status = EFAULT;
else
status = EINVAL;
mutex_exit(&mcdatamutex);
return (status);
}
size = sizeof (struct mc_memory) + (nsegments - 1) *
sizeof (mcmem->segmentids[0]);
mcmem = kmem_zalloc(size, KM_SLEEP);
mcmem->size = memsize;
mcmem->nsegments = nsegments;
seg = (struct seg_info *)seg_head;
for (i = 0; i < nsegments; i++) {
ASSERT(seg != NULL);
mcmem->segmentids[i].globalid = seg->seg_node.id;
mcmem->segmentids[i].localid = seg->seg_node.id;
seg = (struct seg_info *)seg->seg_node.next;
}
mutex_exit(&mcdatamutex);
if (copyout(mcmem, (void *)arg, size))
status = EFAULT;
kmem_free(mcmem, size);
return (status);
case MCIOC_SEG:
if (copyin((void *)arg, &mcseg_in,
sizeof (struct mc_segment)) != 0)
return (EFAULT);
mutex_enter(&mcdatamutex);
if ((seg = (struct seg_info *)mc_node_get(mcseg_in.id,
seg_head)) == NULL) {
DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG: seg not match, "
"id %d\n", mcseg_in.id));
mutex_exit(&mcdatamutex);
return (EFAULT);
}
if (mcseg_in.nbanks < seg->nbanks) {
mcseg_in.nbanks = seg->nbanks;
if (copyout(&mcseg_in, (void *)arg,
sizeof (struct mc_segment)))
status = EFAULT;
else
status = EINVAL;
mutex_exit(&mcdatamutex);
return (status);
}
size = sizeof (struct mc_segment) + (seg->nbanks - 1) *
sizeof (mcseg->bankids[0]);
mcseg = kmem_zalloc(size, KM_SLEEP);
mcseg->id = seg->seg_node.id;
mcseg->ifactor = seg->ifactor;
mcseg->base = seg->base;
mcseg->size = seg->size;
mcseg->nbanks = seg->nbanks;
bank = seg->hb_inseg;
DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:nbanks %d seg 0x%p bank %p\n",
seg->nbanks, (void *)seg, (void *)bank));
i = 0;
while (bank != NULL) {
DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:idx %d bank_id %d\n",
i, bank->bank_node.id));
mcseg->bankids[i].globalid = bank->bank_node.id;
mcseg->bankids[i++].localid =
bank->local_id;
bank = bank->n_inseg;
}
ASSERT(i == seg->nbanks);
mutex_exit(&mcdatamutex);
if (copyout(mcseg, (void *)arg, size))
status = EFAULT;
kmem_free(mcseg, size);
return (status);
case MCIOC_BANK:
if (copyin((void *)arg, &mcbank, sizeof (struct mc_bank)) != 0)
return (EFAULT);
DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank id %d\n", mcbank.id));
mutex_enter(&mcdatamutex);
if ((bank = (struct bank_info *)mc_node_get(mcbank.id,
bank_head)) == NULL) {
mutex_exit(&mcdatamutex);
return (EINVAL);
}
DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank %d (0x%p) valid %hu\n",
bank->bank_node.id, (void *)bank, bank->valid));
mcbank.mask = (~(bank->lk | ~(MADR_LK_MASK >>
MADR_LK_SHIFT))) << MADR_LPA_SHIFT;
mcbank.match = bank->lm << MADR_LPA_SHIFT;
mcbank.size = bank->size;
mcbank.devgrpid.globalid = bank->devgrp_id;
mcbank.devgrpid.localid = bank->devgrp_id % NDGRPS;
mutex_exit(&mcdatamutex);
if (copyout(&mcbank, (void *)arg, sizeof (struct mc_bank)))
return (EFAULT);
return (0);
case MCIOC_DEVGRP:
if (copyin((void *)arg, &mcdevgrp,
sizeof (struct mc_devgrp)) != 0)
return (EFAULT);
mutex_enter(&mcdatamutex);
if ((dgrp = (struct dgrp_info *)mc_node_get(mcdevgrp.id,
dgrp_head)) == NULL) {
DPRINTF(MC_CMD_DEBUG, ("MCIOC_DEVGRP: not match, id "
"%d\n", mcdevgrp.id));
mutex_exit(&mcdatamutex);
return (EINVAL);
}
mcdevgrp.ndevices = dgrp->ndevices;
mcdevgrp.size = dgrp->size;
mutex_exit(&mcdatamutex);
if (copyout(&mcdevgrp, (void *)arg, sizeof (struct mc_devgrp)))
status = EFAULT;
return (status);
case MCIOC_CTRLCONF:
if (copyin((void *)arg, &mcctrlconf_in,
sizeof (struct mc_ctrlconf)) != 0)
return (EFAULT);
mutex_enter(&mcdatamutex);
if (mcctrlconf_in.nmcs < nmcs) {
mcctrlconf_in.nmcs = nmcs;
if (copyout(&mcctrlconf_in, (void *)arg,
sizeof (struct mc_ctrlconf)))
status = EFAULT;
else
status = EINVAL;
mutex_exit(&mcdatamutex);
return (status);
}
size = sizeof (struct mc_ctrlconf) + ((nmcs - 1) *
sizeof (mcctrlconf->mcids[0]));
mcctrlconf = kmem_zalloc(size, KM_SLEEP);
mcctrlconf->nmcs = nmcs;
mctrl = mctrl_head;
i = 0;
while (mctrl != NULL) {
mcctrlconf->mcids[i].globalid = mctrl->id;
mcctrlconf->mcids[i].localid = mctrl->id;
i++;
mctrl = mctrl->next;
}
ASSERT(i == nmcs);
mutex_exit(&mcdatamutex);
if (copyout(mcctrlconf, (void *)arg, size))
status = EFAULT;
kmem_free(mcctrlconf, size);
return (status);
case MCIOC_CONTROL:
if (copyin((void *)arg, &mccontrol_in,
sizeof (struct mc_control)) != 0)
return (EFAULT);
mutex_enter(&mcdatamutex);
if ((mcport = (struct mctrl_info *)mc_node_get(mccontrol_in.id,
mctrl_head)) == NULL) {
mutex_exit(&mcdatamutex);
return (EINVAL);
}
if ((mccontrol_in.ndevgrps < mcport->ndevgrps) ||
(mcport->ndevgrps == 0)) {
mccontrol_in.ndevgrps = mcport->ndevgrps;
if (copyout(&mccontrol_in, (void *)arg,
sizeof (struct mc_control)))
status = EFAULT;
else if (mcport->ndevgrps != 0)
status = EINVAL;
mutex_exit(&mcdatamutex);
return (status);
}
size = sizeof (struct mc_control) + (mcport->ndevgrps - 1) *
sizeof (mccontrol->devgrpids[0]);
mccontrol = kmem_zalloc(size, KM_SLEEP);
mccontrol->id = mcport->mctrl_node.id;
mccontrol->ndevgrps = mcport->ndevgrps;
for (i = 0; i < mcport->ndevgrps; i++) {
mccontrol->devgrpids[i].globalid = mcport->devgrpids[i];
mccontrol->devgrpids[i].localid =
mcport->devgrpids[i] % NDGRPS;
DPRINTF(MC_CMD_DEBUG, ("MCIOC_CONTROL: devgrp id %lu\n",
*(uint64_t *)&mccontrol->devgrpids[i]));
}
mutex_exit(&mcdatamutex);
if (copyout(mccontrol, (void *)arg, size))
status = EFAULT;
kmem_free(mccontrol, size);
return (status);
case MCIOC_ECFLUSH:
mutex_enter(&cpu_lock);
cpu = cpu_get((processorid_t)arg);
mutex_exit(&cpu_lock);
if (cpu == NULL)
return (EINVAL);
xc_one(arg, (xcfunc_t *)cpu_flush_ecache, 0, 0);
return (0);
default:
DPRINTF(MC_CMD_DEBUG, ("DEFAULT: cmd is wrong\n"));
return (EFAULT);
}
}
static int
mc_get_mcregs(struct mc_soft_state *softsp)
{
int i;
int err = 0;
uint64_t madreg;
uint64_t ma_reg_array[NBANKS];
mc_construct(softsp->portid, softsp->memlayoutp);
if (softsp->memlayoutp == NULL)
goto exit;
for (i = 0; i < NBANKS; i++) {
DPRINTF(MC_REG_DEBUG, ("get_mcregs: mapreg=0x%p portid=%d "
"cpu=%d\n", (void *)softsp->mc_base, softsp->portid,
CPU->cpu_id));
kpreempt_disable();
if (softsp->portid == (cpunodes[CPU->cpu_id].portid))
madreg = get_mcr(MADR0OFFSET + (i * REGOFFSET));
else
madreg = *((uint64_t *)(softsp->mc_base + MADR0OFFSET +
(i * REGOFFSET)));
kpreempt_enable();
DPRINTF(MC_REG_DEBUG, ("get_mcregs 2: memlayoutp=0x%p madreg "
"reg=0x%lx\n", softsp->memlayoutp, madreg));
ma_reg_array[i] = madreg;
if ((err = mlayout_add(softsp->portid, i, madreg,
softsp->memlayoutp)) == -1)
break;
}
if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
MEM_CFG_PROP_NAME) != 1) {
(void) ddi_prop_create(DDI_DEV_T_NONE, softsp->dip,
DDI_PROP_CANSLEEP, MEM_CFG_PROP_NAME,
(caddr_t)&ma_reg_array, sizeof (ma_reg_array));
}
exit:
if (!err) {
mutex_enter(&mcdatamutex);
nmcs++;
mutex_exit(&mcdatamutex);
}
return (err);
}
static int
mc_offset_to_addr(struct seg_info *seg,
struct bank_info *bank, uint64_t off, uint64_t *addr)
{
uint64_t base, size, line, remainder;
uint32_t ifactor;
size = ((bank->size / 4) / 64) * 72;
base = size * bank->pos;
if ((off < base) || (off >= (base + size)))
return (-1);
off -= base;
line = off / QWORD_SIZE_BYTES;
remainder = off % QWORD_SIZE_BYTES;
ifactor = (bank->lk ^ 0xF) + 1;
line = (line * ifactor) + bank->lm;
*addr = (line << 6) + seg->base;
*addr += remainder * 16;
return (0);
}
static void
mc_addr_to_offset(struct seg_info *seg,
struct bank_info *bank, uint64_t addr, uint64_t *off)
{
uint64_t base, size, line, remainder;
uint32_t ifactor;
line = (addr - seg->base) / 64;
ifactor = (bank->lk ^ 0xF) + 1;
line = (line - bank->lm)/ifactor;
*off = line * QWORD_SIZE_BYTES;
size = ((bank->size / 4) / 64) * 72;
base = size * bank->pos;
*off += base;
remainder = (addr - seg->base) % 64;
remainder /= 16;
*off += remainder;
}
static uint8_t qwordmap[] =
{
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
7, 8, 9, 10, 11, 12, 13, 14, 15, 4, 5, 6, 0, 1, 2, 3,
};
static int
mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf, int buflen, int *lenp)
{
int i, upper_pa, lower_pa, dimmoffset;
int quadword, pos_cacheline, position, index, idx4dimm;
int qwlayout = synd_code;
short offset, data;
char unum[UNUM_NAMLEN];
struct dimm_info *dimmp;
struct pin_info *pinp;
struct bank_info *bank;
if (qwlayout < -1 || qwlayout >= QWORD_SIZE)
return (EINVAL);
unum[0] = '\0';
upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
DPRINTF(MC_GUNUM_DEBUG, ("qwlayout %d\n", qwlayout));
mutex_enter(&mcdatamutex);
bank = (struct bank_info *)bank_head;
while (bank != NULL) {
int bankid, mcid, bankno_permc;
bankid = bank->bank_node.id;
bankno_permc = bankid % NBANKS;
mcid = bankid / NBANKS;
if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) |
bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
dimmoffset = (bankno_permc % NDGRPS) * NDIMMS;
dimmp = (struct dimm_info *)bank->dimminfop;
ASSERT(dimmp != NULL);
if ((qwlayout >= 0) && (qwlayout < QWORD_SIZE)) {
pinp = (struct pin_info *)&dimmp->data[0];
if (!dimmp->sym_flag)
pinp++;
quadword = (paddr & 0x3f) / 16;
pos_cacheline = ((3 - quadword) * QWORD_SIZE) +
qwordmap[qwlayout];
position = 575 - pos_cacheline;
index = position * 2 / 8;
offset = position % 4;
DPRINTF(MC_GUNUM_DEBUG, ("Pin number %1u\n",
(uint_t)pinp->pintable[pos_cacheline]));
data = pinp->dimmtable[index];
idx4dimm = (data >> ((3 - offset) * 2)) & 3;
(void) strncpy(unum,
(char *)dimmp->label[dimmoffset + idx4dimm],
UNUM_NAMLEN);
DPRINTF(MC_GUNUM_DEBUG, ("unum %s\n", unum));
mc_add_mem_unum_label(unum, mcid, bankno_permc,
idx4dimm);
} else {
char *p = unum;
size_t res = UNUM_NAMLEN;
for (i = 0; (i < NDIMMS) && (res > 0); i++) {
(void) snprintf(p, res, "%s%s",
i == 0 ? "" : " ",
(char *)dimmp->label[dimmoffset + i]);
res -= strlen(p);
p += strlen(p);
}
mc_add_mem_unum_label(unum, mcid, bankno_permc, -1);
}
mutex_exit(&mcdatamutex);
if ((strlen(unum) >= UNUM_NAMLEN) ||
(strlen(unum) >= buflen)) {
return (ENAMETOOLONG);
} else {
(void) strncpy(buf, unum, buflen);
*lenp = strlen(buf);
return (0);
}
}
mutex_exit(&mcdatamutex);
return (ENXIO);
}
static int
mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
{
int upper_pa, lower_pa;
struct bank_info *bank;
struct seg_info *seg;
upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
mutex_enter(&mcdatamutex);
bank = (struct bank_info *)bank_head;
while (bank != NULL) {
if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) |
bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head);
ASSERT(seg != NULL);
ASSERT(paddr >= seg->base);
mc_addr_to_offset(seg, bank, paddr, offp);
mutex_exit(&mcdatamutex);
return (0);
}
mutex_exit(&mcdatamutex);
return (ENXIO);
}
static int
mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr)
{
struct seg_info *seg;
struct bank_info *bank;
int first_seg_id;
int i, found;
ASSERT(sid != NULL);
mutex_enter(&mcdatamutex);
rw_enter(&mcdimmsids_rw, RW_READER);
if (mc_dimm_sids == NULL) {
rw_exit(&mcdimmsids_rw);
return (EAGAIN);
}
for (i = 0; i < max_entries; i++) {
if (mc_dimm_sids[i].mcid == mcid)
break;
}
if (i == max_entries) {
rw_exit(&mcdimmsids_rw);
mutex_exit(&mcdatamutex);
return (ENODEV);
}
first_seg_id = mc_dimm_sids[i].seg_id;
seg = (struct seg_info *)mc_node_get(first_seg_id, seg_head);
rw_exit(&mcdimmsids_rw);
if (seg == NULL) {
mutex_exit(&mcdatamutex);
return (ENODEV);
}
found = 0;
for (bank = seg->hb_inseg; bank; bank = bank->n_inseg) {
ASSERT(bank->valid);
for (i = 0; i < NDIMMS; i++) {
if (strncmp((char *)bank->dimmsidp[i], sid,
DIMM_SERIAL_ID_LEN) == 0)
break;
}
if (i == NDIMMS)
continue;
if (mc_offset_to_addr(seg, bank, off, paddr) == -1)
continue;
found = 1;
break;
}
if (found) {
mutex_exit(&mcdatamutex);
return (0);
}
bank = (struct bank_info *)bank_head;
while (bank != NULL) {
if (!bank->valid) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
if (bank->bank_node.id / NBANKS != mcid) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
if (bank->seg_id == mc_dimm_sids[i].seg_id) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
for (i = 0; i < NDIMMS; i++) {
if (strncmp((char *)bank->dimmsidp[i], sid,
DIMM_SERIAL_ID_LEN) == 0)
break;
}
if (i == NDIMMS) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head);
if (mc_offset_to_addr(seg, bank, off, paddr) == -1) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
found = 1;
break;
}
mutex_exit(&mcdatamutex);
if (found)
return (0);
else
return (ENOENT);
}
static int
mc_get_mem_info(int synd_code, uint64_t paddr,
uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
int *segsp, int *banksp, int *mcidp)
{
int upper_pa, lower_pa;
struct bank_info *bankp;
if (synd_code < -1 || synd_code >= QWORD_SIZE)
return (EINVAL);
upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
mutex_enter(&mcdatamutex);
bankp = (struct bank_info *)bank_head;
while (bankp != NULL) {
struct seg_info *segp;
int bankid, mcid;
bankid = bankp->bank_node.id;
mcid = bankid / NBANKS;
if ((!bankp->valid) || ((~(~(upper_pa ^ bankp->um) |
bankp->uk)) || (~(~(lower_pa ^ bankp->lm) | bankp->lk)))) {
bankp = (struct bank_info *)bankp->bank_node.next;
continue;
}
if ((segp = (struct seg_info *)mc_node_get(bankp->seg_id,
seg_head)) == NULL) {
mutex_exit(&mcdatamutex);
return (EFAULT);
}
*mem_sizep = memsize;
*seg_sizep = segp->size;
*bank_sizep = bankp->size;
*segsp = nsegments;
*banksp = segp->nbanks;
*mcidp = mcid;
mutex_exit(&mcdatamutex);
return (0);
}
mutex_exit(&mcdatamutex);
return (ENXIO);
}
static void
mc_construct(int mc_id, void *dimminfop)
{
int i, j, idx, dmidx;
struct mctrl_info *mctrl;
struct dgrp_info *dgrp;
struct device_info *dev;
struct dimm_info *dimmp = (struct dimm_info *)dimminfop;
mutex_enter(&mcdatamutex);
if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id,
mctrl_head)) != NULL) {
cmn_err(CE_WARN, "mc_construct: mctrl %d exists\n", mc_id);
mutex_exit(&mcdatamutex);
return;
}
mctrl = kmem_zalloc(sizeof (struct mctrl_info), KM_SLEEP);
if (dimminfop == NULL) {
mctrl->mctrl_node.id = mc_id;
mctrl->ndevgrps = 0;
mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
mutex_exit(&mcdatamutex);
return;
}
for (i = 0; i < NDGRPS; i++) {
idx = mc_id * NDGRPS + i;
mctrl->devgrpids[i] = idx;
if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head))
!= NULL) {
cmn_err(CE_WARN, "mc_construct: devgrp %d exists\n",
idx);
continue;
}
dgrp = kmem_zalloc(sizeof (struct dgrp_info), KM_SLEEP);
for (j = 0; j < NDIMMS; j++) {
dmidx = idx * NDIMMS + j;
dgrp->deviceids[j] = dmidx;
if ((dev = (struct device_info *)
mc_node_get(dmidx, device_head)) != NULL) {
cmn_err(CE_WARN, "mc_construct: device %d "
"exists\n", dmidx);
continue;
}
dev = kmem_zalloc(sizeof (struct device_info),
KM_SLEEP);
dev->dev_node.id = dmidx;
dev->size = 0;
(void) strncpy(dev->label, (char *)
dimmp->label[i * NDIMMS + j], MAX_DEVLEN);
mc_node_add((mc_dlist_t *)dev, &device_head,
&device_tail);
}
dgrp->dgrp_node.id = idx;
dgrp->ndevices = NDIMMS;
dgrp->size = 0;
mc_node_add((mc_dlist_t *)dgrp, &dgrp_head, &dgrp_tail);
}
mctrl->mctrl_node.id = mc_id;
mctrl->ndevgrps = NDGRPS;
mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
mutex_exit(&mcdatamutex);
}
static int
mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop)
{
int i, dmidx, idx;
uint32_t ifactor;
int status = 0;
uint64_t size, base;
struct seg_info *seg_curr;
struct bank_info *bank_curr;
struct dgrp_info *dgrp;
struct device_info *dev;
union {
struct {
uint64_t valid : 1;
uint64_t resrv1 : 10;
uint64_t uk : 12;
uint64_t resrv2 : 4;
uint64_t um : 17;
uint64_t resrv3 : 2;
uint64_t lk : 4;
uint64_t resrv4 : 2;
uint64_t lm : 4;
uint64_t resrv5 : 8;
} _s;
uint64_t madreg;
} mcreg;
mcreg.madreg = reg;
DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: mc_id %d, bank num "
"%d, reg 0x%lx\n", mc_id, bank_no, reg));
idx = mc_id * NBANKS + bank_no;
mutex_enter(&mcdatamutex);
if ((bank_curr = (struct bank_info *)mc_node_get(idx, bank_head))
!= NULL) {
cmn_err(CE_WARN, "mlayout_add: bank %d exists\n", bank_no);
goto exit;
}
bank_curr = kmem_zalloc(sizeof (struct bank_info), KM_SLEEP);
bank_curr->bank_node.id = idx;
bank_curr->valid = mcreg._s.valid;
bank_curr->dimminfop = dimminfop;
if (!mcreg._s.valid) {
mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
goto exit;
}
ifactor = (mcreg._s.lk ^ 0xF) + 1;
size = (((mcreg._s.uk & 0x3FF) + 1) * 0x4000000) / ifactor;
base = mcreg._s.um & ~mcreg._s.uk;
base <<= MADR_UPA_SHIFT;
bank_curr->uk = mcreg._s.uk;
bank_curr->um = mcreg._s.um;
bank_curr->lk = mcreg._s.lk;
bank_curr->lm = mcreg._s.lm;
bank_curr->size = size;
bank_curr->pos = bank_no >> 1;
ASSERT((bank_curr->pos == 0) || (bank_curr->pos == 1));
#if defined(lint)
DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add 3: logical bank num %d, "
"lk 0x%lx uk 0x%lx um 0x%lx ifactor 0x%x size 0x%lx base 0x%lx\n",
idx, mcreg._s.lk, mcreg._s.uk, mcreg._s.um, ifactor, size, base));
#else
DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add 3: logical bank num %d, "
"lk 0x%x uk 0x%x um 0x%x ifactor 0x%x size 0x%lx base 0x%lx\n",
idx, mcreg._s.lk, mcreg._s.uk, mcreg._s.um, ifactor, size, base));
#endif
idx = mc_id * NDGRPS + (bank_no % NDGRPS);
if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head)) == NULL) {
cmn_err(CE_WARN, "mlayout_add: dgrp %d doesn't exist\n", idx);
kmem_free(bank_curr, sizeof (struct bank_info));
status = -1;
goto exit;
}
bank_curr->devgrp_id = idx;
dgrp->size += size;
for (i = 0; i < NDIMMS; i++) {
dmidx = dgrp->dgrp_node.id * NDIMMS + i;
dgrp->deviceids[i] = dmidx;
if ((dev = (struct device_info *)mc_node_get(dmidx,
device_head)) == NULL) {
cmn_err(CE_WARN, "mlayout_add:dev %d doesn't exist\n",
dmidx);
kmem_free(bank_curr, sizeof (struct bank_info));
status = -1;
goto exit;
}
dev->size += (size / NDIMMS);
DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add DIMM:id %d, size %lu\n",
dmidx, size));
}
if (seg_curr = seg_match_base(base)) {
seg_curr->nbanks++;
seg_curr->size += size;
if (ifactor > seg_curr->ifactor)
seg_curr->ifactor = ifactor;
bank_curr->seg_id = seg_curr->seg_node.id;
} else {
seg_curr = (struct seg_info *)
kmem_zalloc(sizeof (struct seg_info), KM_SLEEP);
bank_curr->seg_id = seg_id;
seg_curr->seg_node.id = seg_id++;
seg_curr->base = base;
seg_curr->size = size;
seg_curr->nbanks = 1;
seg_curr->ifactor = ifactor;
mc_node_add((mc_dlist_t *)seg_curr, &seg_head, &seg_tail);
nsegments++;
}
bank_curr->local_id = seg_curr->nbanks - 1;
if (seg_curr->hb_inseg != NULL) {
bank_curr->p_inseg = seg_curr->tb_inseg;
bank_curr->n_inseg = seg_curr->tb_inseg->n_inseg;
seg_curr->tb_inseg->n_inseg = bank_curr;
seg_curr->tb_inseg = bank_curr;
} else {
bank_curr->n_inseg = bank_curr->p_inseg = NULL;
seg_curr->hb_inseg = seg_curr->tb_inseg = bank_curr;
}
DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: + bank to seg, id %d\n",
seg_curr->seg_node.id));
if (mc_dimm_sids) {
rw_enter(&mcdimmsids_rw, RW_WRITER);
mc_update_bank(bank_curr);
rw_exit(&mcdimmsids_rw);
}
mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
memsize += size;
if (seg_curr->nbanks > maxbanks)
maxbanks = seg_curr->nbanks;
exit:
mutex_exit(&mcdatamutex);
return (status);
}
static void
mlayout_del(int mc_id, int delete)
{
int i, j, dgrpid, devid, bankid, ndevgrps;
struct seg_info *seg;
struct bank_info *bank_curr;
struct mctrl_info *mctrl;
mc_dlist_t *dgrp_ptr;
mc_dlist_t *dev_ptr;
uint64_t base;
mutex_enter(&mcdatamutex);
if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id, mctrl_head)) !=
NULL) {
ndevgrps = mctrl->ndevgrps;
mc_node_del((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
kmem_free(mctrl, sizeof (struct mctrl_info));
nmcs--;
if (ndevgrps == 0) {
mutex_exit(&mcdatamutex);
return;
}
} else
cmn_err(CE_WARN, "MC mlayout_del: mctrl is not found\n");
for (i = 0; i < NDGRPS; i++) {
dgrpid = mc_id * NDGRPS + i;
if (!(dgrp_ptr = mc_node_get(dgrpid, dgrp_head))) {
cmn_err(CE_WARN, "mlayout_del: no devgrp %d\n", dgrpid);
continue;
}
for (j = 0; j < NDIMMS; j++) {
devid = dgrpid * NDIMMS + j;
if (dev_ptr = mc_node_get(devid, device_head)) {
mc_node_del(dev_ptr, &device_head,
&device_tail);
kmem_free(dev_ptr, sizeof (struct device_info));
} else {
cmn_err(CE_WARN, "mlayout_del: no dev %d\n",
devid);
}
}
mc_node_del(dgrp_ptr, &dgrp_head, &dgrp_tail);
kmem_free(dgrp_ptr, sizeof (struct dgrp_info));
}
for (i = 0; i < NBANKS; i++) {
bankid = mc_id * NBANKS + i;
DPRINTF(MC_DESTRC_DEBUG, ("bank id %d\n", bankid));
if (!(bank_curr = (struct bank_info *)mc_node_get(bankid,
bank_head))) {
cmn_err(CE_WARN, "mlayout_del: no bank %d\n", bankid);
continue;
}
if (bank_curr->valid) {
base = bank_curr->um & ~bank_curr->uk;
base <<= MADR_UPA_SHIFT;
bank_curr->valid = 0;
memsize -= bank_curr->size;
if (!(seg = seg_match_base(base))) {
cmn_err(CE_WARN, "mlayout_del: no seg\n");
mc_node_del((mc_dlist_t *)bank_curr, &bank_head,
&bank_tail);
kmem_free(bank_curr, sizeof (struct bank_info));
continue;
}
if (bank_curr->n_inseg == NULL) {
seg->tb_inseg = bank_curr->p_inseg;
} else {
bank_curr->n_inseg->p_inseg =
bank_curr->p_inseg;
}
if (bank_curr->p_inseg == NULL) {
seg->hb_inseg = bank_curr->n_inseg;
} else {
bank_curr->p_inseg->n_inseg =
bank_curr->n_inseg;
}
seg->nbanks--;
seg->size -= bank_curr->size;
if (seg->nbanks == 0) {
mc_node_del((mc_dlist_t *)seg, &seg_head,
&seg_tail);
kmem_free(seg, sizeof (struct seg_info));
nsegments--;
}
}
mc_node_del((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
kmem_free(bank_curr, sizeof (struct bank_info));
}
if (mc_dimm_sids && delete) {
rw_enter(&mcdimmsids_rw, RW_WRITER);
i = mc_get_sid_cache_index(mc_id);
if (i >= 0) {
mc_dimm_sids[i].state = MC_DIMM_SIDS_INVALID;
if (mc_dimm_sids[i].sids) {
kmem_free(mc_dimm_sids[i].sids,
sizeof (dimm_sid_t) * (NDGRPS * NDIMMS));
mc_dimm_sids[i].sids = NULL;
}
}
rw_exit(&mcdimmsids_rw);
}
mutex_exit(&mcdatamutex);
}
static struct seg_info *
seg_match_base(u_longlong_t base)
{
static struct seg_info *seg_ptr;
seg_ptr = (struct seg_info *)seg_head;
while (seg_ptr != NULL) {
DPRINTF(MC_LIST_DEBUG, ("seg_match: base %lu,given base %llu\n",
seg_ptr->base, base));
if (seg_ptr->base == base)
break;
seg_ptr = (struct seg_info *)seg_ptr->seg_node.next;
}
return (seg_ptr);
}
static void
mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
{
DPRINTF(MC_LIST_DEBUG, ("mc_node_add: node->id %d head %p tail %p\n",
node->id, (void *)*head, (void *)*tail));
if (*head != NULL) {
node->prev = *tail;
node->next = (*tail)->next;
(*tail)->next = node;
*tail = node;
} else {
node->next = node->prev = NULL;
*head = *tail = node;
}
}
static void
mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
{
if (node->next == NULL) {
*tail = node->prev;
} else {
node->next->prev = node->prev;
}
if (node->prev == NULL) {
*head = node->next;
} else {
node->prev->next = node->next;
}
}
static mc_dlist_t *
mc_node_get(int id, mc_dlist_t *head)
{
mc_dlist_t *node;
node = head;
while (node != NULL) {
DPRINTF(MC_LIST_DEBUG, ("mc_node_get: id %d, given id %d\n",
node->id, id));
if (node->id == id)
break;
node = node->next;
}
return (node);
}
static void
mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm)
{
if (&plat_add_mem_unum_label)
plat_add_mem_unum_label(buf, mcid, bank, dimm);
}
static int
mc_get_sid_cache_index(int mcid)
{
int i;
for (i = 0; i < max_entries; i++) {
if (mcid == mc_dimm_sids[i].mcid)
return (i);
}
return (-1);
}
static void
mc_update_bank(struct bank_info *bank)
{
int i, j;
int bankid, mcid, dgrp_no;
bankid = bank->bank_node.id;
mcid = bankid / NBANKS;
i = mc_get_sid_cache_index(mcid);
if (mc_dimm_sids[i].state == MC_DIMM_SIDS_INVALID)
mc_dimm_sids[i].state = MC_DIMM_SIDS_REQUESTED;
mc_dimm_sids[i].seg_id = bank->seg_id;
if (mc_dimm_sids[i].sids == NULL) {
mc_dimm_sids[i].sids = (dimm_sid_t *)kmem_zalloc(
sizeof (dimm_sid_t) * (NDGRPS * NDIMMS), KM_SLEEP);
}
dgrp_no = bank->devgrp_id % NDGRPS;
for (j = 0; j < NDIMMS; j++) {
bank->dimmsidp[j] =
&mc_dimm_sids[i].sids[j + (NDIMMS * dgrp_no)];
}
}
static int
mc_populate_sid_cache(void)
{
struct bank_info *bank;
if (&plat_populate_sid_cache == 0)
return (ENOTSUP);
ASSERT(RW_WRITE_HELD(&mcdimmsids_rw));
bank = (struct bank_info *)bank_head;
while (bank != NULL) {
if (!bank->valid) {
bank = (struct bank_info *)bank->bank_node.next;
continue;
}
mc_update_bank(bank);
bank = (struct bank_info *)bank->bank_node.next;
}
return (plat_populate_sid_cache(mc_dimm_sids, max_entries));
}
static void
mc_init_sid_cache_thr(void)
{
ASSERT(mc_dimm_sids == NULL);
mutex_enter(&mcdatamutex);
rw_enter(&mcdimmsids_rw, RW_WRITER);
mc_dimm_sids = plat_alloc_sid_cache(&max_entries);
(void) mc_populate_sid_cache();
rw_exit(&mcdimmsids_rw);
mutex_exit(&mcdatamutex);
}
static int
mc_init_sid_cache(void)
{
if (&plat_alloc_sid_cache) {
(void) thread_create(NULL, 0, mc_init_sid_cache_thr, NULL, 0,
&p0, TS_RUN, minclsyspri);
return (0);
} else
return (ENOTSUP);
}
static int
mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp)
{
int i;
if (buflen < DIMM_SERIAL_ID_LEN)
return (ENOSPC);
if (!rw_tryenter(&mcdimmsids_rw, RW_READER))
return (EAGAIN);
if (mc_dimm_sids == NULL) {
rw_exit(&mcdimmsids_rw);
return (EAGAIN);
}
for (i = 0; i < max_entries; i++) {
if (mc_dimm_sids[i].mcid == mcid)
break;
}
if ((i == max_entries) || (!mc_dimm_sids[i].sids)) {
rw_exit(&mcdimmsids_rw);
return (ENOENT);
}
(void) strlcpy(buf, mc_dimm_sids[i].sids[dimm],
DIMM_SERIAL_ID_LEN);
*lenp = strlen(buf);
rw_exit(&mcdimmsids_rw);
return (0);
}