1 /* 2 * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24 #include <linux/acpi.h> 25 #include "i915_drv.h" 26 #include "gvt.h" 27 28 static int init_vgpu_opregion(struct intel_vgpu *vgpu, u32 gpa) 29 { 30 void __iomem *host_va = vgpu->gvt->opregion.opregion_va; 31 u8 *buf; 32 int i; 33 34 if (WARN((vgpu_opregion(vgpu)->va), 35 "vgpu%d: opregion has been initialized already.\n", 36 vgpu->id)) 37 return -EINVAL; 38 39 vgpu_opregion(vgpu)->va = (void *)__get_free_pages(GFP_KERNEL | 40 __GFP_ZERO, 41 get_order(INTEL_GVT_OPREGION_SIZE)); 42 43 if (!vgpu_opregion(vgpu)->va) 44 return -ENOMEM; 45 46 memcpy_fromio(vgpu_opregion(vgpu)->va, host_va, 47 INTEL_GVT_OPREGION_SIZE); 48 49 for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) 50 vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i; 51 52 /* for unknown reason, the value in LID field is incorrect 53 * which block the windows guest, so workaround it by force 54 * setting it to "OPEN" 55 */ 56 buf = (u8 *)vgpu_opregion(vgpu)->va; 57 buf[INTEL_GVT_OPREGION_CLID] = 0x3; 58 59 return 0; 60 } 61 62 static int map_vgpu_opregion(struct intel_vgpu *vgpu, bool map) 63 { 64 u64 mfn; 65 int i, ret; 66 67 for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) { 68 mfn = intel_gvt_hypervisor_virt_to_mfn(vgpu_opregion(vgpu)->va 69 + i * PAGE_SIZE); 70 if (mfn == INTEL_GVT_INVALID_ADDR) { 71 gvt_err("fail to get MFN from VA\n"); 72 return -EINVAL; 73 } 74 ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, 75 vgpu_opregion(vgpu)->gfn[i], 76 mfn, 1, map); 77 if (ret) { 78 gvt_err("fail to map GFN to MFN, errno: %d\n", ret); 79 return ret; 80 } 81 } 82 return 0; 83 } 84 85 /** 86 * intel_vgpu_clean_opregion - clean the stuff used to emulate opregion 87 * @vgpu: a vGPU 88 * 89 */ 90 void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu) 91 { 92 gvt_dbg_core("vgpu%d: clean vgpu opregion\n", vgpu->id); 93 94 if (!vgpu_opregion(vgpu)->va) 95 return; 96 97 if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) { 98 map_vgpu_opregion(vgpu, false); 99 free_pages((unsigned long)vgpu_opregion(vgpu)->va, 100 get_order(INTEL_GVT_OPREGION_SIZE)); 101 102 vgpu_opregion(vgpu)->va = NULL; 103 } 104 } 105 106 /** 107 * intel_vgpu_init_opregion - initialize the stuff used to emulate opregion 108 * @vgpu: a vGPU 109 * @gpa: guest physical address of opregion 110 * 111 * Returns: 112 * Zero on success, negative error code if failed. 113 */ 114 int intel_vgpu_init_opregion(struct intel_vgpu *vgpu, u32 gpa) 115 { 116 int ret; 117 118 gvt_dbg_core("vgpu%d: init vgpu opregion\n", vgpu->id); 119 120 if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) { 121 gvt_dbg_core("emulate opregion from kernel\n"); 122 123 ret = init_vgpu_opregion(vgpu, gpa); 124 if (ret) 125 return ret; 126 127 ret = map_vgpu_opregion(vgpu, true); 128 if (ret) 129 return ret; 130 } 131 132 return 0; 133 } 134 135 /** 136 * intel_gvt_clean_opregion - clean host opergion related stuffs 137 * @gvt: a GVT device 138 * 139 */ 140 void intel_gvt_clean_opregion(struct intel_gvt *gvt) 141 { 142 memunmap(gvt->opregion.opregion_va); 143 gvt->opregion.opregion_va = NULL; 144 } 145 146 /** 147 * intel_gvt_init_opregion - initialize host opergion related stuffs 148 * @gvt: a GVT device 149 * 150 * Returns: 151 * Zero on success, negative error code if failed. 152 */ 153 int intel_gvt_init_opregion(struct intel_gvt *gvt) 154 { 155 gvt_dbg_core("init host opregion\n"); 156 157 pci_read_config_dword(gvt->dev_priv->drm.pdev, INTEL_GVT_PCI_OPREGION, 158 &gvt->opregion.opregion_pa); 159 160 gvt->opregion.opregion_va = memremap(gvt->opregion.opregion_pa, 161 INTEL_GVT_OPREGION_SIZE, MEMREMAP_WB); 162 if (!gvt->opregion.opregion_va) { 163 gvt_err("fail to map host opregion\n"); 164 return -EFAULT; 165 } 166 return 0; 167 } 168 169 #define GVT_OPREGION_FUNC(scic) \ 170 ({ \ 171 u32 __ret; \ 172 __ret = (scic & OPREGION_SCIC_FUNC_MASK) >> \ 173 OPREGION_SCIC_FUNC_SHIFT; \ 174 __ret; \ 175 }) 176 177 #define GVT_OPREGION_SUBFUNC(scic) \ 178 ({ \ 179 u32 __ret; \ 180 __ret = (scic & OPREGION_SCIC_SUBFUNC_MASK) >> \ 181 OPREGION_SCIC_SUBFUNC_SHIFT; \ 182 __ret; \ 183 }) 184 185 static const char *opregion_func_name(u32 func) 186 { 187 const char *name = NULL; 188 189 switch (func) { 190 case 0 ... 3: 191 case 5: 192 case 7 ... 15: 193 name = "Reserved"; 194 break; 195 196 case 4: 197 name = "Get BIOS Data"; 198 break; 199 200 case 6: 201 name = "System BIOS Callbacks"; 202 break; 203 204 default: 205 name = "Unknown"; 206 break; 207 } 208 return name; 209 } 210 211 static const char *opregion_subfunc_name(u32 subfunc) 212 { 213 const char *name = NULL; 214 215 switch (subfunc) { 216 case 0: 217 name = "Supported Calls"; 218 break; 219 220 case 1: 221 name = "Requested Callbacks"; 222 break; 223 224 case 2 ... 3: 225 case 8 ... 9: 226 name = "Reserved"; 227 break; 228 229 case 5: 230 name = "Boot Display"; 231 break; 232 233 case 6: 234 name = "TV-Standard/Video-Connector"; 235 break; 236 237 case 7: 238 name = "Internal Graphics"; 239 break; 240 241 case 10: 242 name = "Spread Spectrum Clocks"; 243 break; 244 245 case 11: 246 name = "Get AKSV"; 247 break; 248 249 default: 250 name = "Unknown"; 251 break; 252 } 253 return name; 254 }; 255 256 static bool querying_capabilities(u32 scic) 257 { 258 u32 func, subfunc; 259 260 func = GVT_OPREGION_FUNC(scic); 261 subfunc = GVT_OPREGION_SUBFUNC(scic); 262 263 if ((func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA && 264 subfunc == INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS) 265 || (func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA && 266 subfunc == INTEL_GVT_OPREGION_SCIC_SF_REQEUSTEDCALLBACKS) 267 || (func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSCALLBACKS && 268 subfunc == INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS)) { 269 return true; 270 } 271 return false; 272 } 273 274 /** 275 * intel_vgpu_emulate_opregion_request - emulating OpRegion request 276 * @vgpu: a vGPU 277 * @swsci: SWSCI request 278 * 279 * Returns: 280 * Zero on success, negative error code if failed 281 */ 282 int intel_vgpu_emulate_opregion_request(struct intel_vgpu *vgpu, u32 swsci) 283 { 284 u32 *scic, *parm; 285 u32 func, subfunc; 286 287 scic = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_SCIC; 288 parm = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_PARM; 289 290 if (!(swsci & SWSCI_SCI_SELECT)) { 291 gvt_err("vgpu%d: requesting SMI service\n", vgpu->id); 292 return 0; 293 } 294 /* ignore non 0->1 trasitions */ 295 if ((vgpu_cfg_space(vgpu)[INTEL_GVT_PCI_SWSCI] 296 & SWSCI_SCI_TRIGGER) || 297 !(swsci & SWSCI_SCI_TRIGGER)) { 298 return 0; 299 } 300 301 func = GVT_OPREGION_FUNC(*scic); 302 subfunc = GVT_OPREGION_SUBFUNC(*scic); 303 if (!querying_capabilities(*scic)) { 304 gvt_err("vgpu%d: requesting runtime service: func \"%s\"," 305 " subfunc \"%s\"\n", 306 vgpu->id, 307 opregion_func_name(func), 308 opregion_subfunc_name(subfunc)); 309 /* 310 * emulate exit status of function call, '0' means 311 * "failure, generic, unsupported or unknown cause" 312 */ 313 *scic &= ~OPREGION_SCIC_EXIT_MASK; 314 return 0; 315 } 316 317 *scic = 0; 318 *parm = 0; 319 return 0; 320 } 321