18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 138c2ecf20Sopenharmony_ci * Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 208c2ecf20Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 218c2ecf20Sopenharmony_ci * SOFTWARE. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/acpi.h> 258c2ecf20Sopenharmony_ci#include "i915_drv.h" 268c2ecf20Sopenharmony_ci#include "gvt.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Note: Only for GVT-g virtual VBT generation, other usage must 308c2ecf20Sopenharmony_ci * not do like this. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#define _INTEL_BIOS_PRIVATE 338c2ecf20Sopenharmony_ci#include "display/intel_vbt_defs.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define OPREGION_SIGNATURE "IntelGraphicsMem" 368c2ecf20Sopenharmony_ci#define MBOX_VBT (1<<3) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* device handle */ 398c2ecf20Sopenharmony_ci#define DEVICE_TYPE_CRT 0x01 408c2ecf20Sopenharmony_ci#define DEVICE_TYPE_EFP1 0x04 418c2ecf20Sopenharmony_ci#define DEVICE_TYPE_EFP2 0x40 428c2ecf20Sopenharmony_ci#define DEVICE_TYPE_EFP3 0x20 438c2ecf20Sopenharmony_ci#define DEVICE_TYPE_EFP4 0x10 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct opregion_header { 468c2ecf20Sopenharmony_ci u8 signature[16]; 478c2ecf20Sopenharmony_ci u32 size; 488c2ecf20Sopenharmony_ci u32 opregion_ver; 498c2ecf20Sopenharmony_ci u8 bios_ver[32]; 508c2ecf20Sopenharmony_ci u8 vbios_ver[16]; 518c2ecf20Sopenharmony_ci u8 driver_ver[16]; 528c2ecf20Sopenharmony_ci u32 mboxes; 538c2ecf20Sopenharmony_ci u32 driver_model; 548c2ecf20Sopenharmony_ci u32 pcon; 558c2ecf20Sopenharmony_ci u8 dver[32]; 568c2ecf20Sopenharmony_ci u8 rsvd[124]; 578c2ecf20Sopenharmony_ci} __packed; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct bdb_data_header { 608c2ecf20Sopenharmony_ci u8 id; 618c2ecf20Sopenharmony_ci u16 size; /* data size */ 628c2ecf20Sopenharmony_ci} __packed; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* For supporting windows guest with opregion, here hardcode the emulated 658c2ecf20Sopenharmony_ci * bdb header version as '186', and the corresponding child_device_config 668c2ecf20Sopenharmony_ci * length should be '33' but not '38'. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistruct efp_child_device_config { 698c2ecf20Sopenharmony_ci u16 handle; 708c2ecf20Sopenharmony_ci u16 device_type; 718c2ecf20Sopenharmony_ci u16 device_class; 728c2ecf20Sopenharmony_ci u8 i2c_speed; 738c2ecf20Sopenharmony_ci u8 dp_onboard_redriver; /* 158 */ 748c2ecf20Sopenharmony_ci u8 dp_ondock_redriver; /* 158 */ 758c2ecf20Sopenharmony_ci u8 hdmi_level_shifter_value:4; /* 169 */ 768c2ecf20Sopenharmony_ci u8 hdmi_max_data_rate:4; /* 204 */ 778c2ecf20Sopenharmony_ci u16 dtd_buf_ptr; /* 161 */ 788c2ecf20Sopenharmony_ci u8 edidless_efp:1; /* 161 */ 798c2ecf20Sopenharmony_ci u8 compression_enable:1; /* 198 */ 808c2ecf20Sopenharmony_ci u8 compression_method:1; /* 198 */ 818c2ecf20Sopenharmony_ci u8 ganged_edp:1; /* 202 */ 828c2ecf20Sopenharmony_ci u8 skip0:4; 838c2ecf20Sopenharmony_ci u8 compression_structure_index:4; /* 198 */ 848c2ecf20Sopenharmony_ci u8 skip1:4; 858c2ecf20Sopenharmony_ci u8 slave_port; /* 202 */ 868c2ecf20Sopenharmony_ci u8 skip2; 878c2ecf20Sopenharmony_ci u8 dvo_port; 888c2ecf20Sopenharmony_ci u8 i2c_pin; /* for add-in card */ 898c2ecf20Sopenharmony_ci u8 slave_addr; /* for add-in card */ 908c2ecf20Sopenharmony_ci u8 ddc_pin; 918c2ecf20Sopenharmony_ci u16 edid_ptr; 928c2ecf20Sopenharmony_ci u8 dvo_config; 938c2ecf20Sopenharmony_ci u8 efp_docked_port:1; /* 158 */ 948c2ecf20Sopenharmony_ci u8 lane_reversal:1; /* 184 */ 958c2ecf20Sopenharmony_ci u8 onboard_lspcon:1; /* 192 */ 968c2ecf20Sopenharmony_ci u8 iboost_enable:1; /* 196 */ 978c2ecf20Sopenharmony_ci u8 hpd_invert:1; /* BXT 196 */ 988c2ecf20Sopenharmony_ci u8 slip3:3; 998c2ecf20Sopenharmony_ci u8 hdmi_compat:1; 1008c2ecf20Sopenharmony_ci u8 dp_compat:1; 1018c2ecf20Sopenharmony_ci u8 tmds_compat:1; 1028c2ecf20Sopenharmony_ci u8 skip4:5; 1038c2ecf20Sopenharmony_ci u8 aux_channel; 1048c2ecf20Sopenharmony_ci u8 dongle_detect; 1058c2ecf20Sopenharmony_ci u8 pipe_cap:2; 1068c2ecf20Sopenharmony_ci u8 sdvo_stall:1; /* 158 */ 1078c2ecf20Sopenharmony_ci u8 hpd_status:2; 1088c2ecf20Sopenharmony_ci u8 integrated_encoder:1; 1098c2ecf20Sopenharmony_ci u8 skip5:2; 1108c2ecf20Sopenharmony_ci u8 dvo_wiring; 1118c2ecf20Sopenharmony_ci u8 mipi_bridge_type; /* 171 */ 1128c2ecf20Sopenharmony_ci u16 device_class_ext; 1138c2ecf20Sopenharmony_ci u8 dvo_function; 1148c2ecf20Sopenharmony_ci} __packed; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistruct vbt { 1178c2ecf20Sopenharmony_ci /* header->bdb_offset point to bdb_header offset */ 1188c2ecf20Sopenharmony_ci struct vbt_header header; 1198c2ecf20Sopenharmony_ci struct bdb_header bdb_header; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci struct bdb_data_header general_features_header; 1228c2ecf20Sopenharmony_ci struct bdb_general_features general_features; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci struct bdb_data_header general_definitions_header; 1258c2ecf20Sopenharmony_ci struct bdb_general_definitions general_definitions; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci struct efp_child_device_config child0; 1288c2ecf20Sopenharmony_ci struct efp_child_device_config child1; 1298c2ecf20Sopenharmony_ci struct efp_child_device_config child2; 1308c2ecf20Sopenharmony_ci struct efp_child_device_config child3; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci struct bdb_data_header driver_features_header; 1338c2ecf20Sopenharmony_ci struct bdb_driver_features driver_features; 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void virt_vbt_generation(struct vbt *v) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int num_child; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci memset(v, 0, sizeof(struct vbt)); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci v->header.signature[0] = '$'; 1438c2ecf20Sopenharmony_ci v->header.signature[1] = 'V'; 1448c2ecf20Sopenharmony_ci v->header.signature[2] = 'B'; 1458c2ecf20Sopenharmony_ci v->header.signature[3] = 'T'; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* there's features depending on version! */ 1488c2ecf20Sopenharmony_ci v->header.version = 155; 1498c2ecf20Sopenharmony_ci v->header.header_size = sizeof(v->header); 1508c2ecf20Sopenharmony_ci v->header.vbt_size = sizeof(struct vbt); 1518c2ecf20Sopenharmony_ci v->header.bdb_offset = offsetof(struct vbt, bdb_header); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci strcpy(&v->bdb_header.signature[0], "BIOS_DATA_BLOCK"); 1548c2ecf20Sopenharmony_ci v->bdb_header.version = 186; /* child_dev_size = 33 */ 1558c2ecf20Sopenharmony_ci v->bdb_header.header_size = sizeof(v->bdb_header); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci v->bdb_header.bdb_size = sizeof(struct vbt) - sizeof(struct vbt_header); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* general features */ 1608c2ecf20Sopenharmony_ci v->general_features_header.id = BDB_GENERAL_FEATURES; 1618c2ecf20Sopenharmony_ci v->general_features_header.size = sizeof(struct bdb_general_features); 1628c2ecf20Sopenharmony_ci v->general_features.int_crt_support = 0; 1638c2ecf20Sopenharmony_ci v->general_features.int_tv_support = 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* child device */ 1668c2ecf20Sopenharmony_ci num_child = 4; /* each port has one child */ 1678c2ecf20Sopenharmony_ci v->general_definitions.child_dev_size = 1688c2ecf20Sopenharmony_ci sizeof(struct efp_child_device_config); 1698c2ecf20Sopenharmony_ci v->general_definitions_header.id = BDB_GENERAL_DEFINITIONS; 1708c2ecf20Sopenharmony_ci /* size will include child devices */ 1718c2ecf20Sopenharmony_ci v->general_definitions_header.size = 1728c2ecf20Sopenharmony_ci sizeof(struct bdb_general_definitions) + 1738c2ecf20Sopenharmony_ci num_child * v->general_definitions.child_dev_size; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* portA */ 1768c2ecf20Sopenharmony_ci v->child0.handle = DEVICE_TYPE_EFP1; 1778c2ecf20Sopenharmony_ci v->child0.device_type = DEVICE_TYPE_DP; 1788c2ecf20Sopenharmony_ci v->child0.dvo_port = DVO_PORT_DPA; 1798c2ecf20Sopenharmony_ci v->child0.aux_channel = DP_AUX_A; 1808c2ecf20Sopenharmony_ci v->child0.dp_compat = true; 1818c2ecf20Sopenharmony_ci v->child0.integrated_encoder = true; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* portB */ 1848c2ecf20Sopenharmony_ci v->child1.handle = DEVICE_TYPE_EFP2; 1858c2ecf20Sopenharmony_ci v->child1.device_type = DEVICE_TYPE_DP; 1868c2ecf20Sopenharmony_ci v->child1.dvo_port = DVO_PORT_DPB; 1878c2ecf20Sopenharmony_ci v->child1.aux_channel = DP_AUX_B; 1888c2ecf20Sopenharmony_ci v->child1.dp_compat = true; 1898c2ecf20Sopenharmony_ci v->child1.integrated_encoder = true; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* portC */ 1928c2ecf20Sopenharmony_ci v->child2.handle = DEVICE_TYPE_EFP3; 1938c2ecf20Sopenharmony_ci v->child2.device_type = DEVICE_TYPE_DP; 1948c2ecf20Sopenharmony_ci v->child2.dvo_port = DVO_PORT_DPC; 1958c2ecf20Sopenharmony_ci v->child2.aux_channel = DP_AUX_C; 1968c2ecf20Sopenharmony_ci v->child2.dp_compat = true; 1978c2ecf20Sopenharmony_ci v->child2.integrated_encoder = true; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* portD */ 2008c2ecf20Sopenharmony_ci v->child3.handle = DEVICE_TYPE_EFP4; 2018c2ecf20Sopenharmony_ci v->child3.device_type = DEVICE_TYPE_DP; 2028c2ecf20Sopenharmony_ci v->child3.dvo_port = DVO_PORT_DPD; 2038c2ecf20Sopenharmony_ci v->child3.aux_channel = DP_AUX_D; 2048c2ecf20Sopenharmony_ci v->child3.dp_compat = true; 2058c2ecf20Sopenharmony_ci v->child3.integrated_encoder = true; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* driver features */ 2088c2ecf20Sopenharmony_ci v->driver_features_header.id = BDB_DRIVER_FEATURES; 2098c2ecf20Sopenharmony_ci v->driver_features_header.size = sizeof(struct bdb_driver_features); 2108c2ecf20Sopenharmony_ci v->driver_features.lvds_config = BDB_DRIVER_FEATURE_NO_LVDS; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/** 2148c2ecf20Sopenharmony_ci * intel_vgpu_init_opregion - initialize the stuff used to emulate opregion 2158c2ecf20Sopenharmony_ci * @vgpu: a vGPU 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * Returns: 2188c2ecf20Sopenharmony_ci * Zero on success, negative error code if failed. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ciint intel_vgpu_init_opregion(struct intel_vgpu *vgpu) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u8 *buf; 2238c2ecf20Sopenharmony_ci struct opregion_header *header; 2248c2ecf20Sopenharmony_ci struct vbt v; 2258c2ecf20Sopenharmony_ci const char opregion_signature[16] = OPREGION_SIGNATURE; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci gvt_dbg_core("init vgpu%d opregion\n", vgpu->id); 2288c2ecf20Sopenharmony_ci vgpu_opregion(vgpu)->va = (void *)__get_free_pages(GFP_KERNEL | 2298c2ecf20Sopenharmony_ci __GFP_ZERO, 2308c2ecf20Sopenharmony_ci get_order(INTEL_GVT_OPREGION_SIZE)); 2318c2ecf20Sopenharmony_ci if (!vgpu_opregion(vgpu)->va) { 2328c2ecf20Sopenharmony_ci gvt_err("fail to get memory for vgpu virt opregion\n"); 2338c2ecf20Sopenharmony_ci return -ENOMEM; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* emulated opregion with VBT mailbox only */ 2378c2ecf20Sopenharmony_ci buf = (u8 *)vgpu_opregion(vgpu)->va; 2388c2ecf20Sopenharmony_ci header = (struct opregion_header *)buf; 2398c2ecf20Sopenharmony_ci memcpy(header->signature, opregion_signature, 2408c2ecf20Sopenharmony_ci sizeof(opregion_signature)); 2418c2ecf20Sopenharmony_ci header->size = 0x8; 2428c2ecf20Sopenharmony_ci header->opregion_ver = 0x02000000; 2438c2ecf20Sopenharmony_ci header->mboxes = MBOX_VBT; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* for unknown reason, the value in LID field is incorrect 2468c2ecf20Sopenharmony_ci * which block the windows guest, so workaround it by force 2478c2ecf20Sopenharmony_ci * setting it to "OPEN" 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci buf[INTEL_GVT_OPREGION_CLID] = 0x3; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* emulated vbt from virt vbt generation */ 2528c2ecf20Sopenharmony_ci virt_vbt_generation(&v); 2538c2ecf20Sopenharmony_ci memcpy(buf + INTEL_GVT_OPREGION_VBT_OFFSET, &v, sizeof(struct vbt)); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int map_vgpu_opregion(struct intel_vgpu *vgpu, bool map) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci u64 mfn; 2618c2ecf20Sopenharmony_ci int i, ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) { 2648c2ecf20Sopenharmony_ci mfn = intel_gvt_hypervisor_virt_to_mfn(vgpu_opregion(vgpu)->va 2658c2ecf20Sopenharmony_ci + i * PAGE_SIZE); 2668c2ecf20Sopenharmony_ci if (mfn == INTEL_GVT_INVALID_ADDR) { 2678c2ecf20Sopenharmony_ci gvt_vgpu_err("fail to get MFN from VA\n"); 2688c2ecf20Sopenharmony_ci return -EINVAL; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, 2718c2ecf20Sopenharmony_ci vgpu_opregion(vgpu)->gfn[i], 2728c2ecf20Sopenharmony_ci mfn, 1, map); 2738c2ecf20Sopenharmony_ci if (ret) { 2748c2ecf20Sopenharmony_ci gvt_vgpu_err("fail to map GFN to MFN, errno: %d\n", 2758c2ecf20Sopenharmony_ci ret); 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci vgpu_opregion(vgpu)->mapped = map; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/** 2868c2ecf20Sopenharmony_ci * intel_vgpu_opregion_base_write_handler - Opregion base register write handler 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * @vgpu: a vGPU 2898c2ecf20Sopenharmony_ci * @gpa: guest physical address of opregion 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * Returns: 2928c2ecf20Sopenharmony_ci * Zero on success, negative error code if failed. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ciint intel_vgpu_opregion_base_write_handler(struct intel_vgpu *vgpu, u32 gpa) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci int i, ret = 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci gvt_dbg_core("emulate opregion from kernel\n"); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci switch (intel_gvt_host.hypervisor_type) { 3028c2ecf20Sopenharmony_ci case INTEL_GVT_HYPERVISOR_KVM: 3038c2ecf20Sopenharmony_ci for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) 3048c2ecf20Sopenharmony_ci vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci case INTEL_GVT_HYPERVISOR_XEN: 3078c2ecf20Sopenharmony_ci /** 3088c2ecf20Sopenharmony_ci * Wins guest on Xengt will write this register twice: xen 3098c2ecf20Sopenharmony_ci * hvmloader and windows graphic driver. 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci if (vgpu_opregion(vgpu)->mapped) 3128c2ecf20Sopenharmony_ci map_vgpu_opregion(vgpu, false); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) 3158c2ecf20Sopenharmony_ci vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = map_vgpu_opregion(vgpu, true); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci default: 3208c2ecf20Sopenharmony_ci ret = -EINVAL; 3218c2ecf20Sopenharmony_ci gvt_vgpu_err("not supported hypervisor\n"); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return ret; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/** 3288c2ecf20Sopenharmony_ci * intel_vgpu_clean_opregion - clean the stuff used to emulate opregion 3298c2ecf20Sopenharmony_ci * @vgpu: a vGPU 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_civoid intel_vgpu_clean_opregion(struct intel_vgpu *vgpu) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci gvt_dbg_core("vgpu%d: clean vgpu opregion\n", vgpu->id); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!vgpu_opregion(vgpu)->va) 3378c2ecf20Sopenharmony_ci return; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) { 3408c2ecf20Sopenharmony_ci if (vgpu_opregion(vgpu)->mapped) 3418c2ecf20Sopenharmony_ci map_vgpu_opregion(vgpu, false); 3428c2ecf20Sopenharmony_ci } else if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_KVM) { 3438c2ecf20Sopenharmony_ci /* Guest opregion is released by VFIO */ 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci free_pages((unsigned long)vgpu_opregion(vgpu)->va, 3468c2ecf20Sopenharmony_ci get_order(INTEL_GVT_OPREGION_SIZE)); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci vgpu_opregion(vgpu)->va = NULL; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci#define GVT_OPREGION_FUNC(scic) \ 3548c2ecf20Sopenharmony_ci ({ \ 3558c2ecf20Sopenharmony_ci u32 __ret; \ 3568c2ecf20Sopenharmony_ci __ret = (scic & OPREGION_SCIC_FUNC_MASK) >> \ 3578c2ecf20Sopenharmony_ci OPREGION_SCIC_FUNC_SHIFT; \ 3588c2ecf20Sopenharmony_ci __ret; \ 3598c2ecf20Sopenharmony_ci }) 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci#define GVT_OPREGION_SUBFUNC(scic) \ 3628c2ecf20Sopenharmony_ci ({ \ 3638c2ecf20Sopenharmony_ci u32 __ret; \ 3648c2ecf20Sopenharmony_ci __ret = (scic & OPREGION_SCIC_SUBFUNC_MASK) >> \ 3658c2ecf20Sopenharmony_ci OPREGION_SCIC_SUBFUNC_SHIFT; \ 3668c2ecf20Sopenharmony_ci __ret; \ 3678c2ecf20Sopenharmony_ci }) 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic const char *opregion_func_name(u32 func) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci const char *name = NULL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci switch (func) { 3748c2ecf20Sopenharmony_ci case 0 ... 3: 3758c2ecf20Sopenharmony_ci case 5: 3768c2ecf20Sopenharmony_ci case 7 ... 15: 3778c2ecf20Sopenharmony_ci name = "Reserved"; 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci case 4: 3818c2ecf20Sopenharmony_ci name = "Get BIOS Data"; 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci case 6: 3858c2ecf20Sopenharmony_ci name = "System BIOS Callbacks"; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci default: 3898c2ecf20Sopenharmony_ci name = "Unknown"; 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci return name; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const char *opregion_subfunc_name(u32 subfunc) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci const char *name = NULL; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci switch (subfunc) { 4008c2ecf20Sopenharmony_ci case 0: 4018c2ecf20Sopenharmony_ci name = "Supported Calls"; 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci case 1: 4058c2ecf20Sopenharmony_ci name = "Requested Callbacks"; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci case 2 ... 3: 4098c2ecf20Sopenharmony_ci case 8 ... 9: 4108c2ecf20Sopenharmony_ci name = "Reserved"; 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci case 5: 4148c2ecf20Sopenharmony_ci name = "Boot Display"; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci case 6: 4188c2ecf20Sopenharmony_ci name = "TV-Standard/Video-Connector"; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci case 7: 4228c2ecf20Sopenharmony_ci name = "Internal Graphics"; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci case 10: 4268c2ecf20Sopenharmony_ci name = "Spread Spectrum Clocks"; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci case 11: 4308c2ecf20Sopenharmony_ci name = "Get AKSV"; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci name = "Unknown"; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci return name; 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic bool querying_capabilities(u32 scic) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci u32 func, subfunc; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci func = GVT_OPREGION_FUNC(scic); 4458c2ecf20Sopenharmony_ci subfunc = GVT_OPREGION_SUBFUNC(scic); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if ((func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA && 4488c2ecf20Sopenharmony_ci subfunc == INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS) 4498c2ecf20Sopenharmony_ci || (func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA && 4508c2ecf20Sopenharmony_ci subfunc == INTEL_GVT_OPREGION_SCIC_SF_REQEUSTEDCALLBACKS) 4518c2ecf20Sopenharmony_ci || (func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSCALLBACKS && 4528c2ecf20Sopenharmony_ci subfunc == INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS)) { 4538c2ecf20Sopenharmony_ci return true; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci return false; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/** 4598c2ecf20Sopenharmony_ci * intel_vgpu_emulate_opregion_request - emulating OpRegion request 4608c2ecf20Sopenharmony_ci * @vgpu: a vGPU 4618c2ecf20Sopenharmony_ci * @swsci: SWSCI request 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * Returns: 4648c2ecf20Sopenharmony_ci * Zero on success, negative error code if failed 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ciint intel_vgpu_emulate_opregion_request(struct intel_vgpu *vgpu, u32 swsci) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci u32 scic, parm; 4698c2ecf20Sopenharmony_ci u32 func, subfunc; 4708c2ecf20Sopenharmony_ci u64 scic_pa = 0, parm_pa = 0; 4718c2ecf20Sopenharmony_ci int ret; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci switch (intel_gvt_host.hypervisor_type) { 4748c2ecf20Sopenharmony_ci case INTEL_GVT_HYPERVISOR_XEN: 4758c2ecf20Sopenharmony_ci scic = *((u32 *)vgpu_opregion(vgpu)->va + 4768c2ecf20Sopenharmony_ci INTEL_GVT_OPREGION_SCIC); 4778c2ecf20Sopenharmony_ci parm = *((u32 *)vgpu_opregion(vgpu)->va + 4788c2ecf20Sopenharmony_ci INTEL_GVT_OPREGION_PARM); 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci case INTEL_GVT_HYPERVISOR_KVM: 4818c2ecf20Sopenharmony_ci scic_pa = (vgpu_opregion(vgpu)->gfn[0] << PAGE_SHIFT) + 4828c2ecf20Sopenharmony_ci INTEL_GVT_OPREGION_SCIC; 4838c2ecf20Sopenharmony_ci parm_pa = (vgpu_opregion(vgpu)->gfn[0] << PAGE_SHIFT) + 4848c2ecf20Sopenharmony_ci INTEL_GVT_OPREGION_PARM; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci ret = intel_gvt_hypervisor_read_gpa(vgpu, scic_pa, 4878c2ecf20Sopenharmony_ci &scic, sizeof(scic)); 4888c2ecf20Sopenharmony_ci if (ret) { 4898c2ecf20Sopenharmony_ci gvt_vgpu_err("guest opregion read error %d, gpa 0x%llx, len %lu\n", 4908c2ecf20Sopenharmony_ci ret, scic_pa, sizeof(scic)); 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ret = intel_gvt_hypervisor_read_gpa(vgpu, parm_pa, 4958c2ecf20Sopenharmony_ci &parm, sizeof(parm)); 4968c2ecf20Sopenharmony_ci if (ret) { 4978c2ecf20Sopenharmony_ci gvt_vgpu_err("guest opregion read error %d, gpa 0x%llx, len %lu\n", 4988c2ecf20Sopenharmony_ci ret, scic_pa, sizeof(scic)); 4998c2ecf20Sopenharmony_ci return ret; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci default: 5048c2ecf20Sopenharmony_ci gvt_vgpu_err("not supported hypervisor\n"); 5058c2ecf20Sopenharmony_ci return -EINVAL; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (!(swsci & SWSCI_SCI_SELECT)) { 5098c2ecf20Sopenharmony_ci gvt_vgpu_err("requesting SMI service\n"); 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci /* ignore non 0->1 trasitions */ 5138c2ecf20Sopenharmony_ci if ((vgpu_cfg_space(vgpu)[INTEL_GVT_PCI_SWSCI] 5148c2ecf20Sopenharmony_ci & SWSCI_SCI_TRIGGER) || 5158c2ecf20Sopenharmony_ci !(swsci & SWSCI_SCI_TRIGGER)) { 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci func = GVT_OPREGION_FUNC(scic); 5208c2ecf20Sopenharmony_ci subfunc = GVT_OPREGION_SUBFUNC(scic); 5218c2ecf20Sopenharmony_ci if (!querying_capabilities(scic)) { 5228c2ecf20Sopenharmony_ci gvt_vgpu_err("requesting runtime service: func \"%s\"," 5238c2ecf20Sopenharmony_ci " subfunc \"%s\"\n", 5248c2ecf20Sopenharmony_ci opregion_func_name(func), 5258c2ecf20Sopenharmony_ci opregion_subfunc_name(subfunc)); 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * emulate exit status of function call, '0' means 5288c2ecf20Sopenharmony_ci * "failure, generic, unsupported or unknown cause" 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ci scic &= ~OPREGION_SCIC_EXIT_MASK; 5318c2ecf20Sopenharmony_ci goto out; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci scic = 0; 5358c2ecf20Sopenharmony_ci parm = 0; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ciout: 5388c2ecf20Sopenharmony_ci switch (intel_gvt_host.hypervisor_type) { 5398c2ecf20Sopenharmony_ci case INTEL_GVT_HYPERVISOR_XEN: 5408c2ecf20Sopenharmony_ci *((u32 *)vgpu_opregion(vgpu)->va + 5418c2ecf20Sopenharmony_ci INTEL_GVT_OPREGION_SCIC) = scic; 5428c2ecf20Sopenharmony_ci *((u32 *)vgpu_opregion(vgpu)->va + 5438c2ecf20Sopenharmony_ci INTEL_GVT_OPREGION_PARM) = parm; 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci case INTEL_GVT_HYPERVISOR_KVM: 5468c2ecf20Sopenharmony_ci ret = intel_gvt_hypervisor_write_gpa(vgpu, scic_pa, 5478c2ecf20Sopenharmony_ci &scic, sizeof(scic)); 5488c2ecf20Sopenharmony_ci if (ret) { 5498c2ecf20Sopenharmony_ci gvt_vgpu_err("guest opregion write error %d, gpa 0x%llx, len %lu\n", 5508c2ecf20Sopenharmony_ci ret, scic_pa, sizeof(scic)); 5518c2ecf20Sopenharmony_ci return ret; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ret = intel_gvt_hypervisor_write_gpa(vgpu, parm_pa, 5558c2ecf20Sopenharmony_ci &parm, sizeof(parm)); 5568c2ecf20Sopenharmony_ci if (ret) { 5578c2ecf20Sopenharmony_ci gvt_vgpu_err("guest opregion write error %d, gpa 0x%llx, len %lu\n", 5588c2ecf20Sopenharmony_ci ret, scic_pa, sizeof(scic)); 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci break; 5638c2ecf20Sopenharmony_ci default: 5648c2ecf20Sopenharmony_ci gvt_vgpu_err("not supported hypervisor\n"); 5658c2ecf20Sopenharmony_ci return -EINVAL; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 570