1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2022 Intel Corporation 4 */ 5 6 #include "xe_wopcm.h" 7 8 #include <linux/fault-inject.h> 9 10 #include "regs/xe_guc_regs.h" 11 #include "xe_device.h" 12 #include "xe_force_wake.h" 13 #include "xe_gt_types.h" 14 #include "xe_mmio.h" 15 #include "xe_uc_fw.h" 16 17 /** 18 * DOC: Write Once Protected Content Memory (WOPCM) Layout 19 * 20 * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and 21 * offset registers whose values are calculated and determined by HuC/GuC 22 * firmware size and set of hardware requirements/restrictions as shown below: 23 * 24 * :: 25 * 26 * +=========> +====================+ <== WOPCM Top 27 * ^ | HW contexts RSVD | 28 * | +===> +====================+ <== GuC WOPCM Top 29 * | ^ | | 30 * | | | | 31 * | | | | 32 * | GuC | | 33 * | WOPCM | | 34 * | Size +--------------------+ 35 * WOPCM | | GuC FW RSVD | 36 * | | +--------------------+ 37 * | | | GuC Stack RSVD | 38 * | | +------------------- + 39 * | v | GuC WOPCM RSVD | 40 * | +===> +====================+ <== GuC WOPCM base 41 * | | WOPCM RSVD | 42 * | +------------------- + <== HuC Firmware Top 43 * v | HuC FW | 44 * +=========> +====================+ <== WOPCM Base 45 * 46 * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top. 47 * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6 48 * context). 49 */ 50 51 /* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */ 52 /* FIXME: Larger size require for some platforms, do a proper probe sooner or later */ 53 #define DGFX_WOPCM_SIZE SZ_4M 54 #define LNL_WOPCM_SIZE SZ_8M 55 #define MTL_WOPCM_SIZE SZ_4M 56 #define WOPCM_SIZE SZ_2M 57 58 /* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */ 59 #define WOPCM_RESERVED_SIZE SZ_16K 60 61 /* 16KB reserved at the beginning of GuC WOPCM. */ 62 #define GUC_WOPCM_RESERVED SZ_16K 63 /* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ 64 #define GUC_WOPCM_STACK_RESERVED SZ_8K 65 66 /* GuC WOPCM Offset value needs to be aligned to 16KB. */ 67 #define GUC_WOPCM_OFFSET_ALIGNMENT (1UL << GUC_WOPCM_OFFSET_SHIFT) 68 69 /* 36KB WOPCM reserved at the end of WOPCM */ 70 #define WOPCM_HW_CTX_RESERVED (SZ_32K + SZ_4K) 71 72 static inline struct xe_gt *wopcm_to_gt(struct xe_wopcm *wopcm) 73 { 74 return container_of(wopcm, struct xe_gt, uc.wopcm); 75 } 76 77 static inline struct xe_device *wopcm_to_xe(struct xe_wopcm *wopcm) 78 { 79 return gt_to_xe(wopcm_to_gt(wopcm)); 80 } 81 82 static u32 context_reserved_size(void) 83 { 84 return WOPCM_HW_CTX_RESERVED; 85 } 86 87 static bool __check_layout(struct xe_device *xe, u32 wopcm_size, 88 u32 guc_wopcm_base, u32 guc_wopcm_size, 89 u32 guc_fw_size, u32 huc_fw_size) 90 { 91 const u32 ctx_rsvd = context_reserved_size(); 92 u32 size; 93 94 size = wopcm_size - ctx_rsvd; 95 if (unlikely(guc_wopcm_base >= size || 96 guc_wopcm_size > size - guc_wopcm_base)) { 97 drm_err(&xe->drm, 98 "WOPCM: invalid GuC region layout: %uK + %uK > %uK\n", 99 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K, 100 size / SZ_1K); 101 return false; 102 } 103 104 size = guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED; 105 if (unlikely(guc_wopcm_size < size)) { 106 drm_err(&xe->drm, "WOPCM: no space for %s: %uK < %uK\n", 107 xe_uc_fw_type_repr(XE_UC_FW_TYPE_GUC), 108 guc_wopcm_size / SZ_1K, size / SZ_1K); 109 return false; 110 } 111 112 size = huc_fw_size + WOPCM_RESERVED_SIZE; 113 if (unlikely(guc_wopcm_base < size)) { 114 drm_err(&xe->drm, "WOPCM: no space for %s: %uK < %uK\n", 115 xe_uc_fw_type_repr(XE_UC_FW_TYPE_HUC), 116 guc_wopcm_base / SZ_1K, size / SZ_1K); 117 return false; 118 } 119 120 return true; 121 } 122 123 static bool __wopcm_regs_locked(struct xe_gt *gt, 124 u32 *guc_wopcm_base, u32 *guc_wopcm_size) 125 { 126 u32 reg_base = xe_mmio_read32(>->mmio, DMA_GUC_WOPCM_OFFSET); 127 u32 reg_size = xe_mmio_read32(>->mmio, GUC_WOPCM_SIZE); 128 129 if (!(reg_size & GUC_WOPCM_SIZE_LOCKED) || 130 !(reg_base & GUC_WOPCM_OFFSET_VALID)) 131 return false; 132 133 *guc_wopcm_base = reg_base & GUC_WOPCM_OFFSET_MASK; 134 *guc_wopcm_size = reg_size & GUC_WOPCM_SIZE_MASK; 135 return true; 136 } 137 138 static int __wopcm_init_regs(struct xe_device *xe, struct xe_gt *gt, 139 struct xe_wopcm *wopcm) 140 { 141 u32 base = wopcm->guc.base; 142 u32 size = wopcm->guc.size; 143 u32 huc_agent = xe_uc_fw_is_available(>->uc.huc.fw) ? HUC_LOADING_AGENT_GUC : 0; 144 u32 mask; 145 int err; 146 147 XE_WARN_ON(!(base & GUC_WOPCM_OFFSET_MASK)); 148 XE_WARN_ON(base & ~GUC_WOPCM_OFFSET_MASK); 149 XE_WARN_ON(!(size & GUC_WOPCM_SIZE_MASK)); 150 XE_WARN_ON(size & ~GUC_WOPCM_SIZE_MASK); 151 152 mask = GUC_WOPCM_SIZE_MASK | GUC_WOPCM_SIZE_LOCKED; 153 err = xe_mmio_write32_and_verify(>->mmio, GUC_WOPCM_SIZE, size, mask, 154 size | GUC_WOPCM_SIZE_LOCKED); 155 if (err) 156 goto err_out; 157 158 mask = GUC_WOPCM_OFFSET_MASK | GUC_WOPCM_OFFSET_VALID | huc_agent; 159 err = xe_mmio_write32_and_verify(>->mmio, DMA_GUC_WOPCM_OFFSET, 160 base | huc_agent, mask, 161 base | huc_agent | 162 GUC_WOPCM_OFFSET_VALID); 163 if (err) 164 goto err_out; 165 166 return 0; 167 168 err_out: 169 drm_notice(&xe->drm, "Failed to init uC WOPCM registers!\n"); 170 drm_notice(&xe->drm, "%s(%#x)=%#x\n", "DMA_GUC_WOPCM_OFFSET", 171 DMA_GUC_WOPCM_OFFSET.addr, 172 xe_mmio_read32(>->mmio, DMA_GUC_WOPCM_OFFSET)); 173 drm_notice(&xe->drm, "%s(%#x)=%#x\n", "GUC_WOPCM_SIZE", 174 GUC_WOPCM_SIZE.addr, 175 xe_mmio_read32(>->mmio, GUC_WOPCM_SIZE)); 176 177 return err; 178 } 179 180 u32 xe_wopcm_size(struct xe_device *xe) 181 { 182 if (xe->info.platform >= XE_LUNARLAKE) 183 return LNL_WOPCM_SIZE; 184 else if (IS_DGFX(xe)) 185 return DGFX_WOPCM_SIZE; 186 else if (xe->info.platform == XE_METEORLAKE) 187 return MTL_WOPCM_SIZE; 188 else 189 return WOPCM_SIZE; 190 } 191 192 static u32 max_wopcm_size(struct xe_device *xe) 193 { 194 if (xe->info.platform == XE_NOVALAKE_P) 195 return SZ_16M; 196 else 197 return SZ_8M; 198 } 199 200 /** 201 * xe_wopcm_init() - Initialize the WOPCM structure. 202 * @wopcm: pointer to xe_wopcm. 203 * 204 * This function will partition WOPCM space based on GuC and HuC firmware sizes 205 * and will allocate max remaining for use by GuC. This function will also 206 * enforce platform dependent hardware restrictions on GuC WOPCM offset and 207 * size. It will fail the WOPCM init if any of these checks fail, so that the 208 * following WOPCM registers setup and GuC firmware uploading would be aborted. 209 */ 210 int xe_wopcm_init(struct xe_wopcm *wopcm) 211 { 212 struct xe_device *xe = wopcm_to_xe(wopcm); 213 struct xe_gt *gt = wopcm_to_gt(wopcm); 214 u32 guc_fw_size = xe_uc_fw_get_upload_size(>->uc.guc.fw); 215 u32 huc_fw_size = xe_uc_fw_get_upload_size(>->uc.huc.fw); 216 u32 ctx_rsvd = context_reserved_size(); 217 u32 guc_wopcm_base; 218 u32 guc_wopcm_size; 219 bool locked; 220 int ret = 0; 221 222 if (!guc_fw_size) 223 return -EINVAL; 224 225 wopcm->size = xe_wopcm_size(xe); 226 drm_dbg(&xe->drm, "WOPCM: %uK\n", wopcm->size / SZ_1K); 227 228 xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT); 229 XE_WARN_ON(guc_fw_size >= wopcm->size); 230 XE_WARN_ON(huc_fw_size >= wopcm->size); 231 XE_WARN_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm->size); 232 233 locked = __wopcm_regs_locked(gt, &guc_wopcm_base, &guc_wopcm_size); 234 if (locked) { 235 drm_dbg(&xe->drm, "GuC WOPCM is already locked [%uK, %uK)\n", 236 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 237 /* 238 * When the GuC wopcm base and size are preprogrammed by 239 * BIOS/IFWI, check against the max allowed wopcm size to 240 * validate if the programmed values align to the wopcm layout. 241 * 242 * FIXME: This is giving the maximum overall WOPCM size and not 243 * the size relative to each GT. 244 */ 245 wopcm->size = max_wopcm_size(xe); 246 247 goto check; 248 } 249 250 /* 251 * Aligned value of guc_wopcm_base will determine available WOPCM space 252 * for HuC firmware and mandatory reserved area. 253 */ 254 guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE; 255 guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT); 256 257 /* 258 * Need to clamp guc_wopcm_base now to make sure the following math is 259 * correct. Formal check of whole WOPCM layout will be done below. 260 */ 261 guc_wopcm_base = min(guc_wopcm_base, wopcm->size - ctx_rsvd); 262 263 /* Aligned remainings of usable WOPCM space can be assigned to GuC. */ 264 guc_wopcm_size = wopcm->size - ctx_rsvd - guc_wopcm_base; 265 guc_wopcm_size &= GUC_WOPCM_SIZE_MASK; 266 267 drm_dbg(&xe->drm, "Calculated GuC WOPCM [%uK, %uK)\n", 268 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 269 270 check: 271 if (__check_layout(xe, wopcm->size, guc_wopcm_base, guc_wopcm_size, 272 guc_fw_size, huc_fw_size)) { 273 wopcm->guc.base = guc_wopcm_base; 274 wopcm->guc.size = guc_wopcm_size; 275 XE_WARN_ON(!wopcm->guc.base); 276 XE_WARN_ON(!wopcm->guc.size); 277 } else { 278 drm_notice(&xe->drm, "Unsuccessful WOPCM partitioning\n"); 279 return -E2BIG; 280 } 281 282 if (!locked) 283 ret = __wopcm_init_regs(xe, gt, wopcm); 284 285 return ret; 286 } 287 ALLOW_ERROR_INJECTION(xe_wopcm_init, ERRNO); /* See xe_pci_probe() */ 288