| From f384c2b7d5113952e57e614a17b06cabcb21f8fd Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 19 Nov 2021 15:27:57 -0800 |
| Subject: x86/sev: Fix SEV-ES INS/OUTS instructions for word, dword, and qword |
| |
| From: Michael Sterritt <sterritt@google.com> |
| |
| [ Upstream commit 1d5379d0475419085d3575bd9155f2e558e96390 ] |
| |
| Properly type the operands being passed to __put_user()/__get_user(). |
| Otherwise, these routines truncate data for dependent instructions |
| (e.g., INSW) and only read/write one byte. |
| |
| This has been tested by sending a string with REP OUTSW to a port and |
| then reading it back in with REP INSW on the same port. |
| |
| Previous behavior was to only send and receive the first char of the |
| size. For example, word operations for "abcd" would only read/write |
| "ac". With change, the full string is now written and read back. |
| |
| Fixes: f980f9c31a923 (x86/sev-es: Compile early handler code into kernel image) |
| Signed-off-by: Michael Sterritt <sterritt@google.com> |
| Signed-off-by: Borislav Petkov <bp@suse.de> |
| Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> |
| Reviewed-by: Marc Orr <marcorr@google.com> |
| Reviewed-by: Peter Gonda <pgonda@google.com> |
| Reviewed-by: Joerg Roedel <jroedel@suse.de> |
| Link: https://lkml.kernel.org/r/20211119232757.176201-1-sterritt@google.com |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/kernel/sev-es.c | 57 +++++++++++++++++++++++++++------------- |
| 1 file changed, 39 insertions(+), 18 deletions(-) |
| |
| diff --git a/arch/x86/kernel/sev-es.c b/arch/x86/kernel/sev-es.c |
| index 865e234ea24bd..c222fab112cbd 100644 |
| --- a/arch/x86/kernel/sev-es.c |
| +++ b/arch/x86/kernel/sev-es.c |
| @@ -260,11 +260,6 @@ static enum es_result vc_write_mem(struct es_em_ctxt *ctxt, |
| char *dst, char *buf, size_t size) |
| { |
| unsigned long error_code = X86_PF_PROT | X86_PF_WRITE; |
| - char __user *target = (char __user *)dst; |
| - u64 d8; |
| - u32 d4; |
| - u16 d2; |
| - u8 d1; |
| |
| /* |
| * This function uses __put_user() independent of whether kernel or user |
| @@ -286,26 +281,42 @@ static enum es_result vc_write_mem(struct es_em_ctxt *ctxt, |
| * instructions here would cause infinite nesting. |
| */ |
| switch (size) { |
| - case 1: |
| + case 1: { |
| + u8 d1; |
| + u8 __user *target = (u8 __user *)dst; |
| + |
| memcpy(&d1, buf, 1); |
| if (__put_user(d1, target)) |
| goto fault; |
| break; |
| - case 2: |
| + } |
| + case 2: { |
| + u16 d2; |
| + u16 __user *target = (u16 __user *)dst; |
| + |
| memcpy(&d2, buf, 2); |
| if (__put_user(d2, target)) |
| goto fault; |
| break; |
| - case 4: |
| + } |
| + case 4: { |
| + u32 d4; |
| + u32 __user *target = (u32 __user *)dst; |
| + |
| memcpy(&d4, buf, 4); |
| if (__put_user(d4, target)) |
| goto fault; |
| break; |
| - case 8: |
| + } |
| + case 8: { |
| + u64 d8; |
| + u64 __user *target = (u64 __user *)dst; |
| + |
| memcpy(&d8, buf, 8); |
| if (__put_user(d8, target)) |
| goto fault; |
| break; |
| + } |
| default: |
| WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size); |
| return ES_UNSUPPORTED; |
| @@ -328,11 +339,6 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, |
| char *src, char *buf, size_t size) |
| { |
| unsigned long error_code = X86_PF_PROT; |
| - char __user *s = (char __user *)src; |
| - u64 d8; |
| - u32 d4; |
| - u16 d2; |
| - u8 d1; |
| |
| /* |
| * This function uses __get_user() independent of whether kernel or user |
| @@ -354,26 +360,41 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, |
| * instructions here would cause infinite nesting. |
| */ |
| switch (size) { |
| - case 1: |
| + case 1: { |
| + u8 d1; |
| + u8 __user *s = (u8 __user *)src; |
| + |
| if (__get_user(d1, s)) |
| goto fault; |
| memcpy(buf, &d1, 1); |
| break; |
| - case 2: |
| + } |
| + case 2: { |
| + u16 d2; |
| + u16 __user *s = (u16 __user *)src; |
| + |
| if (__get_user(d2, s)) |
| goto fault; |
| memcpy(buf, &d2, 2); |
| break; |
| - case 4: |
| + } |
| + case 4: { |
| + u32 d4; |
| + u32 __user *s = (u32 __user *)src; |
| + |
| if (__get_user(d4, s)) |
| goto fault; |
| memcpy(buf, &d4, 4); |
| break; |
| - case 8: |
| + } |
| + case 8: { |
| + u64 d8; |
| + u64 __user *s = (u64 __user *)src; |
| if (__get_user(d8, s)) |
| goto fault; |
| memcpy(buf, &d8, 8); |
| break; |
| + } |
| default: |
| WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size); |
| return ES_UNSUPPORTED; |
| -- |
| 2.33.0 |
| |