Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

accel/ivpu: Split FW runtime and global memory buffers

Split firmware boot parameters (4KB) and FW version (4KB) into dedicated
buffer objects, separating them from the FW runtime memory buffer. This
creates three distinct buffers with independent allocation control.

This enables future modifications, particularly allowing the FW
image memory to be moved into a read-only buffer.

Fix user range starting address from incorrect 0x88000000 (2GB + 128MB)
overlapping global aperture on 37XX to intended 0xa0000000 (2GB + 512MB).
This caused no issues as FW aligned this range to 512MB anyway, but
corrected for consistency.

Convert ivpu_hw_range_init() from inline helper to function with overflow
validation to prevent potential security issues from address range
arithmetic overflows.

Improve readability of ivpu_fw_parse() function, remove unrelated constant
defines and validate firmware header values based on vdev->hw->ranges.

Reviewed-by: Lizhi Hou <lizhi.hou@amd.com>
Signed-off-by: Karol Wachowski <karol.wachowski@linux.intel.com>
Link: https://lore.kernel.org/r/20250925074209.1148924-1-karol.wachowski@linux.intel.com

+152 -75
+1 -2
drivers/accel/ivpu/ivpu_drv.c
··· 380 380 drm_WARN_ON(&vdev->drm, atomic_read(&vdev->job_timeout_counter)); 381 381 drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa)); 382 382 383 - /* Update boot params located at first 4KB of FW memory */ 384 - ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem)); 383 + ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem_bp)); 385 384 386 385 ret = ivpu_hw_boot_fw(vdev); 387 386 if (ret) {
+93 -58
drivers/accel/ivpu/ivpu_fw.c
··· 17 17 #include "ivpu_ipc.h" 18 18 #include "ivpu_pm.h" 19 19 20 - #define FW_GLOBAL_MEM_START (2ull * SZ_1G) 21 - #define FW_GLOBAL_MEM_END (3ull * SZ_1G) 22 - #define FW_SHARED_MEM_SIZE SZ_256M /* Must be aligned to FW_SHARED_MEM_ALIGNMENT */ 23 - #define FW_SHARED_MEM_ALIGNMENT SZ_128K /* VPU MTRR limitation */ 24 - #define FW_RUNTIME_MAX_SIZE SZ_512M 25 20 #define FW_SHAVE_NN_MAX_SIZE SZ_2M 26 - #define FW_RUNTIME_MIN_ADDR (FW_GLOBAL_MEM_START) 27 - #define FW_RUNTIME_MAX_ADDR (FW_GLOBAL_MEM_END - FW_SHARED_MEM_SIZE) 28 21 #define FW_FILE_IMAGE_OFFSET (VPU_FW_HEADER_SIZE + FW_VERSION_HEADER_SIZE) 29 22 #define FW_PREEMPT_BUF_MIN_SIZE SZ_4K 30 23 #define FW_PREEMPT_BUF_MAX_SIZE SZ_32M ··· 126 133 return false; 127 134 } 128 135 129 - static bool is_within_range(u64 addr, size_t size, u64 range_start, size_t range_size) 136 + bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range) 130 137 { 131 - if (addr < range_start || addr + size > range_start + range_size) 138 + u64 addr_end; 139 + 140 + if (!range || check_add_overflow(addr, size, &addr_end)) 141 + return false; 142 + 143 + if (addr < range->start || addr_end > range->end) 132 144 return false; 133 145 134 146 return true; ··· 196 198 { 197 199 struct ivpu_fw_info *fw = vdev->fw; 198 200 const struct vpu_firmware_header *fw_hdr = (const void *)fw->file->data; 199 - u64 runtime_addr, image_load_addr, runtime_size, image_size; 201 + struct ivpu_addr_range fw_image_range; 202 + u64 boot_params_addr, boot_params_size; 203 + u64 fw_version_addr, fw_version_size; 204 + u64 runtime_addr, runtime_size; 205 + u64 image_load_addr, image_size; 200 206 201 207 if (fw->file->size <= FW_FILE_IMAGE_OFFSET) { 202 208 ivpu_err(vdev, "Firmware file is too small: %zu\n", fw->file->size); ··· 212 210 return -EINVAL; 213 211 } 214 212 215 - runtime_addr = fw_hdr->boot_params_load_address; 216 - runtime_size = fw_hdr->runtime_size; 217 - image_load_addr = fw_hdr->image_load_address; 218 - image_size = fw_hdr->image_size; 213 + boot_params_addr = fw_hdr->boot_params_load_address; 214 + boot_params_size = SZ_4K; 219 215 220 - if (runtime_addr < FW_RUNTIME_MIN_ADDR || runtime_addr > FW_RUNTIME_MAX_ADDR) { 221 - ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx\n", runtime_addr); 216 + if (!ivpu_is_within_range(boot_params_addr, boot_params_size, &vdev->hw->ranges.runtime)) { 217 + ivpu_err(vdev, "Invalid boot params address: 0x%llx\n", boot_params_addr); 222 218 return -EINVAL; 223 219 } 224 220 225 - if (runtime_size < fw->file->size || runtime_size > FW_RUNTIME_MAX_SIZE) { 226 - ivpu_err(vdev, "Invalid firmware runtime size: %llu\n", runtime_size); 221 + fw_version_addr = fw_hdr->firmware_version_load_address; 222 + fw_version_size = ALIGN(fw_hdr->firmware_version_size, SZ_4K); 223 + 224 + if (fw_version_size != SZ_4K) { 225 + ivpu_err(vdev, "Invalid firmware version size: %u\n", 226 + fw_hdr->firmware_version_size); 227 + return -EINVAL; 228 + } 229 + 230 + if (!ivpu_is_within_range(fw_version_addr, fw_version_size, &vdev->hw->ranges.runtime)) { 231 + ivpu_err(vdev, "Invalid firmware version address: 0x%llx\n", fw_version_addr); 232 + return -EINVAL; 233 + } 234 + 235 + runtime_addr = fw_hdr->image_load_address; 236 + runtime_size = fw_hdr->runtime_size - boot_params_size - fw_version_size; 237 + 238 + image_load_addr = fw_hdr->image_load_address; 239 + image_size = fw_hdr->image_size; 240 + 241 + if (!ivpu_is_within_range(runtime_addr, runtime_size, &vdev->hw->ranges.runtime)) { 242 + ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx and size %llu\n", 243 + runtime_addr, runtime_size); 227 244 return -EINVAL; 228 245 } 229 246 ··· 251 230 return -EINVAL; 252 231 } 253 232 254 - if (image_load_addr < runtime_addr || 255 - image_load_addr + image_size > runtime_addr + runtime_size) { 256 - ivpu_err(vdev, "Invalid firmware load address size: 0x%llx and size %llu\n", 233 + if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) { 234 + ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n", 257 235 image_load_addr, image_size); 236 + return -EINVAL; 237 + } 238 + 239 + if (ivpu_hw_range_init(vdev, &fw_image_range, image_load_addr, image_size)) 240 + return -EINVAL; 241 + 242 + if (!ivpu_is_within_range(fw_hdr->entry_point, SZ_4K, &fw_image_range)) { 243 + ivpu_err(vdev, "Invalid entry point: 0x%llx\n", fw_hdr->entry_point); 258 244 return -EINVAL; 259 245 } 260 246 ··· 270 242 return -EINVAL; 271 243 } 272 244 273 - if (fw_hdr->entry_point < image_load_addr || 274 - fw_hdr->entry_point >= image_load_addr + image_size) { 275 - ivpu_err(vdev, "Invalid entry point: 0x%llx\n", fw_hdr->entry_point); 276 - return -EINVAL; 277 - } 278 245 ivpu_dbg(vdev, FW_BOOT, "Header version: 0x%x, format 0x%x\n", 279 246 fw_hdr->header_version, fw_hdr->image_format); 280 247 ··· 283 260 if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3)) 284 261 return -EINVAL; 285 262 263 + fw->boot_params_addr = boot_params_addr; 264 + fw->boot_params_size = boot_params_size; 265 + fw->fw_version_addr = fw_version_addr; 266 + fw->fw_version_size = fw_version_size; 286 267 fw->runtime_addr = runtime_addr; 287 268 fw->runtime_size = runtime_size; 288 269 fw->image_load_offset = image_load_addr - runtime_addr; ··· 309 282 ivpu_dbg(vdev, FW_BOOT, "Mid-inference preemption %s supported\n", 310 283 ivpu_fw_preempt_buf_size(vdev) ? "is" : "is not"); 311 284 312 - if (fw_hdr->ro_section_start_address && !is_within_range(fw_hdr->ro_section_start_address, 313 - fw_hdr->ro_section_size, 314 - fw_hdr->image_load_address, 315 - fw_hdr->image_size)) { 285 + if (fw_hdr->ro_section_start_address && 286 + !ivpu_is_within_range(fw_hdr->ro_section_start_address, fw_hdr->ro_section_size, 287 + &fw_image_range)) { 316 288 ivpu_err(vdev, "Invalid read-only section: start address 0x%llx, size %u\n", 317 289 fw_hdr->ro_section_start_address, fw_hdr->ro_section_size); 318 290 return -EINVAL; ··· 320 294 fw->read_only_addr = fw_hdr->ro_section_start_address; 321 295 fw->read_only_size = fw_hdr->ro_section_size; 322 296 323 - ivpu_dbg(vdev, FW_BOOT, "Size: file %lu image %u runtime %u shavenn %u\n", 324 - fw->file->size, fw->image_size, fw->runtime_size, fw->shave_nn_size); 325 - ivpu_dbg(vdev, FW_BOOT, "Address: runtime 0x%llx, load 0x%llx, entry point 0x%llx\n", 326 - fw->runtime_addr, image_load_addr, fw->entry_point); 297 + ivpu_dbg(vdev, FW_BOOT, "Boot params: address 0x%llx, size %llu\n", 298 + fw->boot_params_addr, fw->boot_params_size); 299 + ivpu_dbg(vdev, FW_BOOT, "FW version: address 0x%llx, size %llu\n", 300 + fw->fw_version_addr, fw->fw_version_size); 301 + ivpu_dbg(vdev, FW_BOOT, "Runtime: address 0x%llx, size %u\n", 302 + fw->runtime_addr, fw->runtime_size); 303 + ivpu_dbg(vdev, FW_BOOT, "Image load offset: 0x%llx, size %u\n", 304 + fw->image_load_offset, fw->image_size); 327 305 ivpu_dbg(vdev, FW_BOOT, "Read-only section: address 0x%llx, size %u\n", 328 306 fw->read_only_addr, fw->read_only_size); 307 + ivpu_dbg(vdev, FW_BOOT, "FW entry point: 0x%llx\n", fw->entry_point); 308 + ivpu_dbg(vdev, FW_BOOT, "SHAVE NN size: %u\n", fw->shave_nn_size); 329 309 330 310 return 0; 331 311 } ··· 358 326 IVPU_PRINT_WA(disable_d0i3_msg); 359 327 } 360 328 361 - static int ivpu_fw_update_global_range(struct ivpu_device *vdev) 362 - { 363 - struct ivpu_fw_info *fw = vdev->fw; 364 - u64 start = ALIGN(fw->runtime_addr + fw->runtime_size, FW_SHARED_MEM_ALIGNMENT); 365 - u64 size = FW_SHARED_MEM_SIZE; 366 - 367 - if (start + size > FW_GLOBAL_MEM_END) { 368 - ivpu_err(vdev, "No space for shared region, start %lld, size %lld\n", start, size); 369 - return -EINVAL; 370 - } 371 - 372 - ivpu_hw_range_init(&vdev->hw->ranges.global, start, size); 373 - return 0; 374 - } 375 - 376 329 static int ivpu_fw_mem_init(struct ivpu_device *vdev) 377 330 { 378 331 struct ivpu_fw_info *fw = vdev->fw; 379 - struct ivpu_addr_range fw_range; 380 332 int log_verb_size; 381 333 int ret; 382 334 383 - ret = ivpu_fw_update_global_range(vdev); 384 - if (ret) 385 - return ret; 335 + fw->mem_bp = ivpu_bo_create_runtime(vdev, fw->boot_params_addr, fw->boot_params_size, 336 + DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); 337 + if (!fw->mem_bp) { 338 + ivpu_err(vdev, "Failed to create firmware boot params memory buffer\n"); 339 + return -ENOMEM; 340 + } 386 341 387 - fw_range.start = fw->runtime_addr; 388 - fw_range.end = fw->runtime_addr + fw->runtime_size; 389 - fw->mem = ivpu_bo_create(vdev, &vdev->gctx, &fw_range, fw->runtime_size, 390 - DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); 342 + fw->mem_fw_ver = ivpu_bo_create_runtime(vdev, fw->fw_version_addr, fw->fw_version_size, 343 + DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); 344 + if (!fw->mem_fw_ver) { 345 + ivpu_err(vdev, "Failed to create firmware version memory buffer\n"); 346 + ret = -ENOMEM; 347 + goto err_free_bp; 348 + } 349 + 350 + fw->mem = ivpu_bo_create_runtime(vdev, fw->runtime_addr, fw->runtime_size, 351 + DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); 391 352 if (!fw->mem) { 392 353 ivpu_err(vdev, "Failed to create firmware runtime memory buffer\n"); 393 - return -ENOMEM; 354 + ret = -ENOMEM; 355 + goto err_free_fw_ver; 394 356 } 395 357 396 358 ret = ivpu_mmu_context_set_pages_ro(vdev, &vdev->gctx, fw->read_only_addr, ··· 433 407 ivpu_bo_free(fw->mem_log_crit); 434 408 err_free_fw_mem: 435 409 ivpu_bo_free(fw->mem); 410 + err_free_fw_ver: 411 + ivpu_bo_free(fw->mem_fw_ver); 412 + err_free_bp: 413 + ivpu_bo_free(fw->mem_bp); 436 414 return ret; 437 415 } 438 416 ··· 452 422 ivpu_bo_free(fw->mem_log_verb); 453 423 ivpu_bo_free(fw->mem_log_crit); 454 424 ivpu_bo_free(fw->mem); 425 + ivpu_bo_free(fw->mem_fw_ver); 426 + ivpu_bo_free(fw->mem_bp); 455 427 456 428 fw->mem_log_verb = NULL; 457 429 fw->mem_log_crit = NULL; 458 430 fw->mem = NULL; 431 + fw->mem_fw_ver = NULL; 432 + fw->mem_bp = NULL; 459 433 } 460 434 461 435 int ivpu_fw_init(struct ivpu_device *vdev) ··· 632 598 return; 633 599 } 634 600 601 + memset(boot_params, 0, sizeof(*boot_params)); 635 602 vdev->pm->is_warmboot = false; 636 603 637 604 boot_params->magic = VPU_BOOT_PARAMS_MAGIC;
+7
drivers/accel/ivpu/ivpu_fw.h
··· 19 19 const struct firmware *file; 20 20 const char *name; 21 21 char version[FW_VERSION_STR_SIZE]; 22 + struct ivpu_bo *mem_bp; 23 + struct ivpu_bo *mem_fw_ver; 22 24 struct ivpu_bo *mem; 23 25 struct ivpu_bo *mem_shave_nn; 24 26 struct ivpu_bo *mem_log_crit; 25 27 struct ivpu_bo *mem_log_verb; 28 + u64 boot_params_addr; 29 + u64 boot_params_size; 30 + u64 fw_version_addr; 31 + u64 fw_version_size; 26 32 u64 runtime_addr; 27 33 u32 runtime_size; 28 34 u64 image_load_offset; ··· 48 42 u64 last_heartbeat; 49 43 }; 50 44 45 + bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range); 51 46 int ivpu_fw_init(struct ivpu_device *vdev); 52 47 void ivpu_fw_fini(struct ivpu_device *vdev); 53 48 void ivpu_fw_load(struct ivpu_device *vdev);
+16
drivers/accel/ivpu/ivpu_gem.c
··· 15 15 #include <drm/drm_utils.h> 16 16 17 17 #include "ivpu_drv.h" 18 + #include "ivpu_fw.h" 18 19 #include "ivpu_gem.h" 19 20 #include "ivpu_hw.h" 20 21 #include "ivpu_mmu.h" ··· 390 389 err_put: 391 390 drm_gem_object_put(&bo->base.base); 392 391 return NULL; 392 + } 393 + 394 + struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags) 395 + { 396 + struct ivpu_addr_range range; 397 + 398 + if (!ivpu_is_within_range(addr, size, &vdev->hw->ranges.runtime)) { 399 + ivpu_err(vdev, "Invalid runtime BO address 0x%llx size %llu\n", addr, size); 400 + return NULL; 401 + } 402 + 403 + if (ivpu_hw_range_init(vdev, &range, addr, size)) 404 + return NULL; 405 + 406 + return ivpu_bo_create(vdev, &vdev->gctx, &range, size, flags); 393 407 } 394 408 395 409 struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags)
+1
drivers/accel/ivpu/ivpu_gem.h
··· 31 31 struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); 32 32 struct ivpu_bo *ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 33 33 struct ivpu_addr_range *range, u64 size, u32 flags); 34 + struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags); 34 35 struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags); 35 36 void ivpu_bo_free(struct ivpu_bo *bo); 36 37
+29 -7
drivers/accel/ivpu/ivpu_hw.c
··· 20 20 MODULE_PARM_DESC(fail_hw, "<interval>,<probability>,<space>,<times>"); 21 21 #endif 22 22 23 + #define FW_SHARED_MEM_ALIGNMENT SZ_512K /* VPU MTRR limitation */ 24 + 23 25 static char *platform_to_str(u32 platform) 24 26 { 25 27 switch (platform) { ··· 149 147 vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 200000; 150 148 } 151 149 150 + int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start, u64 size) 151 + { 152 + u64 end; 153 + 154 + if (!range || check_add_overflow(start, size, &end)) { 155 + ivpu_err(vdev, "Invalid range: start 0x%llx size %llu\n", start, size); 156 + return -EINVAL; 157 + } 158 + 159 + range->start = start; 160 + range->end = end; 161 + 162 + return 0; 163 + } 164 + 152 165 static void memory_ranges_init(struct ivpu_device *vdev) 153 166 { 154 167 if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { 155 - ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M); 156 - ivpu_hw_range_init(&vdev->hw->ranges.user, 0x88000000, 511 * SZ_1M); 157 - ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x180000000, SZ_2G); 158 - ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_128G); 168 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x84800000, SZ_64M); 169 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M); 170 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0xa0000000, 511 * SZ_1M); 171 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x180000000, SZ_2G); 172 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.dma, 0x200000000, SZ_128G); 159 173 } else { 160 - ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M); 161 - ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x80000000, SZ_2G); 162 - ivpu_hw_range_init(&vdev->hw->ranges.user, 0x100000000, SZ_256G); 174 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x80000000, SZ_64M); 175 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M); 176 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x80000000, SZ_2G); 177 + ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0x100000000, SZ_256G); 163 178 vdev->hw->ranges.dma = vdev->hw->ranges.user; 164 179 } 180 + 181 + drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vdev->hw->ranges.global.start, 182 + FW_SHARED_MEM_ALIGNMENT)); 165 183 } 166 184 167 185 static int wp_enable(struct ivpu_device *vdev)
+3 -6
drivers/accel/ivpu/ivpu_hw.h
··· 21 21 bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq); 22 22 } irq; 23 23 struct { 24 + struct ivpu_addr_range runtime; 24 25 struct ivpu_addr_range global; 25 26 struct ivpu_addr_range user; 26 27 struct ivpu_addr_range shave; ··· 52 51 }; 53 52 54 53 int ivpu_hw_init(struct ivpu_device *vdev); 54 + int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start, 55 + u64 size); 55 56 int ivpu_hw_power_up(struct ivpu_device *vdev); 56 57 int ivpu_hw_power_down(struct ivpu_device *vdev); 57 58 int ivpu_hw_reset(struct ivpu_device *vdev); ··· 72 69 static inline u32 ivpu_hw_ip_irq_handler(struct ivpu_device *vdev, int irq) 73 70 { 74 71 return vdev->hw->irq.ip_irq_handler(vdev, irq); 75 - } 76 - 77 - static inline void ivpu_hw_range_init(struct ivpu_addr_range *range, u64 start, u64 size) 78 - { 79 - range->start = start; 80 - range->end = start + size; 81 72 } 82 73 83 74 static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range)
+1 -1
drivers/accel/ivpu/ivpu_mmu_context.c
··· 568 568 mutex_init(&ctx->lock); 569 569 570 570 if (!context_id) { 571 - start = vdev->hw->ranges.global.start; 571 + start = vdev->hw->ranges.runtime.start; 572 572 end = vdev->hw->ranges.shave.end; 573 573 } else { 574 574 start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
+1 -1
drivers/accel/ivpu/ivpu_pm.c
··· 54 54 static void ivpu_pm_prepare_warm_boot(struct ivpu_device *vdev) 55 55 { 56 56 struct ivpu_fw_info *fw = vdev->fw; 57 - struct vpu_boot_params *bp = ivpu_bo_vaddr(fw->mem); 57 + struct vpu_boot_params *bp = ivpu_bo_vaddr(fw->mem_bp); 58 58 59 59 if (!bp->save_restore_ret_address) { 60 60 ivpu_pm_prepare_cold_boot(vdev);