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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/types.h>
28 #include <sys/dditypes.h>
29 #include <sys/machsystm.h>
30 #include <sys/archsystm.h>
31 #include <sys/prom_plat.h>
32 #include <sys/promif.h>
33 #include <sys/kmem.h>
34 #include <sys/hypervisor_api.h>
35 #include <sys/hsvc.h>
36
37 #ifdef DEBUG
38
39 int hsvc_debug = 0; /* HSVC debug flags */
40
41 /*
42 * Flags to control HSVC debugging
43 */
44 #define DBG_HSVC_REGISTER 0x0001
45 #define DBG_HSVC_UNREGISTER 0x0002
46 #define DBG_HSVC_OBP_CIF 0x0004
47 #define DBG_HSVC_ALLOC 0x0008
48 #define DBG_HSVC_VERSION 0x0010
49 #define DBG_HSVC_REFCNT 0x0020
50 #define DBG_HSVC_SETUP 0x0040
51
52 #define HSVC_CHK_REFCNT(hsvcp) \
53 if (hsvc_debug & DBG_HSVC_REFCNT) hsvc_chk_refcnt(hsvcp)
54
55 #define HSVC_DEBUG(flag, ARGS) \
56 if (hsvc_debug & flag) prom_printf ARGS
57
58 #define HSVC_DUMP() \
59 if (hsvc_debug & DBG_HSVC_SETUP) hsvc_dump()
60
61 #else /* DEBUG */
62
63 #define HSVC_CHK_REFCNT(hsvcp)
64 #define HSVC_DEBUG(flag, args)
65 #define HSVC_DUMP()
66
67 #endif /* DEBUG */
68
69 /*
70 * Each hypervisor API group negotiation is tracked via a
71 * hsvc structure. This structure contains the API group,
72 * currently negotiated major/minor number, a singly linked
73 * list of clients currently registered and a reference count.
74 *
75 * Since the number of API groups is fairly small, negotiated
76 * API groups are maintained via a singly linked list. Also,
77 * sufficient free space is reserved to allow for API group
78 * registration before kmem_xxx interface can be used to
79 * allocate memory dynamically.
80 *
81 * Note that all access to the API group lookup and negotiation
82 * is serialized to support strict HV API interface.
83 */
84
85 typedef struct hsvc {
86 struct hsvc *next; /* next group/free entry */
87 uint64_t group; /* hypervisor service group */
88 uint64_t major; /* major number */
89 uint64_t minor; /* minor number */
90 uint64_t refcnt; /* reference count */
91 hsvc_info_t *clients; /* linked list of clients */
92 } hsvc_t;
93
94
95 /*
96 * Global variables
97 */
98 hsvc_t *hsvc_groups; /* linked list of API groups in use */
99 hsvc_t *hsvc_avail; /* free reserved buffers */
100 kmutex_t hsvc_lock; /* protects linked list and globals */
101
102 /*
103 * Preallocate some space for boot requirements (before kmem_xxx can be
104 * used)
105 */
106 #define HSVC_RESV_BUFS_MAX 16
107 hsvc_t hsvc_resv_bufs[HSVC_RESV_BUFS_MAX];
108
109 /*
110 * Pre-versioning groups (not negotiated by Ontario/Erie FCS release)
111 */
112 static uint64_t hsvc_pre_versioning_groups[] = {
113 HSVC_GROUP_SUN4V,
114 HSVC_GROUP_CORE,
115 HSVC_GROUP_VPCI,
116 HSVC_GROUP_VSC,
117 HSVC_GROUP_NIAGARA_CPU,
118 HSVC_GROUP_NCS,
119 HSVC_GROUP_DIAG
120 };
121
122 #define HSVC_PRE_VERSIONING_GROUP_CNT \
123 (sizeof (hsvc_pre_versioning_groups) / sizeof (uint64_t))
124
125 static boolean_t
pre_versioning_group(uint64_t api_group)126 pre_versioning_group(uint64_t api_group)
127 {
128 int i;
129
130 for (i = 0; i < HSVC_PRE_VERSIONING_GROUP_CNT; i++)
131 if (hsvc_pre_versioning_groups[i] == api_group)
132 return (B_TRUE);
133 return (B_FALSE);
134 }
135
136 static hsvc_t *
hsvc_lookup(hsvc_info_t * hsvcinfop)137 hsvc_lookup(hsvc_info_t *hsvcinfop)
138 {
139 hsvc_t *hsvcp;
140 hsvc_info_t *p;
141
142 for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) {
143 for (p = hsvcp->clients; p != NULL;
144 p = (hsvc_info_t *)p->hsvc_private)
145 if (p == hsvcinfop)
146 break;
147 if (p)
148 break;
149 }
150
151 return (hsvcp);
152 }
153
154 #ifdef DEBUG
155
156 /*
157 * Check client reference count
158 */
159 static void
hsvc_chk_refcnt(hsvc_t * hsvcp)160 hsvc_chk_refcnt(hsvc_t *hsvcp)
161 {
162 int refcnt;
163 hsvc_info_t *p;
164
165 for (refcnt = 0, p = hsvcp->clients; p != NULL;
166 p = (hsvc_info_t *)p->hsvc_private)
167 refcnt++;
168
169 ASSERT(hsvcp->refcnt == refcnt);
170 }
171
172 /*
173 * Dump registered clients information
174 */
175 static void
hsvc_dump(void)176 hsvc_dump(void)
177 {
178 hsvc_t *hsvcp;
179 hsvc_info_t *p;
180
181 mutex_enter(&hsvc_lock);
182
183 prom_printf("hsvc_dump: hsvc_groups: %p hsvc_avail: %p\n",
184 (void *)hsvc_groups, (void *)hsvc_avail);
185
186 for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) {
187 prom_printf(" hsvcp: %p (0x%lx 0x%lx 0x%lx) ref: %ld clients: "
188 "%p\n", (void *)hsvcp, hsvcp->group, hsvcp->major,
189 hsvcp->minor, hsvcp->refcnt, (void *)hsvcp->clients);
190
191 for (p = hsvcp->clients; p != NULL;
192 p = (hsvc_info_t *)p->hsvc_private) {
193 prom_printf(" client %p (0x%lx 0x%lx 0x%lx '%s') "
194 "private: %p\n", (void *)p, p->hsvc_group,
195 p->hsvc_major, p->hsvc_minor, p->hsvc_modname,
196 p->hsvc_private);
197 }
198 }
199
200 mutex_exit(&hsvc_lock);
201 }
202
203 #endif /* DEBUG */
204
205 /*
206 * Allocate a buffer to cache API group information. Note that we
207 * allocate a buffer from reserved pool early on, before kmem_xxx
208 * interface becomes available.
209 */
210 static hsvc_t *
hsvc_alloc(void)211 hsvc_alloc(void)
212 {
213 hsvc_t *hsvcp;
214
215 ASSERT(MUTEX_HELD(&hsvc_lock));
216
217 if (hsvc_avail != NULL) {
218 hsvcp = hsvc_avail;
219 hsvc_avail = hsvcp->next;
220 } else if (kmem_ready) {
221 hsvcp = kmem_zalloc(sizeof (hsvc_t), KM_SLEEP);
222 HSVC_DEBUG(DBG_HSVC_ALLOC,
223 ("hsvc_alloc: hsvc_avail: %p kmem_zalloc hsvcp: %p\n",
224 (void *)hsvc_avail, (void *)hsvcp));
225 } else
226 hsvcp = NULL;
227 return (hsvcp);
228 }
229
230 static void
hsvc_free(hsvc_t * hsvcp)231 hsvc_free(hsvc_t *hsvcp)
232 {
233 ASSERT(hsvcp != NULL);
234 ASSERT(MUTEX_HELD(&hsvc_lock));
235
236 if (hsvcp >= hsvc_resv_bufs &&
237 hsvcp < &hsvc_resv_bufs[HSVC_RESV_BUFS_MAX]) {
238 hsvcp->next = hsvc_avail;
239 hsvc_avail = hsvcp;
240 } else {
241 HSVC_DEBUG(DBG_HSVC_ALLOC,
242 ("hsvc_free: hsvc_avail: %p kmem_free hsvcp: %p\n",
243 (void *)hsvc_avail, (void *)hsvcp));
244 (void) kmem_free(hsvcp, sizeof (hsvc_t));
245 }
246 }
247
248 /*
249 * Link client on the specified hsvc's client list and
250 * bump the reference count.
251 */
252 static void
hsvc_link_client(hsvc_t * hsvcp,hsvc_info_t * hsvcinfop)253 hsvc_link_client(hsvc_t *hsvcp, hsvc_info_t *hsvcinfop)
254 {
255 ASSERT(MUTEX_HELD(&hsvc_lock));
256 HSVC_CHK_REFCNT(hsvcp);
257
258 hsvcinfop->hsvc_private = hsvcp->clients;
259 hsvcp->clients = hsvcinfop;
260 hsvcp->refcnt++;
261 }
262
263 /*
264 * Unlink a client from the specified hsvc's client list and
265 * decrement the reference count, if found.
266 *
267 * Return 0 if client unlinked. Otherwise return -1.
268 */
269 static int
hsvc_unlink_client(hsvc_t * hsvcp,hsvc_info_t * hsvcinfop)270 hsvc_unlink_client(hsvc_t *hsvcp, hsvc_info_t *hsvcinfop)
271 {
272 hsvc_info_t *p, **pp;
273 int status = 0;
274
275 ASSERT(MUTEX_HELD(&hsvc_lock));
276 HSVC_CHK_REFCNT(hsvcp);
277
278 for (pp = &hsvcp->clients; (p = *pp) != NULL;
279 pp = (hsvc_info_t **)&p->hsvc_private) {
280 if (p != hsvcinfop)
281 continue;
282
283 ASSERT(hsvcp->refcnt > 0);
284 hsvcp->refcnt--;
285 *pp = (hsvc_info_t *)p->hsvc_private;
286 p->hsvc_private = NULL;
287 break;
288 }
289
290 if (p == NULL)
291 status = -1;
292
293 return (status);
294 }
295
296 /*
297 * Negotiate/register an API group usage
298 */
299 int
hsvc_register(hsvc_info_t * hsvcinfop,uint64_t * supported_minor)300 hsvc_register(hsvc_info_t *hsvcinfop, uint64_t *supported_minor)
301 {
302 hsvc_t *hsvcp;
303 uint64_t api_group = hsvcinfop->hsvc_group;
304 uint64_t major = hsvcinfop->hsvc_major;
305 uint64_t minor = hsvcinfop->hsvc_minor;
306 int status = 0;
307
308 HSVC_DEBUG(DBG_HSVC_REGISTER,
309 ("hsvc_register %p (0x%lx 0x%lx 0x%lx ID %s)\n", (void *)hsvcinfop,
310 api_group, major, minor, hsvcinfop->hsvc_modname));
311
312 if (hsvcinfop->hsvc_rev != HSVC_REV_1)
313 return (EINVAL);
314
315 mutex_enter(&hsvc_lock);
316
317 /*
318 * Make sure that the hsvcinfop is new (i.e. not already registered).
319 */
320 if (hsvc_lookup(hsvcinfop) != NULL) {
321 mutex_exit(&hsvc_lock);
322 return (EINVAL);
323 }
324
325 /*
326 * Search for the specified api_group
327 */
328 for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next)
329 if (hsvcp->group == api_group)
330 break;
331
332 if (hsvcp) {
333 /*
334 * If major number mismatch, then return ENOTSUP.
335 * Otherwise return currently negotiated minor
336 * and the following status:
337 * ENOTSUP requested minor < current minor
338 * OK requested minor >= current minor
339 */
340
341 if (hsvcp->major != major) {
342 status = ENOTSUP;
343 } else if (hsvcp->minor > minor) {
344 /*
345 * Client requested a lower minor number than
346 * currently in use.
347 */
348 status = ENOTSUP;
349 *supported_minor = hsvcp->minor;
350 } else {
351 /*
352 * Client requested a minor number same or higher
353 * than the one in use. Set supported minor number
354 * and link the client on hsvc client linked list.
355 */
356 *supported_minor = hsvcp->minor;
357 hsvc_link_client(hsvcp, hsvcinfop);
358 }
359 } else {
360 /*
361 * This service group has not been negotiated yet.
362 * Call OBP CIF interface to negotiate a major/minor
363 * number.
364 *
365 * If not enough memory to cache this information, then
366 * return EAGAIN so that the caller can try again later.
367 * Otherwise, process OBP CIF results as follows:
368 *
369 * H_BADTRAP OBP CIF interface is not supported.
370 * If not a pre-versioning group, then
371 * return EINVAL, indicating unsupported
372 * API group. Otherwise, mimic default
373 * behavior (i.e. support only major=1).
374 *
375 * H_EOK Negotiation was successful. Cache
376 * and return supported major/minor,
377 * limiting the minor number to the
378 * requested value.
379 *
380 * H_EINVAL Invalid group. Return EINVAL
381 *
382 * H_ENOTSUPPORTED Unsupported major number. Return
383 * ENOTSUP.
384 *
385 * H_EBUSY Return EAGAIN.
386 *
387 * H_EWOULDBLOCK Return EAGAIN.
388 */
389 hsvcp = hsvc_alloc();
390 if (hsvcp == NULL) {
391 status = EAGAIN;
392 } else {
393 uint64_t hvstat;
394
395 hvstat = prom_set_sun4v_api_version(api_group,
396 major, minor, supported_minor);
397
398 HSVC_DEBUG(DBG_HSVC_OBP_CIF,
399 ("prom_set_sun4v_api_ver: 0x%lx 0x%lx, 0x%lx "
400 " hvstat: 0x%lx sup_minor: 0x%lx\n", api_group,
401 major, minor, hvstat, *supported_minor));
402
403 switch (hvstat) {
404 case H_EBADTRAP:
405 /*
406 * Older firmware does not support OBP CIF
407 * interface. If it's a pre-versioning group,
408 * then assume that the firmware supports
409 * only major=1 and minor=0.
410 */
411 if (!pre_versioning_group(api_group)) {
412 status = EINVAL;
413 break;
414 } else if (major != 1) {
415 status = ENOTSUP;
416 break;
417 }
418
419 /*
420 * It's a preversioning group. Default minor
421 * value to 0.
422 */
423 *supported_minor = 0;
424
425 /*FALLTHROUGH*/
426 case H_EOK:
427 /*
428 * Limit supported minor number to the
429 * requested value and cache the new
430 * API group information.
431 */
432 if (*supported_minor > minor)
433 *supported_minor = minor;
434 hsvcp->group = api_group;
435 hsvcp->major = major;
436 hsvcp->minor = *supported_minor;
437 hsvcp->refcnt = 0;
438 hsvcp->clients = NULL;
439 hsvcp->next = hsvc_groups;
440 hsvc_groups = hsvcp;
441
442 /*
443 * Link the caller on the client linked list.
444 */
445 hsvc_link_client(hsvcp, hsvcinfop);
446 break;
447
448 case H_ENOTSUPPORTED:
449 status = ENOTSUP;
450 break;
451
452 case H_EBUSY:
453 case H_EWOULDBLOCK:
454 status = EAGAIN;
455 break;
456
457 case H_EINVAL:
458 default:
459 status = EINVAL;
460 break;
461 }
462 }
463 /*
464 * Deallocate entry if not used
465 */
466 if (status != 0)
467 hsvc_free(hsvcp);
468 }
469 mutex_exit(&hsvc_lock);
470
471 HSVC_DEBUG(DBG_HSVC_REGISTER,
472 ("hsvc_register(%p) status; %d sup_minor: 0x%lx\n",
473 (void *)hsvcinfop, status, *supported_minor));
474
475 return (status);
476 }
477
478 /*
479 * Unregister an API group usage
480 */
481 int
hsvc_unregister(hsvc_info_t * hsvcinfop)482 hsvc_unregister(hsvc_info_t *hsvcinfop)
483 {
484 hsvc_t **hsvcpp, *hsvcp;
485 uint64_t api_group;
486 uint64_t major, supported_minor;
487 int status = 0;
488
489 if (hsvcinfop->hsvc_rev != HSVC_REV_1)
490 return (EINVAL);
491
492 major = hsvcinfop->hsvc_major;
493 api_group = hsvcinfop->hsvc_group;
494
495 HSVC_DEBUG(DBG_HSVC_UNREGISTER,
496 ("hsvc_unregister %p (0x%lx 0x%lx 0x%lx ID %s)\n",
497 (void *)hsvcinfop, api_group, major, hsvcinfop->hsvc_minor,
498 hsvcinfop->hsvc_modname));
499
500 /*
501 * Search for the matching entry and return EINVAL if no match found.
502 * Otherwise, remove it from our list and unregister the API
503 * group if this was the last reference to that API group.
504 */
505 mutex_enter(&hsvc_lock);
506
507 for (hsvcpp = &hsvc_groups; (hsvcp = *hsvcpp) != NULL;
508 hsvcpp = &hsvcp->next) {
509 if (hsvcp->group != api_group || hsvcp->major != major)
510 continue;
511
512 /*
513 * Search client list for a matching hsvcinfop entry
514 * and unlink it and decrement refcnt, if found.
515 */
516 if (hsvc_unlink_client(hsvcp, hsvcinfop) < 0) {
517 /* client not registered */
518 status = EINVAL;
519 break;
520 }
521
522 /*
523 * Client has been unlinked. If this was the last
524 * reference, unregister API group via OBP CIF
525 * interface.
526 */
527 if (hsvcp->refcnt == 0) {
528 uint64_t hvstat;
529
530 ASSERT(hsvcp->clients == NULL);
531 hvstat = prom_set_sun4v_api_version(api_group, 0, 0,
532 &supported_minor);
533
534 HSVC_DEBUG(DBG_HSVC_OBP_CIF,
535 (" prom unreg group: 0x%lx hvstat: 0x%lx\n",
536 api_group, hvstat));
537
538 /*
539 * Note that the call to unnegotiate an API group
540 * may fail if anyone, including OBP, is using
541 * those services. However, the caller is done
542 * with this API group and should be allowed to
543 * unregister regardless of the outcome.
544 */
545 *hsvcpp = hsvcp->next;
546 hsvc_free(hsvcp);
547 }
548 break;
549 }
550
551 if (hsvcp == NULL)
552 status = EINVAL;
553
554 mutex_exit(&hsvc_lock);
555
556 HSVC_DEBUG(DBG_HSVC_UNREGISTER,
557 ("hsvc_unregister %p status: %d\n", (void *)hsvcinfop, status));
558
559 return (status);
560 }
561
562
563 /*
564 * Get negotiated major/minor version number for an API group
565 */
566 int
hsvc_version(uint64_t api_group,uint64_t * majorp,uint64_t * minorp)567 hsvc_version(uint64_t api_group, uint64_t *majorp, uint64_t *minorp)
568 {
569 int status = 0;
570 uint64_t hvstat;
571 hsvc_t *hsvcp;
572
573 /*
574 * Check if the specified api_group is already in use.
575 * If so, return currently negotiated major/minor number.
576 * Otherwise, call OBP CIF interface to get the currently
577 * negotiated major/minor number.
578 */
579 mutex_enter(&hsvc_lock);
580 for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next)
581 if (hsvcp->group == api_group)
582 break;
583
584 if (hsvcp) {
585 *majorp = hsvcp->major;
586 *minorp = hsvcp->minor;
587 } else {
588 hvstat = prom_get_sun4v_api_version(api_group, majorp, minorp);
589
590 switch (hvstat) {
591 case H_EBADTRAP:
592 /*
593 * Older firmware does not support OBP CIF
594 * interface. If it's a pre-versioning group,
595 * then return default major/minor (i.e. 1/0).
596 * Otherwise, return EINVAL.
597 */
598 if (pre_versioning_group(api_group)) {
599 *majorp = 1;
600 *minorp = 0;
601 } else
602 status = EINVAL;
603 break;
604
605 case H_EINVAL:
606 default:
607 status = EINVAL;
608 break;
609
610 }
611 }
612 mutex_exit(&hsvc_lock);
613
614 HSVC_DEBUG(DBG_HSVC_VERSION,
615 ("hsvc_version(0x%lx) status: %d major: 0x%lx minor: 0x%lx\n",
616 api_group, status, *majorp, *minorp));
617
618 return (status);
619 }
620
621 /*
622 * Initialize framework data structures
623 */
624 void
hsvc_init(void)625 hsvc_init(void)
626 {
627 int i;
628 hsvc_t *hsvcp;
629
630 /*
631 * Initialize global data structures
632 */
633 mutex_init(&hsvc_lock, NULL, MUTEX_DEFAULT, NULL);
634 hsvc_groups = NULL;
635 hsvc_avail = NULL;
636
637 /*
638 * Setup initial free list
639 */
640 mutex_enter(&hsvc_lock);
641 for (i = 0, hsvcp = &hsvc_resv_bufs[0];
642 i < HSVC_RESV_BUFS_MAX; i++, hsvcp++)
643 hsvc_free(hsvcp);
644 mutex_exit(&hsvc_lock);
645 }
646
647
648 /*
649 * Hypervisor services to be negotiated at boot time.
650 *
651 * Note that the kernel needs to negotiate the HSVC_GROUP_SUN4V
652 * API group first, before doing any other negotiation. Also, it
653 * uses hypervisor services belonging to the HSVC_GROUP_CORE API
654 * group only for itself.
655 *
656 * Note that the HSVC_GROUP_DIAG is negotiated on behalf of
657 * any driver/module using DIAG services.
658 */
659 typedef struct hsvc_info_unix_s {
660 hsvc_info_t hsvcinfo;
661 int required;
662 } hsvc_info_unix_t;
663
664 static hsvc_info_unix_t hsvcinfo_unix[] = {
665 {{HSVC_REV_1, NULL, HSVC_GROUP_SUN4V, 1, 0, NULL}, 1},
666 {{HSVC_REV_1, NULL, HSVC_GROUP_CORE, 1, 2, NULL}, 1},
667 {{HSVC_REV_1, NULL, HSVC_GROUP_DIAG, 1, 0, NULL}, 1},
668 {{HSVC_REV_1, NULL, HSVC_GROUP_INTR, 1, 0, NULL}, 0},
669 {{HSVC_REV_1, NULL, HSVC_GROUP_REBOOT_DATA, 1, 0, NULL}, 0},
670 };
671
672 #define HSVCINFO_UNIX_CNT (sizeof (hsvcinfo_unix) / sizeof (hsvc_info_t))
673 static char *hsvcinfo_unix_modname = "unix";
674
675 /*
676 * Initialize framework and register hypervisor services to be used
677 * by the kernel.
678 */
679 void
hsvc_setup()680 hsvc_setup()
681 {
682 int i, status;
683 uint64_t sup_minor;
684 hsvc_info_unix_t *hsvcinfop;
685
686 /*
687 * Initialize framework
688 */
689 hsvc_init();
690
691 /*
692 * Negotiate versioning for required groups
693 */
694 for (hsvcinfop = &hsvcinfo_unix[0], i = 0; i < HSVCINFO_UNIX_CNT;
695 i++, hsvcinfop++) {
696 hsvcinfop->hsvcinfo.hsvc_private = NULL;
697 hsvcinfop->hsvcinfo.hsvc_modname = hsvcinfo_unix_modname;
698 status = hsvc_register(&(hsvcinfop->hsvcinfo), &sup_minor);
699
700 if ((status != 0) && hsvcinfop->required) {
701 cmn_err(CE_PANIC, "%s: cannot negotiate hypervisor "
702 "services - group: 0x%lx major: 0x%lx minor: 0x%lx"
703 " errno: %d\n", hsvcinfop->hsvcinfo.hsvc_modname,
704 hsvcinfop->hsvcinfo.hsvc_group,
705 hsvcinfop->hsvcinfo.hsvc_major,
706 hsvcinfop->hsvcinfo.hsvc_minor, status);
707 }
708 }
709 HSVC_DUMP();
710 }
711