1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * Support routines for managing state related to memory modules.
28 */
29
30#include <cmd_mem.h>
31#include <cmd_dimm.h>
32#include <cmd_bank.h>
33#include <cmd.h>
34#ifdef sun4u
35#include <cmd_dp.h>
36#endif
37#ifdef sun4v
38#include <cmd_branch.h>
39#endif
40
41#include <errno.h>
42#include <strings.h>
43#include <ctype.h>
44#include <fcntl.h>
45#include <unistd.h>
46#include <fm/fmd_api.h>
47#include <sys/fm/protocol.h>
48#include <sys/mem.h>
49#include <sys/nvpair.h>
50
51const char *
52cmd_fmri_get_unum(nvlist_t *fmri)
53{
54	const char *scheme, *unum;
55	uint8_t vers;
56
57	if (nvlist_lookup_pairs(fmri, 0,
58	    FM_VERSION, DATA_TYPE_UINT8, &vers,
59	    FM_FMRI_SCHEME, DATA_TYPE_STRING, &scheme,
60	    FM_FMRI_MEM_UNUM, DATA_TYPE_STRING, &unum,
61	    NULL) != 0 || vers > FM_MEM_SCHEME_VERSION ||
62	    strcmp(scheme, FM_FMRI_SCHEME_MEM) != 0)
63		return (NULL);
64
65	return (unum);
66}
67
68char *
69cmd_mem_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, const char *unum)
70{
71	const char *fmt = "%s_%s_serd";
72	size_t sz = snprintf(NULL, 0, fmt, serdbase, unum) + 1;
73	char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
74	(void) snprintf(nm, sz, fmt, serdbase, unum);
75
76	return (nm);
77}
78
79char *
80cmd_page_serdnm_create(fmd_hdl_t *hdl, const char *serdbase,
81    uint64_t phys_addr)
82{
83	const char *fmt = "%s_%llXserd";
84	size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr) + 1;
85	char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
86	(void) snprintf(nm, sz, fmt, serdbase, phys_addr);
87
88	return (nm);
89}
90
91char *
92cmd_mq_serdnm_create(fmd_hdl_t *hdl, const char *serdbase,
93    uint64_t phys_addr, uint16_t cw, uint16_t pos)
94{
95	const char *fmt = "%s_%llX_%x_%x_serd";
96	size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr, cw, pos) + 1;
97	char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
98	(void) snprintf(nm, sz, fmt, serdbase, phys_addr, cw, pos);
99
100	return (nm);
101}
102
103void
104cmd_mem_case_restore(fmd_hdl_t *hdl, cmd_case_t *cc, fmd_case_t *cp,
105    const char *serdbase, const char *unum)
106{
107	cmd_case_restore(hdl, cc, cp, cmd_mem_serdnm_create(hdl, serdbase,
108	    unum));
109}
110
111void
112cmd_mem_retirestat_create(fmd_hdl_t *hdl, fmd_stat_t *st, const char *unum,
113    uint64_t value, const char *prefix)
114{
115	char *c;
116
117	/*
118	 * Prior to Niagara-2, every bank had to have at least two dimms; it
119	 * was therefore impossible for the retirestat of a bank to ever have
120	 * the same name (strcmp() == 0) as that of a dimm.
121	 *
122	 * Niagara-2 and VF, in "single channel mode" , retrieve an entire
123	 * cache line from a single dimm.  We therefore use a different
124	 * prefix to name the bank retirestat vs. the dimm retirestat,
125	 * or else the DE will abort trying to register a duplicate stat name
126	 * with fmd.
127	 */
128	(void) snprintf(st->fmds_name, sizeof (st->fmds_name), "%s%s",
129	    prefix, unum);
130	(void) snprintf(st->fmds_desc, sizeof (st->fmds_desc),
131	    "retirements for %s", unum);
132	st->fmds_type = FMD_TYPE_UINT64;
133	st->fmds_value.ui64 = value;
134
135	/*
136	 * Sanitize the name of the statistic -- standard unums won't get
137	 * by fmd's validity checker.
138	 */
139	for (c = st->fmds_name; *c != '\0'; c++) {
140		if (!isupper(*c) && !islower(*c) &&
141		    !isdigit(*c) && *c != '-' && *c != '_' && *c != '.')
142			*c = '_';
143	}
144
145	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, 1, st);
146}
147
148int
149cmd_mem_thresh_check(fmd_hdl_t *hdl, uint_t nret)
150{
151	ulong_t npages = cmd_mem_get_phys_pages(hdl);
152	ulong_t wrnpgs;
153
154	fmd_hdl_debug(hdl, "thresh_check: npages is %lu\n", npages);
155	if (npages == 0) {
156		return (0);
157	}
158
159	if (cmd.cmd_thresh_abs_sysmem != 0) {
160		wrnpgs = cmd.cmd_thresh_abs_sysmem;
161	} else {
162		/* threshold is in thousandths of a percent */
163		wrnpgs = npages * cmd.cmd_thresh_tpct_sysmem / 100000;
164	}
165
166	fmd_hdl_debug(hdl, "thresh_check: nret %u, wrn %lu\n", nret, wrnpgs);
167
168	return (nret > wrnpgs);
169}
170
171nvlist_t *
172cmd_mem_fmri_create(const char *unum, char **serids, size_t nserids)
173{
174	nvlist_t *fmri;
175
176	if ((errno = nvlist_alloc(&fmri, NV_UNIQUE_NAME, 0)) != 0)
177		return (NULL);
178
179	if ((errno = nvlist_add_uint8(fmri, FM_VERSION,
180	    FM_MEM_SCHEME_VERSION)) != 0 || (errno = nvlist_add_string(fmri,
181	    FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM)) != 0 || (errno =
182	    nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum)) != 0) {
183		nvlist_free(fmri);
184		return (NULL);
185	}
186
187	if ((nserids > 0) && (serids != NULL)) {
188		(void) nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID,
189		    serids, nserids);
190	}
191	return (fmri);
192}
193
194nvlist_t *
195cmd_mem_fmri_derive(fmd_hdl_t *hdl, uint64_t afar, uint64_t afsr, uint16_t synd)
196{
197	mem_name_t mn;
198	nvlist_t *fmri;
199	int fd;
200
201	if ((fd = open("/dev/mem", O_RDONLY)) < 0)
202		return (NULL);
203
204	mn.m_addr = afar;
205	mn.m_synd = synd;
206	mn.m_type[0] = afsr;
207	mn.m_type[1] = 0;
208	mn.m_namelen = 100;
209
210	for (;;) {
211		mn.m_name = fmd_hdl_alloc(hdl, mn.m_namelen, FMD_SLEEP);
212
213		if (ioctl(fd, MEM_NAME, &mn) == 0)
214			break;
215
216		fmd_hdl_free(hdl, mn.m_name, mn.m_namelen);
217
218		if (errno != ENOSPC) {
219			(void) close(fd);
220			return (NULL);
221		}
222
223		mn.m_namelen *= 2;
224	}
225
226	(void) close(fd);
227
228	fmri = cmd_mem_fmri_create(mn.m_name, NULL, 0);
229	fmd_hdl_free(hdl, mn.m_name, mn.m_namelen);
230
231	return (fmri);
232}
233
234void
235cmd_iorxefrx_queue(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf)
236{
237
238	fmd_hdl_debug(hdl, "queueing IOxE/RxE/FRx for matching\n");
239
240	rf->rf_expid = fmd_timer_install(hdl, (void *)CMD_TIMERTYPE_MEM, NULL,
241	    cmd.cmd_iorxefrx_window);
242	cmd_list_append(&cmd.cmd_iorxefrx, rf);
243}
244
245void
246cmd_iorxefrx_free(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf)
247{
248	/* It's not persisted, so just remove it */
249	cmd_list_delete(&cmd.cmd_iorxefrx, rf);
250	fmd_hdl_free(hdl, rf, sizeof (cmd_iorxefrx_t));
251}
252
253void
254cmd_mem_timeout(fmd_hdl_t *hdl, id_t id)
255{
256	cmd_iorxefrx_t *rf;
257
258	for (rf = cmd_list_next(&cmd.cmd_iorxefrx); rf != NULL;
259	    rf = cmd_list_next(rf)) {
260		if (rf->rf_expid == id) {
261			fmd_hdl_debug(hdl, "reclaiming iorxefrx tid %d\n", id);
262			cmd_iorxefrx_free(hdl, rf);
263			return;
264		}
265	}
266}
267
268void
269cmd_mem_gc(fmd_hdl_t *hdl)
270{
271	cmd_dimm_gc(hdl);
272	cmd_bank_gc(hdl);
273#ifdef sun4v
274	cmd_branch_gc(hdl);
275#endif
276}
277
278void
279cmd_mem_fini(fmd_hdl_t *hdl)
280{
281	cmd_iorxefrx_t *rf;
282
283	cmd_dimm_fini(hdl);
284	cmd_bank_fini(hdl);
285#ifdef sun4v
286	cmd_branch_fini(hdl);
287#endif
288
289	while ((rf = cmd_list_next(&cmd.cmd_iorxefrx)) != NULL)
290		cmd_iorxefrx_free(hdl, rf);
291}
292