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

Configure Feed

Select the types of activity you want to include in your feed.

iommupt: Add the AMD IOMMU v1 page table format

AMD IOMMU v1 is unique in supporting contiguous pages with a variable size
and it can decode the full 64 bit VA space. Unlike other x86 page tables
this explicitly does not do sign extension as part of allowing the entire
64 bit VA space to be supported.

The general design is quite similar to the x86 PAE format, except with a
6th level and quite different PTE encoding.

This format is the only one that uses the PT_FEAT_DYNAMIC_TOP feature in
the existing code as the existing AMDv1 code starts out with a 3 level
table and adds levels on the fly if more IOVA is needed.

Comparing the performance of several operations to the existing version:

iommu_map()
pgsz ,avg new,old ns, min new,old ns , min % (+ve is better)
2^12, 65,64 , 62,61 , -1.01
2^13, 70,66 , 67,62 , -8.08
2^14, 73,69 , 71,65 , -9.09
2^15, 78,75 , 75,71 , -5.05
2^16, 89,89 , 86,84 , -2.02
2^17, 128,121 , 124,112 , -10.10
2^18, 175,175 , 170,163 , -4.04
2^19, 264,306 , 261,279 , 6.06
2^20, 444,525 , 438,489 , 10.10
2^21, 60,62 , 58,59 , 1.01
256*2^12, 381,1833 , 367,1795 , 79.79
256*2^21, 375,1623 , 356,1555 , 77.77
256*2^30, 356,1338 , 349,1277 , 72.72

iommu_unmap()
pgsz ,avg new,old ns, min new,old ns , min % (+ve is better)
2^12, 76,89 , 71,86 , 17.17
2^13, 79,89 , 75,86 , 12.12
2^14, 78,90 , 74,86 , 13.13
2^15, 82,89 , 74,86 , 13.13
2^16, 79,89 , 74,86 , 13.13
2^17, 81,89 , 77,87 , 11.11
2^18, 90,92 , 87,89 , 2.02
2^19, 91,93 , 88,90 , 2.02
2^20, 96,95 , 91,92 , 1.01
2^21, 72,88 , 68,85 , 20.20
256*2^12, 372,6583 , 364,6251 , 94.94
256*2^21, 398,6032 , 392,5758 , 93.93
256*2^30, 396,5665 , 389,5258 , 92.92

The ~5-17x speedup when working with mutli-PTE map/unmaps is because the
AMD implementation rewalks the entire table on every new PTE while this
version retains its position. The same speedup will be seen with dirtys as
well.

The old implementation triggers a compiler optimization that ends up
generating a "rep stos" memset for contiguous PTEs. Since AMD can have
contiguous PTEs that span 2Kbytes of table this is a huge win compared to
a normal movq loop. It is why the unmap side has a fairly flat runtime as
the contiguous PTE sides increases. This version makes it explicit with a
memset64() call.

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Vasant Hegde <vasant.hegde@amd.com>
Tested-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Tested-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>

authored by

Jason Gunthorpe and committed by
Joerg Roedel
879ced2b cdb39d91

