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

book3s64/hash: Add kfence functionality

Now that linear map functionality of debug_pagealloc is made generic,
enable kfence to use this generic infrastructure.

1. Define kfence related linear map variables.
- u8 *linear_map_kf_hash_slots;
- unsigned long linear_map_kf_hash_count;
- DEFINE_RAW_SPINLOCK(linear_map_kf_hash_lock);
2. The linear map size allocated in RMA region is quite small
(KFENCE_POOL_SIZE >> PAGE_SHIFT) which is 512 bytes by default.
3. kfence pool memory is reserved using memblock_phys_alloc() which has
can come from anywhere.
(default 255 objects => ((1+255) * 2) << PAGE_SHIFT = 32MB)
4. The hash slot information for kfence memory gets added in linear map
in hash_linear_map_add_slot() (which also adds for debug_pagealloc).

Reported-by: Pavithra Prakash <pavrampu@linux.vnet.ibm.com>
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://patch.msgid.link/5c2b61941b344077a2b8654dab46efa0322af3af.1729271995.git.ritesh.list@gmail.com

authored by

Ritesh Harjani (IBM) and committed by
Michael Ellerman
8fec58f5 47dd2e63

+150 -19
-5
arch/powerpc/include/asm/kfence.h
··· 10 10 11 11 #include <linux/mm.h> 12 12 #include <asm/pgtable.h> 13 - #include <asm/mmu.h> 14 13 15 14 #ifdef CONFIG_PPC64_ELF_ABI_V1 16 15 #define ARCH_FUNC_PREFIX "." ··· 25 26 26 27 static inline bool arch_kfence_init_pool(void) 27 28 { 28 - #ifdef CONFIG_PPC64 29 - if (!radix_enabled()) 30 - return false; 31 - #endif 32 29 return !kfence_disabled; 33 30 } 34 31 #endif
+150 -14
arch/powerpc/mm/book3s64/hash_utils.c
··· 40 40 #include <linux/random.h> 41 41 #include <linux/elf-randomize.h> 42 42 #include <linux/of_fdt.h> 43 + #include <linux/kfence.h> 43 44 44 45 #include <asm/interrupt.h> 45 46 #include <asm/processor.h> ··· 67 66 #include <asm/pte-walk.h> 68 67 #include <asm/asm-prototypes.h> 69 68 #include <asm/ultravisor.h> 69 + #include <asm/kfence.h> 70 70 71 71 #include <mm/mmu_decl.h> 72 72 ··· 273 271 WARN(1, "%s called on pre-POWER7 CPU\n", __func__); 274 272 } 275 273 276 - #ifdef CONFIG_DEBUG_PAGEALLOC 274 + #if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KFENCE) 277 275 static void kernel_map_linear_page(unsigned long vaddr, unsigned long idx, 278 276 u8 *slots, raw_spinlock_t *lock) 279 277 { ··· 327 325 mmu_linear_psize, 328 326 mmu_kernel_ssize, 0); 329 327 } 328 + #endif 330 329 330 + #ifdef CONFIG_DEBUG_PAGEALLOC 331 331 static u8 *linear_map_hash_slots; 332 332 static unsigned long linear_map_hash_count; 333 333 static DEFINE_RAW_SPINLOCK(linear_map_hash_lock); 334 - static inline void hash_debug_pagealloc_alloc_slots(void) 334 + static void hash_debug_pagealloc_alloc_slots(void) 335 335 { 336 336 unsigned long max_hash_count = ppc64_rma_size / 4; 337 337 ··· 356 352 __func__, linear_map_hash_count, &ppc64_rma_size); 357 353 } 358 354 359 - static inline void hash_debug_pagealloc_add_slot(phys_addr_t paddr, int slot) 355 + static inline void hash_debug_pagealloc_add_slot(phys_addr_t paddr, 356 + int slot) 360 357 { 361 358 if (!debug_pagealloc_enabled() || !linear_map_hash_count) 362 359 return; ··· 391 386 return 0; 392 387 } 393 388 394 - int hash__kernel_map_pages(struct page *page, int numpages, int enable) 395 - { 396 - return hash_debug_pagealloc_map_pages(page, numpages, enable); 397 - } 398 - 399 389 #else /* CONFIG_DEBUG_PAGEALLOC */ 400 - int hash__kernel_map_pages(struct page *page, int numpages, 401 - int enable) 390 + static inline void hash_debug_pagealloc_alloc_slots(void) {} 391 + static inline void hash_debug_pagealloc_add_slot(phys_addr_t paddr, int slot) {} 392 + static int __maybe_unused 393 + hash_debug_pagealloc_map_pages(struct page *page, int numpages, int enable) 402 394 { 403 395 return 0; 404 396 } 405 - static inline void hash_debug_pagealloc_alloc_slots(void) {} 406 - static inline void hash_debug_pagealloc_add_slot(phys_addr_t paddr, int slot) {} 407 397 #endif /* CONFIG_DEBUG_PAGEALLOC */ 398 + 399 + #ifdef CONFIG_KFENCE 400 + static u8 *linear_map_kf_hash_slots; 401 + static unsigned long linear_map_kf_hash_count; 402 + static DEFINE_RAW_SPINLOCK(linear_map_kf_hash_lock); 403 + 404 + static phys_addr_t kfence_pool; 405 + 406 + static inline void hash_kfence_alloc_pool(void) 407 + { 408 + 409 + /* allocate linear map for kfence within RMA region */ 410 + linear_map_kf_hash_count = KFENCE_POOL_SIZE >> PAGE_SHIFT; 411 + linear_map_kf_hash_slots = memblock_alloc_try_nid( 412 + linear_map_kf_hash_count, 1, 413 + MEMBLOCK_LOW_LIMIT, ppc64_rma_size, 414 + NUMA_NO_NODE); 415 + if (!linear_map_kf_hash_slots) { 416 + pr_err("%s: memblock for linear map (%lu) failed\n", __func__, 417 + linear_map_kf_hash_count); 418 + goto err; 419 + } 420 + 421 + /* allocate kfence pool early */ 422 + kfence_pool = memblock_phys_alloc_range(KFENCE_POOL_SIZE, PAGE_SIZE, 423 + MEMBLOCK_LOW_LIMIT, MEMBLOCK_ALLOC_ANYWHERE); 424 + if (!kfence_pool) { 425 + pr_err("%s: memblock for kfence pool (%lu) failed\n", __func__, 426 + KFENCE_POOL_SIZE); 427 + memblock_free(linear_map_kf_hash_slots, 428 + linear_map_kf_hash_count); 429 + linear_map_kf_hash_count = 0; 430 + goto err; 431 + } 432 + memblock_mark_nomap(kfence_pool, KFENCE_POOL_SIZE); 433 + 434 + return; 435 + err: 436 + pr_info("Disabling kfence\n"); 437 + disable_kfence(); 438 + } 439 + 440 + static inline void hash_kfence_map_pool(void) 441 + { 442 + unsigned long kfence_pool_start, kfence_pool_end; 443 + unsigned long prot = pgprot_val(PAGE_KERNEL); 444 + 445 + if (!kfence_pool) 446 + return; 447 + 448 + kfence_pool_start = (unsigned long) __va(kfence_pool); 449 + kfence_pool_end = kfence_pool_start + KFENCE_POOL_SIZE; 450 + __kfence_pool = (char *) kfence_pool_start; 451 + BUG_ON(htab_bolt_mapping(kfence_pool_start, kfence_pool_end, 452 + kfence_pool, prot, mmu_linear_psize, 453 + mmu_kernel_ssize)); 454 + memblock_clear_nomap(kfence_pool, KFENCE_POOL_SIZE); 455 + } 456 + 457 + static inline void hash_kfence_add_slot(phys_addr_t paddr, int slot) 458 + { 459 + unsigned long vaddr = (unsigned long) __va(paddr); 460 + unsigned long lmi = (vaddr - (unsigned long)__kfence_pool) 461 + >> PAGE_SHIFT; 462 + 463 + if (!kfence_pool) 464 + return; 465 + BUG_ON(!is_kfence_address((void *)vaddr)); 466 + BUG_ON(lmi >= linear_map_kf_hash_count); 467 + linear_map_kf_hash_slots[lmi] = slot | 0x80; 468 + } 469 + 470 + static int hash_kfence_map_pages(struct page *page, int numpages, int enable) 471 + { 472 + unsigned long flags, vaddr, lmi; 473 + int i; 474 + 475 + WARN_ON_ONCE(!linear_map_kf_hash_count); 476 + local_irq_save(flags); 477 + for (i = 0; i < numpages; i++, page++) { 478 + vaddr = (unsigned long)page_address(page); 479 + lmi = (vaddr - (unsigned long)__kfence_pool) >> PAGE_SHIFT; 480 + 481 + /* Ideally this should never happen */ 482 + if (lmi >= linear_map_kf_hash_count) { 483 + WARN_ON_ONCE(1); 484 + continue; 485 + } 486 + 487 + if (enable) 488 + kernel_map_linear_page(vaddr, lmi, 489 + linear_map_kf_hash_slots, 490 + &linear_map_kf_hash_lock); 491 + else 492 + kernel_unmap_linear_page(vaddr, lmi, 493 + linear_map_kf_hash_slots, 494 + &linear_map_kf_hash_lock); 495 + } 496 + local_irq_restore(flags); 497 + return 0; 498 + } 499 + #else 500 + static inline void hash_kfence_alloc_pool(void) {} 501 + static inline void hash_kfence_map_pool(void) {} 502 + static inline void hash_kfence_add_slot(phys_addr_t paddr, int slot) {} 503 + static int __maybe_unused 504 + hash_kfence_map_pages(struct page *page, int numpages, int enable) 505 + { 506 + return 0; 507 + } 508 + #endif 509 + 510 + #if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KFENCE) 511 + int hash__kernel_map_pages(struct page *page, int numpages, int enable) 512 + { 513 + void *vaddr = page_address(page); 514 + 515 + if (is_kfence_address(vaddr)) 516 + return hash_kfence_map_pages(page, numpages, enable); 517 + else 518 + return hash_debug_pagealloc_map_pages(page, numpages, enable); 519 + } 520 + 521 + static void hash_linear_map_add_slot(phys_addr_t paddr, int slot) 522 + { 523 + if (is_kfence_address(__va(paddr))) 524 + hash_kfence_add_slot(paddr, slot); 525 + else 526 + hash_debug_pagealloc_add_slot(paddr, slot); 527 + } 528 + #else 529 + static void hash_linear_map_add_slot(phys_addr_t paddr, int slot) {} 530 + #endif 408 531 409 532 /* 410 533 * 'R' and 'C' update notes: ··· 692 559 break; 693 560 694 561 cond_resched(); 695 - hash_debug_pagealloc_add_slot(paddr, ret); 562 + /* add slot info in debug_pagealloc / kfence linear map */ 563 + hash_linear_map_add_slot(paddr, ret); 696 564 } 697 565 return ret < 0 ? ret : 0; 698 566 } ··· 1074 940 bool aligned = true; 1075 941 init_hpte_page_sizes(); 1076 942 1077 - if (!debug_pagealloc_enabled()) { 943 + if (!debug_pagealloc_enabled_or_kfence()) { 1078 944 /* 1079 945 * Pick a size for the linear mapping. Currently, we only 1080 946 * support 16M, 1M and 4K which is the default ··· 1395 1261 prot = pgprot_val(PAGE_KERNEL); 1396 1262 1397 1263 hash_debug_pagealloc_alloc_slots(); 1264 + hash_kfence_alloc_pool(); 1398 1265 /* create bolted the linear mapping in the hash table */ 1399 1266 for_each_mem_range(i, &base, &end) { 1400 1267 size = end - base; ··· 1412 1277 BUG_ON(htab_bolt_mapping(base, base + size, __pa(base), 1413 1278 prot, mmu_linear_psize, mmu_kernel_ssize)); 1414 1279 } 1280 + hash_kfence_map_pool(); 1415 1281 memblock_set_current_limit(MEMBLOCK_ALLOC_ANYWHERE); 1416 1282 1417 1283 /*