Skip to content

Commit d915adb

Browse files
joergroedelkhbecker
authored andcommitted
x86/sev: Unroll string mmio with CC_ATTR_GUEST_UNROLL_STRING_IO
BugLink: https://bugs.launchpad.net/bugs/2020319 The io-specific memcpy/memset functions use string mmio accesses to do their work. Under SEV, the hypervisor can't emulate these instructions because they read/write directly from/to encrypted memory. KVM will inject a page fault exception into the guest when it is asked to emulate string mmio instructions for an SEV guest: BUG: unable to handle page fault for address: ffffc90000065068 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 8000100000067 P4D 8000100000067 PUD 80001000fb067 PMD 80001000fc067 PTE 80000000fed40173 Oops: 0000 [#1] PREEMPT SMP NOPTI CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.17.0-rc7 #3 As string mmio for an SEV guest can not be supported by the hypervisor, unroll the instructions for CC_ATTR_GUEST_UNROLL_STRING_IO enabled kernels. This issue appears when kernels are launched in recent libvirt-managed SEV virtual machines, because virt-install started to add a tpm-crb device to the guest by default and proactively because, raisins: virt-manager/virt-manager@eb58c09 and as that commit says, the default adding of a TPM can be disabled with "virt-install ... --tpm none". The kernel driver for tpm-crb uses memcpy_to/from_io() functions to access MMIO memory, resulting in a page-fault injected by KVM and crashing the kernel at boot. [ bp: Massage and extend commit message. ] Fixes: d8aa7ee ('x86/mm: Add Secure Encrypted Virtualization (SEV) support') Signed-off-by: Joerg Roedel <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Reviewed-by: Tom Lendacky <[email protected]> Cc: <[email protected]> Link: https://lore.kernel.org/r/[email protected] (cherry picked from commit 4009a4a) Signed-off-by: Thadeu Lima de Souza Cascardo <[email protected]>
1 parent c0081ac commit d915adb

File tree

1 file changed

+57
-8
lines changed

1 file changed

+57
-8
lines changed

arch/x86/lib/iomem.c

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ static __always_inline void rep_movs(void *to, const void *from, size_t n)
2222
: "memory");
2323
}
2424

25-
void memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
25+
static void string_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
2626
{
2727
if (unlikely(!n))
2828
return;
@@ -38,9 +38,8 @@ void memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
3838
}
3939
rep_movs(to, (const void *)from, n);
4040
}
41-
EXPORT_SYMBOL(memcpy_fromio);
4241

43-
void memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
42+
static void string_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
4443
{
4544
if (unlikely(!n))
4645
return;
@@ -56,14 +55,64 @@ void memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
5655
}
5756
rep_movs((void *)to, (const void *) from, n);
5857
}
58+
59+
static void unrolled_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
60+
{
61+
const volatile char __iomem *in = from;
62+
char *out = to;
63+
int i;
64+
65+
for (i = 0; i < n; ++i)
66+
out[i] = readb(&in[i]);
67+
}
68+
69+
static void unrolled_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
70+
{
71+
volatile char __iomem *out = to;
72+
const char *in = from;
73+
int i;
74+
75+
for (i = 0; i < n; ++i)
76+
writeb(in[i], &out[i]);
77+
}
78+
79+
static void unrolled_memset_io(volatile void __iomem *a, int b, size_t c)
80+
{
81+
volatile char __iomem *mem = a;
82+
int i;
83+
84+
for (i = 0; i < c; ++i)
85+
writeb(b, &mem[i]);
86+
}
87+
88+
void memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
89+
{
90+
if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
91+
unrolled_memcpy_fromio(to, from, n);
92+
else
93+
string_memcpy_fromio(to, from, n);
94+
}
95+
EXPORT_SYMBOL(memcpy_fromio);
96+
97+
void memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
98+
{
99+
if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
100+
unrolled_memcpy_toio(to, from, n);
101+
else
102+
string_memcpy_toio(to, from, n);
103+
}
59104
EXPORT_SYMBOL(memcpy_toio);
60105

61106
void memset_io(volatile void __iomem *a, int b, size_t c)
62107
{
63-
/*
64-
* TODO: memset can mangle the IO patterns quite a bit.
65-
* perhaps it would be better to use a dumb one:
66-
*/
67-
memset((void *)a, b, c);
108+
if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO)) {
109+
unrolled_memset_io(a, b, c);
110+
} else {
111+
/*
112+
* TODO: memset can mangle the IO patterns quite a bit.
113+
* perhaps it would be better to use a dumb one:
114+
*/
115+
memset((void *)a, b, c);
116+
}
68117
}
69118
EXPORT_SYMBOL(memset_io);

0 commit comments

Comments
 (0)