1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2023-2025 Intel Corporation 4 */ 5 6 #include "abi/guc_relay_actions_abi.h" 7 8 #include "xe_device_types.h" 9 #include "xe_sriov.h" 10 #include "xe_sriov_pf_helpers.h" 11 #include "xe_sriov_printk.h" 12 13 #include "xe_sriov_pf_service.h" 14 #include "xe_sriov_pf_service_types.h" 15 16 /** 17 * xe_sriov_pf_service_init - Early initialization of the SR-IOV PF service. 18 * @xe: the &xe_device to initialize 19 * 20 * Performs early initialization of the SR-IOV PF service. 21 * 22 * This function can only be called on PF. 23 */ 24 void xe_sriov_pf_service_init(struct xe_device *xe) 25 { 26 BUILD_BUG_ON(!GUC_RELAY_VERSION_BASE_MAJOR && !GUC_RELAY_VERSION_BASE_MINOR); 27 BUILD_BUG_ON(GUC_RELAY_VERSION_BASE_MAJOR > GUC_RELAY_VERSION_LATEST_MAJOR); 28 29 xe_assert(xe, IS_SRIOV_PF(xe)); 30 31 /* base versions may differ between platforms */ 32 xe->sriov.pf.service.version.base.major = GUC_RELAY_VERSION_BASE_MAJOR; 33 xe->sriov.pf.service.version.base.minor = GUC_RELAY_VERSION_BASE_MINOR; 34 35 /* latest version is same for all platforms */ 36 xe->sriov.pf.service.version.latest.major = GUC_RELAY_VERSION_LATEST_MAJOR; 37 xe->sriov.pf.service.version.latest.minor = GUC_RELAY_VERSION_LATEST_MINOR; 38 } 39 40 /* Return: 0 on success or a negative error code on failure. */ 41 static int pf_negotiate_version(struct xe_device *xe, 42 u32 wanted_major, u32 wanted_minor, 43 u32 *major, u32 *minor) 44 { 45 struct xe_sriov_pf_service_version base = xe->sriov.pf.service.version.base; 46 struct xe_sriov_pf_service_version latest = xe->sriov.pf.service.version.latest; 47 48 xe_assert(xe, IS_SRIOV_PF(xe)); 49 xe_assert(xe, base.major); 50 xe_assert(xe, base.major <= latest.major); 51 xe_assert(xe, (base.major < latest.major) || (base.minor <= latest.minor)); 52 53 /* VF doesn't care - return our latest */ 54 if (wanted_major == VF2PF_HANDSHAKE_MAJOR_ANY && 55 wanted_minor == VF2PF_HANDSHAKE_MINOR_ANY) { 56 *major = latest.major; 57 *minor = latest.minor; 58 return 0; 59 } 60 61 /* VF wants newer than our - return our latest */ 62 if (wanted_major > latest.major) { 63 *major = latest.major; 64 *minor = latest.minor; 65 return 0; 66 } 67 68 /* VF wants older than min required - reject */ 69 if (wanted_major < base.major || 70 (wanted_major == base.major && wanted_minor < base.minor)) { 71 return -EPERM; 72 } 73 74 /* previous major - return wanted, as we should still support it */ 75 if (wanted_major < latest.major) { 76 /* XXX: we are not prepared for multi-versions yet */ 77 xe_assert(xe, base.major == latest.major); 78 return -ENOPKG; 79 } 80 81 /* same major - return common minor */ 82 *major = wanted_major; 83 *minor = min_t(u32, latest.minor, wanted_minor); 84 return 0; 85 } 86 87 static void pf_connect(struct xe_device *xe, u32 vfid, u32 major, u32 minor) 88 { 89 xe_sriov_pf_assert_vfid(xe, vfid); 90 xe_assert(xe, major || minor); 91 92 xe->sriov.pf.vfs[vfid].version.major = major; 93 xe->sriov.pf.vfs[vfid].version.minor = minor; 94 } 95 96 static void pf_disconnect(struct xe_device *xe, u32 vfid) 97 { 98 xe_sriov_pf_assert_vfid(xe, vfid); 99 100 xe->sriov.pf.vfs[vfid].version.major = 0; 101 xe->sriov.pf.vfs[vfid].version.minor = 0; 102 } 103 104 /** 105 * xe_sriov_pf_service_is_negotiated - Check if VF has negotiated given ABI version. 106 * @xe: the &xe_device 107 * @vfid: the VF identifier 108 * @major: the major version to check 109 * @minor: the minor version to check 110 * 111 * Performs early initialization of the SR-IOV PF service. 112 * 113 * This function can only be called on PF. 114 * 115 * Returns: true if VF can use given ABI version functionality. 116 */ 117 bool xe_sriov_pf_service_is_negotiated(struct xe_device *xe, u32 vfid, u32 major, u32 minor) 118 { 119 xe_sriov_pf_assert_vfid(xe, vfid); 120 121 return major == xe->sriov.pf.vfs[vfid].version.major && 122 minor <= xe->sriov.pf.vfs[vfid].version.minor; 123 } 124 125 /** 126 * xe_sriov_pf_service_handshake_vf - Confirm a connection with the VF. 127 * @xe: the &xe_device 128 * @vfid: the VF identifier 129 * @wanted_major: the major service version expected by the VF 130 * @wanted_minor: the minor service version expected by the VF 131 * @major: the major service version to be used by the VF 132 * @minor: the minor service version to be used by the VF 133 * 134 * Negotiate a VF/PF ABI version to allow VF use the PF services. 135 * 136 * This function can only be called on PF. 137 * 138 * Return: 0 on success or a negative error code on failure. 139 */ 140 int xe_sriov_pf_service_handshake_vf(struct xe_device *xe, u32 vfid, 141 u32 wanted_major, u32 wanted_minor, 142 u32 *major, u32 *minor) 143 { 144 int err; 145 146 xe_sriov_dbg_verbose(xe, "VF%u wants ABI version %u.%u\n", 147 vfid, wanted_major, wanted_minor); 148 149 err = pf_negotiate_version(xe, wanted_major, wanted_minor, major, minor); 150 151 if (err < 0) { 152 xe_sriov_notice(xe, "VF%u failed to negotiate ABI %u.%u (%pe)\n", 153 vfid, wanted_major, wanted_minor, ERR_PTR(err)); 154 pf_disconnect(xe, vfid); 155 } else { 156 xe_sriov_dbg(xe, "VF%u negotiated ABI version %u.%u\n", 157 vfid, *major, *minor); 158 pf_connect(xe, vfid, *major, *minor); 159 } 160 161 return err; 162 } 163 164 /** 165 * xe_sriov_pf_service_reset_vf - Reset a connection with the VF. 166 * @xe: the &xe_device 167 * @vfid: the VF identifier 168 * 169 * Reset a VF driver negotiated VF/PF ABI version. 170 * 171 * After that point, the VF driver will have to perform new version handshake 172 * to continue use of the PF services again. 173 * 174 * This function can only be called on PF. 175 */ 176 void xe_sriov_pf_service_reset_vf(struct xe_device *xe, unsigned int vfid) 177 { 178 pf_disconnect(xe, vfid); 179 } 180 181 static void print_pf_version(struct drm_printer *p, const char *name, 182 const struct xe_sriov_pf_service_version *version) 183 { 184 drm_printf(p, "%s:\t%u.%u\n", name, version->major, version->minor); 185 } 186 187 /** 188 * xe_sriov_pf_service_print_versions - Print ABI versions negotiated with VFs. 189 * @xe: the &xe_device 190 * @p: the &drm_printer 191 * 192 * This function is for PF use only. 193 */ 194 void xe_sriov_pf_service_print_versions(struct xe_device *xe, struct drm_printer *p) 195 { 196 unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(xe); 197 struct xe_sriov_pf_service_version *version; 198 char name[8]; 199 200 xe_assert(xe, IS_SRIOV_PF(xe)); 201 202 print_pf_version(p, "base", &xe->sriov.pf.service.version.base); 203 print_pf_version(p, "latest", &xe->sriov.pf.service.version.latest); 204 205 for (n = 1; n <= total_vfs; n++) { 206 version = &xe->sriov.pf.vfs[n].version; 207 if (!version->major && !version->minor) 208 continue; 209 210 print_pf_version(p, xe_sriov_function_name(n, name, sizeof(name)), version); 211 } 212 } 213 214 #if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST) 215 #include "tests/xe_sriov_pf_service_kunit.c" 216 #endif 217