205 lines · 4.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* hvapi.c: Hypervisor API management. * * Copyright (C) 2007 David S. Miller <davem@davemloft.net> */#include <linux/kernel.h>#include <linux/export.h>#include <linux/init.h> #include <asm/hypervisor.h>#include <asm/oplib.h> /* If the hypervisor indicates that the API setting * calls are unsupported, by returning HV_EBADTRAP or * HV_ENOTSUPPORTED, we assume that API groups with the * PRE_API flag set are major 1 minor 0. */struct api_info {	unsigned long group;	unsigned long major;	unsigned long minor;	unsigned int refcnt;	unsigned int flags;#define FLAG_PRE_API		0x00000001}; static struct api_info api_table[] = {	{ .group = HV_GRP_SUN4V,	.flags = FLAG_PRE_API	},	{ .group = HV_GRP_CORE,		.flags = FLAG_PRE_API	},	{ .group = HV_GRP_INTR,					},	{ .group = HV_GRP_SOFT_STATE,				},	{ .group = HV_GRP_TM,					},	{ .group = HV_GRP_PCI,		.flags = FLAG_PRE_API	},	{ .group = HV_GRP_LDOM,					},	{ .group = HV_GRP_SVC_CHAN,	.flags = FLAG_PRE_API	},	{ .group = HV_GRP_NCS,		.flags = FLAG_PRE_API	},	{ .group = HV_GRP_RNG,					},	{ .group = HV_GRP_PBOOT,				},	{ .group = HV_GRP_TPM,					},	{ .group = HV_GRP_SDIO,					},	{ .group = HV_GRP_SDIO_ERR,				},	{ .group = HV_GRP_REBOOT_DATA,				},	{ .group = HV_GRP_ATU,		.flags = FLAG_PRE_API	},	{ .group = HV_GRP_DAX,					},	{ .group = HV_GRP_NIAG_PERF,	.flags = FLAG_PRE_API	},	{ .group = HV_GRP_FIRE_PERF,				},	{ .group = HV_GRP_N2_CPU,				},	{ .group = HV_GRP_NIU,					},	{ .group = HV_GRP_VF_CPU,				},	{ .group = HV_GRP_KT_CPU,				},	{ .group = HV_GRP_VT_CPU,				},	{ .group = HV_GRP_T5_CPU,				},	{ .group = HV_GRP_DIAG,		.flags = FLAG_PRE_API	},	{ .group = HV_GRP_M7_PERF,				},}; static DEFINE_SPINLOCK(hvapi_lock); static struct api_info *__get_info(unsigned long group){	int i; 	for (i = 0; i < ARRAY_SIZE(api_table); i++) {		if (api_table[i].group == group)			return &api_table[i];	}	return NULL;} static void __get_ref(struct api_info *p){	p->refcnt++;} static void __put_ref(struct api_info *p){	if (--p->refcnt == 0) {		unsigned long ignore; 		sun4v_set_version(p->group, 0, 0, &ignore);		p->major = p->minor = 0;	}} /* Register a hypervisor API specification.  It indicates the * API group and desired major+minor. * * If an existing API registration exists '0' (success) will * be returned if it is compatible with the one being registered. * Otherwise a negative error code will be returned. * * Otherwise an attempt will be made to negotiate the requested * API group/major/minor with the hypervisor, and errors returned * if that does not succeed. */int sun4v_hvapi_register(unsigned long group, unsigned long major,			 unsigned long *minor){	struct api_info *p;	unsigned long flags;	int ret; 	spin_lock_irqsave(&hvapi_lock, flags);	p = __get_info(group);	ret = -EINVAL;	if (p) {		if (p->refcnt) {			ret = -EINVAL;			if (p->major == major) {				*minor = p->minor;				ret = 0;			}		} else {			unsigned long actual_minor;			unsigned long hv_ret; 			hv_ret = sun4v_set_version(group, major, *minor,						   &actual_minor);			ret = -EINVAL;			if (hv_ret == HV_EOK) {				*minor = actual_minor;				p->major = major;				p->minor = actual_minor;				ret = 0;			} else if (hv_ret == HV_EBADTRAP ||				   hv_ret == HV_ENOTSUPPORTED) {				if (p->flags & FLAG_PRE_API) {					if (major == 1) {						p->major = 1;						p->minor = 0;						*minor = 0;						ret = 0;					}				}			}		} 		if (ret == 0)			__get_ref(p);	}	spin_unlock_irqrestore(&hvapi_lock, flags); 	return ret;}EXPORT_SYMBOL(sun4v_hvapi_register); void sun4v_hvapi_unregister(unsigned long group){	struct api_info *p;	unsigned long flags; 	spin_lock_irqsave(&hvapi_lock, flags);	p = __get_info(group);	if (p)		__put_ref(p);	spin_unlock_irqrestore(&hvapi_lock, flags);}EXPORT_SYMBOL(sun4v_hvapi_unregister); int sun4v_hvapi_get(unsigned long group,		    unsigned long *major,		    unsigned long *minor){	struct api_info *p;	unsigned long flags;	int ret; 	spin_lock_irqsave(&hvapi_lock, flags);	ret = -EINVAL;	p = __get_info(group);	if (p && p->refcnt) {		*major = p->major;		*minor = p->minor;		ret = 0;	}	spin_unlock_irqrestore(&hvapi_lock, flags); 	return ret;}EXPORT_SYMBOL(sun4v_hvapi_get); void __init sun4v_hvapi_init(void){	unsigned long group, major, minor; 	group = HV_GRP_SUN4V;	major = 1;	minor = 0;	if (sun4v_hvapi_register(group, major, &minor))		goto bad; 	group = HV_GRP_CORE;	major = 1;	minor = 6;	if (sun4v_hvapi_register(group, major, &minor))		goto bad; 	return; bad:	prom_printf("HVAPI: Cannot register API group "		    "%lx with major(%lu) minor(%lu)\n",		    group, major, minor);	prom_halt();}