1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <console/console.h>
6 #include <device/mmio.h>
7 #include <device/pci_ops.h>
9 #include <intelblocks/systemagent.h>
10 #include <intelblocks/vtd.h>
12 #include <soc/iomap.h>
13 #include <soc/pci_devs.h>
15 /* FSP 2.x VT-d HOB from edk2-platforms */
16 static const uint8_t vtd_pmr_info_data_hob_guid
[16] = {
17 0x45, 0x16, 0xb6, 0x6f, 0x68, 0xf1, 0xbe, 0x46,
18 0x80, 0xec, 0xb5, 0x02, 0x38, 0x5e, 0xe7, 0xe7
21 struct vtd_pmr_info_hob
{
22 uint32_t protected_low_base
;
23 uint32_t protected_low_limit
;
24 uint64_t protected_high_base
;
25 uint64_t protected_high_limit
;
28 static struct vtd_pmr_info_hob
*pmr_hob
;
30 static bool is_vtd_enabled(uintptr_t vtd_base
)
32 uint32_t version
= vtd_read32(vtd_base
, VER_REG
);
34 if (version
== 0 || version
== UINT32_MAX
) {
35 printk(BIOS_WARNING
, "No VT-d @ 0x%08lx\n", vtd_base
);
39 printk(BIOS_DEBUG
, "VT-d @ 0x%08lx, version %x.%x\n",
40 vtd_base
, (version
& 0xf0) >> 4, version
& 0xf);
45 static uint32_t vtd_get_pmr_alignment_lo(uintptr_t vtd_base
)
49 vtd_write32(vtd_base
, PLMLIMIT_REG
, 0xffffffff);
50 value
= vtd_read32(vtd_base
, PLMLIMIT_REG
);
56 static uint64_t vtd_get_pmr_alignment_hi(uintptr_t vtd_base
)
60 vtd_write64(vtd_base
, PHMLIMIT_REG
, 0xffffffffffffffffULL
);
61 value
= vtd_read64(vtd_base
, PHMLIMIT_REG
);
62 value
= ~value
+ 1ULL;
63 value
= value
& ((1ULL << (uint32_t)cpu_phys_address_size()) - 1ULL);
65 /* The host address width can be different than the sizing of the register.
66 * Simply find the least significant bit set and use it as alignment;
68 return __ffs64(value
);
71 static void vtd_set_pmr_low(uintptr_t vtd_base
)
73 uint32_t pmr_lo_align
;
74 uint32_t pmr_lo_limit
;
76 * Typical PMR alignment is 1MB so we should be good but check just in
79 pmr_lo_align
= vtd_get_pmr_alignment_lo(vtd_base
);
80 pmr_lo_limit
= pmr_hob
->protected_low_limit
;
82 if (!IS_ALIGNED(pmr_lo_limit
, pmr_lo_align
)) {
83 pmr_lo_limit
= ALIGN_DOWN(pmr_lo_limit
, pmr_lo_align
);
84 printk(BIOS_WARNING
, "PMR limit low not properly aligned, aligning down to %08x\n",
88 printk(BIOS_INFO
, "Setting DMA protection [0x0 - 0x%08x]\n", pmr_lo_limit
);
89 vtd_write32(vtd_base
, PLMBASE_REG
, 0);
90 vtd_write32(vtd_base
, PLMLIMIT_REG
, pmr_lo_limit
- 1);
93 static void vtd_set_pmr_high(uintptr_t vtd_base
)
95 uint64_t pmr_hi_align
;
96 uint64_t pmr_hi_limit
;
98 * Typical PMR alignment is 1MB so we should be good with above 4G
99 * memory but check just in case.
101 pmr_hi_align
= vtd_get_pmr_alignment_hi(vtd_base
);
102 pmr_hi_limit
= pmr_hob
->protected_high_limit
;
104 /* No memory above 4G? Skip PMR high programming */
105 if (pmr_hi_limit
== 0 || pmr_hi_limit
< 4ULL * GiB
)
108 if (!IS_ALIGNED(pmr_hi_limit
, pmr_hi_align
)) {
109 pmr_hi_limit
= ALIGN_DOWN(pmr_hi_limit
, pmr_hi_align
);
110 printk(BIOS_WARNING
, "PMR High limit not properly aligned, "
111 "aligning down to %016llx\n",
115 printk(BIOS_INFO
, "Setting DMA protection [0x100000000 - 0x%016llx]\n", pmr_hi_limit
);
116 vtd_write64(vtd_base
, PHMBASE_REG
, 4ULL * GiB
);
117 vtd_write64(vtd_base
, PHMLIMIT_REG
, pmr_hi_limit
- 1ULL);
120 static bool disable_pmr_protection(uintptr_t vtd_base
)
122 if (vtd_read32(vtd_base
, PMEN_REG
) & PMEN_PRS
) {
123 vtd_write32(vtd_base
, PMEN_REG
, vtd_read32(vtd_base
, PMEN_REG
) & ~PMEN_EPM
);
124 if (vtd_read32(vtd_base
, PMEN_REG
) & PMEN_PRS
) {
125 printk(BIOS_ERR
, "Failed to disable existing DMA protection\n");
133 static bool enable_pmr_protection(uintptr_t vtd_base
)
135 vtd_write32(vtd_base
, PMEN_REG
, vtd_read32(vtd_base
, PMEN_REG
) | PMEN_EPM
);
136 if (vtd_read32(vtd_base
, PMEN_REG
) & PMEN_PRS
)
142 static const void *locate_pmr_info_hob(void)
148 return (void *)pmr_hob
;
150 hob
= fsp_find_extension_hob_by_guid(vtd_pmr_info_data_hob_guid
, &size
);
153 pmr_hob
= (struct vtd_pmr_info_hob
*)hob
;
154 printk(BIOS_SPEW
, "PMR info HOB:\n"
155 " protected_low_base: %08x\n"
156 " protected_low_limit: %08x\n"
157 " protected_high_base: %016llx\n"
158 " protected_high_limit: %016llx\n",
159 pmr_hob
->protected_low_base
, pmr_hob
->protected_low_limit
,
160 pmr_hob
->protected_high_base
, pmr_hob
->protected_high_limit
);
166 static void vtd_engine_enable_dma_protection(uintptr_t vtd_base
)
168 if (!is_vtd_enabled(vtd_base
)) {
169 printk(BIOS_ERR
, "Not enabling DMA protection, VT-d not found\n");
173 /* At minimum PMR Low must be supported, coreboot executes in 32bit space (for now) */
174 if (!(vtd_read32(vtd_base
, CAP_REG
) & CAP_PMR_LO
)) {
175 printk(BIOS_ERR
, "Not enabling DMA protection, PMR registers not supported\n");
179 if (!locate_pmr_info_hob()) {
180 printk(BIOS_ERR
, "VT-d PMR HOB not found, not enabling DMA protection\n");
184 /* If protection is enabled, disable it first */
185 if (!disable_pmr_protection(vtd_base
)) {
186 printk(BIOS_ERR
, "Not setting DMA protection\n");
190 vtd_set_pmr_low(vtd_base
);
192 if (vtd_read32(vtd_base
, CAP_REG
) & CAP_PMR_HI
)
193 vtd_set_pmr_high(vtd_base
);
195 if (enable_pmr_protection(vtd_base
))
196 printk(BIOS_INFO
, "Successfully enabled VT-d PMR DMA protection\n");
198 printk(BIOS_ERR
, "Enabling VT-d PMR DMA protection failed\n");
201 static const struct hob_resource
*find_resource_hob_by_addr(const uint64_t addr
)
203 const struct hob_header
*hob_iterator
;
204 const struct hob_resource
*res
;
206 if (fsp_hob_iterator_init(&hob_iterator
) != CB_SUCCESS
) {
207 printk(BIOS_ERR
, "Failed to find HOB list\n");
211 while (fsp_hob_iterator_get_next_resource(&hob_iterator
, &res
) == CB_SUCCESS
) {
212 if ((res
->type
== EFI_RESOURCE_MEMORY_RESERVED
) && (res
->addr
== addr
))
219 void *vtd_get_dma_buffer(size_t *size
)
221 const struct hob_resource
*res
;
223 if (!CONFIG(ENABLE_EARLY_DMA_PROTECTION
))
226 if (!locate_pmr_info_hob()) {
227 printk(BIOS_ERR
, "FSP PMR info HOB not found\n");
231 /* PMR low limit will be the DMA buffer base reserved by FSP */
232 res
= find_resource_hob_by_addr((uint64_t)pmr_hob
->protected_low_limit
);
234 printk(BIOS_ERR
, "FSP PMR resource HOB not found\n");
241 return (void *)(uintptr_t)res
->addr
;
249 void vtd_enable_dma_protection(void)
251 if (!CONFIG(ENABLE_EARLY_DMA_PROTECTION
))
254 vtd_engine_enable_dma_protection(VTVC0_BASE_ADDRESS
);
256 * FIXME: GFX VT-d will fail to set PMR (tested on ADL-S).
257 * Should we program PMRs on all VT-d engines?
258 * vtd_engine_enable_dma_protection(GFXVT_BASE_ADDRESS);
259 * vtd_engine_enable_dma_protection(IPUVT_BASE_ADDRESS);
263 static void vtd_disable_pmr_on_resume(void *unused
)
265 /* At minimum PMR Low must be supported */
266 if (!(vtd_read32(VTVC0_BASE_ADDRESS
, CAP_REG
) & CAP_PMR_LO
))
269 if (disable_pmr_protection(VTVC0_BASE_ADDRESS
)) {
270 vtd_write32(VTVC0_BASE_ADDRESS
, PLMBASE_REG
, 0);
271 vtd_write32(VTVC0_BASE_ADDRESS
, PLMLIMIT_REG
, 0);
272 if (vtd_read32(VTVC0_BASE_ADDRESS
, CAP_REG
) & CAP_PMR_HI
) {
273 vtd_write64(VTVC0_BASE_ADDRESS
, PHMBASE_REG
, 0);
274 vtd_write64(VTVC0_BASE_ADDRESS
, PHMLIMIT_REG
, 0);
279 BOOT_STATE_INIT_ENTRY(BS_OS_RESUME
, BS_ON_ENTRY
, vtd_disable_pmr_on_resume
, NULL
);