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 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
22 * Use is subject to license terms.
23 */
24
25#pragma ident	"%Z%%M%	%I%	%E% SMI"
26
27/*
28 * Given a unum including an offset calculate the associated system
29 * address.  This may be different to when the original PA to unum
30 * calculation took place if interleave etc has changed.
31 */
32
33#include <sys/errno.h>
34#include <sys/types.h>
35#include <sys/mc.h>
36
37#include <mcamd_api.h>
38#include <mcamd_err.h>
39
40/*
41 * The submitted unum must have the MC and DIMM numbers and an offset.
42 * Any cs info it has will not be used - we will reconstruct cs info.
43 * This is because cs is not in the topology used for diagnosis.
44 */
45int
46mcamd_unumtopa(struct mcamd_hdl *hdl, mcamd_node_t *root, mc_unum_t *unump,
47    uint64_t *pa)
48{
49	mcamd_node_t *mc, *dimm;
50	uint64_t num, holesz;
51
52	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa: chip %d "
53	    "mc %d dimm %d offset 0x%llx\n", unump->unum_chip, unump->unum_mc,
54	    unump->unum_dimms[0], unump->unum_offset);
55
56	if (!MCAMD_RC_OFFSET_VALID(unump->unum_offset)) {
57		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa: offset "
58		    "invalid\n");
59		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
60	}
61
62	/*
63	 * Search current config for a MC number matching the chip in the
64	 * unum.
65	 */
66	for (mc = mcamd_mc_next(hdl, root, NULL); mc != NULL;
67	    mc = mcamd_mc_next(hdl, root, mc)) {
68		if (!mcamd_get_numprops(hdl,
69		    mc, MCAMD_PROP_NUM, &num,
70		    mc, MCAMD_PROP_DRAMHOLE_SIZE, &holesz,
71		    NULL)) {
72			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_unumtopa: "
73			    "failed to lookup num, dramhole for MC 0x%p\n", mc);
74			return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
75		}
76		if (num == unump->unum_chip)
77			break;
78	}
79	if (mc == NULL) {
80		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa; "
81		    "no match for MC %d\n", unump->unum_chip);
82		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
83	}
84
85	/*
86	 * Search DIMMs of this MC.  We can match against the
87	 * first dimm in the unum - if there is more than one they all
88	 * share the same chip-selects anyway and the pa we will resolve
89	 * to is not finer grained than the 128-bits of a dimm pair.
90	 */
91	for (dimm = mcamd_dimm_next(hdl, mc, NULL); dimm != NULL;
92	    dimm = mcamd_dimm_next(hdl, mc, dimm)) {
93		if (!mcamd_get_numprop(hdl, dimm, MCAMD_PROP_NUM, &num)) {
94			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_unumtopa: "
95			    "failed to lookup num for dimm 0xx%p\n",
96			    dimm);
97			return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
98		}
99		if (num == unump->unum_dimms[0])
100			break;
101	}
102	if (dimm == NULL) {
103		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa; "
104		    "no match for dimm %d cs %d on MC %d\n",
105		    unump->unum_dimms[0], unump->unum_cs, unump->unum_chip);
106		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
107	}
108
109	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa: matched "
110	    "mc 0x%p dimm 0x%p; resolving offset 0x%llx\n",
111	    mc, dimm, unump->unum_offset);
112
113	if (mc_offset_to_pa(hdl, mc, dimm, unump->unum_offset, pa) < 0) {
114		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa: "
115		    "mc_offset_to_pa failed: %s\n", mcamd_errmsg(hdl));
116		return (-1);	/* errno already set */
117	}
118
119	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_unumtopa: "
120	    "mc_offset_to_pa succeeded and returned pa=0x%llx: %\n",
121	    *pa);
122
123	/*
124	 * If this MC has a dram address hole just below 4GB then we must
125	 * hoist all address from the hole start upwards by the hole size
126	 */
127	if (holesz != 0) {
128		if (*pa >= 0x100000000 - holesz)
129			*pa += holesz;
130		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_untopa: hoist "
131		    "above dram hole of size 0x%llx to get pa=0x%llx",
132		    holesz, *pa);
133	}
134
135	return (0);
136}
137