+478
+1
drivers/iommu/Makefile
··· 3 3 obj-$(CONFIG_AMD_IOMMU) += amd/ 4 4 obj-$(CONFIG_INTEL_IOMMU) += intel/ 5 5 obj-$(CONFIG_RISCV_IOMMU) += riscv/ 6 + obj-$(CONFIG_GENERIC_PT) += generic_pt/fmt/ 6 7 obj-$(CONFIG_IOMMU_API) += iommu.o 7 8 obj-$(CONFIG_IOMMU_SUPPORT) += iommu-pages.o 8 9 obj-$(CONFIG_IOMMU_API) += iommu-traces.o
+12
drivers/iommu/generic_pt/Kconfig
··· 30 30 related to struct iommu_domain using GENERIC_PT. It provides a single 31 31 implementation of the page table operations that can be shared by 32 32 multiple drivers. 33 + 34 + if IOMMU_PT 35 + config IOMMU_PT_AMDV1 36 + tristate "IOMMU page table for 64-bit AMD IOMMU v1" 37 + depends on !GENERIC_ATOMIC64 # for cmpxchg64 38 + help 39 + iommu_domain implementation for the AMD v1 page table. AMDv1 is the 40 + "host" page table. It supports granular page sizes of almost every 41 + power of 2 and decodes an full 64-bit IOVA space. 42 + 43 + Selected automatically by an IOMMU driver that uses this format. 44 + endif 33 45 endif
+11
drivers/iommu/generic_pt/fmt/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + iommu_pt_fmt-$(CONFIG_IOMMU_PT_AMDV1) += amdv1 4 + 5 + define create_format 6 + obj-$(2) += iommu_$(1).o 7 + 8 + endef 9 + 10 + $(eval $(foreach fmt,$(iommu_pt_fmt-y),$(call create_format,$(fmt),y))) 11 + $(eval $(foreach fmt,$(iommu_pt_fmt-m),$(call create_format,$(fmt),m)))
+387
drivers/iommu/generic_pt/fmt/amdv1.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES 4 + * 5 + * AMD IOMMU v1 page table 6 + * 7 + * This is described in Section "2.2.3 I/O Page Tables for Host Translations" 8 + * of the "AMD I/O Virtualization Technology (IOMMU) Specification" 9 + * 10 + * Note the level numbering here matches the core code, so level 0 is the same 11 + * as mode 1. 12 + * 13 + */ 14 + #ifndef __GENERIC_PT_FMT_AMDV1_H 15 + #define __GENERIC_PT_FMT_AMDV1_H 16 + 17 + #include "defs_amdv1.h" 18 + #include "../pt_defs.h" 19 + 20 + #include <asm/page.h> 21 + #include <linux/bitfield.h> 22 + #include <linux/container_of.h> 23 + #include <linux/mem_encrypt.h> 24 + #include <linux/minmax.h> 25 + #include <linux/sizes.h> 26 + #include <linux/string.h> 27 + 28 + enum { 29 + PT_MAX_OUTPUT_ADDRESS_LG2 = 52, 30 + PT_MAX_VA_ADDRESS_LG2 = 64, 31 + PT_ITEM_WORD_SIZE = sizeof(u64), 32 + PT_MAX_TOP_LEVEL = 5, 33 + PT_GRANULE_LG2SZ = 12, 34 + PT_TABLEMEM_LG2SZ = 12, 35 + 36 + /* The DTE only has these bits for the top phyiscal address */ 37 + PT_TOP_PHYS_MASK = GENMASK_ULL(51, 12), 38 + }; 39 + 40 + /* PTE bits */ 41 + enum { 42 + AMDV1PT_FMT_PR = BIT(0), 43 + AMDV1PT_FMT_D = BIT(6), 44 + AMDV1PT_FMT_NEXT_LEVEL = GENMASK_ULL(11, 9), 45 + AMDV1PT_FMT_OA = GENMASK_ULL(51, 12), 46 + AMDV1PT_FMT_FC = BIT_ULL(60), 47 + AMDV1PT_FMT_IR = BIT_ULL(61), 48 + AMDV1PT_FMT_IW = BIT_ULL(62), 49 + }; 50 + 51 + /* 52 + * gcc 13 has a bug where it thinks the output of FIELD_GET() is an enum, make 53 + * these defines to avoid it. 54 + */ 55 + #define AMDV1PT_FMT_NL_DEFAULT 0 56 + #define AMDV1PT_FMT_NL_SIZE 7 57 + 58 + static inline pt_oaddr_t amdv1pt_table_pa(const struct pt_state *pts) 59 + { 60 + u64 entry = pts->entry; 61 + 62 + if (pts_feature(pts, PT_FEAT_AMDV1_ENCRYPT_TABLES)) 63 + entry = __sme_clr(entry); 64 + return oalog2_mul(FIELD_GET(AMDV1PT_FMT_OA, entry), PT_GRANULE_LG2SZ); 65 + } 66 + #define pt_table_pa amdv1pt_table_pa 67 + 68 + /* Returns the oa for the start of the contiguous entry */ 69 + static inline pt_oaddr_t amdv1pt_entry_oa(const struct pt_state *pts) 70 + { 71 + u64 entry = pts->entry; 72 + pt_oaddr_t oa; 73 + 74 + if (pts_feature(pts, PT_FEAT_AMDV1_ENCRYPT_TABLES)) 75 + entry = __sme_clr(entry); 76 + oa = FIELD_GET(AMDV1PT_FMT_OA, entry); 77 + 78 + if (FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, entry) == AMDV1PT_FMT_NL_SIZE) { 79 + unsigned int sz_bits = oaffz(oa); 80 + 81 + oa = oalog2_set_mod(oa, 0, sz_bits); 82 + } else if (PT_WARN_ON(FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, entry) != 83 + AMDV1PT_FMT_NL_DEFAULT)) 84 + return 0; 85 + return oalog2_mul(oa, PT_GRANULE_LG2SZ); 86 + } 87 + #define pt_entry_oa amdv1pt_entry_oa 88 + 89 + static inline bool amdv1pt_can_have_leaf(const struct pt_state *pts) 90 + { 91 + /* 92 + * Table 15: Page Table Level Parameters 93 + * The top most level cannot have translation entries 94 + */ 95 + return pts->level < PT_MAX_TOP_LEVEL; 96 + } 97 + #define pt_can_have_leaf amdv1pt_can_have_leaf 98 + 99 + /* Body in pt_fmt_defaults.h */ 100 + static inline unsigned int pt_table_item_lg2sz(const struct pt_state *pts); 101 + 102 + static inline unsigned int 103 + amdv1pt_entry_num_contig_lg2(const struct pt_state *pts) 104 + { 105 + u32 code; 106 + 107 + if (FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, pts->entry) == 108 + AMDV1PT_FMT_NL_DEFAULT) 109 + return ilog2(1); 110 + 111 + PT_WARN_ON(FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, pts->entry) != 112 + AMDV1PT_FMT_NL_SIZE); 113 + 114 + /* 115 + * The contiguous size is encoded in the length of a string of 1's in 116 + * the low bits of the OA. Reverse the equation: 117 + * code = log2_to_int(num_contig_lg2 + item_lg2sz - 118 + * PT_GRANULE_LG2SZ - 1) - 1 119 + * Which can be expressed as: 120 + * num_contig_lg2 = oalog2_ffz(code) + 1 - 121 + * item_lg2sz - PT_GRANULE_LG2SZ 122 + * 123 + * Assume the bit layout is correct and remove the masking. Reorganize 124 + * the equation to move all the arithmetic before the ffz. 125 + */ 126 + code = pts->entry >> (__bf_shf(AMDV1PT_FMT_OA) - 1 + 127 + pt_table_item_lg2sz(pts) - PT_GRANULE_LG2SZ); 128 + return ffz_t(u32, code); 129 + } 130 + #define pt_entry_num_contig_lg2 amdv1pt_entry_num_contig_lg2 131 + 132 + static inline unsigned int amdv1pt_num_items_lg2(const struct pt_state *pts) 133 + { 134 + /* 135 + * Top entry covers bits [63:57] only, this is handled through 136 + * max_vasz_lg2. 137 + */ 138 + if (PT_WARN_ON(pts->level == 5)) 139 + return 7; 140 + return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64)); 141 + } 142 + #define pt_num_items_lg2 amdv1pt_num_items_lg2 143 + 144 + static inline pt_vaddr_t amdv1pt_possible_sizes(const struct pt_state *pts) 145 + { 146 + unsigned int isz_lg2 = pt_table_item_lg2sz(pts); 147 + 148 + if (!amdv1pt_can_have_leaf(pts)) 149 + return 0; 150 + 151 + /* 152 + * Table 14: Example Page Size Encodings 153 + * Address bits 51:32 can be used to encode page sizes greater than 4 154 + * Gbytes. Address bits 63:52 are zero-extended. 155 + * 156 + * 512GB Pages are not supported due to a hardware bug. 157 + * Otherwise every power of two size is supported. 158 + */ 159 + return GENMASK_ULL(min(51, isz_lg2 + amdv1pt_num_items_lg2(pts) - 1), 160 + isz_lg2) & ~SZ_512G; 161 + } 162 + #define pt_possible_sizes amdv1pt_possible_sizes 163 + 164 + static inline enum pt_entry_type amdv1pt_load_entry_raw(struct pt_state *pts) 165 + { 166 + const u64 *tablep = pt_cur_table(pts, u64) + pts->index; 167 + unsigned int next_level; 168 + u64 entry; 169 + 170 + pts->entry = entry = READ_ONCE(*tablep); 171 + if (!(entry & AMDV1PT_FMT_PR)) 172 + return PT_ENTRY_EMPTY; 173 + 174 + next_level = FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, pts->entry); 175 + if (pts->level == 0 || next_level == AMDV1PT_FMT_NL_DEFAULT || 176 + next_level == AMDV1PT_FMT_NL_SIZE) 177 + return PT_ENTRY_OA; 178 + return PT_ENTRY_TABLE; 179 + } 180 + #define pt_load_entry_raw amdv1pt_load_entry_raw 181 + 182 + static inline void 183 + amdv1pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa, 184 + unsigned int oasz_lg2, 185 + const struct pt_write_attrs *attrs) 186 + { 187 + unsigned int isz_lg2 = pt_table_item_lg2sz(pts); 188 + u64 *tablep = pt_cur_table(pts, u64) + pts->index; 189 + u64 entry; 190 + 191 + if (!pt_check_install_leaf_args(pts, oa, oasz_lg2)) 192 + return; 193 + 194 + entry = AMDV1PT_FMT_PR | 195 + FIELD_PREP(AMDV1PT_FMT_OA, log2_div(oa, PT_GRANULE_LG2SZ)) | 196 + attrs->descriptor_bits; 197 + 198 + if (oasz_lg2 == isz_lg2) { 199 + entry |= FIELD_PREP(AMDV1PT_FMT_NEXT_LEVEL, 200 + AMDV1PT_FMT_NL_DEFAULT); 201 + WRITE_ONCE(*tablep, entry); 202 + } else { 203 + unsigned int num_contig_lg2 = oasz_lg2 - isz_lg2; 204 + u64 *end = tablep + log2_to_int(num_contig_lg2); 205 + 206 + entry |= FIELD_PREP(AMDV1PT_FMT_NEXT_LEVEL, 207 + AMDV1PT_FMT_NL_SIZE) | 208 + FIELD_PREP(AMDV1PT_FMT_OA, 209 + oalog2_to_int(oasz_lg2 - PT_GRANULE_LG2SZ - 210 + 1) - 211 + 1); 212 + 213 + /* See amdv1pt_clear_entries() */ 214 + if (num_contig_lg2 <= ilog2(32)) { 215 + for (; tablep != end; tablep++) 216 + WRITE_ONCE(*tablep, entry); 217 + } else { 218 + memset64(tablep, entry, log2_to_int(num_contig_lg2)); 219 + } 220 + } 221 + pts->entry = entry; 222 + } 223 + #define pt_install_leaf_entry amdv1pt_install_leaf_entry 224 + 225 + static inline bool amdv1pt_install_table(struct pt_state *pts, 226 + pt_oaddr_t table_pa, 227 + const struct pt_write_attrs *attrs) 228 + { 229 + u64 entry; 230 + 231 + /* 232 + * IR and IW are ANDed from the table levels along with the PTE. We 233 + * always control permissions from the PTE, so always set IR and IW for 234 + * tables. 235 + */ 236 + entry = AMDV1PT_FMT_PR | 237 + FIELD_PREP(AMDV1PT_FMT_NEXT_LEVEL, pts->level) | 238 + FIELD_PREP(AMDV1PT_FMT_OA, 239 + log2_div(table_pa, PT_GRANULE_LG2SZ)) | 240 + AMDV1PT_FMT_IR | AMDV1PT_FMT_IW; 241 + if (pts_feature(pts, PT_FEAT_AMDV1_ENCRYPT_TABLES)) 242 + entry = __sme_set(entry); 243 + return pt_table_install64(pts, entry); 244 + } 245 + #define pt_install_table amdv1pt_install_table 246 + 247 + static inline void amdv1pt_attr_from_entry(const struct pt_state *pts, 248 + struct pt_write_attrs *attrs) 249 + { 250 + attrs->descriptor_bits = 251 + pts->entry & (AMDV1PT_FMT_FC | AMDV1PT_FMT_IR | AMDV1PT_FMT_IW); 252 + } 253 + #define pt_attr_from_entry amdv1pt_attr_from_entry 254 + 255 + static inline void amdv1pt_clear_entries(struct pt_state *pts, 256 + unsigned int num_contig_lg2) 257 + { 258 + u64 *tablep = pt_cur_table(pts, u64) + pts->index; 259 + u64 *end = tablep + log2_to_int(num_contig_lg2); 260 + 261 + /* 262 + * gcc generates rep stos for the io-pgtable code, and this difference 263 + * can show in microbenchmarks with larger contiguous page sizes. 264 + * rep is slower for small cases. 265 + */ 266 + if (num_contig_lg2 <= ilog2(32)) { 267 + for (; tablep != end; tablep++) 268 + WRITE_ONCE(*tablep, 0); 269 + } else { 270 + memset64(tablep, 0, log2_to_int(num_contig_lg2)); 271 + } 272 + } 273 + #define pt_clear_entries amdv1pt_clear_entries 274 + 275 + static inline bool amdv1pt_entry_is_write_dirty(const struct pt_state *pts) 276 + { 277 + unsigned int num_contig_lg2 = amdv1pt_entry_num_contig_lg2(pts); 278 + u64 *tablep = pt_cur_table(pts, u64) + 279 + log2_set_mod(pts->index, 0, num_contig_lg2); 280 + u64 *end = tablep + log2_to_int(num_contig_lg2); 281 + 282 + for (; tablep != end; tablep++) 283 + if (READ_ONCE(*tablep) & AMDV1PT_FMT_D) 284 + return true; 285 + return false; 286 + } 287 + #define pt_entry_is_write_dirty amdv1pt_entry_is_write_dirty 288 + 289 + static inline void amdv1pt_entry_make_write_clean(struct pt_state *pts) 290 + { 291 + unsigned int num_contig_lg2 = amdv1pt_entry_num_contig_lg2(pts); 292 + u64 *tablep = pt_cur_table(pts, u64) + 293 + log2_set_mod(pts->index, 0, num_contig_lg2); 294 + u64 *end = tablep + log2_to_int(num_contig_lg2); 295 + 296 + for (; tablep != end; tablep++) 297 + WRITE_ONCE(*tablep, READ_ONCE(*tablep) & ~(u64)AMDV1PT_FMT_D); 298 + } 299 + #define pt_entry_make_write_clean amdv1pt_entry_make_write_clean 300 + 301 + static inline bool amdv1pt_entry_make_write_dirty(struct pt_state *pts) 302 + { 303 + u64 *tablep = pt_cur_table(pts, u64) + pts->index; 304 + u64 new = pts->entry | AMDV1PT_FMT_D; 305 + 306 + return try_cmpxchg64(tablep, &pts->entry, new); 307 + } 308 + #define pt_entry_make_write_dirty amdv1pt_entry_make_write_dirty 309 + 310 + /* --- iommu */ 311 + #include <linux/generic_pt/iommu.h> 312 + #include <linux/iommu.h> 313 + 314 + #define pt_iommu_table pt_iommu_amdv1 315 + 316 + /* The common struct is in the per-format common struct */ 317 + static inline struct pt_common *common_from_iommu(struct pt_iommu *iommu_table) 318 + { 319 + return &container_of(iommu_table, struct pt_iommu_amdv1, iommu) 320 + ->amdpt.common; 321 + } 322 + 323 + static inline struct pt_iommu *iommu_from_common(struct pt_common *common) 324 + { 325 + return &container_of(common, struct pt_iommu_amdv1, amdpt.common)->iommu; 326 + } 327 + 328 + static inline int amdv1pt_iommu_set_prot(struct pt_common *common, 329 + struct pt_write_attrs *attrs, 330 + unsigned int iommu_prot) 331 + { 332 + u64 pte = 0; 333 + 334 + if (pt_feature(common, PT_FEAT_AMDV1_FORCE_COHERENCE)) 335 + pte |= AMDV1PT_FMT_FC; 336 + if (iommu_prot & IOMMU_READ) 337 + pte |= AMDV1PT_FMT_IR; 338 + if (iommu_prot & IOMMU_WRITE) 339 + pte |= AMDV1PT_FMT_IW; 340 + 341 + /* 342 + * Ideally we'd have an IOMMU_ENCRYPTED flag set by higher levels to 343 + * control this. For now if the tables use sme_set then so do the ptes. 344 + */ 345 + if (pt_feature(common, PT_FEAT_AMDV1_ENCRYPT_TABLES)) 346 + pte = __sme_set(pte); 347 + 348 + attrs->descriptor_bits = pte; 349 + return 0; 350 + } 351 + #define pt_iommu_set_prot amdv1pt_iommu_set_prot 352 + 353 + static inline int amdv1pt_iommu_fmt_init(struct pt_iommu_amdv1 *iommu_table, 354 + const struct pt_iommu_amdv1_cfg *cfg) 355 + { 356 + struct pt_amdv1 *table = &iommu_table->amdpt; 357 + unsigned int max_vasz_lg2 = PT_MAX_VA_ADDRESS_LG2; 358 + 359 + if (cfg->starting_level == 0 || cfg->starting_level > PT_MAX_TOP_LEVEL) 360 + return -EINVAL; 361 + 362 + if (!pt_feature(&table->common, PT_FEAT_DYNAMIC_TOP) && 363 + cfg->starting_level != PT_MAX_TOP_LEVEL) 364 + max_vasz_lg2 = PT_GRANULE_LG2SZ + 365 + (PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64))) * 366 + (cfg->starting_level + 1); 367 + 368 + table->common.max_vasz_lg2 = 369 + min(max_vasz_lg2, cfg->common.hw_max_vasz_lg2); 370 + table->common.max_oasz_lg2 = 371 + min(PT_MAX_OUTPUT_ADDRESS_LG2, cfg->common.hw_max_oasz_lg2); 372 + pt_top_set_level(&table->common, cfg->starting_level); 373 + return 0; 374 + } 375 + #define pt_iommu_fmt_init amdv1pt_iommu_fmt_init 376 + 377 + static inline void 378 + amdv1pt_iommu_fmt_hw_info(struct pt_iommu_amdv1 *table, 379 + const struct pt_range *top_range, 380 + struct pt_iommu_amdv1_hw_info *info) 381 + { 382 + info->host_pt_root = virt_to_phys(top_range->top_table); 383 + PT_WARN_ON(info->host_pt_root & ~PT_TOP_PHYS_MASK); 384 + info->mode = top_range->top_level + 1; 385 + } 386 + #define pt_iommu_fmt_hw_info amdv1pt_iommu_fmt_hw_info 387 + #endif
+21
drivers/iommu/generic_pt/fmt/defs_amdv1.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES 4 + * 5 + */ 6 + #ifndef __GENERIC_PT_FMT_DEFS_AMDV1_H 7 + #define __GENERIC_PT_FMT_DEFS_AMDV1_H 8 + 9 + #include <linux/generic_pt/common.h> 10 + #include <linux/types.h> 11 + 12 + typedef u64 pt_vaddr_t; 13 + typedef u64 pt_oaddr_t; 14 + 15 + struct amdv1pt_write_attrs { 16 + u64 descriptor_bits; 17 + gfp_t gfp; 18 + }; 19 + #define pt_write_attrs amdv1pt_write_attrs 20 + 21 + #endif
+15
drivers/iommu/generic_pt/fmt/iommu_amdv1.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES 4 + */ 5 + #define PT_FMT amdv1 6 + #define PT_SUPPORTED_FEATURES \ 7 + (BIT(PT_FEAT_FULL_VA) | BIT(PT_FEAT_DYNAMIC_TOP) | \ 8 + BIT(PT_FEAT_FLUSH_RANGE) | BIT(PT_FEAT_FLUSH_RANGE_NO_GAPS) | \ 9 + BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) | \ 10 + BIT(PT_FEAT_AMDV1_FORCE_COHERENCE)) 11 + #define PT_FORCE_ENABLED_FEATURES \ 12 + (BIT(PT_FEAT_DYNAMIC_TOP) | BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) | \ 13 + BIT(PT_FEAT_AMDV1_FORCE_COHERENCE)) 14 + 15 + #include "iommu_template.h"
+19
include/linux/generic_pt/common.h
··· 132 132 PT_FEAT_FMT_START, 133 133 }; 134 134 135 + struct pt_amdv1 { 136 + struct pt_common common; 137 + }; 138 + 139 + enum { 140 + /* 141 + * The memory backing the tables is encrypted. Use __sme_set() to adjust 142 + * the page table pointers in the tree. This only works with 143 + * CONFIG_AMD_MEM_ENCRYPT. 144 + */ 145 + PT_FEAT_AMDV1_ENCRYPT_TABLES = PT_FEAT_FMT_START, 146 + /* 147 + * The PTEs are set to prevent cache incoherent traffic, such as PCI no 148 + * snoop. This is set either at creation time or before the first map 149 + * operation. 150 + */ 151 + PT_FEAT_AMDV1_FORCE_COHERENCE, 152 + }; 153 + 135 154 #endif
+12
include/linux/generic_pt/iommu.h
··· 145 145 static_assert(offsetof(s, pt_iommu_memb.domain) == \ 146 146 offsetof(s, domain_memb)) 147 147 148 + struct pt_iommu_amdv1_cfg { 149 + struct pt_iommu_cfg common; 150 + unsigned int starting_level; 151 + }; 152 + 153 + struct pt_iommu_amdv1_hw_info { 154 + u64 host_pt_root; 155 + u8 mode; 156 + }; 157 + 158 + IOMMU_FORMAT(amdv1, amdpt); 159 + 148 160 #undef IOMMU_PROTOTYPES 149 161 #undef IOMMU_FORMAT 150 162 #endif