/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/dditypes.h>
#include <sys/machsystm.h>
#include <sys/archsystm.h>
#include <sys/prom_plat.h>
#include <sys/hypervisor_api.h>
#ifdef DEBUG
/*
* Flags to control HSVC debugging
*/
#define HSVC_DUMP() \
#else /* DEBUG */
#define HSVC_DUMP()
#endif /* DEBUG */
/*
* Each hypervisor API group negotiation is tracked via a
* hsvc structure. This structure contains the API group,
* list of clients currently registered and a reference count.
*
* Since the number of API groups is fairly small, negotiated
* API groups are maintained via a singly linked list. Also,
* sufficient free space is reserved to allow for API group
* registration before kmem_xxx interface can be used to
* allocate memory dynamically.
*
* Note that all access to the API group lookup and negotiation
* is serialized to support strict HV API interface.
*/
typedef struct hsvc {
} hsvc_t;
/*
* Global variables
*/
/*
* Preallocate some space for boot requirements (before kmem_xxx can be
* used)
*/
/*
*/
};
#define HSVC_PRE_VERSIONING_GROUP_CNT \
(sizeof (hsvc_pre_versioning_groups) / sizeof (uint64_t))
static boolean_t
{
int i;
for (i = 0; i < HSVC_PRE_VERSIONING_GROUP_CNT; i++)
if (hsvc_pre_versioning_groups[i] == api_group)
return (B_TRUE);
return (B_FALSE);
}
static hsvc_t *
{
hsvc_info_t *p;
p = (hsvc_info_t *)p->hsvc_private)
if (p == hsvcinfop)
break;
if (p)
break;
}
return (hsvcp);
}
#ifdef DEBUG
/*
* Check client reference count
*/
static void
{
int refcnt;
hsvc_info_t *p;
p = (hsvc_info_t *)p->hsvc_private)
refcnt++;
}
/*
* Dump registered clients information
*/
static void
hsvc_dump(void)
{
hsvc_info_t *p;
prom_printf("hsvc_dump: hsvc_groups: %p hsvc_avail: %p\n",
(void *)hsvc_groups, (void *)hsvc_avail);
prom_printf(" hsvcp: %p (0x%lx 0x%lx 0x%lx) ref: %ld clients: "
p = (hsvc_info_t *)p->hsvc_private) {
prom_printf(" client %p (0x%lx 0x%lx 0x%lx '%s') "
"private: %p\n", (void *)p, p->hsvc_group,
p->hsvc_private);
}
}
}
#endif /* DEBUG */
/*
* Allocate a buffer to cache API group information. Note that we
* allocate a buffer from reserved pool early on, before kmem_xxx
* interface becomes available.
*/
static hsvc_t *
hsvc_alloc(void)
{
if (hsvc_avail != NULL) {
hsvcp = hsvc_avail;
} else if (kmem_ready) {
("hsvc_alloc: hsvc_avail: %p kmem_zalloc hsvcp: %p\n",
(void *)hsvc_avail, (void *)hsvcp));
} else
return (hsvcp);
}
static void
{
if (hsvcp >= hsvc_resv_bufs &&
hsvc_avail = hsvcp;
} else {
("hsvc_free: hsvc_avail: %p kmem_free hsvcp: %p\n",
(void *)hsvc_avail, (void *)hsvcp));
}
}
/*
* Link client on the specified hsvc's client list and
* bump the reference count.
*/
static void
{
}
/*
* Unlink a client from the specified hsvc's client list and
* decrement the reference count, if found.
*
* Return 0 if client unlinked. Otherwise return -1.
*/
static int
{
int status = 0;
if (p != hsvcinfop)
continue;
p->hsvc_private = NULL;
break;
}
if (p == NULL)
status = -1;
return (status);
}
/*
*/
int
{
int status = 0;
("hsvc_register %p (0x%lx 0x%lx 0x%lx ID %s)\n", (void *)hsvcinfop,
return (EINVAL);
/*
* Make sure that the hsvcinfop is new (i.e. not already registered).
*/
return (EINVAL);
}
/*
* Search for the specified api_group
*/
break;
if (hsvcp) {
/*
* If major number mismatch, then return ENOTSUP.
* Otherwise return currently negotiated minor
* and the following status:
* ENOTSUP requested minor < current minor
* OK requested minor >= current minor
*/
/*
* Client requested a lower minor number than
* currently in use.
*/
} else {
/*
* Client requested a minor number same or higher
* than the one in use. Set supported minor number
* and link the client on hsvc client linked list.
*/
}
} else {
/*
* This service group has not been negotiated yet.
* number.
*
* If not enough memory to cache this information, then
* return EAGAIN so that the caller can try again later.
* Otherwise, process OBP CIF results as follows:
*
* H_BADTRAP OBP CIF interface is not supported.
* If not a pre-versioning group, then
* return EINVAL, indicating unsupported
* API group. Otherwise, mimic default
* behavior (i.e. support only major=1).
*
* H_EOK Negotiation was successful. Cache
* limiting the minor number to the
* requested value.
*
* H_EINVAL Invalid group. Return EINVAL
*
* H_ENOTSUPPORTED Unsupported major number. Return
* ENOTSUP.
*
* H_EBUSY Return EAGAIN.
*
* H_EWOULDBLOCK Return EAGAIN.
*/
hsvcp = hsvc_alloc();
} else {
("prom_set_sun4v_api_ver: 0x%lx 0x%lx, 0x%lx "
" hvstat: 0x%lx sup_minor: 0x%lx\n", api_group,
switch (hvstat) {
case H_EBADTRAP:
/*
* Older firmware does not support OBP CIF
* interface. If it's a pre-versioning group,
* then assume that the firmware supports
* only major=1 and minor=0.
*/
if (!pre_versioning_group(api_group)) {
break;
} else if (major != 1) {
break;
}
/*
* It's a preversioning group. Default minor
* value to 0.
*/
*supported_minor = 0;
/*FALLTHROUGH*/
case H_EOK:
/*
* Limit supported minor number to the
* requested value and cache the new
* API group information.
*/
if (*supported_minor > minor)
*supported_minor = minor;
hsvc_groups = hsvcp;
/*
* Link the caller on the client linked list.
*/
break;
case H_ENOTSUPPORTED:
break;
case H_EBUSY:
case H_EWOULDBLOCK:
break;
case H_EINVAL:
default:
break;
}
}
/*
* Deallocate entry if not used
*/
if (status != 0)
}
("hsvc_register(%p) status; %d sup_minor: 0x%lx\n",
return (status);
}
/*
* Unregister an API group usage
*/
int
{
int status = 0;
return (EINVAL);
("hsvc_unregister %p (0x%lx 0x%lx 0x%lx ID %s)\n",
/*
* Search for the matching entry and return EINVAL if no match found.
* Otherwise, remove it from our list and unregister the API
* group if this was the last reference to that API group.
*/
continue;
/*
* Search client list for a matching hsvcinfop entry
* and unlink it and decrement refcnt, if found.
*/
/* client not registered */
break;
}
/*
* Client has been unlinked. If this was the last
* reference, unregister API group via OBP CIF
* interface.
*/
(" prom unreg group: 0x%lx hvstat: 0x%lx\n",
/*
* Note that the call to unnegotiate an API group
* may fail if anyone, including OBP, is using
* those services. However, the caller is done
* with this API group and should be allowed to
* unregister regardless of the outcome.
*/
}
break;
}
return (status);
}
/*
*/
int
{
int status = 0;
/*
* Check if the specified api_group is already in use.
* Otherwise, call OBP CIF interface to get the currently
*/
break;
if (hsvcp) {
} else {
switch (hvstat) {
case H_EBADTRAP:
/*
* Older firmware does not support OBP CIF
* interface. If it's a pre-versioning group,
* Otherwise, return EINVAL.
*/
if (pre_versioning_group(api_group)) {
*majorp = 1;
*minorp = 0;
} else
break;
case H_EINVAL:
default:
break;
}
}
("hsvc_version(0x%lx) status: %d major: 0x%lx minor: 0x%lx\n",
return (status);
}
/*
* Initialize framework data structures
*/
void
hsvc_init(void)
{
int i;
/*
* Initialize global data structures
*/
hsvc_groups = NULL;
hsvc_avail = NULL;
/*
* Setup initial free list
*/
for (i = 0, hsvcp = &hsvc_resv_bufs[0];
i < HSVC_RESV_BUFS_MAX; i++, hsvcp++)
}
/*
* Hypervisor services to be negotiated at boot time.
*
* Note that the kernel needs to negotiate the HSVC_GROUP_SUN4V
* API group first, before doing any other negotiation. Also, it
* uses hypervisor services belonging to the HSVC_GROUP_CORE API
* group only for itself.
*
* Note that the HSVC_GROUP_DIAG is negotiated on behalf of
*/
typedef struct hsvc_info_unix_s {
int required;
};
/*
* Initialize framework and register hypervisor services to be used
* by the kernel.
*/
void
{
int i, status;
/*
* Initialize framework
*/
hsvc_init();
/*
* Negotiate versioning for required groups
*/
i++, hsvcinfop++) {
"services - group: 0x%lx major: 0x%lx minor: 0x%lx"
}
}
HSVC_DUMP();
}