1 // SPDX-License-Identifier: GPL-2.0 2 /* hvapi.c: Hypervisor API management. 3 * 4 * Copyright (C) 2007 David S. Miller <davem@davemloft.net> 5 */ 6 #include <linux/kernel.h> 7 #include <linux/export.h> 8 #include <linux/init.h> 9 10 #include <asm/hypervisor.h> 11 #include <asm/oplib.h> 12 13 /* If the hypervisor indicates that the API setting 14 * calls are unsupported, by returning HV_EBADTRAP or 15 * HV_ENOTSUPPORTED, we assume that API groups with the 16 * PRE_API flag set are major 1 minor 0. 17 */ 18 struct api_info { 19 unsigned long group; 20 unsigned long major; 21 unsigned long minor; 22 unsigned int refcnt; 23 unsigned int flags; 24 #define FLAG_PRE_API 0x00000001 25 }; 26 27 static struct api_info api_table[] = { 28 { .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API }, 29 { .group = HV_GRP_CORE, .flags = FLAG_PRE_API }, 30 { .group = HV_GRP_INTR, }, 31 { .group = HV_GRP_SOFT_STATE, }, 32 { .group = HV_GRP_TM, }, 33 { .group = HV_GRP_PCI, .flags = FLAG_PRE_API }, 34 { .group = HV_GRP_LDOM, }, 35 { .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API }, 36 { .group = HV_GRP_NCS, .flags = FLAG_PRE_API }, 37 { .group = HV_GRP_RNG, }, 38 { .group = HV_GRP_PBOOT, }, 39 { .group = HV_GRP_TPM, }, 40 { .group = HV_GRP_SDIO, }, 41 { .group = HV_GRP_SDIO_ERR, }, 42 { .group = HV_GRP_REBOOT_DATA, }, 43 { .group = HV_GRP_ATU, .flags = FLAG_PRE_API }, 44 { .group = HV_GRP_DAX, }, 45 { .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API }, 46 { .group = HV_GRP_FIRE_PERF, }, 47 { .group = HV_GRP_N2_CPU, }, 48 { .group = HV_GRP_NIU, }, 49 { .group = HV_GRP_VF_CPU, }, 50 { .group = HV_GRP_KT_CPU, }, 51 { .group = HV_GRP_VT_CPU, }, 52 { .group = HV_GRP_T5_CPU, }, 53 { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API }, 54 { .group = HV_GRP_M7_PERF, }, 55 }; 56 57 static DEFINE_SPINLOCK(hvapi_lock); 58 59 static struct api_info *__get_info(unsigned long group) 60 { 61 int i; 62 63 for (i = 0; i < ARRAY_SIZE(api_table); i++) { 64 if (api_table[i].group == group) 65 return &api_table[i]; 66 } 67 return NULL; 68 } 69 70 static void __get_ref(struct api_info *p) 71 { 72 p->refcnt++; 73 } 74 75 static void __put_ref(struct api_info *p) 76 { 77 if (--p->refcnt == 0) { 78 unsigned long ignore; 79 80 sun4v_set_version(p->group, 0, 0, &ignore); 81 p->major = p->minor = 0; 82 } 83 } 84 85 /* Register a hypervisor API specification. It indicates the 86 * API group and desired major+minor. 87 * 88 * If an existing API registration exists '0' (success) will 89 * be returned if it is compatible with the one being registered. 90 * Otherwise a negative error code will be returned. 91 * 92 * Otherwise an attempt will be made to negotiate the requested 93 * API group/major/minor with the hypervisor, and errors returned 94 * if that does not succeed. 95 */ 96 int sun4v_hvapi_register(unsigned long group, unsigned long major, 97 unsigned long *minor) 98 { 99 struct api_info *p; 100 unsigned long flags; 101 int ret; 102 103 spin_lock_irqsave(&hvapi_lock, flags); 104 p = __get_info(group); 105 ret = -EINVAL; 106 if (p) { 107 if (p->refcnt) { 108 ret = -EINVAL; 109 if (p->major == major) { 110 *minor = p->minor; 111 ret = 0; 112 } 113 } else { 114 unsigned long actual_minor; 115 unsigned long hv_ret; 116 117 hv_ret = sun4v_set_version(group, major, *minor, 118 &actual_minor); 119 ret = -EINVAL; 120 if (hv_ret == HV_EOK) { 121 *minor = actual_minor; 122 p->major = major; 123 p->minor = actual_minor; 124 ret = 0; 125 } else if (hv_ret == HV_EBADTRAP || 126 hv_ret == HV_ENOTSUPPORTED) { 127 if (p->flags & FLAG_PRE_API) { 128 if (major == 1) { 129 p->major = 1; 130 p->minor = 0; 131 *minor = 0; 132 ret = 0; 133 } 134 } 135 } 136 } 137 138 if (ret == 0) 139 __get_ref(p); 140 } 141 spin_unlock_irqrestore(&hvapi_lock, flags); 142 143 return ret; 144 } 145 EXPORT_SYMBOL(sun4v_hvapi_register); 146 147 void sun4v_hvapi_unregister(unsigned long group) 148 { 149 struct api_info *p; 150 unsigned long flags; 151 152 spin_lock_irqsave(&hvapi_lock, flags); 153 p = __get_info(group); 154 if (p) 155 __put_ref(p); 156 spin_unlock_irqrestore(&hvapi_lock, flags); 157 } 158 EXPORT_SYMBOL(sun4v_hvapi_unregister); 159 160 int sun4v_hvapi_get(unsigned long group, 161 unsigned long *major, 162 unsigned long *minor) 163 { 164 struct api_info *p; 165 unsigned long flags; 166 int ret; 167 168 spin_lock_irqsave(&hvapi_lock, flags); 169 ret = -EINVAL; 170 p = __get_info(group); 171 if (p && p->refcnt) { 172 *major = p->major; 173 *minor = p->minor; 174 ret = 0; 175 } 176 spin_unlock_irqrestore(&hvapi_lock, flags); 177 178 return ret; 179 } 180 EXPORT_SYMBOL(sun4v_hvapi_get); 181 182 void __init sun4v_hvapi_init(void) 183 { 184 unsigned long group, major, minor; 185 186 group = HV_GRP_SUN4V; 187 major = 1; 188 minor = 0; 189 if (sun4v_hvapi_register(group, major, &minor)) 190 goto bad; 191 192 group = HV_GRP_CORE; 193 major = 1; 194 minor = 6; 195 if (sun4v_hvapi_register(group, major, &minor)) 196 goto bad; 197 198 return; 199 200 bad: 201 prom_printf("HVAPI: Cannot register API group " 202 "%lx with major(%lu) minor(%lu)\n", 203 group, major, minor); 204 prom_halt(); 205 } 206