summaryrefslogtreecommitdiff
path: root/drivers/iommu/generic_pt/pt_fmt_defaults.h
blob: 69fb7c2314ca10ede92294b6969293a08dfb189b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
 *
 * Default definitions for formats that don't define these functions.
 */
#ifndef __GENERIC_PT_PT_FMT_DEFAULTS_H
#define __GENERIC_PT_PT_FMT_DEFAULTS_H

#include "pt_defs.h"
#include <linux/log2.h>

/* Header self-compile default defines */
#ifndef pt_load_entry_raw
#include "fmt/amdv1.h"
#endif

/*
 * The format must provide PT_GRANULE_LG2SZ, PT_TABLEMEM_LG2SZ, and
 * PT_ITEM_WORD_SIZE. They must be the same at every level excluding the top.
 */
#ifndef pt_table_item_lg2sz
static inline unsigned int pt_table_item_lg2sz(const struct pt_state *pts)
{
	return PT_GRANULE_LG2SZ +
	       (PT_TABLEMEM_LG2SZ - ilog2(PT_ITEM_WORD_SIZE)) * pts->level;
}
#endif

#ifndef pt_pgsz_lg2_to_level
static inline unsigned int pt_pgsz_lg2_to_level(struct pt_common *common,
						unsigned int pgsize_lg2)
{
	return ((unsigned int)(pgsize_lg2 - PT_GRANULE_LG2SZ)) /
	       (PT_TABLEMEM_LG2SZ - ilog2(PT_ITEM_WORD_SIZE));
}
#endif

/*
 * If not supplied by the format then contiguous pages are not supported.
 *
 * If contiguous pages are supported then the format must also provide
 * pt_contig_count_lg2() if it supports a single contiguous size per level,
 * or pt_possible_sizes() if it supports multiple sizes per level.
 */
#ifndef pt_entry_num_contig_lg2
static inline unsigned int pt_entry_num_contig_lg2(const struct pt_state *pts)
{
	return ilog2(1);
}

/*
 * Return the number of contiguous OA items forming an entry at this table level
 */
static inline unsigned short pt_contig_count_lg2(const struct pt_state *pts)
{
	return ilog2(1);
}
#endif

/* If not supplied by the format then dirty tracking is not supported */
#ifndef pt_entry_is_write_dirty
static inline bool pt_entry_is_write_dirty(const struct pt_state *pts)
{
	return false;
}

static inline void pt_entry_make_write_clean(struct pt_state *pts)
{
}

static inline bool pt_dirty_supported(struct pt_common *common)
{
	return false;
}
#else
/* If not supplied then dirty tracking is always enabled */
#ifndef pt_dirty_supported
static inline bool pt_dirty_supported(struct pt_common *common)
{
	return true;
}
#endif
#endif

#ifndef pt_entry_make_write_dirty
static inline bool pt_entry_make_write_dirty(struct pt_state *pts)
{
	return false;
}
#endif

/*
 * Format supplies either:
 *   pt_entry_oa - OA is at the start of a contiguous entry
 * or
 *   pt_item_oa  - OA is adjusted for every item in a contiguous entry
 *
 * Build the missing one
 *
 * The internal helper _pt_entry_oa_fast() allows generating
 * an efficient pt_entry_oa_exact(), it doesn't care which
 * option is selected.
 */
#ifdef pt_entry_oa
static inline pt_oaddr_t pt_item_oa(const struct pt_state *pts)
{
	return pt_entry_oa(pts) |
	       log2_mul(pts->index, pt_table_item_lg2sz(pts));
}
#define _pt_entry_oa_fast pt_entry_oa
#endif

#ifdef pt_item_oa
static inline pt_oaddr_t pt_entry_oa(const struct pt_state *pts)
{
	return log2_set_mod(pt_item_oa(pts), 0,
			    pt_entry_num_contig_lg2(pts) +
				    pt_table_item_lg2sz(pts));
}
#define _pt_entry_oa_fast pt_item_oa
#endif

/*
 * If not supplied by the format then use the constant
 * PT_MAX_OUTPUT_ADDRESS_LG2.
 */
#ifndef pt_max_oa_lg2
static inline unsigned int
pt_max_oa_lg2(const struct pt_common *common)
{
	return PT_MAX_OUTPUT_ADDRESS_LG2;
}
#endif

#ifndef pt_has_system_page_size
static inline bool pt_has_system_page_size(const struct pt_common *common)
{
	return PT_GRANULE_LG2SZ == PAGE_SHIFT;
}
#endif

/*
 * If not supplied by the format then assume only one contiguous size determined
 * by pt_contig_count_lg2()
 */
#ifndef pt_possible_sizes
static inline unsigned short pt_contig_count_lg2(const struct pt_state *pts);

