Print this page
5255 uts shouldn't open-code ISP2
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/i86pc/io/pci/pci_tools.c
+++ new/usr/src/uts/i86pc/io/pci/pci_tools.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
↓ open down ↓ |
14 lines elided |
↑ open up ↑ |
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 23 */
24 24
25 +#include <sys/sysmacros.h>
25 26 #include <sys/types.h>
26 27 #include <sys/mkdev.h>
27 28 #include <sys/stat.h>
28 29 #include <sys/sunddi.h>
29 30 #include <vm/seg_kmem.h>
30 31 #include <sys/machparam.h>
31 32 #include <sys/sunndi.h>
32 33 #include <sys/ontrap.h>
33 34 #include <sys/psm.h>
34 35 #include <sys/pcie.h>
35 36 #include <sys/pci_cfgspace.h>
36 37 #include <sys/pci_tools.h>
37 38 #include <io/pci/pci_tools_ext.h>
38 39 #include <sys/apic.h>
39 40 #include <sys/apix.h>
40 41 #include <io/pci/pci_var.h>
41 42 #include <sys/pci_impl.h>
42 43 #include <sys/promif.h>
43 44 #include <sys/x86_archext.h>
44 45 #include <sys/cpuvar.h>
45 46 #include <sys/pci_cfgacc.h>
46 47
47 48 #ifdef __xpv
48 49 #include <sys/hypervisor.h>
49 50 #endif
50 51
51 52 #define PCIEX_BDF_OFFSET_DELTA 4
52 53 #define PCIEX_REG_FUNC_SHIFT (PCI_REG_FUNC_SHIFT + PCIEX_BDF_OFFSET_DELTA)
53 54 #define PCIEX_REG_DEV_SHIFT (PCI_REG_DEV_SHIFT + PCIEX_BDF_OFFSET_DELTA)
54 55 #define PCIEX_REG_BUS_SHIFT (PCI_REG_BUS_SHIFT + PCIEX_BDF_OFFSET_DELTA)
55 56
56 57 #define SUCCESS 0
57 58
58 59 extern uint64_t mcfg_mem_base;
59 60 int pcitool_debug = 0;
60 61
61 62 /*
62 63 * Offsets of BARS in config space. First entry of 0 means config space.
63 64 * Entries here correlate to pcitool_bars_t enumerated type.
64 65 */
65 66 static uint8_t pci_bars[] = {
66 67 0x0,
67 68 PCI_CONF_BASE0,
68 69 PCI_CONF_BASE1,
69 70 PCI_CONF_BASE2,
70 71 PCI_CONF_BASE3,
71 72 PCI_CONF_BASE4,
72 73 PCI_CONF_BASE5,
73 74 PCI_CONF_ROM
74 75 };
75 76
76 77 /* Max offset allowed into config space for a particular device. */
77 78 static uint64_t max_cfg_size = PCI_CONF_HDR_SIZE;
78 79
79 80 static uint64_t pcitool_swap_endian(uint64_t data, int size);
80 81 static int pcitool_cfg_access(pcitool_reg_t *prg, boolean_t write_flag,
81 82 boolean_t io_access);
82 83 static int pcitool_io_access(pcitool_reg_t *prg, boolean_t write_flag);
83 84 static int pcitool_mem_access(pcitool_reg_t *prg, uint64_t virt_addr,
84 85 boolean_t write_flag);
85 86 static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages);
86 87 static void pcitool_unmap(uint64_t virt_addr, size_t num_pages);
87 88
88 89 /* Extern declarations */
89 90 extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
90 91 psm_intr_op_t, int *);
91 92
92 93 int
93 94 pcitool_init(dev_info_t *dip, boolean_t is_pciex)
94 95 {
95 96 int instance = ddi_get_instance(dip);
96 97
97 98 /* Create pcitool nodes for register access and interrupt routing. */
98 99
99 100 if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
100 101 PCI_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
101 102 DDI_NT_REGACC, 0) != DDI_SUCCESS) {
102 103 return (DDI_FAILURE);
103 104 }
104 105
105 106 if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
106 107 PCI_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
107 108 DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
108 109 ddi_remove_minor_node(dip, PCI_MINOR_REG);
109 110 return (DDI_FAILURE);
110 111 }
111 112
112 113 if (is_pciex)
113 114 max_cfg_size = PCIE_CONF_HDR_SIZE;
114 115
115 116 return (DDI_SUCCESS);
116 117 }
117 118
118 119 void
119 120 pcitool_uninit(dev_info_t *dip)
120 121 {
121 122 ddi_remove_minor_node(dip, PCI_MINOR_INTR);
122 123 ddi_remove_minor_node(dip, PCI_MINOR_REG);
123 124 }
124 125
125 126 /*ARGSUSED*/
126 127 static int
127 128 pcitool_set_intr(dev_info_t *dip, void *arg, int mode)
128 129 {
129 130 ddi_intr_handle_impl_t info_hdl;
130 131 pcitool_intr_set_t iset;
131 132 uint32_t old_cpu;
132 133 int ret, result;
133 134 size_t copyinout_size;
134 135 int rval = SUCCESS;
135 136 apic_get_type_t type_info;
136 137
137 138 /* Version 1 of pcitool_intr_set_t doesn't have flags. */
138 139 copyinout_size = (size_t)&iset.flags - (size_t)&iset;
139 140
140 141 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
141 142 return (EFAULT);
142 143
143 144 switch (iset.user_version) {
144 145 case PCITOOL_V1:
145 146 break;
146 147
147 148 case PCITOOL_V2:
148 149 copyinout_size = sizeof (pcitool_intr_set_t);
149 150 if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
150 151 return (EFAULT);
151 152 break;
152 153
153 154 default:
154 155 iset.status = PCITOOL_OUT_OF_RANGE;
155 156 rval = ENOTSUP;
156 157 goto done_set_intr;
157 158 }
158 159
159 160 if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
160 161 rval = ENOTSUP;
161 162 iset.status = PCITOOL_IO_ERROR;
162 163 goto done_set_intr;
163 164 }
164 165
165 166 info_hdl.ih_private = &type_info;
166 167
167 168 if ((*psm_intr_ops)(NULL, &info_hdl,
168 169 PSM_INTR_OP_APIC_TYPE, NULL) != PSM_SUCCESS) {
169 170 rval = ENOTSUP;
170 171 iset.status = PCITOOL_IO_ERROR;
171 172 goto done_set_intr;
172 173 }
173 174
174 175 if (strcmp(type_info.avgi_type, APIC_APIX_NAME) == 0) {
175 176 if (iset.old_cpu > type_info.avgi_num_cpu) {
176 177 rval = EINVAL;
177 178 iset.status = PCITOOL_INVALID_CPUID;
178 179 goto done_set_intr;
179 180 }
180 181 old_cpu = iset.old_cpu;
181 182 } else {
182 183 if ((old_cpu =
183 184 pci_get_cpu_from_vecirq(iset.ino, IS_VEC)) == -1) {
184 185 iset.status = PCITOOL_IO_ERROR;
185 186 rval = EINVAL;
186 187 goto done_set_intr;
187 188 }
188 189 }
189 190
190 191 if (iset.ino > type_info.avgi_num_intr) {
191 192 rval = EINVAL;
192 193 iset.status = PCITOOL_INVALID_INO;
193 194 goto done_set_intr;
194 195 }
195 196
196 197 iset.status = PCITOOL_SUCCESS;
197 198
198 199 old_cpu &= ~PSMGI_CPU_USER_BOUND;
199 200
200 201 /*
201 202 * For this locally-declared and used handle, ih_private will contain a
202 203 * CPU value, not an ihdl_plat_t as used for global interrupt handling.
203 204 */
204 205 if (strcmp(type_info.avgi_type, APIC_APIX_NAME) == 0) {
205 206 info_hdl.ih_vector = APIX_VIRTVECTOR(old_cpu, iset.ino);
206 207 } else {
207 208 info_hdl.ih_vector = iset.ino;
208 209 }
209 210 info_hdl.ih_private = (void *)(uintptr_t)iset.cpu_id;
210 211 info_hdl.ih_flags = PSMGI_INTRBY_VEC;
211 212 if (pcitool_debug)
212 213 prom_printf("user version:%d, flags:0x%x\n",
213 214 iset.user_version, iset.flags);
214 215
215 216 result = ENOTSUP;
216 217 if ((iset.user_version >= PCITOOL_V2) &&
217 218 (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP)) {
218 219 ret = (*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_GRP_SET_CPU,
219 220 &result);
220 221 } else {
221 222 ret = (*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_SET_CPU,
222 223 &result);
223 224 }
224 225
225 226 if (ret != PSM_SUCCESS) {
226 227 switch (result) {
227 228 case EIO: /* Error making the change */
228 229 rval = EIO;
229 230 iset.status = PCITOOL_IO_ERROR;
230 231 break;
231 232 case ENXIO: /* Couldn't convert vector to irq */
232 233 rval = EINVAL;
233 234 iset.status = PCITOOL_INVALID_INO;
234 235 break;
235 236 case EINVAL: /* CPU out of range */
236 237 rval = EINVAL;
237 238 iset.status = PCITOOL_INVALID_CPUID;
238 239 break;
239 240 case ENOTSUP: /* Requested PSM intr ops missing */
240 241 rval = ENOTSUP;
241 242 iset.status = PCITOOL_IO_ERROR;
242 243 break;
243 244 }
244 245 }
245 246
246 247 /* Return original CPU. */
247 248 iset.cpu_id = old_cpu;
248 249
249 250 /* Return new vector */
250 251 if (strcmp(type_info.avgi_type, APIC_APIX_NAME) == 0) {
251 252 iset.ino = APIX_VIRTVEC_VECTOR(info_hdl.ih_vector);
252 253 }
253 254
254 255 done_set_intr:
255 256 iset.drvr_version = PCITOOL_VERSION;
256 257 if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
257 258 rval = EFAULT;
258 259 return (rval);
259 260 }
260 261
261 262
262 263 /* It is assumed that dip != NULL */
263 264 static void
264 265 pcitool_get_intr_dev_info(dev_info_t *dip, pcitool_intr_dev_t *devs)
265 266 {
266 267 (void) strncpy(devs->driver_name,
267 268 ddi_driver_name(dip), MAXMODCONFNAME-2);
268 269 devs->driver_name[MAXMODCONFNAME-1] = '\0';
269 270 (void) ddi_pathname(dip, devs->path);
270 271 devs->dev_inst = ddi_get_instance(dip);
271 272 }
272 273
273 274 static int
274 275 pcitool_get_intr(dev_info_t *dip, void *arg, int mode)
275 276 {
276 277 /* Array part isn't used here, but oh well... */
277 278 pcitool_intr_get_t partial_iget;
278 279 pcitool_intr_get_t *iget = &partial_iget;
279 280 size_t iget_kmem_alloc_size = 0;
280 281 uint8_t num_devs_ret;
281 282 int copyout_rval;
282 283 int rval = SUCCESS;
283 284 int circ;
284 285 int i;
285 286
286 287 ddi_intr_handle_impl_t info_hdl;
287 288 apic_get_intr_t intr_info;
288 289 apic_get_type_t type_info;
289 290
290 291 /* Read in just the header part, no array section. */
291 292 if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
292 293 DDI_SUCCESS)
293 294 return (EFAULT);
294 295
295 296 if (partial_iget.flags & PCITOOL_INTR_FLAG_GET_MSI) {
296 297 partial_iget.status = PCITOOL_IO_ERROR;
297 298 partial_iget.num_devs_ret = 0;
298 299 rval = ENOTSUP;
299 300 goto done_get_intr;
300 301 }
301 302
302 303 info_hdl.ih_private = &type_info;
303 304
304 305 if ((*psm_intr_ops)(NULL, &info_hdl,
305 306 PSM_INTR_OP_APIC_TYPE, NULL) != PSM_SUCCESS) {
306 307 iget->status = PCITOOL_IO_ERROR;
307 308 iget->num_devs_ret = 0;
308 309 rval = EINVAL;
309 310 goto done_get_intr;
310 311 }
311 312
312 313 if (strcmp(type_info.avgi_type, APIC_APIX_NAME) == 0) {
313 314 if (partial_iget.cpu_id > type_info.avgi_num_cpu) {
314 315 partial_iget.status = PCITOOL_INVALID_CPUID;
315 316 partial_iget.num_devs_ret = 0;
316 317 rval = EINVAL;
317 318 goto done_get_intr;
318 319 }
319 320 }
320 321
321 322 /* Validate argument. */
322 323 if ((partial_iget.ino & APIX_VIRTVEC_VECMASK) >
323 324 type_info.avgi_num_intr) {
324 325 partial_iget.status = PCITOOL_INVALID_INO;
325 326 partial_iget.num_devs_ret = 0;
326 327 rval = EINVAL;
327 328 goto done_get_intr;
328 329 }
329 330
330 331 num_devs_ret = partial_iget.num_devs_ret;
331 332 intr_info.avgi_dip_list = NULL;
332 333 intr_info.avgi_req_flags =
333 334 PSMGI_REQ_CPUID | PSMGI_REQ_NUM_DEVS | PSMGI_INTRBY_VEC;
334 335 /*
335 336 * For this locally-declared and used handle, ih_private will contain a
336 337 * pointer to apic_get_intr_t, not an ihdl_plat_t as used for
337 338 * global interrupt handling.
338 339 */
339 340 info_hdl.ih_private = &intr_info;
340 341
341 342 if (strcmp(type_info.avgi_type, APIC_APIX_NAME) == 0) {
342 343 info_hdl.ih_vector =
343 344 APIX_VIRTVECTOR(partial_iget.cpu_id, partial_iget.ino);
344 345 } else {
345 346 info_hdl.ih_vector = partial_iget.ino;
346 347 }
347 348
348 349 /* Caller wants device information returned. */
349 350 if (num_devs_ret > 0) {
350 351
351 352 intr_info.avgi_req_flags |= PSMGI_REQ_GET_DEVS;
352 353
353 354 /*
354 355 * Allocate room.
355 356 * If num_devs_ret == 0 iget remains pointing to partial_iget.
356 357 */
357 358 iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
358 359 iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
359 360
360 361 /* Read in whole structure to verify there's room. */
361 362 if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
362 363 SUCCESS) {
363 364
364 365 /* Be consistent and just return EFAULT here. */
365 366 kmem_free(iget, iget_kmem_alloc_size);
366 367
367 368 return (EFAULT);
368 369 }
369 370 }
370 371
371 372 bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
372 373 iget->ino = info_hdl.ih_vector;
373 374
374 375 /*
375 376 * Lock device tree branch from the pci root nexus on down if info will
376 377 * be extracted from dips returned from the tree.
377 378 */
378 379 if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) {
379 380 ndi_devi_enter(dip, &circ);
380 381 }
381 382
382 383 /* Call psm_intr_ops(PSM_INTR_OP_GET_INTR) to get information. */
383 384 if ((rval = (*psm_intr_ops)(NULL, &info_hdl,
384 385 PSM_INTR_OP_GET_INTR, NULL)) != PSM_SUCCESS) {
385 386 iget->status = PCITOOL_IO_ERROR;
386 387 iget->num_devs_ret = 0;
387 388 rval = EINVAL;
388 389 goto done_get_intr;
389 390 }
390 391
391 392 /*
392 393 * Fill in the pcitool_intr_get_t to be returned,
393 394 * with the CPU, num_devs_ret and num_devs.
394 395 */
395 396 if (intr_info.avgi_cpu_id == IRQ_UNBOUND ||
396 397 intr_info.avgi_cpu_id == IRQ_UNINIT)
397 398 iget->cpu_id = 0;
398 399 else
399 400 iget->cpu_id = intr_info.avgi_cpu_id & ~PSMGI_CPU_USER_BOUND;
400 401
401 402 /* Number of devices returned by apic. */
402 403 iget->num_devs = intr_info.avgi_num_devs;
403 404
404 405 /* Device info was returned. */
405 406 if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) {
406 407
407 408 /*
408 409 * num devs returned is num devs ret by apic,
409 410 * space permitting.
410 411 */
411 412 iget->num_devs_ret = min(num_devs_ret, intr_info.avgi_num_devs);
412 413
413 414 /*
414 415 * Loop thru list of dips and extract driver, name and instance.
415 416 * Fill in the pcitool_intr_dev_t's with this info.
416 417 */
417 418 for (i = 0; i < iget->num_devs_ret; i++)
418 419 pcitool_get_intr_dev_info(intr_info.avgi_dip_list[i],
419 420 &iget->dev[i]);
420 421
421 422 /* Free kmem_alloc'ed memory of the apic_get_intr_t */
422 423 kmem_free(intr_info.avgi_dip_list,
423 424 intr_info.avgi_num_devs * sizeof (dev_info_t *));
424 425 }
425 426
426 427 done_get_intr:
427 428
428 429 if (intr_info.avgi_req_flags & PSMGI_REQ_GET_DEVS) {
429 430 ndi_devi_exit(dip, circ);
430 431 }
431 432
432 433 iget->drvr_version = PCITOOL_VERSION;
433 434 copyout_rval = ddi_copyout(iget, arg,
434 435 PCITOOL_IGET_SIZE(num_devs_ret), mode);
435 436
436 437 if (iget_kmem_alloc_size > 0)
437 438 kmem_free(iget, iget_kmem_alloc_size);
438 439
439 440 if (copyout_rval != DDI_SUCCESS)
440 441 rval = EFAULT;
441 442
442 443 return (rval);
443 444 }
444 445
445 446 /*ARGSUSED*/
446 447 static int
447 448 pcitool_intr_info(dev_info_t *dip, void *arg, int mode)
448 449 {
449 450 pcitool_intr_info_t intr_info;
450 451 ddi_intr_handle_impl_t info_hdl;
451 452 int rval = SUCCESS;
452 453 apic_get_type_t type_info;
453 454
454 455 /* If we need user_version, and to ret same user version as passed in */
455 456 if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
456 457 DDI_SUCCESS) {
457 458 if (pcitool_debug)
458 459 prom_printf("Error reading arguments\n");
459 460 return (EFAULT);
460 461 }
461 462
462 463 if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
463 464 return (ENOTSUP);
464 465
465 466 info_hdl.ih_private = &type_info;
466 467
467 468 /* For UPPC systems, psm_intr_ops has no entry for APIC_TYPE. */
468 469 if ((rval = (*psm_intr_ops)(NULL, &info_hdl,
469 470 PSM_INTR_OP_APIC_TYPE, NULL)) != PSM_SUCCESS) {
470 471 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_UPPC;
471 472 intr_info.ctlr_version = 0;
472 473 intr_info.num_intr = APIC_MAX_VECTOR;
473 474 } else {
474 475 intr_info.ctlr_version = (uint32_t)info_hdl.ih_ver;
475 476 intr_info.num_cpu = type_info.avgi_num_cpu;
476 477 if (strcmp(type_info.avgi_type,
477 478 APIC_PCPLUSMP_NAME) == 0) {
478 479 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_PCPLUSMP;
479 480 intr_info.num_intr = type_info.avgi_num_intr;
480 481 } else if (strcmp(type_info.avgi_type,
481 482 APIC_APIX_NAME) == 0) {
482 483 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_APIX;
483 484 intr_info.num_intr = type_info.avgi_num_intr;
484 485 } else {
485 486 intr_info.ctlr_type = PCITOOL_CTLR_TYPE_UNKNOWN;
486 487 intr_info.num_intr = APIC_MAX_VECTOR;
487 488 }
488 489 }
489 490
490 491 intr_info.drvr_version = PCITOOL_VERSION;
491 492 if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
492 493 DDI_SUCCESS) {
493 494 if (pcitool_debug)
494 495 prom_printf("Error returning arguments.\n");
495 496 rval = EFAULT;
496 497 }
497 498
498 499 return (rval);
499 500 }
500 501
501 502
502 503
503 504 /*
504 505 * Main function for handling interrupt CPU binding requests and queries.
505 506 * Need to implement later
506 507 */
507 508 int
508 509 pcitool_intr_admn(dev_info_t *dip, void *arg, int cmd, int mode)
509 510 {
510 511 int rval;
511 512
512 513 switch (cmd) {
513 514
514 515 /* Associate a new CPU with a given vector */
515 516 case PCITOOL_DEVICE_SET_INTR:
516 517 rval = pcitool_set_intr(dip, arg, mode);
517 518 break;
518 519
519 520 case PCITOOL_DEVICE_GET_INTR:
520 521 rval = pcitool_get_intr(dip, arg, mode);
521 522 break;
522 523
523 524 case PCITOOL_SYSTEM_INTR_INFO:
524 525 rval = pcitool_intr_info(dip, arg, mode);
525 526 break;
526 527
527 528 default:
528 529 rval = ENOTSUP;
529 530 }
530 531
531 532 return (rval);
532 533 }
533 534
534 535 /*
535 536 * Perform register accesses on the nexus device itself.
536 537 * No explicit PCI nexus device for X86, so not applicable.
537 538 */
538 539
539 540 /*ARGSUSED*/
540 541 int
541 542 pcitool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
542 543 {
543 544 return (ENOTSUP);
544 545 }
545 546
546 547 /* Swap endianness. */
547 548 static uint64_t
548 549 pcitool_swap_endian(uint64_t data, int size)
549 550 {
550 551 typedef union {
551 552 uint64_t data64;
552 553 uint8_t data8[8];
553 554 } data_split_t;
554 555
555 556 data_split_t orig_data;
556 557 data_split_t returned_data;
557 558 int i;
558 559
559 560 orig_data.data64 = data;
560 561 returned_data.data64 = 0;
561 562
562 563 for (i = 0; i < size; i++) {
563 564 returned_data.data8[i] = orig_data.data8[size - 1 - i];
564 565 }
565 566
566 567 return (returned_data.data64);
567 568 }
568 569
569 570 /*
570 571 * A note about ontrap handling:
571 572 *
572 573 * X86 systems on which this module was tested return FFs instead of bus errors
573 574 * when accessing devices with invalid addresses. Ontrap handling, which
574 575 * gracefully handles kernel bus errors, is installed anyway for I/O and mem
575 576 * space accessing (not for pci config space), in case future X86 platforms
576 577 * require it.
577 578 */
578 579
579 580 /* Access device. prg is modified. */
580 581 static int
↓ open down ↓ |
546 lines elided |
↑ open up ↑ |
581 582 pcitool_cfg_access(pcitool_reg_t *prg, boolean_t write_flag,
582 583 boolean_t io_access)
583 584 {
584 585 int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
585 586 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
586 587 int rval = SUCCESS;
587 588 uint64_t local_data;
588 589 pci_cfgacc_req_t req;
589 590 uint32_t max_offset;
590 591
591 - if ((size <= 0) || (size > 8) || ((size & (size - 1)) != 0)) {
592 + if ((size <= 0) || (size > 8) || !ISP2(size)) {
592 593 prg->status = PCITOOL_INVALID_SIZE;
593 594 return (ENOTSUP);
594 595 }
595 596
596 597 /*
597 598 * NOTE: there is no way to verify whether or not the address is
598 599 * valid other than that it is within the maximum offset. The
599 600 * put functions return void and the get functions return -1 on error.
600 601 */
601 602
602 603 if (io_access)
603 604 max_offset = 0xFF;
604 605 else
605 606 max_offset = 0xFFF;
606 607 if (prg->offset + size - 1 > max_offset) {
607 608 prg->status = PCITOOL_INVALID_ADDRESS;
608 609 return (ENOTSUP);
609 610 }
610 611
611 612 prg->status = PCITOOL_SUCCESS;
612 613
613 614 req.rcdip = NULL;
614 615 req.bdf = PCI_GETBDF(prg->bus_no, prg->dev_no, prg->func_no);
615 616 req.offset = prg->offset;
616 617 req.size = size;
617 618 req.write = write_flag;
618 619 req.ioacc = io_access;
619 620 if (write_flag) {
620 621 if (big_endian) {
621 622 local_data = pcitool_swap_endian(prg->data, size);
622 623 } else {
623 624 local_data = prg->data;
624 625 }
625 626 VAL64(&req) = local_data;
626 627 pci_cfgacc_acc(&req);
627 628 } else {
628 629 pci_cfgacc_acc(&req);
629 630 switch (size) {
630 631 case 1:
631 632 local_data = VAL8(&req);
632 633 break;
633 634 case 2:
634 635 local_data = VAL16(&req);
635 636 break;
636 637 case 4:
637 638 local_data = VAL32(&req);
638 639 break;
639 640 case 8:
640 641 local_data = VAL64(&req);
641 642 break;
642 643 }
643 644 if (big_endian) {
644 645 prg->data =
645 646 pcitool_swap_endian(local_data, size);
646 647 } else {
647 648 prg->data = local_data;
648 649 }
649 650 }
650 651 /*
651 652 * Check if legacy IO config access is used, in which case
652 653 * only first 256 bytes are valid.
653 654 */
654 655 if (req.ioacc && (prg->offset + size - 1 > 0xFF)) {
655 656 prg->status = PCITOOL_INVALID_ADDRESS;
656 657 return (ENOTSUP);
657 658 }
658 659
659 660 /* Set phys_addr only if MMIO is used */
660 661 prg->phys_addr = 0;
661 662 if (!req.ioacc && mcfg_mem_base != 0) {
662 663 prg->phys_addr = mcfg_mem_base + prg->offset +
663 664 ((prg->bus_no << PCIEX_REG_BUS_SHIFT) |
664 665 (prg->dev_no << PCIEX_REG_DEV_SHIFT) |
665 666 (prg->func_no << PCIEX_REG_FUNC_SHIFT));
666 667 }
667 668
668 669 return (rval);
669 670 }
670 671
671 672 static int
672 673 pcitool_io_access(pcitool_reg_t *prg, boolean_t write_flag)
673 674 {
674 675 int port = (int)prg->phys_addr;
675 676 size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
676 677 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
677 678 int rval = SUCCESS;
678 679 on_trap_data_t otd;
679 680 uint64_t local_data;
680 681
681 682
682 683 /*
683 684 * on_trap works like setjmp.
684 685 *
685 686 * A non-zero return here means on_trap has returned from an error.
686 687 *
687 688 * A zero return here means that on_trap has just returned from setup.
688 689 */
689 690 if (on_trap(&otd, OT_DATA_ACCESS)) {
690 691 no_trap();
691 692 if (pcitool_debug)
692 693 prom_printf(
693 694 "pcitool_io_access: on_trap caught an error...\n");
694 695 prg->status = PCITOOL_INVALID_ADDRESS;
695 696 return (EFAULT);
696 697 }
697 698
698 699 if (write_flag) {
699 700
700 701 if (big_endian) {
701 702 local_data = pcitool_swap_endian(prg->data, size);
702 703 } else {
703 704 local_data = prg->data;
704 705 }
705 706
706 707 if (pcitool_debug)
707 708 prom_printf("Writing %ld byte(s) to port 0x%x\n",
708 709 size, port);
709 710
710 711 switch (size) {
711 712 case 1:
712 713 outb(port, (uint8_t)local_data);
713 714 break;
714 715 case 2:
715 716 outw(port, (uint16_t)local_data);
716 717 break;
717 718 case 4:
718 719 outl(port, (uint32_t)local_data);
719 720 break;
720 721 default:
721 722 rval = ENOTSUP;
722 723 prg->status = PCITOOL_INVALID_SIZE;
723 724 break;
724 725 }
725 726 } else {
726 727 if (pcitool_debug)
727 728 prom_printf("Reading %ld byte(s) from port 0x%x\n",
728 729 size, port);
729 730
730 731 switch (size) {
731 732 case 1:
732 733 local_data = inb(port);
733 734 break;
734 735 case 2:
735 736 local_data = inw(port);
736 737 break;
737 738 case 4:
738 739 local_data = inl(port);
739 740 break;
740 741 default:
741 742 rval = ENOTSUP;
742 743 prg->status = PCITOOL_INVALID_SIZE;
743 744 break;
744 745 }
745 746
746 747 if (rval == SUCCESS) {
747 748 if (big_endian) {
748 749 prg->data =
749 750 pcitool_swap_endian(local_data, size);
750 751 } else {
751 752 prg->data = local_data;
752 753 }
753 754 }
754 755 }
755 756
756 757 no_trap();
757 758 return (rval);
758 759 }
759 760
760 761 static int
761 762 pcitool_mem_access(pcitool_reg_t *prg, uint64_t virt_addr, boolean_t write_flag)
762 763 {
763 764 size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
764 765 boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
765 766 int rval = DDI_SUCCESS;
766 767 on_trap_data_t otd;
767 768 uint64_t local_data;
768 769
769 770 /*
770 771 * on_trap works like setjmp.
771 772 *
772 773 * A non-zero return here means on_trap has returned from an error.
773 774 *
774 775 * A zero return here means that on_trap has just returned from setup.
775 776 */
776 777 if (on_trap(&otd, OT_DATA_ACCESS)) {
777 778 no_trap();
778 779 if (pcitool_debug)
779 780 prom_printf(
780 781 "pcitool_mem_access: on_trap caught an error...\n");
781 782 prg->status = PCITOOL_INVALID_ADDRESS;
782 783 return (EFAULT);
783 784 }
784 785
785 786 if (write_flag) {
786 787
787 788 if (big_endian) {
788 789 local_data = pcitool_swap_endian(prg->data, size);
789 790 } else {
790 791 local_data = prg->data;
791 792 }
792 793
793 794 switch (size) {
794 795 case 1:
795 796 *((uint8_t *)(uintptr_t)virt_addr) = local_data;
796 797 break;
797 798 case 2:
798 799 *((uint16_t *)(uintptr_t)virt_addr) = local_data;
799 800 break;
800 801 case 4:
801 802 *((uint32_t *)(uintptr_t)virt_addr) = local_data;
802 803 break;
803 804 case 8:
804 805 *((uint64_t *)(uintptr_t)virt_addr) = local_data;
805 806 break;
806 807 default:
807 808 rval = ENOTSUP;
808 809 prg->status = PCITOOL_INVALID_SIZE;
809 810 break;
810 811 }
811 812 } else {
812 813 switch (size) {
813 814 case 1:
814 815 local_data = *((uint8_t *)(uintptr_t)virt_addr);
815 816 break;
816 817 case 2:
817 818 local_data = *((uint16_t *)(uintptr_t)virt_addr);
818 819 break;
819 820 case 4:
820 821 local_data = *((uint32_t *)(uintptr_t)virt_addr);
821 822 break;
822 823 case 8:
823 824 local_data = *((uint64_t *)(uintptr_t)virt_addr);
824 825 break;
825 826 default:
826 827 rval = ENOTSUP;
827 828 prg->status = PCITOOL_INVALID_SIZE;
828 829 break;
829 830 }
830 831
831 832 if (rval == SUCCESS) {
832 833 if (big_endian) {
833 834 prg->data =
834 835 pcitool_swap_endian(local_data, size);
835 836 } else {
836 837 prg->data = local_data;
837 838 }
838 839 }
839 840 }
840 841
841 842 no_trap();
842 843 return (rval);
843 844 }
844 845
845 846 /*
846 847 * Map up to 2 pages which contain the address we want to access.
847 848 *
848 849 * Mapping should span no more than 8 bytes. With X86 it is possible for an
849 850 * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary.
850 851 * We'll never have to map more than two pages.
851 852 */
852 853
853 854 static uint64_t
854 855 pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages)
855 856 {
856 857
857 858 uint64_t page_base = phys_addr & ~MMU_PAGEOFFSET;
858 859 uint64_t offset = phys_addr & MMU_PAGEOFFSET;
859 860 void *virt_base;
860 861 uint64_t returned_addr;
861 862 pfn_t pfn;
862 863
863 864 if (pcitool_debug)
864 865 prom_printf("pcitool_map: Called with PA:0x%p\n",
865 866 (void *)(uintptr_t)phys_addr);
866 867
867 868 *num_pages = 1;
868 869
869 870 /* Desired mapping would span more than two pages. */
870 871 if ((offset + size) > (MMU_PAGESIZE * 2)) {
871 872 if (pcitool_debug)
872 873 prom_printf("boundary violation: "
873 874 "offset:0x%" PRIx64 ", size:%ld, pagesize:0x%lx\n",
874 875 offset, (uintptr_t)size, (uintptr_t)MMU_PAGESIZE);
875 876 return (NULL);
876 877
877 878 } else if ((offset + size) > MMU_PAGESIZE) {
878 879 (*num_pages)++;
879 880 }
880 881
881 882 /* Get page(s) of virtual space. */
882 883 virt_base = vmem_alloc(heap_arena, ptob(*num_pages), VM_NOSLEEP);
883 884 if (virt_base == NULL) {
884 885 if (pcitool_debug)
885 886 prom_printf("Couldn't get virtual base address.\n");
886 887 return (NULL);
887 888 }
888 889
889 890 if (pcitool_debug)
890 891 prom_printf("Got base virtual address:0x%p\n", virt_base);
891 892
892 893 #ifdef __xpv
893 894 /*
894 895 * We should only get here if we are dom0.
895 896 * We're using a real device so we need to translate the MA to a PFN.
896 897 */
897 898 ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
898 899 pfn = xen_assign_pfn(mmu_btop(page_base));
899 900 #else
900 901 pfn = btop(page_base);
901 902 #endif
902 903
903 904 /* Now map the allocated virtual space to the physical address. */
904 905 hat_devload(kas.a_hat, virt_base, mmu_ptob(*num_pages), pfn,
905 906 PROT_READ | PROT_WRITE | HAT_STRICTORDER,
906 907 HAT_LOAD_LOCK);
907 908
908 909 returned_addr = ((uintptr_t)(virt_base)) + offset;
909 910
910 911 if (pcitool_debug)
911 912 prom_printf("pcitool_map: returning VA:0x%p\n",
912 913 (void *)(uintptr_t)returned_addr);
913 914
914 915 return (returned_addr);
915 916 }
916 917
917 918 /* Unmap the mapped page(s). */
918 919 static void
919 920 pcitool_unmap(uint64_t virt_addr, size_t num_pages)
920 921 {
921 922 void *base_virt_addr = (void *)(uintptr_t)(virt_addr & ~MMU_PAGEOFFSET);
922 923
923 924 hat_unload(kas.a_hat, base_virt_addr, ptob(num_pages),
924 925 HAT_UNLOAD_UNLOCK);
925 926 vmem_free(heap_arena, base_virt_addr, ptob(num_pages));
926 927 }
927 928
928 929
929 930 /* Perform register accesses on PCI leaf devices. */
930 931 /*ARGSUSED*/
931 932 int
932 933 pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
933 934 {
934 935 boolean_t write_flag = B_FALSE;
935 936 boolean_t io_access = B_TRUE;
936 937 int rval = 0;
937 938 pcitool_reg_t prg;
938 939 uint8_t size;
939 940
940 941 uint64_t base_addr;
941 942 uint64_t virt_addr;
942 943 size_t num_virt_pages;
943 944
944 945 switch (cmd) {
945 946 case (PCITOOL_DEVICE_SET_REG):
946 947 write_flag = B_TRUE;
947 948
948 949 /*FALLTHRU*/
949 950 case (PCITOOL_DEVICE_GET_REG):
950 951 if (pcitool_debug)
951 952 prom_printf("pci_dev_reg_ops set/get reg\n");
952 953 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
953 954 DDI_SUCCESS) {
954 955 if (pcitool_debug)
955 956 prom_printf("Error reading arguments\n");
956 957 return (EFAULT);
957 958 }
958 959
959 960 if (prg.barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
960 961 prg.status = PCITOOL_OUT_OF_RANGE;
961 962 rval = EINVAL;
962 963 goto done_reg;
963 964 }
964 965
965 966 if (pcitool_debug)
966 967 prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n",
967 968 prg.bus_no, prg.dev_no, prg.func_no);
968 969 /* Validate address arguments of bus / dev / func */
969 970 if (((prg.bus_no &
970 971 (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) !=
971 972 prg.bus_no) ||
972 973 ((prg.dev_no &
973 974 (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) !=
974 975 prg.dev_no) ||
975 976 ((prg.func_no &
976 977 (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) !=
977 978 prg.func_no)) {
978 979 prg.status = PCITOOL_INVALID_ADDRESS;
979 980 rval = EINVAL;
980 981 goto done_reg;
981 982 }
982 983
983 984 size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
984 985
985 986 /* Proper config space desired. */
986 987 if (prg.barnum == 0) {
987 988
988 989 if (pcitool_debug)
989 990 prom_printf(
990 991 "config access: offset:0x%" PRIx64 ", "
991 992 "phys_addr:0x%" PRIx64 "\n",
992 993 prg.offset, prg.phys_addr);
993 994
994 995 if (prg.offset >= max_cfg_size) {
995 996 prg.status = PCITOOL_OUT_OF_RANGE;
996 997 rval = EINVAL;
997 998 goto done_reg;
998 999 }
999 1000 if (max_cfg_size == PCIE_CONF_HDR_SIZE)
1000 1001 io_access = B_FALSE;
1001 1002
1002 1003 rval = pcitool_cfg_access(&prg, write_flag, io_access);
1003 1004 if (pcitool_debug)
1004 1005 prom_printf(
1005 1006 "config access: data:0x%" PRIx64 "\n",
1006 1007 prg.data);
1007 1008
1008 1009 /* IO/ MEM/ MEM64 space. */
1009 1010 } else {
1010 1011
1011 1012 pcitool_reg_t prg2;
1012 1013 bcopy(&prg, &prg2, sizeof (pcitool_reg_t));
1013 1014
1014 1015 /*
1015 1016 * Translate BAR number into offset of the BAR in
1016 1017 * the device's config space.
1017 1018 */
1018 1019 prg2.offset = pci_bars[prg2.barnum];
1019 1020 prg2.acc_attr =
1020 1021 PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
1021 1022
1022 1023 if (pcitool_debug)
1023 1024 prom_printf(
1024 1025 "barnum:%d, bar_offset:0x%" PRIx64 "\n",
1025 1026 prg2.barnum, prg2.offset);
1026 1027 /*
1027 1028 * Get Bus Address Register (BAR) from config space.
1028 1029 * prg2.offset is the offset into config space of the
1029 1030 * BAR desired. prg.status is modified on error.
1030 1031 */
1031 1032 rval = pcitool_cfg_access(&prg2, B_FALSE, B_TRUE);
1032 1033 if (rval != SUCCESS) {
1033 1034 if (pcitool_debug)
1034 1035 prom_printf("BAR access failed\n");
1035 1036 prg.status = prg2.status;
1036 1037 goto done_reg;
1037 1038 }
1038 1039 /*
1039 1040 * Reference proper PCI space based on the BAR.
1040 1041 * If 64 bit MEM space, need to load other half of the
1041 1042 * BAR first.
1042 1043 */
1043 1044
1044 1045 if (pcitool_debug)
1045 1046 prom_printf("bar returned is 0x%" PRIx64 "\n",
1046 1047 prg2.data);
1047 1048 if (!prg2.data) {
1048 1049 if (pcitool_debug)
1049 1050 prom_printf("BAR data == 0\n");
1050 1051 rval = EINVAL;
1051 1052 prg.status = PCITOOL_INVALID_ADDRESS;
1052 1053 goto done_reg;
1053 1054 }
1054 1055 if (prg2.data == 0xffffffff) {
1055 1056 if (pcitool_debug)
1056 1057 prom_printf("BAR data == -1\n");
1057 1058 rval = EINVAL;
1058 1059 prg.status = PCITOOL_INVALID_ADDRESS;
1059 1060 goto done_reg;
1060 1061 }
1061 1062
1062 1063 /*
1063 1064 * BAR has bits saying this space is IO space, unless
1064 1065 * this is the ROM address register.
1065 1066 */
1066 1067 if (((PCI_BASE_SPACE_M & prg2.data) ==
1067 1068 PCI_BASE_SPACE_IO) &&
1068 1069 (prg2.offset != PCI_CONF_ROM)) {
1069 1070 if (pcitool_debug)
1070 1071 prom_printf("IO space\n");
1071 1072
1072 1073 prg2.data &= PCI_BASE_IO_ADDR_M;
1073 1074 prg.phys_addr = prg2.data + prg.offset;
1074 1075
1075 1076 rval = pcitool_io_access(&prg, write_flag);
1076 1077 if ((rval != SUCCESS) && (pcitool_debug))
1077 1078 prom_printf("IO access failed\n");
1078 1079
1079 1080 goto done_reg;
1080 1081
1081 1082
1082 1083 /*
1083 1084 * BAR has bits saying this space is 64 bit memory
1084 1085 * space, unless this is the ROM address register.
1085 1086 *
1086 1087 * The 64 bit address stored in two BAR cells is not
1087 1088 * necessarily aligned on an 8-byte boundary.
1088 1089 * Need to keep the first 4 bytes read,
1089 1090 * and do a separate read of the high 4 bytes.
1090 1091 */
1091 1092
1092 1093 } else if ((PCI_BASE_TYPE_ALL & prg2.data) &&
1093 1094 (prg2.offset != PCI_CONF_ROM)) {
1094 1095
1095 1096 uint32_t low_bytes =
1096 1097 (uint32_t)(prg2.data & ~PCI_BASE_TYPE_ALL);
1097 1098
1098 1099 /*
1099 1100 * Don't try to read the next 4 bytes
1100 1101 * past the end of BARs.
1101 1102 */
1102 1103 if (prg2.offset >= PCI_CONF_BASE5) {
1103 1104 prg.status = PCITOOL_OUT_OF_RANGE;
1104 1105 rval = EIO;
1105 1106 goto done_reg;
1106 1107 }
1107 1108
1108 1109 /*
1109 1110 * Access device.
1110 1111 * prg2.status is modified on error.
1111 1112 */
1112 1113 prg2.offset += 4;
1113 1114 rval = pcitool_cfg_access(&prg2,
1114 1115 B_FALSE, B_TRUE);
1115 1116 if (rval != SUCCESS) {
1116 1117 prg.status = prg2.status;
1117 1118 goto done_reg;
1118 1119 }
1119 1120
1120 1121 if (prg2.data == 0xffffffff) {
1121 1122 prg.status = PCITOOL_INVALID_ADDRESS;
1122 1123 prg.status = EFAULT;
1123 1124 goto done_reg;
1124 1125 }
1125 1126
1126 1127 prg2.data = (prg2.data << 32) + low_bytes;
1127 1128 if (pcitool_debug)
1128 1129 prom_printf(
1129 1130 "64 bit mem space. "
1130 1131 "64-bit bar is 0x%" PRIx64 "\n",
1131 1132 prg2.data);
1132 1133
1133 1134 /* Mem32 space, including ROM */
1134 1135 } else {
1135 1136
1136 1137 if (prg2.offset == PCI_CONF_ROM) {
1137 1138 if (pcitool_debug)
1138 1139 prom_printf(
1139 1140 "Additional ROM "
1140 1141 "checking\n");
1141 1142 /* Can't write to ROM */
1142 1143 if (write_flag) {
1143 1144 prg.status = PCITOOL_ROM_WRITE;
1144 1145 rval = EIO;
1145 1146 goto done_reg;
1146 1147
1147 1148 /* ROM disabled for reading */
1148 1149 } else if (!(prg2.data & 0x00000001)) {
1149 1150 prg.status =
1150 1151 PCITOOL_ROM_DISABLED;
1151 1152 rval = EIO;
1152 1153 goto done_reg;
1153 1154 }
1154 1155 }
1155 1156
1156 1157 if (pcitool_debug)
1157 1158 prom_printf("32 bit mem space\n");
1158 1159 }
1159 1160
1160 1161 /* Common code for all IO/MEM range spaces. */
1161 1162
1162 1163 base_addr = prg2.data;
1163 1164 if (pcitool_debug)
1164 1165 prom_printf(
1165 1166 "addr portion of bar is 0x%" PRIx64 ", "
1166 1167 "base=0x%" PRIx64 ", "
1167 1168 "offset:0x%" PRIx64 "\n",
1168 1169 prg2.data, base_addr, prg.offset);
1169 1170 /*
1170 1171 * Use offset provided by caller to index into
1171 1172 * desired space, then access.
1172 1173 * Note that prg.status is modified on error.
1173 1174 */
1174 1175 prg.phys_addr = base_addr + prg.offset;
1175 1176
1176 1177 virt_addr = pcitool_map(prg.phys_addr, size,
1177 1178 &num_virt_pages);
1178 1179 if (virt_addr == NULL) {
1179 1180 prg.status = PCITOOL_IO_ERROR;
1180 1181 rval = EIO;
1181 1182 goto done_reg;
1182 1183 }
1183 1184
1184 1185 rval = pcitool_mem_access(&prg, virt_addr, write_flag);
1185 1186 pcitool_unmap(virt_addr, num_virt_pages);
1186 1187 }
1187 1188 done_reg:
1188 1189 prg.drvr_version = PCITOOL_VERSION;
1189 1190 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
1190 1191 DDI_SUCCESS) {
1191 1192 if (pcitool_debug)
1192 1193 prom_printf("Error returning arguments.\n");
1193 1194 rval = EFAULT;
1194 1195 }
1195 1196 break;
1196 1197 default:
1197 1198 rval = ENOTTY;
1198 1199 break;
1199 1200 }
1200 1201 return (rval);
1201 1202 }
↓ open down ↓ |
600 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX