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 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <sys/sysmacros.h>
29 #include <sys/machsystm.h>
30 #include <sys/cpuvar.h>
31 #include <sys/ddi_implfuncs.h>
32 #include <px_csr.h>
33 #include <px_regs.h>
34 #include <px_obj.h>
35 #include <sys/pci_tools.h>
36 #include <px_tools_var.h>
37 #include <px_asm_4u.h>
38 #include <px_lib4u.h>
39 #include <px_tools_ext.h>
40
41 /*
42 * Delay needed to have a safe environment envelop any error which could
43 * surface. The larger the number of bridges and switches, the larger the
44 * number needed here.
45 *
46 * The way it works is as follows:
47 *
48 * An access is done which causes an error. Fire errors are handled with
49 * ontrap protection and usually come in first. Fabric errors can come in
50 * later.
51 *
52 * px_phys_peek_4u() disables interrupts. Interrupts are reenabled at the end
53 * of that function if no errors have been caught by the trap handler, or by
54 * peek_fault() which executes when a fire error occurs.
55 *
56 * Fabric error messages get put on an event queue but are not processed until
57 * interrupts are reenabled.
58 *
59 * The delay gives time for the fabric errors to be processed by FMA before
60 * changing the fm error flag back to DDI_FM_ERR_UNEXPECTED. If this isn't
61 * done, then the fabric error which should be safe can panic the system.
62 *
63 * Note: this is a workaround until a better solution is found. While this
64 * number is high, given enough bridges and switches in the device path, this
65 * workaround can break. Also, other PIL 15 interrupts besides the ones we are
66 * enveloping could delay processing of the interrupt we are trying to protect.
67 */
68
69 /*
70 * Set delay to 10 ms
71 */
72 int pxtool_delay_usec = 10000;
73
74 /* Number of inos per root complex. */
75 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
76
77 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
78 typedef union {
79 uint64_t u64;
80 uint32_t u32;
81 uint16_t u16;
82 uint8_t u8;
83 } peek_poke_value_t;
84
85 /*
86 * Safe C wrapper around assy language routine px_phys_peek_4u
87 *
88 * Type is TRUE for big endian, FALSE for little endian.
89 * Size is 1, 2, 4 or 8 bytes.
90 * paddr is the physical address in IO space to access read.
91 * value_p is where the value is returned.
92 */
93 static int
pxtool_safe_phys_peek(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)94 pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
95 uint64_t *value_p)
96 {
97 px_pec_t *pec_p = px_p->px_pec_p;
98 pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
99 on_trap_data_t otd;
100 peek_poke_value_t peek_value;
101 int err = DDI_SUCCESS;
102
103 mutex_enter(&pec_p->pec_pokefault_mutex);
104 pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
105
106 pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
107
108 /*
109 * Set up trap handling to make the access safe.
110 *
111 * on_trap works like setjmp.
112 * Set it up to not panic on data access error,
113 * but to call peek_fault instead.
114 * Call px_phys_peek_4u after trap handling is setup.
115 * When on_trap returns FALSE, it has been setup.
116 * When it returns TRUE, an it has caught an error.
117 */
118 if (!on_trap(&otd, OT_DATA_ACCESS)) {
119 otd.ot_trampoline = (uintptr_t)&peek_fault;
120 err = px_phys_peek_4u(size, paddr, &peek_value.u64, type);
121 } else
122 err = DDI_FAILURE;
123
124 no_trap();
125
126 /*
127 * Workaround: delay taking down safe access env.
128 * For more info, see comments where pxtool_delay_usec is declared.
129 */
130 if ((err == DDI_FAILURE) && (pxtool_delay_usec > 0))
131 delay(drv_usectohz(pxtool_delay_usec));
132
133 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
134 pxu_p->pcitool_addr = NULL;
135 mutex_exit(&pec_p->pec_pokefault_mutex);
136
137 if (err != DDI_FAILURE) {
138 switch (size) {
139 case 8:
140 *value_p = peek_value.u64;
141 break;
142 case 4:
143 *value_p = (uint64_t)peek_value.u32;
144 break;
145 case 2:
146 *value_p = (uint64_t)peek_value.u16;
147 break;
148 case 1:
149 *value_p = (uint64_t)peek_value.u8;
150 break;
151 default:
152 err = DDI_FAILURE;
153 }
154 }
155
156 return (err);
157 }
158
159 /*
160 * Safe C wrapper around assy language routine px_phys_poke_4u
161 *
162 * Type is TRUE for big endian, FALSE for little endian.
163 * Size is 1,2,4 or 8 bytes.
164 * paddr is the physical address in IO space to access read.
165 * value contains the value to be written.
166 */
167 static int
pxtool_safe_phys_poke(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)168 pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
169 uint64_t value)
170 {
171 on_trap_data_t otd;
172 pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
173 px_pec_t *pec_p = px_p->px_pec_p;
174 peek_poke_value_t poke_value;
175 int err = DDI_SUCCESS;
176
177 switch (size) {
178 case 8:
179 poke_value.u64 = value;
180 break;
181 case 4:
182 poke_value.u32 = (uint32_t)value;
183 break;
184 case 2:
185 poke_value.u16 = (uint16_t)value;
186 break;
187 case 1:
188 poke_value.u8 = (uint8_t)value;
189 break;
190 default:
191 return (DDI_FAILURE);
192 }
193
194 mutex_enter(&pec_p->pec_pokefault_mutex);
195 pec_p->pec_ontrap_data = &otd;
196 pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
197 pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
198
199 /*
200 * on_trap works like setjmp.
201 * Set it up to not panic on data access error,
202 * but to call poke_fault instead.
203 * Call px_phys_poke_4u after trap handling is setup.
204 * When on_trap returns FALSE, it has been setup.
205 * When it returns TRUE, an it has caught an error.
206 */
207 if (!on_trap(&otd, OT_DATA_ACCESS)) {
208
209 otd.ot_trampoline = (uintptr_t)&poke_fault;
210 err = px_phys_poke_4u(size, paddr, &poke_value.u64, type);
211 } else
212 err = DDI_FAILURE;
213
214 px_lib_clr_errs(px_p, 0, paddr);
215
216 if (otd.ot_trap & OT_DATA_ACCESS)
217 err = DDI_FAILURE;
218
219 /* Take down protected environment. */
220 no_trap();
221 pec_p->pec_ontrap_data = NULL;
222
223 /*
224 * Workaround: delay taking down safe access env.
225 * For more info, see comments where pxtool_delay_usec is declared.
226 */
227 if (pxtool_delay_usec > 0)
228 delay(drv_usectohz(pxtool_delay_usec));
229
230 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
231 pxu_p->pcitool_addr = NULL;
232 mutex_exit(&pec_p->pec_pokefault_mutex);
233
234 return (err);
235 }
236
237
238 /*
239 * Wrapper around pxtool_safe_phys_peek/poke.
240 *
241 * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately.
242 *
243 * Dip is of the nexus,
244 * phys_addr is the address to write in physical space.
245 * pcitool_status returns more detailed status in addition to a more generic
246 * errno-style function return value.
247 * other args are self-explanatory.
248 *
249 * This function assumes that offset, bdf, and acc_attr are current in
250 * prg_p. It also assumes that prg_p->phys_addr is the final phys addr,
251 * including offset.
252 * This function modifies prg_p status and data.
253 */
254 /*ARGSUSED*/
255 static int
pxtool_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)256 pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p,
257 boolean_t is_write)
258 {
259 dev_info_t *dip = px_p->px_dip;
260 uint64_t phys_addr = prg_p->phys_addr;
261 boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr);
262 size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
263 int rval = SUCCESS;
264
265 /* Alignment checking. Assumes base address is 8-byte aligned. */
266 if (!IS_P2ALIGNED(phys_addr, size)) {
267 DBG(DBG_TOOLS, dip, "not aligned.\n");
268 prg_p->status = PCITOOL_NOT_ALIGNED;
269
270 rval = EINVAL;
271
272 } else if (is_write) { /* Made it through checks. Do the access. */
273
274 DBG(DBG_PHYS_ACC, dip,
275 "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n",
276 size, (endian ? "BE" : "LE"), phys_addr);
277
278 if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr,
279 *data_p) != DDI_SUCCESS) {
280 DBG(DBG_PHYS_ACC, dip,
281 "%d byte %s pxtool_safe_phys_poke at addr "
282 "0x%" PRIx64 " failed\n",
283 size, (endian ? "BE" : "LE"), phys_addr);
284 prg_p->status = PCITOOL_INVALID_ADDRESS;
285
286 rval = EFAULT;
287 }
288
289 } else { /* Read */
290
291 DBG(DBG_PHYS_ACC, dip,
292 "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n",
293 size, (endian ? "BE" : "LE"), phys_addr);
294
295 if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr,
296 data_p) != DDI_SUCCESS) {
297 DBG(DBG_PHYS_ACC, dip,
298 "%d byte %s pxtool_safe_phys_peek at addr "
299 "0x%" PRIx64 " failed\n",
300 size, (endian ? "BE" : "LE"), phys_addr);
301 prg_p->status = PCITOOL_INVALID_ADDRESS;
302
303 rval = EFAULT;
304 }
305 }
306 return (rval);
307 }
308
309
310 int
pxtool_pcicfg_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)311 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
312 uint64_t *data_p, boolean_t is_write)
313 {
314 return (pxtool_access(px_p, prg_p, data_p, is_write));
315 }
316
317 int
pxtool_pciiomem_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)318 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
319 uint64_t *data_p, boolean_t is_write)
320 {
321 return (pxtool_access(px_p, prg_p, data_p, is_write));
322 }
323
324 int
pxtool_dev_reg_ops_platchk(dev_info_t * dip,pcitool_reg_t * prg_p)325 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
326 {
327 /*
328 * Guard against checking a root nexus which is empty.
329 * On some systems this will result in a Fatal Reset.
330 */
331 if (ddi_get_child(dip) == NULL) {
332 DBG(DBG_TOOLS, dip,
333 "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n");
334 prg_p->status = PCITOOL_IO_ERROR;
335 return (ENXIO);
336 }
337
338 return (SUCCESS);
339 }
340
341 /*
342 * Perform register accesses on the nexus device itself.
343 */
344 int
pxtool_bus_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)345 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
346 {
347 pcitool_reg_t prg;
348 uint64_t base_addr;
349 uint32_t reglen;
350 px_t *px_p = DIP_TO_STATE(dip);
351 px_nexus_regspec_t *px_rp = NULL;
352 uint32_t numbanks = 0;
353 boolean_t write_flag = B_FALSE;
354 uint32_t rval = 0;
355
356 if (cmd == PCITOOL_NEXUS_SET_REG)
357 write_flag = B_TRUE;
358
359 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
360
361 /* Read data from userland. */
362 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
363 DDI_SUCCESS) {
364 DBG(DBG_TOOLS, dip, "Error reading arguments\n");
365 return (EFAULT);
366 }
367
368 /* Read reg property which contains starting addr and size of banks. */
369 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
370 "reg", (int **)&px_rp, ®len) == DDI_SUCCESS) {
371 if (((reglen * sizeof (int)) %
372 sizeof (px_nexus_regspec_t)) != 0) {
373 DBG(DBG_TOOLS, dip, "reg prop not well-formed");
374 prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
375 rval = EIO;
376 goto done;
377 }
378 }
379
380 numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
381
382 /* Bounds check the bank number. */
383 if (prg.barnum >= numbanks) {
384 prg.status = PCITOOL_OUT_OF_RANGE;
385 rval = EINVAL;
386 goto done;
387 }
388
389 base_addr = px_rp[prg.barnum].phys_addr;
390 prg.phys_addr = base_addr + prg.offset;
391
392 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", "
393 "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:"
394 "0x%" PRIx64 "\n",
395 base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size);
396
397 if (prg.offset >= px_rp[prg.barnum].size) {
398 prg.status = PCITOOL_OUT_OF_RANGE;
399 rval = EINVAL;
400 goto done;
401 }
402
403 /* Access device. prg.status is modified. */
404 rval = pxtool_access(px_p, &prg, &prg.data, write_flag);
405
406 done:
407 if (px_rp != NULL)
408 ddi_prop_free(px_rp);
409
410 prg.drvr_version = PCITOOL_VERSION;
411 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
412 mode) != DDI_SUCCESS) {
413 DBG(DBG_TOOLS, dip, "Copyout failed.\n");
414 return (EFAULT);
415 }
416
417 return (rval);
418 }
419