/* Return a bitmap of possible leaf page sizes at this level */
static inline pt_vaddr_t pt_possible_sizes(const struct pt_state *pts)
{
	unsigned int isz_lg2 = pt_table_item_lg2sz(pts);

	if (!pt_can_have_leaf(pts))
		return 0;
	return log2_to_int(isz_lg2) |
	       log2_to_int(pt_contig_count_lg2(pts) + isz_lg2);
}
#endif

/* If not supplied by the format then use 0. */
#ifndef pt_full_va_prefix
static inline pt_vaddr_t pt_full_va_prefix(const struct pt_common *common)
{
	return 0;
}
#endif

/* If not supplied by the format then zero fill using PT_ITEM_WORD_SIZE */
#ifndef pt_clear_entries
static inline void pt_clear_entries64(struct pt_state *pts,
				      unsigned int num_contig_lg2)
{
	u64 *tablep = pt_cur_table(pts, u64) + pts->index;
	u64 *end = tablep + log2_to_int(num_contig_lg2);

	PT_WARN_ON(log2_mod(pts->index, num_contig_lg2));
	for (; tablep != end; tablep++)
		WRITE_ONCE(*tablep, 0);
}

static inline void pt_clear_entries32(struct pt_state *pts,
				      unsigned int num_contig_lg2)
{
	u32 *tablep = pt_cur_table(pts, u32) + pts->index;
	u32 *end = tablep + log2_to_int(num_contig_lg2);

	PT_WARN_ON(log2_mod(pts->index, num_contig_lg2));
	for (; tablep != end; tablep++)
		WRITE_ONCE(*tablep, 0);
}

static inline void pt_clear_entries(struct pt_state *pts,
				    unsigned int num_contig_lg2)
{
	if (PT_ITEM_WORD_SIZE == sizeof(u32))
		pt_clear_entries32(pts, num_contig_lg2);
	else
		pt_clear_entries64(pts, num_contig_lg2);
}
#define pt_clear_entries pt_clear_entries
#endif

/* If not supplied then SW bits are not supported */
#ifdef pt_sw_bit
static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
					  unsigned int bitnr)
{
	/* Acquire, pairs with pt_set_sw_bit_release() */
	smp_mb();
	/* For a contiguous entry the sw bit is only stored in the first item. */
	return pts->entry & pt_sw_bit(bitnr);
}
#define pt_test_sw_bit_acquire pt_test_sw_bit_acquire

static inline void pt_set_sw_bit_release(struct pt_state *pts,
					 unsigned int bitnr)
{
#if !IS_ENABLED(CONFIG_GENERIC_ATOMIC64)
	if (PT_ITEM_WORD_SIZE == sizeof(u64)) {
		u64 *entryp = pt_cur_table(pts, u64) + pts->index;
		u64 old_entry = pts->entry;
		u64 new_entry;

		do {
			new_entry = old_entry | pt_sw_bit(bitnr);
		} while (!try_cmpxchg64_release(entryp, &old_entry, new_entry));
		pts->entry = new_entry;
		return;
	}
#endif
	if (PT_ITEM_WORD_SIZE == sizeof(u32)) {
		u32 *entryp = pt_cur_table(pts, u32) + pts->index;
		u32 old_entry = pts->entry;
		u32 new_entry;

		do {
			new_entry = old_entry | pt_sw_bit(bitnr);
		} while (!try_cmpxchg_release(entryp, &old_entry, new_entry));
		pts->entry = new_entry;
	} else
		BUILD_BUG();
}
#define pt_set_sw_bit_release pt_set_sw_bit_release
#else
static inline unsigned int pt_max_sw_bit(struct pt_common *common)
{
	return 0;
}

extern void __pt_no_sw_bit(void);
static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
					  unsigned int bitnr)
{
	__pt_no_sw_bit();
	return false;
}

static inline void pt_set_sw_bit_release(struct pt_state *pts,
					 unsigned int bitnr)
{
	__pt_no_sw_bit();
}
#endif

/*
 * Format can call in the pt_install_leaf_entry() to check the arguments are all
 * aligned correctly.
 */
static inline bool pt_check_install_leaf_args(struct pt_state *pts,
					      pt_oaddr_t oa,
					      unsigned int oasz_lg2)
{
	unsigned int isz_lg2 = pt_table_item_lg2sz(pts);

	if (PT_WARN_ON(oalog2_mod(oa, oasz_lg2)))
		return false;

#ifdef pt_possible_sizes
	if (PT_WARN_ON(isz_lg2 > oasz_lg2 ||
		       oasz_lg2 > isz_lg2 + pt_num_items_lg2(pts)))
		return false;
#else
	if (PT_WARN_ON(oasz_lg2 != isz_lg2 &&
		       oasz_lg2 != isz_lg2 + pt_contig_count_lg2(pts)))
		return false;
#endif

	if (PT_WARN_ON(oalog2_mod(pts->index, oasz_lg2 - isz_lg2)))
		return false;
	return true;
}

#endif