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.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 #define MAX_WOPCM_SIZE SZ_8M 59 60 /* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */ 61 #define WOPCM_RESERVED_SIZE SZ_16K 62 63 /* 16KB reserved at the beginning of GuC WOPCM. */ 64 #define GUC_WOPCM_RESERVED SZ_16K 65 /* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ 66 #define GUC_WOPCM_STACK_RESERVED SZ_8K 67 68 /* GuC WOPCM Offset value needs to be aligned to 16KB. */ 69 #define GUC_WOPCM_OFFSET_ALIGNMENT (1UL << GUC_WOPCM_OFFSET_SHIFT) 70 71 /* 36KB WOPCM reserved at the end of WOPCM */ 72 #define WOPCM_HW_CTX_RESERVED (SZ_32K + SZ_4K) 73 74 static inline struct xe_gt *wopcm_to_gt(struct xe_wopcm *wopcm) 75 { 76 return container_of(wopcm, struct xe_gt, uc.wopcm); 77 } 78 79 static inline struct xe_device *wopcm_to_xe(struct xe_wopcm *wopcm) 80 { 81 return gt_to_xe(wopcm_to_gt(wopcm)); 82 } 83 84 static u32 context_reserved_size(void) 85 { 86 return WOPCM_HW_CTX_RESERVED; 87 } 88 89 static bool __check_layout(struct xe_device *xe, u32 wopcm_size, 90 u32 guc_wopcm_base, u32 guc_wopcm_size, 91 u32 guc_fw_size, u32 huc_fw_size) 92 { 93 const u32 ctx_rsvd = context_reserved_size(); 94 u32 size; 95 96 size = wopcm_size - ctx_rsvd; 97 if (unlikely(guc_wopcm_base >= size || 98 guc_wopcm_size > size - guc_wopcm_base)) { 99 drm_err(&xe->drm, 100 "WOPCM: invalid GuC region layout: %uK + %uK > %uK\n", 101 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K, 102 size / SZ_1K); 103 return false; 104 } 105 106 size = guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED; 107 if (unlikely(guc_wopcm_size < size)) { 108 drm_err(&xe->drm, "WOPCM: no space for %s: %uK < %uK\n", 109 xe_uc_fw_type_repr(XE_UC_FW_TYPE_GUC), 110 guc_wopcm_size / SZ_1K, size / SZ_1K); 111 return false; 112 } 113 114 size = huc_fw_size + WOPCM_RESERVED_SIZE; 115 if (unlikely(guc_wopcm_base < size)) { 116 drm_err(&xe->drm, "WOPCM: no space for %s: %uK < %uK\n", 117 xe_uc_fw_type_repr(XE_UC_FW_TYPE_HUC), 118 guc_wopcm_base / SZ_1K, size / SZ_1K); 119 return false; 120 } 121 122 return true; 123 } 124 125 static bool __wopcm_regs_locked(struct xe_gt *gt, 126 u32 *guc_wopcm_base, u32 *guc_wopcm_size) 127 { 128 u32 reg_base = xe_mmio_read32(>->mmio, DMA_GUC_WOPCM_OFFSET); 129 u32 reg_size = xe_mmio_read32(>->mmio, GUC_WOPCM_SIZE); 130 131 if (!(reg_size & GUC_WOPCM_SIZE_LOCKED) || 132 !(reg_base & GUC_WOPCM_OFFSET_VALID)) 133 return false; 134 135 *guc_wopcm_base = reg_base & GUC_WOPCM_OFFSET_MASK; 136 *guc_wopcm_size = reg_size & GUC_WOPCM_SIZE_MASK; 137 return true; 138 } 139 140 static int __wopcm_init_regs(struct xe_device *xe, struct xe_gt *gt, 141 struct xe_wopcm *wopcm) 142 { 143 u32 base = wopcm->guc.base; 144 u32 size = wopcm->guc.size; 145 u32 huc_agent = xe_uc_fw_is_available(>->uc.huc.fw) ? HUC_LOADING_AGENT_GUC : 0; 146 u32 mask; 147 int err; 148 149 XE_WARN_ON(!(base & GUC_WOPCM_OFFSET_MASK)); 150 XE_WARN_ON(base & ~GUC_WOPCM_OFFSET_MASK); 151 XE_WARN_ON(!(size & GUC_WOPCM_SIZE_MASK)); 152 XE_WARN_ON(size & ~GUC_WOPCM_SIZE_MASK); 153 154 mask = GUC_WOPCM_SIZE_MASK | GUC_WOPCM_SIZE_LOCKED; 155 err = xe_mmio_write32_and_verify(>->mmio, GUC_WOPCM_SIZE, size, mask, 156 size | GUC_WOPCM_SIZE_LOCKED); 157 if (err) 158 goto err_out; 159 160 mask = GUC_WOPCM_OFFSET_MASK | GUC_WOPCM_OFFSET_VALID | huc_agent; 161 err = xe_mmio_write32_and_verify(>->mmio, DMA_GUC_WOPCM_OFFSET, 162 base | huc_agent, mask, 163 base | huc_agent | 164 GUC_WOPCM_OFFSET_VALID); 165 if (err) 166 goto err_out; 167 168 return 0; 169 170 err_out: 171 drm_notice(&xe->drm, "Failed to init uC WOPCM registers!\n"); 172 drm_notice(&xe->drm, "%s(%#x)=%#x\n", "DMA_GUC_WOPCM_OFFSET", 173 DMA_GUC_WOPCM_OFFSET.addr, 174 xe_mmio_read32(>->mmio, DMA_GUC_WOPCM_OFFSET)); 175 drm_notice(&xe->drm, "%s(%#x)=%#x\n", "GUC_WOPCM_SIZE", 176 GUC_WOPCM_SIZE.addr, 177 xe_mmio_read32(>->mmio, GUC_WOPCM_SIZE)); 178 179 return err; 180 } 181 182 u32 xe_wopcm_size(struct xe_device *xe) 183 { 184 return IS_DGFX(xe) ? DGFX_WOPCM_SIZE : 185 xe->info.platform == XE_METEORLAKE ? MTL_WOPCM_SIZE : 186 WOPCM_SIZE; 187 } 188 189 /** 190 * xe_wopcm_init() - Initialize the WOPCM structure. 191 * @wopcm: pointer to xe_wopcm. 192 * 193 * This function will partition WOPCM space based on GuC and HuC firmware sizes 194 * and will allocate max remaining for use by GuC. This function will also 195 * enforce platform dependent hardware restrictions on GuC WOPCM offset and 196 * size. It will fail the WOPCM init if any of these checks fail, so that the 197 * following WOPCM registers setup and GuC firmware uploading would be aborted. 198 */ 199 int xe_wopcm_init(struct xe_wopcm *wopcm) 200 { 201 struct xe_device *xe = wopcm_to_xe(wopcm); 202 struct xe_gt *gt = wopcm_to_gt(wopcm); 203 u32 guc_fw_size = xe_uc_fw_get_upload_size(>->uc.guc.fw); 204 u32 huc_fw_size = xe_uc_fw_get_upload_size(>->uc.huc.fw); 205 u32 ctx_rsvd = context_reserved_size(); 206 u32 guc_wopcm_base; 207 u32 guc_wopcm_size; 208 bool locked; 209 int ret = 0; 210 211 if (!guc_fw_size) 212 return -EINVAL; 213 214 wopcm->size = xe_wopcm_size(xe); 215 drm_dbg(&xe->drm, "WOPCM: %uK\n", wopcm->size / SZ_1K); 216 217 xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT); 218 XE_WARN_ON(guc_fw_size >= wopcm->size); 219 XE_WARN_ON(huc_fw_size >= wopcm->size); 220 XE_WARN_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm->size); 221 222 locked = __wopcm_regs_locked(gt, &guc_wopcm_base, &guc_wopcm_size); 223 if (locked) { 224 drm_dbg(&xe->drm, "GuC WOPCM is already locked [%uK, %uK)\n", 225 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 226 /* 227 * When the GuC wopcm base and size are preprogrammed by 228 * BIOS/IFWI, check against the max allowed wopcm size to 229 * validate if the programmed values align to the wopcm layout. 230 */ 231 wopcm->size = MAX_WOPCM_SIZE; 232 233 goto check; 234 } 235 236 /* 237 * Aligned value of guc_wopcm_base will determine available WOPCM space 238 * for HuC firmware and mandatory reserved area. 239 */ 240 guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE; 241 guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT); 242 243 /* 244 * Need to clamp guc_wopcm_base now to make sure the following math is 245 * correct. Formal check of whole WOPCM layout will be done below. 246 */ 247 guc_wopcm_base = min(guc_wopcm_base, wopcm->size - ctx_rsvd); 248 249 /* Aligned remainings of usable WOPCM space can be assigned to GuC. */ 250 guc_wopcm_size = wopcm->size - ctx_rsvd - guc_wopcm_base; 251 guc_wopcm_size &= GUC_WOPCM_SIZE_MASK; 252 253 drm_dbg(&xe->drm, "Calculated GuC WOPCM [%uK, %uK)\n", 254 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 255 256 check: 257 if (__check_layout(xe, wopcm->size, guc_wopcm_base, guc_wopcm_size, 258 guc_fw_size, huc_fw_size)) { 259 wopcm->guc.base = guc_wopcm_base; 260 wopcm->guc.size = guc_wopcm_size; 261 XE_WARN_ON(!wopcm->guc.base); 262 XE_WARN_ON(!wopcm->guc.size); 263 } else { 264 drm_notice(&xe->drm, "Unsuccessful WOPCM partitioning\n"); 265 return -E2BIG; 266 } 267 268 if (!locked) 269 ret = __wopcm_init_regs(xe, gt, wopcm); 270 271 return ret; 272 } 273 ALLOW_ERROR_INJECTION(xe_wopcm_init, ERRNO); /* See xe_pci_probe() */ 274