1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2022 Intel Corporation 4 */ 5 6 #include "xe_wopcm.h" 7 8 #include "regs/xe_guc_regs.h" 9 #include "xe_device.h" 10 #include "xe_force_wake.h" 11 #include "xe_gt.h" 12 #include "xe_mmio.h" 13 #include "xe_uc_fw.h" 14 15 /** 16 * DOC: Write Once Protected Content Memory (WOPCM) Layout 17 * 18 * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and 19 * offset registers whose values are calculated and determined by HuC/GuC 20 * firmware size and set of hardware requirements/restrictions as shown below: 21 * 22 * :: 23 * 24 * +=========> +====================+ <== WOPCM Top 25 * ^ | HW contexts RSVD | 26 * | +===> +====================+ <== GuC WOPCM Top 27 * | ^ | | 28 * | | | | 29 * | | | | 30 * | GuC | | 31 * | WOPCM | | 32 * | Size +--------------------+ 33 * WOPCM | | GuC FW RSVD | 34 * | | +--------------------+ 35 * | | | GuC Stack RSVD | 36 * | | +------------------- + 37 * | v | GuC WOPCM RSVD | 38 * | +===> +====================+ <== GuC WOPCM base 39 * | | WOPCM RSVD | 40 * | +------------------- + <== HuC Firmware Top 41 * v | HuC FW | 42 * +=========> +====================+ <== WOPCM Base 43 * 44 * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top. 45 * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6 46 * context). 47 */ 48 49 /* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */ 50 /* FIXME: Larger size require for 2 tile PVC, do a proper probe sooner or later */ 51 #define DGFX_WOPCM_SIZE SZ_4M 52 /* FIXME: Larger size require for MTL, do a proper probe sooner or later */ 53 #define MTL_WOPCM_SIZE SZ_4M 54 #define WOPCM_SIZE SZ_2M 55 56 #define MAX_WOPCM_SIZE SZ_8M 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(gt, DMA_GUC_WOPCM_OFFSET); 127 u32 reg_size = xe_mmio_read32(gt, 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(gt, 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(gt, 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(gt, 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(gt, 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 /** 188 * xe_wopcm_init() - Initialize the WOPCM structure. 189 * @wopcm: pointer to xe_wopcm. 190 * 191 * This function will partition WOPCM space based on GuC and HuC firmware sizes 192 * and will allocate max remaining for use by GuC. This function will also 193 * enforce platform dependent hardware restrictions on GuC WOPCM offset and 194 * size. It will fail the WOPCM init if any of these checks fail, so that the 195 * following WOPCM registers setup and GuC firmware uploading would be aborted. 196 */ 197 int xe_wopcm_init(struct xe_wopcm *wopcm) 198 { 199 struct xe_device *xe = wopcm_to_xe(wopcm); 200 struct xe_gt *gt = wopcm_to_gt(wopcm); 201 u32 guc_fw_size = xe_uc_fw_get_upload_size(>->uc.guc.fw); 202 u32 huc_fw_size = xe_uc_fw_get_upload_size(>->uc.huc.fw); 203 u32 ctx_rsvd = context_reserved_size(); 204 u32 guc_wopcm_base; 205 u32 guc_wopcm_size; 206 bool locked; 207 int ret = 0; 208 209 if (!guc_fw_size) 210 return -EINVAL; 211 212 wopcm->size = xe_wopcm_size(xe); 213 drm_dbg(&xe->drm, "WOPCM: %uK\n", wopcm->size / SZ_1K); 214 215 xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT); 216 XE_WARN_ON(guc_fw_size >= wopcm->size); 217 XE_WARN_ON(huc_fw_size >= wopcm->size); 218 XE_WARN_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm->size); 219 220 locked = __wopcm_regs_locked(gt, &guc_wopcm_base, &guc_wopcm_size); 221 if (locked) { 222 drm_dbg(&xe->drm, "GuC WOPCM is already locked [%uK, %uK)\n", 223 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 224 /* 225 * When the GuC wopcm base and size are preprogrammed by 226 * BIOS/IFWI, check against the max allowed wopcm size to 227 * validate if the programmed values align to the wopcm layout. 228 */ 229 wopcm->size = MAX_WOPCM_SIZE; 230 231 goto check; 232 } 233 234 /* 235 * Aligned value of guc_wopcm_base will determine available WOPCM space 236 * for HuC firmware and mandatory reserved area. 237 */ 238 guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE; 239 guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT); 240 241 /* 242 * Need to clamp guc_wopcm_base now to make sure the following math is 243 * correct. Formal check of whole WOPCM layout will be done below. 244 */ 245 guc_wopcm_base = min(guc_wopcm_base, wopcm->size - ctx_rsvd); 246 247 /* Aligned remainings of usable WOPCM space can be assigned to GuC. */ 248 guc_wopcm_size = wopcm->size - ctx_rsvd - guc_wopcm_base; 249 guc_wopcm_size &= GUC_WOPCM_SIZE_MASK; 250 251 drm_dbg(&xe->drm, "Calculated GuC WOPCM [%uK, %uK)\n", 252 guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 253 254 check: 255 if (__check_layout(xe, wopcm->size, guc_wopcm_base, guc_wopcm_size, 256 guc_fw_size, huc_fw_size)) { 257 wopcm->guc.base = guc_wopcm_base; 258 wopcm->guc.size = guc_wopcm_size; 259 XE_WARN_ON(!wopcm->guc.base); 260 XE_WARN_ON(!wopcm->guc.size); 261 } else { 262 drm_notice(&xe->drm, "Unsuccessful WOPCM partitioning\n"); 263 return -E2BIG; 264 } 265 266 if (!locked) 267 ret = __wopcm_init_regs(xe, gt, wopcm); 268 269 return ret; 270 } 271