xref: /linux/drivers/iommu/generic_pt/pt_fmt_defaults.h (revision ce5cfb0fa20dc6454da039612e34325b7b4a8243)
17c5b184dSJason Gunthorpe /* SPDX-License-Identifier: GPL-2.0-only */
27c5b184dSJason Gunthorpe /*
37c5b184dSJason Gunthorpe  * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
47c5b184dSJason Gunthorpe  *
57c5b184dSJason Gunthorpe  * Default definitions for formats that don't define these functions.
67c5b184dSJason Gunthorpe  */
77c5b184dSJason Gunthorpe #ifndef __GENERIC_PT_PT_FMT_DEFAULTS_H
87c5b184dSJason Gunthorpe #define __GENERIC_PT_PT_FMT_DEFAULTS_H
97c5b184dSJason Gunthorpe 
107c5b184dSJason Gunthorpe #include "pt_defs.h"
117c5b184dSJason Gunthorpe #include <linux/log2.h>
127c5b184dSJason Gunthorpe 
137c5b184dSJason Gunthorpe /* Header self-compile default defines */
147c5b184dSJason Gunthorpe #ifndef pt_load_entry_raw
157c5b184dSJason Gunthorpe #include "fmt/amdv1.h"
167c5b184dSJason Gunthorpe #endif
177c5b184dSJason Gunthorpe 
187c5b184dSJason Gunthorpe /*
197c5b184dSJason Gunthorpe  * The format must provide PT_GRANULE_LG2SZ, PT_TABLEMEM_LG2SZ, and
207c5b184dSJason Gunthorpe  * PT_ITEM_WORD_SIZE. They must be the same at every level excluding the top.
217c5b184dSJason Gunthorpe  */
227c5b184dSJason Gunthorpe #ifndef pt_table_item_lg2sz
pt_table_item_lg2sz(const struct pt_state * pts)237c5b184dSJason Gunthorpe static inline unsigned int pt_table_item_lg2sz(const struct pt_state *pts)
247c5b184dSJason Gunthorpe {
257c5b184dSJason Gunthorpe 	return PT_GRANULE_LG2SZ +
267c5b184dSJason Gunthorpe 	       (PT_TABLEMEM_LG2SZ - ilog2(PT_ITEM_WORD_SIZE)) * pts->level;
277c5b184dSJason Gunthorpe }
287c5b184dSJason Gunthorpe #endif
297c5b184dSJason Gunthorpe 
307c5b184dSJason Gunthorpe #ifndef pt_pgsz_lg2_to_level
pt_pgsz_lg2_to_level(struct pt_common * common,unsigned int pgsize_lg2)317c5b184dSJason Gunthorpe static inline unsigned int pt_pgsz_lg2_to_level(struct pt_common *common,
327c5b184dSJason Gunthorpe 						unsigned int pgsize_lg2)
337c5b184dSJason Gunthorpe {
347c5b184dSJason Gunthorpe 	return ((unsigned int)(pgsize_lg2 - PT_GRANULE_LG2SZ)) /
357c5b184dSJason Gunthorpe 	       (PT_TABLEMEM_LG2SZ - ilog2(PT_ITEM_WORD_SIZE));
367c5b184dSJason Gunthorpe }
377c5b184dSJason Gunthorpe #endif
387c5b184dSJason Gunthorpe 
397c5b184dSJason Gunthorpe /*
407c5b184dSJason Gunthorpe  * If not supplied by the format then contiguous pages are not supported.
417c5b184dSJason Gunthorpe  *
427c5b184dSJason Gunthorpe  * If contiguous pages are supported then the format must also provide
437c5b184dSJason Gunthorpe  * pt_contig_count_lg2() if it supports a single contiguous size per level,
447c5b184dSJason Gunthorpe  * or pt_possible_sizes() if it supports multiple sizes per level.
457c5b184dSJason Gunthorpe  */
467c5b184dSJason Gunthorpe #ifndef pt_entry_num_contig_lg2
pt_entry_num_contig_lg2(const struct pt_state * pts)477c5b184dSJason Gunthorpe static inline unsigned int pt_entry_num_contig_lg2(const struct pt_state *pts)
487c5b184dSJason Gunthorpe {
497c5b184dSJason Gunthorpe 	return ilog2(1);
507c5b184dSJason Gunthorpe }
517c5b184dSJason Gunthorpe 
527c5b184dSJason Gunthorpe /*
537c5b184dSJason Gunthorpe  * Return the number of contiguous OA items forming an entry at this table level
547c5b184dSJason Gunthorpe  */
pt_contig_count_lg2(const struct pt_state * pts)557c5b184dSJason Gunthorpe static inline unsigned short pt_contig_count_lg2(const struct pt_state *pts)
567c5b184dSJason Gunthorpe {
577c5b184dSJason Gunthorpe 	return ilog2(1);
587c5b184dSJason Gunthorpe }
597c5b184dSJason Gunthorpe #endif
607c5b184dSJason Gunthorpe 
617c5b184dSJason Gunthorpe /* If not supplied by the format then dirty tracking is not supported */
627c5b184dSJason Gunthorpe #ifndef pt_entry_is_write_dirty
pt_entry_is_write_dirty(const struct pt_state * pts)637c5b184dSJason Gunthorpe static inline bool pt_entry_is_write_dirty(const struct pt_state *pts)
647c5b184dSJason Gunthorpe {
657c5b184dSJason Gunthorpe 	return false;
667c5b184dSJason Gunthorpe }
677c5b184dSJason Gunthorpe 
pt_entry_make_write_clean(struct pt_state * pts)687c5b184dSJason Gunthorpe static inline void pt_entry_make_write_clean(struct pt_state *pts)
697c5b184dSJason Gunthorpe {
707c5b184dSJason Gunthorpe }
717c5b184dSJason Gunthorpe 
pt_dirty_supported(struct pt_common * common)727c5b184dSJason Gunthorpe static inline bool pt_dirty_supported(struct pt_common *common)
737c5b184dSJason Gunthorpe {
747c5b184dSJason Gunthorpe 	return false;
757c5b184dSJason Gunthorpe }
767c5b184dSJason Gunthorpe #else
777c5b184dSJason Gunthorpe /* If not supplied then dirty tracking is always enabled */
787c5b184dSJason Gunthorpe #ifndef pt_dirty_supported
pt_dirty_supported(struct pt_common * common)797c5b184dSJason Gunthorpe static inline bool pt_dirty_supported(struct pt_common *common)
807c5b184dSJason Gunthorpe {
817c5b184dSJason Gunthorpe 	return true;
827c5b184dSJason Gunthorpe }
837c5b184dSJason Gunthorpe #endif
847c5b184dSJason Gunthorpe #endif
857c5b184dSJason Gunthorpe 
867c5b184dSJason Gunthorpe #ifndef pt_entry_make_write_dirty
pt_entry_make_write_dirty(struct pt_state * pts)877c5b184dSJason Gunthorpe static inline bool pt_entry_make_write_dirty(struct pt_state *pts)
887c5b184dSJason Gunthorpe {
897c5b184dSJason Gunthorpe 	return false;
907c5b184dSJason Gunthorpe }
917c5b184dSJason Gunthorpe #endif
927c5b184dSJason Gunthorpe 
937c5b184dSJason Gunthorpe /*
947c5b184dSJason Gunthorpe  * Format supplies either:
957c5b184dSJason Gunthorpe  *   pt_entry_oa - OA is at the start of a contiguous entry
967c5b184dSJason Gunthorpe  * or
977c5b184dSJason Gunthorpe  *   pt_item_oa  - OA is adjusted for every item in a contiguous entry
987c5b184dSJason Gunthorpe  *
997c5b184dSJason Gunthorpe  * Build the missing one
1007c5b184dSJason Gunthorpe  *
1017c5b184dSJason Gunthorpe  * The internal helper _pt_entry_oa_fast() allows generating
1027c5b184dSJason Gunthorpe  * an efficient pt_entry_oa_exact(), it doesn't care which
1037c5b184dSJason Gunthorpe  * option is selected.
1047c5b184dSJason Gunthorpe  */
1057c5b184dSJason Gunthorpe #ifdef pt_entry_oa
pt_item_oa(const struct pt_state * pts)1067c5b184dSJason Gunthorpe static inline pt_oaddr_t pt_item_oa(const struct pt_state *pts)
1077c5b184dSJason Gunthorpe {
1087c5b184dSJason Gunthorpe 	return pt_entry_oa(pts) |
1097c5b184dSJason Gunthorpe 	       log2_mul(pts->index, pt_table_item_lg2sz(pts));
1107c5b184dSJason Gunthorpe }
1117c5b184dSJason Gunthorpe #define _pt_entry_oa_fast pt_entry_oa
1127c5b184dSJason Gunthorpe #endif
1137c5b184dSJason Gunthorpe 
1147c5b184dSJason Gunthorpe #ifdef pt_item_oa
pt_entry_oa(const struct pt_state * pts)1157c5b184dSJason Gunthorpe static inline pt_oaddr_t pt_entry_oa(const struct pt_state *pts)
1167c5b184dSJason Gunthorpe {
1177c5b184dSJason Gunthorpe 	return log2_set_mod(pt_item_oa(pts), 0,
1187c5b184dSJason Gunthorpe 			    pt_entry_num_contig_lg2(pts) +
1197c5b184dSJason Gunthorpe 				    pt_table_item_lg2sz(pts));
1207c5b184dSJason Gunthorpe }
1217c5b184dSJason Gunthorpe #define _pt_entry_oa_fast pt_item_oa
1227c5b184dSJason Gunthorpe #endif
1237c5b184dSJason Gunthorpe 
1247c5b184dSJason Gunthorpe /*
1257c5b184dSJason Gunthorpe  * If not supplied by the format then use the constant
1267c5b184dSJason Gunthorpe  * PT_MAX_OUTPUT_ADDRESS_LG2.
1277c5b184dSJason Gunthorpe  */
1287c5b184dSJason Gunthorpe #ifndef pt_max_oa_lg2
1297c5b184dSJason Gunthorpe static inline unsigned int
pt_max_oa_lg2(const struct pt_common * common)1307c5b184dSJason Gunthorpe pt_max_oa_lg2(const struct pt_common *common)
1317c5b184dSJason Gunthorpe {
1327c5b184dSJason Gunthorpe 	return PT_MAX_OUTPUT_ADDRESS_LG2;
1337c5b184dSJason Gunthorpe }
1347c5b184dSJason Gunthorpe #endif
1357c5b184dSJason Gunthorpe 
1367c5b184dSJason Gunthorpe #ifndef pt_has_system_page_size
pt_has_system_page_size(const struct pt_common * common)1377c5b184dSJason Gunthorpe static inline bool pt_has_system_page_size(const struct pt_common *common)
1387c5b184dSJason Gunthorpe {
1397c5b184dSJason Gunthorpe 	return PT_GRANULE_LG2SZ == PAGE_SHIFT;
1407c5b184dSJason Gunthorpe }
1417c5b184dSJason Gunthorpe #endif
1427c5b184dSJason Gunthorpe 
1437c5b184dSJason Gunthorpe /*
1447c5b184dSJason Gunthorpe  * If not supplied by the format then assume only one contiguous size determined
1457c5b184dSJason Gunthorpe  * by pt_contig_count_lg2()
1467c5b184dSJason Gunthorpe  */
1477c5b184dSJason Gunthorpe #ifndef pt_possible_sizes
1487c5b184dSJason Gunthorpe static inline unsigned short pt_contig_count_lg2(const struct pt_state *pts);
1497c5b184dSJason Gunthorpe 
1507c5b184dSJason Gunthorpe /* Return a bitmap of possible leaf page sizes at this level */
pt_possible_sizes(const struct pt_state * pts)1517c5b184dSJason Gunthorpe static inline pt_vaddr_t pt_possible_sizes(const struct pt_state *pts)
1527c5b184dSJason Gunthorpe {
1537c5b184dSJason Gunthorpe 	unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
1547c5b184dSJason Gunthorpe 
1557c5b184dSJason Gunthorpe 	if (!pt_can_have_leaf(pts))
1567c5b184dSJason Gunthorpe 		return 0;
1577c5b184dSJason Gunthorpe 	return log2_to_int(isz_lg2) |
1587c5b184dSJason Gunthorpe 	       log2_to_int(pt_contig_count_lg2(pts) + isz_lg2);
1597c5b184dSJason Gunthorpe }
1607c5b184dSJason Gunthorpe #endif
1617c5b184dSJason Gunthorpe 
1627c5b184dSJason Gunthorpe /* If not supplied by the format then use 0. */
1637c5b184dSJason Gunthorpe #ifndef pt_full_va_prefix
pt_full_va_prefix(const struct pt_common * common)1647c5b184dSJason Gunthorpe static inline pt_vaddr_t pt_full_va_prefix(const struct pt_common *common)
1657c5b184dSJason Gunthorpe {
1667c5b184dSJason Gunthorpe 	return 0;
1677c5b184dSJason Gunthorpe }
1687c5b184dSJason Gunthorpe #endif
1697c5b184dSJason Gunthorpe 
1707c5b184dSJason Gunthorpe /* If not supplied by the format then zero fill using PT_ITEM_WORD_SIZE */
1717c5b184dSJason Gunthorpe #ifndef pt_clear_entries
pt_clear_entries64(struct pt_state * pts,unsigned int num_contig_lg2)1727c5b184dSJason Gunthorpe static inline void pt_clear_entries64(struct pt_state *pts,
1737c5b184dSJason Gunthorpe 				      unsigned int num_contig_lg2)
1747c5b184dSJason Gunthorpe {
1757c5b184dSJason Gunthorpe 	u64 *tablep = pt_cur_table(pts, u64) + pts->index;
1767c5b184dSJason Gunthorpe 	u64 *end = tablep + log2_to_int(num_contig_lg2);
1777c5b184dSJason Gunthorpe 
1787c5b184dSJason Gunthorpe 	PT_WARN_ON(log2_mod(pts->index, num_contig_lg2));
1797c5b184dSJason Gunthorpe 	for (; tablep != end; tablep++)
1807c5b184dSJason Gunthorpe 		WRITE_ONCE(*tablep, 0);
1817c5b184dSJason Gunthorpe }
1827c5b184dSJason Gunthorpe 
pt_clear_entries32(struct pt_state * pts,unsigned int num_contig_lg2)1837c5b184dSJason Gunthorpe static inline void pt_clear_entries32(struct pt_state *pts,
1847c5b184dSJason Gunthorpe 				      unsigned int num_contig_lg2)
1857c5b184dSJason Gunthorpe {
1867c5b184dSJason Gunthorpe 	u32 *tablep = pt_cur_table(pts, u32) + pts->index;
1877c5b184dSJason Gunthorpe 	u32 *end = tablep + log2_to_int(num_contig_lg2);
1887c5b184dSJason Gunthorpe 
1897c5b184dSJason Gunthorpe 	PT_WARN_ON(log2_mod(pts->index, num_contig_lg2));
1907c5b184dSJason Gunthorpe 	for (; tablep != end; tablep++)
1917c5b184dSJason Gunthorpe 		WRITE_ONCE(*tablep, 0);
1927c5b184dSJason Gunthorpe }
1937c5b184dSJason Gunthorpe 
pt_clear_entries(struct pt_state * pts,unsigned int num_contig_lg2)1947c5b184dSJason Gunthorpe static inline void pt_clear_entries(struct pt_state *pts,
1957c5b184dSJason Gunthorpe 				    unsigned int num_contig_lg2)
1967c5b184dSJason Gunthorpe {
1977c5b184dSJason Gunthorpe 	if (PT_ITEM_WORD_SIZE == sizeof(u32))
1987c5b184dSJason Gunthorpe 		pt_clear_entries32(pts, num_contig_lg2);
1997c5b184dSJason Gunthorpe 	else
2007c5b184dSJason Gunthorpe 		pt_clear_entries64(pts, num_contig_lg2);
2017c5b184dSJason Gunthorpe }
2027c5b184dSJason Gunthorpe #define pt_clear_entries pt_clear_entries
2037c5b184dSJason Gunthorpe #endif
2047c5b184dSJason Gunthorpe 
205*bcc64b57SJason Gunthorpe /* If not supplied then SW bits are not supported */
206*bcc64b57SJason Gunthorpe #ifdef pt_sw_bit
pt_test_sw_bit_acquire(struct pt_state * pts,unsigned int bitnr)207*bcc64b57SJason Gunthorpe static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
208*bcc64b57SJason Gunthorpe 					  unsigned int bitnr)
209*bcc64b57SJason Gunthorpe {
210*bcc64b57SJason Gunthorpe 	/* Acquire, pairs with pt_set_sw_bit_release() */
211*bcc64b57SJason Gunthorpe 	smp_mb();
212*bcc64b57SJason Gunthorpe 	/* For a contiguous entry the sw bit is only stored in the first item. */
213*bcc64b57SJason Gunthorpe 	return pts->entry & pt_sw_bit(bitnr);
214*bcc64b57SJason Gunthorpe }
215*bcc64b57SJason Gunthorpe #define pt_test_sw_bit_acquire pt_test_sw_bit_acquire
216*bcc64b57SJason Gunthorpe 
pt_set_sw_bit_release(struct pt_state * pts,unsigned int bitnr)217*bcc64b57SJason Gunthorpe static inline void pt_set_sw_bit_release(struct pt_state *pts,
218*bcc64b57SJason Gunthorpe 					 unsigned int bitnr)
219*bcc64b57SJason Gunthorpe {
220*bcc64b57SJason Gunthorpe #if !IS_ENABLED(CONFIG_GENERIC_ATOMIC64)
221*bcc64b57SJason Gunthorpe 	if (PT_ITEM_WORD_SIZE == sizeof(u64)) {
222*bcc64b57SJason Gunthorpe 		u64 *entryp = pt_cur_table(pts, u64) + pts->index;
223*bcc64b57SJason Gunthorpe 		u64 old_entry = pts->entry;
224*bcc64b57SJason Gunthorpe 		u64 new_entry;
225*bcc64b57SJason Gunthorpe 
226*bcc64b57SJason Gunthorpe 		do {
227*bcc64b57SJason Gunthorpe 			new_entry = old_entry | pt_sw_bit(bitnr);
228*bcc64b57SJason Gunthorpe 		} while (!try_cmpxchg64_release(entryp, &old_entry, new_entry));
229*bcc64b57SJason Gunthorpe 		pts->entry = new_entry;
230*bcc64b57SJason Gunthorpe 		return;
231*bcc64b57SJason Gunthorpe 	}
232*bcc64b57SJason Gunthorpe #endif
233*bcc64b57SJason Gunthorpe 	if (PT_ITEM_WORD_SIZE == sizeof(u32)) {
234*bcc64b57SJason Gunthorpe 		u32 *entryp = pt_cur_table(pts, u32) + pts->index;
235*bcc64b57SJason Gunthorpe 		u32 old_entry = pts->entry;
236*bcc64b57SJason Gunthorpe 		u32 new_entry;
237*bcc64b57SJason Gunthorpe 
238*bcc64b57SJason Gunthorpe 		do {
239*bcc64b57SJason Gunthorpe 			new_entry = old_entry | pt_sw_bit(bitnr);
240*bcc64b57SJason Gunthorpe 		} while (!try_cmpxchg_release(entryp, &old_entry, new_entry));
241*bcc64b57SJason Gunthorpe 		pts->entry = new_entry;
242*bcc64b57SJason Gunthorpe 	} else
243*bcc64b57SJason Gunthorpe 		BUILD_BUG();
244*bcc64b57SJason Gunthorpe }
245*bcc64b57SJason Gunthorpe #define pt_set_sw_bit_release pt_set_sw_bit_release
246*bcc64b57SJason Gunthorpe #else
pt_max_sw_bit(struct pt_common * common)247*bcc64b57SJason Gunthorpe static inline unsigned int pt_max_sw_bit(struct pt_common *common)
248*bcc64b57SJason Gunthorpe {
249*bcc64b57SJason Gunthorpe 	return 0;
250*bcc64b57SJason Gunthorpe }
251*bcc64b57SJason Gunthorpe 
252*bcc64b57SJason Gunthorpe extern void __pt_no_sw_bit(void);
pt_test_sw_bit_acquire(struct pt_state * pts,unsigned int bitnr)253*bcc64b57SJason Gunthorpe static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
254*bcc64b57SJason Gunthorpe 					  unsigned int bitnr)
255*bcc64b57SJason Gunthorpe {
256*bcc64b57SJason Gunthorpe 	__pt_no_sw_bit();
257*bcc64b57SJason Gunthorpe 	return false;
258*bcc64b57SJason Gunthorpe }
259*bcc64b57SJason Gunthorpe 
pt_set_sw_bit_release(struct pt_state * pts,unsigned int bitnr)260*bcc64b57SJason Gunthorpe static inline void pt_set_sw_bit_release(struct pt_state *pts,
261*bcc64b57SJason Gunthorpe 					 unsigned int bitnr)
262*bcc64b57SJason Gunthorpe {
263*bcc64b57SJason Gunthorpe 	__pt_no_sw_bit();
264*bcc64b57SJason Gunthorpe }
265*bcc64b57SJason Gunthorpe #endif
266*bcc64b57SJason Gunthorpe 
2677c5b184dSJason Gunthorpe /*
2687c5b184dSJason Gunthorpe  * Format can call in the pt_install_leaf_entry() to check the arguments are all
2697c5b184dSJason Gunthorpe  * aligned correctly.
2707c5b184dSJason Gunthorpe  */
pt_check_install_leaf_args(struct pt_state * pts,pt_oaddr_t oa,unsigned int oasz_lg2)2717c5b184dSJason Gunthorpe static inline bool pt_check_install_leaf_args(struct pt_state *pts,
2727c5b184dSJason Gunthorpe 					      pt_oaddr_t oa,
2737c5b184dSJason Gunthorpe 					      unsigned int oasz_lg2)
2747c5b184dSJason Gunthorpe {
2757c5b184dSJason Gunthorpe 	unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
2767c5b184dSJason Gunthorpe 
2777c5b184dSJason Gunthorpe 	if (PT_WARN_ON(oalog2_mod(oa, oasz_lg2)))
2787c5b184dSJason Gunthorpe 		return false;
2797c5b184dSJason Gunthorpe 
2807c5b184dSJason Gunthorpe #ifdef pt_possible_sizes
2817c5b184dSJason Gunthorpe 	if (PT_WARN_ON(isz_lg2 > oasz_lg2 ||
2827c5b184dSJason Gunthorpe 		       oasz_lg2 > isz_lg2 + pt_num_items_lg2(pts)))
2837c5b184dSJason Gunthorpe 		return false;
2847c5b184dSJason Gunthorpe #else
2857c5b184dSJason Gunthorpe 	if (PT_WARN_ON(oasz_lg2 != isz_lg2 &&
2867c5b184dSJason Gunthorpe 		       oasz_lg2 != isz_lg2 + pt_contig_count_lg2(pts)))
2877c5b184dSJason Gunthorpe 		return false;
2887c5b184dSJason Gunthorpe #endif
2897c5b184dSJason Gunthorpe 
2907c5b184dSJason Gunthorpe 	if (PT_WARN_ON(oalog2_mod(pts->index, oasz_lg2 - isz_lg2)))
2917c5b184dSJason Gunthorpe 		return false;
2927c5b184dSJason Gunthorpe 	return true;
2937c5b184dSJason Gunthorpe }
2947c5b184dSJason Gunthorpe 
2957c5b184dSJason Gunthorpe #endif
296