1624Sschwartz /*
2624Sschwartz * CDDL HEADER START
3624Sschwartz *
4624Sschwartz * The contents of this file are subject to the terms of the
52276Sschwartz * Common Development and Distribution License (the "License").
62276Sschwartz * You may not use this file except in compliance with the License.
7624Sschwartz *
8624Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9624Sschwartz * or http://www.opensolaris.org/os/licensing.
10624Sschwartz * See the License for the specific language governing permissions
11624Sschwartz * and limitations under the License.
12624Sschwartz *
13624Sschwartz * When distributing Covered Code, include this CDDL HEADER in each
14624Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15624Sschwartz * If applicable, add the following below this CDDL HEADER, with the
16624Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying
17624Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner]
18624Sschwartz *
19624Sschwartz * CDDL HEADER END
20624Sschwartz */
21624Sschwartz /*
223720Sschwartz * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23624Sschwartz * Use is subject to license terms.
24624Sschwartz */
25624Sschwartz
26624Sschwartz #pragma ident "%Z%%M% %I% %E% SMI"
27624Sschwartz
28624Sschwartz #include <sys/sysmacros.h>
29624Sschwartz #include <sys/machsystm.h>
30624Sschwartz #include <sys/cpuvar.h>
31624Sschwartz #include <sys/ddi_implfuncs.h>
32624Sschwartz #include <px_csr.h>
33624Sschwartz #include <px_regs.h>
34624Sschwartz #include <px_obj.h>
35624Sschwartz #include <sys/pci_tools.h>
36624Sschwartz #include <px_tools_var.h>
37624Sschwartz #include <px_asm_4u.h>
38624Sschwartz #include <px_lib4u.h>
39777Sschwartz #include <px_tools_ext.h>
40624Sschwartz
41624Sschwartz /*
42624Sschwartz * Delay needed to have a safe environment envelop any error which could
43624Sschwartz * surface. The larger the number of bridges and switches, the larger the
44624Sschwartz * number needed here.
45624Sschwartz *
461064Sschwartz * The way it works is as follows:
471064Sschwartz *
481064Sschwartz * An access is done which causes an error. Fire errors are handled with
491064Sschwartz * ontrap protection and usually come in first. Fabric errors can come in
501064Sschwartz * later.
511064Sschwartz *
521064Sschwartz * px_phys_peek_4u() disables interrupts. Interrupts are reenabled at the end
531064Sschwartz * of that function if no errors have been caught by the trap handler, or by
541064Sschwartz * peek_fault() which executes when a fire error occurs.
551064Sschwartz *
561064Sschwartz * Fabric error messages get put on an event queue but are not processed until
571064Sschwartz * interrupts are reenabled.
581064Sschwartz *
591064Sschwartz * The delay gives time for the fabric errors to be processed by FMA before
601064Sschwartz * changing the fm error flag back to DDI_FM_ERR_UNEXPECTED. If this isn't
611064Sschwartz * done, then the fabric error which should be safe can panic the system.
621064Sschwartz *
63624Sschwartz * Note: this is a workaround until a better solution is found. While this
64624Sschwartz * number is high, given enough bridges and switches in the device path, this
65624Sschwartz * workaround can break. Also, other PIL 15 interrupts besides the ones we are
66624Sschwartz * enveloping could delay processing of the interrupt we are trying to protect.
67624Sschwartz */
68*5132Srameshc
69*5132Srameshc /*
70*5132Srameshc * Set delay to 10 ms
71*5132Srameshc */
72*5132Srameshc int pxtool_delay_usec = 10000;
73624Sschwartz
74624Sschwartz /* Number of inos per root complex. */
75624Sschwartz int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
76624Sschwartz
77624Sschwartz /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
78624Sschwartz typedef union {
79624Sschwartz uint64_t u64;
80624Sschwartz uint32_t u32;
81624Sschwartz uint16_t u16;
82624Sschwartz uint8_t u8;
83624Sschwartz } peek_poke_value_t;
84624Sschwartz
85624Sschwartz /*
86624Sschwartz * Safe C wrapper around assy language routine px_phys_peek_4u
87624Sschwartz *
88624Sschwartz * Type is TRUE for big endian, FALSE for little endian.
89624Sschwartz * Size is 1, 2, 4 or 8 bytes.
90624Sschwartz * paddr is the physical address in IO space to access read.
91624Sschwartz * value_p is where the value is returned.
92624Sschwartz */
93624Sschwartz static int
pxtool_safe_phys_peek(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)94624Sschwartz pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
95624Sschwartz uint64_t *value_p)
96624Sschwartz {
97624Sschwartz px_pec_t *pec_p = px_p->px_pec_p;
982276Sschwartz pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
99624Sschwartz on_trap_data_t otd;
100624Sschwartz peek_poke_value_t peek_value;
101624Sschwartz int err = DDI_SUCCESS;
102624Sschwartz
103624Sschwartz mutex_enter(&pec_p->pec_pokefault_mutex);
104624Sschwartz pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
105624Sschwartz
1062276Sschwartz pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
1072276Sschwartz
108624Sschwartz /*
109624Sschwartz * Set up trap handling to make the access safe.
110624Sschwartz *
111624Sschwartz * on_trap works like setjmp.
112624Sschwartz * Set it up to not panic on data access error,
113624Sschwartz * but to call peek_fault instead.
114624Sschwartz * Call px_phys_peek_4u after trap handling is setup.
115624Sschwartz * When on_trap returns FALSE, it has been setup.
116624Sschwartz * When it returns TRUE, an it has caught an error.
117624Sschwartz */
118624Sschwartz if (!on_trap(&otd, OT_DATA_ACCESS)) {
119624Sschwartz otd.ot_trampoline = (uintptr_t)&peek_fault;
120624Sschwartz err = px_phys_peek_4u(size, paddr, &peek_value.u64, type);
121624Sschwartz } else
122624Sschwartz err = DDI_FAILURE;
123624Sschwartz
1241064Sschwartz no_trap();
1251064Sschwartz
126624Sschwartz /*
127624Sschwartz * Workaround: delay taking down safe access env.
128*5132Srameshc * For more info, see comments where pxtool_delay_usec is declared.
129624Sschwartz */
130*5132Srameshc if ((err == DDI_FAILURE) && (pxtool_delay_usec > 0))
131*5132Srameshc delay(drv_usectohz(pxtool_delay_usec));
132624Sschwartz
133624Sschwartz pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
1342276Sschwartz pxu_p->pcitool_addr = NULL;
135624Sschwartz mutex_exit(&pec_p->pec_pokefault_mutex);
136624Sschwartz
137624Sschwartz if (err != DDI_FAILURE) {
138624Sschwartz switch (size) {
139624Sschwartz case 8:
140624Sschwartz *value_p = peek_value.u64;
141624Sschwartz break;
142624Sschwartz case 4:
143624Sschwartz *value_p = (uint64_t)peek_value.u32;
144624Sschwartz break;
145624Sschwartz case 2:
146624Sschwartz *value_p = (uint64_t)peek_value.u16;
147624Sschwartz break;
148624Sschwartz case 1:
149624Sschwartz *value_p = (uint64_t)peek_value.u8;
150624Sschwartz break;
151624Sschwartz default:
152624Sschwartz err = DDI_FAILURE;
153624Sschwartz }
154624Sschwartz }
155624Sschwartz
156624Sschwartz return (err);
157624Sschwartz }
158624Sschwartz
159624Sschwartz /*
160624Sschwartz * Safe C wrapper around assy language routine px_phys_poke_4u
161624Sschwartz *
162624Sschwartz * Type is TRUE for big endian, FALSE for little endian.
163624Sschwartz * Size is 1,2,4 or 8 bytes.
164624Sschwartz * paddr is the physical address in IO space to access read.
165624Sschwartz * value contains the value to be written.
166624Sschwartz */
167624Sschwartz static int
pxtool_safe_phys_poke(px_t * px_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)168624Sschwartz pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
169624Sschwartz uint64_t value)
170624Sschwartz {
171624Sschwartz on_trap_data_t otd;
1722276Sschwartz pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
173624Sschwartz px_pec_t *pec_p = px_p->px_pec_p;
174624Sschwartz peek_poke_value_t poke_value;
175624Sschwartz int err = DDI_SUCCESS;
176624Sschwartz
177624Sschwartz switch (size) {
178624Sschwartz case 8:
179624Sschwartz poke_value.u64 = value;
180624Sschwartz break;
181624Sschwartz case 4:
182624Sschwartz poke_value.u32 = (uint32_t)value;
183624Sschwartz break;
184624Sschwartz case 2:
185624Sschwartz poke_value.u16 = (uint16_t)value;
186624Sschwartz break;
187624Sschwartz case 1:
188624Sschwartz poke_value.u8 = (uint8_t)value;
189624Sschwartz break;
190624Sschwartz default:
191624Sschwartz return (DDI_FAILURE);
192624Sschwartz }
193624Sschwartz
194624Sschwartz mutex_enter(&pec_p->pec_pokefault_mutex);
195624Sschwartz pec_p->pec_ontrap_data = &otd;
196624Sschwartz pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
1972276Sschwartz pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
198624Sschwartz
199624Sschwartz /*
200624Sschwartz * on_trap works like setjmp.
201624Sschwartz * Set it up to not panic on data access error,
202624Sschwartz * but to call poke_fault instead.
203624Sschwartz * Call px_phys_poke_4u after trap handling is setup.
204624Sschwartz * When on_trap returns FALSE, it has been setup.
205624Sschwartz * When it returns TRUE, an it has caught an error.
206624Sschwartz */
207624Sschwartz if (!on_trap(&otd, OT_DATA_ACCESS)) {
208624Sschwartz
209624Sschwartz otd.ot_trampoline = (uintptr_t)&poke_fault;
210624Sschwartz err = px_phys_poke_4u(size, paddr, &poke_value.u64, type);
211624Sschwartz } else
212624Sschwartz err = DDI_FAILURE;
213624Sschwartz
2143274Set142600 px_lib_clr_errs(px_p, 0, paddr);
215624Sschwartz
216624Sschwartz if (otd.ot_trap & OT_DATA_ACCESS)
217624Sschwartz err = DDI_FAILURE;
218624Sschwartz
219624Sschwartz /* Take down protected environment. */
220624Sschwartz no_trap();
2211064Sschwartz pec_p->pec_ontrap_data = NULL;
222624Sschwartz
2231064Sschwartz /*
2241064Sschwartz * Workaround: delay taking down safe access env.
225*5132Srameshc * For more info, see comments where pxtool_delay_usec is declared.
2261064Sschwartz */
227*5132Srameshc if (pxtool_delay_usec > 0)
228*5132Srameshc delay(drv_usectohz(pxtool_delay_usec));
2291064Sschwartz
230624Sschwartz pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
2312276Sschwartz pxu_p->pcitool_addr = NULL;
232624Sschwartz mutex_exit(&pec_p->pec_pokefault_mutex);
233624Sschwartz
234624Sschwartz return (err);
235624Sschwartz }
236624Sschwartz
237624Sschwartz
238624Sschwartz /*
239624Sschwartz * Wrapper around pxtool_safe_phys_peek/poke.
240624Sschwartz *
241624Sschwartz * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately.
242624Sschwartz *
243624Sschwartz * Dip is of the nexus,
244624Sschwartz * phys_addr is the address to write in physical space.
245624Sschwartz * pcitool_status returns more detailed status in addition to a more generic
246624Sschwartz * errno-style function return value.
247624Sschwartz * other args are self-explanatory.
248624Sschwartz *
2491064Sschwartz * This function assumes that offset, bdf, and acc_attr are current in
250624Sschwartz * prg_p. It also assumes that prg_p->phys_addr is the final phys addr,
251624Sschwartz * including offset.
252624Sschwartz * This function modifies prg_p status and data.
253624Sschwartz */
254624Sschwartz /*ARGSUSED*/
255624Sschwartz static int
pxtool_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)2561064Sschwartz pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p,
2571064Sschwartz boolean_t is_write)
258624Sschwartz {
259624Sschwartz dev_info_t *dip = px_p->px_dip;
260624Sschwartz uint64_t phys_addr = prg_p->phys_addr;
261624Sschwartz boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr);
262624Sschwartz size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
263624Sschwartz int rval = SUCCESS;
264624Sschwartz
265624Sschwartz /* Alignment checking. Assumes base address is 8-byte aligned. */
2661064Sschwartz if (!IS_P2ALIGNED(phys_addr, size)) {
267624Sschwartz DBG(DBG_TOOLS, dip, "not aligned.\n");
268624Sschwartz prg_p->status = PCITOOL_NOT_ALIGNED;
269624Sschwartz
270624Sschwartz rval = EINVAL;
271624Sschwartz
272624Sschwartz } else if (is_write) { /* Made it through checks. Do the access. */
273624Sschwartz
274624Sschwartz DBG(DBG_PHYS_ACC, dip,
275624Sschwartz "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n",
276624Sschwartz size, (endian ? "BE" : "LE"), phys_addr);
277624Sschwartz
278624Sschwartz if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr,
279624Sschwartz *data_p) != DDI_SUCCESS) {
280624Sschwartz DBG(DBG_PHYS_ACC, dip,
281624Sschwartz "%d byte %s pxtool_safe_phys_poke at addr "
282624Sschwartz "0x%" PRIx64 " failed\n",
283624Sschwartz size, (endian ? "BE" : "LE"), phys_addr);
284624Sschwartz prg_p->status = PCITOOL_INVALID_ADDRESS;
285624Sschwartz
286624Sschwartz rval = EFAULT;
287624Sschwartz }
288624Sschwartz
289624Sschwartz } else { /* Read */
290624Sschwartz
291624Sschwartz DBG(DBG_PHYS_ACC, dip,
292624Sschwartz "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n",
293624Sschwartz size, (endian ? "BE" : "LE"), phys_addr);
294624Sschwartz
295624Sschwartz if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr,
296624Sschwartz data_p) != DDI_SUCCESS) {
297624Sschwartz DBG(DBG_PHYS_ACC, dip,
298624Sschwartz "%d byte %s pxtool_safe_phys_peek at addr "
299624Sschwartz "0x%" PRIx64 " failed\n",
300624Sschwartz size, (endian ? "BE" : "LE"), phys_addr);
301624Sschwartz prg_p->status = PCITOOL_INVALID_ADDRESS;
302624Sschwartz
303624Sschwartz rval = EFAULT;
304624Sschwartz }
305624Sschwartz }
306624Sschwartz return (rval);
307624Sschwartz }
308624Sschwartz
309624Sschwartz
310624Sschwartz int
pxtool_pcicfg_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)311624Sschwartz pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
3121064Sschwartz uint64_t *data_p, boolean_t is_write)
313624Sschwartz {
3141064Sschwartz return (pxtool_access(px_p, prg_p, data_p, is_write));
315624Sschwartz }
316624Sschwartz
317624Sschwartz int
pxtool_pciiomem_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)3181064Sschwartz pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
319624Sschwartz uint64_t *data_p, boolean_t is_write)
320624Sschwartz {
3211064Sschwartz return (pxtool_access(px_p, prg_p, data_p, is_write));
322624Sschwartz }
323624Sschwartz
324624Sschwartz int
pxtool_dev_reg_ops_platchk(dev_info_t * dip,pcitool_reg_t * prg_p)325624Sschwartz pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
326624Sschwartz {
327624Sschwartz /*
328624Sschwartz * Guard against checking a root nexus which is empty.
329624Sschwartz * On some systems this will result in a Fatal Reset.
330624Sschwartz */
3313720Sschwartz if (ddi_get_child(dip) == NULL) {
332624Sschwartz DBG(DBG_TOOLS, dip,
333624Sschwartz "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n");
334624Sschwartz prg_p->status = PCITOOL_IO_ERROR;
335624Sschwartz return (ENXIO);
336624Sschwartz }
337624Sschwartz
338624Sschwartz return (SUCCESS);
339624Sschwartz }
340624Sschwartz
341624Sschwartz /*
342624Sschwartz * Perform register accesses on the nexus device itself.
343624Sschwartz */
344624Sschwartz int
pxtool_bus_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)345624Sschwartz pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
346624Sschwartz {
347624Sschwartz pcitool_reg_t prg;
348624Sschwartz uint64_t base_addr;
349624Sschwartz uint32_t reglen;
350624Sschwartz px_t *px_p = DIP_TO_STATE(dip);
351624Sschwartz px_nexus_regspec_t *px_rp = NULL;
352624Sschwartz uint32_t numbanks = 0;
353624Sschwartz boolean_t write_flag = B_FALSE;
354624Sschwartz uint32_t rval = 0;
355624Sschwartz
356624Sschwartz if (cmd == PCITOOL_NEXUS_SET_REG)
357624Sschwartz write_flag = B_TRUE;
358624Sschwartz
359624Sschwartz DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
360624Sschwartz
361624Sschwartz /* Read data from userland. */
362624Sschwartz if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
363624Sschwartz DDI_SUCCESS) {
364624Sschwartz DBG(DBG_TOOLS, dip, "Error reading arguments\n");
365624Sschwartz return (EFAULT);
366624Sschwartz }
367624Sschwartz
368624Sschwartz /* Read reg property which contains starting addr and size of banks. */
369624Sschwartz if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
370624Sschwartz "reg", (int **)&px_rp, ®len) == DDI_SUCCESS) {
371624Sschwartz if (((reglen * sizeof (int)) %
372624Sschwartz sizeof (px_nexus_regspec_t)) != 0) {
373624Sschwartz DBG(DBG_TOOLS, dip, "reg prop not well-formed");
374624Sschwartz prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
375624Sschwartz rval = EIO;
376624Sschwartz goto done;
377624Sschwartz }
378624Sschwartz }
379624Sschwartz
380624Sschwartz numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
381624Sschwartz
382624Sschwartz /* Bounds check the bank number. */
383624Sschwartz if (prg.barnum >= numbanks) {
384624Sschwartz prg.status = PCITOOL_OUT_OF_RANGE;
385624Sschwartz rval = EINVAL;
386624Sschwartz goto done;
387624Sschwartz }
388624Sschwartz
389624Sschwartz base_addr = px_rp[prg.barnum].phys_addr;
390624Sschwartz prg.phys_addr = base_addr + prg.offset;
391624Sschwartz
392624Sschwartz DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", "
3931064Sschwartz "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:"
3941064Sschwartz "0x%" PRIx64 "\n",
3951064Sschwartz base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size);
3961064Sschwartz
3971064Sschwartz if (prg.offset >= px_rp[prg.barnum].size) {
3981064Sschwartz prg.status = PCITOOL_OUT_OF_RANGE;
3991064Sschwartz rval = EINVAL;
4001064Sschwartz goto done;
4011064Sschwartz }
402624Sschwartz
403624Sschwartz /* Access device. prg.status is modified. */
4041064Sschwartz rval = pxtool_access(px_p, &prg, &prg.data, write_flag);
405624Sschwartz
406624Sschwartz done:
407624Sschwartz if (px_rp != NULL)
408624Sschwartz ddi_prop_free(px_rp);
409624Sschwartz
4104397Sschwartz prg.drvr_version = PCITOOL_VERSION;
411624Sschwartz if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
412624Sschwartz mode) != DDI_SUCCESS) {
413624Sschwartz DBG(DBG_TOOLS, dip, "Copyout failed.\n");
414624Sschwartz return (EFAULT);
415624Sschwartz }
416624Sschwartz
417624Sschwartz return (rval);
418624Sschwartz }
419