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 2 tile PVC, do a proper probe sooner or later */ 53 #define DGFX_WOPCM_SIZE SZ_4M 54 /* FIXME: Larger size require for MTL, do a proper probe sooner or later */ 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 return IS_DGFX(xe) ? DGFX_WOPCM_SIZE : 183 xe->info.platform == XE_METEORLAKE ? MTL_WOPCM_SIZE : 184 WOPCM_SIZE; 185 } 186 187 static u32 max_wopcm_size(struct xe_device *xe) 188 { 189 if (xe->info.platform == XE_NOVALAKE_P) 190 return SZ_16M; 191 else 192 return SZ_8M; 193 } 194 195 /** 196 * xe_wopcm_init() - Initialize the WOPCM structure. 197 * @wopcm: pointer to xe_wopcm. 198 * 199 * This function will partition WOPCM space based on GuC and HuC firmware sizes 200 * and will allocate max remaining for use by GuC. This function will also 201 * enforce platform dependent hardware restrictions on GuC WOPCM offset and 202 * size. It will fail the WOPCM init if any of these checks fail, so that the 203 * following WOPCM registers setup and GuC firmware uploading would be aborted. 204 */ 205 int xe_wopcm_init(struct xe_wopcm *wopcm) 206 { 207 struct xe_device *xe = wopcm_to_xe(wopcm); 208 struct xe_gt *gt = wopcm_to_gt(wopcm); 209 u32 guc_fw_size = xe_uc_fw_get_upload_size(>->uc.guc.fw); 210 u32 huc_fw_size = xe_uc_fw_get_upload_size(>->uc.huc.fw); 211 u32 ctx_rsvd = context_reserved_size(); 212 u32 guc_wopcm_base; 213 u32 guc_wopcm_size; 214 bool locked; 215 int ret = 0; 216 217 if (!guc_fw_size) 218 return -EINVAL; 219 220 wopcm->size = xe_wopcm_size(xe); 221 drm_dbg(&xe->drm, "WOPCM: %uK\n", wopcm->size / SZ_1K); 222 223 xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT); 224 XE_WARN_ON(guc_fw_size >= wopcm->size); 225 XE_WARN_ON(huc_fw_size >= wopcm->size); 226 XE_WARN_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm->size); 227 228 locked = __wopcm_regs_locked(gt, &guc_wopcm_base, &guc_wopcm_size); 229 if (locked) { 230 drm_dbg(&xe->drm, "GuC WOPCM is already locked [%uK, %uK)\n", 231 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 232 /* 233 * When the GuC wopcm base and size are preprogrammed by 234 * BIOS/IFWI, check against the max allowed wopcm size to 235 * validate if the programmed values align to the wopcm layout. 236 * 237 * FIXME: This is giving the maximum overall WOPCM size and not 238 * the size relative to each GT. 239 */ 240 wopcm->size = max_wopcm_size(xe); 241 242 goto check; 243 } 244 245 /* 246 * Aligned value of guc_wopcm_base will determine available WOPCM space 247 * for HuC firmware and mandatory reserved area. 248 */ 249 guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE; 250 guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT); 251 252 /* 253 * Need to clamp guc_wopcm_base now to make sure the following math is 254 * correct. Formal check of whole WOPCM layout will be done below. 255 */ 256 guc_wopcm_base = min(guc_wopcm_base, wopcm->size - ctx_rsvd); 257 258 /* Aligned remainings of usable WOPCM space can be assigned to GuC. */ 259 guc_wopcm_size = wopcm->size - ctx_rsvd - guc_wopcm_base; 260 guc_wopcm_size &= GUC_WOPCM_SIZE_MASK; 261 262 drm_dbg(&xe->drm, "Calculated GuC WOPCM [%uK, %uK)\n", 263 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 264 265 check: 266 if (__check_layout(xe, wopcm->size, guc_wopcm_base, guc_wopcm_size, 267 guc_fw_size, huc_fw_size)) { 268 wopcm->guc.base = guc_wopcm_base; 269 wopcm->guc.size = guc_wopcm_size; 270 XE_WARN_ON(!wopcm->guc.base); 271 XE_WARN_ON(!wopcm->guc.size); 272 } else { 273 drm_notice(&xe->drm, "Unsuccessful WOPCM partitioning\n"); 274 return -E2BIG; 275 } 276 277 if (!locked) 278 ret = __wopcm_init_regs(xe, gt, wopcm); 279 280 return ret; 281 } 282 ALLOW_ERROR_INJECTION(xe_wopcm_init, ERRNO); /* See xe_pci_probe() */ 283