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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/sysmacros.h>
27 #include <sys/machsystm.h>
28 #include <sys/cpuvar.h>
29 #include <sys/ddi_implfuncs.h>
30 #include <sys/hypervisor_api.h>
31 #include <sys/hsvc.h>
32 #include <px_obj.h>
33 #include <sys/pci_tools.h>
34 #include <sys/pci_cfgacc.h>
35 #include <px_tools_var.h>
36 #include "px_lib4v.h"
37 #include <px_tools_ext.h>
38
39 /*
40 * Delay needed to have a safe environment envelop any error which could
41 * surface. The larger the number of bridges and switches, the larger the
42 * number needed here.
43 *
44 * Note: this is a workaround until a better solution is found. While this
45 * number is high, given enough bridges and switches in the device path, this
46 * workaround can break. Also, other PIL 15 interrupts besides the ones we are
47 * enveloping could delay processing of the interrupt we are trying to protect.
48 */
49 int pxtool_cfg_delay_usec = 2500;
50 int pxtool_iomem_delay_usec = 25000;
51
52 /* Currently there is no way of getting this info from hypervisor. */
53 #define INTERRUPT_MAPPING_ENTRIES 64
54
55 /* Number of inos per root complex. */
56 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
57
58 /* Verify hypervisor version for DIAG functions ra2pa and hpriv. */
59 #define PXTOOL_HYP_VER_UNINIT 0
60 #define PXTOOL_HYP_VER_BAD 1
61 #define PXTOOL_HYP_VER_OK 2
62
63 static int pxtool_hyp_version = PXTOOL_HYP_VER_UNINIT;
64
65 /* Swap endianness. */
66 static uint64_t
pxtool_swap_endian(uint64_t data,int size)67 pxtool_swap_endian(uint64_t data, int size)
68 {
69 typedef union {
70 uint64_t data64;
71 uint8_t data8[8];
72 } data_split_t;
73
74 data_split_t orig_data;
75 data_split_t returned_data;
76 int i;
77
78 orig_data.data64 = data;
79 returned_data.data64 = 0;
80
81 for (i = 0; i < size; i++) {
82 returned_data.data8[7 - i] = orig_data.data8[8 - size + i];
83 }
84
85 return (returned_data.data64);
86 }
87
88 static void
pxtool_validate_diag_hyp_svc(dev_info_t * dip,int * diag_svc_status_p)89 pxtool_validate_diag_hyp_svc(dev_info_t *dip, int *diag_svc_status_p)
90 {
91 uint64_t pxtool_diag_maj_ver;
92 uint64_t pxtool_diag_min_ver;
93 int ret;
94
95 if (*diag_svc_status_p == PXTOOL_HYP_VER_UNINIT) {
96
97 *diag_svc_status_p = PXTOOL_HYP_VER_BAD;
98
99 /*
100 * Verify that hypervisor DIAG API has been
101 * negotiated (by unix).
102 */
103 if ((ret = hsvc_version(HSVC_GROUP_DIAG,
104 &pxtool_diag_maj_ver, &pxtool_diag_min_ver)) != 0) {
105 DBG(DBG_TOOLS, dip,
106 "diag hypervisor svc not negotiated: "
107 "grp:0x%lx, errno:%d\n", HSVC_GROUP_DIAG, ret);
108
109 } else if (pxtool_diag_maj_ver == 1) {
110 /*
111 * Major version 1 is OK.
112 *
113 * Code maintainers: if the version changes, check for
114 * API changes in hv_ra2pa() and hv_hpriv() before
115 * accepting the new version.
116 */
117 *diag_svc_status_p = PXTOOL_HYP_VER_OK;
118
119 } else {
120 DBG(DBG_TOOLS, dip,
121 "diag hypervisor svc: bad major number: "
122 "grp:0x%lx, maj:0x%lx, min:0x%lx\n",
123 HSVC_GROUP_DIAG, pxtool_diag_maj_ver,
124 pxtool_diag_min_ver);
125 }
126 }
127 }
128
129 static int
pxtool_phys_access(px_t * px_p,uintptr_t dev_addr,uint64_t * data_p,boolean_t is_big_endian,boolean_t is_write)130 pxtool_phys_access(px_t *px_p, uintptr_t dev_addr,
131 uint64_t *data_p, boolean_t is_big_endian, boolean_t is_write)
132 {
133 uint64_t rfunc, pfunc;
134 uint64_t rdata_addr, pdata_addr;
135 uint64_t to_addr, from_addr;
136 uint64_t local_data;
137 int rval;
138 dev_info_t *dip = px_p->px_dip;
139
140 DBG(DBG_TOOLS, dip,
141 "pxtool_phys_access: dev_addr:0x%" PRIx64 "\n", dev_addr);
142 DBG(DBG_TOOLS, dip, " data_addr:0x%" PRIx64 ", is_write:%s\n",
143 data_p, (is_write ? "yes" : "no"));
144
145 if (pxtool_hyp_version != PXTOOL_HYP_VER_OK) {
146 pxtool_validate_diag_hyp_svc(dip, &pxtool_hyp_version);
147 if (pxtool_hyp_version != PXTOOL_HYP_VER_OK) {
148 DBG(DBG_TOOLS, dip, "Couldn't validate diag hyp svc\n");
149 return (EPERM);
150 }
151 }
152
153 if ((rfunc = va_to_pa((void *)px_phys_acc_4v)) == (uint64_t)-1) {
154 DBG(DBG_TOOLS, dip, "Error getting real addr for function\n");
155 return (EIO);
156 }
157
158 if ((pfunc = hv_ra2pa(rfunc)) == -1) {
159 DBG(DBG_TOOLS, dip, "Error getting phys addr for function\n");
160 return (EIO);
161 }
162
163 if ((rdata_addr = va_to_pa((void *)&local_data)) == (uint64_t)-1) {
164 DBG(DBG_TOOLS, dip, "Error getting real addr for data_p\n");
165 return (EIO);
166 }
167
168 if ((pdata_addr = hv_ra2pa(rdata_addr)) == -1) {
169 DBG(DBG_TOOLS, dip, "Error getting phys addr for data ptr\n");
170 return (EIO);
171 }
172
173 if (is_write) {
174 to_addr = dev_addr;
175 from_addr = pdata_addr;
176
177 if (is_big_endian)
178 local_data = *data_p;
179 else
180 local_data =
181 pxtool_swap_endian(*data_p, sizeof (uint64_t));
182 } else {
183 to_addr = pdata_addr;
184 from_addr = dev_addr;
185 }
186
187 rval = hv_hpriv((void *)pfunc, from_addr, to_addr, 0);
188 switch (rval) {
189 case H_ENOACCESS: /* Returned by non-debug hypervisor. */
190 rval = ENOTSUP;
191 break;
192 case H_EOK:
193 rval = SUCCESS;
194 break;
195 default:
196 rval = EIO;
197 break;
198 }
199
200 if ((rval == SUCCESS) && (!is_write)) {
201 if (is_big_endian)
202 *data_p = local_data;
203 else
204 *data_p =
205 pxtool_swap_endian(local_data, sizeof (uint64_t));
206 }
207
208 return (rval);
209 }
210
211 /*
212 * This function is for PCI config space access.
213 * It assumes that offset, bdf, acc_attr are valid in prg_p.
214 * This function modifies prg_p status and data.
215 *
216 * prg_p->phys_addr isn't used.
217 */
218
219 int
pxtool_pcicfg_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)220 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
221 uint64_t *data_p, boolean_t is_write)
222 {
223 pci_cfg_data_t data;
224 on_trap_data_t otd;
225 dev_info_t *dip = px_p->px_dip;
226 px_pec_t *pec_p = px_p->px_pec_p;
227 size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
228 int rval = 0;
229 pci_cfgacc_req_t req;
230
231 if ((size <= 0) || (size > 8)) {
232 DBG(DBG_TOOLS, dip, "not supported size.\n");
233 prg_p->status = PCITOOL_INVALID_SIZE;
234 return (ENOTSUP);
235 }
236
237 /* Alignment checking. */
238 if (!IS_P2ALIGNED(prg_p->offset, size)) {
239 DBG(DBG_TOOLS, dip, "not aligned.\n");
240 prg_p->status = PCITOOL_NOT_ALIGNED;
241 return (EINVAL);
242 }
243
244 mutex_enter(&pec_p->pec_pokefault_mutex);
245 pec_p->pec_ontrap_data = &otd;
246
247 req.rcdip = dip;
248 req.bdf = PCI_GETBDF(prg_p->bus_no, prg_p->dev_no, prg_p->func_no);
249 req.offset = prg_p->offset;
250 req.size = size;
251 req.write = is_write;
252 if (is_write) {
253
254 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
255 data.qw = pxtool_swap_endian(*data_p, size);
256 else
257 data.qw = *data_p;
258
259 switch (size) {
260 case sizeof (uint8_t):
261 data.b = (uint8_t)data.qw;
262 break;
263 case sizeof (uint16_t):
264 data.w = (uint16_t)data.qw;
265 break;
266 case sizeof (uint32_t):
267 data.dw = (uint32_t)data.qw;
268 break;
269 case sizeof (uint64_t):
270 break;
271 }
272
273 DBG(DBG_TOOLS, dip, "put: bdf:%d,%d,%d, off:0x%"PRIx64", size:"
274 "0x%"PRIx64", data:0x%"PRIx64"\n",
275 prg_p->bus_no, prg_p->dev_no, prg_p->func_no,
276 prg_p->offset, size, data.qw);
277
278 pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
279
280 if (!on_trap(&otd, OT_DATA_ACCESS)) {
281 otd.ot_trampoline = (uintptr_t)&poke_fault;
282 VAL64(&req) = data.qw;
283 pci_cfgacc_acc(&req);
284 } else
285 rval = H_EIO;
286
287 if (otd.ot_trap & OT_DATA_ACCESS)
288 rval = H_EIO;
289
290 } else {
291
292 data.qw = 0;
293
294 pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
295
296 if (!on_trap(&otd, OT_DATA_ACCESS)) {
297 otd.ot_trampoline = (uintptr_t)&peek_fault;
298 pci_cfgacc_acc(&req);
299 data.qw = VAL64(&req);
300 } else
301 rval = H_EIO;
302
303 switch (size) {
304 case sizeof (uint8_t):
305 data.qw = (uint64_t)data.b;
306 break;
307 case sizeof (uint16_t):
308 data.qw = (uint64_t)data.w;
309 break;
310 case sizeof (uint32_t):
311 data.qw = (uint64_t)data.dw;
312 break;
313 case sizeof (uint64_t):
314 break;
315 }
316
317 DBG(DBG_TOOLS, dip, "get: bdf:%d,%d,%d, off:0x%"PRIx64", size:"
318 "0x%"PRIx64", data:0x%"PRIx64"\n",
319 prg_p->bus_no, prg_p->dev_no, prg_p->func_no,
320 prg_p->offset, size, data.qw);
321 *data_p = data.qw;
322
323 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
324 *data_p = pxtool_swap_endian(*data_p, size);
325 }
326
327 /*
328 * Workaround: delay taking down safe access env.
329 * For more info, see comments where pxtool_cfg_delay_usec is declared.
330 */
331 if (pxtool_cfg_delay_usec > 0)
332 drv_usecwait(pxtool_cfg_delay_usec);
333
334 no_trap();
335 pec_p->pec_ontrap_data = NULL;
336 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
337 mutex_exit(&pec_p->pec_pokefault_mutex);
338
339 if (rval != SUCCESS) {
340 prg_p->status = PCITOOL_INVALID_ADDRESS;
341 rval = EINVAL;
342 } else
343 prg_p->status = PCITOOL_SUCCESS;
344
345 return (rval);
346 }
347
348
349 /*
350 * This function is for PCI IO space and memory space access.
351 * It assumes that offset, bdf, acc_attr are current in prg_p.
352 * It assumes that prg_p->phys_addr is the final phys addr (including offset).
353 * This function modifies prg_p status and data.
354 */
355 int
pxtool_pciiomem_access(px_t * px_p,pcitool_reg_t * prg_p,uint64_t * data_p,boolean_t is_write)356 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
357 uint64_t *data_p, boolean_t is_write)
358 {
359 on_trap_data_t otd;
360 uint32_t io_stat = 0;
361 dev_info_t *dip = px_p->px_dip;
362 px_pec_t *pec_p = px_p->px_pec_p;
363 size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
364 int rval = 0;
365
366 /* Alignment checking. */
367 if (!IS_P2ALIGNED(prg_p->offset, size)) {
368 DBG(DBG_TOOLS, dip, "not aligned.\n");
369 prg_p->status = PCITOOL_NOT_ALIGNED;
370 return (EINVAL);
371 }
372
373 mutex_enter(&pec_p->pec_pokefault_mutex);
374 pec_p->pec_ontrap_data = &otd;
375
376 if (is_write) {
377 pci_device_t bdf = PX_GET_BDF(prg_p);
378
379 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
380 *data_p = pxtool_swap_endian(*data_p, size);
381
382 pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
383
384 if (!on_trap(&otd, OT_DATA_ACCESS)) {
385 otd.ot_trampoline = (uintptr_t)&poke_fault;
386 rval = hvio_poke(px_p->px_dev_hdl, prg_p->phys_addr,
387 size, *data_p, bdf, &io_stat);
388 } else
389 rval = H_EIO;
390
391 if (otd.ot_trap & OT_DATA_ACCESS)
392 rval = H_EIO;
393
394 DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", bdf:0x%x, "
395 "rval:%d, io_stat:%d\n", prg_p->phys_addr, bdf,
396 rval, io_stat);
397 } else {
398
399 *data_p = 0;
400
401 pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
402
403 if (!on_trap(&otd, OT_DATA_ACCESS)) {
404 otd.ot_trampoline = (uintptr_t)&peek_fault;
405 rval = hvio_peek(px_p->px_dev_hdl, prg_p->phys_addr,
406 size, &io_stat, data_p);
407 } else
408 rval = H_EIO;
409
410 DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", "
411 "size:0x%" PRIx64 ", hdl:0x%" PRIx64 ", "
412 "rval:%d, io_stat:%d\n", prg_p->phys_addr,
413 size, px_p->px_dev_hdl, rval, io_stat);
414 DBG(DBG_TOOLS, dip, "read data:0x%" PRIx64 "\n", *data_p);
415
416 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
417 *data_p = pxtool_swap_endian(*data_p, size);
418 }
419
420 /*
421 * Workaround: delay taking down safe access env.
422 * For more info, see comment where pxtool_iomem_delay_usec is declared.
423 */
424 if (pxtool_iomem_delay_usec > 0)
425 delay(drv_usectohz(pxtool_iomem_delay_usec));
426
427 no_trap();
428 pec_p->pec_ontrap_data = NULL;
429 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
430 mutex_exit(&pec_p->pec_pokefault_mutex);
431
432 if (rval != SUCCESS) {
433 prg_p->status = PCITOOL_INVALID_ADDRESS;
434 rval = EINVAL;
435 } else if (io_stat != SUCCESS) {
436 prg_p->status = PCITOOL_IO_ERROR;
437 rval = EIO;
438 } else
439 prg_p->status = PCITOOL_SUCCESS;
440
441 return (rval);
442 }
443
444
445 /*ARGSUSED*/
446 int
pxtool_dev_reg_ops_platchk(dev_info_t * dip,pcitool_reg_t * prg_p)447 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
448 {
449 return (SUCCESS);
450 }
451
452
453 /*
454 * Perform register accesses on the nexus device itself.
455 */
456 int
pxtool_bus_reg_ops(dev_info_t * dip,void * arg,int cmd,int mode)457 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
458 {
459
460 pcitool_reg_t prg;
461 size_t size;
462 px_t *px_p = DIP_TO_STATE(dip);
463 boolean_t is_write = B_FALSE;
464 uint32_t rval = 0;
465
466 if (cmd == PCITOOL_NEXUS_SET_REG)
467 is_write = B_TRUE;
468
469 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
470
471 /* Read data from userland. */
472 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
473 mode) != DDI_SUCCESS) {
474 DBG(DBG_TOOLS, dip, "Error reading arguments\n");
475 return (EFAULT);
476 }
477
478 size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
479
480 DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
481 prg.bus_no, prg.dev_no, prg.func_no);
482 DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
483 prg.barnum, prg.offset, prg.acc_attr);
484 DBG(DBG_TOOLS, dip, "data:0x%" PRIx64 ", phys_addr:0x%" PRIx64 "\n",
485 prg.data, prg.phys_addr);
486
487 /*
488 * If bank num == ff, base phys addr passed in from userland.
489 *
490 * Normal bank specification is invalid, as there is no OBP property to
491 * back it up.
492 */
493 if (prg.barnum != PCITOOL_BASE) {
494 prg.status = PCITOOL_OUT_OF_RANGE;
495 rval = EINVAL;
496 goto done;
497 }
498
499 /* Allow only size of 8-bytes. */
500 if (size != sizeof (uint64_t)) {
501 prg.status = PCITOOL_INVALID_SIZE;
502 rval = EINVAL;
503 goto done;
504 }
505
506 /* Alignment checking. */
507 if (!IS_P2ALIGNED(prg.offset, size)) {
508 DBG(DBG_TOOLS, dip, "not aligned.\n");
509 prg.status = PCITOOL_NOT_ALIGNED;
510 rval = EINVAL;
511 goto done;
512 }
513
514 prg.phys_addr += prg.offset;
515
516 /*
517 * Only the hypervisor can access nexus registers. As a result, there
518 * can be no error recovery in the OS. If there is an error, the
519 * system will go down, but with a trap type 7f. The OS cannot
520 * intervene with this kind of trap.
521 */
522
523 /* Access device. prg.status is modified. */
524 rval = pxtool_phys_access(px_p, prg.phys_addr, &prg.data,
525 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), is_write);
526 done:
527 prg.drvr_version = PCITOOL_VERSION;
528 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
529 mode) != DDI_SUCCESS) {
530 DBG(DBG_TOOLS, dip, "Copyout failed.\n");
531 return (EFAULT);
532 }
533
534 return (rval);
535 }
536